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