--- nono/vm/mpu680x0.cpp 2026/04/29 17:04:32 1.1.1.1 +++ nono/vm/mpu680x0.cpp 2026/04/29 17:04:50 1.1.1.7 @@ -1,19 +1,19 @@ // // 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; // コンストラクタ // @@ -27,13 +27,15 @@ std::unique_ptr gMPUBrHist; // できる。すごい楽。 MPU680x0Device::MPU680x0Device(uint32 reset_vector) { - monitor.Init(79, 16); + monitor_size = nnSize(79, 16); cpu = m68030_init(reset_vector); + // イベントコールバック設定 + event.func = (DeviceCallback_t)&MPU680x0Device::Callback; + // モニタ用の別オブジェクト gMPUATC.reset(new MPUATC(cpu)); - gMPUBrHist.reset(new MPUBrHist(cpu)); } // デストラクタ @@ -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,61 @@ MPU680x0Device::Init() return true; } -// リセット -// (MPU が動くのはこの後 Scheduler::Run() を発行した後) +// イベントコールバック +// ev.code は +// 0x80000000: リセット例外 +// 0x0 〜 0x7: 新しい割り込みレベルを通知 (0-7) void -MPU680x0Device::ResetHard() +MPU680x0Device::Callback(Event& ev) { - m68030_exception_reset(cpu); + if (__predict_false((int32)ev.code < 0)) { + // リセット例外 + m68030_request_reset_exception(cpu); + } else { + // コードが 0-7 なら新しい割り込みレベルの通知 + int level = ev.code; + m68030_interrupt(cpu, level); + } } -// MPU を request で指定されたクロックサイクルだけ実行する。 -// 今回の実行サイクル数が request を越えた命令境界まで実行するので -// 実際には少しはみ出る。 -// また実行中にデバイスアクセスなどによりスケジューラにイベントが -// 追加されるとその次の命令境界で処理を打ち切って戻ってくる。 -// 戻り値は CPU の outer フラグ。 +// 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,9 +145,17 @@ 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::MonitorUpdate(TextScreen& monitor) { m68kreg reg; uint32 ppc; @@ -180,17 +221,17 @@ MPU680x0Device::MonitorUpdate() // XXX どこに置くのがいいか if (reg.state == 0) { - monitor.Print(25, 4, "State: Running"); + monitor.Puts(25, 4, "State: Running"); } else if (reg.state == CPU_REQ_STOP) { - monitor.Print(25, 4, "State: STOP instruction"); + monitor.Puts(25, 4, "State: STOP instruction"); } else if (reg.state == CPU_REQ_HALT) { - monitor.Print(25, 4, "State: Double Bus Fault"); + monitor.Puts(25, 4, TA::On, "State: Double Bus Fault"); } // MMU x = 0; y = 4; - monitor.Print(x, y++, ""); + monitor.Puts(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; @@ -222,7 +263,7 @@ MPU680x0Device::MonitorUpdate() // FPU x = 0; y = 7; - monitor.Print(x, y++, ""); + monitor.Puts(x, y++, ""); for (int i = 0; i < 8; i++) { monitor.Print(x, y + i, "FP%d:%04x_%08x_%08x (%-20s)", i, @@ -290,32 +331,30 @@ MPU680x0Device::MonitorUpdate() // FPIAR monitor.Print(x, y++, "FPIAR:%08x", reg.fpframe.fpf_fpiar); - - return true; } // CPU 側からのコールバック。RESET 命令ハンドラ void m68030_reset_inst(m68kcpu *cpu) { - gVM->ResetSoft(); + gVM->ResetByMPU(); } // // ATC モニタ // -MPUATC::MPUATC(m68kcpu *arg) +MPUATC::MPUATC(m68kcpu *cpu_) { - cpu = arg; + cpu = cpu_; #if !defined(ATC_SINGLE) - monitor.Init(131, m68030ATCTable::LINES + 3); + monitor_size = nnSize(131, m68030ATCTable::LINES + 3); #else - monitor.Init(31, 24); + monitor_size = nnSize(31, 24); #endif } -bool -MPUATC::MonitorUpdate() +void +MPUATC::MonitorUpdate(TextScreen& monitor) { m68030ATCTable *table; #if !defined(ATC_SINGLE) @@ -345,7 +384,7 @@ MPUATC::MonitorUpdate() table = &cpu->atc.tables[fc]; monitor.Print(x, 0, "FC=%d %s", fc, name); - monitor.Print(x, 1, "LAddr PAddr Flag Hit%% Age"); + monitor.Puts(x, 1, "LAddr PAddr Flag Hit% Age"); // 先に統計用の母数を計算 uint64 total = 0; for (i = 0; i < countof(table->hit); i++) { @@ -356,7 +395,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) { @@ -373,13 +412,13 @@ MPUATC::MonitorUpdate() a.IsModified() ? 'M' : '-'); } // ヘッドのヒット率 - monitor.Print(x + 0, i + 2, "head"); + monitor.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); } // 配列のヒット率とミス率 - monitor.Print(x + 17, i + 2, "miss"); + monitor.Puts(x + 17, i + 2, "miss"); for (i = 0; i < countof(table->hit); i++) { r = (double)(table->hit[i] * 100) / total; if (!isnan(r)) { @@ -402,7 +441,7 @@ MPUATC::MonitorUpdate() table = &cpu->atc.tables[0]; monitor.Clear(); - monitor.Print(0, 0, "No. FC LAddr PAddr Flag"); + monitor.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); if (!a->IsInvalid()) { @@ -423,47 +462,4 @@ MPUATC::MonitorUpdate() } } #endif - return true; -} - -// -// ブランチ履歴モニタ -// -MPUBrHist::MPUBrHist(m68kcpu *arg) -{ - cpu = arg; - monitor.Init(70, 16); -} - -bool -MPUBrHist::MonitorUpdate() -{ - 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; }