2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: CMOS Real Time Clock emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
16 /* PRIVATE VARIABLES **********************************************************/
18 static BOOLEAN NmiEnabled
= TRUE
;
19 static BYTE StatusRegA
= CMOS_DEFAULT_STA
;
20 static BYTE StatusRegB
= CMOS_DEFAULT_STB
;
21 static BYTE StatusRegC
= 0;
22 static BYTE AlarmHour
, AlarmMinute
, AlarmSecond
;
23 static CMOS_REGISTERS SelectedRegister
= CMOS_REG_STATUS_D
;
25 /* PUBLIC FUNCTIONS ***********************************************************/
27 BOOLEAN
IsNmiEnabled(VOID
)
32 VOID
CmosWriteAddress(BYTE Value
)
34 /* Update the NMI enabled flag */
35 NmiEnabled
= !(Value
& CMOS_DISABLE_NMI
);
37 /* Get the register number */
38 Value
&= ~CMOS_DISABLE_NMI
;
40 if (Value
< CMOS_REG_MAX
)
42 /* Select the new register */
43 SelectedRegister
= Value
;
47 /* Default to Status Register D */
48 SelectedRegister
= CMOS_REG_STATUS_D
;
52 BYTE
CmosReadData(VOID
)
54 SYSTEMTIME CurrentTime
;
56 /* Get the current time */
57 GetLocalTime(&CurrentTime
);
59 switch (SelectedRegister
)
61 case CMOS_REG_SECONDS
:
63 return (StatusRegB
& CMOS_STB_BINARY
)
65 : BINARY_TO_BCD(CurrentTime
.wSecond
);
68 case CMOS_REG_ALARM_SEC
:
70 return (StatusRegB
& CMOS_STB_BINARY
)
72 : BINARY_TO_BCD(AlarmSecond
);
75 case CMOS_REG_MINUTES
:
77 return (StatusRegB
& CMOS_STB_BINARY
)
79 : BINARY_TO_BCD(CurrentTime
.wMinute
);
82 case CMOS_REG_ALARM_MIN
:
84 return (StatusRegB
& CMOS_STB_BINARY
)
86 : BINARY_TO_BCD(AlarmMinute
);
91 BOOLEAN Afternoon
= FALSE
;
92 BYTE Value
= CurrentTime
.wHour
;
94 if (!(StatusRegB
& CMOS_STB_24HOUR
) && (Value
>= 12))
100 if (!(StatusRegB
& CMOS_STB_BINARY
)) Value
= BINARY_TO_BCD(Value
);
102 /* Convert to 12-hour */
103 if (Afternoon
) Value
|= 0x80;
108 case CMOS_REG_ALARM_HRS
:
110 BOOLEAN Afternoon
= FALSE
;
111 BYTE Value
= AlarmHour
;
113 if (!(StatusRegB
& CMOS_STB_24HOUR
) && (Value
>= 12))
119 if (!(StatusRegB
& CMOS_STB_BINARY
)) Value
= BINARY_TO_BCD(Value
);
121 /* Convert to 12-hour */
122 if (Afternoon
) Value
|= 0x80;
127 case CMOS_REG_DAY_OF_WEEK
:
129 return (StatusRegB
& CMOS_STB_BINARY
)
130 ? CurrentTime
.wDayOfWeek
131 : BINARY_TO_BCD(CurrentTime
.wDayOfWeek
);
136 return (StatusRegB
& CMOS_STB_BINARY
)
138 :BINARY_TO_BCD(CurrentTime
.wDay
);
143 return (StatusRegB
& CMOS_STB_BINARY
)
145 : BINARY_TO_BCD(CurrentTime
.wMonth
);
150 return (StatusRegB
& CMOS_STB_BINARY
)
151 ? (CurrentTime
.wYear
% 100)
152 : BINARY_TO_BCD(CurrentTime
.wYear
% 100);
155 case CMOS_REG_STATUS_A
:
160 case CMOS_REG_STATUS_B
:
165 case CMOS_REG_STATUS_C
:
167 BYTE Value
= StatusRegC
;
169 /* Clear status register C */
172 /* Return the old value */
176 case CMOS_REG_STATUS_D
:
178 /* Our CMOS battery works perfectly forever */
179 return CMOS_BATTERY_OK
;
182 case CMOS_REG_DIAGNOSTICS
:
184 /* Diagnostics found no errors */
195 /* Return to Status Register D */
196 SelectedRegister
= CMOS_REG_STATUS_D
;
199 VOID
CmosWriteData(BYTE Value
)
201 BOOLEAN ChangeTime
= FALSE
;
202 SYSTEMTIME CurrentTime
;
204 /* Get the current time */
205 GetLocalTime(&CurrentTime
);
207 switch (SelectedRegister
)
209 case CMOS_REG_SECONDS
:
212 CurrentTime
.wSecond
= (StatusRegB
& CMOS_STB_BINARY
)
214 : BCD_TO_BINARY(Value
);
219 case CMOS_REG_ALARM_SEC
:
221 AlarmSecond
= (StatusRegB
& CMOS_STB_BINARY
)
223 : BCD_TO_BINARY(Value
);
228 case CMOS_REG_MINUTES
:
231 CurrentTime
.wMinute
= (StatusRegB
& CMOS_STB_BINARY
)
233 : BCD_TO_BINARY(Value
);
238 case CMOS_REG_ALARM_MIN
:
240 AlarmMinute
= (StatusRegB
& CMOS_STB_BINARY
)
242 : BCD_TO_BINARY(Value
);
249 BOOLEAN Afternoon
= FALSE
;
253 if (!(StatusRegB
& CMOS_STB_24HOUR
) && (Value
& 0x80))
259 CurrentTime
.wHour
= (StatusRegB
& CMOS_STB_BINARY
)
261 : BCD_TO_BINARY(Value
);
263 /* Convert to 24-hour format */
264 if (Afternoon
) CurrentTime
.wHour
+= 12;
269 case CMOS_REG_ALARM_HRS
:
271 BOOLEAN Afternoon
= FALSE
;
273 if (!(StatusRegB
& CMOS_STB_24HOUR
) && (Value
& 0x80))
279 AlarmHour
= (StatusRegB
& CMOS_STB_BINARY
)
281 : BCD_TO_BINARY(Value
);
283 /* Convert to 24-hour format */
284 if (Afternoon
) AlarmHour
+= 12;
289 case CMOS_REG_DAY_OF_WEEK
:
292 CurrentTime
.wDayOfWeek
= (StatusRegB
& CMOS_STB_BINARY
)
294 : BCD_TO_BINARY(Value
);
302 CurrentTime
.wDay
= (StatusRegB
& CMOS_STB_BINARY
)
304 : BCD_TO_BINARY(Value
);
312 CurrentTime
.wMonth
= (StatusRegB
& CMOS_STB_BINARY
)
314 : BCD_TO_BINARY(Value
);
323 /* Clear everything except the century */
324 CurrentTime
.wYear
= (CurrentTime
.wYear
/ 100) * 100;
326 CurrentTime
.wYear
+= (StatusRegB
& CMOS_STB_BINARY
)
328 : BCD_TO_BINARY(Value
);
333 case CMOS_REG_STATUS_A
:
339 case CMOS_REG_STATUS_B
:
351 if (ChangeTime
) SetLocalTime(&CurrentTime
);
353 /* Return to Status Register D */
354 SelectedRegister
= CMOS_REG_STATUS_D
;
357 DWORD
RtcGetTicksPerSecond(VOID
)
359 BYTE RateSelect
= StatusRegB
& 0x0F;
363 /* No periodic interrupt */
367 /* 1 and 2 act like 8 and 9 */
368 if (RateSelect
<= 2) RateSelect
+= 7;
370 return 1 << (16 - RateSelect
);
373 VOID
RtcPeriodicTick(VOID
)
376 StatusRegC
|= CMOS_STC_PF
;
378 /* Check if there should be an interrupt on a periodic timer tick */
379 if (StatusRegB
& CMOS_STB_INT_PERIODIC
)
381 StatusRegC
|= CMOS_STC_IRQF
;
384 PicInterruptRequest(RTC_IRQ_NUMBER
);
388 /* Should be called every second */
389 VOID
RtcTimeUpdate(VOID
)
391 SYSTEMTIME CurrentTime
;
393 /* Get the current time */
394 GetLocalTime(&CurrentTime
);
397 StatusRegC
|= CMOS_STC_UF
;
399 /* Check if the time matches the alarm time */
400 if ((CurrentTime
.wHour
== AlarmHour
)
401 && (CurrentTime
.wMinute
== AlarmMinute
)
402 && (CurrentTime
.wSecond
== AlarmSecond
))
404 /* Set the alarm flag */
405 StatusRegC
|= CMOS_STC_AF
;
407 /* Set IRQF if there should be an interrupt */
408 if (StatusRegB
& CMOS_STB_INT_ON_ALARM
) StatusRegC
|= CMOS_STC_IRQF
;
411 /* Check if there should be an interrupt on update */
412 if (StatusRegB
& CMOS_STB_INT_ON_UPDATE
) StatusRegC
|= CMOS_STC_IRQF
;
414 if (StatusRegC
& CMOS_STC_IRQF
)
417 PicInterruptRequest(RTC_IRQ_NUMBER
);