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