--- nono/m680x0/m680x0core.cpp 2026/04/29 17:05:31 1.1.1.1 +++ nono/m680x0/m680x0core.cpp 2026/04/29 17:05:40 1.1.1.3 @@ -12,6 +12,9 @@ #include "scheduler.h" #include "vectortable.h" +#define M680X0_CYCLE_TABLE +#include "m680x0cycle.h" + #define OP_FUNC(name) __CONCAT(op_,name)() // 1命令実行する。 @@ -42,11 +45,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++の)例外スローをキャッチするのは // - メモリ空間をアクセスした先でのバスエラー例外 @@ -172,8 +170,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)) { @@ -240,6 +239,28 @@ MPU680x0Device::GenerateExframe2(uint ve push_2(sr); } +// 例外スタックフレーム $4 を生成する。(68LC040 only) +inline void +MPU680x0Device::GenerateExframe4(uint vector, uint16 sr, uint32 ea) +{ + // 公式の図 (のオフセット) が雑すぎる。 + // + // SP->| STATUS REGISTER + // +$02 | PROGRAM COUNTER (H) ; こっちは次の命令 + // +$04 | (L) + // +$06 | %0100 | VECTOR OFFSET + // +$08 | EFFECTIVE ADDRESS (H) + // +$0a | (L) + // +$0c | PC OF FAULTED INSTRUCTION (H) + // +$0e | (L) + + push_4(ppc); + push_4(ea); + push_2((4 << 12) | (vector << 2)); + push_4(reg.pc); + push_2(sr); +} + // 例外スタックフレーム $7 を生成する。(68040 only) inline void MPU680x0Device::GenerateExframe7(uint vector, uint16 sr) @@ -466,8 +487,9 @@ 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); + constexpr uint32 info = BranchHistory_m680x0::InfoException(vector); + exhist.AddEntry(reg.pc | 1, 0, info); + brhist.AddEntry(reg.pc | 1, 0, info); // デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。 debugger->NotifyExceptionMain(vector); @@ -495,9 +517,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(); @@ -514,8 +540,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"); } @@ -539,13 +565,13 @@ MPU680x0Device::ExceptionReset() void MPU680x0Device::Exception(uint vector) { - uint32 inst = BranchHistory_m680x0::Exception | vector; - ExceptionGeneric(vector, inst); + uint32 info = BranchHistory_m680x0::InfoException(vector); + ExceptionGeneric(vector, info); } // TRAP #15 の例外処理を行う。 // 処理自体は汎用の Exception() とまったく同じだが、 -// IOCS コールの時には分岐履歴の出力が変わるため入口を分けてある。 +// IOCS コールの時に分岐履歴の内容を変えるため入口を分けてある。 void MPU680x0Device::ExceptionTrap15() { @@ -559,38 +585,64 @@ 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); } else { // TRAP#15 っぽい - inst = BranchHistory_m680x0::Exception | vector; + info = BranchHistory_m680x0::InfoException(vector); } - ExceptionGeneric(vector, inst); + ExceptionGeneric(vector, info); +} + +// 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 { + // F ライン例外っぽい。 + info = BranchHistory_m680x0::InfoException(vector); + } + 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; // 例外履歴に記録 (例外発生はブランチ履歴にも記録) excep_counter[vector]++; uint32 from = ppc | (IsSuper() ? 1 : 0); - exhist.AddEntry(from, 0, inst); - brhist.AddEntry(from, 0, inst); + exhist.AddEntry(from, 0, info); + brhist.AddEntry(from, 0, info); // デバッガに通知 (例外ブレーク用) debugger->NotifyExceptionMain(vector); @@ -656,12 +708,15 @@ MPU680x0Device::ExceptionBuserr(uint vec // 例外履歴に記録 (例外発生はブランチ履歴にも記録) 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); + uint32 info = BranchHistory_m680x0::InfoException(vector); + exhist.AddEntry(from, 0, info); + brhist.AddEntry(from, 0, info); // デバッガに通知 (例外ブレーク用) debugger->NotifyExceptionMain(vector); + // 現状 68030 のバスエラーに short/long の区別はないので全部 long 相当。 + CYCLE3(busfault); + // 状態を保存 uint16 sr = GetSR(); @@ -748,9 +803,9 @@ MPU680x0Device::ExceptionInterrupt() // 例外履歴に記録 (例外発生はブランチ履歴にも記録) 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); + uint32 info = BranchHistory_m680x0::InfoException(vector); + exhist.AddEntry(from, 0, info); + brhist.AddEntry(from, 0, info); // デバッガに通知 (例外ブレーク用) debugger->NotifyExceptionMain(vector); @@ -781,6 +836,40 @@ MPU680x0Device::ExceptionInterrupt() return (used_cycle - cycle_start) * clock_nsec + bd.GetWait(); } +// LC040 の未実装浮動小数点命令例外。 +void +MPU680x0Device::ExceptionFPLC(uint32 ea) +{ + const uint vector = M68K::EXCEP_FLINE; + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + excep_counter[vector]++; + uint32 from = ppc | (IsSuper() ? 1 : 0); + constexpr uint32 info = BranchHistory_m680x0::InfoException(vector); + exhist.AddEntry(from, 0, info); + brhist.AddEntry(from, 0, info); + // デバッガに通知 (例外ブレーク用) + debugger->NotifyExceptionMain(vector); + + // 状態を保存 + uint16 sr = GetSR(); + + // S を立てて T1,T0 を落とす + SetSR((GetSR() | M68K::SR_S) & ~M68K::SR_T); + + try { + GenerateExframe4(vector, sr, ea); + + // ベクタにジャンプ + JumpVector(vector, false); + } catch (uint vector2) { + assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector=%u", vector); + + // バスエラー処理へ + ExceptionBuserr(vector2); + } +} + // RTE 命令。 // 例外処理の近くに置いておくほうが便利なので。 void @@ -798,19 +887,33 @@ 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); Jump(pc); break; + case 0x04: + { + // 68LC040 only + if (GetFPUType().Is4060LC() == false) { + Exception(M68K::EXCEP_FORMAT); + return; + } + CYCLE(15); + reg.A[7] += 8; + SetSR(sr); + Jump(pc); + break; + } + case 0x07: { // 68040 only @@ -818,6 +921,7 @@ MPU680x0Device::ops_rte() Exception(M68K::EXCEP_FORMAT); return; } + CYCLE(23); uint16 ssw; reg.A[7] += 4; ssw = pop_2();