|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2024 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // Goldfish (Qemu) RTC+Timer (の RTC のほう)
9: //
10:
11: // RTC デバイスのレジスタは次の通り。(QEMU 仕様)
12: //
13: // +$00.L TIME_LOW RW: ホスト時刻 [nsec] の下位 32 ビット
14: // +$04.L TIME_HIGH RW: ホスト時刻 [nsec] の上位 32 ビット
15: //
16: // TIME_LOW の動作は Timer デバイスと同じだが、こちらはホストの RTC 時刻
17: // (実世界時間) を秒の粒度で返す。つまり time() * 1e9 だと仕様書にはある。
18: // 書き込みも可能 (当然 VM の RTC を書き換える)。
19: // アラームと割り込み機能は一切ないが、レジスタマップは Timer デバイスと
20: // 同じものを使うらしい。
21:
22: #include "goldfish_rtc.h"
1.1.1.2 root 23: #include "mpu.h"
1.1 root 24: #include "textscreen.h"
25:
26: //
27: // GFRTC
28: //
29:
30: // コンストラクタ
31: GFRTCDevice::GFRTCDevice()
32: : inherited()
33: {
34: }
35:
36: // デストラクタ
37: GFRTCDevice::~GFRTCDevice()
38: {
39: }
40:
41: // リセット
42: void
43: GFRTCDevice::ResetHard(bool poweron)
44: {
45: time_nsec = 0;
46: }
47:
48: busdata
1.1.1.2 root 49: GFRTCDevice::ReadPort(uint32 offset)
1.1 root 50: {
51: busdata data;
52:
53: switch (offset) {
54: case GFRTC::TIME_LOW:
55: time_nsec = (uint64)MkTime() * 1000'000'000U;
56: data = (uint32)time_nsec;
57: if (__predict_false(loglevel >= 1)) {
58: if (loglevel >= 3) {
59: putlogn("TIME_LOW -> $%08x", data.Data());
60: } else {
61: putlogn("TIME -> $%08x'%08x",
62: (uint32)(time_nsec >> 32), data.Data());
63: }
1.1.1.2 root 64:
65: if (loglevel >= 2) {
66: struct tm tm, tr;
67: time_t t = (time_t)(time_nsec / 1_sec);
68: gmtime_r(&t, &tm);
69: putlogn("TIME / 1e9: %4u/%02u/%02u %02u:%02u:%02u",
70: tm.tm_year + 1900,
71: tm.tm_mon + 1,
72: tm.tm_mday,
73: tm.tm_hour,
74: tm.tm_min,
75: tm.tm_sec);
76:
77: struct timeval tv;
78: gettimeofday(&tv, NULL);
79: gmtime_r(&tv.tv_sec, &tr);
80: putlogn("gettimeofday: %4u/%02u/%02u %02u:%02u:%02u",
81: tr.tm_year + 1900,
82: tr.tm_mon + 1,
83: tr.tm_mday,
84: tr.tm_hour,
85: tr.tm_min,
86: tr.tm_sec);
87: }
1.1 root 88: }
89: break;
90:
91: case GFRTC::TIME_HIGH:
92: data = (uint32)(time_nsec >> 32);
93: putlog(3, "TIME_HIGH -> $%08x", data.Data());
94: break;
95:
96: case GFRTC::INTR_STATUS:
97: putlog(2, "Read INTR_STATUS (No function)");
98: data = 0;
99: break;
100:
101: case GFRTC::ALARM_STATUS:
102: putlog(2, "Read ALARM_STATUS (No function)");
103: data = 0;
104: break;
105:
106: default:
1.1.1.2 root 107: putlog(1, "Read unknown $%08x", mpu->GetPaddr());
1.1 root 108: data.SetBusErr();
109: break;
110: }
1.1.1.2 root 111:
112: data |= BusData::Size4;
1.1 root 113: return data;
114: }
115:
116: busdata
1.1.1.2 root 117: GFRTCDevice::WritePort(uint32 offset, uint32 data)
1.1 root 118: {
119: busdata r;
120:
121: switch (offset) {
122: case GFRTC::TIME_LOW:
123: {
124: time_nsec |= data;
125:
126: if (__predict_false(loglevel >= 1)) {
127: if (loglevel >= 2) {
128: putlogn("TIME_LOW <- $%08x", (uint32)time_nsec);
129: } else {
130: putlogn("TIME <- $%08x'%08x",
131: (uint32)(time_nsec >> 32), (uint32)time_nsec);
132: }
133: }
134:
135: time_t t = time_nsec / 1000'000'000U;
136: struct tm tm;
137: gmtime_r(&t, &tm);
138: SetYear(tm.tm_year + 1900);
139: SetMon(tm.tm_mon + 1);
140: SetMday(tm.tm_mday);
141: SetHour(tm.tm_hour);
142: SetMin(tm.tm_min);
143: SetSec(tm.tm_sec);
144: break;
145: }
146:
147: case GFRTC::TIME_HIGH:
148: putlog(2, "TIME_HIGH <- $%08x", data);
149: time_nsec = (uint64)data << 32;
150: break;
151:
152: case GFRTC::ALARM_LOW:
153: putlog(2, "ALARM_LOW <- $%08x (No function)", data);
154: break;
155:
156: case GFRTC::ALARM_HIGH:
157: putlog(2, "ALARM_HIGH <- $%08x (No function)", data);
158: break;
159:
160: case GFRTC::INTR_STATUS:
161: putlog(2, "INTR_STATUS <- $%08x (No function)", data);
162: break;
163:
164: case GFRTC::ALARM_CLEAR:
165: putlog(2, "ALARM_CLEAR <- $%08x (No function)", data);
166: break;
167:
168: case GFRTC::INTR_CLEAR:
169: putlog(2, "INTR_CLEAR <- $%08x (No function)", data);
170: break;
171:
172: default:
1.1.1.2 root 173: putlog(1, "Write unknown $%08x <- $%08x", mpu->GetPaddr(), data);
1.1 root 174: r.SetBusErr();
175: break;
176: }
1.1.1.2 root 177:
178: r |= BusData::Size4;
1.1 root 179: return r;
180: }
181:
182: busdata
1.1.1.2 root 183: GFRTCDevice::PeekPort(uint32 offset)
1.1 root 184: {
185: switch (offset) {
1.1.1.2 root 186: case GFRTC::TIME_LOW:
1.1 root 187: {
188: uint64 now = MkTime() * 1000'000'000U;
1.1.1.2 root 189: return now & 0xffffffffU;
1.1 root 190: }
1.1.1.2 root 191: case GFRTC::TIME_HIGH:
192: {
193: uint64 now = MkTime() * 1000'000'000U;
194: return now >> 32;
195: }
196: case GFRTC::INTR_STATUS:
197: case GFRTC::ALARM_STATUS:
1.1 root 198: return 0;
199: default:
1.1.1.2 root 200: return BusData::BusErr;
1.1 root 201: }
202: }
203:
204: // モニタの下請け。(GFTimer から呼ばれる)
205: int
206: GFRTCDevice::MonitorUpdateRTC(TextScreen& screen, int y) const
207: {
208: screen.Puts(0, y++, "<RTC>");
209: screen.Print(0, y++, "%04u/%02u/%02u(%s) %02u:%02u:%02u",
210: year, mon, day, wdays[GetWday()], hour, min, sec);
211: screen.Puts(0, y++, "TimeZone: UTC");
212:
213: return y;
214: }
215:
216: void
217: GFRTCDevice::Tick1Hz()
218: {
219: CountUpSec();
220: }
221:
222: // 内部時刻から time_t を作成する。
223: time_t
224: GFRTCDevice::MkTime() const
225: {
226: struct tm tm;
227:
228: tm.tm_year = year - 1900;
229: tm.tm_mon = mon - 1;
230: tm.tm_mday = day;
231: tm.tm_hour = hour;
232: tm.tm_min = min;
233: tm.tm_sec = sec;
234:
235: return timegm(&tm);
236: }
237:
238: uint
239: GFRTCDevice::GetWday() const
240: {
241: // 曜日は保持していないので一旦日付を作ってから求める。
242: struct tm tm;
243: time_t t = MkTime();
244:
245: gmtime_r(&t, &tm);
246: return tm.tm_wday;
247: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.