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