--- nono/vm/sysclk.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/vm/sysclk.cpp 2026/04/29 17:04:56 1.1.1.9 @@ -1,22 +1,33 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // +// システムクロックは実時間または仮想時間のいずれかに同期するため、 +// システムクロック独自の時刻 stime を持つ。 +// stime は、割り込みを発生させるごとに、LUNA-I なら 16.67msec、 +// LUNA88k なら 10msec 進行する。 + #include "sysclk.h" -#include "mpu.h" +#include "config.h" +#include "interrupt.h" + +std::unique_ptr gSysClk; SysClkDevice::SysClkDevice() + : inherited("SystemClock") { - logname = "sysclk"; - devname = "SystemClock"; + AddAlias("SysClk"); + devaddr = 0x63000000; - event.dev = this; - event.func = (DeviceCallback_t)&SysClkDevice::Callback; + monitor.func = (MonitorCallback_t)&SysClkDevice::MonitorUpdate; + monitor.SetSize(31, 2); + monitor.Regist(ID_MONITOR_SYSCLK); + event.code = 0; - event.time = 16.666667_msec; - event.name = "System Clock"; + event.SetName("System Clock"); } SysClkDevice::~SysClkDevice() @@ -24,51 +35,138 @@ SysClkDevice::~SysClkDevice() } bool +SysClkDevice::Init() +{ + // クロックは機種ごとに異なる + const ConfigItem& item = gConfig->Find("!sysclk-period"); + period = item.AsInt(); + event.time = period; + + // クロックの同期方法 + const ConfigItem& item_sync = gConfig->Find("clock-sync"); + auto clock_sync = item_sync.AsString(); + if (clock_sync == "real") { + sync_rt = true; + } else if (clock_sync == "virtual") { + sync_rt = false; + } else { + item_sync.Err(); + return false; + } + if (sync_rt) { + event.func = (DeviceCallback_t)&SysClkDevice::Callback_rt; + } else { + event.func = (DeviceCallback_t)&SysClkDevice::Callback; + } + + return true; +} + +bool SysClkDevice::PowerOn() { + if (sync_rt) { + stime = gRealtime.Elapsed(); + } + // XXX スーパーリセット時にスケジューラの時刻が再初期化されないので + // ここでどうするかはそのときまた再検討すること。 + // 電源オンですぐ起動 - gScheduler->AddEvent(&event); + sysint = false; + event.Start(); return true; } uint64 -SysClkDevice::Read8(uint32 addr) +SysClkDevice::Read(uint32 offset) { - uint32 an = addr - devaddr; + // 1 ポートしかない + return GetInt(); +} - if (an == 0) { - uint8 data = 0; - if (sysint) { - data |= 0x80; - } - return data; - } else { - // それ以外のポートがどう見えるかとか未調査 - return 0xff; - } +uint64 +SysClkDevice::Write(uint32 offset, uint32 data) +{ + // 何か書き込まれたら、フラグをクリア + putlog(1, "システムクロック割り込みクリア"); + sysint = false; + gInterrupt->NegateINT(this); + return 0; } uint64 -SysClkDevice::Write8(uint32 addr, uint32 data) +SysClkDevice::Peek(uint32 offset) { - uint32 an = addr - devaddr; + return GetInt(); +} - if (an == 0) { - // 何か書き込まれたら、フラグをクリア - sysint = false; - putlog(1, "システムクロック割り込みクリア"); - } else { - // それ以外 (特に +1, +2, +3) で何が起きるかは未調査 +void +SysClkDevice::MonitorUpdate(Monitor *, TextScreen& screen) +{ + int y; + + screen.Clear(); + y = 0; + + int width = stime < 86400_sec ? 20 : 25; + screen.Print(0, y++, "Sync: %s", sync_rt ? "Real Time" : "Virtual Time"); + screen.Print(0, y++, "Time: %*s", width, TimeToStr(stime).c_str()); +} + +// 割り込み状態を取得(?) +uint8 +SysClkDevice::GetInt() const +{ + uint8 data = 0; + + if (sysint) { + data |= 0x80; } - return 0; + return data; } // イベントハンドラ void -SysClkDevice::Callback(int arg) +SysClkDevice::Callback(Event& ev) +{ + putlog(3, "システムクロック割り込み発生"); + stime += period; + sysint = true; + gInterrupt->AssertINT(this); + ev.Start(); +} + +void +SysClkDevice::Callback_rt(Event& ev) { putlog(3, "システムクロック割り込み発生"); + // システムクロックとしての現在時刻。 + // 足す前は前回の割り込み発生時刻なので + // period を足すと現在時刻。 + stime += period; + + uint64 now = gRealtime.Elapsed(); + + // +- period 時間範囲で処理する + if (now > stime + period) { + // 実時間がさらに 1 period 以上進んでいる + // 実時間に追いつかないといけないので、システムクロックの + // 周期を短くして回復運転させる。 + // 周期を短くしすぎるとゲストが処理できなくなるので、 + // 10% カットに留める。 + ev.time = period - period / 10; + } else if (now < stime - period) { + // システムクロックがさらに 1 period 以上進んでいる + // 実時間を追い越してしまっているので、システムクロックの + // 周期を長くして待つ。長くする方はいくらでも長くしていいので、 + // 連続した場合はどんどん長くしていく。 + ev.time += period; + } else { + // おおむね同期している + ev.time = period; + } + sysint = true; - gMPU->Interrupt(5); - gScheduler->AddEvent(&event); + gInterrupt->AssertINT(this); + ev.Start(); }