|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: #include "rp5c15.h"
1.1.1.5 root 8: #include "mpu.h"
1.1 root 9: #include <sys/time.h>
10:
11: // アラームは未実装
12:
13: // XXX RTC の読み書きはロックしたほうがいいような気がする
14:
15: RP5C15Device::RP5C15Device()
1.1.1.8 ! root 16: : inherited("RTC")
1.1 root 17: {
18: devaddr = 0xe8a000;
19: devlen = 0x2000;
1.1.1.8 ! root 20:
! 21: monitor.func = (MonitorCallback_t)&RP5C15Device::MonitorUpdate;
! 22: monitor.SetSize(30, 17);
! 23: monitor.Regist(ID_MONITOR_RTC);
1.1 root 24: }
25:
26: RP5C15Device::~RP5C15Device()
27: {
28: }
29:
1.1.1.7 root 30: // 初期化
1.1 root 31: bool
1.1.1.7 root 32: RP5C15Device::Init()
1.1 root 33: {
34: // ホストの時刻をローカル時刻で取得
35: LoadHostRTC(true);
36:
37: // それをレジスタに反映。
38: UpdateReg(UPDATE_ALL);
39:
40: return true;
41: }
42:
1.1.1.2 root 43: uint64
1.1.1.6 root 44: RP5C15Device::Read(uint32 offset)
1.1.1.2 root 45: {
46: uint8 data;
47: int n;
48:
1.1.1.5 root 49: gMPU->AddCycle(29); // InsideOut p.135
50:
1.1.1.3 root 51: // バンクを展開する
1.1.1.6 root 52: n = bank * 0x10 + offset;
1.1 root 53:
54: // XXX 14, 15 は write only らしいが何が読めるか
55:
56: switch (n) {
57: case 0x1d: // MODE (Bank0 と同じ)
58: case 0x1e: // TEST (Bank0 と同じ)
59: case 0x1f: // RESET (Bank0 と同じ)
60: n -= 0x10;
61: FALLTHROUGH;
62:
63: case 0x00 ... 0x1c:
64: data = reg.r[n];
65: break;
66: default:
1.1.1.3 root 67: __unreachable();
1.1 root 68: }
69:
70: // 下位4ビット(RP5C15) の中の未実装ビットは %0 だが
71: // 上位4ビットは %1 になる。
72: data |= 0xf0;
1.1.1.6 root 73: putlog(2, "RTC Bank%d $%x -> $%02x", bank, offset, data);
1.1 root 74:
75: return data;
76: }
77:
78: uint64
1.1.1.6 root 79: RP5C15Device::Write(uint32 offset, uint32 data)
1.1 root 80: {
81: int n;
82:
1.1.1.5 root 83: gMPU->AddCycle(38); // InsideOut p.135
84:
1.1.1.3 root 85: // バンクを展開する
1.1.1.6 root 86: n = bank * 0x10 + offset;
1.1 root 87:
1.1.1.6 root 88: putlog(2, "RTC Bank%d $%x <- $%x", bank, offset, (data & 0x0f));
1.1 root 89:
90: switch (n) {
91: case RP5C15::SEC1:
92: rtc.sec = reg.sec10 * 10 + (data & reg.mask[n]);
93: UpdateReg(UPDATE_SEC);
94: break;
95: case RP5C15::SEC10:
96: rtc.sec = (data & reg.mask[n]) * 10 + reg.sec1;
97: UpdateReg(UPDATE_SEC);
98: break;
99:
100: case RP5C15::MIN1:
101: rtc.min = reg.min10 * 10 + (data & reg.mask[n]);
102: UpdateReg(UPDATE_MIN);
103: break;
104: case RP5C15::MIN10:
105: rtc.min = (data & reg.mask[n]) * 10 + reg.min1;
106: UpdateReg(UPDATE_MIN);
107: break;
108:
109: case RP5C15::HOUR1: // hour1
110: if (reg.sel24) {
111: // 24時間計
112: rtc.hour = reg.hour10 * 10 + (data & reg.mask[n]);
113: } else {
114: // 12時間計
115: rtc.hour = ((reg.hour10 & 0x02) ? 12 : 0)
116: + (reg.hour10 & 0x01) * 10 + (data & reg.mask[n]);
117: }
118: UpdateReg(UPDATE_HOUR);
119: break;
120: case RP5C15::HOUR10: // hour10
121: if (reg.sel24) {
122: // 24時間計
123: rtc.hour = (data & reg.mask[n]) * 10 + reg.hour1;
124: } else {
125: // 12時間計
126: rtc.hour = ((data & 0x02) ? 12 : 0)
127: + (data & 0x01) * 10 + reg.hour1;
128: }
129: UpdateReg(UPDATE_HOUR);
130: break;
131:
132: case RP5C15::WDAY:
133: // 曜日カウンタは日付などとは一切連動していない
134: rtc.wday = data & reg.mask[n];
135: UpdateReg(UPDATE_DAY);
136: break;
137:
138: case RP5C15::DAY1:
139: rtc.mday = reg.day10 * 10 + (data & reg.mask[n]);
140: UpdateReg(UPDATE_DAY);
141: break;
142: case RP5C15::DAY10:
143: rtc.mday = (data & reg.mask[n]) * 10 + reg.day1;
144: UpdateReg(UPDATE_DAY);
145: break;
146:
147: case RP5C15::MON1:
148: rtc.mon = reg.mon10 * 10 + (data & reg.mask[n]);
149: UpdateReg(UPDATE_MON);
150: break;
151: case RP5C15::MON10:
152: rtc.mon = (data & reg.mask[n]) * 10 + reg.mon1;
153: UpdateReg(UPDATE_MON);
154: break;
155:
156: case RP5C15::YEAR1:
157: rtc.year = 1980 + (reg.year10 * 10) + (data & reg.mask[n]);
158: UpdateReg(UPDATE_YEAR);
159: break;
160: case RP5C15::YEAR10:
161: rtc.year = 1980 + (data & reg.mask[n]) * 10 + reg.year1;
162: UpdateReg(UPDATE_YEAR);
163: break;
164:
165: case RP5C15::CLKSEL:
166: case 0x12 ... 0x1c:
167: // マスクして書き込む
168: reg.r[n] = data & reg.mask[n];
169: break;
170:
171: case RP5C15::ADJUST:
172: {
173: // %0 なら何もしない?
174: if ((data & 1) == 0) {
175: break;
176: }
177: // %1 ならアジャスト。
178:
179: // 現在の秒が 0..29秒なら 0秒に切り下げ。
180: // 30..59秒なら +1分0秒に繰り上げ。
181: // いずれにしても 1秒と10秒の位は 0 にする。
182: uint32 updates = UPDATE_SEC;
183: if (rtc.sec < 30) {
184: // 1分切り下げ
185: rtc.sec = 0;
186: } else {
187: // 1分繰り上げ
188: rtc.sec = 0;
189: updates = CarryMin(updates);
190: }
191: UpdateReg(updates);
192: break;
193: }
194:
195: case RP5C15::MODE:
196: case RP5C15::MODE_BANK1:
197: reg.mode = data & reg.mask[RP5C15::MODE];
198: // bank を更新
199: bank = (data & RP5C15::MODE_BANK);
200: break;
201:
202: case RP5C15::TEST:
203: case RP5C15::TEST_BANK1:
204: data &= reg.mask[RP5C15::TEST];
205: if (data == 0) {
206: // 通常 0 を書き込むこと、となっているので 0 は許可
207: } else {
208: putlog(0, "TEST <- $%1x 未実装書き込み", data);
209: }
210: reg.test = data;
211: break;
212:
213: case RP5C15::RESET:
214: case RP5C15::RESET_BANK1:
215: reg.reset = data & reg.mask[RP5C15::RESET];
216: if ((reg.reset & RP5C15::RESET_TIMER)) {
217: // %1 を書き込むと、秒未満の内部カウンタをリセット
218: // 繰り上がり分の保持も消えるけどいいよな?
219: rtc.cnt = 0;
220: }
221: break;
222:
223: default:
224: break;
225: }
226: return 0;
227: }
228:
229: uint64
1.1.1.6 root 230: RP5C15Device::Peek(uint32 offset)
1.1 root 231: {
232: int n;
233:
1.1.1.3 root 234: // バンクを展開する
1.1.1.6 root 235: n = bank * 0x10 + offset;
1.1 root 236:
237: switch (n) {
238: case RP5C15::MODE_BANK1:
239: case RP5C15::TEST_BANK1:
240: case RP5C15::RESET_BANK1:
241: // Bank0 と同じものが見える
242: n -= 0x10;
243: FALLTHROUGH;
244:
245: case 0x00 ... 0x1c:
246: return reg.r[n] | 0xf0;
247:
248: default:
1.1.1.3 root 249: __unreachable();
1.1 root 250: }
251: }
252:
1.1.1.4 root 253: void
1.1.1.8 ! root 254: RP5C15Device::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 255: {
256: int x;
257: int y;
258:
259: //
260: // #0 #1
261: // 1:4 2
262: //
263: // 0 1 2 3 4 5 6 7 8 9 A B C D E F
264: // Bank0 0 1 2 3 4 5 6 7 8 9 a b c d e f
265: // Bank1
266:
1.1.1.8 ! root 267: screen.Clear();
1.1 root 268:
1.1.1.8 ! root 269: screen.Puts(0, 0, " #0 #1");
1.1 root 270: y = 1;
271: for (int i = 0; i < 13; i++, y++) {
1.1.1.8 ! root 272: screen.Print(0, y, "%X:%x %x", i, reg.r[i], reg.r[i + 16]);
1.1 root 273: }
274: for (int i = 13; i < 16; i++, y++) {
1.1.1.8 ! root 275: screen.Print(0, y, "%X:%x", i, reg.r[i]);
1.1 root 276: }
277:
278: x = 8;
279: y = 0;
280:
281: int year = reg.year10 * 10 + reg.year1 + 1980;
282: int mon = reg.mon10 * 10 + reg.mon1;
283: int day = reg.day10 * 10 + reg.day1;
1.1.1.8 ! root 284: screen.Print(x, y, "%04d/%02d/%02d(%s)",
1.1 root 285: year, mon, day, wdays[reg.wday]);
286: y++;
287:
288: int hour;
289: if (reg.sel24) {
290: hour = reg.hour10 * 10 + reg.hour1;
291: } else {
292: hour = (reg.hour10 & 1) * 10 + reg.hour1;
1.1.1.8 ! root 293: screen.Puts(x, y, (reg.hour10 & 2) == 0 ? "AM" : "PM");
1.1 root 294: }
295: int min = reg.min10 * 10 + reg.min1;
296: int sec = reg.sec10 * 10 + reg.sec1;
1.1.1.8 ! root 297: screen.Print(x + 3, y, "%02d:%02d:%02d", hour, min, sec);
1.1 root 298: y++;
299:
1.1.1.8 ! root 300: screen.Print(x, y++, "Leap %d", reg.leap);
1.1 root 301: y++;
302:
1.1.1.8 ! root 303: screen.Print(x, y++, "CLKOUT %c", clkout ? 'H' : 'L');
! 304: screen.Print(x, y++, "ALARM %c", alarmout ? 'H' : 'L');
1.1 root 305: y++;
306:
1.1.1.8 ! root 307: screen.Print(x, y++, "cnt %2d", rtc.cnt);
1.1 root 308: }
309:
310: // 32Hz クロック入力
311: //
312: // 32Hz ごとに呼ばれて、これが RTC の時計を進める処理をするところ。
313: // RTC を実時間に同期して進めるモードなら実時間を計るスケジューラから、
314: // RTC を仮想時間に同期して進めるモードなら VM スケジューラから呼ばれる。
315: //
316: // CLKOUT 128Hz 以上のモードには対応していない。
317: void
318: RP5C15Device::ClockIn()
319: {
320: uint32 updates;
321:
322: // 32Hz カウンタを進める
323: updates = CountUp();
324:
325: // レジスタの値を更新する
326: UpdateReg(updates);
327:
328: // CLKOUT 端子の切り替え周期は CLKSEL による
329: switch (reg.clksel) {
330: case 0: // ハイインピーダンス
331: case 1: // 16384Hz
332: case 2: // 1024Hz
333: case 3: // 128Hz
334: // 128Hz 点滅とか点灯みたいなもんだろ(適当)
335: clkout = true;
336: break;
337:
338: case 4: // 16Hz
339: clkout = !clkout;
340: // アラーム端子 16Hz パルス出力は %0 ならオン
341: if ((reg.reset & RP5C15::RESET_16Hz) == 0) {
342: alarmout = !alarmout;
343: }
344: break;
345:
346: case 5: // 1Hz
347: // CLKOUT 端子は秒の繰り上がりに同期している。
348: // ALARM 端子の 1Hz パルスもそれに同期するとは書いてないけど、
349: // 面倒なのでとりあえず同じところで処理してしまう。
350: // TIMER_EN (秒への繰り上がり) が無効でも反転自体は起きる気が
351: // するので、ここでは UPDATE_SEC ではなく cnt で判定する。
352: if ((rtc.cnt & 31) == 0) {
353: // CLKOUT は 1秒の繰り上がりが起きる時に 'H' になる
354: clkout = true;
355:
356: // ALARM 反転
357: if ((reg.reset & RP5C15::RESET_1Hz) == 0) {
358: alarmout = !alarmout;
359: }
360: } else if ((rtc.cnt & 31) == 16) {
361: // CLKOUT、その反対側
362: clkout = false;
363:
364: // ALARM 反転
365: if ((reg.reset & RP5C15::RESET_1Hz) == 0) {
366: alarmout = !alarmout;
367: }
368: }
369: break;
370:
371: case 6: // 1/60Hz
372: if ((updates & UPDATE_MIN)) {
373: // 00秒になったので CLKOUT を 'H' に
374: clkout = true;
375: } else if ((updates & UPDATE_SEC) && rtc.sec == 30) {
376: // 30秒になったので CLKOUT を 'L' に
377: clkout = false;
378: }
379: break;
380:
381: case 7: // 'L' 固定
382: clkout = false;
383: break;
384: }
385: }
386:
387: // updates で指定された桁に該当するレジスタを更新する。
388: // 繰り上がりかどうかは処理せず現在の値を RP5C15 レジスタにセットするだけ。
389: void
390: RP5C15Device::UpdateReg(uint32 updates)
391: {
392: if ((updates & UPDATE_SEC)) {
393: reg.sec1 = rtc.sec % 10;
394: reg.sec10 = rtc.sec / 10;
395: }
396: if ((updates & UPDATE_MIN)) {
397: reg.min1 = rtc.min % 10;
398: reg.min10 = rtc.min / 10;
399: }
400: if ((updates & UPDATE_HOUR)) {
401: if (reg.sel24) {
402: // 24時間計モードでは普通の BCD
403: reg.hour1 = rtc.hour % 10;
404: reg.hour10 = rtc.hour / 10;
405: } else {
406: // 12時間計モードなら、時のbit1 が AM/PM を表す
407: int h12 = rtc.hour % 12;
408: reg.hour1 = h12 % 10;
409: reg.hour10 = h12 / 10;
410: reg.hour10 |= (rtc.hour >= 12) ? 0x02 : 0x00;
411: }
412: }
413:
414: if ((updates & UPDATE_DAY)) {
415: reg.wday = rtc.wday;
416: reg.day1 = rtc.mday % 10;
417: reg.day10 = rtc.mday / 10;
418: }
419: if ((updates & UPDATE_MON)) {
420: reg.mon1 = rtc.mon % 10;
421: reg.mon10 = rtc.mon / 10;
422: }
423: if ((updates & UPDATE_YEAR)) {
424: // X680x0 では 0 を 1980年としている。
425: int y = rtc.year - 1980;
426: reg.year1 = y % 10;
427: reg.year10 = y / 10;
428: }
429: }
430:
431: /*static*/ const uint8
432: RP5C15::mask[] = {
433: 0x0f, 0x07, 0x0f, 0x07, // 1秒 10秒 1分 10分
434: 0x0f, 0x03, 0x07, 0x0f, // 1時 10時 曜日 1日
435: 0x01, 0x0f, 0x01, 0x0f, // 10日 1月 10月 1年
436: 0x0f, 0x0d, 0x0f, 0x0f, // 10年 MODE TEST RESET
437:
438: 0x07, 0x01, 0x0f, 0x07, // CLKO Adjust AL1分 AL10分
439: 0x0f, 0x03, 0x07, 0x0f, // AL1時 AL10時 AL曜日 AL1日
440: 0x03, 0x00, 0x01, 0x03, // AL10日 --- 12/24 閏年
441: 0x00 // ---
442: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.