ARM アセンブラで 32ビット定数をロードする [プログラム三昧]
ARM Cortex-M プロセッサでは、もちろんアセンブラでプログラムを書くことが出来ます。 基本的な動作として、32ビットの定数をレジスタに格納する操作について調べてみました。
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-M4 と Cortex-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 を使ったら
しかしながら、 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 でインストラクションに組み込む事の出来る値について書かれたページです。
コメント 0