ReactOS  0.4.15-dev-5126-g3bb451b
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 
25 #define CMOS_RAM_FILE "cmos.ram"
26 
29 
32 
35 
36 /* PRIVATE FUNCTIONS **********************************************************/
37 
39 {
40  BYTE RateSelect = CmosMemory.StatusRegA & 0x0F;
41 
42  if (RateSelect == 0)
43  {
44  /* No periodic interrupt */
46  return;
47  }
48 
49  /* 1 and 2 act like 8 and 9 */
50  if (RateSelect <= 2) RateSelect += 7;
51 
52  SetHardwareTimerDelay(PeriodicTimer, HZ_TO_NS(1 << (16 - RateSelect)));
53  // FIXME: This call keeps EnableCount increasing without compensating it!
55 }
56 
58 {
59  UNREFERENCED_PARAMETER(ElapsedTime);
60 
61  /* Set PF */
63 
64  /* Check if there should be an interrupt on a periodic timer tick */
65  if (CmosMemory.StatusRegB & CMOS_STB_INT_PERIODIC)
66  {
68 
69  /* Interrupt! */
71  }
72 }
73 
74 /* Should be called every second */
75 static VOID FASTCALL RtcTimeUpdate(ULONGLONG ElapsedTime)
76 {
77  SYSTEMTIME CurrentTime;
78 
79  UNREFERENCED_PARAMETER(ElapsedTime);
80 
81  /* Get the current time */
82  GetLocalTime(&CurrentTime);
83 
84  /* Set UF */
86 
87  /* Check if the time matches the alarm time */
88  if ((CurrentTime.wHour == CmosMemory.AlarmHour ) &&
89  (CurrentTime.wMinute == CmosMemory.AlarmMinute) &&
90  (CurrentTime.wSecond == CmosMemory.AlarmSecond))
91  {
92  /* Set the alarm flag */
94 
95  /* Set IRQF if there should be an interrupt */
97  }
98 
99  /* Check if there should be an interrupt on update */
101 
103  {
104  /* Interrupt! */
106  }
107 }
108 
110 {
112 
113  /* Update the NMI enabled flag */
115 
116  /* Get the register number */
118 
119  if (Data < CMOS_REG_MAX)
120  {
121  /* Select the new register */
123  }
124  else
125  {
126  /* Default to Status Register D */
128  }
129 }
130 
132 {
133  BYTE Value;
134  SYSTEMTIME CurrentTime;
135 
137 
138  /* Get the current time */
139  GetLocalTime(&CurrentTime);
140 
141  switch (SelectedRegister)
142  {
143  case CMOS_REG_SECONDS:
144  {
145  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wSecond);
146  break;
147  }
148 
149  case CMOS_REG_ALARM_SEC:
150  {
151  Value = READ_CMOS_DATA(CmosMemory, CmosMemory.AlarmSecond);
152  break;
153  }
154 
155  case CMOS_REG_MINUTES:
156  {
157  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wMinute);
158  break;
159  }
160 
161  case CMOS_REG_ALARM_MIN:
162  {
163  Value = READ_CMOS_DATA(CmosMemory, CmosMemory.AlarmMinute);
164  break;
165  }
166 
167  case CMOS_REG_HOURS:
168  {
169  BOOLEAN Afternoon = FALSE;
170  Value = CurrentTime.wHour;
171 
172  if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
173  {
174  Value -= 12;
175  Afternoon = TRUE;
176  }
177 
179 
180  /* Convert to 12-hour */
181  if (Afternoon) Value |= 0x80;
182 
183  break;
184  }
185 
186  case CMOS_REG_ALARM_HRS:
187  {
188  BOOLEAN Afternoon = FALSE;
189  Value = CmosMemory.AlarmHour;
190 
191  if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
192  {
193  Value -= 12;
194  Afternoon = TRUE;
195  }
196 
198 
199  /* Convert to 12-hour */
200  if (Afternoon) Value |= 0x80;
201 
202  break;
203  }
204 
206  {
207  /*
208  * The CMOS value is 1-based but the
209  * GetLocalTime API value is 0-based.
210  * Correct it.
211  */
212  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wDayOfWeek + 1);
213  break;
214  }
215 
216  case CMOS_REG_DAY:
217  {
218  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wDay);
219  break;
220  }
221 
222  case CMOS_REG_MONTH:
223  {
224  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wMonth);
225  break;
226  }
227 
228  case CMOS_REG_YEAR:
229  {
230  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wYear % 100);
231  break;
232  }
233 
234  case CMOS_REG_CENTURY:
235  {
236  Value = READ_CMOS_DATA(CmosMemory, CurrentTime.wYear / 100 + 19);
237  break;
238  }
239 
240  case CMOS_REG_STATUS_C:
241  {
242  /* Return the old status register value, then clear it */
244  CmosMemory.StatusRegC = 0x00;
245  break;
246  }
247 
248  case CMOS_REG_STATUS_A:
249  case CMOS_REG_STATUS_B:
250  case CMOS_REG_STATUS_D:
253  default:
254  {
255  // ASSERT(SelectedRegister < CMOS_REG_MAX);
257  }
258  }
259 
260  /* Return to Status Register D */
262 
263  return Value;
264 }
265 
267 {
268  BOOLEAN ChangeTime = FALSE;
269  SYSTEMTIME CurrentTime;
270 
272 
273  /* Get the current time */
274  GetLocalTime(&CurrentTime);
275 
276  switch (SelectedRegister)
277  {
278  case CMOS_REG_SECONDS:
279  {
280  ChangeTime = TRUE;
281  CurrentTime.wSecond = WRITE_CMOS_DATA(CmosMemory, Data);
282  break;
283  }
284 
285  case CMOS_REG_ALARM_SEC:
286  {
287  CmosMemory.AlarmSecond = WRITE_CMOS_DATA(CmosMemory, Data);
288  break;
289  }
290 
291  case CMOS_REG_MINUTES:
292  {
293  ChangeTime = TRUE;
294  CurrentTime.wMinute = WRITE_CMOS_DATA(CmosMemory, Data);
295  break;
296  }
297 
298  case CMOS_REG_ALARM_MIN:
299  {
300  CmosMemory.AlarmMinute = WRITE_CMOS_DATA(CmosMemory, Data);
301  break;
302  }
303 
304  case CMOS_REG_HOURS:
305  {
306  BOOLEAN Afternoon = FALSE;
307 
308  ChangeTime = TRUE;
309 
310  if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Data & 0x80))
311  {
312  Data &= ~0x80;
313  Afternoon = TRUE;
314  }
315 
316  CurrentTime.wHour = WRITE_CMOS_DATA(CmosMemory, Data);
317 
318  /* Convert to 24-hour format */
319  if (Afternoon) CurrentTime.wHour += 12;
320 
321  break;
322  }
323 
324  case CMOS_REG_ALARM_HRS:
325  {
326  BOOLEAN Afternoon = FALSE;
327 
328  if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Data & 0x80))
329  {
330  Data &= ~0x80;
331  Afternoon = TRUE;
332  }
333 
335 
336  /* Convert to 24-hour format */
337  if (Afternoon) CmosMemory.AlarmHour += 12;
338 
339  break;
340  }
341 
343  {
344  ChangeTime = TRUE;
345  /*
346  * The CMOS value is 1-based but the
347  * SetLocalTime API value is 0-based.
348  * Correct it.
349  */
350  Data -= 1;
351  CurrentTime.wDayOfWeek = WRITE_CMOS_DATA(CmosMemory, Data);
352  break;
353  }
354 
355  case CMOS_REG_DAY:
356  {
357  ChangeTime = TRUE;
358  CurrentTime.wDay = WRITE_CMOS_DATA(CmosMemory, Data);
359  break;
360  }
361 
362  case CMOS_REG_MONTH:
363  {
364  ChangeTime = TRUE;
365  CurrentTime.wMonth = WRITE_CMOS_DATA(CmosMemory, Data);
366  break;
367  }
368 
369  case CMOS_REG_YEAR:
370  {
371  ChangeTime = TRUE;
372 
373  /* Clear everything except the century */
374  CurrentTime.wYear = (CurrentTime.wYear / 100) * 100;
375  CurrentTime.wYear += WRITE_CMOS_DATA(CmosMemory, Data);
376  break;
377  }
378 
379  case CMOS_REG_CENTURY:
380  {
382  break;
383  }
384 
385  case CMOS_REG_STATUS_A:
386  {
387  CmosMemory.StatusRegA = Data & 0x7F; // Bit 7 is read-only
389  break;
390  }
391 
392  case CMOS_REG_STATUS_B:
393  {
394  CmosMemory.StatusRegB = Data;
395  break;
396  }
397 
398  case CMOS_REG_STATUS_C:
399  case CMOS_REG_STATUS_D:
400  // Status registers C and D are read-only
401  break;
402 
403  /* Is the following correct? */
406  {
407  /* Sync EMS and UMS */
410  break;
411  }
412 
413  /* Is the following correct? */
416  {
417  /* Sync EMS and UMS */
420  break;
421  }
422 
423  default:
424  {
426  }
427  }
428 
429  if (ChangeTime) SetLocalTime(&CurrentTime);
430 
431  /* Return to Status Register D */
433 }
434 
435 
436 /* PUBLIC FUNCTIONS ***********************************************************/
437 
439 {
440  return NmiEnabled;
441 }
442 
443 static inline BOOL
446  _In_ PVOID Buffer,
449 {
450  BOOL Success;
451  ULONG Written;
452 
455  if (BytesWritten)
456  *BytesWritten = (Success ? Written : 0);
457  return Success;
458 }
459 
461 {
462  BOOL Success;
463  WCHAR CmosPath[_countof(NtVdmPath) + _countof("\\" CMOS_RAM_FILE)];
464 
465  /* CMOS file must not be opened before */
467 
468  /* Always open (and if needed, create) a RAM file with shared access */
470  sizeof(CmosPath),
471  L"%s\\" L(CMOS_RAM_FILE),
472  NtVdmPath));
473  if (!Success)
474  DPRINT1("Could not create CMOS file path!\n");
475 
476  if (Success)
477  {
479  hCmosRam = CreateFileW(CmosPath,
482  NULL,
483  OPEN_ALWAYS,
485  NULL);
487  if (!Success)
488  DPRINT1("CMOS opening failed (Error: %u)\n", GetLastError());
489  }
490 
491  /* Clear the CMOS memory */
493 
494  /* Load the file only if it already existed and was opened, not newly created */
495  if (Success)
496  {
497  if ((GetLastError() == ERROR_ALREADY_EXISTS) /* || (GetLastError() == ERROR_FILE_EXISTS) */)
498  {
499  /* Attempt to load the CMOS memory from the RAM file */
500  DWORD CmosSize = sizeof(CmosMemory);
501  Success = ReadFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize, NULL);
502  if (!Success)
503  {
504  DPRINT1("CMOS loading failed (Error: %u)\n", GetLastError());
505  }
506  else if (CmosSize != sizeof(CmosMemory))
507  {
508  /* Invalid CMOS RAM file; reinitialize the CMOS memory */
509  DPRINT1("Invalid CMOS file, read %u bytes, expected %u bytes\n",
510  CmosSize, sizeof(CmosMemory));
511  Success = FALSE;
512  }
513  if (!Success)
514  {
515  /* Reset the CMOS memory and its RAM file */
518  }
519  }
520  else
521  {
522  /* Reset the CMOS RAM file */
524  }
526  }
527 
528  /* Overwrite some registers with default values */
529  CmosMemory.StatusRegA = CMOS_DEFAULT_STA;
530  CmosMemory.StatusRegB = CMOS_DEFAULT_STB;
531  CmosMemory.StatusRegC = 0x00;
532  CmosMemory.StatusRegD = CMOS_BATTERY_OK; // Our CMOS battery works perfectly forever.
533  CmosMemory.Diagnostics = 0x00; // Diagnostics must not find any errors.
534  CmosMemory.ShutdownStatus = 0x00;
536 
537  // HACK: For the moment, set the boot sequence to: 1-Floppy, 2-Hard Disk .
538  CmosMemory.Regs[CMOS_REG_SYSOP] |= (1 << 5);
539 
540  /* Memory settings */
541 
542  /*
543  * Conventional memory size is 640 kB,
544  * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM
545  * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
546  * for more information.
547  */
548  CmosMemory.BaseMemoryLow = LOBYTE(0x0280);
549  CmosMemory.BaseMemoryHigh = HIBYTE(0x0280);
550 
552  CmosMemory.ActualExtMemoryLow = LOBYTE((MAX_ADDRESS - 0x100000) / 1024);
554  CmosMemory.ActualExtMemoryHigh = HIBYTE((MAX_ADDRESS - 0x100000) / 1024);
555 
556  /* Register the I/O Ports */
559 
561  HZ_TO_NS(1),
562  RtcTimeUpdate);
564  HZ_TO_NS(1000),
566 }
567 
569 {
572 
574  {
575  /* Flush the CMOS memory back to the RAM file and close it */
576  BOOL Success;
577  DWORD CmosSize = sizeof(CmosMemory);
578 
579  Success = CmosWriteFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize);
580  if (!Success || (CmosSize != sizeof(CmosMemory)))
581  {
582  DPRINT1("CMOS saving failed (Error: %u), written %u bytes, expected %u bytes\n",
583  GetLastError(), CmosSize, sizeof(CmosMemory));
584  }
585 
588  }
589 }
590 
591 /* EOF */
BYTE Regs[0x40]
Definition: cmos.h:135
_Must_inspect_result_ _In_ WDFIOTARGET _In_opt_ WDFREQUEST _In_opt_ PWDF_MEMORY_DESCRIPTOR _In_opt_ PLONGLONG _In_opt_ PWDF_REQUEST_SEND_OPTIONS _Out_opt_ PULONG_PTR BytesWritten
Definition: wdfiotarget.h:949
#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:35
BYTE Diagnostics
Definition: cmos.h:117
#define CloseHandle
Definition: compat.h:598
#define CMOS_ADDRESS_PORT
Definition: cmos.h:15
#define ERROR_SUCCESS
Definition: deptool.c:10
#define LOBYTE(W)
Definition: jmemdos.c:487
WORD wMonth
Definition: winbase.h:903
#define CMOS_STB_24HOUR
Definition: cmos.h:22
#define TRUE
Definition: types.h:120
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
WORD wDayOfWeek
Definition: winbase.h:904
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_DEVICE_PROPERTY_DATA _In_ DEVPROPTYPE _In_ ULONG _In_opt_ PVOID Data
Definition: wdfdevice.h:4527
static HANDLE hCmosRam
Definition: cmos.c:27
#define HIBYTE(W)
Definition: jmemdos.c:486
static VOID WINAPI CmosWriteData(USHORT Port, BYTE Data)
Definition: cmos.c:266
VOID DisableHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:183
#define INVALID_HANDLE_VALUE
Definition: compat.h:590
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1040
BOOLEAN IsNmiEnabled(VOID)
Definition: cmos.c:438
#define FILE_BEGIN
Definition: compat.h:620
WCHAR NtVdmPath[MAX_PATH]
Definition: ntvdm.c:35
#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:31
#define CMOS_STC_IRQF
Definition: cmos.h:34
#define FILE_SHARE_READ
Definition: compat.h:136
#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:902
static VOID FASTCALL RtcTimeUpdate(ULONGLONG ElapsedTime)
Definition: cmos.c:75
static CMOS_MEMORY CmosMemory
Definition: cmos.c:28
#define L(x)
Definition: ntvdm.h:50
static BOOL CmosWriteFile(_In_ HANDLE FileHandle, _In_ PVOID Buffer, _In_ ULONG BufferSize, _Out_opt_ PULONG BytesWritten)
Definition: cmos.c:444
#define FALSE
Definition: types.h:117
unsigned int BOOL
Definition: ntddk_ex.h:94
VOID CmosCleanup(VOID)
Definition: cmos.c:568
#define GENERIC_WRITE
Definition: nt_native.h:90
WORD wMinute
Definition: winbase.h:907
unsigned char BOOLEAN
PHARDWARE_TIMER CreateHardwareTimer(ULONG Flags, ULONGLONG Delay, PHARDWARE_TIMER_PROC Callback)
Definition: clock.c:144
enum _CMOS_REGISTERS CMOS_REGISTERS
#define _In_
Definition: ms_sal.h:308
static VOID RtcUpdatePeriodicTimer(VOID)
Definition: cmos.c:38
Definition: bufpool.h:45
VOID WINAPI GetLocalTime(OUT LPSYSTEMTIME lpSystemTime)
Definition: time.c:286
static PHARDWARE_TIMER PeriodicTimer
Definition: cmos.c:34
#define CMOS_BATTERY_OK
Definition: cmos.h:18
BYTE ExtMemoryHigh
Definition: cmos.h:127
_Must_inspect_result_ _In_ WDFKEY _In_ PCUNICODE_STRING _Out_opt_ PUSHORT _Inout_opt_ PUNICODE_STRING Value
Definition: wdfregistry.h:406
#define CMOS_DEFAULT_STA
Definition: cmos.h:37
#define ASSERT(a)
Definition: mode.c:44
#define CMOS_STC_UF
Definition: cmos.h:31
static BOOLEAN NmiEnabled
Definition: cmos.c:30
__wchar_t WCHAR
Definition: xmlstorage.h:180
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
NTSTRSAFEVAPI RtlStringCbPrintfW(_Out_writes_bytes_(cbDest) _Always_(_Post_z_) NTSTRSAFE_PWSTR pszDest, _In_ size_t cbDest, _In_ _Printf_format_string_ NTSTRSAFE_PCWSTR pszFormat,...)
Definition: ntstrsafe.h:1173
uint64_t ULONGLONG
Definition: typedefs.h:67
#define _countof(array)
Definition: sndvol32.h:68
#define HARDWARE_TIMER_PRECISE
Definition: clock.h:17
#define WINAPI
Definition: msvc.h:6
#define HARDWARE_TIMER_ENABLED
Definition: clock.h:15
static VOID WINAPI CmosWriteAddress(USHORT Port, BYTE Data)
Definition: cmos.c:109
unsigned long DWORD
Definition: ntddk_ex.h:95
#define SetLastError(x)
Definition: compat.h:611
#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:908
#define FILE_ATTRIBUTE_NORMAL
Definition: compat.h:137
VOID SetHardwareTimerDelay(PHARDWARE_TIMER Timer, ULONGLONG NewDelay)
Definition: clock.c:197
#define WRITE_CMOS_DATA(Cmos, Value)
Definition: cmos.h:44
#define GENERIC_READ
Definition: compat.h:135
BYTE ShutdownStatus
Definition: cmos.h:118
static PHARDWARE_TIMER ClockTimer
Definition: cmos.c:33
unsigned char BYTE
Definition: xxhash.c:193
#define CMOS_STB_INT_ON_UPDATE
Definition: cmos.h:25
WORD wDay
Definition: winbase.h:905
_Must_inspect_result_ _In_opt_ PFLT_INSTANCE _Out_ PHANDLE FileHandle
Definition: fltkernel.h:1230
#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
#define CMOS_DATA_PORT
Definition: halhw.h:12
WORD wHour
Definition: winbase.h:906
#define ReadFile(a, b, c, d, e)
Definition: compat.h:601
#define _Out_opt_
Definition: ms_sal.h:346
BYTE ExtMemoryLow
Definition: cmos.h:126
unsigned int * PULONG
Definition: retypes.h:1
#define NULL
Definition: types.h:112
#define CMOS_DEFAULT_STB
Definition: cmos.h:38
#define DPRINT1
Definition: precomp.h:8
#define CreateFileW
Definition: compat.h:600
#define CMOS_STB_INT_ON_ALARM
Definition: cmos.h:26
#define CMOS_RAM_FILE
Definition: cmos.c:25
#define HZ_TO_NS(Freq)
Definition: clock.h:20
VOID PicInterruptRequest(BYTE Number)
Definition: pic.c:192
unsigned int ULONG
Definition: retypes.h:1
#define UNIMPLEMENTED
Definition: debug.h:115
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
VOID EnableHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:161
BYTE BaseMemoryHigh
Definition: cmos.h:125
VOID CmosInitialize(VOID)
Definition: cmos.c:460
#define ERROR_ALREADY_EXISTS
Definition: disk.h:80
static VOID FASTCALL RtcPeriodicTick(ULONGLONG ElapsedTime)
Definition: cmos.c:57
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:131
VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer)
Definition: clock.c:210
#define SetFilePointer
Definition: compat.h:602
_In_ WDFMEMORY _Out_opt_ size_t * BufferSize
Definition: wdfmemory.h:251