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