--- nono/vm/mpu.cpp 2026/04/29 17:04:28 1.1 +++ nono/vm/mpu.cpp 2026/04/29 17:05:41 1.1.1.16 @@ -1,43 +1,42 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -// MPU + +// +// MPU (共通部分) // -#include +// Device +// | +-----------+ +// +--| MPUDevice | (全 MPU の共通部) +// +-----------+ +// | +// | +---------------+ +// +--| MainMPUDevice | (680x0/88xx0 の共通部) +// | +---------------+ +// | | +// | +-- MPU680x0Device +// | +-- MPU88xx0Device +// | +// +-- MPU64180Device + #include "mpu.h" -#include "m68030excep.h" +#include "config.h" #include "debugger.h" +#include "mainbus.h" #include "scheduler.h" -#include "vm.h" -#include "configfile.h" +#include "syncer.h" -MPUDevice *gMPU; -MPUATC *gMPUATC; -MPUBrHist *gMPUBrHist; +// +// 全 MPU 共通 +// // コンストラクタ -// -// reset_vector にはリセットベクタの番地を指定する。 -// 本来の MPU の動作は、リセット例外時にメモリの $0, $4 番地 (もっと言えば -// FC がプログラム空間の) から SP, PC を読み込むのだが、そのためにメモリ空間 -// を切り替える処理はリセット時にしか必要ないわりに結構手間である。そしてこの -// m68k 恒例のブートストラップ動作はソフトウェア的には一切観測する手段がない。 -// であれば、リセット例外時にこのアドレスから SP, PC の値を直接読み込んだと -// しても動作にはまったく違いは出ない。そしてバス・メモリ空間の実装も簡略化 -// できる。すごい楽。 -MPUDevice::MPUDevice(uint32 reset_vector) +MPUDevice::MPUDevice(uint objid_) + : inherited(objid_) { - logname = "mpu"; - devname = "MPU"; - monitor.Init(78, 16); - - cpu = m68030_init(reset_vector); - - // モニタ用の別オブジェクト - gMPUATC = new MPUATC(cpu); - gMPUBrHist = new MPUBrHist(cpu); } // デストラクタ @@ -45,422 +44,114 @@ MPUDevice::~MPUDevice() { } +// 初期化 bool MPUDevice::Init() { - if (gVM->GetType() == VM::TYPE_LUNA) { - // LUNA は 68881 固定 - cpu->has_fpu = 1; - } else { - // X68k は設定ファイルで指定 - cpu->has_fpu = gConfig->ReadInt("mpu_has_fpu"); - } - return true; -} + debugger = GetDebugger(); -// リセット -// (MPU が動くのはこの後 Scheduler::Run() を発行した後) -void -MPUDevice::ResetHard() -{ - m68030_exception_reset(cpu); -} + // パラメータは継承側でセットしている。 + scheduler->RegistEvent(exec_event); -// MPU を request で指定されたクロックサイクルだけ実行する。 -// 今回の実行サイクル数が request を越えた命令境界まで実行するので -// 実際には少しはみ出る。 -// また実行中にデバイスアクセスなどによりスケジューラにイベントが -// 追加されるとその次の命令境界で処理を打ち切って戻ってくる。 -// 戻り値は CPU の outer フラグ。 -uint32 -MPUDevice::Run(uint64 request) -{ - return m68030_run(cpu, request); + return true; } -// 割り込み発生を MPU に通知 (オートベクタ) void -MPUDevice::Interrupt(int level) +MPUDevice::PowerOff() { - int vector = M68K_EXCEP_LEVEL_BASE + level; - m68030_interrupt(cpu, level, vector); + scheduler->StopEvent(exec_event); } -// 割り込み発生を MPU に通知 (ベクタ) -void -MPUDevice::Interrupt(int level, int vector) -{ - m68030_interrupt(cpu, level, vector); -} -// A-Line 命令エミュレーションのコールバックを指定。 -void -MPUDevice::SetALineCallback(bool (*callback)(m68kcpu *, void*), void *arg) +// +// メイン MPU (680x0/88xx0) 共通部 +// + +// コンストラクタ +MainMPUDevice::MainMPUDevice() + : inherited(OBJ_MPU) { - cpu->aline_callback = callback; - cpu->aline_arg = arg; } -// F-Line 命令エミュレーションのコールバックを指定。 -void -MPUDevice::SetFLineCallback(bool (*callback)(m68kcpu *, void*), void *arg) +// デストラクタ +MainMPUDevice::~MainMPUDevice() { - cpu->fline_callback = callback; - cpu->fline_arg = arg; } -// モニター更新 (レジスタウィンドウ) +// 初期化 bool -MPUDevice::MonitorUpdate() +MainMPUDevice::Init() { - m68kreg reg; - uint32 ppc; - int x; - int y; - - monitor.Clear(); - - // ローカルにコピー - // ただし PPC だけ reg 構造体ではなく cpu クラス側にある - reg = cpu->reg; - ppc = cpu->ppc; - - // データレジスタ、アドレスレジスタ - for (int i = 0; i < 4; i++) { - monitor.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]); + if (inherited::Init() == false) { + return false; } - // 5列目 - x = 50; - // SR - monitor.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 - monitor.Print(x, 1, "VBR:%08x", reg.vbr); - - // *SP - uint ms = (reg.s ? 2 : 0) | (reg.m ? 1 : 0); // T1 T0 S M - if (ms == 3) { - // Supervisor (Master) mode (A7=MSP, USP/ISP) - monitor.Print(x, 2, TA::Disable, "USP:%08x", reg.usp); - monitor.Print(x, 3, TA::Disable, "ISP:%08x", reg.isp); - } else if (ms == 2) { - // Supervisor (Interrupt) mode (A7=ISP, USP/MSP) - monitor.Print(x, 2, TA::Disable, "USP:%08x", reg.usp); - monitor.Print(x, 3, TA::Disable, "MSP:%08x", reg.msp); - } else { - // User mode (A7=USP, ISP/MSP) - monitor.Print(x, 2, TA::Disable, "ISP:%08x", reg.isp); - monitor.Print(x, 3, TA::Disable, "MSP:%08x", reg.msp); - } + mainbus = GetMainbusDevice(); + syncer = GetSyncer(); - // 6列目 - // PC: 01234567 - // SFC:1 DFC:1 - // CACR:01234567 - // CAAR:01234567 - x = 66; - monitor.Print(x, 0, "PC: %08x", ppc); - monitor.Print(x, 1, "SFC:%d DFC:%d", reg.sfc, reg.dfc); - monitor.Print(x, 2, "CACR:%08x", reg.cacr); - monitor.Print(x, 3, "CAAR:%08x", reg.caar); - - // XXX どこに置くのがいいか - if (reg.state == 0) { - monitor.Print(25, 4, "State: Running"); - } else if (reg.state == CPU_REQ_STOP) { - monitor.Print(25, 4, "State: STOP instruction"); - } else if (reg.state == CPU_REQ_HALT) { - monitor.Print(25, 4, "State: Double Bus Fault"); + // MPU クロック。 + // 設定の "mpu-clock" は MHz 単位だが、変数 clock_khz は kHz 単位。 + int val; + const ConfigItem& item = gConfig->Find("mpu-clock"); + if (item.TryFixedDecimal(&val, 3) == false) { + item.Err(); + return false; } - - // MMU - x = 0; - y = 4; - monitor.Print(x, y++, ""); - monitor.Print(x, y, "SRP:%08x_%08x", reg.srp.h, reg.srp.l); - monitor.Print(x, y + 1, "CRP:%08x_%08x", reg.crp.h, reg.crp.l); - x += 23; - for (int i = 0; i < 2; i++) { - uint32 tt = reg.tt[i]; - monitor.Print(x, y + i, "TT%d:%08x(%c%c%c)", - i, tt, - (tt & m68030TT::E) ? 'E' : '-', - (tt & m68030TT::CI) ? 'C' : '-', - (tt & m68030TT::RWM) ? '-' : ((tt & m68030TT::RW) ? 'R' : 'W')); + clock_khz = (uint)val; + // 1MHz 未満はエラー。実際 10MHz 未満でもいい気がするけど + if (clock_khz < 1000) { + item.Err(); + return false; } - x += 19; - monitor.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' : '-'); - monitor.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)); - - // FPU - x = 0; - y = 7; - monitor.Print(x, y++, ""); - for (int i = 0; i < 8; i++) { - monitor.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], - "not yet"); + clock_nsec = 1000 * 1000 / clock_khz; + if (clock_nsec < 1) { + item.Err("Clock speed too high"); + return false; } - x = 51; - - // FPCR - static const char * const rpstr[] = { - ".EXT", - ".SGL", - ".DBL", - ".???", - }; - static const char * const rmstr[] = { - "Near", - "Zero", - "Minus", - "Plus", - }; - uint32 fpcr = reg.fpframe.fpf_fpcr; - monitor.Print(x, y++, "FPCR:%04x", fpcr); - monitor.Print(x + 2, y++, "%s %s %s %s %s %s %s %s", - (fpcr & 0x8000) ? "BS" : "--", - (fpcr & 0x4000) ? "SN" : "--", - (fpcr & 0x2000) ? "OP" : "--", - (fpcr & 0x1000) ? "OV" : "--", - (fpcr & 0x0800) ? "UF" : "--", - (fpcr & 0x0400) ? "DZ" : "--", - (fpcr & 0x0200) ? "I2" : "--", - (fpcr & 0x0100) ? "I1" : "--"); - monitor.Print(x + 2, y++, "RP=%s RM=%s", - rpstr[(fpcr >> 6) & 3], - rmstr[(fpcr >> 4) & 3]); - - // FPSR - uint32 fpsr = reg.fpframe.fpf_fpsr; - uint32 cc = fpsr >> 24; - monitor.Print(x, y++, "FPSR:%08x", fpsr); - monitor.Print(x + 2, y++, "%c %c %s %s Q=$%02x", - (cc & 0x08) ? 'N' : '-', - (cc & 0x04) ? 'Z' : '-', - (cc & 0x02) ? "Inf" : "---", - (cc & 0x01) ? "NAN" : "---", - (fpsr >> 16) & 0xff); - monitor.Print(x + 2, y++, "%s %s %s %s %s %s %s %s", - (fpsr & 0x8000) ? "BS" : "--", - (fpsr & 0x4000) ? "SN" : "--", - (fpsr & 0x2000) ? "OP" : "--", - (fpsr & 0x1000) ? "OV" : "--", - (fpsr & 0x0800) ? "UF" : "--", - (fpsr & 0x0400) ? "DZ" : "--", - (fpsr & 0x0200) ? "I2" : "--", - (fpsr & 0x0100) ? "I1" : "--"); - monitor.Print(x + 2, y++, "%s %s %s %s %s", - (fpsr & 0x80) ? "IOP" : "---", - (fpsr & 0x40) ? "OVFL" : "----", - (fpsr & 0x20) ? "UNFL" : "----", - (fpsr & 0x10) ? "DZ" : "--", - (fpsr & 0x08) ? "INEX" : "----"); + scheduler->ConnectMessage(MessageID::MPU_TRACE_MAIN, this, + ToMessageCallback(&MainMPUDevice::TraceMessage)); - // FPIAR - monitor.Print(x, y++, "FPIAR:%08x", reg.fpframe.fpf_fpiar); + exec_event.SetName("MPU Execute"); return true; } -// CPU 側からのコールバック。RESET 命令ハンドラ +// MPU トレース状態設定要求メッセージ void -m68030_reset_inst(m68kcpu *cpu) -{ - gVM->ResetSoft(); -} - -// -// ATC モニタ -// -MPUATC::MPUATC(m68kcpu *arg) -{ - cpu = arg; -#if !defined(ATC_SINGLE) - monitor.Init(131, m68030ATCTable::LINES + 3); -#else - monitor.Init(31, 24); -#endif -} - -bool -MPUATC::MonitorUpdate() +MainMPUDevice::TraceMessage(MessageID msgid, uint32 arg) { - m68030ATCTable *table; -#if !defined(ATC_SINGLE) - struct { - int fc; - const char *name; - } fclist[] = { - { 1, "User/Data" }, - { 2, "User/Program" }, - { 5, "Super/Data" }, - { 6, "Super/Program" }, - }; - - monitor.Clear(); - - // 行数は左端にだけ - for (int i = 0; i < m68030ATCTable::LINES; i++) { - monitor.Print(0, i + 2, "%02d:", i); + // リセット中は SetTrace しない + if (resetting) { + return; } - for (int t = 0; t < countof(fclist); t++) { - int x = t * 32 + 4; - int fc = fclist[t].fc; - const char *name = fclist[t].name; - double r; - int i; - - table = &cpu->atc.tables[fc]; - monitor.Print(x, 0, "FC=%d %s", fc, name); - monitor.Print(x, 1, "LAddr PAddr Flag Hit%% Age"); - // 先に統計用の母数を計算 - uint64 total = 0; - for (i = 0; i < countof(table->hit); i++) { - total += table->hit[i]; - } - total += table->hit_head; - - // エントリ表示 - for (i = 0; i < countof(table->line); i++) { - m68030ATCLine& a = table->line[i]; - int attr; - if (a.IsInvalid()) { - attr = TA::Disable; - } else if (table->head == &a) { - attr = TA::Em; - } else { - attr = TA::Normal; - } - - monitor.Print(x, i + 2, attr, "%08x %08x %c%c%c", - a.GetLAddr(), - a.GetPAddr(), - a.IsBusError() ? 'B' : '-', - a.IsWProtect() ? 'P' : '-', - a.IsModified() ? 'M' : '-'); - } - // ヘッドのヒット率 - monitor.Print(x + 0, i + 2, "head"); - r = (double)table->hit_head * 100 / total; - if (!isnan(r)) { - monitor.Print(x + 5, i + 2, "%4.1f%%", r); - } - // 配列のヒット率とミス率 - monitor.Print(x + 17, i + 2, "miss"); - for (i = 0; i < countof(table->hit); i++) { - r = (double)(table->hit[i] * 100) / total; - if (!isnan(r)) { - monitor.Print(x + 22, i + 2, "%4.1f%%", r); - } - } - // カウンタ - // 分かりやすさのため最大値からの差(距離)を表示 - for (i = 0; i < countof(table->line); i++) { - m68030ATCLine& a = table->line[i]; - if (!a.IsInvalid()) { - monitor.Print(x + 27, i + 2, "%4d", table->head->age - a.age); - } - } - } -#else - m68030ATCLine *a; - int i; - - table = &cpu->atc.tables[0]; - monitor.Clear(); - - monitor.Print(0, 0, "No. FC LAddr PAddr Flag"); - for (a = table->head, i = 0; a; a = a->next, i++) { - monitor.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; - } - monitor.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' : '-'); - } - } -#endif - return true; -} - -// -// ブランチ履歴モニタ -// -MPUBrHist::MPUBrHist(m68kcpu *arg) -{ - cpu = arg; - monitor.Init(70, 16); + // デバッガから MPU のトレース状態を設定してくれと言われた + SetTrace((bool)arg); } -bool -MPUBrHist::MonitorUpdate() +void +MainMPUDevice::ChangeState(uint32 new_state) { - int i; - uint8 t; + // new_mode (CPU_STATE_*) と + // RequestCPUMode() の引数 Syncer::SYNCMODE_CPU_* は + // 実は同じ値なのでそのまま渡してよいことにする。 + static_assert(CPU_STATE_NORMAL == Syncer::SYNCMODE_CPU_NORMAL, ""); + static_assert(CPU_STATE_STOP == Syncer::SYNCMODE_CPU_STOP, ""); + static_assert(CPU_STATE_HALT == Syncer::SYNCMODE_CPU_HALT, ""); - // 0 1 2 3 4 5 6 - // 0123456789012345678901234567890123456789012345678901234567890123456789 - // 01 $01234567->$01234567 x123456789 16 $01234567->$01234567 x123456789 - - monitor.Clear(); - - // top は現在の位置。 - // brhist[] は古い順に書き込まれるが、表示は新しい順にしたい。 - // ブランチ履歴は書き込み最適化のため256エントリあるが、そんなに - // 奥のほうが気になることはないだろう。 - t = cpu->brhist_top; - for (i = 0; i < 32; i++) { - m68kbrhist& h = cpu->brhist[t--]; - int x = (i / 16) * 36; - int y = i % 16; - if (h.count == 0) { - continue; - } - monitor.Print(x, y, "%02d:$%08x->$%08x", i, - h.from, h.to); - if (h.count > 1) { - monitor.Print(x + 24, y, "x%9u", h.count); - } + if (cpu_state != new_state) { + cpu_state = new_state; + // スケジューラに通知 + syncer->RequestCPUMode(cpu_state); } - return true; } + +// Round Mode (モニタ表示用) +/*static*/ const char * const +MainMPUDevice::rmstr[4] = { + "Near", + "Zero", + "Minus", + "Plus", +};