--- nono/vm/rtc.h 2026/04/29 17:04:45 1.1.1.4 +++ nono/vm/rtc.h 2026/04/29 17:05:11 1.1.1.9 @@ -4,82 +4,137 @@ // Licensed under nono-license.txt // +// +// RTC の共通部分 +// + #pragma once #include "device.h" +#include "event.h" +#include -struct RTC -{ - // 内部カウンタ - // - // 1/32秒ごとに増加する内部カウンタ。これが 32 になると1秒ということ。 - // ただしタイマー停止中でも1秒の繰り上がり1回は記憶できるそうなので、 - // 実際にはこれを 6ビットのカウンタ (63まで) として使用し、 - // 32 になったら秒繰り上がりではなく、タイマー動作状態で 32 以上だったら - // 32 を引いてから秒繰り上がり処理になると思う。 - uint cnt; - - int sec; // 秒(0-59) - int min; // 分(0-59) - int hour; // 時(0-23) - int wday; // 曜日(0-6; 0が日曜) - int mday; // 日(1-31) - int mon; // 月(1-12) - int year; // 年(西暦) - - bool enable_sec; // 秒へのカウントアップを行うなら true -}; - -// 汎用のリアルタイムクロック -// RP5C15 と MK48T02 の共通部分。 -// 本来、これは IODevice である必要はないけど、C++ の制約というか多重継承は -// あまりやりたくないので、これを IODevice 派生クラスとしておく。 +// これ自体が IODevice である必要はないが派生側が IODevice を必要とするため。 class RTCDevice : public IODevice { using inherited = IODevice; public: - RTCDevice(); + RTCDevice(const std::string& objname_); virtual ~RTCDevice() override; - // 初期化 - // VM の RTC に仮想時刻(の初期値)をセットする。 - virtual bool InitRTC() = 0; + bool Init() override; - // 32Hz クロック入力。 + virtual void StartTime(); + + // clock-sync が実時間同期なら true を返す + bool GetSyncRealtime() const { return sync_rt; } + + // 電源 OFF 時の実時間でのクロック入力。 + // 次回のクロック発生までの時間間隔を返す。 + uint64 ClockIn_PowerOff(); + + // 現在の日付時刻を返す。 + // ここで取得できる値は機種によらず共通のフォーマットだが、 + // 値は範囲外の状態になる可能性が常にあることに注意。 // - // 32Hz ごとに呼ばれて、これが RTC の時計を進める処理をするところ。 - // RTC を実時間に同期して進めるモードなら実時間を計るスケジューラから、 - // RTC を仮想時間に同期して進めるモードなら VM スケジューラから呼ばれる。 - // 派生クラス側で用意。 - virtual void ClockIn() = 0; + // RP5C15 の場合、1つのレジスタが10進数一桁ごとを扱っているため、特に + // ゲストプログラムが月の更新するしようとするときに一時的に範囲外の + // 状態になることが避けられない。 + // 例えば、もともと12月31日だったものを9月30日に変更する場合、 + // 上の桁から更新したら(M10,M1,D10,D1) + // 12/31 -> 02/31 -> 09/31 -> 09/31 -> 09/30 + // 下の桁から更新したら(D1,D10,M1,M10) + // 12/31 -> 12/30 -> 12/30 -> 19/30 -> 09/30 + // このように、2/31, 9/31, 19/30 などが中間状態として発生する。 + virtual uint GetSec() const = 0; // 秒(0-59) + virtual uint GetMin() const = 0; // 分(0-59) + virtual uint GetHour() const = 0; // 時(0-23) + virtual uint GetWday() const = 0; // 曜日(0-6、0が日曜) + virtual uint GetMday() const = 0; // 日(1-31) + virtual uint GetMon() const = 0; // 月(1-12) + virtual uint GetYear() const = 0; // 年(西暦) + virtual uint GetLeap() const = 0; // うるう年カウンタ(0-3、0がうるう年) protected: - // ホストの現在時刻を rtc に取得 - void LoadHostRTC(bool use_localtime); + // 32Hz クロック入力。 + // clock-sync の設定に応じて、実時間または仮想時間で + // 32Hz ごとに呼ばれて、これが RTC の時計を進める処理をするところ。 + void ClockIn(); + + // 仮想 RTC のクロック出力 + + // 継承クラス向けに 32Hz で呼び出される。 + virtual void Tick32Hz(); + + // 継承クラス向けに 2Hz で呼び出される。 + // タイミングは、0/32秒目と 16/32秒目。 + virtual void Tick2Hz(); + + // 継承クラス向けに 1Hz で呼び出される。 + // タイミングは、0/32秒目。 + // この関数から秒カウンタ更新をすること。 + virtual void Tick1Hz(); + + // 秒をカウントアップし、秒以上の繰り上がりを処理する。 + void CountUpSec(); + // 分をカウントアップし、分以上の繰り上がりを処理する。 + // RP5C15 の ADJUST 機能を実現するために秒と分離。 + void CountUpMin(); + + virtual void SetSec(uint v) = 0; + virtual void SetMin(uint v) = 0; + virtual void SetHour(uint v) = 0; + virtual void SetWday(uint v) = 0; + virtual void SetMday(uint v) = 0; + virtual void SetMon(uint v) = 0; + virtual void SetYear(uint v) = 0; + virtual void SetLeap(uint v) = 0; + + // 32Hz でカウントアップする内部カウンタ + uint64 cnt {}; + + // 年エポック。システムに依存。 + uint year_epoch {}; + + // 時刻設定時に localtime を使う場合は true + bool use_localtime {}; + + // レジスタとして読める値を定数にすることで、仮想マシン状態を + // 常に同じ状態にするためのパフォーマンス測定用フラグ。 + // なお NVRAM 部分は影響されない。 + bool force_fixed {}; + + // RTC の入力クロックが実時間の場合は true + // 仮想時間の場合は false + bool sync_rt {}; + + // 設定で指定された日付時刻 + struct tm rtc_tm {}; + + // 曜日文字列 (0 = "Sun", ...) + // MK48T02 の誤運用にも対応するため 0..7 の範囲をとる。 + static const char * const wdays[8]; + + private: + // デバッグオプションの日付時刻文字列を解析する。 + bool ParseDate(const std::string& str, int *y, int *m, int *d, int *w); + bool ParseTime(const std::string& str, int *h, int *m, int *s); + + // 現在の月の最終日を返す (月の繰り上がり処理用) + int GetLastMday() const; + + // イベントコールバック + void Callback(Event& ev); - // どの位が更新されたか(繰り上がったか)を示すフラグ。 - static const uint32 UPDATE_SEC = (1U << 0); - static const uint32 UPDATE_MIN = (1U << 1); - static const uint32 UPDATE_HOUR = (1U << 2); - static const uint32 UPDATE_DAY = (1U << 3); - static const uint32 UPDATE_MON = (1U << 4); - static const uint32 UPDATE_YEAR = (1U << 5); - - static const uint32 UPDATE_ALL = 0xffffffffU; - - uint32 CountUp(); // 1/32 秒カウンタの更新 - uint32 CarrySec(uint32); // 秒への繰り上がり - uint32 CarryMin(uint32); // 分への繰り上がり - uint32 CarryHour(uint32); // 時への繰り上がり - uint32 CarryDay(uint32); // 日への繰り上がり - uint32 CarryMon(uint32); // 月への繰り上がり - uint32 CarryYear(uint32); // 年への繰り上がり - bool IsLeapMon() const; // 閏月かどうか + // イベント間隔 + const uint period = 31.25_msec; - RTC rtc {}; // 内部状態 + // システムクロックが刻んでいる時刻 + uint64 stime {}; - static const char * const wdays[7]; // 曜日文字列 (0 = "Sun", ...) + // RTC クロック入力 + Event event { this }; }; -extern std::unique_ptr gRTC; +extern RTCDevice *gRTC;