#author("2023-07-01T11:16:09+09:00;2023-05-30T00:52:32+09:00","","")
#author("2023-08-08T16:47:43+09:00;2023-05-30T00:52:32+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

少しややこしいので、さらにわかりやすい表を以下に載せます。

|リンカ| アドレス | サイズ | 用途 |
|             | 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 |

着目すべきところはirq割り込み時に使用されるスタックサイズ160バイトです。結構少ないかもしれませんので__sp_usr側を侵食しないように注意してください。スタックのバグは修正に多大な時間を使います。

** カンタンなスタックの挙動 [#s8b3cd5b]

ローカル変数のアドレスを覗いてみます。

- 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まで動かした時のスタックはこのようになります。
main関数に入ったタイミングでのスタックポインタ(r13)の値は0x03007F00です。上記の表と同じですね。このままステップ実行をして0x0800206まで動かした時のスタックはこのようになります。

#ref(3.png,nolink)

代入もちゃんとされていて挙動を確認することができました。ちなみに最初のスタックポインタ(r17レジスタ)への代入は[[crt0.s:https://github.com/devkitPro/devkitarm-crtls/blob/master/gba_crt0.s#L68]]内で行われています。
代入もちゃんとされていて挙動を確認することができました。ちなみに最初のスタックポインタ(r13)への代入は[[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

トップ   差分 履歴 リロード   一覧 検索 最終更新   ヘルプ   最終更新のRSS