--- nono/vm/mpu680x0.cpp 2026/04/29 17:05:03 1.1.1.11 +++ nono/vm/mpu680x0.cpp 2026/04/29 17:05:24 1.1.1.16 @@ -4,40 +4,62 @@ // Licensed under nono-license.txt // -// MPU (m680x0) +// +// MPU (MC68030) +// #include "mpu680x0.h" #include "config.h" -#include "m68030bus.h" -#include "m68030excep.h" +#include "debugger.h" +#include "interrupt.h" +#include "m68030cache.h" #include "mainapp.h" +#include "scheduler.h" +#include "syncer.h" #include "uimessage.h" -#include "vm.h" -#include +#include "vectortable.h" +#include + +static Syncer *gSyncer; // コンストラクタ MPU680x0Device::MPU680x0Device() - : inherited("MPU(68030)") + : inherited() { - cpu = m68030_init(); + SetName("MPU(68030)"); - // イベントコールバック設定 - event.SetName("MPU"); - event.func = (DeviceCallback_t)&MPU680x0Device::Callback; + // MMU.xRP.DT == 0 になってはいけない(MMU 構成例外)。 + SetSRP(0x00000001, 0); + SetCRP(0x00000001, 0); + // FPU + fpu_init(); + // キャッシュ + icache.reset(new m68030Cache()); + + // 割り込みイベントコールバック設定 + intr_event.func = ToEventCallback(&MPU680x0Device::InterruptCallback); + intr_event.Regist(GetName() + " Interrupt"); // レジスタモニター - reg_monitor.func = (MonitorCallback_t)&MPU680x0Device::MonitorUpdateReg; - reg_monitor.SetSize(79, 16); + reg_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateReg); + reg_monitor.SetSize(78, 19); reg_monitor.Regist(ID_MONITOR_MPUREG); // ATC モニター - atc_monitor.func = (MonitorCallback_t)&MPU680x0Device::MonitorUpdateATC; + atc_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateATC); #if !defined(ATC_SINGLE) - atc_monitor.SetSize(131, m68030ATCTable::LINES + 3); + atc_monitor.SetSize(135, m68030ATCTable::LINES + 4); #else - atc_monitor.SetSize(31, 24); + atc_monitor.SetSize(32, 24); #endif atc_monitor.Regist(ID_MONITOR_MPUATC); + + // キャッシュモニター + cache_monitor.func = ToMonitorCallback(&MPU680x0Device::MonitorUpdateCache); + cache_monitor.SetSize(48, 20); + cache_monitor.Regist(ID_MONITOR_MPUCACHE); + + SetTrace(false); } // デストラクタ @@ -45,75 +67,192 @@ MPU680x0Device::~MPU680x0Device() { } +void +MPU680x0Device::SetLogLevel(int loglevel_) +{ + inherited::SetLogLevel(loglevel_); + + atc.SetLogLevel(loglevel_); +} + +// 初期化 bool MPU680x0Device::Init() { - // 親クラス if (inherited::Init() == false) { return false; } - // MPU クロックは親 Init() で読み込んで、ここで cpu にセットする - m68030_set_clockspeed(cpu, clock_khz); - // 割り込み時のディレイ - intdelay = cpu->Cycle2Vtime(4); + + debugger = GetDebugger(); + interruptdev = GetInterruptDevice(); + // static 関数から参照するため。うーん…。 + gSyncer = syncer; + + // 割り込み時のディレイ。 + // + // 外部デバイスが 68030 の IPL0-2 をアサートしてから、68030 がそれを + // 認識(確定)するのに 1 ~ 2 クロックかかる。 + // その後レベルを比較するのは次のアップエッジ。 + // その後 /IPEND をアサートするのはその次のアップエッジ。 + // 合わせると、IPL0-2 が変化してから /IPEND がアサートされるまでは + // 2 ~ 3 クロックかかる。(Figure 8-4) + intr_event.time = 3 * clock_nsec; // FPU (6888x) - if (gMainApp.GetVMType() == VMTYPE_LUNA1) { - // LUNA は 68881 固定 (設定は見ない) - cpu->has_fpu = 1; - } else { + if (gMainApp.IsX68030()) { // X68k は設定による - const ConfigItem& item = gConfig->Find("mpu-has-fpu"); - cpu->has_fpu = item.AsInt(); - if (cpu->has_fpu < 0 || cpu->has_fpu > 2) { + 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; } + } else { + // LUNA は 68881 固定 (設定は見ない) + has_fpu = 1; } - cpu->halt_callback = &MPU680x0Device::HaltCallback; - cpu->halt_arg = NULL; + vectortable = GetVectorTable(); return true; } -bool -MPU680x0Device::PowerOn() +// リセット +void +MPU680x0Device::ResetHard(bool poweron) { - m68030_power_on(cpu); - return true; + if (poweron) { + // サイクルを初期化。 + used_cycle = 0; + + // 履歴を初期化。電源(再)投入時のみ行う。 + exhist.Clear(); + brhist.Clear(); + + for (int i = 0; i < 16; i++) { + reg.R[i] = 0xcccccccc; + } + // XXX ただし X68030 IPLROM には A6 を初期化せずに書き込んでいるバグが + // あるため、A6 を概ね RAM のあるあたりに指定しておく…。 + reg.A[6] = 0x000ccccc; + + // この後起きるリセット例外で PC を初期化する前に参照することになるので + // これだけここでも初期化しておく。 + reg.pc = 0; + } + resetting = true; + + // リセット例外を 520 クロック後に起こす。 + // 520 は、電源オン時、Vcc が最小動作規定値に達してから最低 520 クロック + // の間アサートしなければならないという値であって、実際これだけかかる + // とかいう値ではない。が、こちら側の都合で、この後実行される RAM の + // ResetHard() がブートページを用意した後で ExceptionReset() で + // SP, PC をフェッチするという順序にしないといけないため、リセット例外が + // 起動するまでに時間がかかるというところを都合よく真似ておく。 + + exec_event.func = ToEventCallback(&MPU680x0Device::ResetCallback); + exec_event.time = 520 * clock_nsec; + exec_event.SetName(GetName() + " Reset"); + scheduler->RestartEvent(exec_event); } +// リセット例外コールバック。 +// MPU リセットから規定時間後に呼ばれる。 void -MPU680x0Device::ResetHard() +MPU680x0Device::ResetCallback(Event& ev) { - // リセット例外 - m68030_request_reset_exception(cpu); + resetting = false; + intr_pending = false; + + // リセット例外を実行 + int cycle = ExceptionReset(); + + // コールバックを ResetCallback から Exec* に差し替える前のここで + // トレース状態を初期化する。 + SetTrace(debugger->IsTrace()); + + // 以降は通常走行。 + if (cpu_state == CPU_STATE_NORMAL) { + exec_event.func = exec_short; + exec_event.time = cycle * clock_nsec; + exec_event.SetName(GetName() + " Execute"); + scheduler->RestartEvent(exec_event); + } else { + // リセット例外でダブルバスフォールトならここで停止。 + } } -// イベントコールバック -// ev.code は -// 0x0 〜 0x7: 新しい割り込みレベルを通知 (0-7) +// 割り込みチェック、STOP/HALT 処理を含んだ完全な実行サイクルのコールバック。 +// SR の I2:I0 が変わった直後などに呼ばれる。 +// ここでは割り込みをチェックし、必要なら割り込み処理を起動、 +// そうでなければ通常の命令実行を行う。 void -MPU680x0Device::Callback(Event& ev) +MPU680x0Device::ExecFull(Event& ev) { - int level = ev.code; - m68030_interrupt(cpu, level); + if (cpu_state == CPU_STATE_HALT) { + // HALT なら停止。 + return; + } + + // マスクよりも割り込みレベルのほうが高ければ割り込み処理。 + // この時点で intr_pending が true の可能性もあることに留意。 + // 割り込みレベルは変化してないので Level 7 の特別処理は不要 + // (割り込みレベルの変化はここではなく InterruptCallback() で処理)。 + if (intr_level > reg.intr_mask) { + intr_pending = true; + } + + if (intr_pending) { + // 割り込み処理を起動。 + + intr_pending = false; + + // 前の命令実行後となるここで割り込みを起動 + ev.time = ExceptionInterrupt(); + scheduler->StartEvent(ev); + } else { + if (cpu_state == CPU_STATE_NORMAL) { + // コールバックを通常処理に戻してから... + exec_event.func = exec_short; + + // この場で次の命令を実行。 + ExecShort(ev); + } else { + // STOP ならここで停止。 + } + } } -// MPU を vtime [nsec] 分実行する。 -// 戻り値は outer フラグ。 -uint32 -MPU680x0Device::Run(uint32 vtime) +// トレース実行 +void +MPU680x0Device::ExecTrace(Event& ev) { - return m68030_run(cpu, vtime); + debugger->ExecMain(); + ExecFull(ev); } -// 現在の仮想時刻 [nsec] を取得する -uint64 -MPU680x0Device::GetVirtTime() const +// トレース状態を設定する +void +MPU680x0Device::SetTrace(bool enable) { - return m68030_get_vtime(cpu); + if (enable) { + exec_short = ToEventCallback(&MPU680x0Device::ExecTrace); + exec_full = ToEventCallback(&MPU680x0Device::ExecTrace); + } else { + exec_short = ToEventCallback(&MPU680x0Device::ExecShort); + exec_full = ToEventCallback(&MPU680x0Device::ExecFull); + } + exec_event.func = exec_full; + + // STOP 命令中にオンにするには、停止しているイベントを起こす。 + // STOP 命令中にオフにする場合は、何もしなくていい。 + if (enable && exec_event.IsRunning() == false) { + scheduler->StartEvent(exec_event); + } } // 割り込みレベルが変わったことを MPU に通知。 @@ -122,229 +261,272 @@ MPU680x0Device::Interrupt(int level) { assertmsg(0 <= level && level <= 7, "level=%d", level); - // 外部デバイスが 68030 の IPL0-2 をアサートしてから、68030 がそれを - // 認識(確定)するのに 2クロックかかる。ここでレベルを比較して割り込みを - // 上げる場合は /IPEND をアサートする。 - // そこからさらに2クロック後以降の命令境界で割り込みを受け付ける(?) - // らしい? - // この 4クロック待つところはイベントスケジューラがある VM 側で担当する。 - // 本来、レベル比較はここから2クロック後に行われるはずだが、ちょっと省略。 - // 今はコア側でペンディング処理をしているのでこっち側は起きたことを全部 - // 通知する。 - // 割り込みレベルが 0 に下がる時にはこのディレイは不要のはず。 - if (level != 0) { - event.time = intdelay; - } else { - event.time = 0; + intr_event.code = level; + if (intr_event.IsRunning() == false) { + scheduler->StartEvent(intr_event); } - event.code = level; - event.Start(); } -// A-Line 命令エミュレーションのコールバックを指定。 +// 外部割り込みイベントコールバック。 +// ev.code 0-7 は新しい割り込みレベル。 +// +// 割り込みは本来命令境界ごとにチェックするが、実際に割り込みが起きうるのは +// 1) 割り込み信号線が変化した時 +// 2) 割り込みマスクを変更した時 +// しかないはずなので、この時だけ命令境界でチェックする。 +// ここはその 1) のほう。その2のほうは core.cpp:SetSR() 参照。 +// +// 外部割り込みが来たので、レベル比較などをして、必要なら次の命令境界で +// 割り込みチェックを行うようにするところ。 void -MPU680x0Device::SetALineCallback(bool (*callback)(m68kcpu *, void*), void *arg) +MPU680x0Device::InterruptCallback(Event& ev) { - cpu->aline_callback = callback; - cpu->aline_arg = arg; -} + int level = ev.code; + int mask = reg.intr_mask; -// F-Line 命令エミュレーションのコールバックを指定。 -void -MPU680x0Device::SetFLineCallback(bool (*callback)(m68kcpu *, void*), void *arg) -{ - cpu->fline_callback = callback; - cpu->fline_arg = arg; -} + // 信号線が 2 クロック期間安定していること云々は省略 -// MPU ホールト時の MPU からのコールバック関数 -/*static*/ void -MPU680x0Device::HaltCallback(void *arg) -{ - UIMessage::Post(UIMessage::HALT); + // 割り込みを起こすかどうかに関わらずここで更新。 + intr_level = level; + + // ホールトならここまで。 + if (cpu_state == CPU_STATE_HALT) { + return; + } + + // 割り込み例外を起動するのは + // o レベルがマスクより高い + // o マスクに関係なく、レベルが下位から 7 に変化した場合 + // (ここは変化した時だけ呼ばれるのと 7 より上はないので + // level==7 なら必ず「下位から 7 に変化」のはず) + if (level > mask || level == 7) { + // 通知した結果割り込みが受け付けられたので、 + // この命令終了時のコールバックを割り込み処理起動に差し替える。 + MakeNextFull(); + intr_pending = true; + + // STOP 状態だったらここでイベントを開始する + if (cpu_state == CPU_STATE_STOP) { + // STOP から抜ける時間? + exec_event.time = 1 * clock_nsec; + scheduler->StartEvent(exec_event); + } + } } -// MPU からのアクセスをエミュレート -uint64 -MPU680x0Device::Read8(uint32 addr) +// 次の命令境界は Full にする。 +void +MPU680x0Device::MakeNextFull() { - try { - return m68030_read_8(cpu, addr); - } catch (...) { - return (uint64)-1; - } + exec_event.func = exec_full; } -uint64 -MPU680x0Device::Read16(uint32 addr) +void +MPU680x0Device::ChangeState(uint32 new_state) { - try { - return m68030_read_16(cpu, addr); - } catch (...) { - return (uint64)-1; + // new_mode (CPU_STATE_*) と + // RequestCPUMode() の引数 Syncer::SCHED_CPU_* は + // 実は同じ値なのでそのまま渡してよいことにする。 + static_assert(CPU_STATE_NORMAL == Syncer::SCHED_CPU_NORMAL, ""); + static_assert(CPU_STATE_STOP == Syncer::SCHED_CPU_STOP, ""); + static_assert(CPU_STATE_HALT == Syncer::SCHED_CPU_HALT, ""); + + if (cpu_state != new_state) { + cpu_state = new_state; + // スケジューラに通知 + syncer->RequestCPUMode(cpu_state); } } -uint64 -MPU680x0Device::Read32(uint32 addr) +// A-Line 命令エミュレーションのコールバックを指定。 +void +MPU680x0Device::SetALineCallback(bool (*callback)(MPU680x0Device *, void*), + void *arg) { - try { - return m68030_read_32(cpu, addr); - } catch (...) { - return (uint64)-1; - } + aline_callback = callback; + aline_arg = arg; } -uint64 -MPU680x0Device::Write8(uint32 addr, uint32 data) +// F-Line 命令エミュレーションのコールバックを指定。 +void +MPU680x0Device::SetFLineCallback(bool (*callback)(MPU680x0Device *, void*), + void *arg) { - try { - m68030_write_8(cpu, addr, data); - return 0; - } catch (...) { - return (uint64)-1; - } + fline_callback = callback; + fline_arg = arg; } -uint64 -MPU680x0Device::Write16(uint32 addr, uint32 data) +// RESET 命令 +void +MPU680x0Device::ops_reset() { - try { - m68030_write_16(cpu, addr, data); - return 0; - } catch (...) { - return (uint64)-1; - } + auto mainbus = GetMainbusDevice(); + mainbus->ResetByMPU(); } -uint64 -MPU680x0Device::Write32(uint32 addr, uint32 data) +// ホールト状態 +void +MPU680x0Device::Halt() { - try { - m68030_write_32(cpu, addr, data); - return 0; - } catch (...) { - return (uint64)-1; - } + ChangeState(CPU_STATE_HALT); + + // この命令完了後に停止するため exec を差し替える。 + MakeNextFull(); + + // UI に通知 (メッセージボックス表示とか) + UIMessage::Post(UIMessage::HALT); } // モニター更新 (レジスタウィンドウ) void MPU680x0Device::MonitorUpdateReg(Monitor *, TextScreen& screen) { - m68kreg reg; - uint32 ppc; + m68kreg tmp; + uint32 tmp_ppc; + uint32 tmp_state; int x; int y; screen.Clear(); // ローカルにコピー - // ただし PPC だけ reg 構造体ではなく cpu クラス側にある - reg = cpu->reg; - ppc = cpu->ppc; + // ただし一部は 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", - i + 0, reg.da[i], - i + 4, reg.da[i + 4], - i + 0, reg.da[i + 8], - i + 4, reg.da[i + 12]); + i + 0, tmp.R[i], + i + 4, tmp.R[i + 4], + i + 0, tmp.R[i + 8], + i + 4, tmp.R[i + 12]); } // 5列目 x = 50; // SR screen.Print(x, 0, "SR:%04x(%c%c%c%c%c)", - (reg.sr_h() | reg.ccr.Get()), - (reg.ccr.IsX()) ? 'X' : '-', - (reg.ccr.IsN()) ? 'N' : '-', - (reg.ccr.IsZ()) ? 'Z' : '-', - (reg.ccr.IsV()) ? 'V' : '-', - (reg.ccr.IsC()) ? 'C' : '-'); - - // VBR - screen.Print(x, 1, "VBR:%08x", reg.vbr); + tmp.GetSR(), + (tmp.ccr.IsX()) ? 'X' : '-', + (tmp.ccr.IsN()) ? 'N' : '-', + (tmp.ccr.IsZ()) ? 'Z' : '-', + (tmp.ccr.IsV()) ? 'V' : '-', + (tmp.ccr.IsC()) ? 'C' : '-'); // *SP - uint ms = (reg.s ? 2 : 0) | (reg.m ? 1 : 0); // T1 T0 S M + 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", reg.usp); - screen.Print(x, 3, TA::Disable, "ISP:%08x", reg.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", reg.usp); - screen.Print(x, 3, TA::Disable, "MSP:%08x", reg.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", reg.isp); - screen.Print(x, 3, TA::Disable, "MSP:%08x", reg.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", ppc); - screen.Print(x, 1, "SFC:%d DFC:%d", reg.sfc, reg.dfc); - screen.Print(x, 2, "CACR:%08x", reg.cacr); - screen.Print(x, 3, "CAAR:%08x", reg.caar); - - // XXX どこに置くのがいいか - if (reg.state == 0) { - screen.Puts(25, 4, "State: Running"); - } else if (reg.state == CPU_REQ_STOP) { - screen.Puts(25, 4, "State: STOP instruction"); - } else if (reg.state == CPU_REQ_HALT) { - screen.Puts(25, 4, TA::On, "State: Double Bus Fault"); + screen.Print(x, 0, "PC: %08x", tmp_ppc); + screen.Print(x, 1, "SFC:%d DFC:%d", tmp.GetSFC(), tmp.GetDFC()); + screen.Print(x, 2, "VBR:%08x", tmp.vbr); + + switch (tmp_state) { + case CPU_STATE_NORMAL: + screen.Puts(50, 3, "State: Running"); + break; + case CPU_STATE_STOP: + screen.Puts(50, 3, "State: STOP instruction"); + break; + case CPU_STATE_HALT: + screen.Puts(50, 3, TA::On, "State: Double Bus Fault"); + break; + default: + screen.Print(50, 3, TA::On, "corrupted cpu_state=%d", tmp_state); + break; } + // Cache + y = 4; + screen.Puts(0, y++, ""); + 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.Print(50, y, "CAAR:%08x", tmp.caar); + y++; + // MMU x = 0; - y = 4; screen.Puts(x, y++, ""); - screen.Print(x, y, "SRP:%08x_%08x", reg.srp.h, reg.srp.l); - screen.Print(x, y + 1, "CRP:%08x_%08x", reg.crp.h, reg.crp.l); - x += 23; + 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 += 25; for (int i = 0; i < 2; i++) { - uint32 tt = reg.tt[i]; - screen.Print(x, y + i, "TT%d:%08x(%c%c%c)", + uint32 tt = tmp.tt[i]; + screen.Print(x, y + i, + "TT%d:%08x(%c%c%c Addr=$%02x Mask=$%02x FC=%d FCMask=%d)", 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)", - reg.tc, - (reg.tc & m68030TC::TC_E) ? 'E' : '-', - (reg.tc & m68030TC::TC_SRE) ? 'S' : '-', - (reg.tc & m68030TC::TC_FCL) ? 'F' : '-'); - screen.Print(x, y + 1, "MMUSR: %04x(%c%c%c%c%c%c%c N=%d)", - reg.mmusr, - (reg.mmusr & m68030MMUSR::B) ? 'B' : '-', - (reg.mmusr & m68030MMUSR::L) ? 'L' : '-', - (reg.mmusr & m68030MMUSR::S) ? 'S' : '-', - (reg.mmusr & m68030MMUSR::W) ? 'W' : '-', - (reg.mmusr & m68030MMUSR::I) ? 'I' : '-', - (reg.mmusr & m68030MMUSR::M) ? 'M' : '-', - (reg.mmusr & m68030MMUSR::T) ? 'T' : '-', - (reg.mmusr & m68030MMUSR::N)); + (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' : '-', + (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'); + screen.Putc(x++, y, '-'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::W) | 'W'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::I) | 'I'); + screen.Putc(x++, y, TA::OnOff(tmp.mmusr & m68030MMUSR::M) | 'M'); + 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); + y++; // 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, - reg.fpframe.fpf_regs[i * 3 + 0] >> 16, - reg.fpframe.fpf_regs[i * 3 + 1], - reg.fpframe.fpf_regs[i * 3 + 2], + tmp.fpframe.fpf_regs[i * 3 + 0] >> 16, + tmp.fpframe.fpf_regs[i * 3 + 1], + tmp.fpframe.fpf_regs[i * 3 + 2], "not yet"); } @@ -357,13 +539,7 @@ MPU680x0Device::MonitorUpdateReg(Monitor ".DBL", ".???", }; - static const char * const rmstr[] = { - "Near", - "Zero", - "Minus", - "Plus", - }; - uint32 fpcr = reg.fpframe.fpf_fpcr; + 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"); @@ -379,7 +555,7 @@ MPU680x0Device::MonitorUpdateReg(Monitor rmstr[(fpcr >> 4) & 3]); // FPSR - uint32 fpsr = reg.fpframe.fpf_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"); @@ -405,7 +581,7 @@ MPU680x0Device::MonitorUpdateReg(Monitor y++; // FPIAR - screen.Print(x, y++, "FPIAR:%08x", reg.fpframe.fpf_fpiar); + screen.Print(x, y++, "FPIAR:%08x", tmp.fpframe.fpf_fpiar); } // モニター更新 (ATC) @@ -432,15 +608,15 @@ MPU680x0Device::MonitorUpdateATC(Monitor } for (int t = 0; t < countof(fclist); t++) { - int x = t * 32 + 4; + int x = t * 33 + 4; int fc = fclist[t].fc; const char *name = fclist[t].name; double r; int i; - table = &cpu->atc.tables[fc]; + table = &atc.tables[fc]; screen.Print(x, 0, "FC=%d %s", fc, name); - screen.Puts(x, 1, "LAddr PAddr Flag Hit% Age"); + screen.Puts(x, 1, "LAddr PAddr Flag Hit% Age"); // 先に統計用の母数を計算 uint64 total = 0; for (i = 0; i < countof(table->hit); i++) { @@ -460,25 +636,26 @@ MPU680x0Device::MonitorUpdateATC(Monitor attr = TA::Normal; } - screen.Print(x, i + 2, attr, "%08x %08x %c%c%c", + screen.Print(x, i + 2, attr, "%08x %08x %c%c%c%c", 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 (!isnan(r)) { + if (!std::isnan(r)) { screen.Print(x + 5, i + 2, "%4.1f%%", r); } // 配列のヒット率とミス率 - screen.Puts(x + 17, i + 2, "miss"); + screen.Puts(x + 18, i + 2, "miss"); for (i = 0; i < countof(table->hit); i++) { r = (double)(table->hit[i] * 100) / total; - if (!isnan(r)) { - screen.Print(x + 22, i + 2, "%4.1f%%", r); + if (!std::isnan(r)) { + screen.Print(x + 23, i + 2, "%4.1f%%", r); } } // カウンタ @@ -486,15 +663,19 @@ MPU680x0Device::MonitorUpdateATC(Monitor 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); + screen.Print(x + 28, i + 2, "%4d", table->head->age - a.age); } } } + + 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; - table = &cpu->atc.tables[0]; + table = &atc.tables[0]; screen.Clear(); screen.Puts(0, 0, "No. FC LAddr PAddr Flag"); @@ -507,12 +688,13 @@ MPU680x0Device::MonitorUpdateATC(Monitor case 2: dp = 'P'; break; default: dp = '?'; break; } - screen.Print(4, i + 1, "%d(%c%c):%08x %08x %c%c%c", + 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' : '-'); } @@ -520,9 +702,39 @@ MPU680x0Device::MonitorUpdateATC(Monitor #endif } -// CPU 側からのコールバック。RESET 命令ハンドラ +// モニタ更新 (キャッシュ) void -m68030_reset_inst(m68kcpu *cpu) +MPU680x0Device::MonitorUpdateCache(Monitor *, TextScreen& screen) { - gVM->ResetByMPU(); + 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 = 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); + + for (int i = 0; i < 4; i++) { + if (l.valid[i]) { + screen.Print(13 + 9 * i, y, "%08x", l.data[i]); + } else { + screen.Print(13 + 9 * i, y, TA::Disable, "--------"); + } + } + y++; + } + + y++; + screen.Print(0, y, " Not Implemented"); }