- BIOS: Reorganize a bit the header, and close the input thread before closing the input handle (and not after).
- INT32: Fix a comment.
- CMOS: Put CMOS data into a structure called CMOS_MEMORY, introduce READ/WRITE_CMOS_DATA macros for simplifying code. Save CMOS memory into a file (à la Windows' NTVDM), which is loaded at startup.

svn path=/branches/ntvdm/; revision=60913
This commit is contained in:
Hermès Bélusca-Maïto 2013-11-10 13:12:02 +00:00
parent fda5745543
commit bf2731fb9f
7 changed files with 221 additions and 147 deletions

View file

@ -568,6 +568,9 @@ BOOLEAN BiosInitialize(VOID)
VOID BiosCleanup(VOID)
{
/* Close the input thread handle */
if (InputThread != NULL) CloseHandle(InputThread);
/* Restore the old screen buffer */
SetConsoleActiveScreenBuffer(BiosConsoleOutput);
@ -577,9 +580,6 @@ VOID BiosCleanup(VOID)
/* Close the console handles */
if (BiosConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleOutput);
if (BiosConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleInput);
/* Close the input thread handle */
if (InputThread != NULL) CloseHandle(InputThread);
}
WORD BiosPeekCharacter(VOID)

View file

@ -15,8 +15,8 @@
/* DEFINES ********************************************************************/
#define ROM_AREA_START 0xE0000
#define ROM_AREA_END 0xFFFFF
#define ROM_AREA_START 0xE0000
#define ROM_AREA_END 0xFFFFF
#define BDA_SEGMENT 0x40
#define BIOS_SEGMENT 0xF000
@ -34,13 +34,15 @@
#define CONSOLE_FONT_HEIGHT 8
#define BIOS_KBD_BUFFER_SIZE 16
#define BIOS_EQUIPMENT_LIST 0x2C // HACK: Disable FPU for now
#define BIOS_DEFAULT_VIDEO_MODE 0x03
#define BIOS_MAX_PAGES 8
#define BIOS_PAGE_SIZE 0x1000
#define BIOS_MAX_VIDEO_MODE 0x13
#define DEFAULT_ATTRIBUTE 0x07
#define GRAPHICS_VIDEO_SEG 0xA000
#define TEXT_VIDEO_SEG 0xB800
#define DEFAULT_ATTRIBUTE 0x07
#define GRAPHICS_VIDEO_SEG 0xA000
#define TEXT_VIDEO_SEG 0xB800
#define BDA_KBDFLAG_RSHIFT (1 << 0)
#define BDA_KBDFLAG_LSHIFT (1 << 1)
@ -67,8 +69,6 @@ enum
SCROLL_DIRECTION_RIGHT
};
#pragma pack(push, 1)
/*
* BIOS Data Area at 0040:XXXX
*
@ -76,6 +76,7 @@ enum
* and: http://www.bioscentral.com/misc/bda.htm
* for more information.
*/
#pragma pack(push, 1)
typedef struct
{
WORD SerialPorts[4]; // 0x00
@ -142,11 +143,10 @@ typedef struct
BYTE Reserved17[15]; // 0x121
BYTE Reserved18[3]; // 0x130
} BIOS_DATA_AREA, *PBIOS_DATA_AREA;
#pragma pack(pop)
C_ASSERT(sizeof(BIOS_DATA_AREA) == 0x133);
#pragma pack(pop)
/* FUNCTIONS ******************************************************************/
extern PBIOS_DATA_AREA Bda;

View file

@ -15,11 +15,10 @@
/* PRIVATE VARIABLES **********************************************************/
static HANDLE hCmosRam = INVALID_HANDLE_VALUE;
static CMOS_MEMORY CmosMemory;
static BOOLEAN NmiEnabled = TRUE;
static BYTE StatusRegA = CMOS_DEFAULT_STA;
static BYTE StatusRegB = CMOS_DEFAULT_STB;
static BYTE StatusRegC = 0;
static BYTE AlarmHour, AlarmMinute, AlarmSecond;
static CMOS_REGISTERS SelectedRegister = CMOS_REG_STATUS_D;
/* PUBLIC FUNCTIONS ***********************************************************/
@ -59,45 +58,29 @@ BYTE CmosReadData(VOID)
switch (SelectedRegister)
{
case CMOS_REG_SECONDS:
{
return (StatusRegB & CMOS_STB_BINARY)
? CurrentTime.wSecond
: BINARY_TO_BCD(CurrentTime.wSecond);
}
return READ_CMOS_DATA(CmosMemory, CurrentTime.wSecond);
case CMOS_REG_ALARM_SEC:
{
return (StatusRegB & CMOS_STB_BINARY)
? AlarmSecond
: BINARY_TO_BCD(AlarmSecond);
}
return READ_CMOS_DATA(CmosMemory, CmosMemory.AlarmSecond);
case CMOS_REG_MINUTES:
{
return (StatusRegB & CMOS_STB_BINARY)
? CurrentTime.wMinute
: BINARY_TO_BCD(CurrentTime.wMinute);
}
return READ_CMOS_DATA(CmosMemory, CurrentTime.wMinute);
case CMOS_REG_ALARM_MIN:
{
return (StatusRegB & CMOS_STB_BINARY)
? AlarmMinute
: BINARY_TO_BCD(AlarmMinute);
}
return READ_CMOS_DATA(CmosMemory, CmosMemory.AlarmMinute);
case CMOS_REG_HOURS:
{
BOOLEAN Afternoon = FALSE;
BYTE Value = CurrentTime.wHour;
if (!(StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
{
Value -= 12;
Afternoon = TRUE;
}
if (!(StatusRegB & CMOS_STB_BINARY)) Value = BINARY_TO_BCD(Value);
Value = READ_CMOS_DATA(CmosMemory, Value);
/* Convert to 12-hour */
if (Afternoon) Value |= 0x80;
@ -108,15 +91,15 @@ BYTE CmosReadData(VOID)
case CMOS_REG_ALARM_HRS:
{
BOOLEAN Afternoon = FALSE;
BYTE Value = AlarmHour;
BYTE Value = CmosMemory.AlarmHour;
if (!(StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value >= 12))
{
Value -= 12;
Afternoon = TRUE;
}
if (!(StatusRegB & CMOS_STB_BINARY)) Value = BINARY_TO_BCD(Value);
Value = READ_CMOS_DATA(CmosMemory, Value);
/* Convert to 12-hour */
if (Afternoon) Value |= 0x80;
@ -125,70 +108,42 @@ BYTE CmosReadData(VOID)
}
case CMOS_REG_DAY_OF_WEEK:
{
return (StatusRegB & CMOS_STB_BINARY)
? CurrentTime.wDayOfWeek
: BINARY_TO_BCD(CurrentTime.wDayOfWeek);
}
/*
* The CMOS value is 1-based but the
* GetLocalTime API value is 0-based.
* Correct it.
*/
return READ_CMOS_DATA(CmosMemory, CurrentTime.wDayOfWeek + 1);
case CMOS_REG_DAY:
{
return (StatusRegB & CMOS_STB_BINARY)
? CurrentTime.wDay
:BINARY_TO_BCD(CurrentTime.wDay);
}
return READ_CMOS_DATA(CmosMemory, CurrentTime.wDay);
case CMOS_REG_MONTH:
{
return (StatusRegB & CMOS_STB_BINARY)
? CurrentTime.wMonth
: BINARY_TO_BCD(CurrentTime.wMonth);
}
return READ_CMOS_DATA(CmosMemory, CurrentTime.wMonth);
case CMOS_REG_YEAR:
{
return (StatusRegB & CMOS_STB_BINARY)
? (CurrentTime.wYear % 100)
: BINARY_TO_BCD(CurrentTime.wYear % 100);
}
case CMOS_REG_STATUS_A:
{
return StatusRegA;
}
case CMOS_REG_STATUS_B:
{
return StatusRegB;
}
return READ_CMOS_DATA(CmosMemory, CurrentTime.wYear % 100);
case CMOS_REG_STATUS_C:
{
BYTE Value = StatusRegC;
BYTE Value = CmosMemory.StatusRegC;
/* Clear status register C */
StatusRegC = 0;
CmosMemory.StatusRegC = 0x00;
/* Return the old value */
return Value;
}
case CMOS_REG_STATUS_A:
case CMOS_REG_STATUS_B:
case CMOS_REG_STATUS_D:
{
/* Our CMOS battery works perfectly forever */
return CMOS_BATTERY_OK;
}
case CMOS_REG_DIAGNOSTICS:
{
/* Diagnostics found no errors */
return 0;
}
case CMOS_REG_SHUTDOWN_STATUS:
default:
{
/* Read ignored */
return 0;
// ASSERT(SelectedRegister < CMOS_REG_MAX);
return CmosMemory.Regs[SelectedRegister];
}
}
@ -209,38 +164,26 @@ VOID CmosWriteData(BYTE Value)
case CMOS_REG_SECONDS:
{
ChangeTime = TRUE;
CurrentTime.wSecond = (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
CurrentTime.wSecond = WRITE_CMOS_DATA(CmosMemory, Value);
break;
}
case CMOS_REG_ALARM_SEC:
{
AlarmSecond = (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
CmosMemory.AlarmSecond = WRITE_CMOS_DATA(CmosMemory, Value);
break;
}
case CMOS_REG_MINUTES:
{
ChangeTime = TRUE;
CurrentTime.wMinute = (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
CurrentTime.wMinute = WRITE_CMOS_DATA(CmosMemory, Value);
break;
}
case CMOS_REG_ALARM_MIN:
{
AlarmMinute = (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
CmosMemory.AlarmMinute = WRITE_CMOS_DATA(CmosMemory, Value);
break;
}
@ -250,15 +193,13 @@ VOID CmosWriteData(BYTE Value)
ChangeTime = TRUE;
if (!(StatusRegB & CMOS_STB_24HOUR) && (Value & 0x80))
if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value & 0x80))
{
Value &= ~0x80;
Afternoon = TRUE;
}
CurrentTime.wHour = (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
CurrentTime.wHour = WRITE_CMOS_DATA(CmosMemory, Value);
/* Convert to 24-hour format */
if (Afternoon) CurrentTime.wHour += 12;
@ -270,18 +211,16 @@ VOID CmosWriteData(BYTE Value)
{
BOOLEAN Afternoon = FALSE;
if (!(StatusRegB & CMOS_STB_24HOUR) && (Value & 0x80))
if (!(CmosMemory.StatusRegB & CMOS_STB_24HOUR) && (Value & 0x80))
{
Value &= ~0x80;
Afternoon = TRUE;
}
AlarmHour = (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
CmosMemory.AlarmHour = WRITE_CMOS_DATA(CmosMemory, Value);
/* Convert to 24-hour format */
if (Afternoon) AlarmHour += 12;
if (Afternoon) CmosMemory.AlarmHour += 12;
break;
}
@ -289,30 +228,27 @@ VOID CmosWriteData(BYTE Value)
case CMOS_REG_DAY_OF_WEEK:
{
ChangeTime = TRUE;
CurrentTime.wDayOfWeek = (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
/*
* The CMOS value is 1-based but the
* SetLocalTime API value is 0-based.
* Correct it.
*/
Value -= 1;
CurrentTime.wDayOfWeek = WRITE_CMOS_DATA(CmosMemory, Value);
break;
}
case CMOS_REG_DAY:
{
ChangeTime = TRUE;
CurrentTime.wDay = (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
CurrentTime.wDay = WRITE_CMOS_DATA(CmosMemory, Value);
break;
}
case CMOS_REG_MONTH:
{
ChangeTime = TRUE;
CurrentTime.wMonth = (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
CurrentTime.wMonth = WRITE_CMOS_DATA(CmosMemory, Value);
break;
}
@ -323,28 +259,30 @@ VOID CmosWriteData(BYTE Value)
/* Clear everything except the century */
CurrentTime.wYear = (CurrentTime.wYear / 100) * 100;
CurrentTime.wYear += (StatusRegB & CMOS_STB_BINARY)
? Value
: BCD_TO_BINARY(Value);
CurrentTime.wYear += WRITE_CMOS_DATA(CmosMemory, Value);
break;
}
case CMOS_REG_STATUS_A:
{
StatusRegA = Value;
CmosMemory.StatusRegA = Value & 0x7F; // Bit 7 is read-only
break;
}
case CMOS_REG_STATUS_B:
{
StatusRegB = Value;
CmosMemory.StatusRegB = Value;
break;
}
case CMOS_REG_STATUS_C:
case CMOS_REG_STATUS_D:
// Status registers C and D are read-only
break;
default:
{
/* Write ignored */
CmosMemory.Regs[SelectedRegister] = Value;
}
}
@ -356,7 +294,7 @@ VOID CmosWriteData(BYTE Value)
DWORD RtcGetTicksPerSecond(VOID)
{
BYTE RateSelect = StatusRegB & 0x0F;
BYTE RateSelect = CmosMemory.StatusRegB & 0x0F;
if (RateSelect == 0)
{
@ -373,12 +311,12 @@ DWORD RtcGetTicksPerSecond(VOID)
VOID RtcPeriodicTick(VOID)
{
/* Set PF */
StatusRegC |= CMOS_STC_PF;
CmosMemory.StatusRegC |= CMOS_STC_PF;
/* Check if there should be an interrupt on a periodic timer tick */
if (StatusRegB & CMOS_STB_INT_PERIODIC)
if (CmosMemory.StatusRegB & CMOS_STB_INT_PERIODIC)
{
StatusRegC |= CMOS_STC_IRQF;
CmosMemory.StatusRegC |= CMOS_STC_IRQF;
/* Interrupt! */
PicInterruptRequest(RTC_IRQ_NUMBER);
@ -394,26 +332,91 @@ VOID RtcTimeUpdate(VOID)
GetLocalTime(&CurrentTime);
/* Set UF */
StatusRegC |= CMOS_STC_UF;
CmosMemory.StatusRegC |= CMOS_STC_UF;
/* Check if the time matches the alarm time */
if ((CurrentTime.wHour == AlarmHour)
&& (CurrentTime.wMinute == AlarmMinute)
&& (CurrentTime.wSecond == AlarmSecond))
if ((CurrentTime.wHour == CmosMemory.AlarmHour ) &&
(CurrentTime.wMinute == CmosMemory.AlarmMinute) &&
(CurrentTime.wSecond == CmosMemory.AlarmSecond))
{
/* Set the alarm flag */
StatusRegC |= CMOS_STC_AF;
CmosMemory.StatusRegC |= CMOS_STC_AF;
/* Set IRQF if there should be an interrupt */
if (StatusRegB & CMOS_STB_INT_ON_ALARM) StatusRegC |= CMOS_STC_IRQF;
if (CmosMemory.StatusRegB & CMOS_STB_INT_ON_ALARM) CmosMemory.StatusRegC |= CMOS_STC_IRQF;
}
/* Check if there should be an interrupt on update */
if (StatusRegB & CMOS_STB_INT_ON_UPDATE) StatusRegC |= CMOS_STC_IRQF;
if (CmosMemory.StatusRegB & CMOS_STB_INT_ON_UPDATE) CmosMemory.StatusRegC |= CMOS_STC_IRQF;
if (StatusRegC & CMOS_STC_IRQF)
if (CmosMemory.StatusRegC & CMOS_STC_IRQF)
{
/* Interrupt! */
PicInterruptRequest(RTC_IRQ_NUMBER);
}
}
BOOLEAN CmosInitialize(VOID)
{
DWORD CmosSize = sizeof(CmosMemory);
/* File must not be opened before */
ASSERT(hCmosRam == INVALID_HANDLE_VALUE);
/* Clear the CMOS memory */
ZeroMemory(&CmosMemory, sizeof(CmosMemory));
/* Always open (and if needed, create) a RAM file with exclusive access */
SetLastError(0); // For debugging purposes
hCmosRam = CreateFileW(L"cmos.ram",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
DPRINT1("CMOS opening %s ; GetLastError() = %u\n", hCmosRam != INVALID_HANDLE_VALUE ? "succeeded" : "failed", GetLastError());
if (hCmosRam != INVALID_HANDLE_VALUE)
{
BOOL Success = FALSE;
/* Attempt to fill the CMOS memory with the RAM file */
SetLastError(0); // For debugging purposes
Success = ReadFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize, NULL);
if (CmosSize != sizeof(CmosMemory))
{
/* Bad CMOS Ram file. Reinitialize the CMOS memory. */
DPRINT1("Invalid CMOS file, read bytes %u, expected bytes %u\n", CmosSize, sizeof(CmosMemory));
ZeroMemory(&CmosMemory, sizeof(CmosMemory));
}
DPRINT1("CMOS loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError());
SetFilePointer(hCmosRam, 0, NULL, FILE_BEGIN);
}
/* Overwrite some registers with default values */
CmosMemory.StatusRegA = CMOS_DEFAULT_STA;
CmosMemory.StatusRegB = CMOS_DEFAULT_STB;
CmosMemory.StatusRegC = 0x00;
CmosMemory.StatusRegD = CMOS_BATTERY_OK; // Our CMOS battery works perfectly forever.
CmosMemory.Diagnostics = 0x00; // Diagnostics must not find any errors.
CmosMemory.ShutdownStatus = 0x00;
return TRUE;
}
VOID CmosCleanup(VOID)
{
DWORD CmosSize = sizeof(CmosMemory);
if (hCmosRam == INVALID_HANDLE_VALUE) return;
/* Flush the CMOS memory back to the RAM file and close it */
SetFilePointer(hCmosRam, 0, NULL, FILE_BEGIN);
WriteFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize, NULL);
CloseHandle(hCmosRam);
hCmosRam = INVALID_HANDLE_VALUE;
}
/* EOF */

View file

@ -2,7 +2,7 @@
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: cmos.h
* PURPOSE: Real Time Clock emulation (header file)
* PURPOSE: CMOS Real Time Clock emulation
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
@ -43,6 +43,12 @@
#define BINARY_TO_BCD(x) (((x / 10) << 4) | (x % 10))
#define BCD_TO_BINARY(x) (((x >> 4) * 10) + (x & 0x0F))
#define WRITE_CMOS_DATA(Cmos, Value) \
((Cmos).StatusRegB & CMOS_STB_BINARY) ? (Value) : BCD_TO_BINARY(Value)
#define READ_CMOS_DATA(Cmos, Value) \
((Cmos).StatusRegB & CMOS_STB_BINARY) ? (Value) : BINARY_TO_BCD(Value)
typedef enum _CMOS_REGISTERS
{
CMOS_REG_SECONDS,
@ -60,9 +66,63 @@ typedef enum _CMOS_REGISTERS
CMOS_REG_STATUS_C,
CMOS_REG_STATUS_D,
CMOS_REG_DIAGNOSTICS,
CMOS_REG_MAX
CMOS_REG_SHUTDOWN_STATUS,
CMOS_REG_MAX = 0x40
} CMOS_REGISTERS, *PCMOS_REGISTERS;
/*
* CMOS Memory Map
*
* See the following documentation for more information:
* http://www.intel-assembler.it/portale/5/cmos-memory-map-123/cmos-memory-map-123.asp
* http://wiki.osdev.org/CMOS
* http://www.bioscentral.com/misc/cmosmap.htm
*/
#pragma pack(push, 1)
typedef struct
{
BYTE Second; // 0x00
BYTE AlarmSecond; // 0x01
BYTE Minute; // 0x02
BYTE AlarmMinute; // 0x03
BYTE Hour; // 0x04
BYTE AlarmHour; // 0x05
BYTE DayOfWeek; // 0x06
BYTE Day; // 0x07
BYTE Month; // 0x08
BYTE Year; // 0x09
BYTE StatusRegA; // 0x0a
BYTE StatusRegB; // 0x0b
} CMOS_CLOCK, *PCMOS_CLOCK;
typedef struct
{
union
{
struct
{
CMOS_CLOCK; // 0x00 - 0x0b
BYTE StatusRegC; // 0x0c
BYTE StatusRegD; // 0x0d
BYTE Diagnostics; // 0x0e
BYTE ShutdownStatus; // 0x0f
};
BYTE Regs1[0x10]; // 0x00 - 0x0f
BYTE Regs [0x40]; // 0x00 - 0x3f
};
/*
* Extended information 0x40 - 0x7f
*/
} CMOS_MEMORY, *PCMOS_MEMORY;
#pragma pack(pop)
C_ASSERT(sizeof(CMOS_MEMORY) == 0x40);
/* FUNCTIONS ******************************************************************/
BOOLEAN IsNmiEnabled(VOID);
VOID CmosWriteAddress(BYTE Value);
BYTE CmosReadData(VOID);
@ -70,7 +130,10 @@ VOID CmosWriteData(BYTE Value);
DWORD RtcGetTicksPerSecond(VOID);
VOID RtcPeriodicTick(VOID);
VOID RtcTimeUpdate(VOID);
BOOLEAN CmosInitialize(VOID);
VOID CmosCleanup(VOID);
#endif // _CMOS_H_
/* EOF */

View file

@ -11,6 +11,7 @@
#define NDEBUG
#include "emulator.h"
#include "cmos.h"
#include "bios.h"
#include "bop.h"
#include "dos.h"
@ -19,7 +20,6 @@
#include "pic.h"
#include "ps2.h"
#include "timer.h"
#include "cmos.h"
/* PRIVATE VARIABLES **********************************************************/

View file

@ -163,7 +163,7 @@ VOID WINAPI InitializeInt32(WORD BiosSegment)
// HACK: The following instruction should be HLT!
BiosCode[Offset++] = 0x90; // nop
BiosCode[Offset++] = 0xEB; // jmp BOP_SEQ (offset -10)
BiosCode[Offset++] = 0xEB; // jmp BOP_SEQ (offset -11)
BiosCode[Offset++] = 0xF5;
// EXIT:

View file

@ -12,6 +12,7 @@
#include "ntvdm.h"
#include "emulator.h"
#include "cmos.h"
#include "bios.h"
#include "speaker.h"
#include "vga.h"
@ -19,7 +20,6 @@
#include "timer.h"
#include "pic.h"
#include "ps2.h"
#include "cmos.h"
/*
* Activate this line if you want to be able to test NTVDM with:
@ -117,6 +117,13 @@ INT wmain(INT argc, WCHAR *argv[])
goto Cleanup;
}
/* Initialize the CMOS */
if (!CmosInitialize())
{
wprintf(L"FATAL: Failed to initialize the VDM CMOS.\n");
goto Cleanup;
}
/* Initialize the system BIOS */
if (!BiosInitialize())
{
@ -227,6 +234,7 @@ INT wmain(INT argc, WCHAR *argv[])
Cleanup:
SpeakerCleanup();
BiosCleanup();
CmosCleanup();
EmulatorCleanup();
DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");