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