--- nono/vm/mpu88xx0.cpp 2026/04/29 17:04:55 1.1.1.8 +++ nono/vm/mpu88xx0.cpp 2026/04/29 17:05:14 1.1.1.12 @@ -4,17 +4,23 @@ // Licensed under nono-license.txt // -// MPU (m88xx0) +// +// MPU (M88xx0) +// #include "mpu88xx0.h" #include "config.h" -#include "sysctlr.h" +#include "debugger.h" +#include "debugger_private.h" +#include "m88100disasm.h" +#include "scheduler.h" +#include "sync.h" // コンストラクタ MPU88xx0Device::MPU88xx0Device() : inherited("MPU(88xx0)") { - cpu.reset(new m88kcpu()); + cpu.reset(new m88kcpu(this)); // LUNA88K では CMMU の ID は次のように割り振ってあるようだ。 // OpenBSD の sys/arch/luna68k/include/board.h より。 @@ -33,8 +39,8 @@ MPU88xx0Device::MPU88xx0Device() cpu->cmmu[1].SetID(6); // レジスタモニター - monitor.func = (MonitorCallback_t)&MPU88xx0Device::MonitorUpdate; - monitor.SetSize(79, 18); + monitor.func = ToMonitorCallback(&MPU88xx0Device::MonitorUpdate); + monitor.SetSize(79, 28); monitor.Regist(ID_MONITOR_MPUREG); } @@ -50,26 +56,118 @@ MPU88xx0Device::Init() if (inherited::Init() == false) { return false; } - // MPU クロックは親 Init() で読み込んで、ここで cpu にセットする - cpu->SetClockSpeed(clock_khz); - const ConfigItem& item = gConfig->Find("mpu-pseudo-stop"); - cpu->pseudo_stop_enable = (bool)item.AsInt(); + const ConfigItem& item_stop = gConfig->Find("mpu-pseudo-stop"); + cpu->pseudo_stop_enable = (bool)item_stop.AsInt(); + + const ConfigItem& item_altname = gConfig->Find(".m88k-altname"); + m88100disasm::use_altname = item_altname.AsInt(); return true; } -bool -MPU88xx0Device::PowerOn() +// リセット +void +MPU88xx0Device::ResetHard(bool poweron) { - cpu->PowerOn(); - return true; + if (poweron) { + cpu->PowerOn(); + } + resetting = true; + + // リセット例外を 8+256 クロック後に起こす。 + // 8+256 は、電源オン時に最低これだけアサートしなければならないという値 + // のはずで、実際これだけかかるとかいう値ではない。が、こちら側の都合で + // この後実行される RAM の ResetHard() がブートページを用意した後で + // cpu->Reset() で xip をフェッチするという順序にしないといけないため、 + // リセット例外が起動するまでに時間がかかるというところを都合よく真似て + // おく。 + + exec_event.func = ToEventCallback(&MPU88xx0Device::ResetCallback); + exec_event.time = (8 + 256) * clock_nsec; + exec_event.SetName("MPU Reset"); + gScheduler->RestartEvent(exec_event); } +// リセット例外コールバック。 +// MPU リセットから規定時間後に呼ばれる。 void -MPU88xx0Device::ResetHard() +MPU88xx0Device::ResetCallback(Event& ev) { - cpu->RequestReset(); + resetting = false; + + // リセット例外を実行 + int cycle = cpu->Reset(); + + // コールバックを ResetCallback から Exec* に差し替える前のここで + // トレース状態を初期化する。 + SetTrace(gDebugger->IsTrace()); + + // 以降は通常走行 + exec_event.func = exec_normal; + exec_event.time = cycle * clock_nsec; + exec_event.SetName("MPU Execute"); + gScheduler->StartEvent(exec_event); +} + +// 1命令実行のコールバック +void +MPU88xx0Device::ExecNormal(Event& ev) +{ + int cycle = cpu->Exec(); + + ev.time = cycle * clock_nsec; + gScheduler->StartEvent(ev); +} + +// STOP 状態中のコールバック +void +MPU88xx0Device::ExecStop(Event& ev) +{ + int cycle = cpu->ExecPseudoStop(); + + ev.time = cycle * clock_nsec; + gScheduler->StartEvent(ev); +} + +// トレース実行のコールバック +void +MPU88xx0Device::ExecTrace(Event& ev) +{ + gDebugger->Exec(); + ExecNormal(ev); +} + +// トレース状態を設定する +void +MPU88xx0Device::SetTrace(bool enable) +{ + if (enable) { + exec_normal = ToEventCallback(&MPU88xx0Device::ExecTrace); + exec_stop = ToEventCallback(&MPU88xx0Device::ExecTrace); + } else { + exec_normal = ToEventCallback(&MPU88xx0Device::ExecNormal); + exec_stop = ToEventCallback(&MPU88xx0Device::ExecStop); + } + exec_event.func = exec_normal; +} + +// コアからの動作変更通知 +void +MPU88xx0Device::NotifyCPUMode(uint32 new_mode) +{ + // new_mode (m88kcpu::CPU_STATE_*) と + // RequestCPUMode() の引数 Sync::SCHED_CPU_* は + // 実は同じ値なのでそのまま渡してよいことにする。 + static_assert(m88kcpu::CPU_STATE_NORMAL == Sync::SCHED_CPU_NORMAL, ""); + static_assert(m88kcpu::CPU_STATE_STOP == Sync::SCHED_CPU_STOP, ""); + + gSync->RequestCPUMode(new_mode); + if (new_mode == Sync::SCHED_CPU_NORMAL) { + exec_event.func = exec_normal; + } else { + exec_event.func = exec_stop; + } } // アクセス中の論理アドレスを取得 @@ -107,6 +205,122 @@ MPU88xx0Device::Interrupt(int level) cpu->Interrupt(level); } +// DOS call エミュレーションのコールバックを指定 +void +MPU88xx0Device::SetFLineCallback(bool (*callback)(m88kcpu *, void *), void *arg) +{ + cpu->SetFLineCallback(callback, arg); +} + +// MPU からのアクセスをエミュレート +uint64 +MPU88xx0Device::Read8(uint32 addr) +{ + return cpu->cmmu[1].load_8(addr); +} + +uint64 +MPU88xx0Device::Read16(uint32 addr) +{ + if ((addr & 1) == 0) { + return cpu->cmmu[1].load_16(addr); + } else { + uint64 h, l; + h = Read8(addr); + if ((int64)h < 0) { + return h; + } + l = Read8(addr + 1); + if ((int64)l < 0) { + return l; + } + return (h << 8) | l; + } +} + +uint64 +MPU88xx0Device::Read32(uint32 addr) +{ + if ((addr & 3) == 0) { + return cpu->cmmu[1].load_32(addr); + } else if ((addr & 1) == 0) { + uint64 h, l; + h = Read16(addr); + if ((int64)h < 0) { + return h; + } + l = Read16(addr + 2); + if ((int64)l < 0) { + return l; + } + return (h << 16) | l; + } else { + uint64 h, m, l; + h = Read8(addr); + if ((int64)h < 0) { + return h; + } + m = Read16(addr + 1); + if ((int64)m < 0) { + return m; + } + l = Read8(addr + 3); + if ((int64)l < 0) { + return l; + } + return (h << 24) | (m << 8) | l; + } +} + +uint64 +MPU88xx0Device::Write8(uint32 addr, uint32 data) +{ + return cpu->cmmu[1].store_8(addr, data & 0xff); +} + +uint64 +MPU88xx0Device::Write16(uint32 addr, uint32 data) +{ + if ((addr & 1) == 0) { + return cpu->cmmu[1].store_16(addr, data & 0xffff); + } else { + uint64 r; + r = Write8(addr, data >> 8); + if ((int64)r < 0) { + return r; + } + return Write8(addr + 1, data); + } +} + +uint64 +MPU88xx0Device::Write32(uint32 addr, uint32 data) +{ + if ((addr & 3) == 0) { + return cpu->cmmu[1].store_32(addr, data); + } else if ((addr & 1) == 0) { + uint64 r; + r = Write16(addr, data >> 16); + if ((int64)r < 0) { + return r; + } + return Write16(addr + 2, data); + } else { + uint64 r; + r = Write8(addr, data >> 24); + if ((int64)r < 0) { + return r; + } + r = Write16(addr + 1, data >> 8); + if ((int64)r < 0) { + return r; + } + return Write8(addr + 3, data); + } +} + +#define FA(val, bit) TA::OnOff((val) & m88100reg::bit) + // モニター更新 void MPU88xx0Device::MonitorUpdate(Monitor *, TextScreen& screen) @@ -134,27 +348,27 @@ MPU88xx0Device::MonitorUpdate(Monitor *, (reg.pid >> 1) & 0x7f, (reg.pid & 1) ? "Master" : "Checker"); screen.Print(x, y, "psr (cr1):%08x(", reg.psr); - screen.Puts(x + 19, y, TA::OnOff(reg.psr & m88100reg::PSR_SUPER), "S"); + screen.Puts(x + 19, y, FA(reg.psr, PSR_SUPER), "S"); screen.Puts(x + 21, y, ((reg.psr & m88100reg::PSR_BO_LE) ? "BO=LE" : "BO=BE")); - screen.Puts(x + 27, y, TA::OnOff(reg.psr & m88100reg::PSR_SER), "SER"); - screen.Puts(x + 31, y, TA::OnOff(reg.psr & m88100reg::PSR_C), "Cy"); - screen.Puts(x + 34, y, TA::OnOff(reg.psr & m88100reg::PSR_SFD1), "SFD1"); - screen.Puts(x + 39, y, TA::OnOff(reg.psr & m88100reg::PSR_MXM), "MXM"); - screen.Puts(x + 43, y, TA::OnOff(reg.psr & m88100reg::PSR_IND), "IND"); - screen.Puts(x + 47, y, TA::OnOff(reg.psr & m88100reg::PSR_SFRZ), "SFRZ"); + screen.Puts(x + 27, y, FA(reg.psr, PSR_SER), "SER"); + screen.Puts(x + 31, y, FA(reg.psr, PSR_C), "Cy"); + screen.Puts(x + 34, y, FA(reg.psr, PSR_SFD1), "SFD1"); + screen.Puts(x + 39, y, FA(reg.psr, PSR_MXM), "MXM"); + screen.Puts(x + 43, y, FA(reg.psr, PSR_IND), "IND"); + screen.Puts(x + 47, y, FA(reg.psr, PSR_SFRZ), "SFRZ"); screen.Puts(x + 51, y, ")"); y++; screen.Print(x, y, "epsr(cr2):%08x(", reg.epsr); - screen.Puts(x + 19, y, TA::OnOff(reg.epsr & m88100reg::PSR_SUPER), "S"); + screen.Puts(x + 19, y, FA(reg.epsr, PSR_SUPER), "S"); screen.Puts(x + 21, y, ((reg.epsr& m88100reg::PSR_BO_LE) ? "BO=LE" : "BO=BE")); - screen.Puts(x + 27, y, TA::OnOff(reg.epsr & m88100reg::PSR_SER), "SER"); - screen.Puts(x + 31, y, TA::OnOff(reg.epsr & m88100reg::PSR_C), "Cy"); - screen.Puts(x + 34, y, TA::OnOff(reg.epsr & m88100reg::PSR_SFD1), "SFD1"); - screen.Puts(x + 39, y, TA::OnOff(reg.epsr & m88100reg::PSR_MXM), "MXM"); - screen.Puts(x + 43, y, TA::OnOff(reg.epsr & m88100reg::PSR_IND), "IND"); - screen.Puts(x + 47, y, TA::OnOff(reg.epsr & m88100reg::PSR_SFRZ), "SFRZ"); + screen.Puts(x + 27, y, FA(reg.epsr, PSR_SER), "SER"); + screen.Puts(x + 31, y, FA(reg.epsr, PSR_C), "Cy"); + screen.Puts(x + 34, y, FA(reg.epsr, PSR_SFD1), "SFD1"); + screen.Puts(x + 39, y, FA(reg.epsr, PSR_MXM), "MXM"); + screen.Puts(x + 43, y, FA(reg.epsr, PSR_IND), "IND"); + screen.Puts(x + 47, y, FA(reg.epsr, PSR_SFRZ), "SFRZ"); screen.Puts(x + 51, y, ")"); // 制御レジスタ(左中; *IP) @@ -190,7 +404,7 @@ MPU88xx0Device::MonitorUpdate(Monitor *, screen.Print(x, y++, "ssbr(cr3):%08x", reg.ssbr); screen.Print(x, y++, "vbr (cr7):%08x", reg.vbr); - // MMU レジスタ(下段) + // MMU レジスタ(次段) y = 15; for (int i = 0; i <= 2; i++) { int dt = 8 + i * 3; @@ -217,4 +431,113 @@ MPU88xx0Device::MonitorUpdate(Monitor *, screen.Print(60, y, "dma%d(cr%2d):%08x", i, da, reg.cr[da]); y++; } + y++; + + // FPU レジスタ (下段) + screen.Print(0, y, "fpecr(fcr0):%08x(", reg.fpecr); + x = 21; + screen.Puts(x + 0, y, FA(reg.fpecr, FPECR_FIOV), "FIOV"); + screen.Puts(x + 5, y, FA(reg.fpecr, FPECR_FUNIMP), "FUNIMP"); + screen.Puts(x + 12, y, FA(reg.fpecr, FPECR_FPRV), "FPRV"); + screen.Puts(x + 17, y, FA(reg.fpecr, FPECR_FROP), "FROP"); + screen.Puts(x + 22, y, FA(reg.fpecr, FPECR_FDVZ), "FDVZ"); + screen.Puts(x + 27, y, FA(reg.fpecr, FPECR_FUNF), "FUNF"); + screen.Puts(x + 32, y, FA(reg.fpecr, FPECR_FOVF), "FOVF"); + screen.Puts(x + 37, y, FA(reg.fpecr, FPECR_FINX), "FINX"); + screen.Puts(x + 41, y, ")"); + y++; + + screen.Print(0, y++, + "fppt(fcr5): %08x(OpCode=%%%c%c%c%c%c T1=%d T2=%d TD=%d Dest=r%d)", + reg.fppt, + ((reg.fppt >> 15) & 1) + '0', + ((reg.fppt >> 14) & 1) + '0', + ((reg.fppt >> 13) & 1) + '0', + ((reg.fppt >> 12) & 1) + '0', + ((reg.fppt >> 11) & 1) + '0', + ((reg.fppt >> 9) & 3), + ((reg.fppt >> 7) & 3), + ((reg.fppt >> 5) & 3), + (reg.fppt & m88100reg::FPPT_DEST)); + + screen.Print(0, y++, "fphs1(fcr1)/fpls1(fcr2): %08x'%08x", + reg.fphs1, reg.fpls1); + screen.Print(0, y++, "fphs2(fcr3)/fpls2(fcr4): %08x'%08x", + reg.fphs2, reg.fpls2); + + screen.Print(0, y, "fprh(fcr6) /fprl(fcr7): %08x'%08x(", + reg.fprh, reg.fprl); + x = 43; + screen.Puts(x + 0, y, FA(reg.fprh, FPRH_ADDONE), "ADDONE"); + screen.Print(x + 7, y, "RM=%s)", rmstr[(reg.fprh >> 29) & 3]); + y++; + + x = 21; + screen.Print(0, y, "fpit(fcr8): %08x(OpCode=%%%c%c%c%c%c TD=%d", + reg.fpit, + ((reg.fpit >> 15) & 1) + '0', + ((reg.fpit >> 14) & 1) + '0', + ((reg.fpit >> 13) & 1) + '0', + ((reg.fpit >> 12) & 1) + '0', + ((reg.fpit >> 11) & 1) + '0', + ((reg.fpit >> 10) & 1)); + screen.Puts(x + 19, y, FA(reg.fpit, FPIT_EFINV), "EINV"); + screen.Puts(x + 24, y, FA(reg.fpit, FPIT_EFDVZ), "EDVZ"); + screen.Puts(x + 29, y, FA(reg.fpit, FPIT_EFUNF), "EUNF"); + screen.Puts(x + 34, y, FA(reg.fpit, FPIT_EFOVF), "EOVF"); + screen.Puts(x + 39, y, FA(reg.fpit, FPIT_EFINX), "EINX"); + screen.Print(x + 44, y, "Dest=r%d)", reg.fpit & m88100reg::FPIT_DEST); + y++; + + uint32 sign = (reg.fprh & m88100reg::FPRH_SIGN); + uint32 exp = (int32)reg.fpit >> 20; + uint32 mant_h = (reg.fprh & 0x000fffff); + uint32 mant_l = reg.fprl; + screen.Print(12, y, "result: %c%1x.%05x'%08x'grs e%+d", + (sign ? '-' : '+'), + ((reg.fprh >> 20) & 1), + mant_h, + mant_l, + exp - 0x3ff); + screen.Puts(38, y, FA(reg.fprh, FPRH_GUARD), "G"); + screen.Puts(39, y, FA(reg.fprh, FPRH_ROUND), "R"); + screen.Puts(40, y, FA(reg.fprh, FPRH_STICKY), "S"); + screen.Print(48, y, "($%03x)", exp & 0x7ff); + // Inf と NAN はこの形式だけからでは分かりづらいので別途追加で表示。 + // exp は符号拡張しているので指数部 $7ff は exp == -1。 + if (exp == 0xffffffff) { + x = 55; + if ((mant_h | mant_l) == 0) { + if (sign == 0) { + screen.Puts(x, y, "= +Inf"); + } else { + screen.Puts(x, y, "= -Inf"); + } + } else { + if (sign == 0) { + screen.Puts(x, y, "= +NAN"); + } else { + screen.Puts(x, y, "= -NAN"); + } + } + } + y++; + + screen.Print(0, y, "fpsr(fcr62):%08x(", reg.fpsr); + x = 21; + screen.Puts(x + 0, y, FA(reg.fpsr, FPSR_AFINV), "AFINV"); + screen.Puts(x + 6, y, FA(reg.fpsr, FPSR_AFDVZ), "AFDVZ"); + screen.Puts(x + 12, y, FA(reg.fpsr, FPSR_AFUNF), "AFUNF"); + screen.Puts(x + 18, y, FA(reg.fpsr, FPSR_AFOVF), "AFOVF"); + screen.Puts(x + 24, y, FA(reg.fpsr, FPSR_AFINX), "AFINX"); + screen.Puts(x + 29, y, ")"); + y++; + + screen.Print(0, y, "fpcr(fcr63):%08x(", reg.fpcr); + screen.Puts(x + 0, y, FA(reg.fpcr, FPCR_EFINV), "EFINV"); + screen.Puts(x + 6, y, FA(reg.fpcr, FPCR_EFDVZ), "EFDVZ"); + screen.Puts(x + 12, y, FA(reg.fpcr, FPCR_EFUNF), "EFUNF"); + screen.Puts(x + 18, y, FA(reg.fpcr, FPCR_EFOVF), "EFOVF"); + screen.Puts(x + 24, y, FA(reg.fpcr, FPCR_EFINX), "EFINX"); + screen.Print(x + 30, y, "RM=%s)", rmstr[(reg.fpcr >> 14) & 3]); }