--- nono/vm/vm.cpp 2026/04/29 17:04:32 1.1.1.3 +++ nono/vm/vm.cpp 2026/04/29 17:05:17 1.1.1.9 @@ -1,144 +1,169 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// VM (基本クラス) // #include "vm.h" -#include "bus.h" +#include "debugger.h" #include "mainapp.h" +#include "mainbus.h" +#include "mainram.h" #include "mpu.h" -#include "ram.h" -#include "renderer.h" +#include "power.h" #include "scheduler.h" +#include "signalthread.h" +#include "syncer.h" -std::unique_ptr gVM; +// グローバル参照用 +VM *gVM; // コンストラクタ VM::VM() { - // どこでやるのがいいかわからんけど、このコンストラクタはメインスレッドで - // 作られるので、ここでメインスレッドの名前を設定。 - pthread_setname_np("Main"); - - // Scheduler は必ず先頭。(Scheduler::Init のコメント参照) - gScheduler.reset(new Scheduler()); - buserr.reset(new BusErrDevice()); - - // まず全域をバスエラーで初期化。 - // この後継承先で必要に応じて上書きする。 - for (int i = 0; i < countof(::devtable); i++) { - ::devtable[i] = buserr.get(); - } + // SignalThread は他のスレッドより先に起動する必要があるので、先頭。 + // Scheduler は以降の他のデバイスからのイベント登録を受け付ける必要が + // あるためその次。 + NEWDV(SignalThread, new SignalThread()); + NEWDV(Scheduler, new Scheduler()); + NEWDV(Syncer, new Syncer()); + NEWDV(Power, new PowerDevice()); + NEWDV(Debugger, new Debugger()); + + NEWDV(Mainbus, new MainbusDevice()); + NEWDV(BusErr, new BusErrDevice()); } // デストラクタ VM::~VM() { - // スケジューラを停止。 - // スケジューラ(VMスレッド)からのアクセスさえなくなれば - // 後の有象無象はグローバルデストラクタにお任せでもいいか。 - // だめだったらもっと頑張る。 - gScheduler->Terminate(); - gScheduler.reset(); + Dispose(); + gVM = NULL; } -// コンストラクタ後の初期化 -bool -VM::Create() +// 終了 +void +VM::Dispose() { - for (const auto& d : gDevices) { - if (!d->Create()) { - return false; + // まずスレッドを止める。 + // デバッガスレッドがスケジューラスレッドにアクセスするのでデバッガが先。 + if ((bool)pDebugger) { + GetDebugger()->TerminateThread(); + } + if ((bool)pScheduler) { + GetScheduler()->TerminateThread(); + } + + // 残りのスレッドを停止 + for (auto obj : gMainApp.GetObjects()) { + auto th = dynamic_cast(obj); + if (th) { + th->TerminateThread(); } } - return true; + // 作ったときと逆順で解放する + if ((bool)pDebugger) { + pDebugger.reset(); + } + + if ((bool)pScheduler) { + pScheduler.reset(); + } + + // ほかのオブジェクトの解放はグローバルデストラクタにおまかせ。 + // だめだったらまた考える。 } -// 初期化 +// コンストラクタ後の初期化 bool -VM::Init() +VM::Create() { - for (const auto& d : gDevices) { - if (!d->Init()) { - return false; + // d->Create() は Object を追加する場合があるため、ここで + // ranged for は使えない。また追加されたデバイスの Create() も(念のため) + // 実行したいため、ループ終了条件で毎回 size() を取り出して比較する。 + for (int i = 0; i < gMainApp.GetObjects().size(); i++) { + auto dev = dynamic_cast(gMainApp.GetObjects()[i]); + if (dev) { + if (!dev->Create()) { + return false; + } } } return true; } -// 設定の適用 +// 初期化 bool -VM::Apply() +VM::Init() { - for (const auto& d : gDevices) { - if (!d->Apply()) { - return false; + for (auto obj : gMainApp.GetObjects()) { + auto dev = dynamic_cast(obj); + if (dev) { + if (!dev->Init()) { + return false; + } } } return true; } -// VM の全デバイスの電源をオン +// スレッド開始 bool -VM::DevicePowerOn() +VM::StartThread() { - for (const auto& d : gDevices) { - if (!d->PowerOn()) { - return false; + for (auto obj : gMainApp.GetObjects()) { + auto th = dynamic_cast(obj); + if (th) { + if (!th->StartThread()) { + return false; + } } } - // ハードウェアリセット - ResetHard(); - - // スケジューラを開始 - // (MPU の ResetHard() より後で起動時に1回だけ呼び出す) - gScheduler->Run(); - return true; } -// VM の全デバイスの電源をオフ -// (Scheduler から呼ばれる) +// 設定の適用 bool -VM::DevicePowerOff() +VM::Apply() { - for (const auto& d : gDevices) { - if (!d->PowerOff()) { - return false; + for (auto obj : gMainApp.GetObjects()) { + auto dev = dynamic_cast(obj); + if (dev) { + if (!dev->Apply()) { + return false; + } } } - // GUI に通知 - if (poweroff_callback) { - poweroff_callback(); - } - return true; -} -// VM 電源オフ時のコールバックを設定する -void -VM::SetPowerOffCallback(void (*callback)()) -{ - poweroff_callback = callback; + return true; } -// ハードウェアリセット +// ブートページを切り替える (共通処理) +// 派生クラスから呼ぶこと。 void -VM::ResetHard() +VM::SwitchBootPage(Device *parent, bool isrom) { - for (const auto& d : gDevices) { - d->ResetHard(); + // ログ表示 + if (isrom) { + parent->putlogf(1, lstr("SwitchBootPage ROM (Boot)")); + } else { + parent->putlogf(1, lstr("SwitchBootPage RAM (Normal)")); } + + // 同期モード指示 + GetSyncer()->RequestBootPageMode(isrom); } // MPU の RESET 命令によるリセット void -VM::ResetSoft() +VM::ResetByMPU() { - for (const auto& d : gDevices) { - d->ResetSoft(); - } }