Annotation of nono/vm/rp5c15.cpp, revision 1.1.1.8

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.