--- nono/vm/mpu680x0.cpp 2026/04/29 17:05:41 1.1.1.20 +++ nono/vm/mpu680x0.cpp 2026/04/29 17:05:59 1.1.1.22 @@ -23,6 +23,7 @@ #include "mpu680x0.h" #include "config.h" #include "debugger.h" +#include "event.h" #include "exttostr.h" #include "interrupt.h" #include "m68030cache.h" @@ -70,8 +71,6 @@ MPU680x0Device::MPU680x0Device() // 例外カウンタ excep_counter.resize(256); - - SetTrace(false); } // デストラクタ @@ -149,6 +148,25 @@ MPU680x0Device::Init() break; } + // 機種ごとの NetBSD カーネルエントリポイント。 + // 履歴で TRAP #0 がシステムコールかどうかの判定に使う。 + switch (gMainApp.GetVMType()) { + case VMType::LUNA1: + netbsd_entrypoint = 0xc000; + break; + case VMType::X68030: + netbsd_entrypoint = 0x0022; + break; + case VMType::NEWS: + netbsd_entrypoint = 0x0000; + break; + case VMType::VIRT68K: + netbsd_entrypoint = 0x2000; + break; + default: + __unreachable(); + } + interruptdev = GetInterruptDevice(); vectortable = GetVectorTable(); @@ -160,10 +178,11 @@ MPU680x0Device::Init() // その後 /IPEND をアサートするのはその次のアップエッジ。 // 合わせると、IPL0-2 が変化してから /IPEND がアサートされるまでは // 2 ~ 3 クロックかかる。(Figure 8-4) - intr_event.func = ToEventCallback(&MPU680x0Device::InterruptCallback); - intr_event.time = 3 * clock_nsec; - intr_event.SetName(GetName() + " Interrupt"); - scheduler->RegistEvent(intr_event); + auto evman = GetEventManager(); + intr_event = evman->Regist(this, + ToEventCallback(&MPU680x0Device::InterruptCallback), + GetName() + " Interrupt"); + intr_event->time = 3 * clock_tsec; // レジスタモニタの行数。 int height = 9; @@ -214,16 +233,16 @@ MPU680x0Device::ResetHard(bool poweron) // SP, PC をフェッチするという順序にしないといけないため、リセット例外が // 起動するまでに時間がかかるというところを都合よく真似ておく。 - exec_event.func = ToEventCallback(&MPU680x0Device::ResetCallback); - exec_event.time = 520 * clock_nsec; - exec_event.SetName(GetName() + " Reset"); + exec_event->SetCallback(&MPU680x0Device::ResetCallback); + exec_event->time = 520 * clock_tsec; + exec_event->SetName(GetName() + " Reset"); scheduler->RestartEvent(exec_event); } // リセット例外コールバック。 // MPU リセットから規定時間後に呼ばれる。 void -MPU680x0Device::ResetCallback(Event& ev) +MPU680x0Device::ResetCallback(Event *ev) { resetting = false; intr_pending = false; @@ -237,9 +256,9 @@ MPU680x0Device::ResetCallback(Event& ev) // 以降は通常走行。 if (cpu_state == CPU_STATE_NORMAL) { - exec_event.func = exec_short; - exec_event.time = cycle * clock_nsec; - exec_event.SetName(GetName() + " Execute"); + exec_event->SetCallback(exec_short); + exec_event->time = cycle * clock_tsec; + exec_event->SetName(GetName() + " Execute"); scheduler->RestartEvent(exec_event); } else { // リセット例外でダブルバスフォールトならここで停止。 @@ -251,7 +270,7 @@ MPU680x0Device::ResetCallback(Event& ev) // ここでは割り込みをチェックし、必要なら割り込み処理を起動、 // そうでなければ通常の命令実行を行う。 void -MPU680x0Device::ExecFull(Event& ev) +MPU680x0Device::ExecFull(Event *ev) { if (cpu_state == CPU_STATE_HALT) { // HALT なら停止。 @@ -272,12 +291,12 @@ MPU680x0Device::ExecFull(Event& ev) intr_pending = false; // 前の命令実行後となるここで割り込みを起動 - ev.time = ExceptionInterrupt(); + ev->time = ExceptionInterrupt(); scheduler->StartEvent(ev); } else { if (cpu_state == CPU_STATE_NORMAL) { // コールバックを通常処理に戻してから... - exec_event.func = exec_short; + exec_event->SetCallback(exec_short); // この場で次の命令を実行。 ExecShort(ev); @@ -289,7 +308,7 @@ MPU680x0Device::ExecFull(Event& ev) // トレース実行 void -MPU680x0Device::ExecTrace(Event& ev) +MPU680x0Device::ExecTrace(Event *ev) { debugger->ExecMain(); ExecFull(ev); @@ -306,11 +325,11 @@ MPU680x0Device::SetTrace(bool enable) exec_short = ToEventCallback(&MPU680x0Device::ExecShort); exec_full = ToEventCallback(&MPU680x0Device::ExecFull); } - exec_event.func = exec_full; + exec_event->SetCallback(exec_full); // STOP 命令中にオンにするには、停止しているイベントを起こす。 // STOP 命令中にオフにする場合は、何もしなくていい。 - if (enable && exec_event.IsRunning() == false) { + if (enable && exec_event->IsRunning() == false) { scheduler->StartEvent(exec_event); } } @@ -321,8 +340,8 @@ MPU680x0Device::Interrupt(int level) { assertmsg(0 <= level && level <= 7, "level=%d", level); - intr_event.code = level; - if (intr_event.IsRunning() == false) { + intr_event->code = level; + if (intr_event->IsRunning() == false) { scheduler->StartEvent(intr_event); } } @@ -339,9 +358,9 @@ MPU680x0Device::Interrupt(int level) // 外部割り込みが来たので、レベル比較などをして、必要なら次の命令境界で // 割り込みチェックを行うようにするところ。 void -MPU680x0Device::InterruptCallback(Event& ev) +MPU680x0Device::InterruptCallback(Event *ev) { - uint level = ev.code; + uint level = ev->code; uint mask = reg.intr_mask; // 信号線が 2 クロック期間安定していること云々は省略 @@ -368,7 +387,7 @@ MPU680x0Device::InterruptCallback(Event& // STOP 状態だったらここでイベントを開始する if (cpu_state == CPU_STATE_STOP) { // STOP から抜ける時間? - exec_event.time = 1 * clock_nsec; + exec_event->time = 1 * clock_tsec; scheduler->StartEvent(exec_event); } } @@ -378,7 +397,7 @@ MPU680x0Device::InterruptCallback(Event& void MPU680x0Device::MakeNextFull() { - exec_event.func = exec_full; + exec_event->SetCallback(exec_full); } // A-Line 命令エミュレーションのコールバックを指定。 @@ -403,7 +422,6 @@ MPU680x0Device::SetFLineCallback(bool (* void MPU680x0Device::ops_reset() { - auto mainbus = GetMainbusDevice(); mainbus->ResetByMPU(); } @@ -417,12 +435,12 @@ MPU680x0Device::Halt() MakeNextFull(); // UI に通知 (メッセージボックス表示とか) - UIMessage::Post(UIMessage::HALT); + gMainApp.GetUIMessage()->Post(UIMessage::HALT); } // モニター更新の下請け (レジスタ共通部分) void -MPU680x0Device::MonitorUpdateRegCommon(TextScreen& screen, +MPU680x0Device::MonitorScreenRegCommon(TextScreen& screen, m68kreg& tmp, uint32 tmp_ppc, uint32 tmp_state) { int x; @@ -490,7 +508,7 @@ MPU680x0Device::MonitorUpdateRegCommon(T // モニタ更新の下請け (FPU) int -MPU680x0Device::MonitorUpdateFPU(TextScreen& screen, int y, m68kreg& tmp) +MPU680x0Device::MonitorScreenFPU(TextScreen& screen, int y, m68kreg& tmp) { int x; @@ -586,17 +604,20 @@ MPU68030Device::MPU68030Device() // レジスタモニター (サイズは Init で確定する) reg_monitor = gMonitorManager->Regist(ID_MONITOR_MPUREG, this); - reg_monitor->func = ToMonitorCallback(&MPU68030Device::MonitorUpdateReg); + reg_monitor->SetCallback(&MPU68030Device::MonitorScreenReg); // ATC モニター atc_monitor = gMonitorManager->Regist(ID_MONITOR_MPUATC, this); - atc_monitor->func = ToMonitorCallback(&MPU68030Device::MonitorUpdateATC); + atc_monitor->SetCallback(&MPU68030Device::MonitorScreenATC); +#if defined(M68030_CUSTOM_ATC) atc_monitor->SetSize(114, m68030ATCTable::LINES + 6); +#else + atc_monitor->SetSize(45, m68030ATCTable::LINES + 2); +#endif // キャッシュモニター cache_monitor = gMonitorManager->Regist(ID_MONITOR_MPUCACHE, this); - cache_monitor->func = - ToMonitorCallback(&MPU68030Device::MonitorUpdateCache); + cache_monitor->SetCallback(&MPU68030Device::MonitorScreenCache); cache_monitor->SetSize(48, 20 * 2 + 1); } @@ -613,7 +634,18 @@ MPU68030Device::SetLogLevel(int loglevel atc.SetLogLevel(loglevel_); } -static int +void +MPU68030Device::ResetHard(bool poweron) +{ + inherited::ResetHard(poweron); + + if (poweron) { + // 電源(再)投入で統計情報もクリア。 + atc.ResetStat(); + } +} + +static int __unused sort_atc(const void *a, const void *b) { const uint32 *ua = (const uint32 *)a; @@ -623,7 +655,7 @@ sort_atc(const void *a, const void *b) // モニター更新 (レジスタ) void -MPU68030Device::MonitorUpdateReg(Monitor *, TextScreen& screen) +MPU68030Device::MonitorScreenReg(Monitor *, TextScreen& screen) { m68kreg tmp; uint32 tmp_ppc; @@ -639,21 +671,21 @@ MPU68030Device::MonitorUpdateReg(Monitor tmp_state = cpu_state; // 共通部分。 - MonitorUpdateRegCommon(screen, tmp, tmp_ppc, tmp_state); + MonitorScreenRegCommon(screen, tmp, tmp_ppc, tmp_state); // Cache/MMU 部分。 y = 4; - y = MonitorUpdateMMU(screen, y, tmp); + y = MonitorScreenMMU(screen, y, tmp); // FPU はある時だけ表示。 if (HasFPU()) { - MonitorUpdateFPU(screen, y, tmp); + MonitorScreenFPU(screen, y, tmp); } } // モニター更新の下請け (Cache/MMU 部分) int -MPU68030Device::MonitorUpdateMMU(TextScreen& screen, int y, m68kreg& tmp) +MPU68030Device::MonitorScreenMMU(TextScreen& screen, int y, m68kreg& tmp) { int x; @@ -727,12 +759,15 @@ MPU68030Device::MonitorUpdateMMU(TextScr // モニター更新 (68030 ATC) void -MPU68030Device::MonitorUpdateATC(Monitor *, TextScreen& screen) +MPU68030Device::MonitorScreenATC(Monitor *, TextScreen& screen) { - m68030ATCTable *table; + TA e_attr = mmu_tc.e ? TA::Normal : TA::Disable; screen.Clear(); +#if defined(M68030_CUSTOM_ATC) + m68030ATCTable_Custom *table; + struct { int fc; const char *name; @@ -748,7 +783,7 @@ MPU68030Device::MonitorUpdateATC(Monitor int fc = fclist[t].fc; const char *name = fclist[t].name; - table = atc.fctables[fc]; + table = atc.GetTable(fc); screen.Print(x, 0, "FC=%u %s", fc, name); screen.Puts(x, 1, "No. LAddr PAddr Flag"); @@ -763,15 +798,15 @@ MPU68030Device::MonitorUpdateATC(Monitor for (uint i = 0; i < m68030ATCTable::LINES; i++) { int idx = sbuf[i * 2 + 1]; m68030ATCLine& a = table->line[idx]; - TA attr; + TA v_attr; if (a.IsInvalid()) { - attr = TA::Disable; + v_attr = TA::Disable; } else { - attr = TA::Normal; + v_attr = TA::Normal; } - screen.Print(x, i + 2, attr, "[%02x] %08x %08x %c%c%c%c", - idx, + screen.Print(x, i + 2, e_attr, "[%02x]", idx); + screen.Print(x + 5, i + 2, v_attr, "%08x %08x %c%c%c%c", a.GetLAddr(), a.GetPAddr(), a.IsBusError() ? 'B' : '-', @@ -787,39 +822,41 @@ MPU68030Device::MonitorUpdateATC(Monitor bool tt_once_hit; { - std::unique_lock lock(table->atchist_mtx); - for (int i = 0; i < table->atchist.Length(); i++) { - const auto& s = table->atchist.Peek(i); + std::lock_guard lock(table->hist_mtx); + for (int i = 0; i < table->hist.Length(); i++) { + const auto& s = table->hist.Peek(i); total += s.total; tthit += s.tthit; miss += s.miss; } - tt_once_hit = table->atchist_tt_once_hit; + tt_once_hit = table->hist_tt_once_hit; } int y = m68030ATCTable::LINES + 2; - screen.Puts(x + 1, y, "TT Match"); + screen.Puts(x + 1, y, e_attr, "TT Match"); if (tt_once_hit) { - screen.Print(x + 10, y, "%5.1f%%", (double)(tthit * 100) / total); + screen.Print(x + 10, y, e_attr, "%5.1f%%", + (double)(tthit * 100) / total); } else { - screen.Puts(x + 12, y, "0"); + screen.Puts(x + 12, y, e_attr, "0"); } y++; - screen.Puts(x + 1, y, "ATC Match"); + screen.Puts(x + 1, y, e_attr, "ATC Match"); if (total == 0) { - screen.Puts(x + 12, y, "0"); + screen.Puts(x + 12, y, e_attr, "0"); } else { - screen.Print(x + 10, y, "%5.1f%%", + screen.Print(x + 10, y, e_attr, "%5.1f%%", (double)((total - miss - tthit) * 100) / total); } y++; - screen.Puts(x + 1, y, "ATC Miss"); + screen.Puts(x + 1, y, e_attr, "ATC Miss"); if (miss == 0) { - screen.Puts(x + 12, y, "0"); + screen.Puts(x + 12, y, e_attr, "0"); } else { - screen.Print(x + 10, y, "%5.1f%%", (double)(miss * 100) / total); + screen.Print(x + 10, y, e_attr, "%5.1f%%", + (double)(miss * 100) / total); } y++; } @@ -828,6 +865,54 @@ MPU68030Device::MonitorUpdateATC(Monitor "Note: The real 68030 ATC is single FC-mixed 22-entries. " "This ATC with FC-separated %u-entries is by nono.", m68030ATCTable::LINES); +#else + // 00 [00] SD(5).01234'567 01234'567 ---- + screen.Puts(0, 0, "No Idx FC LAddr PAddr Flag Hit%"); + uint y = 1; + auto table = atc.GetTable(); + for (uint pri = 0; pri < m68030ATCTable::LINES; pri++) { + uint idx = table->GetIdx(pri); + const m68030ATCLine *a = &table->line[idx]; + TA attr; + if (a->IsInvalid()) { + attr = TA::Disable; + } else { + attr = TA::Normal; + } + uint fc = a->GetFC(); + screen.Print(0, y, e_attr, "%2d [%02d]", pri, idx); + screen.Print(8, y++, attr, + "%u(%s).%06x'00 %06x'00 %c%c%c%c", + fc, FCstr(fc), + (a->GetLAddr() >> 8), + (a->GetPAddr() >> 8), + (a->IsBusError() ? 'B' : '-'), + (a->IsCInhibit() ? 'C' : '-'), + (a->IsWProtect() ? 'P' : '-'), + (a->IsModified() ? 'M' : '-')); + } + + // ATC ヒット/ミス率。 + m68030ATCStat tmp = table->stat; + std::array hitrate {}; + float missrate = 0; + // total はヒットとミスの合計。 + uint64 total = tmp.miss; + for (auto v : tmp.hit) { + total += v; + } + if (total != 0) { + for (uint i = 0; i < hitrate.size(); i++) { + hitrate[i] = (float)tmp.hit[i] / total * 100; + } + missrate = (float)tmp.miss / total * 100; + } + y = 1; + for (auto r : hitrate) { + screen.Print(38, y++, e_attr, "%6.2f%%", r); + } + screen.Print(31, y, e_attr, "ATCMiss%6.2f%%", missrate); +#endif } // 統計情報を更新 (Syncer から呼ばれる) @@ -835,26 +920,7 @@ void MPU68030Device::CalcStat() { // ATC のヒット率。 - for (auto& table : atc.tables) { - // ここはロックなしでやってしまう - m68030ATCStat s; - s.total = table->atcstat.total; - s.tthit = table->atcstat.tthit; - s.miss = table->atcstat.miss; - table->atcstat.total = 0; - table->atcstat.tthit = 0; - table->atcstat.miss = 0; - - // こっちはロックする - { - std::unique_lock lock(table->atchist_mtx); - - table->atchist.EnqueueForce(s); - if (__predict_false(s.tthit != 0)) { - table->atchist_tt_once_hit = true; - } - } - } + atc.CalcStat(); // キャッシュのヒット率。 icache->CalcStat(); @@ -875,7 +941,7 @@ dcache_fc2str(const m68030CacheLine& l) // モニタ更新 (68030 キャッシュ) void -MPU68030Device::MonitorUpdateCache(Monitor *, TextScreen& screen) +MPU68030Device::MonitorScreenCache(Monitor *, TextScreen& screen) { int y; @@ -888,16 +954,16 @@ MPU68030Device::MonitorUpdateCache(Monit y = 0; screen.Puts(0, y++, ""); - y = MonitorUpdateCache1(screen, y, icache.get(), icache_fc2str); + y = MonitorScreenCache1(screen, y, icache.get(), icache_fc2str); y++; screen.Puts(0, y++, ""); - MonitorUpdateCache1(screen, y, dcache.get(), dcache_fc2str); + MonitorScreenCache1(screen, y, dcache.get(), dcache_fc2str); } // モニタ更新 (68030 キャッシュ片方の下請け) int -MPU68030Device::MonitorUpdateCache1(TextScreen& screen, int y, +MPU68030Device::MonitorScreenCache1(TextScreen& screen, int y, m68030Cache *cache, char (*fc2str)(const m68030CacheLine&)) { screen.Puts(0, y, "Tag Address Data[0]"); @@ -925,7 +991,7 @@ MPU68030Device::MonitorUpdateCache1(Text // 統計。 float hit = 0.0; { - std::unique_lock lock(cache->hist_mtx); + std::lock_guard lock(cache->hist_mtx); int len = cache->hist.Length(); if (len != 0) { for (int i = 0; i < cache->hist.Length(); i++) { @@ -942,6 +1008,17 @@ MPU68030Device::MonitorUpdateCache1(Text return y; } +/*static*/ const char +MPU68030Device::fcstr[] = + "--\0\0" // 0 + "UD\0\0" // 1 + "UP\0\0" // 2 + "--\0\0" // 3 + "--\0\0" // 4 + "SD\0\0" // 5 + "SP\0\0" // 6 + "--"; // 7 + // // 68040 @@ -967,11 +1044,11 @@ MPU68040Device::MPU68040Device() // レジスタモニター (サイズは Init で確定する) reg_monitor = gMonitorManager->Regist(ID_MONITOR_MPUREG, this); - reg_monitor->func = ToMonitorCallback(&MPU68040Device::MonitorUpdateReg); + reg_monitor->SetCallback(&MPU68040Device::MonitorScreenReg); // ATC モニター (Inst/Data) atc_monitor = gMonitorManager->Regist(ID_MONITOR_MPUATC, this); - atc_monitor->func = ToMonitorCallback(&MPU68040Device::MonitorUpdateATC); + atc_monitor->SetCallback(&MPU68040Device::MonitorScreenATC); atc_monitor->SetSize(125, 34); } @@ -982,7 +1059,7 @@ MPU68040Device::~MPU68040Device() // モニター更新 (レジスタ) void -MPU68040Device::MonitorUpdateReg(Monitor *, TextScreen& screen) +MPU68040Device::MonitorScreenReg(Monitor *, TextScreen& screen) { m68kreg tmp; uint32 tmp_ppc; @@ -1000,22 +1077,22 @@ MPU68040Device::MonitorUpdateReg(Monitor tmp_fpu40 = fpu40; // 共通部分。 - MonitorUpdateRegCommon(screen, tmp, tmp_ppc, tmp_state); + MonitorScreenRegCommon(screen, tmp, tmp_ppc, tmp_state); // Cache/MMU 部分。 y = 4; - y = MonitorUpdateMMU(screen, y, tmp); + y = MonitorScreenMMU(screen, y, tmp); // FPU はある時だけ表示。 if (HasFPU()) { - y = MonitorUpdateFPU(screen, y, tmp); - MonitorUpdateFPU40(screen, y, tmp_fpu40); + y = MonitorScreenFPU(screen, y, tmp); + MonitorScreenFPU40(screen, y, tmp_fpu40); } } // モニター更新の下請け (Cache/MMU 部分) int -MPU68040Device::MonitorUpdateMMU(TextScreen& screen, int y, m68kreg& tmp) +MPU68040Device::MonitorScreenMMU(TextScreen& screen, int y, m68kreg& tmp) { int x; @@ -1052,17 +1129,17 @@ MPU68040Device::MonitorUpdateMMU(TextScr y -= 4; x = 33; - MonitorUpdateTT(screen, x, y++, "ITT0", tmp.itt[0]); - MonitorUpdateTT(screen, x, y++, "ITT1", tmp.itt[1]); - MonitorUpdateTT(screen, x, y++, "DTT0", tmp.dtt[0]); - MonitorUpdateTT(screen, x, y++, "DTT1", tmp.dtt[1]); + MonitorScreenTT(screen, x, y++, "ITT0", tmp.itt[0]); + MonitorScreenTT(screen, x, y++, "ITT1", tmp.itt[1]); + MonitorScreenTT(screen, x, y++, "DTT0", tmp.dtt[0]); + MonitorScreenTT(screen, x, y++, "DTT1", tmp.dtt[1]); return y; } // *TTn 1つ分の下請け。 void -MPU68040Device::MonitorUpdateTT(TextScreen& screen, int x, int y, +MPU68040Device::MonitorScreenTT(TextScreen& screen, int x, int y, const char *name, uint32 tt) { bool e = (tt & m68040TT::E);