--- nono/vm/sysclk.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/vm/sysclk.cpp 2026/04/29 17:05:59 1.1.1.20 @@ -1,74 +1,186 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // +// +// LUNA システムクロック +// + +// システムクロックは実時間または仮想時間のいずれかに同期するため、 +// システムクロック独自の時刻 stime を持つ。 +// stime は、割り込みを発生させるごとに、LUNA-I なら 16.67msec、 +// LUNA-88K なら 10msec 進行する。 + #include "sysclk.h" -#include "mpu.h" +#include "config.h" +#include "event.h" +#include "interrupt.h" +#include "monitor.h" +#include "scheduler.h" +#include "syncer.h" +// コンストラクタ SysClkDevice::SysClkDevice() + : inherited(OBJ_SYSCLK) { - logname = "sysclk"; - devname = "SystemClock"; - devaddr = 0x63000000; - - event.dev = this; - event.func = (DeviceCallback_t)&SysClkDevice::Callback; - event.code = 0; - event.time = 16.666667_msec; - event.name = "System Clock"; + monitor = gMonitorManager->Regist(ID_MONITOR_SYSCLK, this); + monitor->SetCallback(&SysClkDevice::MonitorScreen); + monitor->SetSize(32, 4); } +// デストラクタ SysClkDevice::~SysClkDevice() { } +// 初期化 bool -SysClkDevice::PowerOn() +SysClkDevice::Init() { - // 電源オンですぐ起動 - gScheduler->AddEvent(&event); + 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; } -uint64 -SysClkDevice::Read8(uint32 addr) +// 電源オン/リセット +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) { - uint32 an = addr - devaddr; + int y; + + screen.Clear(); + y = 0; - if (an == 0) { - uint8 data = 0; - if (sysint) { - data |= 0x80; + 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; } - return data; + std::string s = TimeToStr(delta); + int offset = width - s.size(); + screen.Print(6 + offset, y++, "%c%s", (neg ? '-' : '+'), s.c_str()); } else { - // それ以外のポートがどう見えるかとか未調査 - return 0xff; + screen.Puts(0, y++, "Sync: Virtual Time"); + screen.Puts(0, y++, TA::Disable, "Time:"); + screen.Puts(0, y++, TA::Disable, "Diff:"); } } -uint64 -SysClkDevice::Write8(uint32 addr, uint32 data) +// 割り込み状態を取得(?) +uint8 +SysClkDevice::GetInt() const { - uint32 an = addr - devaddr; + uint8 data = 0; - if (an == 0) { - // 何か書き込まれたら、フラグをクリア - sysint = false; - putlog(1, "システムクロック割り込みクリア"); - } else { - // それ以外 (特に +1, +2, +3) で何が起きるかは未調査 + if (sysint) { + data |= 0x80; } - return 0; + return data; } // イベントハンドラ void -SysClkDevice::Callback(int arg) +SysClkDevice::Callback(Event *ev) { - putlog(3, "システムクロック割り込み発生"); + putlog(3, "Interrupt occured"); + sysint = true; - gMPU->Interrupt(5); - gScheduler->AddEvent(&event); + 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); }