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

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

unix.superglobalmegacorp.com

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