目的
筆者は最近任天堂GBAのプログラムを研究しています。
昨日投稿した記事
アセンブリでGBAプログラミング:ボタン判定
の中でGBA向けに以下のようなコードを書きました。
@ 押されたら塗り潰し
bl FillScreenRed
@ ...省略...
FillScreenRed:
push {lr}
@ VRAM = 0x06000000
ldr r0, =0x06000000
mov r1, #0x1F @ 赤 (RGB15)
ldr r2, =38400 @ 240*160
FillLoop:
strh r1, [r0], #2
subs r2, r2, #1
bne FillLoop
pop {lr}
bx lr
BL命令を実行すると、戻り先のアドレスが自動的にLR レジスタに格納されます。そのため、bl ラベルで関数に跳び、bx lrで呼び出し元の次の命令に戻ることができます。
この書き方はARMでの定石ですが、本当にLR レジスタに戻り先のアドレスが入っているのかを確認してみます。
GBAではデバッグが難しいため、今回はLinuxが動作するARM環境で実験します。
追記: 64ビットでも同様の検証を行いました。
64ビットARMアセンブリでBL RETの動作を確認
検証用コード
test.s
.global _start
_start:
mov r0, #42 @ 引数に42を入れる
bl myfunc @ 関数呼び出し
b . @ 無限ループ
myfunc:
add r0, r0, #1 @ 引数を+1
bx lr @ 呼び出し元に戻る
コンパイル後、逆アセンブルすると命令毎の実際のアドレスが確認できます。
as -o test.o test.s
ld -o test test.o
objdump -d test > test.asm
test.asm
test: file format elf32-littlearm
Disassembly of section .text:
00010054 <_start>:
10054: e3a0002a mov r0, #42 @ 0x2a
10058: eb000000 bl 10060 <myfunc>
1005c: eafffffe b 1005c <_start+0x8>
00010060 <myfunc>:
10060: e2800001 add r0, r0, #1
10064: e12fff1e bx lr
GDBを使い、myfuncに入ったところにブレイクポイントを置き、LRレジスタの中に本当に返りアドレスが格納されているかを確認します。
gdb ./test
break *0x10060
run
display/x $lr
quit
実際の操作記録
$lr = 0x1005cと表示されており、実際にLR レジスタにb .のアドレスが格納されていることが確認できます。
testuser@CasaOS:~/atest$ cat test.s
.global _start
_start:
mov r0, #42 @ 引数に42を入れる
bl myfunc @ 関数呼び出し
b . @ 無限ループ
myfunc:
add r0, r0, #1 @ 引数を+1
bx lr @ 呼び出し元に戻る
testuser@CasaOS:~/atest$ as -o test.o test.s
testuser@CasaOS:~/atest$ ld -o test test.o
testuser@CasaOS:~/atest$ objdump -d test > test.asm
testuser@CasaOS:~/atest$ cat test.asm
test: file format elf32-littlearm
Disassembly of section .text:
00010054 <_start>:
10054: e3a0002a mov r0, #42 @ 0x2a
10058: eb000000 bl 10060 <myfunc>
1005c: eafffffe b 1005c <_start+0x8>
00010060 <myfunc>:
10060: e2800001 add r0, r0, #1
10064: e12fff1e bx lr
testuser@CasaOS:~/atest$ gdb ./test
GNU gdb (Debian 13.1-3) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
(No debugging symbols found in ./test)
(gdb) break *0x10060
Breakpoint 1 at 0x10060
(gdb) run
Starting program: /home/testuser/atest/test
Breakpoint 1, 0x00010060 in myfunc ()
(gdb) display/x $lr
1: /x $lr = 0x1005c
(gdb) quit
A debugging session is active.
Inferior 1 [process 3260] will be killed.
Quit anyway? (y or n) y
testuser@CasaOS:~/atest$
動作環境(Linux / ARM 版)
- OS: Armbian 23.08.0-trunk (Debian 12 Bookworm)
- Kernel: Linux 6.1.38-meson (armv7l)
- CPU: ARM Cortex-A5, 4 cores, Little Endian
- GCC: 12.2.0
- Architecture: armv7l
- 備考: Windows10からSSH接続して使用
動作環境確認コマンドは以下の通り
uname -a
cat /etc/os-release
gcc --version
lscpu

