mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
[NTVDM]
Clean up the code and properly separate different modules into different files. svn path=/branches/ntvdm/; revision=59344
This commit is contained in:
parent
44f2787b1f
commit
4c4c69fc37
16 changed files with 1229 additions and 999 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
36
subsystems/ntvdm/bios.h
Normal file
36
subsystems/ntvdm/bios.h
Normal file
|
@ -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
|
|
@ -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;
|
||||
|
|
133
subsystems/ntvdm/dos.h
Normal file
133
subsystems/ntvdm/dos.h
Normal file
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
85
subsystems/ntvdm/emulator.h
Normal file
85
subsystems/ntvdm/emulator.h
Normal file
|
@ -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 */
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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];
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
#ifndef _NTVDM_H_
|
||||
#define _NTVDM_H_
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include <windows.h>
|
||||
|
@ -23,193 +26,8 @@
|
|||
#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 */
|
||||
|
|
205
subsystems/ntvdm/pic.c
Normal file
205
subsystems/ntvdm/pic.c
Normal file
|
@ -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 */
|
57
subsystems/ntvdm/pic.h
Normal file
57
subsystems/ntvdm/pic.h
Normal file
|
@ -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 */
|
313
subsystems/ntvdm/ps2.c
Normal file
313
subsystems/ntvdm/ps2.c
Normal file
|
@ -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 */
|
||||
|
33
subsystems/ntvdm/ps2.h
Normal file
33
subsystems/ntvdm/ps2.h
Normal file
|
@ -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
|
235
subsystems/ntvdm/timer.c
Normal file
235
subsystems/ntvdm/timer.c
Normal file
|
@ -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 */
|
||||
|
56
subsystems/ntvdm/timer.h
Normal file
56
subsystems/ntvdm/timer.h
Normal file
|
@ -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 */
|
||||
|
Loading…
Reference in a new issue