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

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

unix.superglobalmegacorp.com

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