--- nono/vm/vm.cpp 2026/04/29 17:04:36 1.1.1.4 +++ nono/vm/vm.cpp 2026/04/29 17:05:50 1.1.1.15 @@ -4,32 +4,37 @@ // Licensed under nono-license.txt // +// +// VM (基本クラス) +// + #include "vm.h" -#include "bus.h" +#include "debugger.h" +#include "event.h" #include "mainapp.h" -#include "mpu.h" -#include "mythread.h" -#include "ram.h" -#include "renderer.h" +#include "mainbus.h" #include "scheduler.h" - -std::unique_ptr gVM; +#include "signalthread.h" +#include "syncer.h" +#include "uimessage.h" // コンストラクタ -VM::VM() +VM::VM(const char *vmname_) { - // どこでやるのがいいかわからんけど、このコンストラクタはメインスレッドで - // 作られるので、ここでメインスレッドの名前を設定。 - PTHREAD_SETNAME("Main"); - - // バスエラーデバイスだけここで作成。 - buserr.reset(new BusErrDevice()); - - // まず全域をバスエラーで初期化。 - // この後継承先で必要に応じて上書きする。 - for (int i = 0; i < countof(::devtable); i++) { - ::devtable[i] = buserr.get(); - } + vmname = vmname_; + assert(vmname); + + // SignalThread は他のスレッドより先に起動する必要があるので、先頭。 + // EventManager は Scheduler に必要。 + // Scheduler は以降の他のデバイスからのイベント登録を受け付ける必要が + // あるためその次。 + // Debugger は HostCOM と stdio ドライバが競合した場合に優先したいため + // もあって必ず先に配置する必要がある。 + NEWDV(SignalThread, new SignalThread()); + NEWDV(EventManager, new EventManager()); + NEWDV(Scheduler, new Scheduler()); + NEWDV(Syncer, new Syncer()); + NEWDV(Debugger, new Debugger()); } // デストラクタ @@ -42,113 +47,131 @@ VM::~VM() void VM::Dispose() { - if ((bool)gScheduler) { - // スケジューラを停止。 - // スケジューラ(VMスレッド)からのアクセスさえなくなれば - // 後の有象無象はグローバルデストラクタにお任せでもいいか。 - // だめだったらもっと頑張る。 - gScheduler->Terminate(); - gScheduler.reset(); - } -} + // (もう止まってるはずだが) 念のため UIMessage の送出を止める。 + gMainApp.GetUIMessage()->StopUIMessage(); -// コンストラクタ後の初期化 -bool -VM::Create() -{ - 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(); + } + + if ((bool)pEventManager) { + pEventManager.reset(); + } + + // ほかのオブジェクトの解放はグローバルデストラクタにおまかせ。 + // だめだったらまた考える。 } -// 初期化 +// 動的コンストラクション。 bool -VM::Init() +VM::Create(VM::CreationPhase phase) { - for (const auto& d : gDevices) { - if (!d->Init()) { - return false; + // d->Create() は Object を追加する場合があるため、ここで + // ranged for は使えない。また追加されたデバイスの Create()/Create2() も + // 実行したいため、ループ終了条件で毎回 size() を取り出して比較する。 + for (int i = 0; i < gMainApp.GetObjects().size(); i++) { + auto dev = dynamic_cast(gMainApp.GetObjects()[i]); + if (dev) { + if (phase == CreationPhase::First) { + if (!dev->Create()) { + return false; + } + } else { + if (!dev->Create2()) { + return false; + } + } } } - return true; -} + // ログのためのメンバ変数初期化。 + // 同じオブジェクトに対して複数回呼んでも副作用はない。 + for (auto obj : gMainApp.GetObjects()) { + auto dev = dynamic_cast(obj); + if (dev) { + dev->EarlyInit(); + } + } -// 設定の適用 -bool -VM::Apply() -{ - for (const auto& d : gDevices) { - if (!d->Apply()) { - return false; + // 名無しオブジェクトがあれば開発中に検出したい。 + // 名無しでも動作には影響ないのでリリース版では怒られないでほしい。 +#if !defined(RELEASE) + for (auto obj : gMainApp.GetObjects()) { + if (obj->GetName().empty()) { + printf("Objects:\n"); + for (auto o : gMainApp.GetObjects()) { + printf("%s\n", o->GetName().c_str()); + } + assert(obj->GetName().empty() == false); } } +#endif return true; } -// VM の全デバイスの電源をオン +// 初期化 bool -VM::DevicePowerOn() +VM::Init() { - for (const auto& d : gDevices) { - if (!d->PowerOn()) { - return false; + for (auto obj : gMainApp.GetObjects()) { + auto dev = dynamic_cast(obj); + if (dev) { + if (!dev->Init()) { + return false; + } } } - // ハードウェアリセット - ResetHard(); + // デバイスの初期化が全部出来たのでメインバスの初期化。 + auto mainbus = dynamic_cast(pMainbus.get()); + if (mainbus->InitMainbus() == false) { + return false; + } - // スケジューラを開始 - // (MPU の ResetHard() より後で起動時に1回だけ呼び出す) - gScheduler->Run(); + // イベントが出揃ったのでスケジューラモニタの行数を確定。 + auto scheduler = dynamic_cast(pScheduler.get()); + if (scheduler->Init2() == false) { + return false; + } return true; } -// VM の全デバイスの電源をオフ -// (Scheduler から呼ばれる) +// スレッド開始 bool -VM::DevicePowerOff() +VM::StartThread() { - for (const auto& d : gDevices) { - if (!d->PowerOff()) { - return false; + for (auto obj : gMainApp.GetObjects()) { + auto th = dynamic_cast(obj); + if (th) { + if (!th->StartThread()) { + return false; + } } } - // GUI に通知 - if (poweroff_callback) { - poweroff_callback(); - } - return true; -} - -// VM 電源オフ時のコールバックを設定する -void -VM::SetPowerOffCallback(void (*callback)()) -{ - poweroff_callback = callback; -} - -// ハードウェアリセット -void -VM::ResetHard() -{ - for (const auto& d : gDevices) { - d->ResetHard(); - } -} -// MPU の RESET 命令によるリセット -void -VM::ResetSoft() -{ - for (const auto& d : gDevices) { - d->ResetSoft(); - } + return true; }