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

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

unix.superglobalmegacorp.com

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