HOME

例外発生時の動作

パワーオンリセット

<2008/11/27>

  1. パワーオンリセット時に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番地がリセットスタートのアドレスになる。外部メモリ参照
     
  2. 0000_0000番地にはリセットルーチンを置いておく。
    ここのラベル名は_ResetHandler:、RSTHandlerセクション(0000_0000)内である。
     a) EXPEVT(例外事象レジスタ)から例外コードを取得(リセットの場合は0000) → r0
     b.) _RESET_Vectors(VECTTBLセクションの先頭)のアドレスを取得 → r1
     c) @(r0×4+r1)で_PowerON_Resetのアドレスを取得してそこにjamp
     
  3. 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。
     
  4. main()
    ここはCのmain関数だわね。 Pセクション(0000_2000)に配置されてる。
    周辺I/Oなどを初期化するわけだ。
    以上がリセットからmain()までの流れなり。
     
図6 セクション名とアドレス、ラベル(関数名)の一覧表





その他の例外

<2008/12/03記>
 例外に関しては導入ガイドに詳しく載ってるよ。読めば全部分かるです。割と分かりやすいマニュアルと思うです。
 HEWで生成されるファイルと中身の関係は導入ガイド P10
 例外(リセット以外)に関しては導入ガイド P18から。

 まんず基本的な例外発生時の流れなのだ。

  1. SRとPCを退避用レジスタにコピー。
     
  2. SRを以下のように設定。
    モードビット(MD)=1(特権モード)
    レジスタバンクビット(RB)=1(BANK1を使用)
    例外/割り込みブロックビット(BL)=1(全割り込みマスク)
     
  3. PC に「VBR +例外要因毎のオフセット値」を設定する。
     
図7 例外発生時のオフセット値と実行されるルーチン





例外処理ハンドラの詳細

 ここでは例外発生で最初に実行される例外処理の入口と、ユーザールーチンから戻ってきた出口部分のアセンブラコードを合わせて「例外処理ハンドラ」と記述している。
 vhandler.src 内の _INTHandlerPRG: が例外処理ハンドラ入口なのだ。導入ガイド P19
 例外処理ハンドラの出口は __int_term:。導入ガイド P20
 ソースと説明が離れてて読みにくいし、セクション指定とスタートアップとの関係も掴みにくいんで、ちょい書き直し。

図8 例外処理ハンドラの処理の流れ
  1. 0000_0800番地の_INTHandlerPRG が例外処理ハンドラの入口.(リンカのセクション指定で、0000_0800にプログラムを置いてる。図6。)
    800Hの根拠は、スタートアップ時にVBRに700Hをセットし、一般例外発生時のオフセット100Hを足した値が800H。そんだけ〜
     
  2. 例外ハンドラが実行される時点で、SRのRB=1(バンク1)、BL=1(割り込みマスク)となっている。
     
  3. まずはレジスタ退避。
    待避してるのは良く使うレジスタだけなのだ。MACHやMACLは待避してないんで、使ってるならマクロを書き直すのだ。
    SH4系でFPUを使ってるなら、そいつらも待避しないとならんべな。
      
  4. EXPEVT(例外事象レジスタ)から例外コードを取得して、ベクターテーブルからユーザーの書いた例外処理ルーチンのアドレスを取得。
    ユーザー例外処理ルーチン実行中の割り込み許可レベルをセットするために、割り込みマスク値も取得しておく(次の処理でSSRのI3にセット)。
     
  5. ユーザー例外処理ルーチンにジャンプするために、SSRやらSPCをセット。
    PR(戻り番地格納用レジスタ
    HARD取説 P68)には例外処理ハンドラの出口 __int_term: のアドレスをセット。
    ここでBLビットをクリアしてるんで、多重割り込みは許可された状態になってるよ。
    で、ユーザー例外処理ルーチンにジャンプ。
     
  6. んで、ユーザー例外処理ルーチンを実行し、最後にRTSで戻る。
    そうすっと例外処理ハンドラの出口 __int_term: に飛んでくるな。
     
  7. __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