--- nono/vm/vm.cpp 2026/04/29 17:04:53 1.1.1.7 +++ nono/vm/vm.cpp 2026/04/29 17:05:37 1.1.1.13 @@ -4,33 +4,31 @@ // Licensed under nono-license.txt // +// +// VM (基本クラス) +// + #include "vm.h" -#include "bus.h" +#include "debugger.h" #include "mainapp.h" -#include "mpu.h" -#include "mythread.h" -#include "ram.h" -#include "renderer.h" -#include "rtc.h" +#include "mainbus.h" #include "scheduler.h" - -std::unique_ptr gVM; +#include "signalthread.h" +#include "syncer.h" // コンストラクタ -VM::VM() +VM::VM(const char *vmname_) { - // どこでやるのがいいかわからんけど、このコンストラクタはメインスレッドで - // 作られるので、ここでメインスレッドの名前を設定。 - PTHREAD_SETNAME("Main"); - - // バスエラーデバイスだけここで作成。 - gBusErr.reset(new BusErrDevice()); - - // まず全域をバスエラーで初期化。 - // この後継承先で必要に応じて上書きする。 - for (int i = 0; i < countof(::devtable); i++) { - ::devtable[i] = gBusErr.get(); - } + vmname = vmname_; + assert(vmname); + + // SignalThread は他のスレッドより先に起動する必要があるので、先頭。 + // Scheduler は以降の他のデバイスからのイベント登録を受け付ける必要が + // あるためその次。 + NEWDV(SignalThread, new SignalThread()); + NEWDV(Scheduler, new Scheduler()); + NEWDV(Syncer, new Syncer()); + NEWDV(Debugger, new Debugger()); } // デストラクタ @@ -43,27 +41,64 @@ VM::~VM() void VM::Dispose() { - if ((bool)gScheduler) { - // スケジューラを停止。 - // スケジューラ(VMスレッド)からのアクセスさえなくなれば - // 後の有象無象はグローバルデストラクタにお任せでもいいか。 - // だめだったらもっと頑張る。 - gScheduler->Terminate(); - gScheduler.reset(); + // まずスレッドを止める。 + // デバッガスレッドがスケジューラスレッドにアクセスするのでデバッガが先。 + if ((bool)pDebugger) { + GetDebugger()->TerminateThread(); + } + if ((bool)pScheduler) { + GetScheduler()->TerminateThread(); } + + // 残りのスレッドを停止 + for (auto obj : gMainApp.GetObjects()) { + auto th = dynamic_cast(obj); + if (th) { + th->TerminateThread(); + } + } + + // 作ったときと逆順で解放する + if ((bool)pDebugger) { + pDebugger.reset(); + } + + if ((bool)pScheduler) { + pScheduler.reset(); + } + + // ほかのオブジェクトの解放はグローバルデストラクタにおまかせ。 + // だめだったらまた考える。 } -// コンストラクタ後の初期化 +// 動的コンストラクション。 bool -VM::Create() +VM::Create(VM::CreationPhase phase) { - // d->Create() は gDevices に要素を追加する場合があるため、ここで - // ranged for は使えない。また追加されたデバイスの Create() も(念のため) + // d->Create() は Object を追加する場合があるため、ここで + // ranged for は使えない。また追加されたデバイスの Create()/Create2() も // 実行したいため、ループ終了条件で毎回 size() を取り出して比較する。 - for (int i = 0; i < gDevices.size(); i++) { - Device *d = gDevices[i]; - if (!d->Create()) { - return false; + 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; + } + } + } + } + + // ログのためのメンバ変数初期化。 + // 同じオブジェクトに対して複数回呼んでも副作用はない。 + for (auto obj : gMainApp.GetObjects()) { + auto dev = dynamic_cast(obj); + if (dev) { + dev->EarlyInit(); } } @@ -74,40 +109,58 @@ VM::Create() bool VM::Init() { - for (const auto& d : gDevices) { - if (!d->Init()) { - return false; + for (auto obj : gMainApp.GetObjects()) { + auto dev = dynamic_cast(obj); + if (dev) { + if (!dev->Init()) { + return false; + } } } + // デバイスの初期化が全部出来たのでメインバスの初期化。 + auto mainbus = dynamic_cast(pMainbus.get()); + if (mainbus->InitMainbus() == false) { + return false; + } + + // イベントが出揃ったのでスケジューラモニタの行数を確定。 + auto scheduler = dynamic_cast(pScheduler.get()); + if (scheduler->Init2() == false) { + return false; + } + return true; } -// 設定の適用 +// スレッド開始 bool -VM::Apply() +VM::StartThread() { - for (const auto& d : gDevices) { - if (!d->Apply()) { - return false; + for (auto obj : gMainApp.GetObjects()) { + auto th = dynamic_cast(obj); + if (th) { + if (!th->StartThread()) { + return false; + } } } return true; } -// ブートページを切り替える (共通処理) -// 派生クラスから呼ぶこと。 -void -VM::SwitchBootPage(Device *parent, bool isrom) +// 設定の適用 +bool +VM::Apply() { - // ログ表示 - if (isrom) { - parent->putlogf(1, lstr("SwitchBootPage ROM (Boot)")); - } else { - parent->putlogf(1, lstr("SwitchBootPage RAM (Normal)")); + for (auto obj : gMainApp.GetObjects()) { + auto dev = dynamic_cast(obj); + if (dev) { + if (!dev->Apply()) { + return false; + } + } } - // スケジューラに指示 - gScheduler->RequestBootPageMode(isrom); + return true; }