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

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

//
// LUNA システムクロック
//

// システムクロックは実時間または仮想時間のいずれかに同期するため、
// システムクロック独自の時刻 stime を持つ。
// stime は、割り込みを発生させるごとに、LUNA-I なら 16.67msec、
// LUNA-88K なら 10msec 進行する。

#include "sysclk.h"
#include "config.h"
#include "event.h"
#include "interrupt.h"
#include "monitor.h"
#include "scheduler.h"
#include "syncer.h"

// コンストラクタ
SysClkDevice::SysClkDevice()
	: inherited(OBJ_SYSCLK)
{
	monitor = gMonitorManager->Regist(ID_MONITOR_SYSCLK, this);
	monitor->SetCallback(&SysClkDevice::MonitorScreen);
	monitor->SetSize(32, 4);
}

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

// 初期化
bool
SysClkDevice::Init()
{
	interrupt = GetInterruptDevice();

	// クロックは機種ごとに異なる
	const ConfigItem& item = gConfig->Find("!sysclk-freq");
	freq = item.AsInt();
	period = 1_sec / freq;

	// クロックの同期方法
	syncer = GetSyncer();
	sync_rt = syncer->GetSyncRealtime();

	auto evman = GetEventManager();
	event = evman->Regist(this,
		ToEventCallback(&SysClkDevice::Callback),
		"System Clock");
	event->time = period;

	return true;
}

// 電源オン/リセット
void
SysClkDevice::ResetHard(bool poweron)
{
	// XXX たぶん電源オンだけで作用する?
	if (poweron) {
		if (sync_rt) {
			stime = GetSyncer()->GetRealTime();
		}

		// 電源オンですぐ起動
		scheduler->RestartEvent(event);
	}

	// さすがに割り込みネゲートくらいするよな?
	sysint = false;
	ChangeInterrupt();
}

busdata
SysClkDevice::ReadPort(uint32 offset)
{
	// 1 ポートしかない
	busdata data = GetInt();
	data |= BusData::Size1;
	return data;
}

busdata
SysClkDevice::WritePort(uint32 offset, uint32 data)
{
	// 何か書き込まれたら、フラグをクリア
	putlog(3, "Clear system clock interrupt");
	sysint = false;
	ChangeInterrupt();

	busdata r = BusData::Size1;
	return r;
}

busdata
SysClkDevice::PeekPort(uint32 offset)
{
	return GetInt();
}

bool
SysClkDevice::PokePort(uint32 offset, uint32 data)
{
	return false;
}

void
SysClkDevice::MonitorScreen(Monitor *, TextScreen& screen)
{
	int y;

	screen.Clear();
	y = 0;

	int width = stime < 86400_sec ? 20 : 25;
	screen.Print(0, y++, "Freq: %u Hz", freq);
	if (sync_rt) {
		screen.Puts(0,  y++, "Sync: Real Time");
		screen.Print(0, y++, "Time:  %*s", width, TimeToStr(stime).c_str());

		// TimeToStr は負は想定していないし、符号は数値の直前に足したい。
		screen.Puts(0, y, "Diff:");
		bool neg = false;
		if (delta < 0) {
			delta = -delta;
			neg = true;
		}
		std::string s = TimeToStr(delta);
		int offset = width - s.size();
		screen.Print(6 + offset, y++, "%c%s", (neg ? '-' : '+'), s.c_str());
	} else {
		screen.Puts(0, y++, "Sync: Virtual Time");
		screen.Puts(0, y++, TA::Disable, "Time:");
		screen.Puts(0, y++, TA::Disable, "Diff:");
	}
}

// 割り込み状態を取得(?)
uint8
SysClkDevice::GetInt() const
{
	uint8 data = 0;

	if (sysint) {
		data |= 0x80;
	}
	return data;
}

// イベントハンドラ
void
SysClkDevice::Callback(Event *ev)
{
	putlog(3, "Interrupt occured");

	sysint = true;
	ChangeInterrupt();

	if (sync_rt) {
		// システムクロックとしての現在時刻。
		// 足す前は前回の割り込み発生時刻なので period を足すと現在時刻。
		stime += period;

		// ホスト時間とここで作った実時間との差。
		uint64 now = syncer->GetRealTime();
		delta = (int64)(now - stime);

		scheduler->StartRealtimeEvent(ev, stime, period);
	} else {
		scheduler->StartEvent(ev);
	}
}

// 割り込み信号線の状態を変える。
void
SysClkDevice::ChangeInterrupt()
{
	interrupt->ChangeINT(this, sysint);
}

unix.superglobalmegacorp.com

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