SSブログ

ARM アセンブラで 32ビット定数をロードする [プログラム三昧]このエントリーを含むはてなブックマーク#

ARM Cortex-M プロセッサでは、もちろんアセンブラでプログラムを書くことが出来ます。 基本的な動作として、32ビットの定数をレジスタに格納する操作について調べてみました。

LDR Rd,=const という疑似命令

LDR Rd,=const 構文

32ビットの定数をレジスタに格納する場合、アセンブラで使用される "LDR Rd,=const" という構文があります。 この構文は、疑似命令として解釈され、適切な別の命令に変換されます。 詳細は、 LDR Rd, =const を使用したイミディエート値のロード に書いてあります。 これによると、変換できるものは MOV, MOVN 命令に変換され、それ以外は PC 相対アドレッシングでメモリアクセスを行うのだそうです。 どんな風に変換されるのか、試してみます。

Cortex-M4 の場合

最初は、 Cortex-M4 の場合です。 uVision 5.14 評価版を使い、いくつかの定数ロードをアセンブルしてみました。

        AREA    |.text|, CODE, READONLY

__main\
        PROC
        EXPORT  __main
            
        LDR     R0,=0x000000FF
        LDR     R0,=0x01540000
        LDR     R0,=0xAAAAAAAA
        LDR     R0,=0x00550055
        LDR     R0,=0xAA00AA00
        LDR     R0,=0xFFF66FFF
        LDR     R0,=0x0000FFFF
        LDR     R0,=0x12341234
        LDR     R0,=0x12345678

        B       .
        ENDP
            
        END

MOV 命令または MOVN 命令に変換できる条件は、MOV および MVN を使用したイミディエート値のロードに書いてあります。

0x0 ~ 0xFF(0 ~ 255)の範囲内にある任意の 8 ビットイミディエート値。
符号なし8ビットの値は、そのままインストラクションに組み込まれます。
     7:         LDR     R0,=0x000000FF 
0x000000C0 F04F00FF  MOV      r0,#0xFF
任意のビット数だけ左シフトした任意の 8 ビットイミディエート値。
8ビットの値をシフトした値も、インストラクションに組み込まれます。 たとえば、 0x55 を18ビットシフトした値 0x01540000 がロードできます。
     8:         LDR     R0,=0x01540000 
0x000000C4 F04F70AA  MOV      r0,#0x1540000
レジスタのすべての 4 バイトに対して複製した任意の 8 ビットパターン。
4バイト(32ビット)の各バイトに8ビットのパターンを入れた値も、インストラクションに組み込まれます。 たとえば、すべてのバイトに 0xAA が入った 0xAAAAAAAA がロードできます。
     9:         LDR     R0,=0xAAAAAAAA 
0x000000C8 F04F30AA  MOV      r0,#0xAAAAAAAA
バイト 1 と 3 がゼロに設定されているときに、バイト 0 と 2 に対して複製した任意の 8 ビットパターン。
バイト 1 と 3 が 0x00 で、バイト 0 と 2 に同じ値が入った値も、インストラクションに組み込まれます。 たとえば、バイト 3:2 と 1:0 に16ビットの 0x0055 が入った 0x00550055 がロードできます。
    10:         LDR     R0,=0x00550055 
0x000000CC F04F1055  MOV      r0,#0x550055
バイト 0 と 2 がゼロに設定されているときに、バイト 1 と 3 に対して複製した任意の 8 ビットパターン。
同様に、バイト 0 と 2 が 0x00 で、バイト 1 と 3 に同じ値が入った値も、インストラクションに組み込まれます。 たとえば、バイト 3:2 と 1:0 に16ビットの 0xAA00 が入った 0xAA00AA00 がロードできます。
    11:         LDR     R0,=0xAA00AA00 
0x000000D0 F04F20AA  MOV      r0,#0xAA00AA00
32 ビットの MVN 命令では、これらの値のビット単位の補数をロードできます。その数値は -(n+1) です。 ここで、 n は MOV で使用できる値です。
上記の1の補数(符号付き数値では負の値)を扱うことが出来ます。 たとえば、 0x00099000 の補数である 0xFFF66FFF をロードできます。
    12:         LDR     R0,=0xFFF66FFF 
0x000000D4 F46F2019  MVN      r0,#0x99000
32 ビットの MOV 命令では、0x0 ~ 0xFFFF(0 ~ 65535)の範囲内にある任意の 16 ビットの数値をロードできます。
16ビットの値であれば、そのままインストラクションに組み込まれます。 この時のニーモニックは MOVW と表現されます。
    13:         LDR     R0,=0x0000FFFF 
0x000000D8 F64F70FF  MOVW     r0,#0xFFFF
その他の値
その他の値は、32ビットのインストラクションに組み込まれることなく、 PC 相対アドレッシングでリテラルプールと呼ばれるメモリ領域に配置された32ビットの値を読み込む命令に変換されます。
    14:         LDR     R0,=0x12341234 
0x000000DC 4801      LDR      r0,[pc,#4]  ; @0x000000E4
    15:         LDR     R0,=0x12345678 
0x000000DE 4802      LDR      r0,[pc,#8]  ; @0x000000E8
:
:
0x000000E4 1234      DCW      0x1234
0x000000E6 1234      DCW      0x1234
0x000000E8 5678      DCW      0x5678
0x000000EA 1234      DCW      0x1234

Cortex-M3 の場合

次は、ターゲットを Coretex-M3 に変更して実験しました。

     7:         LDR     R0,=0x000000FF 
0x000000C0 F04F00FF  MOV      r0,#0xFF
     8:         LDR     R0,=0x01540000 
0x000000C4 F04F70AA  MOV      r0,#0x1540000
     9:         LDR     R0,=0xAAAAAAAA 
0x000000C8 F04F30AA  MOV      r0,#0xAAAAAAAA
    10:         LDR     R0,=0x00550055 
0x000000CC F04F1055  MOV      r0,#0x550055
    11:         LDR     R0,=0xAA00AA00 
0x000000D0 F04F20AA  MOV      r0,#0xAA00AA00
    12:         LDR     R0,=0xFFF66FFF 
0x000000D4 F46F2019  MVN      r0,#0x99000
    13:         LDR     R0,=0x0000FFFF 
0x000000D8 F64F70FF  MOVW     r0,#0xFFFF
    14:         LDR     R0,=0x12341234 
0x000000DC 4801      LDR      r0,[pc,#4]  ; @0x000000E4
    15:         LDR     R0,=0x12345678 
0x000000DE 4802      LDR      r0,[pc,#8]  ; @0x000000E8
:
:
0x000000E4 1234      DCW      0x1234
0x000000E6 1234      DCW      0x1234
0x000000E8 5678      DCW      0x5678
0x000000EA 1234      DCW      0x1234

その結果、 Cortex-M4 と同じ結果となりました。 この範囲では、 Cortex-M4Cortex-M3 には、違いが無いようです。

Cortex-M0 の場合

次は、 Cortex-M0 で試してみました。結果は、以下の通りです。

     7:         LDR     R0,=0x000000FF 
0x000000C0 4804      LDR      r0,[pc,#16]  ; @0x000000D4
     8:         LDR     R0,=0x01540000 
0x000000C2 4805      LDR      r0,[pc,#20]  ; @0x000000D8
     9:         LDR     R0,=0xAAAAAAAA 
0x000000C4 4805      LDR      r0,[pc,#20]  ; @0x000000DC
    10:         LDR     R0,=0x00550055 
0x000000C6 4806      LDR      r0,[pc,#24]  ; @0x000000E0
    11:         LDR     R0,=0xAA00AA00 
0x000000C8 4806      LDR      r0,[pc,#24]  ; @0x000000E4
    12:         LDR     R0,=0xFFF66FFF 
0x000000CA 4807      LDR      r0,[pc,#28]  ; @0x000000E8
    13:         LDR     R0,=0x0000FFFF 
0x000000CC 4807      LDR      r0,[pc,#28]  ; @0x000000EC
    14:         LDR     R0,=0x12341234 
0x000000CE 4808      LDR      r0,[pc,#32]  ; @0x000000F0
    15:         LDR     R0,=0x12345678 
    16:  
0x000000D0 4808      LDR      r0,[pc,#32]  ; @0x000000F4
    17:         B       . 
0x000000D2 E7FE      B        0x000000D2
0x000000D4 00FF      DCW      0x00FF
0x000000D6 0000      DCW      0x0000
0x000000D8 0000      DCW      0x0000
0x000000DA 0154      DCW      0x0154
0x000000DC AAAA      DCW      0xAAAA
0x000000DE AAAA      DCW      0xAAAA
0x000000E0 0055      DCW      0x0055
0x000000E2 0055      DCW      0x0055
0x000000E4 AA00      DCW      0xAA00
0x000000E6 AA00      DCW      0xAA00
0x000000E8 6FFF      DCW      0x6FFF
0x000000EA FFF6      DCW      0xFFF6
0x000000EC FFFF      DCW      0xFFFF
0x000000EE 0000      DCW      0x0000
0x000000F0 1234      DCW      0x1234
0x000000F2 1234      DCW      0x1234
0x000000F4 5678      DCW      0x5678
0x000000F6 1234      DCW      0x1234

全ての定数ロードが PC 相対アドレッシングに変換されました。 Cortex-M0 では、32ビット MOV 命令が使用できないので、いずれもメモリアクセスに変換されてしまいました。

Thumb Mode を使ったら

Thumb Mode を使うオプション

しかしながら、 Cortex-M0 には、 Thumb Mode と呼ばれている命令体系にインストラクションに値を組み込んだものが存在します。 アセンブラで Thumb Mode を使うには、メニューアイテムの Project → Options... で、 Asm タブの "Thumb Mode" をチェックします。

     7:         LDR     R0,=0x000000FF 
0x000000C0 20FF      MOVS     r0,#0xFF
     8:         LDR     R0,=0x01540000 
0x000000C2 4804      LDR      r0,[pc,#16]  ; @0x000000D4
     9:         LDR     R0,=0xAAAAAAAA 
0x000000C4 4804      LDR      r0,[pc,#16]  ; @0x000000D8
    10:         LDR     R0,=0x00550055 
0x000000C6 4805      LDR      r0,[pc,#20]  ; @0x000000DC
    11:         LDR     R0,=0xAA00AA00 
0x000000C8 4805      LDR      r0,[pc,#20]  ; @0x000000E0
    12:         LDR     R0,=0xFFF66FFF 
0x000000CA 4806      LDR      r0,[pc,#24]  ; @0x000000E4
    13:         LDR     R0,=0x0000FFFF 
0x000000CC 4806      LDR      r0,[pc,#24]  ; @0x000000E8
    14:         LDR     R0,=0x12341234 
0x000000CE 4807      LDR      r0,[pc,#28]  ; @0x000000EC
    15:         LDR     R0,=0x12345678 
    16:  
0x000000D0 4807      LDR      r0,[pc,#28]  ; @0x000000F0
    17:         B       . 
0x000000D2 E7FE      B        0x000000D2
0x000000D4 0000      DCW      0x0000
0x000000D6 0154      DCW      0x0154
0x000000D8 AAAA      DCW      0xAAAA
0x000000DA AAAA      DCW      0xAAAA
0x000000DC 0055      DCW      0x0055
0x000000DE 0055      DCW      0x0055
0x000000E0 AA00      DCW      0xAA00
0x000000E2 AA00      DCW      0xAA00
0x000000E4 6FFF      DCW      0x6FFF
0x000000E6 FFF6      DCW      0xFFF6
0x000000E8 FFFF      DCW      0xFFFF
0x000000EA 0000      DCW      0x0000
0x000000EC 1234      DCW      0x1234
0x000000EE 1234      DCW      0x1234
0x000000F0 5678      DCW      0x5678
0x000000F2 1234      DCW      0x1234

すると、8ビットのデータを扱った場合に限り、インストラクションに値を組み込んだ MOVS という命令を使いました。 これで、高いメモリアクセスコストを支払わなくて済みます。

参考文書

LDR Rd, =const を使用したイミディエート値のロード
LDR 疑似命令について書かれたページです。
MOV および MVN を使用したイミディエート値のロード
MOV でインストラクションに組み込む事の出来る値について書かれたページです。

nice!(0)  コメント(0)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。