--- nono/vm/mk48t02.cpp 2026/04/29 17:04:32 1.1.1.3 +++ nono/vm/mk48t02.cpp 2026/04/29 17:04:55 1.1.1.9 @@ -1,14 +1,13 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "mk48t02.h" -#include "mainapp.h" #include "config.h" -#include -#include -#include +#include "mainapp.h" +#include "mpu.h" // // MK48T02 @@ -20,101 +19,41 @@ // 0x44000000 から 0x48000000 の手前まで 2KB 単位でミラーが見える。 MK48T02Device::MK48T02Device() + : inherited("MK48T02") { - logname = "mk48t02"; - devname = "MK48T02"; + AddAlias("NVRAM"); + AddAlias("RTC"); + devaddr = 0x45000000; - monitor.Init(25, 2); - fd = -1; + monitor.func = (MonitorCallback_t)&MK48T02Device::MonitorUpdate; + monitor.SetSize(25, 2); + monitor.Regist(ID_MONITOR_MK48T02); } MK48T02Device::~MK48T02Device() { - if (mem) { - munmap(mem, memlen); - mem = NULL; - } - if (fd >= 0) { - close(fd); - fd = -1; - } } bool MK48T02Device::Init() { - struct stat st; - // ファイルのロード filename = gMainApp.GetVMDir() + "NVRAM.DAT"; - memlen = 2040; - - fd = open(filename.c_str(), O_RDWR); - if (fd == -1) { - if (errno != ENOENT) { - warn("NVRAM \"%s\" open failed", filename.c_str()); - return false; - } else { - // オープンできない理由が ENOENT ならファイルを作ってみる - fd = open(filename.c_str(), O_RDWR | O_CREAT, 0644); - if (fd < 0) { - warn("NVRAM \"%s\" create failed", filename.c_str()); - return false; - } - - // ゼロで埋める (可変長配列は一様初期化できない(環境がある)ようだ) - char zerobuf[memlen]; - memset(zerobuf, 0, memlen); - if (write(fd, zerobuf, sizeof(zerobuf)) < 0) { - warn("NVRAM \"%s\" write failed", filename.c_str()); - close(fd); - return false; - } - warnx("NVRAM \"%s\" created", filename.c_str()); - // FALLTHROUGH - } - } - - // ファイルサイズをチェック。 - // ファイルが短くても mmap はしれっと成功してしまうようだ。 - if (fstat(fd, &st) == -1) { - warn("NVRAM \"%s\" fstat failed", filename.c_str()); - close(fd); - return false; - } - if (st.st_size < memlen) { - warnx("NVRAM \"%s\" is too short (filesize is expected %d but %d)", - filename.c_str(), memlen, (int)st.st_size); - close(fd); - return false; - } - if (st.st_size > memlen) { - // 大きすぎるほうはワーニングだけでいいか - warnx("NVRAM \"%s\" is too large (ignore %d bytes)", - filename.c_str(), (int)st.st_size - memlen); - } - - mem = (uint8 *)mmap(NULL, memlen, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - if (mem == MAP_FAILED) { - warn("NVRAM \"%s\" mmap failed", filename.c_str()); - close(fd); + file.SetFilename(filename); + file.SetDispname(string_format("NVRAM \"%s\"", filename.c_str())); + mem = file.OpenCreate(2040); + if (mem == NULL) { return false; } // レジスタ値固定オプション (パフォーマンス測定用) - force_fixed = gConfig->Get(".rtc-force-fixed").AsInt(); + force_fixed = gConfig->Find(".rtc-force-fixed").AsInt(); if (force_fixed) { - putmsg("force_fixed"); + putmsgn("force_fixed"); } - return true; -} -// RTC を初期化 -bool -MK48T02Device::InitRTC() -{ + // RTC を初期化 // ホストの時刻を UTC で取得 LoadHostRTC(false); @@ -136,17 +75,16 @@ MK48T02Device::BCD2num(uint8 bcd) } uint64 -MK48T02Device::Read(uint32 addr) +MK48T02Device::Read(uint32 offset) { - uint32 an = addr; uint32 data; - if (an < 2040) { - data = mem[an]; - putlog(3, "$%08x -> $%02x", addr, data); + if (offset < 2040) { + data = mem[offset]; + putlog(3, "$%08x -> $%02x", gMPU->GetPaddr(), data); } else { // XXX ReadBit が立っていない時は何が読めるのか - switch (an - 2040) { + switch (offset - 2040) { case 0: // コントロールレジスタ data = reg.ctrl; break; @@ -188,20 +126,20 @@ MK48T02Device::Read(uint32 addr) // VM を作り、パフォーマンス測定を安定させる。 data = 3; } + putlog(3, "$%08x -> $%02x", gMPU->GetPaddr(), data); } return data; } uint64 -MK48T02Device::Write(uint32 addr, uint32 data) +MK48T02Device::Write(uint32 offset, uint32 data) { - uint32 an = addr; - - if (an < 2040) { - putlog(3, "$%08x <- $%02x", addr, data); - mem[an] = data; + if (offset < 2040) { + putlog(3, "$%08x <- $%02x", gMPU->GetPaddr(), data); + mem[offset] = data; } else { - switch (an - 2040) { + putlog(3, "$%08x <- $%02x", gMPU->GetPaddr(), data); + switch (offset - 2040) { case 0: // コントロール WriteCtrl(data); break; @@ -250,15 +188,14 @@ MK48T02Device::Write(uint32 addr, uint32 } uint64 -MK48T02Device::Peek(uint32 addr) +MK48T02Device::Peek(uint32 offset) { - uint32 an = addr; uint8 data; - if (an < 2040) { - return mem[an]; + if (offset < 2040) { + return mem[offset]; } else { - switch (an - 2040) { + switch (offset - 2040) { case 0: // コントロールレジスタ return reg.ctrl; case 1: // 秒 | STOP @@ -289,25 +226,23 @@ MK48T02Device::Peek(uint32 addr) } } -bool -MK48T02Device::MonitorUpdate() +void +MK48T02Device::MonitorUpdate(Monitor *, TextScreen& screen) { - monitor.Clear(); + screen.Clear(); - monitor.Print(0, 0, "WR:%d RD:%d", + screen.Print(0, 0, "WR:%d RD:%d", (reg.ctrl & MK48T02Clock::WRITE) ? 1 : 0, (reg.ctrl & MK48T02Clock::READ) ? 1 : 0); - monitor.Print(0, 1, "ST:%d KS:%d", + screen.Print(0, 1, "ST:%d KS:%d", reg.stop, reg.kickstart); // XXX レジスタの値はラッチされるまで意味がないので // どうしたものか。 - monitor.Print(10, 0, "%4d/%02d/%02d(%s)", + screen.Print(10, 0, "%4d/%02d/%02d(%s)", rtc.year, rtc.mon, rtc.mday, wdays[rtc.wday]); - monitor.Print(10, 1, "%02d:%02d:%02d", + screen.Print(10, 1, "%02d:%02d:%02d", rtc.hour, rtc.min, rtc.sec); - - return true; } // コントロールレジスタへの書き込み @@ -363,9 +298,9 @@ MK48T02Device::PeekString(uint32 addr) c { std::string rv = ""; - uint32 an = addr & 0x7ff; - for (; mem[an]; an++) { - rv += mem[an]; + addr &= 0x7ff; + for (; mem[addr]; addr++) { + rv += mem[addr]; } return rv; } @@ -379,17 +314,31 @@ MK48T02Device::WriteString(uint32 addr, { int i = 0; - // XXX ここの文字列はゼロ終端なのかどうか - if (data.length() == 0 || data.length() > 15) { + // ここの文字列は16バイト未満なら一見ゼロ終端のようだが、 + // 16バイトならゼロ終端せず16文字とするフォーマット。 + + if (data.length() > 16) { return false; } addr &= 0x7ff; for (; i < data.length(); i++) { - Write(addr + i, data[i]); + mem[addr + i] = data[i]; } for (; i < 16; i++) { - Write(addr + i, 0); + mem[addr + i] = 0; } return true; } + +// 全域をゼロクリアする。 +// クリアするだけなので、その後何か書いたりチェックサムを計算したりする +// 必要はある。 +// (内蔵 ROM からの書き込み用) +void +MK48T02Device::ClearAll() +{ + for (int i = 0, sz = file.GetMemSize(); i < sz; i++) { + mem[i] = 0; + } +}