Convert mc1416818a to new world order
[qemupp.git] / pc / mc146818a.cpp
blob2ae2dc2c1a5729934e2b9c1ac6b9814f0a109e56
1 #include "mc146818a.hpp"
2 #include "mc146818a_def.hpp"
3 #include "util.hpp"
5 #include <string.h>
6 #include <stdio.h>
8 /* month is between 0 and 11. */
9 static int get_days_in_month(int month, int year)
11 static const int days_tab[12] = {
12 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
14 int d;
15 if ((unsigned )month >= 12) {
16 return 31;
18 d = days_tab[month];
19 if (month == 1) {
20 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
21 d++;
24 return d;
27 /* update 'tm' to the next second */
28 static void next_second(struct tm *tm)
30 int days_in_month;
32 tm->tm_sec++;
33 if ((unsigned)tm->tm_sec >= 60) {
34 tm->tm_sec = 0;
35 tm->tm_min++;
36 if ((unsigned)tm->tm_min >= 60) {
37 tm->tm_min = 0;
38 tm->tm_hour++;
39 if ((unsigned)tm->tm_hour >= 24) {
40 tm->tm_hour = 0;
41 /* next day */
42 tm->tm_wday++;
43 if ((unsigned)tm->tm_wday >= 7)
44 tm->tm_wday = 0;
45 days_in_month = get_days_in_month(tm->tm_mon,
46 tm->tm_year + 1900);
47 tm->tm_mday++;
48 if (tm->tm_mday < 1) {
49 tm->tm_mday = 1;
50 } else if (tm->tm_mday > days_in_month) {
51 tm->tm_mday = 1;
52 tm->tm_mon++;
53 if (tm->tm_mon >= 12) {
54 tm->tm_mon = 0;
55 tm->tm_year++;
63 RTC::RTC(int32_t base_year, struct tm *tm, DriftMode mode) :
64 base_year(base_year), current_tm(*tm), drift_mode(mode)
66 uint8_t val;
68 for (size_t i = 0; i < this->cmos_data.size(); i++) {
69 this->cmos_data[i] = 0;
72 this->periodic_timer.set(this, &RTC::on_periodic);
73 this->second_timer.set(this, &RTC::on_second);
74 this->second_timer2.set(this, &RTC::on_second2);
76 val = this->to_bcd((tm->tm_year / 100) + 19);
78 this->cmos_data[RTC_REG_A] = 0x26;
79 this->cmos_data[RTC_REG_B] = 0x02;
80 this->cmos_data[RTC_REG_C] = 0x00;
81 this->cmos_data[RTC_REG_D] = 0x80;
82 this->cmos_data[REG_IBM_CENTURY_BYTE] = val;
83 this->cmos_data[REG_IBM_PS2_CENTURY_BYTE] = val;
85 this->copy_date();
86 this->next_second_time = now(SEC);
87 next_second(&this->current_tm);
88 this->second_timer2.update(this->next_second_time, SEC);
89 this->reset();
92 void RTC::reset(void)
94 this->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE);
95 this->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF);
96 this->irq.lower();
97 #if 1
98 /* FIXME */
99 this->periodic_timer.cancel();
100 this->cmos_data[RTC_REG_A] = 0x26;
101 this->cmos_data[RTC_REG_B] = 0x02;
102 #endif
105 uint8_t RTC::cmos_read(uint8_t index)
107 uint8_t ret;
109 switch (index) {
110 case RTC_SECONDS:
111 case RTC_MINUTES:
112 case RTC_HOURS:
113 case RTC_DAY_OF_WEEK:
114 case RTC_DAY_OF_MONTH:
115 case RTC_MONTH:
116 case RTC_YEAR:
117 ret = this->cmos_data[index];
118 break;
119 case RTC_REG_A:
120 ret = this->cmos_data[index];
121 break;
122 case RTC_REG_C:
123 ret = this->cmos_data[index];
124 this->irq.lower();
125 this->cmos_data[RTC_REG_C] = 0x00;
126 break;
127 default:
128 ret = this->cmos_data[index];
129 break;
132 return ret;
135 void RTC::cmos_write(uint8_t index, uint8_t data)
137 switch (index) {
138 case RTC_SECONDS_ALARM:
139 case RTC_MINUTES_ALARM:
140 case RTC_HOURS_ALARM:
141 this->cmos_data[index] = data;
142 break;
143 case RTC_SECONDS:
144 case RTC_MINUTES:
145 case RTC_HOURS:
146 case RTC_DAY_OF_WEEK:
147 case RTC_DAY_OF_MONTH:
148 case RTC_MONTH:
149 case RTC_YEAR:
150 this->cmos_data[index] = data;
151 /* if in set mode, do not update the time */
152 if (!(this->cmos_data[RTC_REG_B] & REG_B_SET)) {
153 this->set_time();
155 break;
156 case RTC_REG_A:
157 /* UIP bit is read only */
158 this->cmos_data[RTC_REG_A] &= REG_A_UIP;
159 this->cmos_data[RTC_REG_A] |= (data & ~REG_A_UIP);
160 this->next_periodic_time = -1;
161 this->missed_ticks = 0;
162 this->update_timer(now(PC_FREQ1));
163 break;
164 case RTC_REG_B:
165 if (data & REG_B_SET) {
166 /* set mode: reset UIP mode */
167 this->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
168 data &= ~REG_B_UIE;
169 } else {
170 /* if disabling set mode, update the time */
171 if (this->cmos_data[RTC_REG_B] & REG_B_SET) {
172 this->set_time();
175 this->cmos_data[RTC_REG_B] = data;
176 this->next_periodic_time = -1;
177 this->missed_ticks = 0;
178 this->update_timer(now(PC_FREQ1));
179 break;
180 case RTC_REG_C:
181 case RTC_REG_D:
182 /* cannot write to them */
183 break;
184 default:
185 this->cmos_data[index] = data;
186 break;
190 int RTC::from_bcd(int a)
192 if (this->cmos_data[RTC_REG_B] & REG_B_DM) {
193 return a;
194 } else {
195 return ((a >> 4) * 10) + (a & 0x0f);
199 int RTC::to_bcd(int a)
201 if (this->cmos_data[RTC_REG_B] & REG_B_DM) {
202 return a;
203 } else {
204 return ((a / 10) << 4) | (a % 10);
208 void RTC::update_timer(int64_t current_time)
210 int period_code;
211 uint64_t period;
213 period_code = this->cmos_data[RTC_REG_A] & 0x0f;
214 if ((period_code == 0) ||
215 !(this->cmos_data[RTC_REG_B] & REG_B_PIE)) {
216 this->periodic_timer.cancel();
217 return;
220 if (period_code <= 2) {
221 period_code += 7;
223 /* period in 32 Khz cycles */
224 period = 1 << (period_code - 1);
226 if (this->next_periodic_time == -1) {
227 this->next_periodic_time = current_time;
229 this->next_periodic_time += period;
231 switch (this->drift_mode) {
232 case DRIFT_DROP:
233 /* Our next timer fires backwards in time, so drop the missed interrupts
234 * and advance the timer to the next logical period boundary.
236 if (this->next_periodic_time < current_time) {
237 uint64_t missed_time;
239 /* Compute the amount of time that we missed rounded to the period
240 * boundary. This is the time we ignore and drop interrupts for.
241 * The rounding step is important to make the timer stable with
242 * respect its period boundaries.
244 missed_time = current_time - this->next_periodic_time;
245 missed_time &= ~(period - 1);
246 this->next_periodic_time += missed_time + period;
248 break;
249 case DRIFT_REINJECT_GRADUAL:
250 if (this->next_periodic_time < current_time) {
251 uint64_t missed_time = current_time - this->next_periodic_time;
252 uint64_t ticks = (missed_time / period);
254 /* I don't understand this +1 . It could be a rounding
255 * issue. I know that we need to see 4 missed ticks when
256 * not in gradual mode
258 this->missed_ticks += 2 * (ticks + 1);
259 this->next_periodic_time += ticks * period + period;
261 if (this->missed_ticks) {
262 this->next_periodic_time -= period / 2;
263 this->missed_ticks--;
265 break;
266 case DRIFT_REINJECT_FAST:
267 /* nothing to do */
268 break;
271 this->periodic_timer.update(this->next_periodic_time, PC_FREQ1);
274 void RTC::set_time(void)
276 struct tm *tm = &this->current_tm;
278 tm->tm_sec = this->from_bcd(this->cmos_data[RTC_SECONDS]);
279 tm->tm_min = this->from_bcd(this->cmos_data[RTC_MINUTES]);
280 tm->tm_hour = this->from_bcd(this->cmos_data[RTC_HOURS] & 0x7f);
281 if (!(this->cmos_data[RTC_REG_B] & 0x02) &&
282 (this->cmos_data[RTC_HOURS] & 0x80)) {
283 tm->tm_hour += 12;
285 tm->tm_wday = this->from_bcd(this->cmos_data[RTC_DAY_OF_WEEK]) - 1;
286 tm->tm_mday = this->from_bcd(this->cmos_data[RTC_DAY_OF_MONTH]);
287 tm->tm_mon = this->from_bcd(this->cmos_data[RTC_MONTH]) - 1;
288 tm->tm_year = this->from_bcd(this->cmos_data[RTC_YEAR]) +
289 this->base_year - 1900;
292 void RTC::copy_date(void)
294 const struct tm *tm = &this->current_tm;
295 int year;
297 this->cmos_data[RTC_SECONDS] = this->to_bcd(tm->tm_sec);
298 this->cmos_data[RTC_MINUTES] = this->to_bcd(tm->tm_min);
299 if (this->cmos_data[RTC_REG_B] & 0x02) {
300 /* 24 hour format */
301 this->cmos_data[RTC_HOURS] = this->to_bcd(tm->tm_hour);
302 } else {
303 /* 12 hour format */
304 this->cmos_data[RTC_HOURS] = this->to_bcd(tm->tm_hour % 12);
305 if (tm->tm_hour >= 12)
306 this->cmos_data[RTC_HOURS] |= 0x80;
308 this->cmos_data[RTC_DAY_OF_WEEK] = this->to_bcd(tm->tm_wday + 1);
309 this->cmos_data[RTC_DAY_OF_MONTH] = this->to_bcd(tm->tm_mday);
310 this->cmos_data[RTC_MONTH] = this->to_bcd(tm->tm_mon + 1);
311 year = (tm->tm_year - this->base_year) % 100;
312 if (year < 0) {
313 year += 100;
315 this->cmos_data[RTC_YEAR] = this->to_bcd(year);
318 void RTC::on_periodic(void)
320 this->update_timer(now(PC_FREQ1));
321 if (this->cmos_data[RTC_REG_B] & REG_B_PIE) {
322 this->cmos_data[RTC_REG_C] |= 0xc0;
323 this->irq.raise();
327 void RTC::on_second(void)
329 /* if the oscillator is not in normal operation, we do not update */
330 if ((this->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
331 this->next_second_time += 1;
332 this->second_timer.update(this->next_second_time, SEC);
333 } else {
334 uint64_t next_deadline;
336 next_second(&this->current_tm);
338 if (!(this->cmos_data[RTC_REG_B] & REG_B_SET)) {
339 /* update in progress bit */
340 this->cmos_data[RTC_REG_A] |= REG_A_UIP;
343 next_deadline = time_to_ns(this->next_second_time, SEC);
344 next_deadline += time_to_ns(244, USEC);
345 this->second_timer2.update(next_deadline, NSEC);
349 void RTC::on_second2(void)
351 if (!(this->cmos_data[RTC_REG_B] & REG_B_SET)) {
352 this->copy_date();
355 /* check alarm */
356 if (this->cmos_data[RTC_REG_B] & REG_B_AIE) {
357 if (((this->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
358 this->from_bcd(this->cmos_data[RTC_SECONDS_ALARM]) == this->current_tm.tm_sec) &&
359 ((this->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
360 this->from_bcd(this->cmos_data[RTC_MINUTES_ALARM]) == this->current_tm.tm_min) &&
361 ((this->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
362 this->from_bcd(this->cmos_data[RTC_HOURS_ALARM]) == this->current_tm.tm_hour)) {
364 this->cmos_data[RTC_REG_C] |= 0xa0;
365 this->irq.raise();
369 /* update ended interrupt */
370 this->cmos_data[RTC_REG_C] |= REG_C_UF;
371 if (this->cmos_data[RTC_REG_B] & REG_B_UIE) {
372 this->cmos_data[RTC_REG_C] |= REG_C_IRQF;
373 this->irq.raise();
376 /* clear update in progress bit */
377 this->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
379 this->next_second_time += 1;
380 this->second_timer.update(this->next_second_time, SEC);
383 uint8_t RTC::read(uint8_t addr)
385 if (addr == 0) {
386 return 0xFF;
388 return this->cmos_read(this->cmos_index);
391 void RTC::write(uint8_t addr, uint8_t data)
393 if (addr == 0) {
394 this->cmos_index = data & 0x7F;
395 } else {
396 this->cmos_write(this->cmos_index, data);
400 void RTC::pickle(Marshaller *m, const char *name)
402 m->start_struct(name, "RTC");
403 marshal(m, "irq", &this->irq);
404 marshal(m, "base_year", &this->base_year);
405 marshal(m, "cmos_data", &this->cmos_data);
406 marshal(m, "current_tm", &this->current_tm);
407 marshal(m, "next_periodic_time", &this->next_periodic_time);
408 marshal(m, "next_second_time", &this->next_periodic_time);
409 marshal(m, "periodic_timer", &this->periodic_timer);
410 marshal(m, "second_timer", &this->second_timer);
411 marshal(m, "second_timer2", &this->second_timer2);
412 marshal(m, "drift_mode", &this->drift_mode);
413 marshal(m, "missed_ticks", &this->missed_ticks);
414 marshal(m, "cmos_index", &this->cmos_index);
415 m->end_struct();
418 /* FIXME This doesn't belong here */
419 void marshal(Marshaller *m, const char *name, struct tm *obj)
421 m->start_struct(name, "tm");
422 marshal(m, "tm_sec", &obj->tm_sec);
423 marshal(m, "tm_min", &obj->tm_min);
424 marshal(m, "tm_hour", &obj->tm_hour);
425 marshal(m, "tm_mday", &obj->tm_mday);
426 marshal(m, "tm_mon", &obj->tm_mon);
427 marshal(m, "tm_year", &obj->tm_year);
428 marshal(m, "tm_wday", &obj->tm_wday);
429 marshal(m, "tm_yday", &obj->tm_yday);
430 marshal(m, "tm_isdst", &obj->tm_isdst);
431 m->end_struct();