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