|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: #include "mk48t02.h"
1.1.1.3 root 8: #include "config.h"
1.1.1.4 root 9: #include "mainapp.h"
1.1.1.7 root 10: #include "mpu.h"
1.1 root 11:
12: //
13: // MK48T02
14: //
15: // SRAM 部分はビッグエンディアンのままメモリに置かれるので、
16: // 必要ならアクセス時に都度変換すること。
17: //
18: // 仕様は 0x45000000 から 2KB だが、
19: // 0x44000000 から 0x48000000 の手前まで 2KB 単位でミラーが見える。
20:
21: MK48T02Device::MK48T02Device()
1.1.1.9 ! root 22: : inherited("MK48T02")
1.1 root 23: {
1.1.1.9 ! root 24: AddAlias("NVRAM");
! 25: AddAlias("RTC");
! 26:
1.1 root 27: devaddr = 0x45000000;
1.1.1.9 ! root 28:
! 29: monitor.func = (MonitorCallback_t)&MK48T02Device::MonitorUpdate;
! 30: monitor.SetSize(25, 2);
! 31: monitor.Regist(ID_MONITOR_MK48T02);
1.1 root 32: }
33:
34: MK48T02Device::~MK48T02Device()
35: {
36: }
37:
38: bool
39: MK48T02Device::Init()
40: {
41: // ファイルのロード
1.1.1.3 root 42: filename = gMainApp.GetVMDir() + "NVRAM.DAT";
1.1.1.4 root 43: file.SetFilename(filename);
44: file.SetDispname(string_format("NVRAM \"%s\"", filename.c_str()));
45: mem = file.OpenCreate(2040);
46: if (mem == NULL) {
1.1 root 47: return false;
48: }
49:
50: // レジスタ値固定オプション (パフォーマンス測定用)
1.1.1.4 root 51: force_fixed = gConfig->Find(".rtc-force-fixed").AsInt();
1.1 root 52: if (force_fixed) {
1.1.1.7 root 53: putmsgn("force_fixed");
1.1 root 54: }
55:
1.1.1.8 root 56: // RTC を初期化
1.1 root 57: // ホストの時刻を UTC で取得
58: LoadHostRTC(false);
59:
60: return true;
61: }
62:
63: // バイナリ表現を BCD 表現に変換する
64: /*static*/ inline uint8
65: MK48T02Device::num2BCD(uint8 num)
66: {
67: return ((num / 10) << 4) + (num % 10);
68: }
69:
70: // BCD 表現をバイナリ表現に変換する
71: /*static*/ inline uint8
72: MK48T02Device::BCD2num(uint8 bcd)
73: {
74: return (bcd >> 4) * 10 + (bcd & 0x0f);
75: }
76:
77: uint64
1.1.1.7 root 78: MK48T02Device::Read(uint32 offset)
1.1 root 79: {
80: uint32 data;
81:
1.1.1.7 root 82: if (offset < 2040) {
83: data = mem[offset];
84: putlog(3, "$%08x -> $%02x", gMPU->GetPaddr(), data);
1.1 root 85: } else {
86: // XXX ReadBit が立っていない時は何が読めるのか
1.1.1.7 root 87: switch (offset - 2040) {
1.1 root 88: case 0: // コントロールレジスタ
89: data = reg.ctrl;
90: break;
91: case 1: // 秒
92: data = reg.sec;
93: if (reg.stop) {
94: data |= MK48T02Clock::STOP;
95: }
96: break;
97: case 2: // 分
98: data = reg.min;
99: break;
100: case 3: // 時
101: data = reg.hour;
102: if (reg.kickstart) {
103: data |= MK48T02Clock::KICKSTART;
104: }
105: break;
106: case 4: // 曜日
107: data = reg.wday;
108: break;
109: case 5: // 日
110: data = reg.mday;
111: break;
112: case 6: // 月
113: data = reg.mon;
114: break;
115: case 7: // 年
116: data = reg.year;
117: break;
118: default:
1.1.1.3 root 119: __unreachable();
1.1 root 120: }
121:
122: if (force_fixed) {
123: // パフォーマンス測定をする際に、RTC が実時間を返すと
124: // ゲストの状態が毎回異なることになるため、
125: // いつ何時も同じ値を返すことで、世界から隔絶された
126: // VM を作り、パフォーマンス測定を安定させる。
127: data = 3;
128: }
1.1.1.7 root 129: putlog(3, "$%08x -> $%02x", gMPU->GetPaddr(), data);
1.1 root 130: }
131: return data;
132: }
133:
134: uint64
1.1.1.7 root 135: MK48T02Device::Write(uint32 offset, uint32 data)
1.1 root 136: {
1.1.1.7 root 137: if (offset < 2040) {
138: putlog(3, "$%08x <- $%02x", gMPU->GetPaddr(), data);
139: mem[offset] = data;
1.1 root 140: } else {
1.1.1.7 root 141: putlog(3, "$%08x <- $%02x", gMPU->GetPaddr(), data);
142: switch (offset - 2040) {
1.1 root 143: case 0: // コントロール
144: WriteCtrl(data);
145: break;
146: case 1: // 秒 (b7 は STOP BIT)
147: reg.stop = (data & MK48T02Clock::STOP);
148: if ((reg.ctrl & MK48T02Clock::WRITE)) {
149: rtc.sec = BCD2num(data & 0x7f);
150: }
151: break;
152: case 2: // 分
153: if ((reg.ctrl & MK48T02Clock::WRITE)) {
154: rtc.min = BCD2num(data & 0x7f);
155: }
156: break;
157: case 3: // 時 (b7 は KICK START)
158: reg.kickstart = (data & MK48T02Clock::KICKSTART);
159: if ((reg.ctrl & MK48T02Clock::WRITE)) {
160: rtc.hour = BCD2num(data & 0x3f);
161: }
162: break;
163: case 4: // 曜日 (b6 は Frequency Test (未実装))
164: if ((reg.ctrl & MK48T02Clock::WRITE)) {
165: rtc.wday = data & 0x07;
166: }
167: break;
168: case 5: // 日
169: if ((reg.ctrl & MK48T02Clock::WRITE)) {
170: rtc.mday = BCD2num(data & 0x3f);
171: }
172: break;
173: case 6: // 月
174: if ((reg.ctrl & MK48T02Clock::WRITE)) {
175: rtc.mon = BCD2num(data & 0x1f);
176: }
177: break;
178: case 7: // 年
179: if ((reg.ctrl & MK48T02Clock::WRITE)) {
180: rtc.year = BCD2num(data) + MK48T02Clock::YEAR_EPOCH;
181: }
182: break;
183: default:
1.1.1.3 root 184: __unreachable();
1.1 root 185: }
186: }
187: return 0;
188: }
189:
190: uint64
1.1.1.7 root 191: MK48T02Device::Peek(uint32 offset)
1.1 root 192: {
193: uint8 data;
194:
1.1.1.7 root 195: if (offset < 2040) {
196: return mem[offset];
1.1 root 197: } else {
1.1.1.7 root 198: switch (offset - 2040) {
1.1 root 199: case 0: // コントロールレジスタ
200: return reg.ctrl;
201: case 1: // 秒 | STOP
202: data = reg.sec;
203: if (reg.stop) {
204: data |= MK48T02Clock::STOP;
205: }
206: return data;
207: case 2: // 分
208: return reg.min;
209: case 3: // 時
210: data = reg.hour;
211: if (reg.kickstart) {
212: data |= MK48T02Clock::KICKSTART;
213: }
214: return data;
215: case 4: // 曜日
216: return reg.wday;
217: case 5: // 日
218: return reg.mday;
219: case 6: // 月
220: return reg.mon;
221: case 7: // 年
222: return reg.year;
223: default:
1.1.1.3 root 224: __unreachable();
1.1 root 225: }
226: }
227: }
228:
1.1.1.5 root 229: void
1.1.1.9 ! root 230: MK48T02Device::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 231: {
1.1.1.9 ! root 232: screen.Clear();
1.1 root 233:
1.1.1.9 ! root 234: screen.Print(0, 0, "WR:%d RD:%d",
1.1 root 235: (reg.ctrl & MK48T02Clock::WRITE) ? 1 : 0,
236: (reg.ctrl & MK48T02Clock::READ) ? 1 : 0);
1.1.1.9 ! root 237: screen.Print(0, 1, "ST:%d KS:%d",
1.1 root 238: reg.stop,
239: reg.kickstart);
240: // XXX レジスタの値はラッチされるまで意味がないので
241: // どうしたものか。
1.1.1.9 ! root 242: screen.Print(10, 0, "%4d/%02d/%02d(%s)",
1.1 root 243: rtc.year, rtc.mon, rtc.mday, wdays[rtc.wday]);
1.1.1.9 ! root 244: screen.Print(10, 1, "%02d:%02d:%02d",
1.1 root 245: rtc.hour, rtc.min, rtc.sec);
246: }
247:
248: // コントロールレジスタへの書き込み
249: void
250: MK48T02Device::WriteCtrl(uint32 data)
251: {
252: reg.ctrl = data;
253:
254: if ((reg.ctrl & MK48T02Clock::WRITE)) {
255: // WRITE ビットを立てると計時を止める
256: rtc.enable_sec = false;
257: } else {
258: // WRITE ビットを下ろすと計時を再開して
259: rtc.enable_sec = true;
260: // KickStart, FreqTest をゼロにする
261: reg.kickstart = false;
262: }
263:
264: if ((reg.ctrl & MK48T02Clock::READ)) {
265: // READ ビットを立てたところで計時をとめて(?)
266: // 値をレジスタにラッチ(?)
267: rtc.enable_sec = false;
268: reg.sec = num2BCD(rtc.sec);
269: reg.min = num2BCD(rtc.min);
270: reg.hour = num2BCD(rtc.hour);
271: reg.wday = rtc.wday;
272: reg.mday = num2BCD(rtc.mday);
273: reg.mon = num2BCD(rtc.mon);
274: reg.year = num2BCD(rtc.year - MK48T02Clock::YEAR_EPOCH);
275: } else {
276: // READ ビットを下ろすと計時を再開
277: rtc.enable_sec = true;
278: }
279: }
280:
281: // 32Hz クロック入力
282: //
283: // 32Hz ごとに呼ばれて、これが RTC の時計を進める処理をするところ。
284: // RTC を実時間に同期して進めるモードなら実時間を計るスケジューラから、
285: // RTC を仮想時間に同期して進めるモードなら VM スケジューラから呼ばれる。
286: void
287: MK48T02Device::ClockIn()
288: {
289: // 32Hz カウンタを進める。
290: // 内部表現とデバイスから読み出せる値が同じなので、更新処理は不要。
291: CountUp();
292: }
1.1.1.3 root 293:
294: // 指定したアドレスからのゼロ終端文字列を返す
295: // (内蔵 ROM からのアクセス用)
296: std::string
297: MK48T02Device::PeekString(uint32 addr) const
298: {
299: std::string rv = "";
300:
1.1.1.7 root 301: addr &= 0x7ff;
302: for (; mem[addr]; addr++) {
303: rv += mem[addr];
1.1.1.3 root 304: }
305: return rv;
306: }
307:
308: // 指定したアドレスに文字列を書き込み、
309: // 16 バイトに満たないところはゼロでパディングする。
310: // 書き込めれば true を、書き込めなければ false を返す。
311: // (内蔵 ROM からの書き込み用)
312: bool
313: MK48T02Device::WriteString(uint32 addr, const std::string& data)
314: {
315: int i = 0;
316:
1.1.1.9 ! root 317: // ここの文字列は16バイト未満なら一見ゼロ終端のようだが、
! 318: // 16バイトならゼロ終端せず16文字とするフォーマット。
! 319:
! 320: if (data.length() > 16) {
1.1.1.3 root 321: return false;
322: }
323:
324: addr &= 0x7ff;
325: for (; i < data.length(); i++) {
1.1.1.9 ! root 326: mem[addr + i] = data[i];
1.1.1.3 root 327: }
328: for (; i < 16; i++) {
1.1.1.9 ! root 329: mem[addr + i] = 0;
1.1.1.3 root 330: }
331: return true;
332: }
1.1.1.9 ! root 333:
! 334: // 全域をゼロクリアする。
! 335: // クリアするだけなので、その後何か書いたりチェックサムを計算したりする
! 336: // 必要はある。
! 337: // (内蔵 ROM からの書き込み用)
! 338: void
! 339: MK48T02Device::ClearAll()
! 340: {
! 341: for (int i = 0, sz = file.GetMemSize(); i < sz; i++) {
! 342: mem[i] = 0;
! 343: }
! 344: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.