File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / vm.cpp
Revision 1.1.1.15 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:50 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, v026, v025, HEAD
nono 1.5.0

//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//

//
// VM (基本クラス)
//

#include "vm.h"
#include "debugger.h"
#include "event.h"
#include "mainapp.h"
#include "mainbus.h"
#include "scheduler.h"
#include "signalthread.h"
#include "syncer.h"
#include "uimessage.h"

// コンストラクタ
VM::VM(const char *vmname_)
{
	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());
}

// デストラクタ
VM::~VM()
{
	Dispose();
}

// 終了
void
VM::Dispose()
{
	// (もう止まってるはずだが) 念のため UIMessage の送出を止める。
	gMainApp.GetUIMessage()->StopUIMessage();

	// スレッドを止める。
	// デバッガスレッドがスケジューラスレッドにアクセスするのでデバッガが先。
	if ((bool)pDebugger) {
		GetDebugger()->TerminateThread();
	}
	if ((bool)pScheduler) {
		GetScheduler()->TerminateThread();
	}

	// 残りのスレッドを停止
	for (auto obj : gMainApp.GetObjects()) {
		auto th = dynamic_cast<ThreadDevice *>(obj);
		if (th) {
			th->TerminateThread();
		}
	}

	// 作ったときと逆順で解放する
	if ((bool)pDebugger) {
		pDebugger.reset();
	}

	if ((bool)pScheduler) {
		pScheduler.reset();
	}

	if ((bool)pEventManager) {
		pEventManager.reset();
	}

	// ほかのオブジェクトの解放はグローバルデストラクタにおまかせ。
	// だめだったらまた考える。
}

// 動的コンストラクション。
bool
VM::Create(VM::CreationPhase phase)
{
	// d->Create() は Object を追加する場合があるため、ここで
	// ranged for は使えない。また追加されたデバイスの Create()/Create2() も
	// 実行したいため、ループ終了条件で毎回 size() を取り出して比較する。
	for (int i = 0; i < gMainApp.GetObjects().size(); i++) {
		auto dev = dynamic_cast<Device *>(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<Device *>(obj);
		if (dev) {
			dev->EarlyInit();
		}
	}

	// 名無しオブジェクトがあれば開発中に検出したい。
	// 名無しでも動作には影響ないのでリリース版では怒られないでほしい。
#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;
}

// 初期化
bool
VM::Init()
{
	for (auto obj : gMainApp.GetObjects()) {
		auto dev = dynamic_cast<Device *>(obj);
		if (dev) {
			if (!dev->Init()) {
				return false;
			}
		}
	}

	// デバイスの初期化が全部出来たのでメインバスの初期化。
	auto mainbus = dynamic_cast<MainbusDevice*>(pMainbus.get());
	if (mainbus->InitMainbus() == false) {
		return false;
	}

	// イベントが出揃ったのでスケジューラモニタの行数を確定。
	auto scheduler = dynamic_cast<Scheduler*>(pScheduler.get());
	if (scheduler->Init2() == false) {
		return false;
	}

	return true;
}

// スレッド開始
bool
VM::StartThread()
{
	for (auto obj : gMainApp.GetObjects()) {
		auto th = dynamic_cast<ThreadDevice *>(obj);
		if (th) {
			if (!th->StartThread()) {
				return false;
			}
		}
	}

	return true;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.