--- nono/vm/vm.cpp 2026/04/29 17:04:50 1.1.1.6 +++ nono/vm/vm.cpp 2026/04/29 17:05:29 1.1.1.12 @@ -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"); - - // バスエラーデバイスだけここで作成。 - buserr.reset(new BusErrDevice()); - - // まず全域をバスエラーで初期化。 - // この後継承先で必要に応じて上書きする。 - for (int i = 0; i < countof(::devtable); i++) { - ::devtable[i] = buserr.get(); - } + vmname = vmname_; + assert(vmname); + + // SignalThread は他のスレッドより先に起動する必要があるので、先頭。 + // Scheduler は以降の他のデバイスからのイベント登録を受け付ける必要が + // あるためその次。 + NEWDV(SignalThread, new SignalThread()); + NEWDV(Scheduler, new Scheduler()); + NEWDV(Syncer, new Syncer()); + NEWDV(Debugger, new Debugger()); } // デストラクタ @@ -43,23 +41,49 @@ 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() { - for (const auto& d : gDevices) { - if (!d->Create()) { - 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; + } } } @@ -70,82 +94,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; + } } } - return true; -} + // デバイスの初期化が全部出来たのでメインバスの初期化。 + auto mainbus = dynamic_cast(pMainbus.get()); + if (mainbus->InitMainbus() == false) { + return false; + } -// 設定の適用 -bool -VM::Apply() -{ - for (const auto& d : gDevices) { - if (!d->Apply()) { - return false; - } + // イベントが出揃ったのでスケジューラモニタの行数を確定。 + auto scheduler = dynamic_cast(pScheduler.get()); + if (scheduler->Init2() == false) { + return false; } return true; } -// VM の全デバイスの電源をオン +// スレッド開始 bool -VM::DevicePowerOn() +VM::StartThread() { - // 時刻開始 - // RTC::PowerOn() でもいいかも知れないけど念のため全 PowerOn より先に。 - if (!gRTC->InitRTC()) { - return false; - } - - 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(); - - // スケジューラを開始 - 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; -} -// ハードウェアリセット -void -VM::ResetHard() -{ - for (const auto& d : gDevices) { - d->ResetHard(); - } + return true; }