#author("2023-05-24T15:56:58+09:00","","") * スタック領域周辺 [#iaa66e44] gccはリンカスクリプトを通してメモリの配置を決定しています。リンカスクリプトのファイルは「C:\devkitPro\devkitARM\arm-none-eabi\lib」のgba_cart.ld(カードリッジ用)と、gba_mb.ld(マルチブート用)です。[[gba_cart.ld:https://github.com/devkitPro/devkitarm-crtls/blob/master/gba_cart.ld]]はカードリッジを作る上で必要な情報です。あまり目立たないとはいえ一度は目を通してください。スタック領域周辺について、gba_cart.ldと、make実行すると作成される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側を侵食しないように注意してください。バグると多大な時間を使うハメになります。 |リンカ| アドレス | サイズ | 用途 | | | 3007FFCh | 4 | Pointer to user IRQ handler (32bit ARM code) | | __irq_flags | 3007FF8h | 2 | 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に0xbeefbeefを代入していて、アドレスは0x3007ED8と一番古いことがわかります。疑問に思うことは、このアドレスはどこからやってくるのかということです。NO$GBAを使って検証してみます。まずコンパイルされたmapファイルからmain関数の開始アドレスを確認します。 - test.map .text.startup 0x080001f8 0x90 obj/main.o 0x080001f8 main 0x080001f8ということがわかったので、ここをブレークポイントにします。 #ref(2.png,nolink) main関数に入ったタイミングでのスタックポインタ(r17)の値は0x03007F00です。このままステップ実行をして0x0800206まで動かした時のスタックはこのようになります。 #ref(3.png,nolink) 代入もちゃんとされていて挙動を確認することができました。ちなみにr17レジスタへの代入は[[crt0.s:https://github.com/devkitPro/devkitarm-crtls/blob/master/gba_crt0.s#L68]]内で行われています。 - crt0.s @--------------------------------------------------------------------------------- start_vector: @--------------------------------------------------------------------------------- mov r0, #0x4000000 @ REG_BASE str r0, [r0, #0x208] mov r0, #0x12 @ Switch to IRQ Mode msr cpsr, r0 ldr sp, =__sp_irq @ Set IRQ stack mov r0, #0x1f @ Switch to System Mode msr cpsr, r0 ldr sp, =__sp_usr @ Set user stack ←ココ! ** 履歴 [#j0abcb62] - 2023/05/22