[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Wed, 26 Jun 2013 22:58:41 +0000 (22:58 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Wed, 26 Jun 2013 22:58:41 +0000 (22:58 +0000)
Clean up the code and properly separate different modules into different files.

svn path=/branches/ntvdm/; revision=59344

16 files changed:
subsystems/ntvdm/CMakeLists.txt
subsystems/ntvdm/bios.c
subsystems/ntvdm/bios.h [new file with mode: 0644]
subsystems/ntvdm/dos.c
subsystems/ntvdm/dos.h [new file with mode: 0644]
subsystems/ntvdm/emulator.c
subsystems/ntvdm/emulator.h [new file with mode: 0644]
subsystems/ntvdm/hardware.c [deleted file]
subsystems/ntvdm/ntvdm.c
subsystems/ntvdm/ntvdm.h
subsystems/ntvdm/pic.c [new file with mode: 0644]
subsystems/ntvdm/pic.h [new file with mode: 0644]
subsystems/ntvdm/ps2.c [new file with mode: 0644]
subsystems/ntvdm/ps2.h [new file with mode: 0644]
subsystems/ntvdm/timer.c [new file with mode: 0644]
subsystems/ntvdm/timer.h [new file with mode: 0644]

index 48c9c80..7381ec4 100644 (file)
@@ -5,7 +5,9 @@ list(APPEND SOURCE
     bios.c
     dos.c
     emulator.c
-    hardware.c
+    pic.c
+    timer.c
+    ps2.c
     ntvdm.c
     ntvdm.rc)
 
index 6524910..88afa6d 100644 (file)
@@ -6,10 +6,39 @@
  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  */
 
-#include "ntvdm.h"
+/* INCLUDES *******************************************************************/
 
-BYTE CursorRow, CursorCol;
-WORD ConsoleWidth, ConsoleHeight;
+#include "bios.h"
+#include "emulator.h"
+#include "pic.h"
+#include "timer.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+static BYTE CursorRow, CursorCol;
+static WORD ConsoleWidth, ConsoleHeight;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static COORD BiosVideoAddressToCoord(ULONG Address)
+{
+    COORD Result = {0, 0};
+    CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
+    HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+
+    if (!GetConsoleScreenBufferInfo(ConsoleOutput, &ConsoleInfo))
+    {
+        ASSERT(FALSE);
+        return Result;
+    }
+
+    Result.X = ((Address - CONSOLE_VIDEO_MEM_START) >> 1) % ConsoleInfo.dwSize.X;
+    Result.Y = ((Address - CONSOLE_VIDEO_MEM_START) >> 1) / ConsoleInfo.dwSize.X;
+
+    return Result;
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
 
 BOOLEAN BiosInitialize()
 {
@@ -83,24 +112,6 @@ BOOLEAN BiosInitialize()
     return TRUE;
 }
 
-static COORD BiosVideoAddressToCoord(ULONG Address)
-{
-    COORD Result = {0, 0};
-    CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
-    HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
-
-    if (!GetConsoleScreenBufferInfo(ConsoleOutput, &ConsoleInfo))
-    {
-        ASSERT(FALSE);
-        return Result;
-    }
-
-    Result.X = ((Address - CONSOLE_VIDEO_MEM_START) >> 1) % ConsoleInfo.dwSize.X;
-    Result.Y = ((Address - CONSOLE_VIDEO_MEM_START) >> 1) / ConsoleInfo.dwSize.X;
-
-    return Result;
-}
-
 VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress)
 {
     ULONG i;
diff --git a/subsystems/ntvdm/bios.h b/subsystems/ntvdm/bios.h
new file mode 100644 (file)
index 0000000..c3e2b01
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            bios.h
+ * PURPOSE:         VDM BIOS (header file)
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+#ifndef _BIOS_H_
+#define _BIOS_H_
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+/* DEFINES ********************************************************************/
+
+#define CONSOLE_VIDEO_MEM_START 0xB8000
+#define CONSOLE_VIDEO_MEM_END 0xBFFFF
+#define ROM_AREA_START 0xC0000
+#define ROM_AREA_END 0xFFFFF
+#define BIOS_PIC_MASTER_INT 0x08
+#define BIOS_PIC_SLAVE_INT 0x70
+#define BIOS_SEGMENT 0xF000
+#define VIDEO_BIOS_INTERRUPT 0x10
+#define CONSOLE_FONT_HEIGHT 8
+
+/* FUNCTIONS ******************************************************************/
+
+BOOLEAN BiosInitialize();
+VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress);
+VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress);
+VOID BiosVideoService();
+VOID BiosHandleIrq(BYTE IrqNumber);
+
+#endif
index 1ba037a..ae84dd7 100644 (file)
@@ -6,10 +6,17 @@
  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  */
 
-#include "ntvdm.h"
+/* INCLUDES *******************************************************************/
 
-WORD CurrentPsp = SYSTEM_PSP, LastError = 0;
-DWORD DiskTransferArea;
+#include "dos.h"
+#include "emulator.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+static WORD CurrentPsp = SYSTEM_PSP;
+static DWORD DiskTransferArea;
+
+/* PRIVATE FUNCTIONS **********************************************************/
 
 static VOID DosCombineFreeBlocks(WORD StartBlock)
 {
@@ -78,6 +85,8 @@ static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
     return DestSegment;
 }
 
+/* PUBLIC FUNCTIONS ***********************************************************/
+
 WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
 {
     WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0;
diff --git a/subsystems/ntvdm/dos.h b/subsystems/ntvdm/dos.h
new file mode 100644 (file)
index 0000000..b88dadb
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            dos.h
+ * PURPOSE:         VDM DOS Kernel (header file)
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+#ifndef _DOS_H_
+#define _DOS_H_
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+/* DEFINES ********************************************************************/
+
+#define DOS_VERSION 0x0600
+#define DOS_CONFIG_PATH L"%SystemRoot%\\system32\\CONFIG.NT"
+#define DOS_COMMAND_INTERPRETER L"%SystemRoot%\\system32\\COMMAND.COM /k %SystemRoot%\\system32\\AUTOEXEC.NT"
+#define FIRST_MCB_SEGMENT 0x1000
+#define USER_MEMORY_SIZE 0x8FFFF
+#define SYSTEM_PSP 0x08
+#define SYSTEM_ENV_BLOCK 0x800
+#define SEGMENT_TO_MCB(seg) ((PDOS_MCB)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
+#define SEGMENT_TO_PSP(seg) ((PDOS_PSP)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
+
+#pragma pack(push, 1)
+
+typedef struct _DOS_MCB
+{
+    CHAR BlockType;
+    WORD OwnerPsp;
+    WORD Size;
+    BYTE Unused[3];
+    CHAR Name[8];
+} DOS_MCB, *PDOS_MCB;
+
+typedef struct _DOS_FCB
+{
+    BYTE DriveNumber;
+    CHAR FileName[8];
+    CHAR FileExt[3];
+    WORD BlockNumber;
+    WORD RecordSize;
+    DWORD FileSize;
+    WORD LastWriteDate;
+    WORD LastWriteTime;
+    BYTE Reserved[8];
+    BYTE BlockRecord;
+    BYTE RecordNumber[3];
+} DOS_FCB, *PDOS_FCB;
+
+typedef struct _DOS_PSP
+{
+    BYTE Exit[2];
+    WORD MemSize;
+    BYTE Reserved0[6];
+    DWORD TerminateAddress;
+    DWORD BreakAddress;
+    DWORD CriticalAddress;
+    WORD ParentPsp;
+    BYTE HandleTable[20];
+    WORD EnvBlock;
+    DWORD LastStack;
+    WORD HandleTableSize;
+    DWORD HandleTablePtr;
+    DWORD PreviousPsp;
+    DWORD Reserved1;
+    WORD DosVersion;
+    BYTE Reserved2[14];
+    BYTE FarCall[3];
+    BYTE Reserved3[9];
+    DOS_FCB Fcb;
+    BYTE CommandLineSize;
+    CHAR CommandLine[127];
+} DOS_PSP, *PDOS_PSP;
+
+typedef struct _DOS_SFT_ENTRY
+{
+    WORD ReferenceCount;
+    WORD Mode;
+    BYTE Attribute;
+    WORD DeviceInfo;
+    DWORD DriveParamBlock;
+    WORD FirstCluster;
+    WORD FileTime;
+    WORD FileDate;
+    DWORD FileSize;
+    DWORD CurrentOffset;
+    WORD LastClusterAccessed;
+    DWORD DirEntSector;
+    BYTE DirEntryIndex;
+    CHAR FileName[11];
+    BYTE Reserved0[6];
+    WORD OwnerPsp;
+    BYTE Reserved1[8];
+} DOS_SFT_ENTRY, *PDOS_SFT_ENTRY;
+
+typedef struct _DOS_SFT
+{
+    DWORD NextTablePtr;
+    WORD FileCount;
+    DOS_SFT_ENTRY Entry[ANYSIZE_ARRAY];
+} DOS_SFT, *PDOS_SFT;
+
+typedef struct _DOS_INPUT_BUFFER
+{
+    BYTE MaxLength, Length;
+    CHAR Buffer[ANYSIZE_ARRAY];
+} DOS_INPUT_BUFFER, *PDOS_INPUT_BUFFER;
+
+#pragma pack(pop)
+
+/* FUNCTIONS ******************************************************************/
+
+WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable);
+BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable);
+BOOLEAN DosFreeMemory(WORD BlockData);
+VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment);
+BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock);
+VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode);
+CHAR DosReadCharacter();
+VOID DosPrintCharacter(CHAR Character);
+VOID DosInt20h(WORD CodeSegment);
+VOID DosInt21h(WORD CodeSegment);
+VOID DosBreakInterrupt();
+BOOLEAN DosInitialize();
+
+#endif
+
+/* EOF */
+
index 80dfac9..67b0cd3 100644 (file)
@@ -8,14 +8,21 @@
 
 /* INCLUDES *******************************************************************/
 
-#include "ntvdm.h"
-#include <softx86/softx86.h>
-#include <softx86/softx87.h>
+#include "emulator.h"
+#include "bios.h"
+#include "dos.h"
+#include "pic.h"
+#include "ps2.h"
+#include "timer.h"
 
-softx86_ctx EmulatorContext;
-softx87_ctx FpuEmulatorContext;
+/* PRIVATE VARIABLES **********************************************************/
+
+static softx86_ctx EmulatorContext;
+static softx87_ctx FpuEmulatorContext;
 static BOOLEAN A20Line = FALSE;
 
+/* PRIVATE FUNCTIONS **********************************************************/
+
 static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
 {
     /* If the A20 line is disabled, mask bit 20 */
diff --git a/subsystems/ntvdm/emulator.h b/subsystems/ntvdm/emulator.h
new file mode 100644 (file)
index 0000000..fc94305
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            emulator.h
+ * PURPOSE:         Minimal x86 machine emulator for the VDM (header file)
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+#ifndef _EMULATOR_H_
+#define _EMULATOR_H_
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+#include <softx86/softx86.h>
+#include <softx86/softx87.h>
+
+/* DEFINES ********************************************************************/
+
+#define EMULATOR_FLAG_CF (1 << 0)
+#define EMULATOR_FLAG_PF (1 << 2)
+#define EMULATOR_FLAG_AF (1 << 4)
+#define EMULATOR_FLAG_ZF (1 << 6)
+#define EMULATOR_FLAG_SF (1 << 7)
+#define EMULATOR_FLAG_TF (1 << 8)
+#define EMULATOR_FLAG_IF (1 << 9)
+#define EMULATOR_FLAG_DF (1 << 10)
+#define EMULATOR_FLAG_OF (1 << 11)
+#define EMULATOR_FLAG_NT (1 << 14)
+#define EMULATOR_FLAG_RF (1 << 16)
+#define EMULATOR_FLAG_VM (1 << 17)
+#define EMULATOR_FLAG_AC (1 << 18)
+#define EMULATOR_FLAG_VIF (1 << 19)
+#define EMULATOR_FLAG_VIP (1 << 20)
+#define EMULATOR_FLAG_ID (1 << 21)
+#define SPECIAL_INT_NUM 0xFF
+
+enum
+{
+    EMULATOR_EXCEPTION_DIVISION_BY_ZERO,
+    EMULATOR_EXCEPTION_DEBUG,
+    EMULATOR_EXCEPTION_NMI,
+    EMULATOR_EXCEPTION_BREAKPOINT,
+    EMULATOR_EXCEPTION_OVERFLOW,
+    EMULATOR_EXCEPTION_BOUND,
+    EMULATOR_EXCEPTION_INVALID_OPCODE,
+    EMULATOR_EXCEPTION_NO_FPU
+};
+
+typedef enum
+{
+    EMULATOR_REG_AX,
+    EMULATOR_REG_CX,
+    EMULATOR_REG_DX,
+    EMULATOR_REG_BX,
+    EMULATOR_REG_SI,
+    EMULATOR_REG_DI,
+    EMULATOR_REG_SP,
+    EMULATOR_REG_BP,
+    EMULATOR_REG_ES,
+    EMULATOR_REG_CS,
+    EMULATOR_REG_SS,
+    EMULATOR_REG_DS,
+} EMULATOR_REGISTER;
+
+/* FUNCTIONS ******************************************************************/
+
+BOOLEAN EmulatorInitialize();
+VOID EmulatorSetStack(WORD Segment, WORD Offset);
+VOID EmulatorExecute(WORD Segment, WORD Offset);
+VOID EmulatorInterrupt(BYTE Number);
+VOID EmulatorExternalInterrupt(BYTE Number);
+ULONG EmulatorGetRegister(ULONG Register);
+VOID EmulatorSetRegister(ULONG Register, ULONG Value);
+BOOLEAN EmulatorGetFlag(ULONG Flag);
+VOID EmulatorSetFlag(ULONG Flag);
+VOID EmulatorClearFlag(ULONG Flag);
+VOID EmulatorStep();
+VOID EmulatorCleanup();
+VOID EmulatorSetA20(BOOLEAN Enabled);
+
+#endif
+
+/* EOF */
+
diff --git a/subsystems/ntvdm/hardware.c b/subsystems/ntvdm/hardware.c
deleted file mode 100644 (file)
index 904d499..0000000
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * COPYRIGHT:       GPL - See COPYING in the top level directory
- * PROJECT:         ReactOS Virtual DOS Machine
- * FILE:            hardware.c
- * PURPOSE:         Minimal hardware emulation
- * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
- */
-
-/* INCLUDES *******************************************************************/
-
-#include "ntvdm.h"
-
-typedef struct _PIC
-{
-    BOOLEAN Initialization;
-    BYTE MaskRegister;
-    BYTE InServiceRegister;
-    BYTE IntOffset;
-    BYTE ConfigRegister;
-    BYTE CascadeRegister;
-    BOOLEAN CascadeRegisterSet;
-    BOOLEAN AutoEoi;
-    BOOLEAN Slave;
-    BOOLEAN ReadIsr;
-} PIC, *PPIC;
-
-enum
-{
-    PIT_MODE_INT_ON_TERMINAL_COUNT,
-    PIT_MODE_HARDWARE_ONE_SHOT,
-    PIT_MODE_RATE_GENERATOR,
-    PIT_MODE_SQUARE_WAVE,
-    PIT_MODE_SOFTWARE_STROBE,
-    PIT_MODE_HARDWARE_STROBE
-};
-
-typedef struct _PIT_CHANNEL
-{
-    WORD ReloadValue;
-    WORD CurrentValue;
-    WORD LatchedValue;
-    INT Mode;
-    BOOLEAN Pulsed;
-    BOOLEAN LatchSet;
-    BOOLEAN InputFlipFlop;
-    BOOLEAN OutputFlipFlop;
-    BYTE AccessMode;
-} PIT_CHANNEL, *PPIT_CHANNEL;
-
-static PIC MasterPic, SlavePic;
-static PIT_CHANNEL PitChannels[PIT_CHANNELS];
-static BYTE KeyboardQueue[KEYBOARD_BUFFER_SIZE];
-static BOOLEAN KeyboardQueueEmpty = TRUE;
-static UINT KeyboardQueueStart = 0;
-static UINT KeyboardQueueEnd = 0;
-static BYTE KeyboardResponse = 0;
-static BOOLEAN KeyboardReadResponse = FALSE, KeyboardWriteResponse = FALSE;
-static BYTE KeyboardConfig = PS2_DEFAULT_CONFIG;
-
-static BOOLEAN KeyboardQueuePush(BYTE ScanCode)
-{
-    /* Check if the keyboard queue is full */
-    if (!KeyboardQueueEmpty && (KeyboardQueueStart == KeyboardQueueEnd))
-    {
-        return FALSE;
-    }
-
-    /* Insert the value in the queue */
-    KeyboardQueue[KeyboardQueueEnd] = ScanCode;
-    KeyboardQueueEnd++;
-    KeyboardQueueEnd %= KEYBOARD_BUFFER_SIZE;
-
-    /* Since we inserted a value, it's not empty anymore */
-    KeyboardQueueEmpty = FALSE;
-
-    return TRUE;
-}
-
-static BOOLEAN KeyboardQueuePop(BYTE *ScanCode)
-{
-    /* Make sure the keyboard queue is not empty */
-    if (KeyboardQueueEmpty) return FALSE;
-
-    /* Get the scan code */
-    *ScanCode = KeyboardQueue[KeyboardQueueStart];
-
-    /* Remove the value from the queue */
-    KeyboardQueueStart++;
-    KeyboardQueueStart %= KEYBOARD_BUFFER_SIZE;
-
-    /* Check if the queue is now empty */
-    if (KeyboardQueueStart == KeyboardQueueEnd)
-    {
-        KeyboardQueueEmpty = TRUE;
-    }
-
-    return TRUE;
-}
-
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-BYTE PicReadCommand(BYTE Port)
-{
-    PPIC Pic;
-
-    /* Which PIC are we accessing? */
-    if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
-    else Pic = &SlavePic;
-
-    if (Pic->ReadIsr)
-    {
-        /* Read the in-service register */
-        Pic->ReadIsr = FALSE;
-        return Pic->InServiceRegister;
-    }
-    else
-    {
-        /* The IRR is always 0, as the emulated CPU receives the interrupt instantly */
-        return 0;
-    }
-}
-
-VOID PicWriteCommand(BYTE Port, BYTE Value)
-{
-    PPIC Pic;
-
-    /* Which PIC are we accessing? */
-    if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
-    else Pic = &SlavePic;
-
-    if (Value & PIC_ICW1)
-    {
-        /* Start initialization */
-        Pic->Initialization = TRUE;
-        Pic->IntOffset = 0xFF;
-        Pic->CascadeRegisterSet = FALSE;
-        Pic->ConfigRegister = Value;
-        return;
-    }
-
-    if (Value & PIC_OCW3)
-    {
-        /* This is an OCR3 */
-        if (Value == PIC_OCW3_READ_ISR)
-        {
-            /* Return the ISR on next read from command port */
-            Pic->ReadIsr = TRUE;
-        }
-
-        return;
-    }
-
-    /* This is an OCW2 */
-    if (Value & PIC_OCW2_EOI)
-    {
-        if (Value & PIC_OCW2_SL)
-        {
-            /* If the SL bit is set, clear a specific IRQ */
-            Pic->InServiceRegister &= ~(1 << (Value & PIC_OCW2_NUM_MASK));
-        }
-        else
-        {
-            /* Otherwise, clear all of them */
-            Pic->InServiceRegister = 0;
-        }
-    }
-}
-
-BYTE PicReadData(BYTE Port)
-{
-    /* Read the mask register */
-    if (Port == PIC_MASTER_DATA) return MasterPic.MaskRegister;
-    else return SlavePic.MaskRegister;
-}
-
-VOID PicWriteData(BYTE Port, BYTE Value)
-{
-    PPIC Pic;
-
-    /* Which PIC are we accessing? */
-    if (Port == PIC_MASTER_DATA) Pic = &MasterPic;
-    else Pic = &SlavePic;
-
-    /* Is the PIC ready? */
-    if (!Pic->Initialization)
-    {
-        /* Yes, this is an OCW1 */
-        Pic->MaskRegister = Value;
-        return;
-    }
-
-    /* Has the interrupt offset been set? */
-    if (Pic->IntOffset == 0xFF)
-    {
-        /* This is an ICW2, set the offset (last three bits always zero) */
-        Pic->IntOffset = Value & 0xF8;
-
-        /* Check if we are in single mode and don't need an ICW4 */
-        if ((Pic->ConfigRegister & PIC_ICW1_SINGLE)
-            && !(Pic->ConfigRegister & PIC_ICW1_ICW4))
-        {
-            /* Yes, done initializing */
-            Pic->Initialization = FALSE;
-        }
-        return;
-    }
-
-    /* Check if we are in cascade mode and the cascade register was not set */
-    if (!(Pic->ConfigRegister & PIC_ICW1_SINGLE) && !Pic->CascadeRegisterSet)
-    {
-        /* This is an ICW3 */
-        Pic->CascadeRegister = Value;
-        Pic->CascadeRegisterSet = TRUE;
-
-        /* Check if we need an ICW4 */
-        if (!(Pic->ConfigRegister & PIC_ICW1_ICW4))
-        {
-            /* No, done initializing */
-            Pic->Initialization = FALSE;
-        }
-        return;
-    }
-
-    /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
-    if (Value & PIC_ICW4_AEOI)
-    {
-        /* Use automatic end-of-interrupt */
-        Pic->AutoEoi = TRUE;
-    }
-
-    /* Done initializing */
-    Pic->Initialization = FALSE;
-}
-
-VOID PicInterruptRequest(BYTE Number)
-{
-    BYTE i;
-
-    if (Number >= 0 && Number < 8)
-    {
-        /* Check if any of the higher-priorirty interrupts are busy */
-        for (i = 0; i <= Number ; i++)
-        {
-            if (MasterPic.InServiceRegister & (1 << Number)) return;
-        }
-
-        /* Check if the interrupt is masked */
-        if (MasterPic.MaskRegister & (1 << Number)) return;
-
-        /* Set the appropriate bit in the ISR and interrupt the CPU */
-        if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << Number;
-        EmulatorExternalInterrupt(MasterPic.IntOffset + Number);
-    }
-    else if (Number >= 8 && Number < 16)
-    {
-        Number -= 8;
-
-        /*
-         * The slave PIC is connected to IRQ 2, always! If the master PIC
-         * was misconfigured, don't do anything.
-         */
-        if (!(MasterPic.CascadeRegister & (1 << 2))
-            || SlavePic.CascadeRegister != 2)
-        {
-            return;
-        }
-
-        /* Check if any of the higher-priorirty interrupts are busy */
-        if (MasterPic.InServiceRegister != 0) return;
-        for (i = 0; i <= Number ; i++)
-        {
-            if (SlavePic.InServiceRegister & (1 << Number)) return;
-        }
-
-        /* Check if the interrupt is masked */
-        if (SlavePic.MaskRegister & (1 << Number)) return;
-
-        /* Set the IRQ 2 bit in the master ISR */
-        if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << 2;
-
-        /* Set the appropriate bit in the ISR and interrupt the CPU */
-        if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= 1 << Number;
-        EmulatorExternalInterrupt(SlavePic.IntOffset + Number);
-    }
-}
-
-VOID PitWriteCommand(BYTE Value)
-{
-    BYTE Channel = Value >> 6;
-    BYTE Mode = (Value >> 1) & 0x07;
-
-    /* Check if this is a counter latch command */
-    if (((Value >> 4) & 3) == 0)
-    {
-        PitChannels[Channel].LatchSet = TRUE;
-        PitChannels[Channel].LatchedValue = PitChannels[Channel].CurrentValue;
-        return;
-    }
-
-    /* Set the access mode and reset flip-flops */
-    PitChannels[Channel].AccessMode = (Value >> 4) & 3;
-    PitChannels[Channel].Pulsed = FALSE;
-    PitChannels[Channel].LatchSet = FALSE;
-    PitChannels[Channel].InputFlipFlop = FALSE;
-    PitChannels[Channel].OutputFlipFlop = FALSE;
-
-    switch (Mode)
-    {
-        case 0:
-        case 1:
-        case 2:
-        case 3:
-        case 4:
-        case 5:
-        {
-            PitChannels[Channel].Mode = Mode;
-            break;
-        }
-
-        case 6:
-        {
-            PitChannels[Channel].Mode = PIT_MODE_RATE_GENERATOR;
-            break;
-        }
-
-        case 7:
-        {
-            PitChannels[Channel].Mode = PIT_MODE_SQUARE_WAVE;
-            break;
-        }
-    }
-}
-
-BYTE PitReadData(BYTE Channel)
-{
-    WORD CurrentValue = PitChannels[Channel].CurrentValue;
-    BYTE AccessMode = PitChannels[Channel].AccessMode;
-
-    /* Check if the value was latched */
-    if (PitChannels[Channel].LatchSet)
-    {
-        CurrentValue = PitChannels[Channel].LatchedValue;
-
-        if (AccessMode == 1 || AccessMode == 2)
-        {
-            /* The latched value was read as one byte */
-            PitChannels[Channel].LatchSet = FALSE;
-        }
-    }
-
-    /* Use the flip-flop for access mode 3 */
-    if (AccessMode == 3)
-    {
-        AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
-        PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
-
-        /* Check if this was the last read for the latched value */
-        if (!PitChannels[Channel].InputFlipFlop)
-        {
-            /* Yes, the latch value was read as two bytes */
-            PitChannels[Channel].LatchSet = FALSE;
-        }
-    }
-
-    switch (AccessMode)
-    {
-        case 1:
-        {
-            /* Low byte */
-            return CurrentValue & 0x00FF;
-        }
-
-        case 2:
-        {
-            /* High byte */
-            return CurrentValue >> 8;
-        }
-    }
-
-    /* Shouldn't get here */
-    return 0;
-}
-
-VOID PitWriteData(BYTE Channel, BYTE Value)
-{
-    BYTE AccessMode = PitChannels[Channel].AccessMode;
-
-    /* Use the flip-flop for access mode 3 */
-    if (PitChannels[Channel].AccessMode == 3)
-    {
-        AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
-        PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
-    }
-
-    switch (AccessMode)
-    {
-        case 1:
-        {
-            /* Low byte */
-            PitChannels[Channel].ReloadValue &= 0xFF00;
-            PitChannels[Channel].ReloadValue |= Value;
-            break;
-        }
-
-        case 2:
-        {
-            /* High byte */
-            PitChannels[Channel].ReloadValue &= 0x00FF;
-            PitChannels[Channel].ReloadValue |= Value << 8;
-        }
-    }
-}
-
-VOID PitDecrementCount()
-{
-    INT i;
-
-    for (i = 0; i < PIT_CHANNELS; i++)
-    {
-        switch (PitChannels[i].Mode)
-        {
-            case PIT_MODE_INT_ON_TERMINAL_COUNT:
-            {
-                /* Decrement the value */
-                PitChannels[i].CurrentValue--;
-
-                /* Did it fall to the terminal count? */
-                if (PitChannels[i].CurrentValue == 0 && !PitChannels[i].Pulsed)
-                {
-                    /* Yes, raise the output line */
-                    if (i == 0) PicInterruptRequest(0);
-                    PitChannels[i].Pulsed = TRUE;
-                }
-                break;
-            }
-
-            case PIT_MODE_RATE_GENERATOR:
-            {
-                /* Decrement the value */
-                PitChannels[i].CurrentValue--;
-
-                /* Did it fall to zero? */
-                if (PitChannels[i].CurrentValue != 0) break;
-
-                /* Yes, raise the output line and reload */
-                if (i == 0) PicInterruptRequest(0);
-                PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
-
-                break;
-            }
-
-            case PIT_MODE_SQUARE_WAVE:
-            {
-                /* Decrement the value by 2 */
-                PitChannels[i].CurrentValue -= 2;
-
-                /* Did it fall to zero? */
-                if (PitChannels[i].CurrentValue != 0) break;
-
-                /* Yes, toggle the flip-flop */
-                PitChannels[i].OutputFlipFlop = !PitChannels[i].OutputFlipFlop;
-
-                /* Did this create a rising edge in the signal? */
-                if (PitChannels[i].OutputFlipFlop)
-                {
-                    /* Yes, IRQ 0 if this is channel 0 */
-                    if (i == 0) PicInterruptRequest(0);
-                }
-
-                /* Reload the value, but make sure it's even */
-                if (PitChannels[i].ReloadValue % 2)
-                {
-                    /* It's odd, reduce it by 1 */
-                    PitChannels[i].CurrentValue = PitChannels[i].ReloadValue - 1;
-                }
-                else
-                {
-                    /* It was even */
-                    PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
-                }
-
-                break;
-            }
-
-            case PIT_MODE_SOFTWARE_STROBE:
-            {
-                // TODO: NOT IMPLEMENTED
-                break;
-            }
-
-            case PIT_MODE_HARDWARE_ONE_SHOT:
-            case PIT_MODE_HARDWARE_STROBE:
-            {
-                /* These modes do not work on x86 PCs */
-                break;
-            }
-        }
-    }
-}
-
-BYTE KeyboardReadStatus()
-{
-    BYTE Status = 0;
-
-    /* Set the first bit if the data can be read */
-    if (KeyboardReadResponse || !KeyboardQueueEmpty) Status |= 1 << 0;
-
-    /* Always set bit 2 */
-    Status |= 1 << 2;
-
-    /* Set bit 3 if the next byte goes to the controller */
-    if (KeyboardWriteResponse) Status |= 1 << 3;
-
-    return Status;
-}
-
-VOID KeyboardWriteCommand(BYTE Command)
-{
-    switch (Command)
-    {
-        /* Read configuration byte */
-        case 0x20:
-        {
-            KeyboardResponse = KeyboardConfig;
-            KeyboardReadResponse = TRUE;
-
-            break;
-        }
-
-        /* Write configuration byte */
-        case 0x60:
-        /* Write controller output port */
-        case 0xD1:
-        /* Write keyboard output buffer */
-        case 0xD2:
-        /* Write mouse output buffer */
-        case 0xD3:
-        /* Write mouse input buffer */
-        case 0xD4:
-        {
-            /* These commands require a response */
-            KeyboardResponse = Command;
-            KeyboardWriteResponse = TRUE;
-
-            break;
-        }
-
-        /* Disable mouse */
-        case 0xA7:
-        {
-            // TODO: Mouse support
-
-            break;
-        }
-
-        /* Enable mouse */
-        case 0xA8:
-        {
-            // TODO: Mouse support
-
-            break;
-        }
-
-        /* Test mouse port */
-        case 0xA9:
-        {
-            KeyboardResponse = 0;
-            KeyboardReadResponse = TRUE;
-
-            break;
-        }
-
-        /* Test PS/2 controller */
-        case 0xAA:
-        {
-            KeyboardResponse = 0x55;
-            KeyboardReadResponse = TRUE;
-
-            break;
-        }
-
-        /* Disable keyboard */
-        case 0xAD:
-        {
-            // TODO: Not implemented
-            break;
-        }
-
-        /* Enable keyboard */
-        case 0xAE:
-        {
-            // TODO: Not implemented
-            break;
-        }
-
-        /* Read controller output port */
-        case 0xD0:
-        {
-            // TODO: Not implemented
-            break;
-        }
-
-        /* CPU Reset */
-        case 0xF0:
-        case 0xF2:
-        case 0xF4:
-        case 0xF6:
-        case 0xF8:
-        case 0xFA:
-        case 0xFC:
-        case 0xFE:
-        {
-            /* Stop the simulation */
-            VdmRunning = FALSE;
-
-            break;
-        }
-    }
-}
-
-BYTE KeyboardReadData()
-{
-    BYTE Value = 0;
-
-    /* If there was a response byte from the controller, return it */
-    if (KeyboardReadResponse)
-    {
-        KeyboardReadResponse = FALSE;
-        return KeyboardResponse;
-    }
-    
-    /* Otherwise, read the data from the queue */
-    KeyboardQueuePop(&Value);
-
-    return Value;
-}
-
-VOID KeyboardWriteData(BYTE Data)
-{
-    /* Check if the controller is waiting for a response */
-    if (KeyboardWriteResponse)
-    {
-        KeyboardWriteResponse = FALSE;
-
-        /* Check which command it was */
-        switch (KeyboardResponse)
-        {
-            /* Write configuration byte */
-            case 0x60:
-            {
-                KeyboardConfig = Data;
-                break;
-            }
-
-            /* Write controller output */
-            case 0xD1:
-            {
-                /* Check if bit 0 is unset */
-                if (!(Data & (1 << 0)))
-                {
-                    /* CPU disabled - end simulation */
-                    VdmRunning = FALSE;
-                }
-
-                /* Update the A20 line setting */
-                EmulatorSetA20(Data & (1 << 1));
-
-                break;
-            }
-            
-            case 0xD2:
-            {
-                /* Push the data byte to the keyboard queue */
-                KeyboardQueuePush(Data);
-
-                break;
-            }
-
-            case 0xD3:
-            {
-                // TODO: Mouse support
-                break;
-            }
-
-            case 0xD4:
-            {
-                // TODO: Mouse support
-                break;
-            }
-        }
-
-        return;
-    }
-
-    // TODO: Implement PS/2 device commands
-}
-
-VOID CheckForInputEvents()
-{
-    PINPUT_RECORD Buffer;
-    HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
-    DWORD i, j, Count, TotalEvents;
-    BYTE ScanCode;
-
-    /* Get the number of input events */
-    if (!GetNumberOfConsoleInputEvents(ConsoleInput, &Count)) return;
-    if (Count == 0) return;
-
-    /* Allocate the buffer */
-    Buffer = (PINPUT_RECORD)HeapAlloc(GetProcessHeap(), 0, Count * sizeof(INPUT_RECORD));
-    if (Buffer == NULL) return;
-
-    /* Peek the input events */
-    if (!ReadConsoleInput(ConsoleInput, Buffer, Count, &TotalEvents)) goto Cleanup;
-
-    for (i = 0; i < TotalEvents; i++)
-    {
-        /* Check if this is a key event */
-        if (Buffer[i].EventType != KEY_EVENT) continue;
-
-        /* Get the scan code */
-        ScanCode = Buffer[i].Event.KeyEvent.wVirtualScanCode;
-
-        /* If this is a key release, set the highest bit in the scan code */
-        if (!Buffer[i].Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
-
-        /* Push the scan code onto the keyboard queue */
-        for (j = 0; j < Buffer[i].Event.KeyEvent.wRepeatCount; j++)
-        {
-            KeyboardQueuePush(ScanCode);
-        }
-
-        /* Yes, IRQ 1 */
-        PicInterruptRequest(1);
-
-        /* Stop the loop */
-        break;
-    }
-
-Cleanup:
-    HeapFree(GetProcessHeap(), 0, Buffer);
-}
index f20457c..351352a 100644 (file)
@@ -6,7 +6,17 @@
  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  */
 
+/* INCLUDES *******************************************************************/
+
 #include "ntvdm.h"
+#include "emulator.h"
+#include "bios.h"
+#include "dos.h"
+#include "timer.h"
+#include "pic.h"
+#include "ps2.h"
+
+/* PUBLIC VARIABLES ***********************************************************/
 
 BOOLEAN VdmRunning = TRUE;
 LPVOID BaseAddress = NULL;
@@ -22,6 +32,8 @@ LPCWSTR ExceptionName[] =
     L"FPU Not Available"
 };
 
+/* PUBLIC FUNCTIONS ***********************************************************/
+
 VOID DisplayMessage(LPCWSTR Format, ...)
 {
     WCHAR Buffer[256];
index 3709d7d..7e71e2b 100644 (file)
@@ -6,6 +6,9 @@
  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  */
 
+#ifndef _NTVDM_H_
+#define _NTVDM_H_
+
 /* INCLUDES *******************************************************************/
 
 #include <windows.h>
 #define MAX_SEGMENT 0xFFFF
 #define MAX_OFFSET 0xFFFF
 #define MAX_ADDRESS TO_LINEAR(MAX_SEGMENT, MAX_OFFSET)
-#define ROM_AREA_START 0xC0000
-#define ROM_AREA_END 0xFFFFF
-#define BIOS_PIC_MASTER_INT 0x08
-#define BIOS_PIC_SLAVE_INT 0x70
-#define BIOS_SEGMENT 0xF000
-#define VIDEO_BIOS_INTERRUPT 0x10
-#define SPECIAL_INT_NUM 0xFF
-#define SEGMENT_TO_MCB(seg) ((PDOS_MCB)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
-#define SEGMENT_TO_PSP(seg) ((PDOS_PSP)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
 #define STEPS_PER_CYCLE 256
 
-/* DOS constants */
-#define DOS_VERSION 0x0600
-#define DOS_CONFIG_PATH L"%SystemRoot%\\system32\\CONFIG.NT"
-#define DOS_COMMAND_INTERPRETER L"%SystemRoot%\\system32\\COMMAND.COM /k %SystemRoot%\\system32\\AUTOEXEC.NT"
-#define FIRST_MCB_SEGMENT 0x1000
-#define USER_MEMORY_SIZE 0x8FFFF
-#define SYSTEM_PSP 0x08
-#define SYSTEM_ENV_BLOCK 0x800
-
-/* System console constants */
-#define CONSOLE_FONT_HEIGHT 8
-#define CONSOLE_VIDEO_MEM_START 0xB8000
-#define CONSOLE_VIDEO_MEM_END 0xBFFFF
-
-/* Programmable interval timer (PIT) */
-#define PIT_CHANNELS 3
-#define PIT_BASE_FREQUENCY 1193182LL
-#define PIT_DATA_PORT(x) (0x40 + (x))
-#define PIT_COMMAND_PORT 0x43
-
-/* Programmable interrupt controller (PIC) */
-#define PIC_MASTER_CMD 0x20
-#define PIC_MASTER_DATA 0x21
-#define PIC_SLAVE_CMD 0xA0
-#define PIC_SLAVE_DATA 0xA1
-#define PIC_ICW1 0x10
-#define PIC_ICW1_ICW4 (1 << 0)
-#define PIC_ICW1_SINGLE (1 << 1)
-#define PIC_ICW4_8086 (1 << 0)
-#define PIC_ICW4_AEOI (1 << 1)
-#define PIC_OCW2_NUM_MASK 0x07
-#define PIC_OCW2_EOI (1 << 5)
-#define PIC_OCW2_SL (1 << 6)
-#define PIC_OCW3 (1 << 3)
-#define PIC_OCW3_READ_ISR 0x0B
-
-/* 8042 PS/2 controller */
-#define KEYBOARD_BUFFER_SIZE 32
-#define PS2_DATA_PORT 0x60
-#define PS2_CONTROL_PORT 0x64
-#define PS2_DEFAULT_CONFIG 0x05
-#define KEYBOARD_ACK 0xFA
-#define KEYBOARD_RESEND 0xFE
-
-#define EMULATOR_FLAG_CF (1 << 0)
-#define EMULATOR_FLAG_PF (1 << 2)
-#define EMULATOR_FLAG_AF (1 << 4)
-#define EMULATOR_FLAG_ZF (1 << 6)
-#define EMULATOR_FLAG_SF (1 << 7)
-#define EMULATOR_FLAG_TF (1 << 8)
-#define EMULATOR_FLAG_IF (1 << 9)
-#define EMULATOR_FLAG_DF (1 << 10)
-#define EMULATOR_FLAG_OF (1 << 11)
-#define EMULATOR_FLAG_NT (1 << 14)
-#define EMULATOR_FLAG_RF (1 << 16)
-#define EMULATOR_FLAG_VM (1 << 17)
-#define EMULATOR_FLAG_AC (1 << 18)
-#define EMULATOR_FLAG_VIF (1 << 19)
-#define EMULATOR_FLAG_VIP (1 << 20)
-#define EMULATOR_FLAG_ID (1 << 21)
-
-enum
-{
-    EMULATOR_EXCEPTION_DIVISION_BY_ZERO,
-    EMULATOR_EXCEPTION_DEBUG,
-    EMULATOR_EXCEPTION_NMI,
-    EMULATOR_EXCEPTION_BREAKPOINT,
-    EMULATOR_EXCEPTION_OVERFLOW,
-    EMULATOR_EXCEPTION_BOUND,
-    EMULATOR_EXCEPTION_INVALID_OPCODE,
-    EMULATOR_EXCEPTION_NO_FPU
-};
-
-typedef enum
-{
-    EMULATOR_REG_AX,
-    EMULATOR_REG_CX,
-    EMULATOR_REG_DX,
-    EMULATOR_REG_BX,
-    EMULATOR_REG_SI,
-    EMULATOR_REG_DI,
-    EMULATOR_REG_SP,
-    EMULATOR_REG_BP,
-    EMULATOR_REG_ES,
-    EMULATOR_REG_CS,
-    EMULATOR_REG_SS,
-    EMULATOR_REG_DS,
-} EMULATOR_REGISTER;
-
-#pragma pack(push, 1)
-
-typedef struct _DOS_MCB
-{
-    CHAR BlockType;
-    WORD OwnerPsp;
-    WORD Size;
-    BYTE Unused[3];
-    CHAR Name[8];
-} DOS_MCB, *PDOS_MCB;
-
-typedef struct _DOS_FCB
-{
-    BYTE DriveNumber;
-    CHAR FileName[8];
-    CHAR FileExt[3];
-    WORD BlockNumber;
-    WORD RecordSize;
-    DWORD FileSize;
-    WORD LastWriteDate;
-    WORD LastWriteTime;
-    BYTE Reserved[8];
-    BYTE BlockRecord;
-    BYTE RecordNumber[3];
-} DOS_FCB, *PDOS_FCB;
-
-typedef struct _DOS_PSP
-{
-    BYTE Exit[2];
-    WORD MemSize;
-    BYTE Reserved0[6];
-    DWORD TerminateAddress;
-    DWORD BreakAddress;
-    DWORD CriticalAddress;
-    WORD ParentPsp;
-    BYTE HandleTable[20];
-    WORD EnvBlock;
-    DWORD LastStack;
-    WORD HandleTableSize;
-    DWORD HandleTablePtr;
-    DWORD PreviousPsp;
-    DWORD Reserved1;
-    WORD DosVersion;
-    BYTE Reserved2[14];
-    BYTE FarCall[3];
-    BYTE Reserved3[9];
-    DOS_FCB Fcb;
-    BYTE CommandLineSize;
-    CHAR CommandLine[127];
-} DOS_PSP, *PDOS_PSP;
-
-typedef struct _DOS_SFT_ENTRY
-{
-    WORD ReferenceCount;
-    WORD Mode;
-    BYTE Attribute;
-    WORD DeviceInfo;
-    DWORD DriveParamBlock;
-    WORD FirstCluster;
-    WORD FileTime;
-    WORD FileDate;
-    DWORD FileSize;
-    DWORD CurrentOffset;
-    WORD LastClusterAccessed;
-    DWORD DirEntSector;
-    BYTE DirEntryIndex;
-    CHAR FileName[11];
-    BYTE Reserved0[6];
-    WORD OwnerPsp;
-    BYTE Reserved1[8];
-} DOS_SFT_ENTRY, *PDOS_SFT_ENTRY;
-
-typedef struct _DOS_SFT
-{
-    DWORD NextTablePtr;
-    WORD FileCount;
-    DOS_SFT_ENTRY Entry[ANYSIZE_ARRAY];
-} DOS_SFT, *PDOS_SFT;
-
-typedef struct _DOS_INPUT_BUFFER
-{
-    BYTE MaxLength, Length;
-    CHAR Buffer[ANYSIZE_ARRAY];
-} DOS_INPUT_BUFFER, *PDOS_INPUT_BUFFER;
-
-#pragma pack(pop)
-
 /* FUNCTIONS ******************************************************************/
 
 extern LPVOID BaseAddress;
@@ -217,47 +35,7 @@ extern BOOLEAN VdmRunning;
 extern LPCWSTR ExceptionName[];
 
 VOID DisplayMessage(LPCWSTR Format, ...);
-BOOLEAN BiosInitialize();
-VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress);
-VOID BiosPrintCharacter(CHAR Character, BYTE Attribute);
-BOOLEAN DosInitialize();
-WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable);
-BOOLEAN DosFreeMemory(WORD BlockData);
-BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable);
-BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock);
-VOID DosInt20h(WORD CodeSegment);
-VOID DosInt21h(WORD CodeSegment);
-VOID DosBreakInterrupt();
-VOID BiosVideoService();
-VOID BiosHandleIrq(BYTE IrqNumber);
-BYTE PicReadCommand(BYTE Port);
-VOID PicWriteCommand(BYTE Port, BYTE Value);
-BYTE PicReadData(BYTE Port);
-VOID PicWriteData(BYTE Port, BYTE Value);
-VOID PicInterruptRequest(BYTE Number);
-VOID PitInitialize();
-VOID PitWriteCommand(BYTE Value);
-BYTE PitReadData(BYTE Channel);
-VOID PitWriteData(BYTE Channel, BYTE Value);
-VOID PitDecrementCount();
-VOID CheckForInputEvents();
-BYTE KeyboardReadStatus();
-VOID KeyboardWriteCommand(BYTE Command);
-BYTE KeyboardReadData();
-VOID KeyboardWriteData(BYTE Data);
-VOID EmulatorSetStack(WORD Segment, WORD Offset);
-VOID EmulatorExecute(WORD Segment, WORD Offset);
-VOID EmulatorInterrupt(BYTE Number);
-VOID EmulatorExternalInterrupt(BYTE Number);
-ULONG EmulatorGetRegister(ULONG Register);
-VOID EmulatorSetRegister(ULONG Register, ULONG Value);
-VOID EmulatorSetFlag(ULONG Flag);
-VOID EmulatorClearFlag(ULONG Flag);
-BOOLEAN EmulatorGetFlag(ULONG Flag);
-BOOLEAN EmulatorInitialize();
-VOID EmulatorStep();
-VOID EmulatorCleanup();
-VOID EmulatorHalt();
-VOID EmulatorSetA20(BOOLEAN Enabled);
+
+#endif
 
 /* EOF */
diff --git a/subsystems/ntvdm/pic.c b/subsystems/ntvdm/pic.c
new file mode 100644 (file)
index 0000000..1e205a3
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            pic.c
+ * PURPOSE:         Programmable Interrupt Controller emulation
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "pic.h"
+#include "emulator.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+static PIC MasterPic, SlavePic;
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+BYTE PicReadCommand(BYTE Port)
+{
+    PPIC Pic;
+
+    /* Which PIC are we accessing? */
+    if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
+    else Pic = &SlavePic;
+
+    if (Pic->ReadIsr)
+    {
+        /* Read the in-service register */
+        Pic->ReadIsr = FALSE;
+        return Pic->InServiceRegister;
+    }
+    else
+    {
+        /* The IRR is always 0, as the emulated CPU receives the interrupt instantly */
+        return 0;
+    }
+}
+
+VOID PicWriteCommand(BYTE Port, BYTE Value)
+{
+    PPIC Pic;
+
+    /* Which PIC are we accessing? */
+    if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
+    else Pic = &SlavePic;
+
+    if (Value & PIC_ICW1)
+    {
+        /* Start initialization */
+        Pic->Initialization = TRUE;
+        Pic->IntOffset = 0xFF;
+        Pic->CascadeRegisterSet = FALSE;
+        Pic->ConfigRegister = Value;
+        return;
+    }
+
+    if (Value & PIC_OCW3)
+    {
+        /* This is an OCR3 */
+        if (Value == PIC_OCW3_READ_ISR)
+        {
+            /* Return the ISR on next read from command port */
+            Pic->ReadIsr = TRUE;
+        }
+
+        return;
+    }
+
+    /* This is an OCW2 */
+    if (Value & PIC_OCW2_EOI)
+    {
+        if (Value & PIC_OCW2_SL)
+        {
+            /* If the SL bit is set, clear a specific IRQ */
+            Pic->InServiceRegister &= ~(1 << (Value & PIC_OCW2_NUM_MASK));
+        }
+        else
+        {
+            /* Otherwise, clear all of them */
+            Pic->InServiceRegister = 0;
+        }
+    }
+}
+
+BYTE PicReadData(BYTE Port)
+{
+    /* Read the mask register */
+    if (Port == PIC_MASTER_DATA) return MasterPic.MaskRegister;
+    else return SlavePic.MaskRegister;
+}
+
+VOID PicWriteData(BYTE Port, BYTE Value)
+{
+    PPIC Pic;
+
+    /* Which PIC are we accessing? */
+    if (Port == PIC_MASTER_DATA) Pic = &MasterPic;
+    else Pic = &SlavePic;
+
+    /* Is the PIC ready? */
+    if (!Pic->Initialization)
+    {
+        /* Yes, this is an OCW1 */
+        Pic->MaskRegister = Value;
+        return;
+    }
+
+    /* Has the interrupt offset been set? */
+    if (Pic->IntOffset == 0xFF)
+    {
+        /* This is an ICW2, set the offset (last three bits always zero) */
+        Pic->IntOffset = Value & 0xF8;
+
+        /* Check if we are in single mode and don't need an ICW4 */
+        if ((Pic->ConfigRegister & PIC_ICW1_SINGLE)
+            && !(Pic->ConfigRegister & PIC_ICW1_ICW4))
+        {
+            /* Yes, done initializing */
+            Pic->Initialization = FALSE;
+        }
+        return;
+    }
+
+    /* Check if we are in cascade mode and the cascade register was not set */
+    if (!(Pic->ConfigRegister & PIC_ICW1_SINGLE) && !Pic->CascadeRegisterSet)
+    {
+        /* This is an ICW3 */
+        Pic->CascadeRegister = Value;
+        Pic->CascadeRegisterSet = TRUE;
+
+        /* Check if we need an ICW4 */
+        if (!(Pic->ConfigRegister & PIC_ICW1_ICW4))
+        {
+            /* No, done initializing */
+            Pic->Initialization = FALSE;
+        }
+        return;
+    }
+
+    /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
+    if (Value & PIC_ICW4_AEOI)
+    {
+        /* Use automatic end-of-interrupt */
+        Pic->AutoEoi = TRUE;
+    }
+
+    /* Done initializing */
+    Pic->Initialization = FALSE;
+}
+
+VOID PicInterruptRequest(BYTE Number)
+{
+    BYTE i;
+
+    if (Number >= 0 && Number < 8)
+    {
+        /* Check if any of the higher-priorirty interrupts are busy */
+        for (i = 0; i <= Number ; i++)
+        {
+            if (MasterPic.InServiceRegister & (1 << Number)) return;
+        }
+
+        /* Check if the interrupt is masked */
+        if (MasterPic.MaskRegister & (1 << Number)) return;
+
+        /* Set the appropriate bit in the ISR and interrupt the CPU */
+        if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << Number;
+        EmulatorExternalInterrupt(MasterPic.IntOffset + Number);
+    }
+    else if (Number >= 8 && Number < 16)
+    {
+        Number -= 8;
+
+        /*
+         * The slave PIC is connected to IRQ 2, always! If the master PIC
+         * was misconfigured, don't do anything.
+         */
+        if (!(MasterPic.CascadeRegister & (1 << 2))
+            || SlavePic.CascadeRegister != 2)
+        {
+            return;
+        }
+
+        /* Check if any of the higher-priorirty interrupts are busy */
+        if (MasterPic.InServiceRegister != 0) return;
+        for (i = 0; i <= Number ; i++)
+        {
+            if (SlavePic.InServiceRegister & (1 << Number)) return;
+        }
+
+        /* Check if the interrupt is masked */
+        if (SlavePic.MaskRegister & (1 << Number)) return;
+
+        /* Set the IRQ 2 bit in the master ISR */
+        if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << 2;
+
+        /* Set the appropriate bit in the ISR and interrupt the CPU */
+        if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= 1 << Number;
+        EmulatorExternalInterrupt(SlavePic.IntOffset + Number);
+    }
+}
+
+/* EOF */
diff --git a/subsystems/ntvdm/pic.h b/subsystems/ntvdm/pic.h
new file mode 100644 (file)
index 0000000..6503dce
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            pic.h
+ * PURPOSE:         Programmable Interrupt Controller emulation (header file)
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+#ifndef _PIC_H_
+#define _PIC_H_
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+/* DEFINES ********************************************************************/
+
+#define PIC_MASTER_CMD 0x20
+#define PIC_MASTER_DATA 0x21
+#define PIC_SLAVE_CMD 0xA0
+#define PIC_SLAVE_DATA 0xA1
+#define PIC_ICW1 0x10
+#define PIC_ICW1_ICW4 (1 << 0)
+#define PIC_ICW1_SINGLE (1 << 1)
+#define PIC_ICW4_8086 (1 << 0)
+#define PIC_ICW4_AEOI (1 << 1)
+#define PIC_OCW2_NUM_MASK 0x07
+#define PIC_OCW2_EOI (1 << 5)
+#define PIC_OCW2_SL (1 << 6)
+#define PIC_OCW3 (1 << 3)
+#define PIC_OCW3_READ_ISR 0x0B
+
+typedef struct _PIC
+{
+    BOOLEAN Initialization;
+    BYTE MaskRegister;
+    BYTE InServiceRegister;
+    BYTE IntOffset;
+    BYTE ConfigRegister;
+    BYTE CascadeRegister;
+    BOOLEAN CascadeRegisterSet;
+    BOOLEAN AutoEoi;
+    BOOLEAN Slave;
+    BOOLEAN ReadIsr;
+} PIC, *PPIC;
+
+/* FUNCTIONS ******************************************************************/
+
+BYTE PicReadCommand(BYTE Port);
+VOID PicWriteCommand(BYTE Port, BYTE Value);
+BYTE PicReadData(BYTE Port);
+VOID PicWriteData(BYTE Port, BYTE Value);
+VOID PicInterruptRequest(BYTE Number);
+
+#endif
+
+/* EOF */
diff --git a/subsystems/ntvdm/ps2.c b/subsystems/ntvdm/ps2.c
new file mode 100644 (file)
index 0000000..0484e71
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            ps2.c
+ * PURPOSE:         PS/2 controller emulation
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "ps2.h"
+#include "emulator.h"
+#include "pic.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+static BYTE KeyboardQueue[KEYBOARD_BUFFER_SIZE];
+static BOOLEAN KeyboardQueueEmpty = TRUE;
+static UINT KeyboardQueueStart = 0;
+static UINT KeyboardQueueEnd = 0;
+static BYTE KeyboardResponse = 0;
+static BOOLEAN KeyboardReadResponse = FALSE, KeyboardWriteResponse = FALSE;
+static BYTE KeyboardConfig = PS2_DEFAULT_CONFIG;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static BOOLEAN KeyboardQueuePush(BYTE ScanCode)
+{
+    /* Check if the keyboard queue is full */
+    if (!KeyboardQueueEmpty && (KeyboardQueueStart == KeyboardQueueEnd))
+    {
+        return FALSE;
+    }
+
+    /* Insert the value in the queue */
+    KeyboardQueue[KeyboardQueueEnd] = ScanCode;
+    KeyboardQueueEnd++;
+    KeyboardQueueEnd %= KEYBOARD_BUFFER_SIZE;
+
+    /* Since we inserted a value, it's not empty anymore */
+    KeyboardQueueEmpty = FALSE;
+
+    return TRUE;
+}
+
+static BOOLEAN KeyboardQueuePop(BYTE *ScanCode)
+{
+    /* Make sure the keyboard queue is not empty */
+    if (KeyboardQueueEmpty) return FALSE;
+
+    /* Get the scan code */
+    *ScanCode = KeyboardQueue[KeyboardQueueStart];
+
+    /* Remove the value from the queue */
+    KeyboardQueueStart++;
+    KeyboardQueueStart %= KEYBOARD_BUFFER_SIZE;
+
+    /* Check if the queue is now empty */
+    if (KeyboardQueueStart == KeyboardQueueEnd)
+    {
+        KeyboardQueueEmpty = TRUE;
+    }
+
+    return TRUE;
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+BYTE KeyboardReadStatus()
+{
+    BYTE Status = 0;
+
+    /* Set the first bit if the data can be read */
+    if (KeyboardReadResponse || !KeyboardQueueEmpty) Status |= 1 << 0;
+
+    /* Always set bit 2 */
+    Status |= 1 << 2;
+
+    /* Set bit 3 if the next byte goes to the controller */
+    if (KeyboardWriteResponse) Status |= 1 << 3;
+
+    return Status;
+}
+
+VOID KeyboardWriteCommand(BYTE Command)
+{
+    switch (Command)
+    {
+        /* Read configuration byte */
+        case 0x20:
+        {
+            KeyboardResponse = KeyboardConfig;
+            KeyboardReadResponse = TRUE;
+
+            break;
+        }
+
+        /* Write configuration byte */
+        case 0x60:
+        /* Write controller output port */
+        case 0xD1:
+        /* Write keyboard output buffer */
+        case 0xD2:
+        /* Write mouse output buffer */
+        case 0xD3:
+        /* Write mouse input buffer */
+        case 0xD4:
+        {
+            /* These commands require a response */
+            KeyboardResponse = Command;
+            KeyboardWriteResponse = TRUE;
+
+            break;
+        }
+
+        /* Disable mouse */
+        case 0xA7:
+        {
+            // TODO: Mouse support
+
+            break;
+        }
+
+        /* Enable mouse */
+        case 0xA8:
+        {
+            // TODO: Mouse support
+
+            break;
+        }
+
+        /* Test mouse port */
+        case 0xA9:
+        {
+            KeyboardResponse = 0;
+            KeyboardReadResponse = TRUE;
+
+            break;
+        }
+
+        /* Test PS/2 controller */
+        case 0xAA:
+        {
+            KeyboardResponse = 0x55;
+            KeyboardReadResponse = TRUE;
+
+            break;
+        }
+
+        /* Disable keyboard */
+        case 0xAD:
+        {
+            // TODO: Not implemented
+            break;
+        }
+
+        /* Enable keyboard */
+        case 0xAE:
+        {
+            // TODO: Not implemented
+            break;
+        }
+
+        /* Read controller output port */
+        case 0xD0:
+        {
+            // TODO: Not implemented
+            break;
+        }
+
+        /* CPU Reset */
+        case 0xF0:
+        case 0xF2:
+        case 0xF4:
+        case 0xF6:
+        case 0xF8:
+        case 0xFA:
+        case 0xFC:
+        case 0xFE:
+        {
+            /* Stop the simulation */
+            VdmRunning = FALSE;
+
+            break;
+        }
+    }
+}
+
+BYTE KeyboardReadData()
+{
+    BYTE Value = 0;
+
+    /* If there was a response byte from the controller, return it */
+    if (KeyboardReadResponse)
+    {
+        KeyboardReadResponse = FALSE;
+        return KeyboardResponse;
+    }
+    
+    /* Otherwise, read the data from the queue */
+    KeyboardQueuePop(&Value);
+
+    return Value;
+}
+
+VOID KeyboardWriteData(BYTE Data)
+{
+    /* Check if the controller is waiting for a response */
+    if (KeyboardWriteResponse)
+    {
+        KeyboardWriteResponse = FALSE;
+
+        /* Check which command it was */
+        switch (KeyboardResponse)
+        {
+            /* Write configuration byte */
+            case 0x60:
+            {
+                KeyboardConfig = Data;
+                break;
+            }
+
+            /* Write controller output */
+            case 0xD1:
+            {
+                /* Check if bit 0 is unset */
+                if (!(Data & (1 << 0)))
+                {
+                    /* CPU disabled - end simulation */
+                    VdmRunning = FALSE;
+                }
+
+                /* Update the A20 line setting */
+                EmulatorSetA20(Data & (1 << 1));
+
+                break;
+            }
+            
+            case 0xD2:
+            {
+                /* Push the data byte to the keyboard queue */
+                KeyboardQueuePush(Data);
+
+                break;
+            }
+
+            case 0xD3:
+            {
+                // TODO: Mouse support
+                break;
+            }
+
+            case 0xD4:
+            {
+                // TODO: Mouse support
+                break;
+            }
+        }
+
+        return;
+    }
+
+    // TODO: Implement PS/2 device commands
+}
+
+VOID CheckForInputEvents()
+{
+    PINPUT_RECORD Buffer;
+    HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
+    DWORD i, j, Count, TotalEvents;
+    BYTE ScanCode;
+
+    /* Get the number of input events */
+    if (!GetNumberOfConsoleInputEvents(ConsoleInput, &Count)) return;
+    if (Count == 0) return;
+
+    /* Allocate the buffer */
+    Buffer = (PINPUT_RECORD)HeapAlloc(GetProcessHeap(), 0, Count * sizeof(INPUT_RECORD));
+    if (Buffer == NULL) return;
+
+    /* Peek the input events */
+    if (!ReadConsoleInput(ConsoleInput, Buffer, Count, &TotalEvents)) goto Cleanup;
+
+    for (i = 0; i < TotalEvents; i++)
+    {
+        /* Check if this is a key event */
+        if (Buffer[i].EventType != KEY_EVENT) continue;
+
+        /* Get the scan code */
+        ScanCode = Buffer[i].Event.KeyEvent.wVirtualScanCode;
+
+        /* If this is a key release, set the highest bit in the scan code */
+        if (!Buffer[i].Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
+
+        /* Push the scan code onto the keyboard queue */
+        for (j = 0; j < Buffer[i].Event.KeyEvent.wRepeatCount; j++)
+        {
+            KeyboardQueuePush(ScanCode);
+        }
+
+        /* Yes, IRQ 1 */
+        PicInterruptRequest(1);
+
+        /* Stop the loop */
+        break;
+    }
+
+Cleanup:
+    HeapFree(GetProcessHeap(), 0, Buffer);
+}
+
+/* EOF */
+
diff --git a/subsystems/ntvdm/ps2.h b/subsystems/ntvdm/ps2.h
new file mode 100644 (file)
index 0000000..2b426b1
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            ps2.h
+ * PURPOSE:         PS/2 controller emulation (header file)
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+#ifndef _PS2_H_
+#define _PS2_H_
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+/* DEFINES ********************************************************************/
+
+#define KEYBOARD_BUFFER_SIZE 32
+#define PS2_DATA_PORT 0x60
+#define PS2_CONTROL_PORT 0x64
+#define PS2_DEFAULT_CONFIG 0x05
+#define KEYBOARD_ACK 0xFA
+#define KEYBOARD_RESEND 0xFE
+
+/* FUNCTIONS ******************************************************************/
+
+BYTE KeyboardReadStatus();
+VOID KeyboardWriteCommand(BYTE Command);
+BYTE KeyboardReadData();
+VOID KeyboardWriteData(BYTE Data);
+VOID CheckForInputEvents();
+
+#endif
diff --git a/subsystems/ntvdm/timer.c b/subsystems/ntvdm/timer.c
new file mode 100644 (file)
index 0000000..76adccb
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            timer.c
+ * PURPOSE:         Programmable Interval Timer emulation
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "timer.h"
+#include "pic.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+static PIT_CHANNEL PitChannels[PIT_CHANNELS];
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+VOID PitWriteCommand(BYTE Value)
+{
+    BYTE Channel = Value >> 6;
+    BYTE Mode = (Value >> 1) & 0x07;
+
+    /* Check if this is a counter latch command */
+    if (((Value >> 4) & 3) == 0)
+    {
+        PitChannels[Channel].LatchSet = TRUE;
+        PitChannels[Channel].LatchedValue = PitChannels[Channel].CurrentValue;
+        return;
+    }
+
+    /* Set the access mode and reset flip-flops */
+    PitChannels[Channel].AccessMode = (Value >> 4) & 3;
+    PitChannels[Channel].Pulsed = FALSE;
+    PitChannels[Channel].LatchSet = FALSE;
+    PitChannels[Channel].InputFlipFlop = FALSE;
+    PitChannels[Channel].OutputFlipFlop = FALSE;
+
+    switch (Mode)
+    {
+        case 0:
+        case 1:
+        case 2:
+        case 3:
+        case 4:
+        case 5:
+        {
+            PitChannels[Channel].Mode = Mode;
+            break;
+        }
+
+        case 6:
+        {
+            PitChannels[Channel].Mode = PIT_MODE_RATE_GENERATOR;
+            break;
+        }
+
+        case 7:
+        {
+            PitChannels[Channel].Mode = PIT_MODE_SQUARE_WAVE;
+            break;
+        }
+    }
+}
+
+BYTE PitReadData(BYTE Channel)
+{
+    WORD CurrentValue = PitChannels[Channel].CurrentValue;
+    BYTE AccessMode = PitChannels[Channel].AccessMode;
+
+    /* Check if the value was latched */
+    if (PitChannels[Channel].LatchSet)
+    {
+        CurrentValue = PitChannels[Channel].LatchedValue;
+
+        if (AccessMode == 1 || AccessMode == 2)
+        {
+            /* The latched value was read as one byte */
+            PitChannels[Channel].LatchSet = FALSE;
+        }
+    }
+
+    /* Use the flip-flop for access mode 3 */
+    if (AccessMode == 3)
+    {
+        AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
+        PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
+
+        /* Check if this was the last read for the latched value */
+        if (!PitChannels[Channel].InputFlipFlop)
+        {
+            /* Yes, the latch value was read as two bytes */
+            PitChannels[Channel].LatchSet = FALSE;
+        }
+    }
+
+    switch (AccessMode)
+    {
+        case 1:
+        {
+            /* Low byte */
+            return CurrentValue & 0x00FF;
+        }
+
+        case 2:
+        {
+            /* High byte */
+            return CurrentValue >> 8;
+        }
+    }
+
+    /* Shouldn't get here */
+    return 0;
+}
+
+VOID PitWriteData(BYTE Channel, BYTE Value)
+{
+    BYTE AccessMode = PitChannels[Channel].AccessMode;
+
+    /* Use the flip-flop for access mode 3 */
+    if (PitChannels[Channel].AccessMode == 3)
+    {
+        AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
+        PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
+    }
+
+    switch (AccessMode)
+    {
+        case 1:
+        {
+            /* Low byte */
+            PitChannels[Channel].ReloadValue &= 0xFF00;
+            PitChannels[Channel].ReloadValue |= Value;
+            break;
+        }
+
+        case 2:
+        {
+            /* High byte */
+            PitChannels[Channel].ReloadValue &= 0x00FF;
+            PitChannels[Channel].ReloadValue |= Value << 8;
+        }
+    }
+}
+
+VOID PitDecrementCount()
+{
+    INT i;
+
+    for (i = 0; i < PIT_CHANNELS; i++)
+    {
+        switch (PitChannels[i].Mode)
+        {
+            case PIT_MODE_INT_ON_TERMINAL_COUNT:
+            {
+                /* Decrement the value */
+                PitChannels[i].CurrentValue--;
+
+                /* Did it fall to the terminal count? */
+                if (PitChannels[i].CurrentValue == 0 && !PitChannels[i].Pulsed)
+                {
+                    /* Yes, raise the output line */
+                    if (i == 0) PicInterruptRequest(0);
+                    PitChannels[i].Pulsed = TRUE;
+                }
+                break;
+            }
+
+            case PIT_MODE_RATE_GENERATOR:
+            {
+                /* Decrement the value */
+                PitChannels[i].CurrentValue--;
+
+                /* Did it fall to zero? */
+                if (PitChannels[i].CurrentValue != 0) break;
+
+                /* Yes, raise the output line and reload */
+                if (i == 0) PicInterruptRequest(0);
+                PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
+
+                break;
+            }
+
+            case PIT_MODE_SQUARE_WAVE:
+            {
+                /* Decrement the value by 2 */
+                PitChannels[i].CurrentValue -= 2;
+
+                /* Did it fall to zero? */
+                if (PitChannels[i].CurrentValue != 0) break;
+
+                /* Yes, toggle the flip-flop */
+                PitChannels[i].OutputFlipFlop = !PitChannels[i].OutputFlipFlop;
+
+                /* Did this create a rising edge in the signal? */
+                if (PitChannels[i].OutputFlipFlop)
+                {
+                    /* Yes, IRQ 0 if this is channel 0 */
+                    if (i == 0) PicInterruptRequest(0);
+                }
+
+                /* Reload the value, but make sure it's even */
+                if (PitChannels[i].ReloadValue % 2)
+                {
+                    /* It's odd, reduce it by 1 */
+                    PitChannels[i].CurrentValue = PitChannels[i].ReloadValue - 1;
+                }
+                else
+                {
+                    /* It was even */
+                    PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
+                }
+
+                break;
+            }
+
+            case PIT_MODE_SOFTWARE_STROBE:
+            {
+                // TODO: NOT IMPLEMENTED
+                break;
+            }
+
+            case PIT_MODE_HARDWARE_ONE_SHOT:
+            case PIT_MODE_HARDWARE_STROBE:
+            {
+                /* These modes do not work on x86 PCs */
+                break;
+            }
+        }
+    }
+}
+
+/* EOF */
+
diff --git a/subsystems/ntvdm/timer.h b/subsystems/ntvdm/timer.h
new file mode 100644 (file)
index 0000000..107d3a7
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            timer.h
+ * PURPOSE:         Programmable Interval Timer emulation (header file)
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+/* DEFINES ********************************************************************/
+
+#define PIT_CHANNELS 3
+#define PIT_BASE_FREQUENCY 1193182LL
+#define PIT_DATA_PORT(x) (0x40 + (x))
+#define PIT_COMMAND_PORT 0x43
+
+enum
+{
+    PIT_MODE_INT_ON_TERMINAL_COUNT,
+    PIT_MODE_HARDWARE_ONE_SHOT,
+    PIT_MODE_RATE_GENERATOR,
+    PIT_MODE_SQUARE_WAVE,
+    PIT_MODE_SOFTWARE_STROBE,
+    PIT_MODE_HARDWARE_STROBE
+};
+
+typedef struct _PIT_CHANNEL
+{
+    WORD ReloadValue;
+    WORD CurrentValue;
+    WORD LatchedValue;
+    INT Mode;
+    BOOLEAN Pulsed;
+    BOOLEAN LatchSet;
+    BOOLEAN InputFlipFlop;
+    BOOLEAN OutputFlipFlop;
+    BYTE AccessMode;
+} PIT_CHANNEL, *PPIT_CHANNEL;
+
+/* FUNCTIONS ******************************************************************/
+
+VOID PitWriteCommand(BYTE Value);
+BYTE PitReadData(BYTE Channel);
+VOID PitWriteData(BYTE Channel, BYTE Value);
+VOID PitDecrementCount();
+
+#endif
+
+/* EOF */
+