ロムモニタからロードされたプログラムに割りこみベクタを引き渡す部分を書きました。
次はNMI。NMIにはやりたかったことがあるんだ。「RAMにプログラムをロードし たままNMIかけて最初から起動する。」これがあるとタイミングプロブレムか な...という時にロードする手間が省けていい。
- 割り込みベクタはロム領域の0x1cから0xf0までに配置される。
- これは書き込み不可能なので、ここからRAMに置いたルーチンを介してユーザプログラムの割り込みハンドラを呼ぶ。
- ユーザの割り込みハンドラは直接rteで帰るのでRAMに置いたルーチンには帰ってこない。→スタックは使えない。
- 割りこみなので一切のレジスタは退避せずには使えない。
- メモリ間接は@@aa:8なので、ユーザ領域にはまったく届かない。
- モトローラSフォーマットで読みこんでいるので、ユーザの仮想ベクタテーブルの位置をシンボルから探すことはできない。
ロムに置くのは
const interrupt_handler_t vector_table_rom[]
__attribute ((section (".intvecs_rom"))) =
{
nmi, /* 7 0x1c NMI */
jmp08, /* 8 0x20 TRAPA#0 */
jmp09, /* 9 0x24 TRAPA#1 */
jmp10, /* 10 0x28 TRAPA#2 */
jmp11, /* 11 0x2c TRAPA#3 */
jmp12, /* 12 0x30 IRQ0 */
jmp13, /* 13 0x34 IRQ1 */
こんな感じで、データ領域にあるjmp??にジャンプするようにしておきます。
その領域は、
.h8300h
.section .vector_link_table
.globl _jmp07
_jmp07: .long 0
.globl _jmp08
_jmp08: .long 0
.globl _jmp09
_jmp09: .long 0
.globl _jmp10
_jmp10: .long 0
ただ単に場所をとって置くだけ。実行時に埋めます。jmp @aa:24命令は4byteな
ので4byteづつ。
仮想ベクタテーブルは.dataセクションの中に。
const interrupt_handler_t vector_table_ram[]
__attribute ((section (".intvecs_ram"))) =
{
nmi, /* 7 0x1c NMI */
trapa0, /* 8 0x20 TRAPA#0 */
trapa1, /* 9 0x24 TRAPA#1 */
trapa2, /* 10 0x28 TRAPA#2 */
ロムモニタの起動時には引数のvirtual_vector_table_addrを0で呼んで、
モニタ用のベクタテーブルにします。ユーザプログラムをロードした後は
virtual_vector_table_addr(決め打ち)から参照して設定します。
void
vector_table_update (addr_t virtual_vector_table_addr)
{
extern uint32_t vector_link_table_start[];
int i;
uint32_t *jmp = vector_link_table_start;
const uint32_t *vec;
if (virtual_vector_table_addr)
vec = (const uint32_t *)virtual_vector_table_addr;
else
vec = (const uint32_t *)vector_table_ram;
// Install ROM->RAM vector link.
for (i = 7; i <= 60; i++, vec++, jmp++)
{
// 'jmp @aa:24' instruction.
*jmp = (*vec & 0xffffff) | 0x5a000000;
}
}
これで、ロードしたユーザプログラムからも割り込みが使えるようになりました。
次はNMI。NMIにはやりたかったことがあるんだ。「RAMにプログラムをロードし たままNMIかけて最初から起動する。」これがあるとタイミングプロブレムか な...という時にロードする手間が省けていい。
スタートルーチンに小細工を入れました。NMIの時に最初からやり直せるように
+--------------
-4 | NMI用のリターンアドレスとCCR rteが読みこむフォーマット。
+--------------
-8 | NMI時に復帰すべきスタックアドレス。そのアドレスには上のデータが入っている。
+--------------
-12| er6 明示的にジャンプした用 (callee saved)
+--------------
-16| er5 同上
+--------------
-20| er4 同上
+--------------
.h8300h
.section .text
.globl start
start:
; Set stack.
mov.l #_stack_start, sp
; Set return address and CCR for NMI.
mov.l #start, er0
mov.l er0, @-sp-------------------------+
mov.b #0x80, r0l |
mov.b r0l, @sp ; interrupt disable |
; Set stack address to restore. |
mov.l sp, er0 |
mov.l er0, @-sp ---------+ |
loop: jmp @_machine_startup | |
jmp @loop | |
/* NOTREACHED */ | |
| |
こうしておいて、 | |
| |
| |
.global _nmi | |
_nmi: | |
mov.l #_stack_start, er1 | |
sub.l #0x14, er1 | |
mov.l @(0x0c, er1) sp <----+ |
mov.l @(0x08, er1) er6 |
mov.l @(0x04, er1) er5 |
mov.l @(0x00, er1) er4 |
rte |
で元に戻る。この場合spの差ししめすところは@(0x10, er1)で、この場合冗長
だけれど、サブルーチンコールから設定した時はリターンアドレスがその時の
スタックに入っているので、rteが参照するスタックの位置の指定が必要。
逆にサブルーチンコールからのジャンプがなければ単純に
.global _nmi_simple
_nmi_simple:
mov.l #_stack_start, sp
subs #4, sp
rte
でいい。
サブルーチンコールでジャンプしてNMIで戻って来るには、
.globl _jump_user_program
_jump_user_program:
mov.l #_stack_start, er1 ; save current context to stack bottom
sub.l #0x14, er1
stc.b ccr, r2l
mov.b r2l, @sp ; add CCR
mov.b sp, @(0x0c, er1) ; stack address
mov.l er6, @(0x08, er1) ; callee saved
mov.l er5, @(0x04, er1) ; callee saved
mov.l er4, @(0x00, er1) ; callee saved
jmp @er0
'rte'用にCCRも設定しておく。スタートに戻りたい時は割り込み禁止にしない
といけないので、単純に0x80をCCRに入れていた。
ここは現在のスタックにリターンアドレスとCCRを入れているのでspの差ししめす
@(0x0c, er1)が必要。
まだユーザプログラムからロムモニタに戻れないので、どこか勘違いしてるかも。
