|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: #include "mk48t02.h"
1.1.1.3 root 8: #include "config.h"
1.1.1.4 root 9: #include "mainapp.h"
1.1.1.7 root 10: #include "mpu.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;
1.1.1.5 root 26: monitor_size = nnSize(25, 2);
1.1 root 27: }
28:
29: MK48T02Device::~MK48T02Device()
30: {
31: }
32:
33: bool
34: MK48T02Device::Init()
35: {
36: // ファイルのロード
1.1.1.3 root 37: filename = gMainApp.GetVMDir() + "NVRAM.DAT";
1.1.1.4 root 38: file.SetFilename(filename);
39: file.SetDispname(string_format("NVRAM \"%s\"", filename.c_str()));
40: mem = file.OpenCreate(2040);
41: if (mem == NULL) {
1.1 root 42: return false;
43: }
44:
45: // レジスタ値固定オプション (パフォーマンス測定用)
1.1.1.4 root 46: force_fixed = gConfig->Find(".rtc-force-fixed").AsInt();
1.1 root 47: if (force_fixed) {
1.1.1.7 root 48: putmsgn("force_fixed");
1.1 root 49: }
50:
1.1.1.8 ! root 51: // RTC を初期化
1.1 root 52: // ホストの時刻を UTC で取得
53: LoadHostRTC(false);
54:
55: return true;
56: }
57:
58: // バイナリ表現を BCD 表現に変換する
59: /*static*/ inline uint8
60: MK48T02Device::num2BCD(uint8 num)
61: {
62: return ((num / 10) << 4) + (num % 10);
63: }
64:
65: // BCD 表現をバイナリ表現に変換する
66: /*static*/ inline uint8
67: MK48T02Device::BCD2num(uint8 bcd)
68: {
69: return (bcd >> 4) * 10 + (bcd & 0x0f);
70: }
71:
72: uint64
1.1.1.7 root 73: MK48T02Device::Read(uint32 offset)
1.1 root 74: {
75: uint32 data;
76:
1.1.1.7 root 77: if (offset < 2040) {
78: data = mem[offset];
79: putlog(3, "$%08x -> $%02x", gMPU->GetPaddr(), data);
1.1 root 80: } else {
81: // XXX ReadBit が立っていない時は何が読めるのか
1.1.1.7 root 82: switch (offset - 2040) {
1.1 root 83: case 0: // コントロールレジスタ
84: data = reg.ctrl;
85: break;
86: case 1: // 秒
87: data = reg.sec;
88: if (reg.stop) {
89: data |= MK48T02Clock::STOP;
90: }
91: break;
92: case 2: // 分
93: data = reg.min;
94: break;
95: case 3: // 時
96: data = reg.hour;
97: if (reg.kickstart) {
98: data |= MK48T02Clock::KICKSTART;
99: }
100: break;
101: case 4: // 曜日
102: data = reg.wday;
103: break;
104: case 5: // 日
105: data = reg.mday;
106: break;
107: case 6: // 月
108: data = reg.mon;
109: break;
110: case 7: // 年
111: data = reg.year;
112: break;
113: default:
1.1.1.3 root 114: __unreachable();
1.1 root 115: }
116:
117: if (force_fixed) {
118: // パフォーマンス測定をする際に、RTC が実時間を返すと
119: // ゲストの状態が毎回異なることになるため、
120: // いつ何時も同じ値を返すことで、世界から隔絶された
121: // VM を作り、パフォーマンス測定を安定させる。
122: data = 3;
123: }
1.1.1.7 root 124: putlog(3, "$%08x -> $%02x", gMPU->GetPaddr(), data);
1.1 root 125: }
126: return data;
127: }
128:
129: uint64
1.1.1.7 root 130: MK48T02Device::Write(uint32 offset, uint32 data)
1.1 root 131: {
1.1.1.7 root 132: if (offset < 2040) {
133: putlog(3, "$%08x <- $%02x", gMPU->GetPaddr(), data);
134: mem[offset] = data;
1.1 root 135: } else {
1.1.1.7 root 136: putlog(3, "$%08x <- $%02x", gMPU->GetPaddr(), data);
137: switch (offset - 2040) {
1.1 root 138: case 0: // コントロール
139: WriteCtrl(data);
140: break;
141: case 1: // 秒 (b7 は STOP BIT)
142: reg.stop = (data & MK48T02Clock::STOP);
143: if ((reg.ctrl & MK48T02Clock::WRITE)) {
144: rtc.sec = BCD2num(data & 0x7f);
145: }
146: break;
147: case 2: // 分
148: if ((reg.ctrl & MK48T02Clock::WRITE)) {
149: rtc.min = BCD2num(data & 0x7f);
150: }
151: break;
152: case 3: // 時 (b7 は KICK START)
153: reg.kickstart = (data & MK48T02Clock::KICKSTART);
154: if ((reg.ctrl & MK48T02Clock::WRITE)) {
155: rtc.hour = BCD2num(data & 0x3f);
156: }
157: break;
158: case 4: // 曜日 (b6 は Frequency Test (未実装))
159: if ((reg.ctrl & MK48T02Clock::WRITE)) {
160: rtc.wday = data & 0x07;
161: }
162: break;
163: case 5: // 日
164: if ((reg.ctrl & MK48T02Clock::WRITE)) {
165: rtc.mday = BCD2num(data & 0x3f);
166: }
167: break;
168: case 6: // 月
169: if ((reg.ctrl & MK48T02Clock::WRITE)) {
170: rtc.mon = BCD2num(data & 0x1f);
171: }
172: break;
173: case 7: // 年
174: if ((reg.ctrl & MK48T02Clock::WRITE)) {
175: rtc.year = BCD2num(data) + MK48T02Clock::YEAR_EPOCH;
176: }
177: break;
178: default:
1.1.1.3 root 179: __unreachable();
1.1 root 180: }
181: }
182: return 0;
183: }
184:
185: uint64
1.1.1.7 root 186: MK48T02Device::Peek(uint32 offset)
1.1 root 187: {
188: uint8 data;
189:
1.1.1.7 root 190: if (offset < 2040) {
191: return mem[offset];
1.1 root 192: } else {
1.1.1.7 root 193: switch (offset - 2040) {
1.1 root 194: case 0: // コントロールレジスタ
195: return reg.ctrl;
196: case 1: // 秒 | STOP
197: data = reg.sec;
198: if (reg.stop) {
199: data |= MK48T02Clock::STOP;
200: }
201: return data;
202: case 2: // 分
203: return reg.min;
204: case 3: // 時
205: data = reg.hour;
206: if (reg.kickstart) {
207: data |= MK48T02Clock::KICKSTART;
208: }
209: return data;
210: case 4: // 曜日
211: return reg.wday;
212: case 5: // 日
213: return reg.mday;
214: case 6: // 月
215: return reg.mon;
216: case 7: // 年
217: return reg.year;
218: default:
1.1.1.3 root 219: __unreachable();
1.1 root 220: }
221: }
222: }
223:
1.1.1.5 root 224: void
225: MK48T02Device::MonitorUpdate(TextScreen& monitor)
1.1 root 226: {
227: monitor.Clear();
228:
229: monitor.Print(0, 0, "WR:%d RD:%d",
230: (reg.ctrl & MK48T02Clock::WRITE) ? 1 : 0,
231: (reg.ctrl & MK48T02Clock::READ) ? 1 : 0);
232: monitor.Print(0, 1, "ST:%d KS:%d",
233: reg.stop,
234: reg.kickstart);
235: // XXX レジスタの値はラッチされるまで意味がないので
236: // どうしたものか。
237: monitor.Print(10, 0, "%4d/%02d/%02d(%s)",
238: rtc.year, rtc.mon, rtc.mday, wdays[rtc.wday]);
239: monitor.Print(10, 1, "%02d:%02d:%02d",
240: rtc.hour, rtc.min, rtc.sec);
241: }
242:
243: // コントロールレジスタへの書き込み
244: void
245: MK48T02Device::WriteCtrl(uint32 data)
246: {
247: reg.ctrl = data;
248:
249: if ((reg.ctrl & MK48T02Clock::WRITE)) {
250: // WRITE ビットを立てると計時を止める
251: rtc.enable_sec = false;
252: } else {
253: // WRITE ビットを下ろすと計時を再開して
254: rtc.enable_sec = true;
255: // KickStart, FreqTest をゼロにする
256: reg.kickstart = false;
257: }
258:
259: if ((reg.ctrl & MK48T02Clock::READ)) {
260: // READ ビットを立てたところで計時をとめて(?)
261: // 値をレジスタにラッチ(?)
262: rtc.enable_sec = false;
263: reg.sec = num2BCD(rtc.sec);
264: reg.min = num2BCD(rtc.min);
265: reg.hour = num2BCD(rtc.hour);
266: reg.wday = rtc.wday;
267: reg.mday = num2BCD(rtc.mday);
268: reg.mon = num2BCD(rtc.mon);
269: reg.year = num2BCD(rtc.year - MK48T02Clock::YEAR_EPOCH);
270: } else {
271: // READ ビットを下ろすと計時を再開
272: rtc.enable_sec = true;
273: }
274: }
275:
276: // 32Hz クロック入力
277: //
278: // 32Hz ごとに呼ばれて、これが RTC の時計を進める処理をするところ。
279: // RTC を実時間に同期して進めるモードなら実時間を計るスケジューラから、
280: // RTC を仮想時間に同期して進めるモードなら VM スケジューラから呼ばれる。
281: void
282: MK48T02Device::ClockIn()
283: {
284: // 32Hz カウンタを進める。
285: // 内部表現とデバイスから読み出せる値が同じなので、更新処理は不要。
286: CountUp();
287: }
1.1.1.3 root 288:
289: // 指定したアドレスからのゼロ終端文字列を返す
290: // (内蔵 ROM からのアクセス用)
291: std::string
292: MK48T02Device::PeekString(uint32 addr) const
293: {
294: std::string rv = "";
295:
1.1.1.7 root 296: addr &= 0x7ff;
297: for (; mem[addr]; addr++) {
298: rv += mem[addr];
1.1.1.3 root 299: }
300: return rv;
301: }
302:
303: // 指定したアドレスに文字列を書き込み、
304: // 16 バイトに満たないところはゼロでパディングする。
305: // 書き込めれば true を、書き込めなければ false を返す。
306: // (内蔵 ROM からの書き込み用)
307: bool
308: MK48T02Device::WriteString(uint32 addr, const std::string& data)
309: {
310: int i = 0;
311:
312: // XXX ここの文字列はゼロ終端なのかどうか
313: if (data.length() == 0 || data.length() > 15) {
314: return false;
315: }
316:
317: addr &= 0x7ff;
318: for (; i < data.length(); i++) {
319: Write(addr + i, data[i]);
320: }
321: for (; i < 16; i++) {
322: Write(addr + i, 0);
323: }
324: return true;
325: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.