--- nono/m680x0/m680x0core.cpp 2026/04/29 17:05:35 1.1.1.2 +++ nono/m680x0/m680x0core.cpp 2026/04/29 17:05:57 1.1.1.5 @@ -7,17 +7,21 @@ #include "mpu680x0.h" #include "bitops.h" #include "debugger.h" +#include "event.h" #include "interrupt.h" #include "mainbus.h" #include "scheduler.h" #include "vectortable.h" +#define M680X0_CYCLE_TABLE +#include "m680x0cycle.h" + #define OP_FUNC(name) __CONCAT(op_,name)() // 1命令実行する。 // 割り込みチェックなどを省いたショート版。 void -MPU680x0Device::ExecShort(Event& ev) +MPU680x0Device::ExecShort(Event *ev) { uint64 cycle_start = used_cycle; @@ -42,11 +46,6 @@ MPU680x0Device::ExecShort(Event& ev) switch (ir6) { #include "m680x0switch.h" } - - if (__predict_false(icache_enable == false)) { - used_cycle += cycle_nocache; - cycle_nocache = 0; - } } catch (uint vector) { // ここで(C++の)例外スローをキャッチするのは // - メモリ空間をアクセスした先でのバスエラー例外 @@ -72,7 +71,7 @@ MPU680x0Device::ExecShort(Event& ev) // 外部割り込みの起動は VM 側で処理している。 int cycle = used_cycle - cycle_start; - ev.time = cycle * clock_nsec + buswait; + ev->time = cycle * clock_tsec + buswait; scheduler->StartEvent(ev); } @@ -172,8 +171,9 @@ MPU680x0Device::JumpVector(uint vector, // VBR 込みのジャンプ先が確定したところで今度はブランチ履歴に記録。 // この時のブランチ元はベクタアドレス // (reg.pc は fetch によって進んでしまうので先に保存) - uint32 inst = BranchHistory_m680x0::VectorJump; - brhist.AddEntry(vecaddr | (IsSuper() ? 1 : 0), newpc, inst); + uint32 from = vecaddr | (IsSuper() ? 1 : 0); + uint32 info = BranchHistory_m680x0::InfoVectorJump(); + brhist.AddEntry(from, newpc, info); // 奇数番地ならアドレスエラー if (__predict_false((newpc & 1) != 0)) { @@ -267,7 +267,6 @@ inline void MPU680x0Device::GenerateExframe7(uint vector, uint16 sr) { uint16 ssw; - uint32 fault_addr; uint32 pc; uint32 ea = 0; uint16 wb1s = 0; @@ -307,7 +306,7 @@ MPU680x0Device::GenerateExframe7(uint ve // +$30.L PUSH DATA LW1 (PD1) // +$34.L PUSH DATA LW2 (PD2) // +$38.L PUSH DATA LW3 (PD3) - // +$40 + // +$3c // こっちから上に向かって PUSH // SSW @@ -317,12 +316,11 @@ MPU680x0Device::GenerateExframe7(uint ve // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ // この後のスタック操作で bus が変化する前に保存。 - fault_addr = bus.laddr.Addr(); + const busaddr fault = bus.laddr; - ssw = bus.laddr.GetFC(); // TM - ssw |= (bus.size & 3) << 5; - bool is_write = bus.IsWrite(); - if (is_write == false) { + ssw = fault.GetFC(); // TM(=FC) + ssw |= (fault.GetSize() & 3) << 5; // Size の下位2ビット + if (fault.IsRead()) { ssw |= M68040::SSW_RW; } if (bus.atcfault) { @@ -346,13 +344,13 @@ MPU680x0Device::GenerateExframe7(uint ve push_4(wb2a); // WB2A push_4(0); // WB3D push_4(0); // WB3A - push_4(fault_addr); + push_4(fault.Addr()); push_2(wb1s); // WB1S push_2(wb2s); // WB2S push_2(0); // WB3S push_2(ssw); push_4(ea); - push_2(0x7000 | vector); + push_2(0x7000 | (vector << 2)); push_4(pc); push_2(sr); } @@ -362,15 +360,22 @@ inline void MPU680x0Device::GenerateExframeB(uint vector, uint16 sr) { uint16 ssw; - uint32 fault_addr; uint32 stageBaddr; stageBaddr = reg.pc + 2; + // この後のスタック操作で bus が変化する前に保存 - fault_addr = bus.laddr.Addr(); + const busaddr fault = bus.laddr; + + // SSW + // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |FC |FB |RC |RB | X | X | X |DF |RM |RW | SIZE | X | FC | + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - // DF は常に立てる必要がある?? - ssw = bus.laddr.GetSSW() | M68030::SSW_SIZE(bus.size) | M68030::SSW_DF; + ssw = (fault.Get() >> 33) & 0x47; // R/W+FC + ssw |= (fault.GetSize() & 3) << 4; // Size の下位2ビット + ssw |= M68030::SSW_DF; // DF は常に立てる必要がある?? if (mmu_enable && (ssw & M68K::FC_DATA) != 0) { // 命令アクセスなら RB をセット。 ssw |= M68030::SSW_RB; @@ -422,7 +427,7 @@ MPU680x0Device::GenerateExframeB(uint ve reg.A[7] -= 8; push_4(0); // DATA OUTPUT BUFFER reg.A[7] -= 4; - push_4(fault_addr); // DATA CYCLE FAULT ADDRESS + push_4(fault.Addr()); // DATA CYCLE FAULT ADDRESS push_2(0); // INSTRUCTION PIPE STAGE B push_2(0); // INSTRUCTION PIPE STAGE C push_2(ssw); @@ -488,10 +493,10 @@ MPU680x0Device::ExceptionReset() // m88k と動作を揃えておく。 const uint vector = 0; excep_counter[0]++; - exhist.AddEntry(reg.pc | 1, 0, BranchHistory_m680x0::Exception | 0); - brhist.AddEntry(reg.pc | 1, 0, BranchHistory_m680x0::Exception | 0); - // デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。 - debugger->NotifyExceptionMain(vector); + constexpr uint32 info = BranchHistory_m680x0::InfoException(vector); + exhist.AddEntry(reg.pc | 1, 0, info); + brhist.AddEntry(reg.pc | 1, 0, info); + last_vector = vector; // RESET 信号線をネゲートしてから SP の読み込み開始まで 4クロック // (Section 7.8, Figure 7-64) @@ -517,9 +522,13 @@ MPU680x0Device::ExceptionReset() // 5. キャッシュの EI|ED, FI|FD, IBE|DBE, WA をクリア // 6. キャッシュエントリをすべてクリア (CI|CD) - cycle_nocache = 0; ResetCache(); + // キャッシュ状態が変わったので、サイクル表も更新。 + if (GetMPUType() == m680x0MPUType::M68030) { + cycle_table = cycle_table_030ncc; + } + // 7. TC:E、TTn:E をクリア ResetMMU(); @@ -536,8 +545,8 @@ MPU680x0Device::ExceptionReset() // 大丈夫のはずだけど fetch_4() が reg.pc を書き換えるので一応 reg.pc = pc; // リセットベクタは常に 0, 4 番地から取得したことになっている - uint32 inst = BranchHistory_m680x0::VectorJump; - brhist.AddEntry(0x00000004 | 1, reg.pc, inst); + uint32 from = 0x00000004 | 1; + brhist.AddEntry(from, reg.pc, BranchHistory_m680x0::InfoVectorJump()); } catch (uint) { DoubleBusFault("reset exception"); } @@ -556,18 +565,57 @@ MPU680x0Device::ExceptionReset() // ただし以下の例外はここではなくそれぞれ固有のを呼ぶこと。 // o リセット例外は ExceptionReset() // o 割り込み例外は ExceptionInterrupt() -// o TRAP#15 例外は ExceptionTrap15() +// o TRAP#0 例外は ExceptionTrap0() .. NetBSD システムコール用 +// o TRAP#15 例外は ExceptionTrap15() .. IOCS コール用 // o バスエラー・アドレスエラーは ExceptionBuserr() void MPU680x0Device::Exception(uint vector) { - uint32 inst = BranchHistory_m680x0::Exception | vector; - ExceptionGeneric(vector, inst); + uint32 info = BranchHistory_m680x0::InfoException(vector); + ExceptionGeneric(vector, info); +} + +// TRAP #0 の例外処理を行う。 +// 処理自体は汎用の Exception() とまったく同じだが、 +// NetBSD システムコールの時に分岐履歴の内容を変えるため入口を分けてある。 +void +MPU680x0Device::ExceptionTrap0() +{ + // 今発生した TRAP #0 が NetBSD/m68k システムコールっぽいか調べる。 + // + // NetBSD/m68k 方面をやってる時は TRAP #0 はシステムコールを表示して + // ほしい。一方 Human68k などでは TRAP #0 は別用途なので、システムコール + // かのように表示されるのは紛らわしいので、 + // 出来れば今呼ばれたこれが NetBSD/m68k システムコールなのか、そうでない + // TRAP #0 なのかは区別したい。 + // + // NetBSD/m68k カーネルが実行中かどうかは厳密には分からないが、これが + // 呼ばれた時点で、 + // 1) MMU がオン。 + // 2) カーネルのエントリポイントの最初の命令が move.w #$2700,sr 命令。 + // を満たせば NetBSD/m68k システムコールと判定することにする。 + // カーネルエントリポイントは機種ごとに固有で、不変ではないものの + // ブートローダ側にも影響があるためおそらくそう簡単には変わらないだろう。 + // だめならまた考える。 + + const uint vector = M68K::EXCEP_TRAP0; + uint32 info; + if (__predict_true(mmu_enable)) { + // カーネルエントリポイントの最初の命令はどれも move.w #$2700,sr。 + if (mainbus->Peek4(netbsd_entrypoint) == 0x46fc2700) { + // NetBSD/m68k カーネルっぽい。 + info = BranchHistory_m680x0::InfoNetBSDSyscall(reg.D[0] & 0xffff); + ExceptionGeneric(vector, info); + return; + } + } + // その他の TRAP#0 っぽい + Exception(vector); } // TRAP #15 の例外処理を行う。 // 処理自体は汎用の Exception() とまったく同じだが、 -// IOCS コールの時には分岐履歴の出力が変わるため入口を分けてある。 +// IOCS コールの時に分岐履歴の内容を変えるため入口を分けてある。 void MPU680x0Device::ExceptionTrap15() { @@ -581,40 +629,66 @@ MPU680x0Device::ExceptionTrap15() // 区別したい。 // // IOCS コールかどうかは厳密には分からないが、これが呼ばれた時点で - // ワークエリアの $cbc == 0x03、$cbe == 0xff であれば IOCS コールと + // ワークエリアの $cb6 == 0x02、$cb7 == 0x03 であれば IOCS コールと // 判定することにする。 - // $cbc は MPU 種別で 3:68030。 - // $cbe は MMU 有無で 0xff:有り。 - // 今の所 68030/MMU有り しかサポートしていないので。 + // $cb6、$cb7 はシステムポートの機種とクロック数 (を加工したもの) + // なので、本体が X68030 であれば常に 0x02、0x03 になるはず。 // だめならまた考える。 const uint vector = M68K::EXCEP_TRAP15; - uint32 inst; - if (mainbus->Peek1(0xcbc) == 0x03 && mainbus->Peek1(0xcbe) == 0xff) { + uint32 info; + if (mainbus->Peek1(0xcb6) == 0x02 && mainbus->Peek1(0xcb7) == 0x03) { // IOCS コールっぽい - inst = BranchHistory_m680x0::IOCSCall | (reg.D[0] & 0xff); + info = BranchHistory_m680x0::InfoIOCSCall(reg.D[0] & 0xff); + ExceptionGeneric(vector, info); + return; + } + + // その他の TRAP#15 っぽい + Exception(vector); +} + +// F ライン例外処理を行う。 +// 処理自体は Exception() とまったく同じだが、 +// DOS コールの時に分岐履歴の内容を変えるため入口を分けてある。 +void +MPU680x0Device::ExceptionFLine() +{ + // 今発生した F ライン例外が DOS コールっぽいかどうか調べる。 + // といいつつ今のところ IOCS ワークで判定している。 + // DOS コールは本来 Human68k が起動しているかどうかなので、 + // IOCS ワークがあるかどうかとは違うが、とりあえず。 + // このすぐ上の ExceptionTrap15() のコメントも参照。 + + const uint vector = M68K::EXCEP_FLINE; + uint32 info; + if ((ir & 0xff00) == 0xff00 && + mainbus->Peek1(0xcb6) == 0x02 && mainbus->Peek1(0xcb7) == 0x03) + { + // DOS コールっぽい。 + info = BranchHistory_m680x0::InfoDOSCall(ir); } else { - // TRAP#15 っぽい - inst = BranchHistory_m680x0::Exception | vector; + // F ライン例外っぽい。 + info = BranchHistory_m680x0::InfoException(vector); } - ExceptionGeneric(vector, inst); + ExceptionGeneric(vector, info); } // 汎用の例外処理本体。 // ext_vector は下位8ビットがベクタ番号、 // 8ビット目が %1 なら浮動小数点未実装命令例外 (vector は F ライン)。 +// info は履歴に記録するやつ。 void -MPU680x0Device::ExceptionGeneric(uint ext_vector, uint32 inst) +MPU680x0Device::ExceptionGeneric(uint ext_vector, uint32 info) { uint vector = ext_vector & 0xff; // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + last_vector = vector; excep_counter[vector]++; uint32 from = ppc | (IsSuper() ? 1 : 0); - exhist.AddEntry(from, 0, inst); - brhist.AddEntry(from, 0, inst); - // デバッガに通知 (例外ブレーク用) - debugger->NotifyExceptionMain(vector); + exhist.AddEntry(from, 0, info); + brhist.AddEntry(from, 0, info); // 状態を保存 uint16 sr = GetSR(); @@ -676,13 +750,15 @@ MPU680x0Device::ExceptionBuserr(uint vec "vector=%u", vector); // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + last_vector = vector; excep_counter[vector]++; uint32 from = ppc | (IsSuper() ? 1 : 0); - uint32 inst = BranchHistory_m680x0::Exception | vector; - exhist.AddEntry(from, 0, inst); - brhist.AddEntry(from, 0, inst); - // デバッガに通知 (例外ブレーク用) - debugger->NotifyExceptionMain(vector); + uint32 info = BranchHistory_m680x0::InfoException(vector); + exhist.AddEntry(from, 0, info); + brhist.AddEntry(from, 0, info); + + // 現状 68030 のバスエラーに short/long の区別はないので全部 long 相当。 + CYCLE3(busfault); // 状態を保存 uint16 sr = GetSR(); @@ -768,13 +844,12 @@ MPU680x0Device::ExceptionInterrupt() } // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + last_vector = vector; excep_counter[vector]++; uint32 from = ppc | (IsSuper() ? 1 : 0); - uint32 inst = BranchHistory_m680x0::Exception | vector; - exhist.AddEntry(from, 0, inst); - brhist.AddEntry(from, 0, inst); - // デバッガに通知 (例外ブレーク用) - debugger->NotifyExceptionMain(vector); + uint32 info = BranchHistory_m680x0::InfoException(vector); + exhist.AddEntry(from, 0, info); + brhist.AddEntry(from, 0, info); // 状態を保存 uint16 oldsr = GetSR(); @@ -800,7 +875,7 @@ MPU680x0Device::ExceptionInterrupt() ExceptionBuserr(vector2); } - return (used_cycle - cycle_start) * clock_nsec + bd.GetWait(); + return (used_cycle - cycle_start) * clock_tsec + bd.GetWait(); } // LC040 の未実装浮動小数点命令例外。 @@ -808,15 +883,14 @@ void MPU680x0Device::ExceptionFPLC(uint32 ea) { const uint vector = M68K::EXCEP_FLINE; - const uint32 inst = BranchHistory_m680x0::Exception | vector; // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + last_vector = vector; excep_counter[vector]++; uint32 from = ppc | (IsSuper() ? 1 : 0); - exhist.AddEntry(from, 0, inst); - brhist.AddEntry(from, 0, inst); - // デバッガに通知 (例外ブレーク用) - debugger->NotifyExceptionMain(vector); + constexpr uint32 info = BranchHistory_m680x0::InfoException(vector); + exhist.AddEntry(from, 0, info); + brhist.AddEntry(from, 0, info); // 状態を保存 uint16 sr = GetSR(); @@ -854,13 +928,13 @@ MPU680x0Device::ops_rte() type = frame >> 12; switch (type) { case 0: - CYCLE2(18, 20); + CYCLE3(rte_frame0); SetSR(sr); Jump(pc); break; case 2: - CYCLE2(18, 20); + CYCLE3(rte_frame2); // +08.L (INSTRUCTION) ADDRESS を読み捨てる。 reg.A[7] += 4; SetSR(sr); @@ -874,6 +948,7 @@ MPU680x0Device::ops_rte() Exception(M68K::EXCEP_FORMAT); return; } + CYCLE(15); reg.A[7] += 8; SetSR(sr); Jump(pc); @@ -887,6 +962,7 @@ MPU680x0Device::ops_rte() Exception(M68K::EXCEP_FORMAT); return; } + CYCLE(23); uint16 ssw; reg.A[7] += 4; ssw = pop_2();