--- nono/m680x0/m68030subr.cpp 2026/04/29 17:04:34 1.1.1.2 +++ nono/m680x0/m68030subr.cpp 2026/04/29 17:04:49 1.1.1.7 @@ -7,6 +7,8 @@ #include "m68030core.h" #include "m68030bus.h" #include "m68030fpu.h" +#include "debugger.h" +#include "scheduler.h" static void m68030_change_intr(m68kcpu *, int); static void m68030_set_intr(m68kcpu *, int); @@ -35,6 +37,38 @@ m68030_init(uint32 reset_vector) return cpu; } +// 仮想時刻を返す +uint64 +m68030_get_vtime(m68kcpu *cpu) +{ + return cpu->total_vtime + cpu->Cycle2Vtime(cpu->used_cycle); +} + +// クロックを設定する。 +void +m68030_set_clockspeed(m68kcpu *cpu, uint32 clock_khz_) +{ + cpu->clock_khz = clock_khz_; + + // サイクル数を nsec に変換する際の係数。25MHz なら c2v は 40 [nsec/clock] + cpu->c2v = 1000 * 1000 / cpu->clock_khz; + + // nsec をクロック数に変換する際の係数、つまり c2v の逆数。 + // (http://hp.vector.co.jp/authors/VA003988/how_to_o.htm#27_2) + int b = 32 - __builtin_clz(cpu->c2v) - 1; + int r = 32 + b; + // 小数部を近接丸め(0捨1入)するため一桁余分に計算しておいて.. + uint64 f = (1ULL << (r + 1)) / cpu->c2v; + // 最下位ビットを丸める + if ((f & 1) != 0) { + f += 1; + } + f >>= 1; + + cpu->v2c_factor = f; + cpu->v2c_shift = r; +} + // 二重バスフォールト static void m68030_double_bus_fault(m68kcpu *cpu, const char *where) @@ -47,16 +81,42 @@ m68030_double_bus_fault(m68kcpu *cpu, co } } -// リセット例外を発生させる。 +// リセット例外を発生させる。(外部ルーチン) +void +m68030_request_reset_exception(m68kcpu *cpu) +{ + cpu->atomic_reqflag |= CPU_REQ_RESET; +} + +// リセット例外処理。 // 各レジスタを所定の状態にして、リセットベクタを読み込む。 // リセットベクタが読み込めなければ HALT 状態に移行。 void m68030_exception_reset(m68kcpu *cpu) { + // 履歴を初期化。リセット前の履歴いらんよな? + cpu->exhist.Clear(); + cpu->brhist.Clear(); + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + // リセット例外発生時の PC にはあまり意味がないので 0 にするか。 + // 履歴のリセットベクタは 0 の代わりに 1 を使う仕様になっている。 + const uint vector = 0; + cpu->exhist.AddEntry(0 | 1, 0, 0x8000 | (vector + 1)); + cpu->brhist.AddEntry(0 | 1, 0, 0x8000 | (vector + 1)); + // デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。 + debugger_notify_exception(vector); + // 1. T0, T1 クリア // 2. S をセット、M をクリア // 3. 割り込みレベルを 7 + // 受け付け中だった割り込みをクリア。 + // STOP 状態とホールト状態を解除。 + cpu->intr_pending = 0; + cpu->atomic_reqflag &= ~(CPU_REQ_INTR | CPU_REQ_STOP | CPU_REQ_HALT); + cpu->reg.state = Scheduler::SCHED_CPU_NORMAL; + // ここは set_sr を使わずに設定する。 cpu->reg.s = true; cpu->reg.m = false; @@ -87,26 +147,21 @@ m68030_exception_reset(m68kcpu *cpu) try { RegA(7) = m68030_read_32(cpu, cpu->reset_vector); RegPC = m68030_read_32(cpu, cpu->reset_vector + 4); + // リセットベクタは常に 0, 4 番地から取得したことになっている + cpu->brhist.AddEntry(0x00000004 | 1, RegPC, 0x8000); } catch (int) { m68030_double_bus_fault(cpu, "reset exception"); } } -// MPU 外部からの割り込み受付。 -// オートベクタの場合は vector にオートベクタの例外ベクタを指定のこと。 +// 割り込みを受理 void -m68030_interrupt(m68kcpu *cpu, int level, int vector) +m68030_interrupt(m68kcpu *cpu, int level) { - std::unique_lock lock(cpu->intrmtx); - - // この割り込みレベルがすでにペンディングなら何もしない - if ((int8)(cpu->intr_pending << level) < 0) { - return; - } - - // 割り込みを受理 - cpu->intr_pending |= (0x80U >> level); - cpu->intr_vector[level] = vector; + // レベル比較等は VM 側で行ってあるので、ここは割り込みの受理。 + // といってもフラグを立てるだけで実際の処理はメインループから呼ばれる + // m68030_exception_interrupt() (少し下にある) で行っている。 + cpu->intr_pending = level; cpu->atomic_reqflag |= CPU_REQ_INTR; } @@ -116,7 +171,6 @@ static void m68030_set_intr(m68kcpu *cpu, int level) { cpu->reg.intr_level = level; - cpu->intr_mask = 0x7f >> level; } // 割り込みのチェックとレベルの変更。 @@ -209,14 +263,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() を呼び出す。 @@ -234,22 +290,33 @@ 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 | (cpu->reg.s ? 1 : 0), newpc, 0x8000); + + CHECK_ADDRESS_ERROR(newpc); + RegPC = newpc; // 実際はここでパイプライン充填のために3ワードフェッチするので、 // ベクタテーブルはフェッチ出来たけどそのジャンプ先がバスエラーに @@ -378,6 +445,13 @@ m68030_exception(m68kcpu *cpu, int vecto return; } + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + uint32 from = RegPPC | (cpu->reg.s ? 1 : 0); + cpu->exhist.AddEntry(from, 0, 0x8000 | vector); + cpu->brhist.AddEntry(from, 0, 0x8000 | vector); + // デバッガに通知 (例外ブレーク用) + debugger_notify_exception(vector); + // 状態を保存 uint16 sr = RegSR; @@ -430,6 +504,13 @@ m68030_exception_buserr(m68kcpu *cpu, in assertmsg((vector == M68K_EXCEP_BUSERR || vector == M68K_EXCEP_ADDRERR), "vector=%d", vector); + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + uint32 from = RegPPC | (cpu->reg.s ? 1 : 0); + cpu->exhist.AddEntry(from, 0, 0x8000 | vector); + cpu->brhist.AddEntry(from, 0, 0x8000 | vector); + // デバッガに通知 (例外ブレーク用) + debugger_notify_exception(vector); + // 状態を保存 uint16 sr = RegSR; @@ -463,15 +544,27 @@ m68030_exception_buserr(m68kcpu *cpu, in } // 割り込み例外処理を行う。 -// intrmtx なしで呼び出すこと。 // 割り込みチェックは m68030core.cpp::m68030_flush_interrupt() で判定している。 void -m68030_exception_interrupt(m68kcpu *cpu, int level, int vector) +m68030_exception_interrupt(m68kcpu *cpu, int level) { - if (0) { - cpulog(2, "Interrupt lv=%d vec=%d occured: %s", - level, vector, m68030_get_exception_name(vector)); - } + // ユーザーズマニュアル(日本語) p.399 図8-5 の中ほど + // 「/IPEND をネゲート、割り込みアクノリッジサイクルを実行」 + // 以降に相当。 + + // 割り込みアクノリッジサイクルを実行 + int vector = m68030_interrupt_acknowledge(level); + if (vector == -1) { + vector = 24 + level; + } + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + uint32 from = RegPPC | (cpu->reg.s ? 1 : 0); + uint32 inst = 0x8000 | (level << 8) | vector; + cpu->exhist.AddEntry(from, 0, inst); + cpu->brhist.AddEntry(from, 0, inst); + // デバッガに通知 (例外ブレーク用) + debugger_notify_exception(vector); // 状態を保存 uint16 sr = RegSR; @@ -571,24 +664,25 @@ m68030_rte(m68kcpu *cpu) } 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", @@ -613,15 +707,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, @@ -629,22 +724,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, }; // ベクタ番号から表示名を返す