--- nono/vm/mpu680x0.cpp 2026/04/29 17:05:32 1.1.1.18 +++ nono/vm/mpu680x0.cpp 2026/04/29 17:05:49 1.1.1.21 @@ -8,9 +8,22 @@ // MPU (MC68030/MC68040) // +// MPU/FPU 種別について: +// +// 構造上やむをえないが MPU が 68030 なら FPU は fpu-type で指定し、 +// MPU が 68040/68LC040 なら mpu-type で MPU/FPU を指定する。 +// +// mpu-type fpu-type +// -------- -------- +// 68030 none : 68030、FPU なし +// 68030 68881 : 68030 + 68881 +// 68LC040 - : 68040 (FPU なし) +// 68040 - : 68040 (FPU あり) + #include "mpu680x0.h" #include "config.h" #include "debugger.h" +#include "event.h" #include "exttostr.h" #include "interrupt.h" #include "m68030cache.h" @@ -18,12 +31,9 @@ #include "mainapp.h" #include "mainbus.h" #include "scheduler.h" -#include "syncer.h" #include "uimessage.h" #include "vectortable.h" -static Syncer *gSyncer; - // ブートストラップ。設定に応じてインスタンスを生成して返す。 // ここではとりあえず 68040 かそれ以外なら 68030 を生成しておき、 // Init() でもう一度 (機種情報も含めて) チェックする。 @@ -36,8 +46,8 @@ NewMPU680x0Device() std::string mpu_type = item.AsString(); // 設定に応じて継承側のクラスを作成する。 - // この段階では 68040 かそれ以外だけ判断する。 - if (mpu_type == "68040") { + // この段階では知らないものは一旦 68030 にしておく。 + if (mpu_type == "68040" || strcasecmp(mpu_type.c_str(), "68LC040") == 0) { return new MPU68040Device(); } else { return new MPU68030Device(); @@ -61,8 +71,6 @@ MPU680x0Device::MPU680x0Device() // 例外カウンタ excep_counter.resize(256); - - SetTrace(false); } // デストラクタ @@ -79,19 +87,28 @@ MPU680x0Device::Init() } // なる早で、もう一度 MPU 設定を読んで判定。そのついでに FPU も判定。 - const ConfigItem& mputype_item = gConfig->Find("mpu-type"); - std::string mputype = mputype_item.AsString(); - // mpu-type の設定値が不正な場合でも (コンストラクト時点ではまだエラーを + // "mpu-type" の設定値が不正な場合でも (コンストラクト時点ではまだエラーを // 報告できない構造なので) 一旦 MPU68030Device インスタンスとしてここに // 来る。なのでここで改めてチェックしてエラー報告する必要がある。 // mpu_type (メンバ変数) 自体はすでにセットされている。 - if (mputype != "68030" && mputype != "68040") { + const ConfigItem& mputype_item = gConfig->Find("mpu-type"); + std::string mputype = mputype_item.AsString(); + if (mputype == "68030") { + // FPU はこのすぐ次で機種ごとに選ぶ。 + } else if (mputype == "68040") { + fpu_type = m680x0FPUType::M68040; + } else if (strcasecmp(mputype.c_str(), "68LC040") == 0) { + fpu_type = m680x0FPUType::M68LC040; + // 名前も再設定? + mpu_name = "MC68LC040"; + SetName("MPU(68LC040)"); + } else { mputype_item.Err(); return false; } // 機種ごとに MPU/FPU のサポート状況が違う。 - // fpu_type はここでセットする。 + // 68030 なら fpu_type はここでセットする。 switch (gMainApp.GetVMType()) { case VMType::LUNA1: case VMType::NEWS: @@ -122,8 +139,7 @@ MPU680x0Device::Init() return false; } } else { - // 68040 なら FPU は現状 !LC040 固定。 - fpu_type = m680x0FPUType::M68040; + // 68040 なら FPU はすぐ上で設定済み。 } break; @@ -132,11 +148,8 @@ MPU680x0Device::Init() break; } - debugger = GetDebugger(); interruptdev = GetInterruptDevice(); vectortable = GetVectorTable(); - // static 関数から参照するため。うーん…。 - gSyncer = syncer; // 割り込みイベントコールバック設定。 // @@ -146,10 +159,11 @@ MPU680x0Device::Init() // その後 /IPEND をアサートするのはその次のアップエッジ。 // 合わせると、IPL0-2 が変化してから /IPEND がアサートされるまでは // 2 ~ 3 クロックかかる。(Figure 8-4) - intr_event.func = ToEventCallback(&MPU680x0Device::InterruptCallback); - intr_event.time = 3 * clock_nsec; - intr_event.SetName(GetName() + " Interrupt"); - scheduler->RegistEvent(intr_event); + auto evman = GetEventManager(); + intr_event = evman->Regist(this, + ToEventCallback(&MPU680x0Device::InterruptCallback), + GetName() + " Interrupt"); + intr_event->time = 3 * clock_nsec; // レジスタモニタの行数。 int height = 9; @@ -200,16 +214,16 @@ MPU680x0Device::ResetHard(bool poweron) // SP, PC をフェッチするという順序にしないといけないため、リセット例外が // 起動するまでに時間がかかるというところを都合よく真似ておく。 - exec_event.func = ToEventCallback(&MPU680x0Device::ResetCallback); - exec_event.time = 520 * clock_nsec; - exec_event.SetName(GetName() + " Reset"); + exec_event->func = ToEventCallback(&MPU680x0Device::ResetCallback); + exec_event->time = 520 * clock_nsec; + exec_event->SetName(GetName() + " Reset"); scheduler->RestartEvent(exec_event); } // リセット例外コールバック。 // MPU リセットから規定時間後に呼ばれる。 void -MPU680x0Device::ResetCallback(Event& ev) +MPU680x0Device::ResetCallback(Event *ev) { resetting = false; intr_pending = false; @@ -223,9 +237,9 @@ MPU680x0Device::ResetCallback(Event& ev) // 以降は通常走行。 if (cpu_state == CPU_STATE_NORMAL) { - exec_event.func = exec_short; - exec_event.time = cycle * clock_nsec; - exec_event.SetName(GetName() + " Execute"); + exec_event->func = exec_short; + exec_event->time = cycle * clock_nsec; + exec_event->SetName(GetName() + " Execute"); scheduler->RestartEvent(exec_event); } else { // リセット例外でダブルバスフォールトならここで停止。 @@ -237,7 +251,7 @@ MPU680x0Device::ResetCallback(Event& ev) // ここでは割り込みをチェックし、必要なら割り込み処理を起動、 // そうでなければ通常の命令実行を行う。 void -MPU680x0Device::ExecFull(Event& ev) +MPU680x0Device::ExecFull(Event *ev) { if (cpu_state == CPU_STATE_HALT) { // HALT なら停止。 @@ -258,12 +272,12 @@ MPU680x0Device::ExecFull(Event& ev) intr_pending = false; // 前の命令実行後となるここで割り込みを起動 - ev.time = ExceptionInterrupt(); + ev->time = ExceptionInterrupt(); scheduler->StartEvent(ev); } else { if (cpu_state == CPU_STATE_NORMAL) { // コールバックを通常処理に戻してから... - exec_event.func = exec_short; + exec_event->func = exec_short; // この場で次の命令を実行。 ExecShort(ev); @@ -275,7 +289,7 @@ MPU680x0Device::ExecFull(Event& ev) // トレース実行 void -MPU680x0Device::ExecTrace(Event& ev) +MPU680x0Device::ExecTrace(Event *ev) { debugger->ExecMain(); ExecFull(ev); @@ -292,11 +306,11 @@ MPU680x0Device::SetTrace(bool enable) exec_short = ToEventCallback(&MPU680x0Device::ExecShort); exec_full = ToEventCallback(&MPU680x0Device::ExecFull); } - exec_event.func = exec_full; + exec_event->func = exec_full; // STOP 命令中にオンにするには、停止しているイベントを起こす。 // STOP 命令中にオフにする場合は、何もしなくていい。 - if (enable && exec_event.IsRunning() == false) { + if (enable && exec_event->IsRunning() == false) { scheduler->StartEvent(exec_event); } } @@ -307,8 +321,8 @@ MPU680x0Device::Interrupt(int level) { assertmsg(0 <= level && level <= 7, "level=%d", level); - intr_event.code = level; - if (intr_event.IsRunning() == false) { + intr_event->code = level; + if (intr_event->IsRunning() == false) { scheduler->StartEvent(intr_event); } } @@ -325,9 +339,9 @@ MPU680x0Device::Interrupt(int level) // 外部割り込みが来たので、レベル比較などをして、必要なら次の命令境界で // 割り込みチェックを行うようにするところ。 void -MPU680x0Device::InterruptCallback(Event& ev) +MPU680x0Device::InterruptCallback(Event *ev) { - uint level = ev.code; + uint level = ev->code; uint mask = reg.intr_mask; // 信号線が 2 クロック期間安定していること云々は省略 @@ -354,7 +368,7 @@ MPU680x0Device::InterruptCallback(Event& // STOP 状態だったらここでイベントを開始する if (cpu_state == CPU_STATE_STOP) { // STOP から抜ける時間? - exec_event.time = 1 * clock_nsec; + exec_event->time = 1 * clock_nsec; scheduler->StartEvent(exec_event); } } @@ -364,24 +378,7 @@ MPU680x0Device::InterruptCallback(Event& void MPU680x0Device::MakeNextFull() { - exec_event.func = exec_full; -} - -void -MPU680x0Device::ChangeState(uint32 new_state) -{ - // 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); - } + exec_event->func = exec_full; } // A-Line 命令エミュレーションのコールバックを指定。 @@ -420,7 +417,7 @@ MPU680x0Device::Halt() MakeNextFull(); // UI に通知 (メッセージボックス表示とか) - UIMessage::Post(UIMessage::HALT); + gMainApp.GetUIMessage()->Post(UIMessage::HALT); } // モニター更新の下請け (レジスタ共通部分) @@ -790,7 +787,7 @@ MPU68030Device::MonitorUpdateATC(Monitor bool tt_once_hit; { - std::unique_lock lock(table->atchist_mtx); + std::lock_guard lock(table->atchist_mtx); for (int i = 0; i < table->atchist.Length(); i++) { const auto& s = table->atchist.Peek(i); total += s.total; @@ -850,7 +847,7 @@ MPU68030Device::CalcStat() // こっちはロックする { - std::unique_lock lock(table->atchist_mtx); + std::lock_guard lock(table->atchist_mtx); table->atchist.EnqueueForce(s); if (__predict_false(s.tthit != 0)) { @@ -928,7 +925,7 @@ MPU68030Device::MonitorUpdateCache1(Text // 統計。 float hit = 0.0; { - std::unique_lock lock(cache->hist_mtx); + std::lock_guard lock(cache->hist_mtx); int len = cache->hist.Length(); if (len != 0) { for (int i = 0; i < cache->hist.Length(); i++) { @@ -958,6 +955,8 @@ MPU68040Device::MPU68040Device() mpu_name = "MC68040"; SetName("MPU(68040)"); + cycle_table = cycle_table_040; + mmu_itt[0].reset(new m68040TT("ITT0", ®.itt[0])); mmu_itt[1].reset(new m68040TT("ITT1", ®.itt[1])); mmu_dtt[0].reset(new m68040TT("DTT0", ®.dtt[0])); @@ -973,7 +972,7 @@ MPU68040Device::MPU68040Device() // ATC モニター (Inst/Data) atc_monitor = gMonitorManager->Regist(ID_MONITOR_MPUATC, this); atc_monitor->func = ToMonitorCallback(&MPU68040Device::MonitorUpdateATC); - atc_monitor->SetSize(121, 34); + atc_monitor->SetSize(125, 34); } // デストラクタ