初めに
ARMのMOV
命令で使用できる即値には制限があります。
8ビット値0x00
〜0xFF
を偶数ビット分左にずらして表せる数しか即値として使用ができません。
今回は実際にアセンブリでMOVを使用するコードを書き、動作を検証します。
※即値制限を説明しているサイトは沢山あるのですが、例が無く理解が難しかったので実際に自分で見てみることにしました。
コンパイル及び逆アセンブルのコマンド
### ARM上のLinuxで実行する場合
as -o test.o test.s
ld -o test test.o
objdump -d test > test.asm
### X86上のLinuxで実行する場合
arm-none-eabi-as -mcpu=arm7tdmi -o test.o test.s
arm-none-eabi-ld -Ttext=0x00000000 -o test.elf test.o
arm-none-eabi-objdump -d test.elf > test.asm
コード
0x00
〜0xFF
は0回
ずらすとして考えるので自ずと全て使用が出来ます。
.global _start
_start:
MOV r0, #0xFF
MOV r2, #0x7F
MOV r3, #0
MOV r0, #0xFF000000
MOV r1, #0xA5000000
B .
test.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e3a000ff mov r0, #255 ; 0xff
4: e3a0207f mov r2, #127 ; 0x7f
8: e3a03000 mov r3, #0
c: e3a004ff mov r0, #-16777216 ; 0xff000000
10: e3a014a5 mov r1, #-1526726656 ; 0xa5000000
14: eafffffe b 14 <_start+0x14>
考察
MOV r0, #0xFF000000
この命令の機械語はe3a004ff
となります。
これを2進数にすると11100011101000000000010011111111
e3a004ff
を表す部分は下位12ビット010011111111
です。
このうち下位8ビット11111111
が8ビット値0xFF
を表しており、上位4ビット0100
はずらす回数を表しています。
0100
は3なので3×2=6、つまり0xFF
を6ビット分左にずらすと0xFF000000
が表現できます。
MOV r1, #0xA5000000
同じく機械語はe3a014a5
これを2進数にすると11100011101000000001010010100101
下位12ビット=010010100101
12ビットのうち、
上位4ビット=0100
=6回ずらす
下位8ビット=10100101
=0xA5
0xA5
を左に6ビットずらすと0xA5000000
となります。
MOVが使用できない例
8ビット値をずらすことで表現できない値は、コンパイルするとエラーが出ます。
.global _start
_start:
MOV r1, #0x12345678
B .
test@test-ThinkPad-X280:~/kaihatsu/armtest$ arm-none-eabi-as -mcpu=arm7tdmi -o test.o test.s
test.s: Assembler messages:
test.s:3: Error: invalid constant (12345678) after fixup
この場合はLDR
命令を使うことで解決ができます
LDR命令
MOV命令で指定できない即値
e51f1000
内で32ビットの値を持たず、メモリ上に置いた32ビットの値を指定するだけなので、MOVの様に制限されず任意の値を即値として指定することができます。
.global _start
_start:
LDR r1, =0x12345678
B .
test.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e51f1000 ldr r1, [pc, #-0] ; 8 <_start+0x8>
4: eafffffe b 4 <_start+0x4>
8: 12345678 .word 0x12345678
MOV命令で指定できる即値
LDR
命令を使用したとしてもMOV
で使用可能な数値の場合はアセンブラが自動的にMOV
命令を生成します。
test@test-ThinkPad-X280:~/kaihatsu/armtest$ cat test.s
.global _start
_start:
LDR r1, =0xFF
B .
test.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e3a010ff mov r1, #255 ; 0xff
4: eafffffe b 4 <_start+0x4>
[追記]
上位16ビットと下位16ビットに分けて格納する方法もあります。
ARM 32ビットでMOVW/MOVTによる即値代入とLDR命令の挙動を検証