Я занимаюсь разработкой программного обеспечения на процессоре 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);
}
}