ReactOS  0.4.14-dev-608-gd495a4f
cmos.c
Go to the documentation of this file.
1 /*
2  * COPYRIGHT: GPL - See COPYING in the top level directory
3  * PROJECT: ReactOS Virtual DOS Machine
4  * FILE: subsystems/mvdm/ntvdm/hardware/cmos.c
5  * PURPOSE: CMOS Real Time Clock emulation
6  * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "ntvdm.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 #include "emulator.h"
17 #include "cmos.h"
18 
19 #include "io.h"
20 #include "pic.h"
21 #include "clock.h"
22 
23 /* PRIVATE VARIABLES **********************************************************/
24 
27 
30 
33 
34 /* PRIVATE FUNCTIONS **********************************************************/
35 
37 {
38  BYTE RateSelect = CmosMemory.StatusRegA & 0x0F;
39 
40  if (RateSelect == 0)
41  {
42  /* No periodic interrupt */
44  return;
45  }
46 
47  /* 1 and 2 act like 8 and 9 */
48  if (RateSelect <= 2) RateSelect += 7;
49 
50  SetHardwareTimerDelay(PeriodicTimer, HZ_TO_NS(1 << (16 - RateSelect)));
51  // FIXME: This call keeps EnableCount increasing without compensating it!
53 }
54 
56 {
57  UNREFERENCED_PARAMETER(ElapsedTime);
58 
59  /* Set PF */
61 
62  /* Check if there should be an interrupt on a periodic timer tick */
63  if (CmosMemory.StatusRegB & CMOS_STB_INT_PERIODIC)
64  {
66 
67  /* Interrupt! */
69  }
70 }
71 
72 /* Should be called every second */
73 static VOID FASTCALL RtcTimeUpdate(ULONGLONG ElapsedTime)
74 {
75  SYSTEMTIME CurrentTime;
76 
77  UNREFERENCED_PARAMETER(ElapsedTime);
78 
79  /* Get the current time */
80  GetLocalTime(&CurrentTime);
81 
82  /* Set UF */
84 
85  /* Check if the time matches the alarm time */
86  if ((CurrentTime.wHour == CmosMemory.AlarmHour ) &&
87  (CurrentTime.wMinute == CmosMemory.AlarmMinute) &&
88  (CurrentTime.wSecond == CmosMemory.AlarmSecond))
89  {
90  /* Set the alarm flag */
92 
93  /* Set IRQF if there should be an interrupt */
95  }
96 
97  /* Check if there should be an interrupt on update */
99 
101  {
102  /* Interrupt! */
104  }
105 }
106 
108 {
110 
111  /* Update the NMI enabled flag */
113 
114  /* Get the register number */
116 
117  if (Data < CMOS_REG_MAX)
118  {
119  /* Select the new register */
121  }
122  else
123  {
124  /* Default to Status Register D */
126  }
127 }
128 
130 {
131  BYTE Value;
132  SYSTEMTIME CurrentTime;
133 
135 
136  /* Get the current time */
137  GetLocalTime(&CurrentTime);
138 
139  switch (SelectedRegister)
140  {
141  case CMOS_REG_SECONDS:
142  {
143  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wSecond);
144  break;
145  }
146 
147  case CMOS_REG_ALARM_SEC:
148  {
149  Value = READ_CMOS_DATA(CmosMemory, CmosMemory.AlarmSecond);
150  break;
151  }
152 
153  case CMOS_REG_MINUTES:
154  {
155  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wMinute);
156  break;
157  }
158 
159  case CMOS_REG_ALARM_MIN:
160  {
161  Value = READ_CMOS_DATA(CmosMemory, CmosMemory.AlarmMinute);
162  break;
163  }
164 
165  case CMOS_REG_HOURS:
166  {
167  BOOLEAN Afternoon = FALSE;
168  Value = CurrentTime.wHour;
169 
170  if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
171  {
172  Value -= 12;
173  Afternoon = TRUE;
174  }
175 
177 
178  /* Convert to 12-hour */
179  if (Afternoon) Value |= 0x80;
180 
181  break;
182  }
183 
184  case CMOS_REG_ALARM_HRS:
185  {
186  BOOLEAN Afternoon = FALSE;
187  Value = CmosMemory.AlarmHour;
188 
189  if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
190  {
191  Value -= 12;
192  Afternoon = TRUE;
193  }
194 
196 
197  /* Convert to 12-hour */
198  if (Afternoon) Value |= 0x80;
199 
200  break;
201  }
202 
204  {
205  /*
206  * The CMOS value is 1-based but the
207  * GetLocalTime API value is 0-based.
208  * Correct it.
209  */
210  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wDayOfWeek + 1);
211  break;
212  }
213 
214  case CMOS_REG_DAY:
215  {
216  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wDay);
217  break;
218  }
219 
220  case CMOS_REG_MONTH:
221  {
222  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wMonth);
223  break;
224  }
225 
226  case CMOS_REG_YEAR:
227  {
228  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wYear % 100);
229  break;
230  }
231 
232  case CMOS_REG_CENTURY:
233  {
234  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wYear / 100 + 19);
235  break;
236  }
237 
238  case CMOS_REG_STATUS_C:
239  {
240  /* Return the old status register value, then clear it */
242  CmosMemory.StatusRegC = 0x00;
243  break;
244  }
245 
246  case CMOS_REG_STATUS_A:
247  case CMOS_REG_STATUS_B:
248  case CMOS_REG_STATUS_D:
251  default:
252  {
253  // ASSERT(SelectedRegister < CMOS_REG_MAX);
255  }
256  }
257 
258  /* Return to Status Register D */
260 
261  return Value;
262 }
263 
265 {
266  BOOLEAN ChangeTime = FALSE;
267  SYSTEMTIME CurrentTime;
268 
270 
271  /* Get the current time */
272  GetLocalTime(&CurrentTime);
273 
274  switch (SelectedRegister)
275  {
276  case CMOS_REG_SECONDS:
277  {
278  ChangeTime = TRUE;
279  CurrentTime.wSecond = WRITE_CMOS_DATA(CmosMemory, Data);
280  break;
281  }
282 
283  case CMOS_REG_ALARM_SEC:
284  {
285  CmosMemory.AlarmSecond = WRITE_CMOS_DATA(CmosMemory, Data);
286  break;
287  }
288 
289  case CMOS_REG_MINUTES:
290  {
291  ChangeTime = TRUE;
292  CurrentTime.wMinute = WRITE_CMOS_DATA(CmosMemory, Data);
293  break;
294  }
295 
296  case CMOS_REG_ALARM_MIN:
297  {
298  CmosMemory.AlarmMinute = WRITE_CMOS_DATA(CmosMemory, Data);
299  break;
300  }
301 
302  case CMOS_REG_HOURS:
303  {
304  BOOLEAN Afternoon = FALSE;
305 
306  ChangeTime = TRUE;
307 
308  if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Data & 0x80))
309  {
310  Data &= ~0x80;
311  Afternoon = TRUE;
312  }
313 
314  CurrentTime.wHour = WRITE_CMOS_DATA(CmosMemory, Data);
315 
316  /* Convert to 24-hour format */
317  if (Afternoon) CurrentTime.wHour += 12;
318 
319  break;
320  }
321 
322  case CMOS_REG_ALARM_HRS:
323  {
324  BOOLEAN Afternoon = FALSE;
325 
326  if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Data & 0x80))
327  {
328  Data &= ~0x80;
329  Afternoon = TRUE;
330  }
331 
333 
334  /* Convert to 24-hour format */
335  if (Afternoon) CmosMemory.AlarmHour += 12;
336 
337  break;
338  }
339 
341  {
342  ChangeTime = TRUE;
343  /*
344  * The CMOS value is 1-based but the
345  * SetLocalTime API value is 0-based.
346  * Correct it.
347  */
348  Data -= 1;
349  CurrentTime.wDayOfWeek = WRITE_CMOS_DATA(CmosMemory, Data);
350  break;
351  }
352 
353  case CMOS_REG_DAY:
354  {
355  ChangeTime = TRUE;
356  CurrentTime.wDay = WRITE_CMOS_DATA(CmosMemory, Data);
357  break;
358  }
359 
360  case CMOS_REG_MONTH:
361  {
362  ChangeTime = TRUE;
363  CurrentTime.wMonth = WRITE_CMOS_DATA(CmosMemory, Data);
364  break;
365  }
366 
367  case CMOS_REG_YEAR:
368  {
369  ChangeTime = TRUE;
370 
371  /* Clear everything except the century */
372  CurrentTime.wYear = (CurrentTime.wYear / 100) * 100;
373  CurrentTime.wYear += WRITE_CMOS_DATA(CmosMemory, Data);
374  break;
375  }
376 
377  case CMOS_REG_CENTURY:
378  {
380  break;
381  }
382 
383  case CMOS_REG_STATUS_A:
384  {
385  CmosMemory.StatusRegA = Data & 0x7F; // Bit 7 is read-only
387  break;
388  }
389 
390  case CMOS_REG_STATUS_B:
391  {
392  CmosMemory.StatusRegB = Data;
393  break;
394  }
395 
396  case CMOS_REG_STATUS_C:
397  case CMOS_REG_STATUS_D:
398  // Status registers C and D are read-only
399  break;
400 
401  /* Is the following correct? */
404  {
405  /* Sync EMS and UMS */
408  break;
409  }
410 
411  /* Is the following correct? */
414  {
415  /* Sync EMS and UMS */
418  break;
419  }
420 
421  default:
422  {
424  }
425  }
426 
427  if (ChangeTime) SetLocalTime(&CurrentTime);
428 
429  /* Return to Status Register D */
431 }
432 
433 
434 /* PUBLIC FUNCTIONS ***********************************************************/
435 
437 {
438  return NmiEnabled;
439 }
440 
442 {
443  DWORD CmosSize = sizeof(CmosMemory);
444 
445  /* File must not be opened before */
447 
448  /* Clear the CMOS memory */
450 
451  /* Always open (and if needed, create) a RAM file with shared access */
452  SetLastError(0); // For debugging purposes
453  hCmosRam = CreateFileW(L"cmos.ram",
456  NULL,
457  OPEN_ALWAYS,
459  NULL);
460  DPRINT1("CMOS opening %s (Error: %u)\n", hCmosRam != INVALID_HANDLE_VALUE ? "succeeded" : "failed", GetLastError());
461 
463  {
464  BOOL Success;
465 
466  /* Attempt to fill the CMOS memory with the RAM file */
467  SetLastError(0); // For debugging purposes
468  Success = ReadFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize, NULL);
469  if (CmosSize != sizeof(CmosMemory))
470  {
471  /* Bad CMOS RAM file. Reinitialize the CMOS memory. */
472  DPRINT1("Invalid CMOS file, read bytes %u, expected bytes %u\n", CmosSize, sizeof(CmosMemory));
474  }
475  DPRINT1("CMOS loading %s (Error: %u)\n", Success ? "succeeded" : "failed", GetLastError());
477  }
478 
479  /* Overwrite some registers with default values */
480  CmosMemory.StatusRegA = CMOS_DEFAULT_STA;
481  CmosMemory.StatusRegB = CMOS_DEFAULT_STB;
482  CmosMemory.StatusRegC = 0x00;
483  CmosMemory.StatusRegD = CMOS_BATTERY_OK; // Our CMOS battery works perfectly forever.
484  CmosMemory.Diagnostics = 0x00; // Diagnostics must not find any errors.
485  CmosMemory.ShutdownStatus = 0x00;
487 
488  // HACK: For the moment, set the boot sequence to: 1-Floppy, 2-Hard Disk .
489  CmosMemory.Regs[CMOS_REG_SYSOP] |= (1 << 5);
490 
491  /* Memory settings */
492 
493  /*
494  * Conventional memory size is 640 kB,
495  * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM
496  * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
497  * for more information.
498  */
499  CmosMemory.BaseMemoryLow = LOBYTE(0x0280);
500  CmosMemory.BaseMemoryHigh = HIBYTE(0x0280);
501 
503  CmosMemory.ActualExtMemoryLow = LOBYTE((MAX_ADDRESS - 0x100000) / 1024);
505  CmosMemory.ActualExtMemoryHigh = HIBYTE((MAX_ADDRESS - 0x100000) / 1024);
506 
507  /* Register the I/O Ports */
510 
512  HZ_TO_NS(1),
513  RtcTimeUpdate);
515  HZ_TO_NS(1000),
517 }
518 
520 {
521  DWORD CmosSize = sizeof(CmosMemory);
522 
523  if (hCmosRam == INVALID_HANDLE_VALUE) return;
524 
527 
528  /* Flush the CMOS memory back to the RAM file and close it */
530  WriteFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize, NULL);
531 
534 }
535 
536 /* EOF */
BYTE Regs[0x40]
Definition: cmos.h:135
_In_opt_ ULONG _Out_ PULONG Value
Definition: rtlfuncs.h:2343
#define CMOS_STC_PF
Definition: cmos.h:33
BYTE StatusRegD
Definition: cmos.h:116
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
CPPORT Port[4]
Definition: headless.c:34
#define TRUE
Definition: types.h:120
BYTE Diagnostics
Definition: cmos.h:117
#define CloseHandle
Definition: compat.h:406
#define CMOS_ADDRESS_PORT
Definition: cmos.h:15
#define LOBYTE(W)
Definition: jmemdos.c:487
WORD wMonth
Definition: winbase.h:878
#define CMOS_STB_24HOUR
Definition: cmos.h:22
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:323
WORD wDayOfWeek
Definition: winbase.h:879
static HANDLE hCmosRam
Definition: cmos.c:25
#define HIBYTE(W)
Definition: jmemdos.c:486
static VOID WINAPI CmosWriteData(USHORT Port, BYTE Data)
Definition: cmos.c:264
VOID DisableHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:183
#define INVALID_HANDLE_VALUE
Definition: compat.h:399
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
BOOLEAN IsNmiEnabled(VOID)
Definition: cmos.c:436
#define FILE_SHARE_WRITE
Definition: nt_native.h:681
#define FASTCALL
Definition: nt_native.h:50
#define READ_CMOS_DATA(Cmos, Value)
Definition: cmos.h:47
static CMOS_REGISTERS SelectedRegister
Definition: cmos.c:29
#define CMOS_STC_IRQF
Definition: cmos.h:34
#define FILE_SHARE_READ
Definition: compat.h:125
DWORD WINAPI DECLSPEC_HOTPATCH SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod)
Definition: fileinfo.c:204
#define MAX_ADDRESS
VOID RegisterIoPort(USHORT Port, EMULATOR_INB_PROC InHandler, EMULATOR_OUTB_PROC OutHandler)
Definition: io.c:320
BYTE StatusRegC
Definition: cmos.h:115
BYTE ActualExtMemoryLow
Definition: cmos.h:130
WORD wYear
Definition: winbase.h:877
static VOID FASTCALL RtcTimeUpdate(ULONGLONG ElapsedTime)
Definition: cmos.c:73
static CMOS_MEMORY CmosMemory
Definition: cmos.c:26
unsigned int BOOL
Definition: ntddk_ex.h:94
VOID CmosCleanup(VOID)
Definition: cmos.c:519
#define GENERIC_WRITE
Definition: nt_native.h:90
WORD wMinute
Definition: winbase.h:882
unsigned char BOOLEAN
PHARDWARE_TIMER CreateHardwareTimer(ULONG Flags, ULONGLONG Delay, PHARDWARE_TIMER_PROC Callback)
Definition: clock.c:144
smooth NULL
Definition: ftsmooth.c:416
enum _CMOS_REGISTERS CMOS_REGISTERS
_In_ LPGUID _In_ PVOID Data
Definition: classpnp.h:778
static VOID RtcUpdatePeriodicTimer(VOID)
Definition: cmos.c:36
VOID WINAPI GetLocalTime(OUT LPSYSTEMTIME lpSystemTime)
Definition: time.c:286
static PHARDWARE_TIMER PeriodicTimer
Definition: cmos.c:32
#define CMOS_BATTERY_OK
Definition: cmos.h:18
BYTE ExtMemoryHigh
Definition: cmos.h:127
#define CMOS_DEFAULT_STA
Definition: cmos.h:37
#define CMOS_STC_UF
Definition: cmos.h:31
static BOOLEAN NmiEnabled
Definition: cmos.c:28
uint64_t ULONGLONG
Definition: typedefs.h:65
#define HARDWARE_TIMER_PRECISE
Definition: clock.h:17
#define WINAPI
Definition: msvc.h:6
#define CMOS_DATA_PORT
Definition: halp.h:15
#define HARDWARE_TIMER_ENABLED
Definition: clock.h:15
static VOID WINAPI CmosWriteAddress(USHORT Port, BYTE Data)
Definition: cmos.c:107
unsigned long DWORD
Definition: ntddk_ex.h:95
#define SetLastError(x)
Definition: compat.h:417
#define OPEN_ALWAYS
Definition: disk.h:70
#define CMOS_STB_INT_PERIODIC
Definition: cmos.h:27
BYTE EquipmentList
Definition: cmos.h:123
BYTE BaseMemoryLow
Definition: cmos.h:124
WORD wSecond
Definition: winbase.h:883
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel) ?(CompletionRoutine !=NULL) :TRUE)
#define FILE_ATTRIBUTE_NORMAL
Definition: compat.h:126
static const WCHAR L[]
Definition: oid.c:1250
VOID SetHardwareTimerDelay(PHARDWARE_TIMER Timer, ULONGLONG NewDelay)
Definition: clock.c:197
#define WRITE_CMOS_DATA(Cmos, Value)
Definition: cmos.h:44
unsigned char BYTE
Definition: mem.h:68
#define GENERIC_READ
Definition: compat.h:124
BYTE ShutdownStatus
Definition: cmos.h:118
static PHARDWARE_TIMER ClockTimer
Definition: cmos.c:31
#define CMOS_STB_INT_ON_UPDATE
Definition: cmos.h:25
WORD wDay
Definition: winbase.h:880
#define FILE_BEGIN
Definition: winbase.h:112
#define CMOS_STC_AF
Definition: cmos.h:32
#define CMOS_EQUIPMENT_LIST
Definition: cmos.h:41
#define CMOS_DISABLE_NMI
Definition: cmos.h:17
unsigned short USHORT
Definition: pedump.c:61
#define RTC_IRQ_NUMBER
Definition: cmos.h:14
WORD wHour
Definition: winbase.h:881
BYTE ExtMemoryLow
Definition: cmos.h:126
#define CMOS_DEFAULT_STB
Definition: cmos.h:38
#define DPRINT1
Definition: precomp.h:8
#define CreateFileW
Definition: compat.h:408
#define CMOS_STB_INT_ON_ALARM
Definition: cmos.h:26
#define HZ_TO_NS(Freq)
Definition: clock.h:20
VOID PicInterruptRequest(BYTE Number)
Definition: pic.c:192
#define UNIMPLEMENTED
Definition: debug.h:114
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:261
VOID EnableHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:161
BYTE BaseMemoryHigh
Definition: cmos.h:125
VOID CmosInitialize(VOID)
Definition: cmos.c:441
static VOID FASTCALL RtcPeriodicTick(ULONGLONG ElapsedTime)
Definition: cmos.c:55
BYTE ActualExtMemoryHigh
Definition: cmos.h:131
BOOL WINAPI SetLocalTime(IN CONST SYSTEMTIME *lpSystemTime)
Definition: time.c:356
static BYTE WINAPI CmosReadData(USHORT Port)
Definition: cmos.c:129
BOOL WINAPI ReadFile(IN HANDLE hFile, IN LPVOID lpBuffer, IN DWORD nNumberOfBytesToRead, OUT LPDWORD lpNumberOfBytesRead OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:123
VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:210