|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.