I'm developing a software on 8051 processor. A frequent job is to divide the high and low byte of a 16bit address. I want to see there are how many ways to achieve it. The ways I come up so far are: (say ptr is a 16bit pointer, and int is 16bit int) [note the rn and arn is registers]
ADDH = (unsigned int) ptr >> 8;
ADDL = (unsigned int) ptr & 0x00FF;
SDCC gives the following assembly 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 gives me:
; 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
which has many useless code IMHO.
ADDH = ((unsigned char *)&ptr)[0];
ADDL = ((unsigned char *)&ptr)[1];
SDCC gives me:
; 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 gives me:
; SOURCE LINE # 37
006A 850000 R MOV ADDH,ptr
; SOURCE LINE # 38
006D 850000 R MOV ADDL,ptr+01H
which is the same with SDCC version.
ADDH = ptr / 256;
ADDL = ptr % 256;
SDCC gives:
; 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
I've no idea why sdcc use the r7 register...
Keil C51 gives me:
; 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
I've no idea why Keil use R2 register neither...
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 gives me
; 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 gives me:
; SOURCE LINE # 26
0028 850000 R MOV ADDH,uptr
; SOURCE LINE # 27
002B 850000 R MOV ADDL,uptr+01H
which is very smiler to the pointers trick. However, this approach require two more bytes memory the store the union.
Does anyone have any other bright ideas? ;)
And anyone can tell me which way is more efficient?
In case anyone interested, here is the test case:
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);
}
}
The most efficient way is completely dependent on the compiler. You definitely have to figure out how to get an assembly listing from your compiler for an 8051 project.
One method you might try that is similar to those already mentioned is a union:
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];