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

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

unix.superglobalmegacorp.com

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