スタック領域周辺

gccはリンカスクリプトを通してメモリの配置を決定しています。リンカスクリプトのファイルは「C:\devkitPro\devkitARM\arm-none-eabi\lib」のgba_cart.ld(カードリッジ用)と、gba_mb.ld(マルチブート用)です。gba_cart.ldはロムを作る上で必要な情報です。あまり目立たないとはいえ一度は目を通してください。スタック領域周辺については、gba_cart.ldと、(make実行すると作成される)mapファイルをぞれぞれ抜粋しました。

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

リンカアドレスサイズ用途
3007FFCh4Pointer to user IRQ handler (32bit ARM code)
__irq_flags3007FF8h2Interrupt Check Flag (for IntrWait/VBlankIntrWait functions)
3007FF4h4Allocated Area
3007FF0h4Pointer to Sound Buffer
3007FE0h16Allocated Area
__sp_irq3007FA0h160Default area for SP_irq Interrupt Stack
__sp_usr3007F00h-Default area for SP_usr Stack

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

カンタンなスタックの挙動

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

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();
	}
}
1.png

変数iに0xbeefbeefを代入していて、アドレスは0x3007ED8と一番古いことがわかりました。疑問に思うことは、このアドレスはどこからやってくるのかということです。NO$GBAを使って検証してみます。まずコンパイルされたmapファイルから、main関数の開始アドレスを確認します。

0x080001f8ということがわかったので、ここをブレークポイントします。

2.png

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

3.png

代入もちゃんとされていて挙動を確認することができました。ちなみに最初のスタックポインタ(r13)への代入はcrt0.s内で行われています。

履歴


添付ファイル: file3.png 21件 [詳細] file2.png 21件 [詳細] file1.png 22件 [詳細]

トップ   差分 履歴 リロード   一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2023-05-30 (火) 00:52:32