--- nono/vm/mpu680x0.cpp 2026/04/29 17:05:17 1.1.1.15 +++ nono/vm/mpu680x0.cpp 2026/04/29 17:05:32 1.1.1.18 @@ -5,52 +5,62 @@ // // -// MPU (MC68030) +// MPU (MC68030/MC68040) // #include "mpu680x0.h" #include "config.h" #include "debugger.h" +#include "exttostr.h" #include "interrupt.h" +#include "m68030cache.h" +#include "m68040mmu.h" #include "mainapp.h" +#include "mainbus.h" #include "scheduler.h" #include "syncer.h" #include "uimessage.h" #include "vectortable.h" -#include "vm.h" -#include static Syncer *gSyncer; +// ブートストラップ。設定に応じてインスタンスを生成して返す。 +// ここではとりあえず 68040 かそれ以外なら 68030 を生成しておき、 +// Init() でもう一度 (機種情報も含めて) チェックする。 +// Init() (か Create()) まで進まないとエラー終了出来ないが、 +// その時点では MainMPU のインスタンスは必要なので。 +MPU680x0Device * +NewMPU680x0Device() +{ + const ConfigItem& item = gConfig->Find("mpu-type"); + std::string mpu_type = item.AsString(); + + // 設定に応じて継承側のクラスを作成する。 + // この段階では 68040 かそれ以外だけ判断する。 + if (mpu_type == "68040") { + return new MPU68040Device(); + } else { + return new MPU68030Device(); + } +} + + +// +// 68030/040 共通クラス +// + // コンストラクタ MPU680x0Device::MPU680x0Device() : inherited() { - SetName("MPU(68030)"); + // MPU 種別は継承側で設定する。設定されないままなら Init() でエラー。 + mpu_type = m680x0MPUType::NONE; - // MMU.xRP.DT == 0 になってはいけない(MMU 構成例外)。 - SetSRP(0x00000001, 0); - SetCRP(0x00000001, 0); // FPU fpu_init(); - // 割り込みイベントコールバック設定 - intr_event.func = ToEventCallback(&MPU680x0Device::InterruptCallback); - intr_event.Regist(GetName() + " Interrupt"); - - // レジスタモニター - reg_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateReg); - reg_monitor.SetSize(79, 16); - reg_monitor.Regist(ID_MONITOR_MPUREG); - - // ATC モニター - atc_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateATC); -#if !defined(ATC_SINGLE) - atc_monitor.SetSize(131, m68030ATCTable::LINES + 3); -#else - atc_monitor.SetSize(31, 24); -#endif - atc_monitor.Regist(ID_MONITOR_MPUATC); + // 例外カウンタ + excep_counter.resize(256); SetTrace(false); } @@ -68,12 +78,67 @@ MPU680x0Device::Init() return false; } + // なる早で、もう一度 MPU 設定を読んで判定。そのついでに FPU も判定。 + const ConfigItem& mputype_item = gConfig->Find("mpu-type"); + std::string mputype = mputype_item.AsString(); + // mpu-type の設定値が不正な場合でも (コンストラクト時点ではまだエラーを + // 報告できない構造なので) 一旦 MPU68030Device インスタンスとしてここに + // 来る。なのでここで改めてチェックしてエラー報告する必要がある。 + // mpu_type (メンバ変数) 自体はすでにセットされている。 + if (mputype != "68030" && mputype != "68040") { + mputype_item.Err(); + return false; + } + + // 機種ごとに MPU/FPU のサポート状況が違う。 + // fpu_type はここでセットする。 + switch (gMainApp.GetVMType()) { + case VMType::LUNA1: + case VMType::NEWS: + // NEWS は元々 68030 モデルのみ。 + // うち NWS-1750 は 68030/68882 固定のようだがここでは 68881 とする。 + // LUNA-I は基本的に 68030/68881 モデルのみ。 + if (mpu_type != m680x0MPUType::M68030) { + mputype_item.Err("Only 68030 can be specified for vmtype=%s", + gMainApp.GetVMTypeStr().c_str()); + return false; + } + fpu_type = m680x0FPUType::M68881; + break; + + case VMType::X68030: + case VMType::VIRT68K: + // 68030/68040 をサポート。mpu_type は継承コンストラクタで設定済み。 + if (mpu_type == m680x0MPUType::M68030) { + // 68030 なら FPU は設定で指定可能。 + const ConfigItem& fputype_item = gConfig->Find("fpu-type"); + std::string fputype = fputype_item.AsString(); + if (fputype == "none" || fputype == "0") { + fpu_type = m680x0FPUType::M68030_NOFPU; + } else if (fputype == "68881" || fputype == "1") { + fpu_type = m680x0FPUType::M68881; + } else { + fputype_item.Err(); + return false; + } + } else { + // 68040 なら FPU は現状 !LC040 固定。 + fpu_type = m680x0FPUType::M68040; + } + break; + + default: + VMPANIC("vmtype=%s not configured", gMainApp.GetVMTypeStr().c_str()); + break; + } + debugger = GetDebugger(); interruptdev = GetInterruptDevice(); + vectortable = GetVectorTable(); // static 関数から参照するため。うーん…。 gSyncer = syncer; - // 割り込み時のディレイ。 + // 割り込みイベントコールバック設定。 // // 外部デバイスが 68030 の IPL0-2 をアサートしてから、68030 がそれを // 認識(確定)するのに 1 ~ 2 クロックかかる。 @@ -81,27 +146,23 @@ 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); - // FPU (6888x) - if (gMainApp.IsX68030()) { - // X68k は設定による - const ConfigItem& item = gConfig->Find("fpu-type"); - std::string type = item.AsString(); - if (type == "none" || type == "0") { - has_fpu = 0; - } else if (type == "68881" || type == "1") { - has_fpu = 1; - } else { - item.Err(); - return false; + // レジスタモニタの行数。 + int height = 9; + if (mpu_type == m680x0MPUType::M68030) { + height++; + } + if (HasFPU()) { + height += 12; + if (GetFPUType().Is4060FPU()) { + height += 3; } - } else { - // LUNA は 68881 固定 (設定は見ない) - has_fpu = 1; } - - vectortable = GetVectorTable(); + reg_monitor->SetSize(78, height); return true; } @@ -197,9 +258,7 @@ MPU680x0Device::ExecFull(Event& ev) intr_pending = false; // 前の命令実行後となるここで割り込みを起動 - int cycle = ExceptionInterrupt(); - - ev.time = cycle * clock_nsec; + ev.time = ExceptionInterrupt(); scheduler->StartEvent(ev); } else { if (cpu_state == CPU_STATE_NORMAL) { @@ -268,19 +327,19 @@ 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 クロック期間安定していること云々は省略 - // ホールトなら何もしない + // 割り込みを起こすかどうかに関わらずここで更新。 + intr_level = level; + + // ホールトならここまで。 if (cpu_state == CPU_STATE_HALT) { return; } - // 割り込みを起こすかどうかに関わらずここで更新 - intr_level = level; - // 割り込み例外を起動するのは // o レベルがマスクより高い // o マスクに関係なく、レベルが下位から 7 に変化した場合 @@ -347,7 +406,8 @@ MPU680x0Device::SetFLineCallback(bool (* void MPU680x0Device::ops_reset() { - gVM->ResetByMPU(); + auto mainbus = GetMainbusDevice(); + mainbus->ResetByMPU(); } // ホールト状態 @@ -363,91 +423,16 @@ MPU680x0Device::Halt() UIMessage::Post(UIMessage::HALT); } -// MPU からのアクセスをエミュレート -uint64 -MPU680x0Device::Read8(uint32 addr) -{ - try { - return read_8(addr); - } catch (...) { - return (uint64)-1; - } -} - -uint64 -MPU680x0Device::Read16(uint32 addr) -{ - try { - return read_16(addr); - } catch (...) { - return (uint64)-1; - } -} - -uint64 -MPU680x0Device::Read32(uint32 addr) -{ - try { - return read_32(addr); - } catch (...) { - return (uint64)-1; - } -} - -uint64 -MPU680x0Device::Write8(uint32 addr, uint32 data) -{ - try { - write_8(addr, data); - return 0; - } catch (...) { - return (uint64)-1; - } -} - -uint64 -MPU680x0Device::Write16(uint32 addr, uint32 data) -{ - try { - write_16(addr, data); - return 0; - } catch (...) { - return (uint64)-1; - } -} - -uint64 -MPU680x0Device::Write32(uint32 addr, uint32 data) -{ - try { - write_32(addr, data); - return 0; - } catch (...) { - return (uint64)-1; - } -} - -// モニター更新 (レジスタウィンドウ) +// モニター更新の下請け (レジスタ共通部分) void -MPU680x0Device::MonitorUpdateReg(Monitor *, TextScreen& screen) +MPU680x0Device::MonitorUpdateRegCommon(TextScreen& screen, + m68kreg& tmp, uint32 tmp_ppc, uint32 tmp_state) { - m68kreg tmp; - uint32 tmp_ppc; - uint32 tmp_state; int x; - int y; - - screen.Clear(); - - // ローカルにコピー - // ただし一部は reg ではなく cpu クラス本体にある - tmp = reg; - tmp_ppc = ppc; - tmp_state = cpu_state; // データレジスタ、アドレスレジスタ 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], @@ -465,76 +450,268 @@ MPU680x0Device::MonitorUpdateReg(Monitor (tmp.ccr.IsV()) ? 'V' : '-', (tmp.ccr.IsC()) ? 'C' : '-'); - // VBR - screen.Print(x, 1, "VBR:%08x", tmp.vbr); - // *SP uint ms = (tmp.s ? 2 : 0) | (tmp.m ? 1 : 0); // T1 T0 S M if (ms == 3) { // Supervisor (Master) mode (A7=MSP, USP/ISP) - screen.Print(x, 2, TA::Disable, "USP:%08x", tmp.usp); - screen.Print(x, 3, TA::Disable, "ISP:%08x", tmp.isp); + screen.Print(x, 1, TA::Disable, "USP:%08x", tmp.usp); + screen.Print(x, 2, TA::Disable, "ISP:%08x", tmp.isp); } else if (ms == 2) { // Supervisor (Interrupt) mode (A7=ISP, USP/MSP) - screen.Print(x, 2, TA::Disable, "USP:%08x", tmp.usp); - screen.Print(x, 3, TA::Disable, "MSP:%08x", tmp.msp); + screen.Print(x, 1, TA::Disable, "USP:%08x", tmp.usp); + screen.Print(x, 2, TA::Disable, "MSP:%08x", tmp.msp); } else { // User mode (A7=USP, ISP/MSP) - screen.Print(x, 2, TA::Disable, "ISP:%08x", tmp.isp); - screen.Print(x, 3, TA::Disable, "MSP:%08x", tmp.msp); + screen.Print(x, 1, TA::Disable, "ISP:%08x", tmp.isp); + screen.Print(x, 2, TA::Disable, "MSP:%08x", tmp.msp); } // 6列目 - // PC: 01234567 - // SFC:1 DFC:1 - // CACR:01234567 - // CAAR:01234567 + // PC: 01234567 + // SFC:1 DFC:1 + // VBR:01234567 x = 66; - screen.Print(x, 0, "PC: %08x", tmp_ppc); - screen.Print(x, 1, "SFC:%d DFC:%d", tmp.sfc, tmp.dfc); - screen.Print(x, 2, "CACR:%08x", tmp.cacr); - screen.Print(x, 3, "CAAR:%08x", tmp.caar); + screen.Print(x, 0, "PC: %08x", tmp_ppc); + screen.Print(x, 1, "SFC:%u DFC:%u", tmp.GetSFC(), tmp.GetDFC()); + screen.Print(x, 2, "VBR:%08x", tmp.vbr); - // XXX どこに置くのがいいか switch (tmp_state) { case CPU_STATE_NORMAL: - screen.Puts(25, 4, "State: Running"); + screen.Puts(50, 3, "State: Running"); break; case CPU_STATE_STOP: - screen.Puts(25, 4, "State: STOP instruction"); + screen.Puts(50, 3, "State: STOP instruction"); break; case CPU_STATE_HALT: - screen.Puts(25, 4, TA::On, "State: Double Bus Fault"); + screen.Puts(50, 3, TA::On, "State: Double Bus Fault"); break; default: - screen.Print(25, 4, TA::On, "corrupted cpu_state=%d", tmp_state); + screen.Print(50, 3, TA::On, "corrupted cpu_state=%u", tmp_state); break; } +} + +// モニタ更新の下請け (FPU) +int +MPU680x0Device::MonitorUpdateFPU(TextScreen& screen, int y, m68kreg& tmp) +{ + int x; + +/* +0123456789012345678901234567890123456789012345678901234567890123456789012345 +FP0:7fff_ffffffff_ffffffff = NAN +FPCR: 0000 BS SN OP OV UF DZ I2 I1 Prec=.X Mode=Minus +FPSR: 00000000 N Z INF NAN Q=$xx BS SN OP OV UF DZ I2 I1 AV AO AU AD AI +FPIAR:00000000 +*/ - // MMU x = 0; + screen.Puts(x, y++, ""); + for (int i = 0; i < 8; i++) { + screen.Print(x, y++, "FP%u:%04x_%08x_%08x = %s", + i, + tmp.fpframe.fpf_regs[i * 3 + 0] >> 16, + tmp.fpframe.fpf_regs[i * 3 + 1], + tmp.fpframe.fpf_regs[i * 3 + 2], + ExtToStr(&tmp.fpframe.fpf_regs[i * 3]).c_str()); + } + + // FPCR + uint32 fpcr = tmp.fpframe.fpf_fpcr; + screen.Puts(0, y, "FPCR:"); + screen.Print(10, y, "%04x", fpcr); + x = 34; + screen.Puts(x + 0, y, TA::OnOff(fpcr & 0x8000), "BS"); + screen.Puts(x + 3, y, TA::OnOff(fpcr & 0x4000), "SN"); + screen.Puts(x + 6, y, TA::OnOff(fpcr & 0x2000), "OE"); + screen.Puts(x + 9, y, TA::OnOff(fpcr & 0x1000), "OF"); + screen.Puts(x + 12, y, TA::OnOff(fpcr & 0x0800), "UF"); + screen.Puts(x + 15, y, TA::OnOff(fpcr & 0x0400), "DZ"); + screen.Puts(x + 18, y, TA::OnOff(fpcr & 0x0200), "X2"); + screen.Puts(x + 21, y, TA::OnOff(fpcr & 0x0100), "X1"); + screen.Print(x + 25, y, "RP=.%c RM=%s", + "XSD?"[(fpcr >> 6) & 3], + rmstr[(fpcr >> 4) & 3]); + y++; + + // FPSR + uint32 fpsr = tmp.fpframe.fpf_fpsr; + uint32 cc = fpsr >> 24; + screen.Print(0, y, "FPSR: %08x", fpsr); + x = 15; + screen.Puts(x + 0, y, TA::OnOff(cc & 0x08), "N"); + screen.Puts(x + 2, y, TA::OnOff(cc & 0x04), "Z"); + screen.Puts(x + 4, y, TA::OnOff(cc & 0x02), "Inf"); + screen.Puts(x + 8, y, TA::OnOff(cc & 0x01), "NAN"); + screen.Print(x + 12, y, "Q=$%02x", (fpsr >> 16) & 0xff); + x = 34; + screen.Puts(x + 0, y, TA::OnOff(fpsr & 0x8000), "BS"); + screen.Puts(x + 3, y, TA::OnOff(fpsr & 0x4000), "SN"); + screen.Puts(x + 6, y, TA::OnOff(fpsr & 0x2000), "OE"); + screen.Puts(x + 9, y, TA::OnOff(fpsr & 0x1000), "OF"); + screen.Puts(x + 12, y, TA::OnOff(fpsr & 0x0800), "UF"); + screen.Puts(x + 15, y, TA::OnOff(fpsr & 0x0400), "DZ"); + screen.Puts(x + 18, y, TA::OnOff(fpsr & 0x0200), "X2"); + screen.Puts(x + 21, y, TA::OnOff(fpsr & 0x0100), "X1"); + screen.Puts(x + 25, y, TA::OnOff(fpsr & 0x80), "AV"); + screen.Puts(x + 28, y, TA::OnOff(fpsr & 0x40), "AO"); + screen.Puts(x + 31, y, TA::OnOff(fpsr & 0x20), "AU"); + screen.Puts(x + 34, y, TA::OnOff(fpsr & 0x10), "AZ"); + screen.Puts(x + 37, y, TA::OnOff(fpsr & 0x08), "AX"); + y++; + + // FPIAR + screen.Print(0, y++, "FPIAR:%08x", tmp.fpframe.fpf_fpiar); + + return y; +} + + +// +// 68030 +// + +// コンストラクタ +MPU68030Device::MPU68030Device() + : inherited() +{ + mpu_type = m680x0MPUType::M68030; + mpu_name = "MC68030"; + SetName("MPU(68030)"); + + // MMU.xRP.DT == 0 になってはいけない(MMU 構成例外)。 + SetSRP(0x00000001, 0); + SetCRP(0x00000001, 0); + + // キャッシュ + icache.reset(new m68030Cache()); + dcache.reset(new m68030Cache()); + + // レジスタモニター (サイズは Init で確定する) + reg_monitor = gMonitorManager->Regist(ID_MONITOR_MPUREG, this); + reg_monitor->func = ToMonitorCallback(&MPU68030Device::MonitorUpdateReg); + + // ATC モニター + atc_monitor = gMonitorManager->Regist(ID_MONITOR_MPUATC, this); + atc_monitor->func = ToMonitorCallback(&MPU68030Device::MonitorUpdateATC); + atc_monitor->SetSize(114, m68030ATCTable::LINES + 6); + + // キャッシュモニター + cache_monitor = gMonitorManager->Regist(ID_MONITOR_MPUCACHE, this); + cache_monitor->func = + ToMonitorCallback(&MPU68030Device::MonitorUpdateCache); + cache_monitor->SetSize(48, 20 * 2 + 1); +} + +// デストラクタ +MPU68030Device::~MPU68030Device() +{ +} + +void +MPU68030Device::SetLogLevel(int loglevel_) +{ + inherited::SetLogLevel(loglevel_); + + atc.SetLogLevel(loglevel_); +} + +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; +} + +// モニター更新 (レジスタ) +void +MPU68030Device::MonitorUpdateReg(Monitor *, TextScreen& screen) +{ + m68kreg tmp; + uint32 tmp_ppc; + uint32 tmp_state; + int y; + + screen.Clear(); + + // ローカルにコピーする。 + // ただし一部は reg ではなく cpu クラス本体にある。 + tmp = reg; + tmp_ppc = ppc; + tmp_state = cpu_state; + + // 共通部分。 + MonitorUpdateRegCommon(screen, tmp, tmp_ppc, tmp_state); + + // Cache/MMU 部分。 y = 4; + y = MonitorUpdateMMU(screen, y, tmp); + + // FPU はある時だけ表示。 + if (HasFPU()) { + MonitorUpdateFPU(screen, y, tmp); + } +} + +// モニター更新の下請け (Cache/MMU 部分) +int +MPU68030Device::MonitorUpdateMMU(TextScreen& screen, int y, m68kreg& tmp) +{ + int x; + + // Cache + screen.Puts(0, y++, ""); + screen.Print(0, y, "CACR:%08x(", tmp.cacr); + screen.Puts(14, y, TA::OnOff(tmp.cacr & M68030::CACR_WA), "WA"); + screen.Puts(17, y, TA::OnOff(tmp.cacr & M68030::CACR_DBE), "DBE"); + screen.Puts(21, y, "- -"); + screen.Puts(25, y, TA::OnOff(tmp.cacr & M68030::CACR_FD), "FD"); + screen.Puts(28, y, TA::OnOff(tmp.cacr & M68030::CACR_ED), "ED"); + screen.Puts(31, y, "000"); + screen.Puts(35, y, TA::OnOff(tmp.cacr & M68030::CACR_IBE), "IBE"); + screen.Puts(39, y, "- -"); + screen.Puts(43, y, TA::OnOff(tmp.cacr & M68030::CACR_FI), "FI"); + screen.Puts(46, y, TA::OnOff(tmp.cacr & M68030::CACR_EI), "EI"); + screen.Putc(48, y, ')'); + screen.Print(50, y, "CAAR:%08x", tmp.caar); + y++; + + // MMU + x = 0; screen.Puts(x, y++, ""); screen.Print(x, y, "SRP:%08x_%08x", tmp.srp.h, tmp.srp.l); screen.Print(x, y + 1, "CRP:%08x_%08x", tmp.crp.h, tmp.crp.l); - x += 23; + x += 25; for (int i = 0; i < 2; i++) { uint32 tt = tmp.tt[i]; - screen.Print(x, y + i, "TT%d:%08x(%c%c%c)", + screen.Print(x, y + i, + "TT%u:%08x(%c%c%c Addr=$%02x Mask=$%02x FC=%u FCMask=%u)", i, tt, (tt & m68030TT::E) ? 'E' : '-', (tt & m68030TT::CI) ? 'C' : '-', - (tt & m68030TT::RWM) ? '-' : ((tt & m68030TT::RW) ? 'R' : 'W')); - } - x += 19; - screen.Print(x, y, "TC:%08x(%c%c%c)", + (tt & m68030TT::RWM) ? '-' : ((tt & m68030TT::RW) ? 'R' : 'W'), + (tt >> 24), + (tt >> 16) & 0xff, + (tt >> 4) & 0x07, + (tt ) & 0x07); + } + y += 2; + screen.Print(0, y, + "TC:%08x(%c%c%c IS=$%x TIA-D=%x:%x:%x:%x PS=$%x)", tmp.tc, (tmp.tc & m68030TC::TC_E) ? 'E' : '-', (tmp.tc & m68030TC::TC_SRE) ? 'S' : '-', - (tmp.tc & m68030TC::TC_FCL) ? 'F' : '-'); - y++; - screen.Print(x, y, "MMUSR: %04x(", tmp.mmusr); - x += 12; + (tmp.tc & m68030TC::TC_FCL) ? 'F' : '-', + (tmp.tc >> 16) & 0xf, + (tmp.tc >> 12) & 0xf, + (tmp.tc >> 8) & 0xf, + (tmp.tc >> 4) & 0xf, + (tmp.tc ) & 0xf, + (tmp.tc >> 20) & 0xf); + x = 49; + screen.Print(x, y, "MMUSR:%04x(", tmp.mmusr); + x += 11; screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::B) | 'B'); screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::L) | 'L'); screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::S) | 'S'); @@ -545,81 +722,20 @@ 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); - - // FPU - x = 0; - y = 7; - screen.Puts(x, y++, ""); - for (int i = 0; i < 8; i++) { - screen.Print(x, y + i, "FP%d:%04x_%08x_%08x (%-20s)", - i, - tmp.fpframe.fpf_regs[i * 3 + 0] >> 16, - tmp.fpframe.fpf_regs[i * 3 + 1], - tmp.fpframe.fpf_regs[i * 3 + 2], - "not yet"); - } - - x = 51; - - // FPCR - static const char * const rpstr[] = { - ".EXT", - ".SGL", - ".DBL", - ".???", - }; - uint32 fpcr = tmp.fpframe.fpf_fpcr; - screen.Print(x, y++, "FPCR:%04x", fpcr); - screen.Puts(x + 2, y, TA::OnOff(fpcr & 0x8000), "BS"); - screen.Puts(x + 5, y, TA::OnOff(fpcr & 0x4000), "SN"); - screen.Puts(x + 8, y, TA::OnOff(fpcr & 0x2000), "OP"); - screen.Puts(x + 11, y, TA::OnOff(fpcr & 0x1000), "OV"); - screen.Puts(x + 14, y, TA::OnOff(fpcr & 0x0800), "UF"); - screen.Puts(x + 17, y, TA::OnOff(fpcr & 0x0400), "DZ"); - screen.Puts(x + 20, y, TA::OnOff(fpcr & 0x0200), "I2"); - screen.Puts(x + 23, y, TA::OnOff(fpcr & 0x0100), "I1"); - y++; - screen.Print(x + 2, y++, "RP=%s RM=%s", - rpstr[(fpcr >> 6) & 3], - rmstr[(fpcr >> 4) & 3]); - - // FPSR - uint32 fpsr = tmp.fpframe.fpf_fpsr; - uint32 cc = fpsr >> 24; - screen.Print(x, y++, "FPSR:%08x", fpsr); - screen.Puts(x + 2, y, TA::OnOff(cc & 0x08), "N"); - screen.Puts(x + 4, y, TA::OnOff(cc & 0x04), "Z"); - screen.Puts(x + 6, y, TA::OnOff(cc & 0x02), "Inf"); - screen.Puts(x + 10, y, TA::OnOff(cc & 0x01), "NAN"); - screen.Print(x + 14, y, "Q=$%02x", (fpsr >> 16) & 0xff); - y++; - screen.Puts(x + 2, y, TA::OnOff(fpsr & 0x8000), "BS"); - screen.Puts(x + 5, y, TA::OnOff(fpsr & 0x4000), "SN"); - screen.Puts(x + 8, y, TA::OnOff(fpsr & 0x2000), "OP"); - screen.Puts(x + 11, y, TA::OnOff(fpsr & 0x1000), "OV"); - screen.Puts(x + 14, y, TA::OnOff(fpsr & 0x0800), "UF"); - screen.Puts(x + 17, y, TA::OnOff(fpsr & 0x0400), "DZ"); - screen.Puts(x + 20, y, TA::OnOff(fpsr & 0x0200), "I2"); - screen.Puts(x + 23, y, TA::OnOff(fpsr & 0x0100), "I1"); - y++; - screen.Puts(x + 2, y, TA::OnOff(fpsr & 0x80), "IOP"); - screen.Puts(x + 6, y, TA::OnOff(fpsr & 0x80), "OVFL"); - screen.Puts(x + 11, y, TA::OnOff(fpsr & 0x80), "UNFL"); - screen.Puts(x + 16, y, TA::OnOff(fpsr & 0x80), "DZ"); - screen.Puts(x + 19, y, TA::OnOff(fpsr & 0x80), "INEX"); + screen.Print(x, y, "---N=%u)", tmp.mmusr & m68030MMUSR::N); y++; - // FPIAR - screen.Print(x, y++, "FPIAR:%08x", tmp.fpframe.fpf_fpiar); + return y; } -// モニター更新 (ATC) +// モニター更新 (68030 ATC) void -MPU680x0Device::MonitorUpdateATC(Monitor *, TextScreen& screen) +MPU68030Device::MonitorUpdateATC(Monitor *, TextScreen& screen) { m68030ATCTable *table; -#if !defined(ATC_SINGLE) + + screen.Clear(); + struct { int fc; const char *name; @@ -630,98 +746,353 @@ 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 * 32 + 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", + screen.Print(x, i + 2, attr, "[%02x] %08x %08x %c%c%c%c", + idx, a.GetLAddr(), a.GetPAddr(), a.IsBusError() ? 'B' : '-', + a.IsCInhibit() ? 'C' : '-', 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 + 17, 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 + 22, 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 + 27, 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.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++; + + 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.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 +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; } } } -#else - m68030ATCLine *a; - int i; - table = &atc.tables[0]; + // キャッシュのヒット率。 + 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()]; +} + +// モニタ更新 (68030 キャッシュ) +void +MPU68030Device::MonitorUpdateCache(Monitor *, TextScreen& screen) +{ + int y; + screen.Clear(); - 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; + // + // 01234567890123456789012345678901234567890123456789 + // Tag Address Data + // S.012345'00: 00000000 00000000 -------- 00000000 + + 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); +} + +// モニタ更新 (68030 キャッシュ片方の下請け) +int +MPU68030Device::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++; + + 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, attr, "%08x", l.data[i]); + } else { + screen.Puts(13 + 9 * i, y, TA::Disable, "--------"); } - screen.Print(4, i + 1, "%d(%c%c):%08x %08x %c%c%c", - a->fc, - (a->fc & 4) ? 'S' : 'U', - dp, - a->GetLAddr(), a->GetPAddr(), - a->IsBusError() ? 'B' : '-', - a->IsWProtect() ? 'W' : '-', - a->IsModified() ? 'M' : '-'); } + 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(1, y++, attr, "Cache Read Hit %5.1f%%", hit); + + return y; +} + + +// +// 68040 +// + +// コンストラクタ +MPU68040Device::MPU68040Device() + : inherited() +{ + mpu_type = m680x0MPUType::M68040; + mpu_name = "MC68040"; + SetName("MPU(68040)"); + + mmu_itt[0].reset(new m68040TT("ITT0", ®.itt[0])); + mmu_itt[1].reset(new m68040TT("ITT1", ®.itt[1])); + mmu_dtt[0].reset(new m68040TT("DTT0", ®.dtt[0])); + mmu_dtt[1].reset(new m68040TT("DTT1", ®.dtt[1])); + mmu_tc.reset(new m68040TC()); + atc_inst.reset(new m68040ATC()); + atc_data.reset(new m68040ATC()); + + // レジスタモニター (サイズは Init で確定する) + reg_monitor = gMonitorManager->Regist(ID_MONITOR_MPUREG, this); + reg_monitor->func = ToMonitorCallback(&MPU68040Device::MonitorUpdateReg); + + // ATC モニター (Inst/Data) + atc_monitor = gMonitorManager->Regist(ID_MONITOR_MPUATC, this); + atc_monitor->func = ToMonitorCallback(&MPU68040Device::MonitorUpdateATC); + atc_monitor->SetSize(121, 34); +} + +// デストラクタ +MPU68040Device::~MPU68040Device() +{ +} + +// モニター更新 (レジスタ) +void +MPU68040Device::MonitorUpdateReg(Monitor *, TextScreen& screen) +{ + m68kreg tmp; + uint32 tmp_ppc; + uint32 tmp_state; + FPU40 tmp_fpu40; + int y; + + screen.Clear(); + + // ローカルにコピーする。 + // ただし一部は reg ではなく cpu クラス本体にある。 + tmp = reg; + tmp_ppc = ppc; + tmp_state = cpu_state; + tmp_fpu40 = fpu40; + + // 共通部分。 + MonitorUpdateRegCommon(screen, tmp, tmp_ppc, tmp_state); + + // Cache/MMU 部分。 + y = 4; + y = MonitorUpdateMMU(screen, y, tmp); + + // FPU はある時だけ表示。 + if (HasFPU()) { + y = MonitorUpdateFPU(screen, y, tmp); + MonitorUpdateFPU40(screen, y, tmp_fpu40); } -#endif +} + +// モニター更新の下請け (Cache/MMU 部分) +int +MPU68040Device::MonitorUpdateMMU(TextScreen& screen, int y, m68kreg& tmp) +{ + int x; + + screen.Puts(0, y++, ""); + screen.Print(0, y, "SRP:%08x TC:%04x( )", tmp.srp40, tmp.tc40); + screen.Putc(24, y, TA::OnOff(tmp.tc40 & m68040TC::E) | 'E'); + screen.Putc(26, y, TA::OnOff(tmp.tc40 & m68040TC::P) | 'P'); + y++; + screen.Print(0, y++, "URP:%08x", tmp.urp40); + screen.Print(0, y, "MMUSR:%05x'%03x", + (tmp.mmusr40 >> 12), + (tmp.mmusr40 & 0xfff)); + x = 16; + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::B) | 'B'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::G) | 'G'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::U1)| 'U'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::U0)| 'U'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::S) | 'S'); + x++; + screen.Print(x, y, "CM%u", (tmp.mmusr40 & m68040MMUSR::CM) >> 5); + x += 4; + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::M) | 'M'); + screen.Putc(x++, y, '-'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::W) | 'W'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::T) | 'T'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr40 & m68040MMUSR::R) | 'R'); + y++; + + screen.Print(0, y, "CACR:%08x(", tmp.cacr); + screen.Puts(14, y, TA::OnOff(tmp.cacr & M68040::CACR_DE), "DE"); + screen.Puts(17, y, TA::OnOff(tmp.cacr & M68040::CACR_IE), "IE"); + screen.Putc(19, y, ')'); + y++; + + 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]); + + return y; +} + +// *TTn 1つ分の下請け。 +void +MPU68040Device::MonitorUpdateTT(TextScreen& screen, int x, int y, + const char *name, uint32 tt) +{ + bool e = (tt & m68040TT::E); + + screen.Print(x, y, "%s:%08x(", name, tt); + x += 14; + screen.Putc(x++, y, TA::OnOff(e) | 'E'); + x++; + screen.Print(x, y, (e ? TA::Normal : TA::Disable), + "Base=$%02x Mask=$%02x", (tt >> 24), (tt >> 16) & 0xff); + + x += 18; + if (e) { + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::S_IGN) | 'S'); + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::S_FC2) | 'S'); + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::U1) | 'U'); + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::U0) | 'U'); + x++; + screen.Print(x, y, "CM%u", (tt & m68040TT::CM) >> 5); + x += 4; + screen.Putc(x++, y, TA::OnOff(tt & m68040TT::W) | 'W'); + } else { + screen.Puts(x, y, TA::Disable, "---- --- -"); + x += 10; + } + screen.Putc(x, y, ')'); +} + +void +MPU68040Device::CalcStat() +{ }