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