ColdFire でアセンブラ (5) - タスク・コンテキストを作らせる [ColdFire (ColdeFire) V1]
ColdFire で割り込み(例外)が発生すると、スーパバイザモードに移行します。 割り込み処理ルーチンでタスク・コンテキストを切り替える処理を考えます。
ステートメント・レベルで記述する
最初の例は、コンテキストの退避と復帰だけをアセンブラで記述したものです。
dword count; void trap_content(void) { count++; } dword *this_SSP; dword *system_SSP; __interrupt VectorNumber_Vtrap0 trap0_isr (void) { __asm { lea -64(a7),a7 // スタックフレームの確保 movem d0-d7/a0-a6,(a7) // レジスタを退避する move usp,a0 // USPを取り出す move a0,60(a7) // USPを退避させる move a7,this_SSP // SSPを退避させる move system_SSP,a7 // システムSSPを設定する } trap_content(); // 処理の実体 __asm { move this_SSP,a7 // SSPを復帰させる move 60(a7),a0 // USPを取り出す move a0,usp // USPを復帰させる movem (a7),d0-d7/a0-a6 // レジスタを復帰させる lea 64(a7),a7 // スタックフレームの解放 } }
これをコンパイルすると、関数の最初と最後の部分は、このようになります。
; 28: __interrupt VectorNumber_Vtrap0 trap0_isr (void) { ; 28: { ; 29: __asm { 0x00000000 _trap0_isr: ; trap0_isr: 0x00000000 0x2F08 move.l a0,-(a7) 0x00000002 0x2F01 move.l d1,-(a7) ; ; 30: lea -64(a7),a7 // スタックフレームの確保 0x00000004 0x4FEFFFC0 lea -64(a7),a7 ; ; 31: movem d0-d7/a0-a6,(a7) // レジスタを退避する 0x00000008 0x48D77FFF movem.l d0-d7/a0-a6,(a7) : ; 42: movem (a7),d0-d7/a0-a6 // レジスタを復帰させる 0x00000034 0x4CD77FFF movem.l (a7),d0-d7/a0-a6 ; ; 43: lea 64(a7),a7 // スタックフレームの解放 ; 44: } 0x00000038 0x4FEF0040 lea 64(a7),a7 ; ; 45: } 0x0000003C 0x221F move.l (a7)+,d1 0x0000003E 0x205F movea.l (a7)+,a0 0x00000040 0x4E73 rte 0x00000042 0x51FC trapf
このようにタスク・コンテキストを構築する前に 作業領域として使用しているD1とA0を退避させるコードが作成されてしまい、 タスク・コンテキストとして使えなくなってしまいます。
キーワードnakedを使う
勝手に作業領域を確保させないためには、 nakedというキーワードを付けます。
__interrupt VectorNumber_Vtrap1 trap1_isr (void) { __asm { naked lea -64(a7),a7 // スタックフレームの確保 movem d0-d7/a0-a6,(a7) // レジスタを退避する move usp,a0 // USPを取り出す move a0,60(a7) // USPを退避させる move a7,this_SSP // SSPを退避させる move system_SSP,a7 // システムSSPを設定する } trap_content(); // 処理の実体 __asm { move this_SSP,a7 // SSPを復帰させる move 60(a7),a0 // USPを取り出す move a0,usp // USPを復帰させる movem (a7),d0-d7/a0-a6 // レジスタを復帰させる lea 64(a7),a7 // スタックフレームの解放 rte // 例外処理の終了 } }
このキーワードを付けると、 最後のRTE命令さえも生成してくれないので、 注意が必要です。 コンパイルするとこうなります。
; 47: __interrupt VectorNumber_Vtrap1 trap1_isr (void) { ; 48: __asm { ; 49: naked ; 50: lea -64(a7),a7 // スタックフレームの確保 0x00000000 _trap1_isr: ; trap1_isr: 0x00000000 0x4FEFFFC0 lea -64(a7),a7 ; ; 51: movem d0-d7/a0-a6,(a7) // レジスタを退避する 0x00000004 0x48D77FFF movem.l d0-d7/a0-a6,(a7) : ; 62: movem (a7),d0-d7/a0-a6 // レジスタを復帰させる 0x00000030 0x4CD77FFF movem.l (a7),d0-d7/a0-a6 ; ; 63: lea 64(a7),a7 // スタックフレームの解放 0x00000034 0x4FEF0040 lea 64(a7),a7 ; ; 64: rte // 例外処理の終了 ; 65: } 0x00000038 0x4E73 rte ; ; 66: } 0x0000003A 0x51FC trapf
前処理と後処理は、全く生成されないので、 ユーザの責任で確実にレジスタを退避させる必要があります。
関数レベルで記述する
上の二つの例では、処理の実体をCで書きたいがために、 ステートメントレベルでアセンブラ記述にしていました。 ところが、アセンブラでスタックを切り替えていることから、 局所変数などのスタックを使用する文などは事実上使用できません。 それだったら、全部アセンブラで書いてしまえというのが、次の例です。
__asm __interrupt VectorNumber_Vtrap2 trap2_isr (void) { lea -64(a7),a7 // スタックフレームの確保 movem d0-d7/a0-a6,(a7) // レジスタを退避する move usp,a0 // USPを取り出す move a0,60(a7) // USPを退避させる move a7,this_SSP // SSPを退避させる move system_SSP,a7 // システムSSPを設定する jsr trap_content // 処理の実体 move this_SSP,a7 // SSPを復帰させる move 60(a7),a0 // USPを取り出す move a0,usp // USPを復帰させる movem (a7),d0-d7/a0-a6 // レジスタを復帰させる lea 64(a7),a7 // スタックフレームの解放 rte // 例外処理の終了 }
この例では、引数も返り値もない関数trap_contentを使っていますが、 単純なもの数個でよければ、受け渡しをすることもできます。 コンパイル結果は、このようになります。
; 68: __asm __interrupt VectorNumber_Vtrap2 trap2_isr (void) { ; 69: lea -64(a7),a7 // スタックフレームの確保 0x00000000 _trap2_isr: ; trap2_isr: 0x00000000 0x4FEFFFC0 lea -64(a7),a7 ; ; 70: movem d0-d7/a0-a6,(a7) // レジスタを退避する 0x00000004 0x48D77FFF movem.l d0-d7/a0-a6,(a7) : ; 79: movem (a7),d0-d7/a0-a6 // レジスタを復帰させる 0x0000002A 0x4CD77FFF movem.l (a7),d0-d7/a0-a6 ; ; 80: lea 64(a7),a7 // スタックフレームの解放 0x0000002E 0x4FEF0040 lea 64(a7),a7 ; ; 81: rte // 例外処理の終了 0x00000032 0x4E73 rte
trap_contentには、 アセンブラでは書きにくいような複雑な処理を書くことができます。
コメント 0