4

8051 プロセッサでソフトウェアを開発しています。よくある作業は、16 ビット アドレスの上位バイトと下位バイトを分割することです。それを達成する方法がいくつあるか見てみたい。私がこれまでに思いついた方法は次のとおりです。

ビット演算

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
多くの役に立たないコードIMHOがあります。

ポインタートリック


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
これは、ポインターのトリックに非常に適しています。ただし、このアプローチでは、共用体を格納するためにさらに 2 バイトのメモリが必要になります。

他に明るいアイデアを持っている人はいますか?;)

そして、どちらの方法がより効率的か誰にも教えてもらえますか?

興味のある方のために、テストケースは次のとおりです。


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

4 に答える 4

6

最も効率的な方法は、コンパイラに完全に依存しています。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];
于 2010-03-29T15:39:52.563 に答える
3

アドレスを分割する別のそれほど明るくない方法:

 ADDH = ptr / 256;
 ADDL = ptr % 256;
于 2010-03-29T14:31:29.977 に答える
2

単一の命令で実行されるため、最も効率的なのは最初の命令です。

いいえ!嘘をつきました、ごめんなさい。8051 命令セットには 1 ビット シフト命令しかないことを忘れていました。2 番目の方が速いはずですが、コンパイラは愚かなコードを生成する可能性があるため、注意してアセンブリ コードを確認してください。

于 2010-03-29T14:26:06.463 に答える
2

2 つの定義を作成するだけです (次のように)。

より簡単で、エラーが発生しにくいようです。

#define HI(x)  ((x) >> 8)
#define LO(x)  ((x) & 0xFF)
于 2010-04-22T16:40:07.043 に答える