--- nono/vm/mk48t02.h 2026/04/29 17:04:52 1.1.1.8 +++ nono/vm/mk48t02.h 2026/04/29 17:05:33 1.1.1.16 @@ -4,49 +4,109 @@ // Licensed under nono-license.txt // +// +// NVRAM/RTC (MK48T02) +// + #pragma once -#include "device.h" -#include "mappedfile.h" #include "rtc.h" +#include "mappedfile.h" // MK48T02 の時計部分のレジスタと内部状態 struct MK48T02Clock { - // コントロールレジスタは以下の3つを保持。 - uint8 ctrl; + // MK48T02 の場合: + // + // b7 b6 b5 b4 b3 b2 b1 b0 + // +----+----+----+----+----+----+----+----+ + // 7ff | YEAR (4+4bit) | (YEAR: 00..99) + // +----+----+----+----+----+----+----+----+ + // 7fe | XX XX XX | MONTH (1+4bit) | (MONTH: 01..12) + // +----+----+----+----+----+----+----+----+ + // 7fd | XX XX | DATE (2+4bit) | (DATE: 01..31) + // +----+----+----+----+----+----+----+----+ + // 7fc | XX | FT | XX XX XX |WEEKDAY (3bit)| (WEEKDAY: 1..7) + // +----+----+----+----+----+----+----+----+ + // 7fb | KS | XX | HOUR (2+4bit) | (HOURS: 00..23) + // +----+----+----+----+----+----+----+----+ + // 7fa | XX | MINUTES (3+4bit) | (MINUTES: 00..59) + // +----+----+----+----+----+----+----+----+ + // 7f9 | ST | SECONDS (3+4bit) | (SECONDS: 00..59) + // +----+----+----+----+----+----+----+----+ + // 7f8 | W | R | CALIBRATION | (CONTROL) + // +----+----+----+----+----+----+----+----+ + // + // 'XX' はデータシートでは '0' だが書き込んだ値が読み返せるようだ。 + // SPARCstation2 の PROM で少なくとも +7fa の b7 はそう観測されている + // ように思える(Issue#75)。他のビットも全部同じ挙動をするかは確認手段 + // が手元にないので分からないが、内部構造的にはただの SRAM が見えてそう + // な気もするので、とりあえずデータシートで '0' になっているところは + // 全てメモリとして扱うことにする。何か分かったらまた対応する。 + // + // + // (参考までに) M48T02 の場合: + // + // 互換チップ M48T02 は、そのデータシート上 '0' のところでも、常に %0 + // しか読み出せないところ (以下表中 '0')と、書き込んだ値が読み出せる + // ところ (以下表中 'XX') とがあるようだ。 + // また機種によっては以下の位置に BLE/BE 拡張と CEB/CB 拡張がある。 + // BLE, BL は Battery Low Enable, Battery Low。(BL は Read Only) + // CEB, CB は Century Enable Bit, Century Bit。 + // + // b7 b6 b5 b4 b3 b2 b1 b0 + // +----+----+----+----+----+----+----+----+ + // 7ff | YEAR | + // +----+----+----+----+----+----+----+----+ + // 7fe | 0 0 0 | MONTH | + // +----+----+----+----+----+----+----+----+ + // 7fd | BLE| BL | DATE | + // +----+----+----+----+----+----+----+----+ + // 7fc | XX | FT | CEB| CB | 0 | WEEKDAY | + // +----+----+----+----+----+----+----+----+ + // 7fb | KS | 0 | HOUR | + // +----+----+----+----+----+----+----+----+ + // 7fa | 0 | MINUTES | + // +----+----+----+----+----+----+----+----+ + // 7f9 | ST | SECONDS | + // +----+----+----+----+----+----+----+----+ + // 7f8 | W | R | CALIBRATION | + // +----+----+----+----+----+----+----+----+ + + // コントロールレジスタは全ビットを保持。 + // ただし b5-b0 のキャリブレーションは未実装。 static const uint8 WRITE = 0x80; // WRITE BIT static const uint8 READ = 0x40; // READ BIT - static const uint8 SIGN = 0x20; // SIGN BIT (?) - // 秒カウンタ(BCD) - uint8 sec; - // 秒カウンタの bit7 が STOP ビット - static const uint8 STOP = 0x80; - // STOP 状態を保持 - bool stop; - - // 分カウンタ(BCD) - uint8 min; - - // 時カウンタ(BCD) - uint8 hour; - // 時カウンタの bit7 が KICKSTART ビット - static const uint8 KICKSTART = 0x80; + // 内部のカウンタと毎秒ラッチされるレジスタの2ステージ構成になっている。 + // ここには制御ビット部分は含まない。 + struct calendar { + uint8 sec; // BCD 秒(00-59) + uint8 min; // BCD 分(00-59) + uint8 hour; // BCD 時(00-23) + uint8 wday; // 曜日(1-7; 7=日曜) + uint8 mday; // BCD 日(01-31) + uint8 mon; // BCD 月(01-12) + uint8 year; // BCD 年(00-99; 西暦年の下二桁) + }; + calendar in {}; // 内部カウンタ + + // 秒カウンタの bit7 が STOP ビット。 + static const uint8 STOP = 0x80; + // STOP 状態を保持。 + uint8 stop {}; + + // 時カウンタの bit7 が KICKSTART ビット。 + // KICKSTART は内部発振器のスタートに使用するが、改良版である + // M48T02 (K なし)では廃止された。動作は実装しない。 + static const uint8 KICKSTART = 0x80; // KICKSTART 状態を保持 - bool kickstart; + uint8 kickstart {}; - // 曜日カウンタ - uint8 wday; - static const uint8 FREQTEST = 0x40; // Frequency Test (未実装) - - // 日カウンタ - uint8 mday; - // 月カウンタ - uint8 mon; - // 年カウンタ (0 が 1970年) - uint8 year; - static const int YEAR_EPOCH = 1970; + // 曜日カウンタの bit6 が Frequency Test ビット。動作は実装しない。 + static const uint8 FREQTEST = 0x40; + // FreqTest 状態を保持 + uint8 freqtest {}; }; class MK48T02Device : public RTCDevice @@ -54,36 +114,71 @@ class MK48T02Device : public RTCDevice using inherited = RTCDevice; public: MK48T02Device(); - virtual ~MK48T02Device() override; + ~MK48T02Device() override; bool Init() override; - void MonitorUpdate(TextScreen&) override; - void ClockIn() override; + void StartTime() override; // 指定したアドレスからの文字列を読み出す (内蔵 ROM からのアクセス用) std::string PeekString(uint32 addr) const; // 指定したアドレスに文字列を書き込む (内蔵 ROM からのアクセス用) bool WriteString(uint32 addr, const std::string& data); + // 全域をゼロクリアする (内蔵 ROM からのアクセス用) + void ClearAll(); + + // 内蔵カウンタへのアクセス。 + // ラッチカウンタではないことに注意。 + uint GetSec() const override; // 秒(0-59) + uint GetMin() const override; // 分(0-59) + uint GetHour() const override; // 時(0-23) + uint GetWday() const override; // 曜日(0が日曜) + uint GetMday() const override; // 日(1-31) + uint GetMon() const override; // 月(1-12) + uint GetYear() const override; // 年(西暦) + uint GetLeap() const override; // うるう年カウンタ(0..3) // BusIO インタフェース // (内蔵 ROM からのアクセス用に特別に public にしてある) - uint64 Read(uint32 offset); - uint64 Write(uint32 offset, uint32 data); - uint64 Peek(uint32 offset); - + busdata ReadPort(uint32 offset); + busdata WritePort(uint32 offset, uint32 data); + busdata PeekPort(uint32 offset); protected: - // BusIO インタフェース static const uint32 NPORT = 2048; + bool PokePort(uint32 offset, uint32 data); + + // 仮想RTCからのクロック + void Tick1Hz() override; + + // 内蔵カウンタへのアクセス。 + void SetSec(uint v) override; + void SetMin(uint v) override; + void SetHour(uint v) override; + void SetWday(uint v) override; + void SetMday(uint v) override; + void SetMon(uint v) override; + void SetYear(uint v) override; + void SetLeap(uint v) override; private: // バイナリ <-> BCD 変換 static inline uint8 num2BCD(uint8); static inline uint8 BCD2num(uint8); + // 年のデコード。 + uint DecodeYear(uint bcd_year) const; + // コントロールレジスタへの書き込み void WriteCtrl(uint32 data); + // NVRAM からカレンダへロード + MK48T02Clock::calendar Load() const; + + // カレンダを NVRAM にストア + void Store(const MK48T02Clock::calendar& cal); + + DECLARE_MONITOR_CALLBACK(MonitorUpdate); + // ファイル std::string filename {}; MappedFile file {}; @@ -92,8 +187,15 @@ class MK48T02Device : public RTCDevice // 時計 MK48T02Clock reg {}; - // レジスタとして読める値を定数にすることで、仮想マシン状態を - // 常に同じ状態にするためのパフォーマンス測定用フラグ。 - // なお NVRAM 部分は影響されない。 - bool force_fixed {}; + // うるう年補正量 + uint adjust_leap {}; + + // 外部レジスタをホールド(更新停止)しているときは true + bool hold {}; + + Monitor *monitor {}; }; + +static inline MK48T02Device *GetMK48T02Device() { + return Object::GetObject(OBJ_RTC); +}