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

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

unix.superglobalmegacorp.com

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