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

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

//
// 電源周り
//

#include "power.h"
#include "event.h"
#include "mainapp.h"
#include "mfp.h"
#include "scheduler.h"
#include "syncer.h"
#include "uimessage.h"

//
// 共通クラス
//

// コンストラクタ
PowerDevice::PowerDevice()
	: inherited(OBJ_POWER)
{
}

// デストラクタ
PowerDevice::~PowerDevice()
{
}

// 初期化
bool
PowerDevice::Init()
{
	syncer = GetSyncer();
	uimessage = gMainApp.GetUIMessage();

	scheduler->ConnectMessage(MessageID::RESET, this,
		ToMessageCallback(&PowerDevice::ResetMessage));
	scheduler->ConnectMessage(MessageID::POWEROFF_RESTART, this,
		ToMessageCallback(&PowerDevice::PowerOffMessage));
	scheduler->ConnectMessage(MessageID::POWER_BUTTON, this,
		ToMessageCallback(&PowerDevice::PowerButtonMessage));

	return true;
}

// 電源ボタンを押す。 (外部から呼ばれる用)
void
PowerDevice::PushPowerButton()
{
	scheduler->SendMessage(MessageID::POWER_BUTTON);
}

// リセットをスケジューラに指示する。(外部から呼ばれる用)
void
PowerDevice::MakeResetHard()
{
	scheduler->SendMessage(MessageID::RESET);
}

// リスタートをスケジューラに指示する。(外部から呼ばれる用)
void
PowerDevice::MakeRestart()
{
	scheduler->SendMessage(MessageID::POWEROFF_RESTART);
}

// 電源ボタンのメッセージコールバック
void
PowerDevice::PowerButtonMessage(MessageID msgid, uint32 arg)
{
	DoPowerButton();
}

// リセットのメッセージコールバック
void
PowerDevice::ResetMessage(MessageID msgid, uint32 arg)
{
	DoResetHard();
}

// 電源オフのメッセージコールバック
void
PowerDevice::PowerOffMessage(MessageID msgid, uint32 arg)
{
	DoPowerOff(msgid);
}

// 電源ボタン操作。
// ただ電源をオンオフするだけなら共通クラスのこれが使える。
// そうでない場合は継承側で対応すること。
void
PowerDevice::DoPowerButton()
{
	putlog(1, "PowerButton");

	if (ispower == false) {
		DoPowerOn();
	} else {
		DoPowerOff(MessageID::POWEROFF_EXIT);
	}

	// LED の状態が変わったことを UI に通知。
	led = ispower;
	uimessage->Post(UIMessage::LED);
}

// 電源オン操作 (Device::ResetHard ではない)
void
PowerDevice::DoPowerOn()
{
	putlog(1, "PowerOn");

	if (ispower == false) {
		// 全デバイス電源オン
		ispower = true;
		for (auto obj : gMainApp.GetObjects()) {
			auto dev = dynamic_cast<Device *>(obj);
			if (dev) {
				dev->ResetHard(true);
			}
		}

		// 動作モード変更
		syncer->NotifyPowerOffMode(false);
	}
}

// リセット操作 (Device::ResetHard ではない)
void
PowerDevice::DoResetHard()
{
	putlog(1, "Reset");

	if (ispower) {
		// 全デバイスをリセット
		for (auto obj : gMainApp.GetObjects()) {
			auto dev = dynamic_cast<Device *>(obj);
			if (dev) {
				dev->ResetHard(false);
			}
		}
	}
}

// 電源オフ操作 (Device::PowerOff ではない)
void
PowerDevice::DoPowerOff(MessageID msgid)
{
	putlog(1, "PowerOff");

	assertmsg(msgid == MessageID::POWEROFF_EXIT ||
	          msgid == MessageID::POWEROFF_RESTART,
		"msgid=%d", (int)msgid);

	if (ispower) {
		// 全デバイス電源オフ
		for (auto obj : gMainApp.GetObjects()) {
			auto dev = dynamic_cast<Device *>(obj);
			if (dev) {
				dev->PowerOff();
			}
		}
		ispower = false;

		// 動作モード変更
		syncer->NotifyPowerOffMode(true);
	}

	// 電源オフ後の動作は引数によって異なる
	if (msgid == MessageID::POWEROFF_RESTART) {
		// 時間を始め直す
		scheduler->StartTime();
		// 再び電源オンにする (リスタート)
		DoPowerOn();
	} else {
		// そうでなければ、GUI に終了を通知
		uimessage->Post(UIMessage::APPEXIT);
	}
}

// 電源ボタンの状態を返す。
PowerButtonState
PowerDevice::GetPowerButtonState() const
{
	if (alternate_switch) {
		return (PowerButtonState)power_button;
	} else {
		return PowerButtonState::NoState;
	}
}

// システム内の電源オン信号を受け取る。
// 呼ぶなら継承クラス側で対応すること。
void
PowerDevice::SetSystemPowerOn(bool poweron)
{
	assertmsg(false, "PowerDevice::SetSystemPowerOn should not be called.");
}


//
// 電源周り (LUNA)
//

// LUNA はフロントのモーメンタリスイッチで電源オンにする。オフには出来ない。
// 電源オフは PIO1 から制御する。PIO1 の PC4 を %0 にすると 1msec 後に
// 電源がオフになる。PC4 を %1 にすることで電源オフを回避できる。
// 特に PIO のモード変更をすると PC0-7 出力が一旦 %0 になるので、
// 必ず間髪入れずに回避しなければならないらしい。どうして…。
//
// 電源ボタン操作 -> PowerOn
// PC4 を %0      -> PowerOff イベントを開始
// PC4 を %1      -> PowerOff イベントを停止

// コンストラクタ
LunaPowerDevice::LunaPowerDevice()
	: inherited()
{
}

// デストラクタ
LunaPowerDevice::~LunaPowerDevice()
{
}

// 初期化
bool
LunaPowerDevice::Init()
{
	if (inherited::Init() == false) {
		return false;
	}

	auto evman = GetEventManager();
	event = evman->Regist(this,
		ToEventCallback(&LunaPowerDevice::PowerCallback),
		"Power Off");
	event->time = 1_msec;

	return true;
}

// PIO1 の PC4 が繋がっているということにする。
void
LunaPowerDevice::SetSystemPowerOn(bool poweron)
{
	if (poweron) {
		if (event->IsRunning()) {
			putlog(1, "Cancel Power-Off");
			scheduler->StopEvent(event);
		}
	} else {
		if (event->IsRunning() == false) {
			putlog(1, "Start Power-Off timer");
			scheduler->RestartEvent(event);
		}
	}
}

// LUNA の電源オフが信号状態の変化に対し 1msec 遅れて応答することを
// エミュレートするためのイベントコールバック。
void
LunaPowerDevice::PowerCallback(Event *ev)
{
	DoPowerOff(MessageID::POWEROFF_EXIT);

	// LED の状態が変わったことを UI に通知。
	led = ispower;
	uimessage->Post(UIMessage::LED);
}


//
// 電源周り (X680x0)
//

// X68030 はフロントのオルタネートスイッチで電源オンにする。
// RTC 等でも電源オンは可能だが現状サポートしていないので省略。
//
// 電源オン後は以下のいずれか一つでも成り立っている間は電源オン。
//  1 電源ボタンがオン状態
//  2 ALARM_OUT がアサート
//  3 システムポートが電源オフコマンドを発行していない。

// コンストラクタ
X68030PowerDevice::X68030PowerDevice()
	: inherited()
{
	alternate_switch = true;
}

// デストラクタ
X68030PowerDevice::~X68030PowerDevice()
{
}

// 電源ボタン操作
void
X68030PowerDevice::DoPowerButton()
{
	putlog(1, "PowerButton");

	// X68030 の電源ボタンはオルタネート動作
	power_button = !power_button;
	Change();

	// MFP の POWSW 信号は負論理
	GetMFPDevice()->SetPowSW(!power_button);
}

// RP5C15 の ALARM_OUT を受け取る
void
X68030PowerDevice::SetAlarmOut(bool alarm_out_)
{
	alarm_out = alarm_out_;
	Change();
}

// システムポートからの電源オフコマンド発行で false が来る。
// 便宜上、電源投入時にはシステムポートがこれを true にする。
void
X68030PowerDevice::SetSystemPowerOn(bool poweron)
{
	system_poweron = poweron;
	Change();
}

// 電源状態の変更。
// power_button、alarm_out、system_poweron のいずれかの変更で呼ぶこと。
void
X68030PowerDevice::Change()
{
	bool old_power = ispower;
	bool new_power = power_button | alarm_out | system_poweron;
	putlog(1, "Change: old=%u new=%u (button=%u alarm=%u system=%u)",
		old_power, new_power, power_button, alarm_out, system_poweron);

	if (old_power == false && new_power) {
		DoPowerOn();
	} else if (old_power && new_power == false) {
		DoPowerOff(MessageID::POWEROFF_EXIT);
	}

	bool old_led = led;
	// 電源 LED は X68030 の電源制御部の回路による。
	led = power_button | alarm_out;

	if (old_led != led) {
		// LED の状態が変わったことを UI に通知
		uimessage->Post(UIMessage::LED);
	}
}

unix.superglobalmegacorp.com

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