--- nono/vm/mpu680x0.cpp 2026/04/29 17:05:24 1.1.1.16 +++ nono/vm/mpu680x0.cpp 2026/04/29 17:05:28 1.1.1.17 @@ -14,11 +14,11 @@ #include "interrupt.h" #include "m68030cache.h" #include "mainapp.h" +#include "mainbus.h" #include "scheduler.h" #include "syncer.h" #include "uimessage.h" #include "vectortable.h" -#include static Syncer *gSyncer; @@ -35,28 +35,23 @@ MPU680x0Device::MPU680x0Device() fpu_init(); // キャッシュ icache.reset(new m68030Cache()); + dcache.reset(new m68030Cache()); - // 割り込みイベントコールバック設定 - intr_event.func = ToEventCallback(&MPU680x0Device::InterruptCallback); - intr_event.Regist(GetName() + " Interrupt"); + // 例外カウンタ + excep_counter.resize(256); - // レジスタモニター + // レジスタモニター (サイズは Init で確定する) reg_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateReg); - reg_monitor.SetSize(78, 19); reg_monitor.Regist(ID_MONITOR_MPUREG); // ATC モニター atc_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateATC); -#if !defined(ATC_SINGLE) - atc_monitor.SetSize(135, m68030ATCTable::LINES + 4); -#else - atc_monitor.SetSize(32, 24); -#endif + atc_monitor.SetSize(114, m68030ATCTable::LINES + 6); atc_monitor.Regist(ID_MONITOR_MPUATC); // キャッシュモニター cache_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateCache); - cache_monitor.SetSize(48, 20); + cache_monitor.SetSize(48, 20 * 2 + 1); cache_monitor.Regist(ID_MONITOR_MPUCACHE); SetTrace(false); @@ -116,8 +111,16 @@ MPU680x0Device::Init() has_fpu = 1; } + // レジスタモニタに FPU を載せるか。 + reg_monitor.SetSize(78, 10 + (has_fpu ? 9 : 0)); + vectortable = GetVectorTable(); + // 割り込みイベントコールバック設定 + intr_event.func = ToEventCallback(&MPU680x0Device::InterruptCallback); + intr_event.SetName(GetName() + " Interrupt"); + scheduler->RegistEvent(intr_event); + return true; } @@ -281,8 +284,8 @@ MPU680x0Device::Interrupt(int level) void MPU680x0Device::InterruptCallback(Event& ev) { - int level = ev.code; - int mask = reg.intr_mask; + uint level = ev.code; + uint mask = reg.intr_mask; // 信号線が 2 クロック期間安定していること云々は省略 @@ -397,7 +400,7 @@ MPU680x0Device::MonitorUpdateReg(Monitor // データレジスタ、アドレスレジスタ for (int i = 0; i < 4; i++) { - screen.Print(0, i, "D%d:%08x D%d:%08x A%d:%08x A%d:%08x", + screen.Print(0, i, "D%u:%08x D%u:%08x A%u:%08x A%u:%08x", i + 0, tmp.R[i], i + 4, tmp.R[i + 4], i + 0, tmp.R[i + 8], @@ -437,7 +440,7 @@ MPU680x0Device::MonitorUpdateReg(Monitor // VBR:01234567 x = 66; screen.Print(x, 0, "PC: %08x", tmp_ppc); - screen.Print(x, 1, "SFC:%d DFC:%d", tmp.GetSFC(), tmp.GetDFC()); + screen.Print(x, 1, "SFC:%u DFC:%u", tmp.GetSFC(), tmp.GetDFC()); screen.Print(x, 2, "VBR:%08x", tmp.vbr); switch (tmp_state) { @@ -451,7 +454,7 @@ MPU680x0Device::MonitorUpdateReg(Monitor screen.Puts(50, 3, TA::On, "State: Double Bus Fault"); break; default: - screen.Print(50, 3, TA::On, "corrupted cpu_state=%d", tmp_state); + screen.Print(50, 3, TA::On, "corrupted cpu_state=%u", tmp_state); break; } @@ -461,12 +464,15 @@ MPU680x0Device::MonitorUpdateReg(Monitor screen.Print(0, y, "CACR:%08x(", tmp.cacr); screen.Puts(14, y, TA::OnOff(tmp.cacr & M68K::CACR_WA), "WA"); screen.Puts(17, y, TA::OnOff(tmp.cacr & M68K::CACR_DBE), "DBE"); - screen.Puts(21, y, TA::OnOff(tmp.cacr & M68K::CACR_FD), "FD"); - screen.Puts(24, y, TA::OnOff(tmp.cacr & M68K::CACR_ED), "ED"); - screen.Puts(27, y, TA::OnOff(tmp.cacr & M68K::CACR_IBE), "IBE"); - screen.Puts(31, y, TA::OnOff(tmp.cacr & M68K::CACR_FI), "FI"); - screen.Puts(34, y, TA::OnOff(tmp.cacr & M68K::CACR_EI), "EI"); - screen.Putc(36, y, ')'); + screen.Puts(21, y, "- -"); + screen.Puts(25, y, TA::OnOff(tmp.cacr & M68K::CACR_FD), "FD"); + screen.Puts(28, y, TA::OnOff(tmp.cacr & M68K::CACR_ED), "ED"); + screen.Puts(31, y, "000"); + screen.Puts(35, y, TA::OnOff(tmp.cacr & M68K::CACR_IBE), "IBE"); + screen.Puts(39, y, "- -"); + screen.Puts(43, y, TA::OnOff(tmp.cacr & M68K::CACR_FI), "FI"); + screen.Puts(46, y, TA::OnOff(tmp.cacr & M68K::CACR_EI), "EI"); + screen.Putc(48, y, ')'); screen.Print(50, y, "CAAR:%08x", tmp.caar); y++; @@ -479,7 +485,7 @@ MPU680x0Device::MonitorUpdateReg(Monitor for (int i = 0; i < 2; i++) { uint32 tt = tmp.tt[i]; screen.Print(x, y + i, - "TT%d:%08x(%c%c%c Addr=$%02x Mask=$%02x FC=%d FCMask=%d)", + "TT%u:%08x(%c%c%c Addr=$%02x Mask=$%02x FC=%u FCMask=%u)", i, tt, (tt & m68030TT::E) ? 'E' : '-', (tt & m68030TT::CI) ? 'C' : '-', @@ -515,14 +521,25 @@ MPU680x0Device::MonitorUpdateReg(Monitor screen.Puts(x, y, "- -"); x += 3; screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::T) | 'T'); - screen.Print(x, y, "---N=%d)", tmp.mmusr & m68030MMUSR::N); + screen.Print(x, y, "---N=%u)", tmp.mmusr & m68030MMUSR::N); y++; // FPU + if (has_fpu != 0) { + MonitorUpdateFPU(screen, y, tmp); + } +} + +// モニタ (FPU) +void +MPU680x0Device::MonitorUpdateFPU(TextScreen& screen, int y, m68kreg& tmp) +{ + int x; + x = 0; screen.Puts(x, y++, ""); for (int i = 0; i < 8; i++) { - screen.Print(x, y + i, "FP%d:%04x_%08x_%08x (%-20s)", + screen.Print(x, y + i, "FP%u:%04x_%08x_%08x (%-20s)", i, tmp.fpframe.fpf_regs[i * 3 + 0] >> 16, tmp.fpframe.fpf_regs[i * 3 + 1], @@ -584,12 +601,23 @@ MPU680x0Device::MonitorUpdateReg(Monitor screen.Print(x, y++, "FPIAR:%08x", tmp.fpframe.fpf_fpiar); } +static int +sort_atc(const void *a, const void *b) +{ + const uint32 *ua = (const uint32 *)a; + const uint32 *ub = (const uint32 *)b; + return *ub - *ua; +} + + // モニター更新 (ATC) void MPU680x0Device::MonitorUpdateATC(Monitor *, TextScreen& screen) { m68030ATCTable *table; -#if !defined(ATC_SINGLE) + + screen.Clear(); + struct { int fc; const char *name; @@ -600,43 +628,35 @@ MPU680x0Device::MonitorUpdateATC(Monitor { 6, "Super/Program" }, }; - screen.Clear(); - - // 行数は左端にだけ - for (int i = 0; i < m68030ATCTable::LINES; i++) { - screen.Print(0, i + 2, "%02d:", i); - } - - for (int t = 0; t < countof(fclist); t++) { - int x = t * 33 + 4; + for (uint t = 0; t < countof(fclist); t++) { + int x = t * 29; int fc = fclist[t].fc; const char *name = fclist[t].name; - double r; - int i; - table = &atc.tables[fc]; - screen.Print(x, 0, "FC=%d %s", fc, name); - screen.Puts(x, 1, "LAddr PAddr Flag Hit% Age"); - // 先に統計用の母数を計算 - uint64 total = 0; - for (i = 0; i < countof(table->hit); i++) { - total += table->hit[i]; + table = atc.fctables[fc]; + screen.Print(x, 0, "FC=%u %s", fc, name); + screen.Puts(x, 1, "No. LAddr PAddr Flag"); + + std::array sbuf; + for (uint i = 0; i < m68030ATCTable::LINES; i++) { + sbuf[i * 2 + 0] = table->line[i].age; + sbuf[i * 2 + 1] = i; } - total += table->hit_head; + qsort(&sbuf[0], m68030ATCTable::LINES, sizeof(uint32) * 2, sort_atc); // エントリ表示 - for (i = 0; i < countof(table->line); i++) { - m68030ATCLine& a = table->line[i]; + for (uint i = 0; i < m68030ATCTable::LINES; i++) { + int idx = sbuf[i * 2 + 1]; + m68030ATCLine& a = table->line[idx]; TA attr; if (a.IsInvalid()) { attr = TA::Disable; - } else if (table->head == &a) { - attr = TA::Em; } else { attr = TA::Normal; } - screen.Print(x, i + 2, attr, "%08x %08x %c%c%c%c", + screen.Print(x, i + 2, attr, "[%02x] %08x %08x %c%c%c%c", + idx, a.GetLAddr(), a.GetPAddr(), a.IsBusError() ? 'B' : '-', @@ -644,62 +664,98 @@ MPU680x0Device::MonitorUpdateATC(Monitor a.IsWProtect() ? 'P' : '-', a.IsModified() ? 'M' : '-'); } - // ヘッドのヒット率 - screen.Puts(x + 0, i + 2, "head"); - r = (double)table->hit_head * 100 / total; - if (!std::isnan(r)) { - screen.Print(x + 5, i + 2, "%4.1f%%", r); - } - // 配列のヒット率とミス率 - screen.Puts(x + 18, i + 2, "miss"); - for (i = 0; i < countof(table->hit); i++) { - r = (double)(table->hit[i] * 100) / total; - if (!std::isnan(r)) { - screen.Print(x + 23, i + 2, "%4.1f%%", r); + + // ヒット率とミス率。 + uint64 total = 0; + uint64 tthit = 0; + uint64 miss = 0; + 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); + total += s.total; + tthit += s.tthit; + miss += s.miss; } + tt_once_hit = table->atchist_tt_once_hit; } - // カウンタ - // 分かりやすさのため最大値からの差(距離)を表示 - for (i = 0; i < countof(table->line); i++) { - m68030ATCLine& a = table->line[i]; - if (!a.IsInvalid()) { - screen.Print(x + 28, i + 2, "%4d", table->head->age - a.age); - } + + int y = m68030ATCTable::LINES + 2; + screen.Puts(x + 1, y, "TT Match"); + if (tt_once_hit) { + screen.Print(x + 10, y, "%5.1f%%", (double)(tthit * 100) / total); + } else { + screen.Puts(x + 12, y, "0"); } - } + y++; - screen.Print(0, 25, - "Note: Real 68030's ATC is 22 entries, FC-mixed. " - "This FC-separated one is by nono."); -#else - m68030ATCLine *a; - int i; + screen.Puts(x + 1, y, "ATC Match"); + if (total == 0) { + screen.Puts(x + 12, y, "0"); + } else { + screen.Print(x + 10, y, "%5.1f%%", + (double)((total - miss - tthit) * 100) / total); + } + y++; - table = &atc.tables[0]; - screen.Clear(); + screen.Puts(x + 1, y, "ATC Miss"); + if (miss == 0) { + screen.Puts(x + 12, y, "0"); + } else { + screen.Print(x + 10, y, "%5.1f%%", (double)(miss * 100) / total); + } + y++; + } - screen.Puts(0, 0, "No. FC LAddr PAddr Flag"); - for (a = table->head, i = 0; a; a = a->next, i++) { - screen.Print(0, i + 1, "%02d:", i); - if (!a->IsInvalid()) { - char dp; - switch (a->fc & 3) { - case 1: dp = 'D'; break; - case 2: dp = 'P'; break; - default: dp = '?'; break; + screen.Print(0, m68030ATCTable::LINES + 5, + "Note: The real 68030 ATC is single FC-mixed 22-entries. " + "This ATC with FC-separated %u-entries is by nono.", + m68030ATCTable::LINES); +} + +// 統計情報を更新 (Syncer から呼ばれる) +void +MPU680x0Device::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; } - screen.Print(4, i + 1, "%d(%c%c):%08x %08x %c%c%c%c", - a->fc, - (a->fc & 4) ? 'S' : 'U', - dp, - a->GetLAddr(), a->GetPAddr(), - a->IsBusError() ? 'B' : '-', - a->IsCInhibit() ? 'C' : '-', - a->IsWProtect() ? 'W' : '-', - a->IsModified() ? 'M' : '-'); } } -#endif + + // キャッシュのヒット率。 + icache->CalcStat(); + dcache->CalcStat(); +} + +static char +icache_fc2str(const m68030CacheLine& l) +{ + return l.IsSuper() ? 'S' : 'U'; +} + +static char +dcache_fc2str(const m68030CacheLine& l) +{ + return "0U234S67"[l.GetFC()]; } // モニタ更新 (キャッシュ) @@ -707,34 +763,66 @@ void MPU680x0Device::MonitorUpdateCache(Monitor *, TextScreen& screen) { int y; + screen.Clear(); - // + // // 01234567890123456789012345678901234567890123456789 // Tag Address Data // S.012345'00: 00000000 00000000 -------- 00000000 - screen.Print(0, 0, ""); - screen.Print(0, 1, "Tag Address Data"); + y = 0; + screen.Puts(0, y++, ""); + y = MonitorUpdateCache1(screen, y, icache.get(), icache_fc2str); + + y++; + screen.Puts(0, y++, ""); + MonitorUpdateCache1(screen, y, dcache.get(), dcache_fc2str); +} + +// モニタ更新 (キャッシュ片方の下請け) +int +MPU680x0Device::MonitorUpdateCache1(TextScreen& screen, int y, + m68030Cache *cache, char (*fc2str)(const m68030CacheLine&)) +{ + screen.Puts(0, y, "Tag Address Data[0]"); + screen.Puts(13 + 1 * 9, y, "[1]"); + screen.Puts(13 + 2 * 9, y, "[2]"); + screen.Puts(13 + 3 * 9, y, "[3]"); + y++; - y = 2; - for (int idx = 0; idx < icache->line.size(); idx++) { - auto& l = icache->line[idx]; - screen.Print(0, y, "%c.%06x'%x0:", - (l.IsSuper() ? 'S' : 'U'), - (l.TagAddr() >> 8), - idx); + TA attr = cache->enable ? TA::Normal : TA::Disable; + for (int idx = 0; idx < cache->line.size(); idx++) { + auto& l = cache->line[idx]; + screen.Print(0, y, attr, "%c.%06x'%x0:", + fc2str(l), (l.TagAddr() >> 8), idx); for (int i = 0; i < 4; i++) { if (l.valid[i]) { - screen.Print(13 + 9 * i, y, "%08x", l.data[i]); + screen.Print(13 + 9 * i, y, attr, "%08x", l.data[i]); } else { - screen.Print(13 + 9 * i, y, TA::Disable, "--------"); + screen.Puts(13 + 9 * i, y, TA::Disable, "--------"); } } y++; } + // 統計。 + float hit = 0.0; + { + std::unique_lock lock(cache->hist_mtx); + int len = cache->hist.Length(); + if (len != 0) { + for (int i = 0; i < cache->hist.Length(); i++) { + const auto& s = cache->hist.Peek(i); + hit += s.hit; + } + hit /= len; + } + } + y++; - screen.Print(0, y, " Not Implemented"); + screen.Print(1, y++, attr, "Cache Read Hit %5.1f%%", hit); + + return y; }