--- nono/m680x0/m68030subr.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/m680x0/m68030subr.cpp 2026/04/29 17:04:38 1.1.1.3 @@ -1,12 +1,13 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "header.h" #include "m68030core.h" #include "m68030bus.h" #include "m68030fpu.h" +#include "debugger.h" static void m68030_change_intr(m68kcpu *, int); static void m68030_set_intr(m68kcpu *, int); @@ -36,11 +37,15 @@ m68030_init(uint32 reset_vector) } // 二重バスフォールト -// XXX どうするかね static void m68030_double_bus_fault(m68kcpu *cpu, const char *where) { - PANIC("double bus fault in %s", where); + cpu->atomic_reqflag |= CPU_REQ_RELEASE | CPU_REQ_HALT; + + // コールバックがあれば呼び出す + if (cpu->halt_callback) { + cpu->halt_callback(cpu->halt_arg); + } } // リセット例外を発生させる。 @@ -49,6 +54,8 @@ m68030_double_bus_fault(m68kcpu *cpu, co void m68030_exception_reset(m68kcpu *cpu) { + // 例外履歴ちょっとどうするかは置いておく + // 1. T0, T1 クリア // 2. S をセット、M をクリア // 3. 割り込みレベルを 7 @@ -205,14 +212,16 @@ m68030_restore_reg_pd(m68kcpu *cpu) // リセット例外は外部から m68030_exception_reset() を呼び出す。 // // 1. バスエラー例外 -// 物理アドレス空間アクセス後のバスエラーは、物理空間側(VM側)から -// (C++の)例外がスローされるので、catch したところで処理する。 -// -// MMU による論理アドレスでのバスエラーについては後で考える。 +// MMU による論理アドレスでのバスエラーは (m68030_mmu_translate_*() が +// false を返した後) m68030bus.cpp ルーチンが (C++の) 例外をスローする。 +// 物理アドレス空間アクセス後のバスエラーは (VM側の read*()/write*() が +// (int64)-1 を返した後) m68030bus.cpp ルーチンが (C++) の例外をスローする。 +// どちらのケースも m68030core.cpp のメインループで catch し、その catch +// ブロック内で m68030_exception() を呼び出す。 // // 2. アドレスエラー例外 // アドレスエラーは m68030_jump() 内でのみ発生するはずで、その場で -// 例外処理をするはず。 +// m68030_exception() を呼んで処理する。 // // 3. 命令中で起きる例外 // 命令途中で起きる(確定的な)例外は m68030_exception() を呼び出す。 @@ -230,22 +239,41 @@ m68030_restore_reg_pd(m68kcpu *cpu) // o 1ワード目が不当命令 // -> 命令がそれぞれ m68030_exception() で処理。 // o 1ワード目の EA が不当命令 -// -> (C++の)例外スローして catch 内で処理。 +// -> (C++の)例外をスローして catch 内で m68030_exception() を実行。 // o 2ワード目が不当命令 // -> 命令がそれぞれ m68030_exception() で処理。 // // 4. 割り込み例外 -// zzz +// 外部デバイスは m68030_interrpt() を呼び出すことで MPU に割り込みを要求 +// する。その結果 MPU は直後の命令境界で割り込みをチェックし、 +// m68030_exception_interrupt() を呼び出す。 +// // vector にジャンプする。vector はベクタ番号(アドレスではない)。 static inline void m68030_jump_vector(m68kcpu *cpu, int vector) { + uint32 vecaddr; uint32 newpc; - RegPC = RegVBR + vector * 4; + vecaddr = RegVBR + vector * 4; + RegPC = vecaddr; newpc = m68030_fetch_32(cpu); - m68030_jump(cpu, newpc); + // VBR 込みのジャンプ先が確定したところで今度はブランチ履歴に記録。 + // この時のブランチ元はベクタアドレス + // (RegPC は fetch によって進んでしまうので先に保存) + cpu->brhist.AddEntry(vecaddr, newpc, 0x8000); + + CHECK_ADDRESS_ERROR(newpc); + RegPC = newpc; + + // 実際はここでパイプライン充填のために3ワードフェッチするので、 + // ベクタテーブルはフェッチ出来たけどそのジャンプ先がバスエラーに + // なるケースはここでバスエラーが出なければいけないが、まだ + // キャッシュを実装していないので、ここで1ワードフェッチしてみて + // PC を戻すあほっぽい方法を取る。 + m68030_fetch_16(cpu); + RegPC -= 2; } // 例外スタックフレーム $0 を生成する。 @@ -328,8 +356,8 @@ m68030_generate_exframe_b(m68kcpu *cpu, // で DF=1 ならプリデクリメント発生前の An に戻すという処理をする。 // RTE 時点で DF=0 なら命令を再実行しないため An も巻き戻してはならず、 // そのためプリデクリメント分の巻き戻しは RTE 時点まで行えない。 - // 一方、ポストインクリメントのケースはこの少し下の m68030_exception() - // 内のコメント参照。 + // 一方、ポストインクリメントのケースはこの少し下の + // m68030_exception_buserr() 内のコメント参照。 for (int i = 7; i >= 0; i--) { m68030_push_32(cpu, cpu->save_pd[i]); } @@ -360,68 +388,106 @@ m68030_generate_exframe_b(m68kcpu *cpu, void m68030_exception(m68kcpu *cpu, int vector) { + // バスエラー・アドレスエラーは別で処理。 + if (vector <= M68K_EXCEP_ADDRERR) { + m68030_exception_buserr(cpu, vector); + return; + } + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + cpu->exhist.AddEntry(RegPPC, 0, 0x8000 | vector); + cpu->brhist.AddEntry(RegPPC, 0, 0x8000 | vector); + // デバッガに通知 (例外ブレーク用) + debugger_notifyexception(vector); + // 状態を保存 uint16 sr = RegSR; - switch (vector) { - case M68K_EXCEP_BUSERR: // バスエラー - case M68K_EXCEP_ADDRERR: // アドレスエラー - // SR を書き換える前に! A7 が切り替わってしまう。 - - // ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。 - // EA 処理の時点で先にインクリメントしているのだが、バスエラーが - // 起きたということは実際にはポストインクリメントは発生しないので、 - // ここで無条件に巻き戻してよい。 - // 一方、プリデクリメントのケースはこの少し上の exframe_b の中の - // コメント参照。 - m68030_restore_reg_pi(cpu); - } - // S を立てて T1,T0 を落とす m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T); - switch (vector) { - case M68K_EXCEP_BUSERR: // バスエラー - case M68K_EXCEP_ADDRERR: // アドレスエラー - - // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが - // 使えればショートを使うのだが、ここでは全部ロングを使う。 - try { - m68030_generate_exframe_b(cpu, vector, sr); - } catch (...) { - m68030_double_bus_fault(cpu, "exception"); - return; + try { + switch (vector) { + case M68K_EXCEP_ILLEGAL: // 不当命令 + case M68K_EXCEP_PRIV: // 特権違反例外 + case M68K_EXCEP_ALINE: // Aライン命令例外 + case M68K_EXCEP_FLINE: // Fライン命令例外 + // スタックに積む PC はこの命令のアドレス + m68030_generate_exframe_0(cpu, vector, RegPPC, sr); + break; + + case M68K_EXCEP_TRAP0 // TRAP#0 + ... M68K_EXCEP_TRAP15: // TRAP#15 + // スタックに積む PC は次の命令 + m68030_generate_exframe_0(cpu, vector, RegPC, sr); + break; + + case M68K_EXCEP_ZERODIV: // ゼロ除算 + case M68K_EXCEP_CHK: // CHK + case M68K_EXCEP_TRAPV: // TRAPV + case M68K_EXCEP_MMU_CONFIG: // MMU 構成例外 + m68030_generate_exframe_2(cpu, vector, sr); + break; + + default: + PANIC("$%08x exception vector %d ?", RegPPC, vector); + break; } - break; - case M68K_EXCEP_ILLEGAL: // 不当命令 - case M68K_EXCEP_PRIV: // 特権違反例外 - case M68K_EXCEP_ALINE: // Aライン命令例外 - case M68K_EXCEP_FLINE: // Fライン命令例外 - // スタックに積む PC はこの命令のアドレス - m68030_generate_exframe_0(cpu, vector, RegPPC, sr); - break; - - case M68K_EXCEP_TRAP0 // TRAP#0 - ... M68K_EXCEP_TRAP15: // TRAP#15 - // スタックに積む PC は次の命令 - m68030_generate_exframe_0(cpu, vector, RegPC, sr); - break; + // ベクタにジャンプ + m68030_jump_vector(cpu, vector); + } catch (int vector2) { + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector); - case M68K_EXCEP_ZERODIV: // ゼロ除算 - case M68K_EXCEP_CHK: // CHK - case M68K_EXCEP_TRAPV: // TRAPV - case M68K_EXCEP_MMU_CONFIG: // MMU 構成例外 - m68030_generate_exframe_2(cpu, vector, sr); - break; - - default: - PANIC("$%08x exception vector %d ?", RegPPC, vector); - break; + // バスエラー処理へ + m68030_exception_buserr(cpu, vector2); } +} + +// バスエラー・アドレスエラー例外処理を行う。 +// 他の例外とは違いが多いので分けた。 +void +m68030_exception_buserr(m68kcpu *cpu, int vector) +{ + assertmsg((vector == M68K_EXCEP_BUSERR || vector == M68K_EXCEP_ADDRERR), + "vector=%d", vector); + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + cpu->exhist.AddEntry(RegPPC, 0, 0x8000 | vector); + cpu->brhist.AddEntry(RegPPC, 0, 0x8000 | vector); + // デバッガに通知 (例外ブレーク用) + debugger_notifyexception(vector); + + // 状態を保存 + uint16 sr = RegSR; + + // ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。 + // EA 処理の時点で先にインクリメントしているのだが、バスエラーが + // 起きたということは実際にはポストインクリメントは発生しないので、 + // ここで無条件に巻き戻してよい。SR を書き換えると A7 が切り替わって + // しまうので、この巻き戻しは SR を書き換える前のここで行うこと。 + // 一方、プリデクリメントのケースはこの少し上の exframe_b の中の + // コメント参照。 + m68030_restore_reg_pi(cpu); + + // S を立てて T1,T0 を落とす + m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T); - // ベクタにジャンプ - m68030_jump_vector(cpu, vector); + try { + // フレームを作成。 + // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが + // 使えればショートを使うのだが、ここでは全部ロングを使う。 + m68030_generate_exframe_b(cpu, vector, sr); + // ベクタにジャンプ + m68030_jump_vector(cpu, vector); + } catch (int vector2) { + // ここに来るのはバスエラーだけのはず + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector); + + // バスエラー・アドレスエラー処理中のバスエラーは + // ダブルバスフォールト。 + m68030_double_bus_fault(cpu, "bus/address error handling"); + } } // 割り込み例外処理を行う。 @@ -430,10 +496,11 @@ m68030_exception(m68kcpu *cpu, int vecto void m68030_exception_interrupt(m68kcpu *cpu, int level, int vector) { - if (0) { - cpulog(2, "Interrupt lv=%d vec=%d occured: %s", - level, vector, m68030_get_exception_name(vector)); - } + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + cpu->exhist.AddEntry(RegPPC, 0, 0x8000 | (level << 8) | vector); + cpu->brhist.AddEntry(RegPPC, 0, 0x8000 | (level << 8) | vector); + // デバッガに通知 (例外ブレーク用) + debugger_notifyexception(vector); // 状態を保存 uint16 sr = RegSR; @@ -443,13 +510,20 @@ m68030_exception_interrupt(m68kcpu *cpu, // 割り込みレベルを変更 m68030_change_intr(cpu, level); - // 例外スタックフレーム $0 を生成。 - // スタックに積む PC は次の命令位置。 - // M ビットは未実装なのでスローアウェイとか無視 - m68030_generate_exframe_0(cpu, vector, RegPC, sr); + try { + // 例外スタックフレーム $0 を生成。 + // スタックに積む PC は次の命令位置。 + // M ビットは未実装なのでスローアウェイとか無視 + m68030_generate_exframe_0(cpu, vector, RegPC, sr); + + // ベクタにジャンプ + m68030_jump_vector(cpu, vector); + } catch (int vector2) { + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector); - // ベクタにジャンプ - m68030_jump_vector(cpu, vector); + // バスエラー処理へ + m68030_exception_buserr(cpu, vector2); + } } // RTE 命令。 @@ -461,82 +535,90 @@ m68030_rte(m68kcpu *cpu) uint16 sr; uint16 frame; - sr = m68030_pop_16(cpu); - pc = m68030_pop_32(cpu); - frame = m68030_pop_16(cpu); - switch (frame >> 12) { - case 0: - CYCLE2(18, 20); - m68030_set_sr(cpu, sr); - m68030_jump(cpu, pc); - return; - - case 2: - CYCLE2(18, 20); - // +08.L INSTRUCTION ADDRESS を読み捨てる - RegA(7) += 4; - m68030_set_sr(cpu, sr); - m68030_jump(cpu, pc); - return; - - case 0x0b: - { - uint32 stage_b; - uint16 ssw; - - CYCLE(76); - RegA(7) += 2; // $08.W - ssw = m68030_pop_16(cpu); // $0a.W SSW - RegA(7) += 4 * 6; // $0c.L*6 - stage_b = m68030_pop_32(cpu); // $24.L Stage B - RegA(7) += 2 * 9; // $28.W*9 - // $3a..$58 ここが save_pd の取り出し - cpu->flag_pd = m68030_pop_16(cpu); - for (int i = 0; i < 8; i++) { - cpu->save_pd[i] = m68030_pop_32(cpu); - } - // A7 レジスタをリストアするかもしれない前に Super/User を - // 復帰しておかないといけない - m68030_set_sr(cpu, sr); - - // DFビットセットなら命令を再実行するため、-(An) で変化 - // したレジスタを巻き戻す。 - // DFビットクリアなら例外要因の命令はハンドラで処理された - // とみなして、次の命令に進む。 - if ((ssw & SSW_DF)) { - m68030_restore_reg_pd(cpu); - } else { - pc = stage_b - 2; + try { + sr = m68030_pop_16(cpu); + pc = m68030_pop_32(cpu); + frame = m68030_pop_16(cpu); + switch (frame >> 12) { + case 0: + CYCLE2(18, 20); + m68030_set_sr(cpu, sr); + m68030_jump(cpu, pc); + break; + + case 2: + CYCLE2(18, 20); + // +08.L INSTRUCTION ADDRESS を読み捨てる + RegA(7) += 4; + m68030_set_sr(cpu, sr); + m68030_jump(cpu, pc); + break; + + case 0x0b: + { + uint32 stage_b; + uint16 ssw; + + CYCLE(76); + RegA(7) += 2; // $08.W + ssw = m68030_pop_16(cpu); // $0a.W SSW + RegA(7) += 4 * 6; // $0c.L*6 + stage_b = m68030_pop_32(cpu); // $24.L Stage B + RegA(7) += 2 * 9; // $28.W*9 + // $3a..$58 ここが save_pd の取り出し + cpu->flag_pd = m68030_pop_16(cpu); + for (int i = 0; i < 8; i++) { + cpu->save_pd[i] = m68030_pop_32(cpu); + } + // A7 レジスタをリストアするかもしれない前に Super/User を + // 復帰しておかないといけない + m68030_set_sr(cpu, sr); + + // DFビットセットなら命令を再実行するため、-(An) で変化 + // したレジスタを巻き戻す。 + // DFビットクリアなら例外要因の命令はハンドラで処理された + // とみなして、次の命令に進む。 + if ((ssw & SSW_DF)) { + m68030_restore_reg_pd(cpu); + } else { + pc = stage_b - 2; + } + m68030_jump(cpu, pc); + break; + } + + default: + PANIC("RTE frame=$%x not supported", frame >> 12); + break; } - m68030_jump(cpu, pc); - return; - } + } catch (int vector2) { + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector2=%d", vector2); - default: - break; + // RTE 処理中のバスエラーはダブルバスフォールト。 + m68030_double_bus_fault(cpu, "bus/address error handling"); } - PANIC("RTE frame=$%x not supported", frame >> 12); } static const char * const exception_name_table[] = { + // 0123456789012345678 <- 例外履歴欄の横幅 /* 0 */ "Reset", /* 1 */ NULL, /* 2 */ "Bus Error", /* 3 */ "Address Error", /* 4 */ "Illegal Instruction", - /* 5 */ "Div by Zero", - /* 6 */ "CHK/CHK2 Instruction", - /* 7 */ "TRAP Instruction", - /* 8 */ "Priviledge Violation", + /* 5 */ "Zero Divide", + /* 6 */ "CHK/CHK2 Insn", + /* 7 */ "TRAPV, *TRAPcc Insn", + /* 8 */ "PriviledgeViolation", /* 9 */ "Trace", - /* 10 */ "A line emulation", - /* 11 */ "F line emulation", + /* 10 */ "Line 1010 emulator", + /* 11 */ "Line 1111 emulator", /* 12 */ NULL, - /* 13 */ "Co-processor", - /* 14 */ "Format", - /* 15 */ NULL, - /* 16 */ "Uninitialized Interrupt", - /* 17 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 13 */ "CoproProtoViolation", + /* 14 */ "Format Error", + /* 15 */ "Uninitialized Intr", + /* 16 */ NULL, NULL, NULL, NULL, + /* 20 */ NULL, NULL, NULL, NULL, /* 24 */ "Spurious Interrupt", /* 25 */ "Level 1 Interrupt", /* 26 */ "Level 2 Interrupt", @@ -561,15 +643,16 @@ static const char * const exception_name /* 45 */ "Trap #13", /* 46 */ "Trap #14", /* 47 */ "Trap #15", + // 0123456789012345678 /* 48 */ "FPCP Branch", - /* 49 */ "FPCP Inexcact", - /* 50 */ "FPCP Div by Zero", + /* 49 */ "FPCP InexcactResult", + /* 50 */ "FPCP Divide by Zero", /* 51 */ "FPCP UnderFlow", - /* 52 */ "FPCP Operation Error", + /* 52 */ "FPCP Operand Error", /* 53 */ "FPCP OverFlow", - /* 54 */ "FPCP SNAN", + /* 54 */ "FPCP Signaling NAN", /* 55 */ NULL, - /* 56 */ "MMU Configuration Error", + /* 56 */ "MMU Config Error", /* 57 */ NULL, /* 58 */ NULL, /* 59 */ NULL, @@ -577,22 +660,108 @@ static const char * const exception_name /* 61 */ NULL, /* 62 */ NULL, /* 63 */ NULL, - /* 64 */ "MFP Alarm", - /* 65 */ "MFP EXPON", - /* 66 */ "MFP POWSW", - /* 67 */ "MFP FMIRQ", - /* 68 */ "MFP Timer-D", - /* 69 */ "MFP Timer-C", - /* 70 */ "MFP V-Disp", - /* 71 */ "MFP GPIP5", - /* 72 */ "MFP Timer-B", - /* 73 */ "MFP TXError", - /* 74 */ "MFP TXEmpty", - /* 75 */ "MFP RXError", - /* 76 */ "MFP RXFull", - /* 77 */ "MFP Timer-A", - /* 78 */ "MFP CIRQ", - /* 79 */ "MFP H-Sync", + // 0123456789012345678 + /* $40 */ "MFP Alarm", + /* $41 */ "MFP EXPON", + /* $42 */ "MFP POWSW", + /* $43 */ "MFP FM IRQ", + /* $44 */ "MFP Timer-D", + /* $45 */ "MFP Timer-C", + /* $46 */ "MFP V-Disp", + /* $47 */ "MFP GPIP5", + /* $48 */ "MFP Timer-B", + /* $49 */ "MFP TX Error", + /* $4a */ "MFP TX Empty", + /* $4b */ "MFP RX Error", + /* $4c */ "MFP RX Full", + /* $4d */ "MFP Timer-A", + /* $4e */ "MFP CRTC IRQ", + /* $4f */ "MFP H-Sync", + /* $50 */ "SCC#B TX Empty", + /* $51 */ "SCC#B TX Empty", + /* $52 */ "SCC#B", + /* $53 */ "SCC#B", + /* $54 */ "SCC#B", + /* $55 */ "SCC#B", + /* $56 */ "SCC#B", + /* $57 */ "SCC#B", + /* $58 */ "SCC#A TX Empty", + /* $59 */ "SCC#A TX Empty", + /* $5a */ "SCC#A", + /* $5b */ "SCC#A", + /* $5c */ "SCC#A", + /* $5d */ "SCC#A", + /* $5e */ "SCC#A", + /* $5f */ "SCC#A", + + // 0123456789012345678 + /* $60 */ "I/O FDC Intr", + /* $61 */ "I/O FDD Intr", + /* $62 */ "I/O HDC Intr", + /* $63 */ "I/O PRN Intr", + /* $64 */ "DMAC#0 Complete", + /* $65 */ "DMAC#0 Error", + /* $66 */ "DMAC#1 Complete", + /* $67 */ "DMAC#1 Error", + /* $68 */ "DMAC#2 Complete", + /* $69 */ "DMAC#2 Error", + /* $6a */ "DMAC#3 Complete", + /* $6b */ "DMAC#3 Error", + /* $6c */ "SPC Interrupt", + /* $6d */ NULL, + /* $6e */ NULL, + /* $6f */ NULL, + + /* $70 */ NULL, NULL, NULL, NULL, + /* $74 */ NULL, NULL, NULL, NULL, + /* $78 */ NULL, NULL, NULL, NULL, + /* $7c */ NULL, NULL, NULL, NULL, + /* $80 */ NULL, NULL, NULL, NULL, // MIDI + /* $84 */ NULL, NULL, NULL, NULL, // MIDI + /* $88 */ NULL, NULL, NULL, NULL, // MIDI + /* $8c */ NULL, NULL, NULL, NULL, // MIDI + /* $90 */ NULL, NULL, NULL, NULL, + /* $94 */ NULL, NULL, NULL, NULL, + /* $98 */ NULL, NULL, NULL, NULL, + /* $9c */ NULL, NULL, NULL, NULL, + /* $a0 */ NULL, NULL, NULL, NULL, // MIDI + /* $a4 */ NULL, NULL, NULL, NULL, // MIDI + /* $a8 */ NULL, NULL, NULL, NULL, // MIDI + /* $ac */ NULL, NULL, NULL, NULL, // MIDI + /* $b0 */ NULL, NULL, NULL, NULL, // RS-232C + /* $b4 */ NULL, NULL, NULL, NULL, // RS-232C + /* $b8 */ NULL, NULL, NULL, NULL, // RS-232C + /* $bc */ NULL, NULL, NULL, NULL, // RS-232C + /* $c0 */ NULL, NULL, NULL, NULL, // RS-232C + /* $c4 */ NULL, NULL, NULL, NULL, // RS-232C + /* $c8 */ NULL, NULL, NULL, NULL, // RS-232C + /* $cc */ NULL, NULL, NULL, NULL, // RS-232C + /* $d0 */ NULL, NULL, NULL, NULL, + /* $d4 */ NULL, NULL, NULL, NULL, + /* $d8 */ NULL, NULL, NULL, NULL, + /* $dc */ NULL, NULL, NULL, NULL, + /* $e0 */ NULL, NULL, NULL, NULL, + /* $e4 */ NULL, NULL, NULL, NULL, + /* $e8 */ NULL, NULL, NULL, NULL, + /* $ec */ NULL, NULL, NULL, NULL, + + // 0123456789012345678 + /* $f0 */ "PSX16x50 Interrupt", + /* $f1 */ NULL, + /* $f2 */ NULL, + /* $f3 */ NULL, + /* $f4 */ NULL, + /* $f5 */ NULL, + /* $f6 */ "ExSPC Interrupt", + /* $f7 */ NULL, // TS-6BSI + /* $f8 */ "Nereid#1 Interrupt", + /* $f9 */ "Neptune-X Interrupt", + /* $fa */ "Nereid#1 USB", + /* $fb */ "Nereid#0 USB", + /* $fc */ NULL, + /* $fd */ NULL, + /* $fe */ NULL, + /* $ff */ NULL, }; // ベクタ番号から表示名を返す