Как разделить старший / младший байт от 16-битного адреса? - PullRequest
4 голосов
/ 29 марта 2010

Я занимаюсь разработкой программного обеспечения на процессоре 8051. Частой задачей является разделение старшего и младшего байта 16-битного адреса. Я хочу видеть, как много способов достичь этого. До сих пор я подходил так: (скажем, ptr - 16-битный указатель, а int - 16-битный int)

битовая операция

ADDH = (unsigned int) ptr >> 8;
ADDL = (unsigned int) ptr & 0x00FF;

SDCC дает следующий код сборки

<code>
;   t.c:32: ADDH = (unsigned int) ptr >> 8;
    mov ar6,r3
    mov ar7,r4
    mov _main_ADDH_1_1,r7
;   t.c:33: ADDL = (unsigned int) ptr & 0x00FF;
    mov _main_ADDL_1_1,r6
Keil C51 дает мне:
<code>
                                           ; SOURCE LINE # 32
0045 AA00        R     MOV     R2,ptr+01H
0047 A900        R     MOV     R1,ptr+02H
0049 AE02              MOV     R6,AR2
004B EE                MOV     A,R6
004C F500        R     MOV     ADDH,A
                                           ; SOURCE LINE # 33
004E AF01              MOV     R7,AR1
0050 EF                MOV     A,R7
0051 F500        R     MOV     ADDL,A
в котором много бесполезного кода ИМХО.

указатель трюк

<code>
ADDH = ((unsigned char *)&ptr)[0];
ADDL = ((unsigned char *)&ptr)[1];
SDCC дает мне:
<code>
;   t.c:37: ADDH = ((unsigned char *)&ptr)[0];
    mov _main_ADDH_1_1,_main_ptr_1_1
;   t.c:38: ADDL = ((unsigned char *)&ptr)[1];
    mov _main_ADDL_1_1,(_main_ptr_1_1 + 0x0001)
Keil C51 дает мне:
<code>
                                           ; SOURCE LINE # 37
006A 850000      R     MOV     ADDH,ptr
                                           ; SOURCE LINE # 38
006D 850000      R     MOV     ADDL,ptr+01H
то же самое с версией SDCC.

математический подход Андрея

<code>
 ADDH = ptr / 256;
 ADDL = ptr % 256;

SDCC дает:

<code>
;   t.c:42: ADDH = (unsigned int)ptr / 256;
    mov ar5,r3
    mov ar6,r4
    mov ar7,r6
    mov _main_ADDH_1_1,r7
;   t.c:43: ADDL = (unsigned int)ptr % 256;
    mov _main_ADDL_1_1,r5
Я понятия не имею, почему sdcc использует регистр r7 ... Keil C51 дает мне:
<code>
                                           ; SOURCE LINE # 42
0079 AE00        R     MOV     R6,ptr
007B AF00        R     MOV     R7,ptr+01H
007D AA06              MOV     R2,AR6
007F EA                MOV     A,R2
0080 F500        R     MOV     ADDH,A
                                           ; SOURCE LINE # 43
0082 8F00        R     MOV     ADDL,R7
Я понятия не имею, почему Keil использует регистр R2 ни ...

профсоюзный подход Семая

<code>
typedef union
   {
   unsigned short u16;
   unsigned char u8[2];
   } U16_U8;</p>

<p>U16_U8 ptr;</p>

<p>// Do something to set the variable ptr
ptr.u16 = ?;</p>

<p>ADDH = ptr.u8[0];
ADDL = ptr.u8[1];

SDCC дает мне

<code>
;   t.c:26: ADDH = uptr.u8[0];
    mov _main_ADDH_1_1,_main_uptr_1_1
;   t.c:27: ADDL = uptr.u8[1];
    mov _main_ADDL_1_1,(_main_uptr_1_1 + 0x0001)
Keil C51 дает мне:
<code>
                                           ; SOURCE LINE # 26
0028 850000      R     MOV     ADDH,uptr
                                           ; SOURCE LINE # 27
002B 850000      R     MOV     ADDL,uptr+01H
что очень улыбчиво на уловку указателей. Однако этот подход требует еще двух байтов памяти для хранения объединения.

У кого-нибудь есть другие яркие идеи? ;)

А кто-нибудь может сказать мне, какой путь более эффективен?

В случае, если кому-то интересно, вот контрольный пример:


typedef union
{
    unsigned short u16;
    unsigned char u8[2];
} U16_U8;</p>

<p>// call a function on the ADDs to avoid optimizition
void swap(unsigned char *a, unsigned char *b)
{
    unsigned char tm;
    tm = *a;
    *a = *b;
    *b = tm;
}</p>

<p>main (void)
{
    char c[] = "hello world.";
    unsigned char xdata *ptr = (unsigned char xdata *)c;
    unsigned char ADDH, ADDL;
    unsigned char i = 0;</p>

<pre><code>U16_U8 uptr;
uptr.u16 = (unsigned short)ptr;

for ( ; i < 4 ; i++, uptr.u16++){
    ADDH = uptr.u8[0];
    ADDL = uptr.u8[1];
    swap(&ADDH, &ADDL);
}

for ( ; i < 4 ; i++, ptr++){
    ADDH = (unsigned int) ptr >> 8;
    ADDL = (unsigned int) ptr & 0x00FF;
    swap(&ADDH, &ADDL);
}
for ( ; i < 4 ; i++, ptr++){
    ADDH = ((unsigned char *)&ptr)[0];
    ADDL = ((unsigned char *)&ptr)[1];
    swap(&ADDH, &ADDL);
}
for ( ; i < 4 ; i++, ptr++){
    ADDH = (unsigned int)ptr / 256;
    ADDL = (unsigned int)ptr % 256;
    swap(&ADDH, &ADDL);
}

}

Ответы [ 4 ]

6 голосов
/ 29 марта 2010

Самый эффективный способ полностью зависит от компилятора.Вам определенно нужно выяснить, как получить список сборок от вашего компилятора для проекта 8051.

Один из способов, который вы можете попробовать, похож на уже упомянутый, - это объединение:

typedef union
   {
   unsigned short u16;
   unsigned char u8[2];
   } U16_U8;

U16_U8 ptr;

// Do something to set the variable ptr
ptr.u16 = ?;

ADDH = ptr.u8[0];
ADDL = ptr.u8[1];
3 голосов
/ 29 марта 2010

Еще один не очень яркий способ разбить адрес:

 ADDH = ptr / 256;
 ADDL = ptr % 256;
2 голосов
/ 22 апреля 2010

Я просто создаю два определения (следующим образом).

Это кажется более прямым и менее подверженным ошибкам.

#define HI(x)  ((x) >> 8)
#define LO(x)  ((x) & 0xFF)
2 голосов
/ 29 марта 2010

наиболее эффективным является первый, так как это делается в одной инструкции.

НЕТ! Я соврал тебе извините. Я забыл, что в наборе команд 8051 есть только 1-битные инструкции сдвига. Второй должен быть быстрее, но компилятор может генерировать глупый код, поэтому будьте осторожны и проверьте код сборки.

...