|
|
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:
1.1.1.10 root 7: //
8: // RTC (RP5C15)
9: //
1.1 root 10:
11: // アラームは未実装
12:
1.1.1.10 root 13: #include "rp5c15.h"
1.1.1.11 root 14: #include "mfp.h"
1.1.1.16! root 15: #include "monitor.h"
1.1.1.11 root 16: #include "power.h"
1.1 root 17:
1.1.1.10 root 18: // コンストラクタ
1.1 root 19: RP5C15Device::RP5C15Device()
1.1.1.12 root 20: : inherited()
1.1 root 21: {
1.1.1.10 root 22: // X68k の RTC epoch は 1980年固定で変更の必要はないので、設定も見ない。
1.1.1.9 root 23: year_epoch = 1980;
1.1.1.10 root 24: // X68k は通常ローカルタイム (というか JST) で使用する前提
1.1.1.9 root 25: use_localtime = true;
26:
1.1.1.16! root 27: monitor = gMonitorManager->Regist(ID_MONITOR_RTC, this);
! 28: monitor->func = ToMonitorCallback(&RP5C15Device::MonitorUpdate);
! 29: monitor->SetSize(38, 22);
1.1 root 30: }
31:
1.1.1.10 root 32: // デストラクタ
1.1 root 33: RP5C15Device::~RP5C15Device()
34: {
35: }
36:
1.1.1.7 root 37: // 初期化
1.1 root 38: bool
1.1.1.7 root 39: RP5C15Device::Init()
1.1 root 40: {
1.1.1.12 root 41: if (inherited::Init() == false) {
42: return false;
43: }
44:
45: mfp = GetMFPDevice();
1.1.1.14 root 46: power = dynamic_cast<X68030PowerDevice *>(GetPowerDevice());
1.1.1.12 root 47:
1.1.1.9 root 48: // デフォルトで 24 時間計にしておく
49: reg.sel24 = 0x01;
50: // デフォルトで 1HZ_ON, 16HZ_ON をネゲート(1)しておく
51: reg.reset = RP5C15::RESET_1Hz | RP5C15::RESET_16Hz;
1.1 root 52:
1.1.1.12 root 53: return true;
1.1 root 54: }
55:
1.1.1.14 root 56: busdata
1.1.1.15 root 57: RP5C15Device::ReadPort(uint32 offset)
1.1.1.2 root 58: {
1.1.1.14 root 59: busdata data;
1.1.1.2 root 60:
1.1.1.3 root 61: // バンクを展開する
1.1.1.15 root 62: uint bank = (reg.mode & RP5C15::MODE_BANK);
63: uint n = bank * 0x10 + offset;
1.1 root 64:
65: // XXX 14, 15 は write only らしいが何が読めるか
66:
67: switch (n) {
68: case 0x1d: // MODE (Bank0 と同じ)
69: case 0x1e: // TEST (Bank0 と同じ)
70: case 0x1f: // RESET (Bank0 と同じ)
71: n -= 0x10;
72: FALLTHROUGH;
73:
74: case 0x00 ... 0x1c:
75: data = reg.r[n];
76: break;
77: default:
1.1.1.12 root 78: VMPANIC("corrupted n=$%x", n);
1.1 root 79: }
80:
81: // 下位4ビット(RP5C15) の中の未実装ビットは %0 だが
82: // 上位4ビットは %1 になる。
83: data |= 0xf0;
1.1.1.15 root 84: putlog(2, "Bank%u $%x -> $%02x", bank, offset, data.Data());
1.1.1.14 root 85:
86: // InsideOut p.135
87: const busdata read_wait = busdata::Wait(29 * 40_nsec);
88: data |= read_wait;
1.1 root 89:
1.1.1.15 root 90: data |= BusData::Size1;
1.1 root 91: return data;
92: }
93:
1.1.1.14 root 94: busdata
1.1.1.15 root 95: RP5C15Device::WritePort(uint32 offset, uint32 data)
1.1 root 96: {
1.1.1.3 root 97: // バンクを展開する
1.1.1.15 root 98: uint bank = (reg.mode & RP5C15::MODE_BANK);
99: uint n = bank * 0x10 + offset;
1.1 root 100:
1.1.1.15 root 101: putlog(2, "Bank%u $%x <- $%x", bank, offset, (data & 0x0f));
1.1 root 102:
103: switch (n) {
104: case RP5C15::SEC1:
105: case RP5C15::SEC10:
106: case RP5C15::MIN1:
107: case RP5C15::MIN10:
1.1.1.9 root 108: case RP5C15::HOUR1:
109: case RP5C15::HOUR10:
110: case RP5C15::WDAY: // 曜日カウンタは日付などとは一切連動していない
1.1 root 111: case RP5C15::DAY1:
112: case RP5C15::DAY10:
113: case RP5C15::MON1:
114: case RP5C15::MON10:
115: case RP5C15::YEAR1:
116: case RP5C15::YEAR10:
1.1.1.9 root 117: case RP5C15::SEL24:
118: case RP5C15::LEAP:
119: reg.r[n] = data & reg.mask[n];
1.1 root 120: break;
121:
122: case RP5C15::CLKSEL:
1.1.1.9 root 123: reg.clksel = data & reg.mask[n];
124: switch (reg.clksel) {
125: case 0: // ハイインピーダンス
126: case 1: // 16384Hz
127: case 2: // 1024Hz
128: case 3: // 128Hz
129: // 128Hz 点滅とか点灯みたいなもんだろ(適当)
130: clkout = true;
131: break;
132:
133: // (?) たぶん内部ではずっとカウンタ部分は動いていて
134: // 出力位置を切り替えているだけのはずなので、
135: // CLKSEL を変えた時の初期状態は内部カウンタの状態に依存するはず。
136: case 4: // 16Hz
137: clkout = ((cnt & 1) == 0);
138: break;
139: case 5: // 1Hz
140: clkout = ((cnt & 16) == 0);
141: break;
142: case 6: // 1/60Hz
143: clkout = (cnt_60sec < 30);
144: break;
145: case 7:
146: clkout = false;
147: break;
148: }
149: break;
150:
151: // アラーム関係は未実装
152: case RP5C15::ALARM_MIN1 ... RP5C15::ALARM_DAY10:
153: case RP5C15::notused1:
154: case RP5C15::notused2:
1.1 root 155: // マスクして書き込む
156: reg.r[n] = data & reg.mask[n];
157: break;
158:
159: case RP5C15::ADJUST:
160: {
161: // %0 なら何もしない?
162: if ((data & 1) == 0) {
163: break;
164: }
165: // %1 ならアジャスト。
166:
167: // 現在の秒が 0..29秒なら 0秒に切り下げ。
168: // 30..59秒なら +1分0秒に繰り上げ。
169: // いずれにしても 1秒と10秒の位は 0 にする。
1.1.1.9 root 170: // XXX 秒以下のカウンタもクリアするのでは?
171: if (GetSec() >= 30) {
1.1 root 172: // 1分繰り上げ
1.1.1.9 root 173: CountUpMin();
1.1 root 174: }
1.1.1.9 root 175: // マニュアルを読む限り秒以下をクリア(?)
176: reg.sec1 = 0;
177: reg.sec10 = 0;
178: cnt = 0;
179: clock_hold = false;
1.1 root 180: break;
181: }
182:
183: case RP5C15::MODE:
184: case RP5C15::MODE_BANK1:
185: reg.mode = data & reg.mask[RP5C15::MODE];
1.1.1.9 root 186:
187: if ((reg.mode & RP5C15::MODE_TIMER_EN)) {
188: if (clock_hold) {
189: // ホールドされていた秒カウントアップを実行
190: CountUpSec();
191: clock_hold = false;
192: }
193: }
1.1 root 194: break;
195:
196: case RP5C15::TEST:
197: case RP5C15::TEST_BANK1:
198: data &= reg.mask[RP5C15::TEST];
199: if (data == 0) {
200: // 通常 0 を書き込むこと、となっているので 0 は許可
201: } else {
1.1.1.11 root 202: putlog(0, "TEST <- $%1x (NOT IMPLEMENTED)", data);
1.1 root 203: }
204: reg.test = data;
205: break;
206:
207: case RP5C15::RESET:
208: case RP5C15::RESET_BANK1:
209: reg.reset = data & reg.mask[RP5C15::RESET];
210: if ((reg.reset & RP5C15::RESET_TIMER)) {
211: // %1 を書き込むと、秒未満の内部カウンタをリセット
1.1.1.9 root 212: // 繰り上がり分の保持も消す。
213: cnt = 0;
214: clock_hold = false;
215: }
216: if ((reg.reset & RP5C15::RESET_16Hz)) {
217: alarm_16Hz = false;
218: }
219: if ((reg.reset & RP5C15::RESET_1Hz)) {
220: alarm_1Hz = false;
1.1 root 221: }
1.1.1.11 root 222: ChangeAlarmOut();
1.1 root 223: break;
224:
225: default:
226: break;
227: }
1.1.1.14 root 228:
229: // InsideOut p.135
230: const busdata write_wait = busdata::Wait(38 * 40_nsec);
231:
1.1.1.15 root 232: busdata r = write_wait;
233: r |= BusData::Size1;
234: return r;
1.1 root 235: }
236:
1.1.1.14 root 237: busdata
1.1.1.15 root 238: RP5C15Device::PeekPort(uint32 offset)
1.1 root 239: {
240: int n;
241:
1.1.1.3 root 242: // バンクを展開する
1.1.1.9 root 243: int bank = (reg.mode & RP5C15::MODE_BANK);
1.1.1.6 root 244: n = bank * 0x10 + offset;
1.1 root 245:
246: switch (n) {
247: case RP5C15::MODE_BANK1:
248: case RP5C15::TEST_BANK1:
249: case RP5C15::RESET_BANK1:
250: // Bank0 と同じものが見える
251: n -= 0x10;
252: FALLTHROUGH;
253:
254: case 0x00 ... 0x1c:
255: return reg.r[n] | 0xf0;
256:
257: default:
1.1.1.12 root 258: return 0xff;
1.1 root 259: }
260: }
261:
1.1.1.15 root 262: bool
263: RP5C15Device::PokePort(uint32 offset, uint32 data)
264: {
265: return false;
266: }
267:
1.1.1.4 root 268: void
1.1.1.8 root 269: RP5C15Device::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 270: {
1.1.1.9 root 271: int x = 18;
272: int y = 0;
1.1 root 273:
1.1.1.8 root 274: screen.Clear();
1.1 root 275:
1.1.1.15 root 276: uint year = reg.year10 * 10 + reg.year1 + year_epoch;
277: uint hour10;
1.1.1.9 root 278: const char *ampm;
1.1 root 279: if (reg.sel24) {
1.1.1.9 root 280: ampm = "";
281: hour10 = reg.hour10;
1.1 root 282: } else {
1.1.1.9 root 283: ampm = (reg.hour10 & 2) == 0 ? " AM": " PM";
284: hour10 = reg.hour10 & 1;
1.1 root 285: }
1.1.1.15 root 286: screen.Print(0, y++, "%04u[%x%x]/%x%x/%x%x(%s) %x%x:%x%x:%x%x.%02x%s",
1.1.1.9 root 287: year, reg.year10, reg.year1,
288: reg.mon10, reg.mon1, reg.day10, reg.day1, wdays[reg.wday],
289: hour10, reg.hour1, reg.min10, reg.min1, reg.sec10, reg.sec1,
290: ((uint32)cnt & 0x1f), ampm);
291:
292: screen.Print(0, y, "TimeZone: %s", (use_localtime) ? "Local" : "UTC");
1.1.1.15 root 293: screen.Print(x, y, "Epoch: %4u", year_epoch);
1.1.1.9 root 294:
295: // バンク0
296: y = 3;
297: screen.Print(0, y++, "Bank0");
298: static const char * const b0names[] = {
299: " 1sec",
300: "10sec",
301: " 1min",
302: "10min",
303: " 1hour",
304: "10hour",
305: "wday",
306: " 1day",
307: "10day",
308: " 1mon",
309: "10mon",
310: " 1year",
311: "10year",
312: };
313: for (int i = 0; i < countof(b0names); i++) {
314: screen.Print(0, y, "$%x(%s)", i, b0names[i]);
315: screen.Print(10, y, ": %x", reg.r[i]);
316: y++;
317: }
318:
319: // バンク1
320: y = 3;
321: screen.Print(x, y++, "Bank1");
322: static const char * const b1names[] = {
323: "CLKSEL",
324: "ADJ",
325: "AL 1m",
326: "AL10m",
327: "AL 1h",
328: "AL10h",
329: "ALwday",
330: "AL 1d",
331: "AL10d",
332: "",
333: "12/24H",
334: "LEAP",
335: "",
336: };
337: for (int i = 0; i < countof(b1names); i++) {
338: if (b1names[i][0] != '\0') {
339: screen.Print(x, y, "$%x(%s)", i, b1names[i]);
340: screen.Print(x + 10, y, ": %x", reg.r[16 + i]);
341: } else {
342: // $9 と $c は欠番
343: screen.Print(x, y, "$%x - ", i);
344: }
345: y++;
346: }
347:
348: // CLKSEL
349: static const char * const clksel_s[] = {
350: "HiZ",
351: "16384Hz",
352: "1024Hz",
353: "128Hz",
354: "16Hz",
355: "1Hz",
356: "1/60Hz",
357: "L",
358: };
359: screen.Print(x + 14, 4, "(%s)", clksel_s[reg.clksel]);
360:
361: // 12/24H
1.1.1.15 root 362: screen.Print(x + 14, 14, "(%uH)", reg.sel24 ? 24 : 12);
1.1.1.9 root 363:
364: // MODE
365: y = 17;
1.1.1.14 root 366: screen.Print(0, y, "$d(MODE): %x", reg.mode);
367: screen.Puts(14, y, TA::OnOff(reg.mode & RP5C15::MODE_TIMER_EN), "TIMER_EN");
368: screen.Puts(23, y, TA::OnOff(reg.mode & RP5C15::MODE_ALARM_EN), "ALARM_EN");
1.1.1.15 root 369: screen.Print(32, y, "BANK=%u", reg.mode & RP5C15::MODE_BANK);
1.1.1.9 root 370:
371: // TEST
1.1 root 372: y++;
1.1.1.14 root 373: screen.Print(0, y, "$e(TEST): %x", reg.test);
1.1 root 374:
1.1.1.9 root 375: // RESET
1.1 root 376: y++;
1.1.1.14 root 377: screen.Print(0, y, "$f(RESET): %x", reg.reset);
378: screen.Print(14, y, TA::OnOff(reg.reset & RP5C15::RESET_1Hz), "!1Hz");
379: screen.Print(19, y, TA::OnOff(reg.reset & RP5C15::RESET_16Hz), "!16Hz");
380: screen.Puts(25, y, TA::OnOff(reg.reset & RP5C15::RESET_TIMER), "TIMER");
381: screen.Puts(31, y, TA::OnOff(reg.reset & RP5C15::RESET_ALARM), "ALARM");
1.1 root 382:
1.1.1.9 root 383: // PIN
1.1 root 384: y++;
1.1.1.9 root 385: y++;
386: // ハードウェアの ~ALARM ピンは負論理なのでアクティブ L だが、
387: // ここでは分かりやすさ優先で正論理としている。
388: screen.Print(0, y, "Pin: CLKOUT=%c ALARM=%c",
389: clkout ? 'H' : 'L',
1.1.1.11 root 390: alarm_out ? 'H' : 'L');
1.1.1.9 root 391: }
1.1 root 392:
1.1.1.9 root 393: // 秒を取得
394: uint
395: RP5C15Device::GetSec() const
396: {
397: return reg.sec10 * 10 + reg.sec1;
1.1 root 398: }
399:
1.1.1.9 root 400: // 秒を設定
401: void
402: RP5C15Device::SetSec(uint v)
403: {
404: reg.sec1 = v % 10;
405: reg.sec10 = v / 10;
406: }
1.1 root 407:
1.1.1.9 root 408: // 分を取得
409: uint
410: RP5C15Device::GetMin() const
411: {
412: return reg.min10 * 10 + reg.min1;
413: }
414:
415: // 分を設定
416: void
417: RP5C15Device::SetMin(uint v)
418: {
419: reg.min1 = v % 10;
420: reg.min10 = v / 10;
421: }
422:
423: // 時を取得
424: uint
425: RP5C15Device::GetHour() const
426: {
427: if (reg.sel24) {
428: return reg.hour10 * 10 + reg.hour1;
429: } else {
1.1.1.15 root 430: uint h10;
1.1.1.9 root 431: h10 = (reg.hour10 & 0x1) * 10 + ((reg.hour10 & 0x2) >> 1) * 12;
432: return h10 + reg.hour1;
433: }
434: }
1.1 root 435:
1.1.1.9 root 436: // 時を設定
437: void
438: RP5C15Device::SetHour(uint v)
439: {
440: if (reg.sel24) {
441: reg.hour1 = v % 10;
442: reg.hour10 = v / 10;
443: } else {
1.1.1.15 root 444: uint pm = (v < 12) ? 0x0 : 0x2;
1.1.1.9 root 445: v %= 12;
446: reg.hour1 = v % 10;
447: reg.hour10 = v / 10 + pm;
448: }
449: }
450:
451: // 曜日を取得
452: uint
453: RP5C15Device::GetWday() const
454: {
455: return reg.wday;
456: }
457:
458: // 曜日を設定
459: void
460: RP5C15Device::SetWday(uint v)
461: {
462: reg.wday = v;
463: }
464:
465: // 日を取得
466: uint
467: RP5C15Device::GetMday() const
468: {
469: return reg.day10 * 10 + reg.day1;
470: }
471:
472: // 日を設定
473: void
474: RP5C15Device::SetMday(uint v)
475: {
476: reg.day1 = v % 10;
477: reg.day10 = v / 10;
478: }
479:
480: // 月を取得
481: uint
482: RP5C15Device::GetMon() const
483: {
484: return reg.mon10 * 10 + reg.mon1;
485: }
486:
487: // 月を設定
488: void
489: RP5C15Device::SetMon(uint v)
490: {
491: reg.mon1 = v % 10;
492: reg.mon10 = v / 10;
493: }
494:
495: // 年を取得
496: uint
497: RP5C15Device::GetYear() const
498: {
499: return reg.year10 * 10 + reg.year1 + year_epoch;
500: }
501:
502: // 年を設定
503: void
504: RP5C15Device::SetYear(uint v)
505: {
506: v -= year_epoch;
507: reg.year1 = v % 10;
508: reg.year10 = v / 10;
509: }
510:
511: uint
512: RP5C15Device::GetLeap() const
513: {
514: return reg.leap;
515: }
516:
517: void
518: RP5C15Device::SetLeap(uint v)
519: {
520: reg.leap = v & 3;
521: }
522:
1.1.1.11 root 523: // ALARM OUT 信号
524: void
525: RP5C15Device::ChangeAlarmOut()
526: {
527: bool old = alarm_out;
528:
529: // アラーム出力は内部のアラーム用 16Hz, 1Hz と
530: // アラーム時刻一致の alarm_on との OR 論理となっている。
531: alarm_out = alarm_16Hz | alarm_1Hz | alarm_on;
532:
533: if (old != alarm_out) {
1.1.1.12 root 534: power->SetAlarmOut(alarm_out);
535: mfp->SetAlarmOut(alarm_out);
1.1.1.11 root 536: }
537: }
538:
1.1.1.9 root 539: // 32Hz 仮想 RTC からのコール
540: void
541: RP5C15Device::Tick32Hz()
542: {
543: if (reg.clksel == 4) {
1.1.1.10 root 544: // CLKOUT 16Hz は duty50% なのでオン/オフ駆動は 32Hz になる
1.1.1.9 root 545: clkout = ((cnt & 1) == 0);
546: }
547:
548: // ALARM 端子の 16Hz パルスも CLKOUT に同期するとは書いてないけど、
549: // 面倒なのでとりあえず同じところで処理してしまう。
550: // データシートの書き方的には同じ波形になると思われる。
551:
552: // アラーム端子 16Hz パルス出力は %0 ならオン
553: if ((reg.reset & RP5C15::RESET_16Hz) == 0) {
554: alarm_16Hz = !alarm_16Hz;
1.1.1.11 root 555: ChangeAlarmOut();
1.1.1.9 root 556: }
557: }
558:
559: // 2Hz 仮想 RTC からのコール
560: // 分周段 0/32, 16/32 秒でコールされる。
561: void
562: RP5C15Device::Tick2Hz()
563: {
564: if (reg.clksel == 5) {
1.1.1.10 root 565: // CLKOUT 1Hz は duty50% なのでオン/オフ駆動は 2Hz になる。
1.1 root 566: // CLKOUT 端子は秒の繰り上がりに同期している。
567: // TIMER_EN (秒への繰り上がり) が無効でも反転自体は起きる気が
568: // するので、ここでは UPDATE_SEC ではなく cnt で判定する。
1.1.1.10 root 569: // CLKOUT は 1秒の繰り上がりが起きる時に 'H' になる。
1.1 root 570:
1.1.1.9 root 571: clkout = ((cnt & 15) == 0);
572: }
1.1 root 573:
1.1.1.9 root 574: // ALARM 端子の 1Hz パルスも CLKOUT に同期するとは書いてないけど、
575: // 面倒なのでとりあえず同じところで処理してしまう。
576: // データシートの書き方的には同じ波形になると思われる。
1.1 root 577:
1.1.1.9 root 578: // アラーム端子 16Hz パルス出力は %0 ならオン
579: if ((reg.reset & RP5C15::RESET_1Hz) == 0) {
580: alarm_1Hz = !alarm_1Hz;
1.1.1.11 root 581: ChangeAlarmOut();
1.1 root 582: }
583: }
584:
1.1.1.9 root 585: // 1Hz 仮想 RTC からのコール
586: // 分周段 0/32 秒でコールされる。
1.1 root 587: void
1.1.1.9 root 588: RP5C15Device::Tick1Hz()
1.1 root 589: {
1.1.1.9 root 590: if ((reg.mode & RP5C15::MODE_TIMER_EN)) {
591: // 秒カウンタ更新
592: CountUpSec();
593: } else {
594: // 秒カウンタは更新させない。内部のクロックホールドをアサート。
595: clock_hold = true;
596: }
597:
598: // CLKOUT 1/60 Hz については
599: // 「秒のカウントアップのタイミングでアップエッジ」
600: // としか書かれておらず、00 or 30 秒で動作するとは書かれていない。
601: // データシートの図からはカレンダとは独立しているように読める。
602:
1.1.1.10 root 603: // 1/60Hz CLKOUT のための 60sec カウンタ。
1.1.1.9 root 604: // このカウンタは clksel によらず常にカウントする。
605: cnt_60sec++;
606: if (cnt_60sec == 60) {
607: cnt_60sec = 0;
1.1 root 608: }
609:
1.1.1.9 root 610: if (reg.clksel == 6) {
1.1.1.10 root 611: // CLKOUT 1/60Hz は duty50% なのでオン/オフ駆動は 30sec になる
1.1.1.9 root 612: clkout = (cnt_60sec < 30);
1.1 root 613: }
614: }
615:
616: /*static*/ const uint8
617: RP5C15::mask[] = {
618: 0x0f, 0x07, 0x0f, 0x07, // 1秒 10秒 1分 10分
619: 0x0f, 0x03, 0x07, 0x0f, // 1時 10時 曜日 1日
620: 0x01, 0x0f, 0x01, 0x0f, // 10日 1月 10月 1年
621: 0x0f, 0x0d, 0x0f, 0x0f, // 10年 MODE TEST RESET
622:
623: 0x07, 0x01, 0x0f, 0x07, // CLKO Adjust AL1分 AL10分
624: 0x0f, 0x03, 0x07, 0x0f, // AL1時 AL10時 AL曜日 AL1日
625: 0x03, 0x00, 0x01, 0x03, // AL10日 --- 12/24 閏年
626: 0x00 // ---
627: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.