Annotation of nono/vm/mk48t02.cpp, revision 1.1.1.2

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2018 [email protected]
                      4: //
                      5: 
                      6: #include "mk48t02.h"
                      7: #include "configfile.h"
                      8: #include <sys/fcntl.h>
                      9: #include <sys/mman.h>
1.1.1.2 ! root       10: #include <sys/stat.h>
1.1       root       11: 
                     12: //
                     13: // MK48T02
                     14: //
                     15: // SRAM 部分はビッグエンディアンのままメモリに置かれるので、
                     16: // 必要ならアクセス時に都度変換すること。
                     17: //
                     18: // 仕様は 0x45000000 から 2KB だが、
                     19: // 0x44000000 から 0x48000000 の手前まで 2KB 単位でミラーが見える。
                     20: 
                     21: MK48T02Device::MK48T02Device()
                     22: {
                     23:        logname = "mk48t02";
                     24:        devname = "MK48T02";
                     25:        devaddr = 0x45000000;
                     26:        monitor.Init(25, 2);
                     27: 
                     28:        fd = -1;
                     29: }
                     30: 
                     31: MK48T02Device::~MK48T02Device()
                     32: {
                     33:        if (mem) {
                     34:                munmap(mem, memlen);
                     35:                mem = NULL;
                     36:        }
                     37:        if (fd >= 0) {
                     38:                close(fd);
                     39:                fd = -1;
                     40:        }
                     41: }
                     42: 
                     43: bool
                     44: MK48T02Device::Init()
                     45: {
1.1.1.2 ! root       46:        struct stat st;
        !            47: 
1.1       root       48:        // ファイルのロード
                     49:        filename = gConfig->GetConfigDir() + "NVRAM.DAT";
1.1.1.2 ! root       50:        memlen = 2040;
1.1       root       51: 
                     52:        fd = open(filename.c_str(), O_RDWR);
                     53:        if (fd == -1) {
1.1.1.2 ! root       54:                warn("NVRAM \"%s\" open failed", filename.c_str());
1.1       root       55:                return false;
                     56:        }
                     57: 
1.1.1.2 ! root       58:        // ファイルサイズをチェック。
        !            59:        // ファイルが短くても mmap はしれっと成功してしまうようだ。
        !            60:        if (fstat(fd, &st) == -1) {
        !            61:                warn("NVRAM \"%s\" fstat failed", filename.c_str());
        !            62:                close(fd);
        !            63:                return false;
        !            64:        }
        !            65:        if (st.st_size < memlen) {
        !            66:                warnx("NVRAM \"%s\" is too short (filesize is expected %d but %d)",
        !            67:                        filename.c_str(), memlen, (int)st.st_size);
        !            68:                close(fd);
        !            69:                return false;
        !            70:        }
        !            71:        if (st.st_size > memlen) {
        !            72:                // 大きすぎるほうはワーニングだけでいいか
        !            73:                warnx("NVRAM \"%s\" is too large (ignore %d bytes)",
        !            74:                        filename.c_str(), (int)st.st_size - memlen);
        !            75:        }
        !            76: 
1.1       root       77:        mem = (uint8 *)mmap(NULL, memlen, PROT_READ | PROT_WRITE,
                     78:                MAP_SHARED, fd, 0);
                     79:        if (mem == MAP_FAILED) {
1.1.1.2 ! root       80:                warn("NVRAM \"%s\" mmap failed", filename.c_str());
1.1       root       81:                close(fd);
                     82:                return false;
                     83:        }
                     84: 
                     85:        // レジスタ値固定オプション (パフォーマンス測定用)
                     86:        force_fixed = gConfig->ReadInt("rtc_force_fixed");
                     87:        if (force_fixed) {
                     88:                putmsg("force_fixed");
                     89:        }
                     90:        return true;
                     91: }
                     92: 
                     93: // RTC を初期化
                     94: bool
                     95: MK48T02Device::InitRTC()
                     96: {
                     97:        // ホストの時刻を UTC で取得
                     98:        LoadHostRTC(false);
                     99: 
                    100:        return true;
                    101: }
                    102: 
                    103: // バイナリ表現を BCD 表現に変換する
                    104: /*static*/ inline uint8
                    105: MK48T02Device::num2BCD(uint8 num)
                    106: {
                    107:        return ((num / 10) << 4) + (num % 10);
                    108: }
                    109: 
                    110: // BCD 表現をバイナリ表現に変換する
                    111: /*static*/ inline uint8
                    112: MK48T02Device::BCD2num(uint8 bcd)
                    113: {
                    114:        return (bcd >> 4) * 10 + (bcd & 0x0f);
                    115: }
                    116: 
                    117: uint64
                    118: MK48T02Device::Read8(uint32 addr)
                    119: {
                    120:        // アドレスは 2KB 単位でミラーになっている。
                    121:        uint32 an = addr & 0x7ff;
                    122:        uint32 data;
                    123: 
                    124:        if (an < 2040) {
                    125:                data = mem[an];
                    126:                putlog(3, "$%08x -> $%02x", addr, data);
                    127:        } else {
                    128:                // XXX ReadBit が立っていない時は何が読めるのか
                    129:                switch (an - 2040) {
                    130:                 case 0:        // コントロールレジスタ
                    131:                        data = reg.ctrl;
                    132:                        break;
                    133:                 case 1:        // 秒
                    134:                        data = reg.sec;
                    135:                        if (reg.stop) {
                    136:                                data |= MK48T02Clock::STOP;
                    137:                        }
                    138:                        break;
                    139:                 case 2:        // 分
                    140:                        data = reg.min;
                    141:                        break;
                    142:                 case 3:        // 時
                    143:                        data = reg.hour;
                    144:                        if (reg.kickstart) {
                    145:                                data |= MK48T02Clock::KICKSTART;
                    146:                        }
                    147:                        break;
                    148:                 case 4:        // 曜日
                    149:                        data = reg.wday;
                    150:                        break;
                    151:                 case 5:        // 日
                    152:                        data = reg.mday;
                    153:                        break;
                    154:                 case 6:        // 月
                    155:                        data = reg.mon;
                    156:                        break;
                    157:                 case 7:        // 年
                    158:                        data = reg.year;
                    159:                        break;
                    160:                 default:
                    161:                        putlog(0, "未実装バイト読み込み $%08x", addr);
                    162:                        data = 0xff;
                    163:                        break;
                    164:                }
                    165: 
                    166:                if (force_fixed) {
                    167:                        // パフォーマンス測定をする際に、RTC が実時間を返すと
                    168:                        // ゲストの状態が毎回異なることになるため、
                    169:                        // いつ何時も同じ値を返すことで、世界から隔絶された
                    170:                        // VM を作り、パフォーマンス測定を安定させる。
                    171:                        data = 3;
                    172:                }
                    173:        }
                    174:        return data;
                    175: }
                    176: 
                    177: uint64
                    178: MK48T02Device::Read16(uint32 addr)
                    179: {
                    180:        // アドレスは 2KB 単位でミラーになっている。
                    181:        uint32 an = addr & 0x7ff;
                    182:        uint32 data;
                    183: 
                    184:        if (an < 2040) {
                    185:                data = be16toh(*(uint16 *)&mem[an]);
                    186:                putlog(3, "$%08x -> $%04x", addr, data);
                    187:        } else {
                    188:                putlog(0, "未実装ワード読み込み $%08x", addr);
                    189:                data = 0xffff;
                    190:        }
                    191:        return data;
                    192: }
                    193: 
                    194: uint64
                    195: MK48T02Device::Read32(uint32 addr)
                    196: {
                    197:        // アドレスは 2KB 単位でミラーになっている。
                    198:        uint32 an = addr & 0x7ff;
                    199:        uint32 data;
                    200: 
                    201:        if (an < 2040) {
                    202:                data = be32toh(*(uint32 *)&mem[an]);
                    203:                putlog(3, "$%08x -> $%08x", addr, data);
                    204:        } else {
                    205:                putlog(0, "未実装ロング読み込み $%08x", addr);
                    206:                data = 0xffffffff;
                    207:        }
                    208:        return data;
                    209: }
                    210: 
                    211: uint64
                    212: MK48T02Device::Write8(uint32 addr, uint32 data)
                    213: {
                    214:        // アドレスは 2KB 単位でミラーになっている。
                    215:        uint32 an = addr & 0x7ff;
                    216: 
                    217:        if (an < 2040) {
                    218:                putlog(3, "$%08x <- $%02x", addr, data);
                    219:                mem[an] = data;
                    220:        } else {
                    221:                switch (an - 2040) {
                    222:                 case 0:        // コントロール
                    223:                        WriteCtrl(data);
                    224:                        break;
                    225:                 case 1:        // 秒 (b7 は STOP BIT)
                    226:                        reg.stop = (data & MK48T02Clock::STOP);
                    227:                        if ((reg.ctrl & MK48T02Clock::WRITE)) {
                    228:                                rtc.sec = BCD2num(data & 0x7f);
                    229:                        }
                    230:                        break;
                    231:                 case 2:        // 分
                    232:                        if ((reg.ctrl & MK48T02Clock::WRITE)) {
                    233:                                rtc.min = BCD2num(data & 0x7f);
                    234:                        }
                    235:                        break;
                    236:                 case 3:        // 時 (b7 は KICK START)
                    237:                        reg.kickstart = (data & MK48T02Clock::KICKSTART);
                    238:                        if ((reg.ctrl & MK48T02Clock::WRITE)) {
                    239:                                rtc.hour = BCD2num(data & 0x3f);
                    240:                        }
                    241:                        break;
                    242:                 case 4:        // 曜日 (b6 は Frequency Test (未実装))
                    243:                        if ((reg.ctrl & MK48T02Clock::WRITE)) {
                    244:                                rtc.wday = data & 0x07;
                    245:                        }
                    246:                        break;
                    247:                 case 5:        // 日
                    248:                        if ((reg.ctrl & MK48T02Clock::WRITE)) {
                    249:                                rtc.mday = BCD2num(data & 0x3f);
                    250:                        }
                    251:                        break;
                    252:                 case 6:        // 月
                    253:                        if ((reg.ctrl & MK48T02Clock::WRITE)) {
                    254:                                rtc.mon = BCD2num(data & 0x1f);
                    255:                        }
                    256:                        break;
                    257:                 case 7:        // 年
                    258:                        if ((reg.ctrl & MK48T02Clock::WRITE)) {
                    259:                                rtc.year = BCD2num(data) + MK48T02Clock::YEAR_EPOCH;
                    260:                        }
                    261:                        break;
                    262:                 default:
                    263:                        putlog(0, "未実装バイト書き込み $%08x <- %02x", addr, data);
                    264:                        break;
                    265:                }
                    266:        }
                    267:        return 0;
                    268: }
                    269: 
                    270: uint64
                    271: MK48T02Device::Write16(uint32 addr, uint32 data)
                    272: {
                    273:        // アドレスは 2KB 単位でミラーになっている。
                    274:        uint32 an = addr & 0x7ff;
                    275: 
                    276:        if (an < 2040) {
                    277:                putlog(3, "$%08x <- $%04x", addr, data);
                    278:                *(uint16 *)&mem[an] = htobe16(data);
                    279:        } else {
                    280:                putlog(0, "未実装ワード書き込み $%08x <- %04x", addr, data);
                    281:        }
                    282:        return 0;
                    283: }
                    284: 
                    285: uint64
                    286: MK48T02Device::Write32(uint32 addr, uint32 data)
                    287: {
                    288:        // アドレスは 2KB 単位でミラーになっている。
                    289:        uint32 an = addr & 0x7ff;
                    290: 
                    291:        if (an < 2040) {
                    292:                putlog(3, "$%08x <- $%08x", addr, data);
                    293:                *(uint32 *)&mem[an] = htobe32(data);
                    294:        } else {
                    295:                putlog(0, "未実装ロング書き込み $%08x <- %08x", addr, data);
                    296:        }
                    297:        return 0;
                    298: }
                    299: 
                    300: uint64
                    301: MK48T02Device::Peek8(uint32 addr)
                    302: {
                    303:        // アドレスは 2KB 単位でミラーになっている。
                    304:        uint32 an = addr & 0x7ff;
                    305:        uint8 data;
                    306: 
                    307:        if (an < 2040) {
                    308:                return mem[an];
                    309:        } else {
                    310:                switch (an - 2040) {
                    311:                 case 0:        // コントロールレジスタ
                    312:                        return reg.ctrl;
                    313:                 case 1:        // 秒 | STOP
                    314:                        data = reg.sec;
                    315:                        if (reg.stop) {
                    316:                                data |= MK48T02Clock::STOP;
                    317:                        }
                    318:                        return data;
                    319:                 case 2:        // 分
                    320:                        return reg.min;
                    321:                 case 3:        // 時
                    322:                        data = reg.hour;
                    323:                        if (reg.kickstart) {
                    324:                                data |= MK48T02Clock::KICKSTART;
                    325:                        }
                    326:                        return data;
                    327:                 case 4:        // 曜日
                    328:                        return reg.wday;
                    329:                 case 5:        // 日
                    330:                        return reg.mday;
                    331:                 case 6:        // 月
                    332:                        return reg.mon;
                    333:                 case 7:        // 年
                    334:                        return reg.year;
                    335:                 default:
                    336:                        return 0xff;
                    337:                }
                    338:        }
                    339: }
                    340: 
                    341: bool
                    342: MK48T02Device::MonitorUpdate()
                    343: {
                    344:        monitor.Clear();
                    345: 
                    346:        monitor.Print(0, 0, "WR:%d RD:%d",
                    347:                (reg.ctrl & MK48T02Clock::WRITE) ? 1 : 0,
                    348:                (reg.ctrl & MK48T02Clock::READ)  ? 1 : 0);
                    349:        monitor.Print(0, 1, "ST:%d KS:%d",
                    350:                reg.stop,
                    351:                reg.kickstart);
                    352:        // XXX レジスタの値はラッチされるまで意味がないので
                    353:        // どうしたものか。
                    354:        monitor.Print(10, 0, "%4d/%02d/%02d(%s)",
                    355:                rtc.year, rtc.mon, rtc.mday, wdays[rtc.wday]);
                    356:        monitor.Print(10, 1, "%02d:%02d:%02d",
                    357:                rtc.hour, rtc.min, rtc.sec);
                    358: 
                    359:        return true;
                    360: }
                    361: 
                    362: // コントロールレジスタへの書き込み
                    363: void
                    364: MK48T02Device::WriteCtrl(uint32 data)
                    365: {
                    366:        reg.ctrl = data;
                    367: 
                    368:        if ((reg.ctrl & MK48T02Clock::WRITE)) {
                    369:                // WRITE ビットを立てると計時を止める
                    370:                rtc.enable_sec = false;
                    371:        } else {
                    372:                // WRITE ビットを下ろすと計時を再開して
                    373:                rtc.enable_sec = true;
                    374:                // KickStart, FreqTest をゼロにする
                    375:                reg.kickstart = false;
                    376:        }
                    377: 
                    378:        if ((reg.ctrl & MK48T02Clock::READ)) {
                    379:                // READ ビットを立てたところで計時をとめて(?)
                    380:                // 値をレジスタにラッチ(?)
                    381:                rtc.enable_sec = false;
                    382:                reg.sec  = num2BCD(rtc.sec);
                    383:                reg.min  = num2BCD(rtc.min);
                    384:                reg.hour = num2BCD(rtc.hour);
                    385:                reg.wday = rtc.wday;
                    386:                reg.mday = num2BCD(rtc.mday);
                    387:                reg.mon  = num2BCD(rtc.mon);
                    388:                reg.year = num2BCD(rtc.year - MK48T02Clock::YEAR_EPOCH);
                    389:        } else {
                    390:                // READ ビットを下ろすと計時を再開
                    391:                rtc.enable_sec = true;
                    392:        }
                    393: }
                    394: 
                    395: // 32Hz クロック入力
                    396: //
                    397: // 32Hz ごとに呼ばれて、これが RTC の時計を進める処理をするところ。
                    398: // RTC を実時間に同期して進めるモードなら実時間を計るスケジューラから、
                    399: // RTC を仮想時間に同期して進めるモードなら VM スケジューラから呼ばれる。
                    400: void
                    401: MK48T02Device::ClockIn()
                    402: {
                    403:        // 32Hz カウンタを進める。
                    404:        // 内部表現とデバイスから読み出せる値が同じなので、更新処理は不要。
                    405:        CountUp();
                    406: }

unix.superglobalmegacorp.com

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