--- nono/m680x0/m68030core.cpp 2026/04/29 17:05:15 1.1.1.8 +++ nono/m680x0/m68030core.cpp 2026/04/29 17:05:22 1.1.1.10 @@ -14,12 +14,13 @@ // 1命令実行する。 // 割り込みチェックなどを省いたショート版。 -// 戻り値は消費したサイクル数。 void MPU680x0Device::ExecShort(Event& ev) { uint64 cycle_start = used_cycle; + buswait = 0; + try { // 1命令版では switch-case を使うしかない。 ppc = reg.pc; @@ -40,8 +41,10 @@ MPU680x0Device::ExecShort(Event& ev) #include "m68030switch.h" } - used_cycle += cycle_nocache; - cycle_nocache = 0; + if (__predict_false(icache_enable == false)) { + used_cycle += cycle_nocache; + cycle_nocache = 0; + } } catch (int vector) { // ここで(C++の)例外スローをキャッチするのは // - メモリ空間をアクセスした先でのバスエラー例外 @@ -55,13 +58,17 @@ MPU680x0Device::ExecShort(Event& ev) putlogn("vector 0x%x", vector); } } - Exception(vector); + if (vector == M68K::EXCEP_BUSERR) { + ExceptionBuserr(vector); + } else { + Exception(vector); + } } // 外部割り込みの起動は VM 側で処理している。 int cycle = used_cycle - cycle_start; - ev.time = cycle * clock_nsec; + ev.time = cycle * clock_nsec + buswait; scheduler->StartEvent(ev); } @@ -116,8 +123,9 @@ MPU680x0Device::SetSR(uint16 data) reg.A[7] = reg.msp; } - // バスの FC2 を変更 - bus.SetFC2(reg.s); + // FC2-0 + fc_inst.Set(0, busaddr::Fetch, reg.s); + fc_data.Set(0, busaddr::D, reg.s); // XXX 他に何か状態変えないといけないことあったっけか。 } @@ -150,7 +158,7 @@ MPU680x0Device::restore_reg_pd() // vector にジャンプする。vector はベクタ番号(アドレスではない)。 void -MPU680x0Device::JumpVector(int vector) +MPU680x0Device::JumpVector(int vector, bool from_buserr) { uint32 vecaddr; uint32 newpc; @@ -161,11 +169,17 @@ MPU680x0Device::JumpVector(int vector) // VBR 込みのジャンプ先が確定したところで今度はブランチ履歴に記録。 // この時のブランチ元はベクタアドレス // (reg.pc は fetch によって進んでしまうので先に保存) - brhist.AddEntry(vecaddr | (IsSuper() ? 1 : 0), newpc, 0x8000); + uint32 inst = BranchHistory_m680x0::VectorJump; + brhist.AddEntry(vecaddr | (IsSuper() ? 1 : 0), newpc, inst); // 奇数番地ならアドレスエラー if (__predict_false((newpc & 1) != 0)) { - Exception(M68K::EXCEP_ADDRERR); + if (from_buserr) { + // バスエラー処理中ならダブルバスフォールト。 + throw M68K::EXCEP_ADDRERR; + } else { + ExceptionBuserr(M68K::EXCEP_ADDRERR); + } return; } reg.pc = newpc; @@ -225,9 +239,14 @@ MPU680x0Device::GenerateExframeB(int vec stageBaddr = reg.pc + 2; // この後のスタック操作で bus が変化する前に保存 - fault_addr = bus.laddr; - // DF ビットを立てておくと RTE した後この命令を再実行。 - ssw = bus.ssw | M68K::SSW_DF; + fault_addr = (uint32)bus.laddr; + + // DF は常に立てる必要がある?? + ssw = bus.GetSSW() | M68K::SSW_DF; + if (mmu_enable && (ssw & M68K::FC_DATA) != 0) { + // 命令アクセスなら RB をセット。 + ssw |= M68K::SSW_RB; + } // 例外スタックフレーム $B // SP+$00.W STATUS REGISTER @@ -294,14 +313,14 @@ MPU680x0Device::GenerateExframeB(int vec // 1. バスエラー例外 // MMU による論理アドレスでのバスエラーは (Translate*() が false を返した後) // m68030bus.cpp ルーチンが (C++の) 例外をスローする。 -// 物理アドレス空間アクセス後のバスエラーは (VM側の read*()/write*() が -// (int64)-1 を返した後) m68030bus.cpp ルーチンが (C++) の例外をスローする。 +// 物理アドレス空間アクセス後のバスエラーは (VM側の Read*()/Write*() が +// BusErr を返した後) m68030bus.cpp ルーチンが (C++) の例外をスローする。 // どちらのケースも m68030core.cpp のメインループで catch し、その catch -// ブロック内で Exception() を呼び出す。 +// ブロック内で ExceptionBuserr() を呼び出す。 // // 2. アドレスエラー例外 // アドレスエラーは Jump() 内でのみ発生するはずで、その場で -// Exception() を呼んで処理する。 +// ExceptionBuserr() を呼んで処理する。 // // 3. 命令中で起きる例外 // 命令途中で起きる(確定的な)例外は Exception() を呼び出す。 @@ -339,10 +358,9 @@ MPU680x0Device::ExceptionReset() // 例外履歴に記録 (例外発生はブランチ履歴にも記録) // m68k では今の所リセット例外発生時の PC にはあまり意味がないけど、 // m88k と動作を揃えておく。 - // 履歴のリセットベクタは 0 の代わりに 1 を使う仕様になっている。 const uint vector = 0; - exhist.AddEntry(reg.pc | 1, 0, 0x8000 | (vector + 1)); - brhist.AddEntry(reg.pc | 1, 0, 0x8000 | (vector + 1)); + exhist.AddEntry(reg.pc | 1, 0, BranchHistory_m680x0::Exception | 0); + brhist.AddEntry(reg.pc | 1, 0, BranchHistory_m680x0::Exception | 0); // デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。 debugger->NotifyExceptionMain(vector); @@ -360,14 +378,15 @@ MPU680x0Device::ExceptionReset() // ここは SetSR を使わずに設定する。 reg.s = true; reg.m = false; - bus.SetFC2(reg.s); + fc_inst = busaddr::S | busaddr::Fetch; + fc_data = busaddr::S | busaddr::D; // ここは割り込みレベル変化のチェックは不要でセットだけでいい。 reg.intr_mask = 7; // 4. VBR をクリア reg.vbr = 0; // 5. キャッシュ - reg.cacr = 0; // XXX + SetCACR(0); // 6. キャッシュエントリクリア // 7. TC:E、TTn:E をクリア SetTC(GetTC() & ~m68030TC::TC_E); @@ -384,7 +403,8 @@ MPU680x0Device::ExceptionReset() // 大丈夫のはずだけど fetch_32() が reg.pc を書き換えるので一応 reg.pc = pc; // リセットベクタは常に 0, 4 番地から取得したことになっている - brhist.AddEntry(0x00000004 | 1, reg.pc, 0x8000); + uint32 inst = BranchHistory_m680x0::VectorJump; + brhist.AddEntry(0x00000004 | 1, reg.pc, inst); } catch (int) { DoubleBusFault("reset exception"); } @@ -399,21 +419,62 @@ MPU680x0Device::ExceptionReset() return used_cycle - cycle_start; } -// 例外処理 (リセット・割り込み以外) を行う。 -// vector はベクタ番号。 +// 汎用の例外処理を行う。vector はベクタ番号。 +// ただし以下の例外はここではなくそれぞれ固有のを呼ぶこと。 +// o リセット例外は ExceptionReset() +// o 割り込み例外は ExceptionInterrupt() +// o TRAP#15 例外は ExceptionTrap15() +// o バスエラー・アドレスエラーは ExceptionBuserr() void MPU680x0Device::Exception(int vector) { - // バスエラー・アドレスエラーは別で処理。 - if (vector <= M68K::EXCEP_ADDRERR) { - ExceptionBuserr(vector); - return; + uint32 inst = BranchHistory_m680x0::Exception | vector; + ExceptionGeneric(vector, inst); +} + +// TRAP #15 の例外処理を行う。 +// 処理自体は汎用の Exception() とまったく同じだが、 +// IOCS コールの時には分岐履歴の出力が変わるため入口を分けてある。 +void +MPU680x0Device::ExceptionTrap15() +{ + // 今発生した TRAP #15 が IOCS コールっぽいかどうか調べる。 + // + // Human68k 方面をやってる時は TRAP #15 は IOCS コールで表示してほしい。 + // 一方、NetBSD のカーネル内では TRAP #15 は IOCS コールではない別用途 + // なので IOCS コールかのように表示されるのは紛らわしい。さらに言えば + // NetBSD でもブートローダの時点はまだ IOCS コールを使うので、出来れば + // 今呼ばれたこれが IOCS コールなのか、そうでない TRAP #15 なのかは + // 区別したい。 + // + // IOCS コールかどうかは厳密には分からないが、これが呼ばれた時点で + // ワークエリアの $cbc == 0x03、$cbe == 0xff であれば IOCS コールと + // 判定することにする。 + // $cbc は MPU 種別で 3:68030。 + // $cbe は MMU 有無で 0xff:有り。 + // 今の所 68030/MMU有り しかサポートしていないので。 + // だめならまた考える。 + + const int vector = M68K::EXCEP_TRAP15; + uint32 inst; + if (mainbus->Peek8(0xcbc) == 0x03 && mainbus->Peek8(0xcbe) == 0xff) { + // IOCS コールっぽい + inst = BranchHistory_m680x0::IOCSCall | (reg.D[0] & 0xff); + } else { + // TRAP#15 っぽい + inst = BranchHistory_m680x0::Exception | vector; } + ExceptionGeneric(vector, inst); +} +// 汎用の例外処理本体。 +void +MPU680x0Device::ExceptionGeneric(int vector, uint32 inst) +{ // 例外履歴に記録 (例外発生はブランチ履歴にも記録) uint32 from = ppc | (IsSuper() ? 1 : 0); - exhist.AddEntry(from, 0, 0x8000 | vector); - brhist.AddEntry(from, 0, 0x8000 | vector); + exhist.AddEntry(from, 0, inst); + brhist.AddEntry(from, 0, inst); // デバッガに通知 (例外ブレーク用) debugger->NotifyExceptionMain(vector); @@ -452,7 +513,7 @@ MPU680x0Device::Exception(int vector) } // ベクタにジャンプ - JumpVector(vector); + JumpVector(vector, false); } catch (int vector2) { assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector=%d", vector); @@ -471,8 +532,9 @@ MPU680x0Device::ExceptionBuserr(int vect // 例外履歴に記録 (例外発生はブランチ履歴にも記録) uint32 from = ppc | (IsSuper() ? 1 : 0); - exhist.AddEntry(from, 0, 0x8000 | vector); - brhist.AddEntry(from, 0, 0x8000 | vector); + uint32 inst = BranchHistory_m680x0::Exception | vector; + exhist.AddEntry(from, 0, inst); + brhist.AddEntry(from, 0, inst); // デバッガに通知 (例外ブレーク用) debugger->NotifyExceptionMain(vector); @@ -497,10 +559,11 @@ MPU680x0Device::ExceptionBuserr(int vect // 使えればショートを使うのだが、ここでは全部ロングを使う。 GenerateExframeB(vector, sr); // ベクタにジャンプ - JumpVector(vector); + JumpVector(vector, true); } catch (int vector2) { - // ここに来るのはバスエラーだけのはず - assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector=%d", vector); + // ここに来るのはバスエラーかアドレスエラーだけのはず + assertmsg(vector2 == M68K::EXCEP_BUSERR || + vector2 == M68K::EXCEP_ADDRERR, "vector=%d", vector); // バスエラー・アドレスエラー処理中のバスエラーは // ダブルバスフォールト。 @@ -510,10 +573,11 @@ MPU680x0Device::ExceptionBuserr(int vect // 割り込み例外処理を行う。 // 割り込みの受け付けとレベルのチェック等の後、次命令境界で呼ばれるやつ。 -int +// この処理にかかる時間 [nsec] を返す。 +uint64 MPU680x0Device::ExceptionInterrupt() { - int cycle_start = used_cycle; + uint64 cycle_start = used_cycle; int level = intr_level; // ユーザーズマニュアル(日本語) p.399 図8-5 の中ほど @@ -524,14 +588,23 @@ MPU680x0Device::ExceptionInterrupt() ChangeState(CPU_STATE_NORMAL); // 割り込みアクノリッジサイクルを実行 - int vector = interruptdev->InterruptAcknowledge(level); - if (vector == -1) { - vector = 24 + level; + uint32 vector; + busdata bd = interruptdev->InterruptAcknowledge(level); + if (__predict_false(bd.IsBusErr())) { + // 外部回路がバスエラーを返すと Spurious を発行する。(7.4.1.3) + vector = M68K::EXCEP_SPURIOUS; + } else { + vector = bd.Data(); + if ((int32)vector < 0) { + // オートベクタ要求 (7.4.1.2) + // (負数(-1)ならオートベクタということにしている) + vector = M68K::EXCEP_LEVEL_BASE + level; + } } // 例外履歴に記録 (例外発生はブランチ履歴にも記録) uint32 from = ppc | (IsSuper() ? 1 : 0); - uint32 inst = 0x8000 | (level << 8) | vector; + uint32 inst = BranchHistory_m680x0::Exception | vector; exhist.AddEntry(from, 0, inst); brhist.AddEntry(from, 0, inst); // デバッガに通知 (例外ブレーク用) @@ -553,7 +626,7 @@ MPU680x0Device::ExceptionInterrupt() GenerateExframe0(vector, reg.pc, oldsr); // ベクタにジャンプ - JumpVector(vector); + JumpVector(vector, false); } catch (int vector2) { assertmsg(vector2 == M68K::EXCEP_BUSERR, "vector=%d", vector); @@ -561,7 +634,7 @@ MPU680x0Device::ExceptionInterrupt() ExceptionBuserr(vector2); } - return used_cycle - cycle_start; + return (used_cycle - cycle_start) * clock_nsec + bd.GetWait(); } // RTE 命令。