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