--- nono/vm/mpu680x0.cpp 2026/04/29 17:04:32 1.1 +++ nono/vm/mpu680x0.cpp 2026/04/29 17:04:55 1.1.1.9 @@ -1,39 +1,41 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // // MPU (m680x0) #include "mpu680x0.h" -#include "m68030excep.h" #include "config.h" +#include "m68030excep.h" #include "mainapp.h" #include "vm.h" #include -std::unique_ptr gMPUATC; -std::unique_ptr gMPUBrHist; - // コンストラクタ -// -// reset_vector にはリセットベクタの番地を指定する。 -// 本来の MPU の動作は、リセット例外時にメモリの $0, $4 番地 (もっと言えば -// FC がプログラム空間の) から SP, PC を読み込むのだが、そのためにメモリ空間 -// を切り替える処理はリセット時にしか必要ないわりに結構手間である。そしてこの -// m68k 恒例のブートストラップ動作はソフトウェア的には一切観測する手段がない。 -// であれば、リセット例外時にこのアドレスから SP, PC の値を直接読み込んだと -// しても動作にはまったく違いは出ない。そしてバス・メモリ空間の実装も簡略化 -// できる。すごい楽。 -MPU680x0Device::MPU680x0Device(uint32 reset_vector) -{ - monitor.Init(79, 16); - - cpu = m68030_init(reset_vector); - - // モニタ用の別オブジェクト - gMPUATC.reset(new MPUATC(cpu)); - gMPUBrHist.reset(new MPUBrHist(cpu)); +MPU680x0Device::MPU680x0Device() + : inherited("MPU(68030)") +{ + cpu = m68030_init(); + + // イベントコールバック設定 + event.SetName("MPU"); + event.func = (DeviceCallback_t)&MPU680x0Device::Callback; + + // レジスタモニター + reg_monitor.func = (MonitorCallback_t)&MPU680x0Device::MonitorUpdateReg; + reg_monitor.SetSize(79, 16); + reg_monitor.Regist(ID_MONITOR_MPUREG); + + // ATC モニター + atc_monitor.func = (MonitorCallback_t)&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); } // デストラクタ @@ -44,13 +46,22 @@ MPU680x0Device::~MPU680x0Device() bool MPU680x0Device::Init() { + // 親クラス + if (inherited::Init() == false) { + return false; + } + // MPU クロックは親 Init() で読み込んで、ここで cpu にセットする + m68030_set_clockspeed(cpu, clock_khz); + // 割り込み時のディレイ + intdelay = cpu->Cycle2Vtime(4); + // FPU (6888x) - if (gMainApp.GetVMType() == VMTYPE_LUNA) { + if (gMainApp.GetVMType() == VMTYPE_LUNA1) { // LUNA は 68881 固定 (設定は見ない) cpu->has_fpu = 1; } else { // X68k は設定による - const ConfigItem& item = gConfig->Get("mpu-has-fpu"); + const ConfigItem& item = gConfig->Find("mpu-has-fpu"); cpu->has_fpu = item.AsInt(); if (cpu->has_fpu < 0 || cpu->has_fpu > 2) { item.Err(); @@ -61,39 +72,68 @@ MPU680x0Device::Init() return true; } -// リセット -// (MPU が動くのはこの後 Scheduler::Run() を発行した後) +bool +MPU680x0Device::PowerOn() +{ + m68030_power_on(cpu); + return true; +} + void MPU680x0Device::ResetHard() { - m68030_exception_reset(cpu); + // リセット例外 + m68030_request_reset_exception(cpu); } -// MPU を request で指定されたクロックサイクルだけ実行する。 -// 今回の実行サイクル数が request を越えた命令境界まで実行するので -// 実際には少しはみ出る。 -// また実行中にデバイスアクセスなどによりスケジューラにイベントが -// 追加されるとその次の命令境界で処理を打ち切って戻ってくる。 -// 戻り値は CPU の outer フラグ。 +// イベントコールバック +// ev.code は +// 0x0 〜 0x7: 新しい割り込みレベルを通知 (0-7) +void +MPU680x0Device::Callback(Event& ev) +{ + int level = ev.code; + m68030_interrupt(cpu, level); +} + +// MPU を vtime [nsec] 分実行する。 +// 戻り値は outer フラグ。 uint32 -MPU680x0Device::Run(uint64 request) +MPU680x0Device::Run(uint32 vtime) { - return m68030_run(cpu, request); + return m68030_run(cpu, vtime); } -// 割り込み発生を MPU に通知 (オートベクタ) -void -MPU680x0Device::Interrupt(int level) +// 現在の仮想時刻 [nsec] を取得する +uint64 +MPU680x0Device::GetVirtTime() const { - int vector = M68K_EXCEP_LEVEL_BASE + level; - m68030_interrupt(cpu, level, vector); + return m68030_get_vtime(cpu); } -// 割り込み発生を MPU に通知 (ベクタ) +// 割り込みレベルが変わったことを MPU に通知。 void -MPU680x0Device::Interrupt(int level, int vector) +MPU680x0Device::Interrupt(int level) { - m68030_interrupt(cpu, level, vector); + 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; + } + event.code = level; + event.Start(); } // A-Line 命令エミュレーションのコールバックを指定。 @@ -112,16 +152,24 @@ MPU680x0Device::SetFLineCallback(bool (* cpu->fline_arg = arg; } +// ダブルバスフォールトのコールバックを指定。 +void +MPU680x0Device::SetHaltCallback(void (*callback)(void *), void *arg) +{ + cpu->halt_callback = callback; + cpu->halt_arg = arg; +} + // モニター更新 (レジスタウィンドウ) -bool -MPU680x0Device::MonitorUpdate() +void +MPU680x0Device::MonitorUpdateReg(Monitor *, TextScreen& screen) { m68kreg reg; uint32 ppc; int x; int y; - monitor.Clear(); + screen.Clear(); // ローカルにコピー // ただし PPC だけ reg 構造体ではなく cpu クラス側にある @@ -130,7 +178,7 @@ MPU680x0Device::MonitorUpdate() // データレジスタ、アドレスレジスタ for (int i = 0; i < 4; i++) { - monitor.Print(0, i, "D%d:%08x D%d:%08x A%d:%08x A%d:%08x", + 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], @@ -140,7 +188,7 @@ MPU680x0Device::MonitorUpdate() // 5列目 x = 50; // SR - monitor.Print(x, 0, "SR:%04x(%c%c%c%c%c)", + 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' : '-', @@ -149,22 +197,22 @@ MPU680x0Device::MonitorUpdate() (reg.ccr.IsC()) ? 'C' : '-'); // VBR - monitor.Print(x, 1, "VBR:%08x", reg.vbr); + screen.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); + screen.Print(x, 2, TA::Disable, "USP:%08x", reg.usp); + screen.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); + screen.Print(x, 2, TA::Disable, "USP:%08x", reg.usp); + screen.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); + screen.Print(x, 2, TA::Disable, "ISP:%08x", reg.isp); + screen.Print(x, 3, TA::Disable, "MSP:%08x", reg.msp); } // 6列目 @@ -173,42 +221,42 @@ MPU680x0Device::MonitorUpdate() // 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); + 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) { - monitor.Print(25, 4, "State: Running"); + screen.Puts(25, 4, "State: Running"); } else if (reg.state == CPU_REQ_STOP) { - monitor.Print(25, 4, "State: STOP instruction"); + screen.Puts(25, 4, "State: STOP instruction"); } else if (reg.state == CPU_REQ_HALT) { - monitor.Print(25, 4, "State: Double Bus Fault"); + screen.Puts(25, 4, TA::On, "State: Double Bus Fault"); } // 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); + 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; for (int i = 0; i < 2; i++) { uint32 tt = reg.tt[i]; - monitor.Print(x, y + i, "TT%d:%08x(%c%c%c)", + screen.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')); } x += 19; - monitor.Print(x, y, "TC:%08x(%c%c%c)", + 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' : '-'); - monitor.Print(x, y + 1, "MMUSR: %04x(%c%c%c%c%c%c%c N=%d)", + 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' : '-', @@ -222,9 +270,9 @@ MPU680x0Device::MonitorUpdate() // FPU x = 0; y = 7; - monitor.Print(x, y++, ""); + screen.Puts(x, y++, ""); for (int i = 0; i < 8; i++) { - monitor.Print(x, y + i, "FP%d:%04x_%08x_%08x (%-20s)", + 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], @@ -248,8 +296,8 @@ MPU680x0Device::MonitorUpdate() "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", + screen.Print(x, y++, "FPCR:%04x", fpcr); + screen.Print(x + 2, y++, "%s %s %s %s %s %s %s %s", (fpcr & 0x8000) ? "BS" : "--", (fpcr & 0x4000) ? "SN" : "--", (fpcr & 0x2000) ? "OP" : "--", @@ -258,21 +306,21 @@ MPU680x0Device::MonitorUpdate() (fpcr & 0x0400) ? "DZ" : "--", (fpcr & 0x0200) ? "I2" : "--", (fpcr & 0x0100) ? "I1" : "--"); - monitor.Print(x + 2, y++, "RP=%s RM=%s", + screen.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", + screen.Print(x, y++, "FPSR:%08x", fpsr); + screen.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", + screen.Print(x + 2, y++, "%s %s %s %s %s %s %s %s", (fpsr & 0x8000) ? "BS" : "--", (fpsr & 0x4000) ? "SN" : "--", (fpsr & 0x2000) ? "OP" : "--", @@ -281,7 +329,7 @@ MPU680x0Device::MonitorUpdate() (fpsr & 0x0400) ? "DZ" : "--", (fpsr & 0x0200) ? "I2" : "--", (fpsr & 0x0100) ? "I1" : "--"); - monitor.Print(x + 2, y++, "%s %s %s %s %s", + screen.Print(x + 2, y++, "%s %s %s %s %s", (fpsr & 0x80) ? "IOP" : "---", (fpsr & 0x40) ? "OVFL" : "----", (fpsr & 0x20) ? "UNFL" : "----", @@ -289,33 +337,12 @@ MPU680x0Device::MonitorUpdate() (fpsr & 0x08) ? "INEX" : "----"); // FPIAR - monitor.Print(x, y++, "FPIAR:%08x", reg.fpframe.fpf_fpiar); - - return true; + screen.Print(x, y++, "FPIAR:%08x", reg.fpframe.fpf_fpiar); } -// CPU 側からのコールバック。RESET 命令ハンドラ +// モニター更新 (ATC) 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() +MPU680x0Device::MonitorUpdateATC(Monitor *, TextScreen& screen) { m68030ATCTable *table; #if !defined(ATC_SINGLE) @@ -329,11 +356,11 @@ MPUATC::MonitorUpdate() { 6, "Super/Program" }, }; - monitor.Clear(); + screen.Clear(); // 行数は左端にだけ for (int i = 0; i < m68030ATCTable::LINES; i++) { - monitor.Print(0, i + 2, "%02d:", i); + screen.Print(0, i + 2, "%02d:", i); } for (int t = 0; t < countof(fclist); t++) { @@ -344,8 +371,8 @@ MPUATC::MonitorUpdate() 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"); + 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++) { @@ -356,7 +383,7 @@ MPUATC::MonitorUpdate() // エントリ表示 for (i = 0; i < countof(table->line); i++) { m68030ATCLine& a = table->line[i]; - int attr; + TA attr; if (a.IsInvalid()) { attr = TA::Disable; } else if (table->head == &a) { @@ -365,7 +392,7 @@ MPUATC::MonitorUpdate() attr = TA::Normal; } - monitor.Print(x, i + 2, attr, "%08x %08x %c%c%c", + screen.Print(x, i + 2, attr, "%08x %08x %c%c%c", a.GetLAddr(), a.GetPAddr(), a.IsBusError() ? 'B' : '-', @@ -373,17 +400,17 @@ MPUATC::MonitorUpdate() a.IsModified() ? 'M' : '-'); } // ヘッドのヒット率 - monitor.Print(x + 0, i + 2, "head"); + screen.Puts(x + 0, i + 2, "head"); r = (double)table->hit_head * 100 / total; if (!isnan(r)) { - monitor.Print(x + 5, i + 2, "%4.1f%%", r); + screen.Print(x + 5, i + 2, "%4.1f%%", r); } // 配列のヒット率とミス率 - monitor.Print(x + 17, i + 2, "miss"); + screen.Puts(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); + screen.Print(x + 22, i + 2, "%4.1f%%", r); } } // カウンタ @@ -391,7 +418,7 @@ MPUATC::MonitorUpdate() 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); + screen.Print(x + 27, i + 2, "%4d", table->head->age - a.age); } } } @@ -400,11 +427,11 @@ MPUATC::MonitorUpdate() int i; table = &cpu->atc.tables[0]; - monitor.Clear(); + screen.Clear(); - monitor.Print(0, 0, "No. FC LAddr PAddr Flag"); + screen.Puts(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); + screen.Print(0, i + 1, "%02d:", i); if (!a->IsInvalid()) { char dp; switch (a->fc & 3) { @@ -412,7 +439,7 @@ MPUATC::MonitorUpdate() case 2: dp = 'P'; break; default: dp = '?'; break; } - monitor.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", a->fc, (a->fc & 4) ? 'S' : 'U', dp, @@ -423,47 +450,11 @@ MPUATC::MonitorUpdate() } } #endif - return true; -} - -// -// ブランチ履歴モニタ -// -MPUBrHist::MPUBrHist(m68kcpu *arg) -{ - cpu = arg; - monitor.Init(70, 16); } -bool -MPUBrHist::MonitorUpdate() +// CPU 側からのコールバック。RESET 命令ハンドラ +void +m68030_reset_inst(m68kcpu *cpu) { - int i; - uint8 t; - - // 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); - } - } - return true; + gVM->ResetByMPU(); }