HOME
例外発生時の動作
パワーオンリセット
<2008/11/27>
- パワーオンリセット時にCPUは以下の動作をする。
a) プログラムカウンタ(PC) にA000_0000を設定。
b) 例外事象レジスタ(EXPEVT)に例外コードを設定。
c) ステータスレジスタを以下のように設定。
モードビット(MD)=1(特権モード)
レジスタバンクビット(RB)=1(BANK1を使用)
例外/割り込みブロックビット(BL)=1(全割り込みマスク)
FPUディスエーブルビット(FD)=0
割り込みマスクビット(I3〜I0)=1111b(マスクレベル15=禁止)
d> A000_0000にjamp.。
実際は0000_0000番地がリセットスタートのアドレスになる。外部メモリ参照
- 0000_0000番地にはリセットルーチンを置いておく。
ここのラベル名は_ResetHandler:、RSTHandlerセクション(0000_0000)内である。
a) EXPEVT(例外事象レジスタ)から例外コードを取得(リセットの場合は0000) → r0
b.) _RESET_Vectors(VECTTBLセクションの先頭)のアドレスを取得 → r1
c) @(r0×4+r1)で_PowerON_Resetのアドレスを取得してそこにjamp
- PowerON_Reset()
ここはCで書かれてる resetprg.c
関数名はPowerON_Reset()、PResetPRGセクション(0000_1000)内である。
この関数は#pragma entry指定されているので、最初にスタックがR15にセットされる。
次にVBRを設定。設定値はINTHandlerPRG - 100h = 700h。
INTHandlerPRGは一般例外が発生したときに実行されるルーチンである。
ここ、ちょっと分かりにくいんで詳しく書く。導入ガイド P18
「リセット以外の例外が発生した場合、PCは(VBR +例外要因毎のオフセット値)に設定され・・・」
例外要因毎のオフセット値・・・一般例外の時のオフセット値は100hなのだ。図7参照
つまり一般例外が発生するとVBR+100hのアドレスに飛んでくるわけ。
INTHandlerPRGルーチンを800hに置いてる場合、VBRには700hをセットしろって事なのだ。
つまり「INTHandlerPRG..800h - 100h = 700h」ってことな。
後で割り込みの所でもう一度詳しく書く予定。
VBRの設定が終わったら、次にB,Dセクションの初期化をする。
_INITSCT()はライブラリ内だけど、dbsct.cを覗け。導入ガイド P25
Bセクションはゼロクリアし、DセクションをRセクションにコピーだわな。
続いて念のためにSRを特権モード,割り込み全マスクなどをして、main()へjamp。
- main()
ここはCのmain関数だわね。 Pセクション(0000_2000)に配置されてる。
周辺I/Oなどを初期化するわけだ。
以上がリセットからmain()までの流れなり。
図6 セクション名とアドレス、ラベル(関数名)の一覧表 |
 |
その他の例外
<2008/12/03記>
例外に関しては導入ガイドに詳しく載ってるよ。読めば全部分かるです。割と分かりやすいマニュアルと思うです。
HEWで生成されるファイルと中身の関係は導入ガイド P10。
例外(リセット以外)に関しては導入ガイド P18から。
まんず基本的な例外発生時の流れなのだ。
- SRとPCを退避用レジスタにコピー。
- SRを以下のように設定。
モードビット(MD)=1(特権モード)
レジスタバンクビット(RB)=1(BANK1を使用)
例外/割り込みブロックビット(BL)=1(全割り込みマスク)
- PC に「VBR +例外要因毎のオフセット値」を設定する。
図7 例外発生時のオフセット値と実行されるルーチン |
 |
例外処理ハンドラの詳細
ここでは例外発生で最初に実行される例外処理の入口と、ユーザールーチンから戻ってきた出口部分のアセンブラコードを合わせて「例外処理ハンドラ」と記述している。
vhandler.src 内の _INTHandlerPRG: が例外処理ハンドラ入口なのだ。導入ガイド P19
例外処理ハンドラの出口は __int_term:。導入ガイド P20。
ソースと説明が離れてて読みにくいし、セクション指定とスタートアップとの関係も掴みにくいんで、ちょい書き直し。
図8 例外処理ハンドラの処理の流れ |
 |
- 0000_0800番地の_INTHandlerPRG が例外処理ハンドラの入口.(リンカのセクション指定で、0000_0800にプログラムを置いてる。図6。)
800Hの根拠は、スタートアップ時にVBRに700Hをセットし、一般例外発生時のオフセット100Hを足した値が800H。そんだけ〜
- 例外ハンドラが実行される時点で、SRのRB=1(バンク1)、BL=1(割り込みマスク)となっている。
- まずはレジスタ退避。
待避してるのは良く使うレジスタだけなのだ。MACHやMACLは待避してないんで、使ってるならマクロを書き直すのだ。
SH4系でFPUを使ってるなら、そいつらも待避しないとならんべな。
- EXPEVT(例外事象レジスタ)から例外コードを取得して、ベクターテーブルからユーザーの書いた例外処理ルーチンのアドレスを取得。
ユーザー例外処理ルーチン実行中の割り込み許可レベルをセットするために、割り込みマスク値も取得しておく(次の処理でSSRのI3にセット)。
- ユーザー例外処理ルーチンにジャンプするために、SSRやらSPCをセット。
PR(戻り番地格納用レジスタHARD取説 P68)には例外処理ハンドラの出口 __int_term: のアドレスをセット。
ここでBLビットをクリアしてるんで、多重割り込みは許可された状態になってるよ。
で、ユーザー例外処理ルーチンにジャンプ。
- んで、ユーザー例外処理ルーチンを実行し、最後にRTSで戻る。
そうすっと例外処理ハンドラの出口 __int_term: に飛んでくるな。
- __int_term:では、待避しておいたレジスタを復帰して、通常プログラムに戻って終わり。
てなわけで、やれやれ例外ハンドラは無事終了じゃ。
ユーザー例外処理ルーチンの書き方
やり方は二通り。
例外別に飛び先の関数を分ける方式。導入ガイド P24
1,intprg.src:使いたい例外のラベル名をコメントアウト。
2,ラベル名先頭のアンダースコアを取った関数をCのソースに記述。
全部の例外を一つの関数で処理し、その関数内でINTEVT2で要因を分ける方式。
1,vect.src:使いたい例外のラベル名をアンダースコア+Cの関数名に変更。
2,vect.inc:同じ名前をglobal宣言。
二つ目の方式で、例えばTMU0割り込みを AllInterruptEvent() と言う関数に飛ばす場合。
|
vecttbl.src の変更箇所 |
|
:
:
;H'400 TMU TUNI0
;.data.l _INT_TMU_TUNI0 ; ← 元のラベルをコメントアウトする
.data.l _AllInterruptEvent ; ← アンダースコア+関数名を宣言
:
:
;H'400 TMU TUNI0
.data.b H'F0
; ← 割り込みマスク値を適当に決める
:
: |
で、vect.inc のTMU0部分にも同じ名前をglobal宣言する。
Cのソースに void AllInterruptEvent(void) とすれば良いんじゃ。
HOME