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

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

unix.superglobalmegacorp.com

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