- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2023-05-22T12:27:11+09:00","","")
#author("2023-05-22T16:44:37+09:00","","")
* スタック領域周辺 [#iaa66e44]
gccはリンカスクリプトを通してメモリの配置を決定しています。ファイルは「C:\devkitPro\devkitARM\arm-none-eabi\lib」のカードリッジ用の「gba_cart.ld」と
、マルチブート用の「gba_mb.ld」です。スタック領域周辺はけっこう入り組んでいるため解説していこうと思います。
、マルチブート用の「gba_mb.ld」です。
** スタック領域周辺 [#u76c6e02]
[[gba_cart.ld:https://github.com/devkitPro/devkitarm-crtls/blob/master/gba_cart.ld]]はカードリッジを作る上で必要な情報です。自動化されているとはいえ一度は目を通してください。スタック領域についてですがgba_cart.ldと、ロムファイルを作ると作成されるmapファイルをぞれぞれ抜粋しました。
[[gba_cart.ld:https://github.com/devkitPro/devkitarm-crtls/blob/master/gba_cart.ld]]はカードリッジを作る上で必要な情報です。あまり目立たないとはいえ一度は目を通してください。スタック領域周辺についてですが、gba_cart.ldと、ロムファイルを作ると作成されるmapファイルをぞれぞれ抜粋しました。
- gba_cart.ld
MEMORY {
rom : ORIGIN = 0x08000000, LENGTH = 32M
iwram : ORIGIN = 0x03000000, LENGTH = 32K
ewram : ORIGIN = 0x02000000, LENGTH = 256K
}
__text_start = ORIGIN(rom);
__eheap_end = ORIGIN(ewram) + LENGTH(ewram);
__iwram_start = ORIGIN(iwram);
__iwram_top = ORIGIN(iwram) + LENGTH(iwram);;
__sp_irq = __iwram_top - 0x060;
__sp_usr = __sp_irq - 0x0a0;
__irq_flags = 0x03007ff8;
- mapファイル
0x08000000 __text_start = ORIGIN (rom)
0x02040000 __eheap_end = (ORIGIN (ewram) + LENGTH (ewram))
0x03000000 __iwram_start = ORIGIN (iwram)
0x03008000 __iwram_top = (ORIGIN (iwram) + LENGTH (iwram))
0x03007fa0 __sp_irq = (__iwram_top - 0x60)
0x03007f00 __sp_usr = (__sp_irq - 0xa0)
0x03007ff8 __irq_flags = 0x3007ff8
0x08000000 . = __text_start
さらにわかりやすい図にする以下のようになります。着目すべきところはirq割り込み時に使用されるスタックサイズが160バイトしかありません。__sp_usr側を侵食してしまわないように注意してください。バグ取りにシャレにならないほどの多大な時間を使うハメになります。
さらにわかりやすい図にすると以下のようになります。着目すべきところはirq割り込み時に使用されるスタックサイズが160バイトしかありません。__sp_usr側を侵食してしまわないように注意してください。バグ取りにシャレにならないほどの多大な時間を使うハメになります。
|リンカ| アドレス | サイズ | 用途 |
| | 3007FFCh | 4 | Pointer to user IRQ handler (32bit ARM code) |
| __irq_flags | 3007FF8h | 4 | Interrupt Check Flag (for IntrWait/VBlankIntrWait functions) |
| | 3007FF4h | 4 | Allocated Area |
| | 3007FF0h | 4 | Pointer to Sound Buffer |
| | 3007FE0h | 16 | Allocated Area |
| __sp_irq | 3007FA0h | 160 | Default area for SP_irq Interrupt Stack |
| __sp_usr | 3007F00h | - | Default area for SP_usr Stack |
** カンタンなスタックの挙動 [#s8b3cd5b]
main関数内のローカル変数のアドレスを見てみるプログラムです。
- main.c
IWRAM_CODE int main(void)
{
REG_WSCNT = 0x4317;
u32 i = 0xbeefbeef;
u32 j = 0;
u32 k = 0;
char a[10];
BgInit();
IrqInit();
BgDrawPrintf(0, 0, "&i = %X", (u32)&i);
BgDrawPrintf(0, 1, "&j = %X", (u32)&j);
BgDrawPrintf(0, 2, "&k = %X", (u32)&k);
for(i=0; i<10; i++)
{
BgDrawPrintf(0, 4+i, "&a[%d] = %X", i, (u32)&a[i]);
}
for(;;)
{
VBlankIntrWait();
}
}
#ref(1.png,nolink)
変数iが一番古いアドレスになります。この値はどこからやってくるのかNO$GBAを使って検証してみましょう。まずコンパイルされたmapファイルからmain関数の開始アドレスを確認します。
変数iが一番古いアドレスになります。この値はどこからやってくるのかNO$GBAを使って検証してみます。まずコンパイルされたmapファイルからmain関数の開始アドレスを確認します。
- test.map
.text.startup 0x080001f8 0x90 obj/main.o
0x080001f8 main
0x080001f8ということがわかったので、ここをブレークポイントをします。
#ref(2.png,nolink)
main関数に入ったタイミングでのスタックポインタ(r17)の値は0x03007F00です。ちなみにr17レジスタへの代入はcrt0.s内で行われています。さて、このままF4キーを押してステップ実行をして0x0800206まで動かした時のスタックはこのようになります。
main関数に入ったタイミングでのスタックポインタ(r17)の値は0x03007F00です。ちなみにr17レジスタへの代入はcrt0.s内で行われています。さて、このままステップ実行をして0x0800206まで動かした時のスタックはこのようになります。
#ref(3.png,nolink)
代入もちゃんとされていて挙動を確認することができました。
** 履歴 [#j0abcb62]
- 2023/05/22