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