Annotation of nono/vm/mk48t02.cpp, revision 1.1.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.