Как разделить старший / младший байт от 16-битного адреса?
Я разрабатываю программное обеспечение на 8051 процессоре. Частой задачей является разделение старшего и младшего байта 16-битного адреса. Я хочу видеть, сколько способов достичь этого. Пока я подхожу так: (скажем, ptr - 16-битный указатель, а int - 16-битный int) [обратите внимание, что rn и arn - это регистры]
побитовая операция
ADDH = (unsigned int) ptr >> 8;
ADDL = (unsigned int) ptr & 0x00FF;
SDCC дает следующий код сборки
; 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 дает мне:
; 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
в котором много бесполезного кода ИМХО.хитрость указателя
ADDH = ((unsigned char *)&ptr)[0];
ADDL = ((unsigned char *)&ptr)[1];
SDCC дает мне:
; 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 дает мне:
; SOURCE LINE # 37
006A 850000 R MOV ADDH,ptr
; SOURCE LINE # 38
006D 850000 R MOV ADDL,ptr+01H
что то же самое с версией SDCC.Андрей математический подход
ADDH = ptr / 256;
ADDL = ptr % 256;
SDCC дает:
; 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 дает мне:
; 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 ни...профсоюзный подход Семая
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];
SDCC дает мне
; 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 дает мне:
; 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;
// 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;
}
main (void)
{
char c[] = "hello world.";
unsigned char xdata *ptr = (unsigned char xdata *)c;
unsigned char ADDH, ADDL;
unsigned char i = 0;
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 ответа
Самый эффективный способ полностью зависит от компилятора. Вы определенно должны выяснить, как получить список сборок от вашего компилятора для проекта 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];
Еще один не очень яркий способ разделить адрес:
ADDH = ptr / 256;
ADDL = ptr % 256;
Я просто создаю два определения (следующим образом).
Это кажется более прямым и менее подверженным ошибкам.
#define HI(x) ((x) >> 8)
#define LO(x) ((x) & 0xFF)
Наиболее эффективным является первый, так как это делается в одной инструкции.
НЕТ! Я соврал тебе извините. Я забыл, что в наборе команд 8051 есть только 1-битные инструкции сдвига. Второй должен быть быстрее, но компилятор может генерировать глупый код, поэтому будьте осторожны и проверяйте код сборки.