mirror of
https://github.com/reactos/reactos.git
synced 2024-08-01 00:58:16 +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
|
@ -5,7 +5,9 @@ list(APPEND SOURCE
|
||||||
bios.c
|
bios.c
|
||||||
dos.c
|
dos.c
|
||||||
emulator.c
|
emulator.c
|
||||||
hardware.c
|
pic.c
|
||||||
|
timer.c
|
||||||
|
ps2.c
|
||||||
ntvdm.c
|
ntvdm.c
|
||||||
ntvdm.rc)
|
ntvdm.rc)
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,39 @@
|
||||||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ntvdm.h"
|
/* INCLUDES *******************************************************************/
|
||||||
|
|
||||||
BYTE CursorRow, CursorCol;
|
#include "bios.h"
|
||||||
WORD ConsoleWidth, ConsoleHeight;
|
#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()
|
BOOLEAN BiosInitialize()
|
||||||
{
|
{
|
||||||
|
@ -83,24 +112,6 @@ BOOLEAN BiosInitialize()
|
||||||
return TRUE;
|
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)
|
VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress)
|
||||||
{
|
{
|
||||||
ULONG i;
|
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>
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ntvdm.h"
|
/* INCLUDES *******************************************************************/
|
||||||
|
|
||||||
WORD CurrentPsp = SYSTEM_PSP, LastError = 0;
|
#include "dos.h"
|
||||||
DWORD DiskTransferArea;
|
#include "emulator.h"
|
||||||
|
|
||||||
|
/* PRIVATE VARIABLES **********************************************************/
|
||||||
|
|
||||||
|
static WORD CurrentPsp = SYSTEM_PSP;
|
||||||
|
static DWORD DiskTransferArea;
|
||||||
|
|
||||||
|
/* PRIVATE FUNCTIONS **********************************************************/
|
||||||
|
|
||||||
static VOID DosCombineFreeBlocks(WORD StartBlock)
|
static VOID DosCombineFreeBlocks(WORD StartBlock)
|
||||||
{
|
{
|
||||||
|
@ -78,6 +85,8 @@ static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
|
||||||
return DestSegment;
|
return DestSegment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||||
|
|
||||||
WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
|
WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
|
||||||
{
|
{
|
||||||
WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0;
|
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 *******************************************************************/
|
/* INCLUDES *******************************************************************/
|
||||||
|
|
||||||
#include "ntvdm.h"
|
#include "emulator.h"
|
||||||
#include <softx86/softx86.h>
|
#include "bios.h"
|
||||||
#include <softx86/softx87.h>
|
#include "dos.h"
|
||||||
|
#include "pic.h"
|
||||||
|
#include "ps2.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
softx86_ctx EmulatorContext;
|
/* PRIVATE VARIABLES **********************************************************/
|
||||||
softx87_ctx FpuEmulatorContext;
|
|
||||||
|
static softx86_ctx EmulatorContext;
|
||||||
|
static softx87_ctx FpuEmulatorContext;
|
||||||
static BOOLEAN A20Line = FALSE;
|
static BOOLEAN A20Line = FALSE;
|
||||||
|
|
||||||
|
/* PRIVATE FUNCTIONS **********************************************************/
|
||||||
|
|
||||||
static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
||||||
{
|
{
|
||||||
/* If the A20 line is disabled, mask bit 20 */
|
/* 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>
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* INCLUDES *******************************************************************/
|
||||||
|
|
||||||
#include "ntvdm.h"
|
#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;
|
BOOLEAN VdmRunning = TRUE;
|
||||||
LPVOID BaseAddress = NULL;
|
LPVOID BaseAddress = NULL;
|
||||||
|
@ -22,6 +32,8 @@ LPCWSTR ExceptionName[] =
|
||||||
L"FPU Not Available"
|
L"FPU Not Available"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||||
|
|
||||||
VOID DisplayMessage(LPCWSTR Format, ...)
|
VOID DisplayMessage(LPCWSTR Format, ...)
|
||||||
{
|
{
|
||||||
WCHAR Buffer[256];
|
WCHAR Buffer[256];
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTVDM_H_
|
||||||
|
#define _NTVDM_H_
|
||||||
|
|
||||||
/* INCLUDES *******************************************************************/
|
/* INCLUDES *******************************************************************/
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -23,193 +26,8 @@
|
||||||
#define MAX_SEGMENT 0xFFFF
|
#define MAX_SEGMENT 0xFFFF
|
||||||
#define MAX_OFFSET 0xFFFF
|
#define MAX_OFFSET 0xFFFF
|
||||||
#define MAX_ADDRESS TO_LINEAR(MAX_SEGMENT, MAX_OFFSET)
|
#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
|
#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 ******************************************************************/
|
/* FUNCTIONS ******************************************************************/
|
||||||
|
|
||||||
extern LPVOID BaseAddress;
|
extern LPVOID BaseAddress;
|
||||||
|
@ -217,47 +35,7 @@ extern BOOLEAN VdmRunning;
|
||||||
extern LPCWSTR ExceptionName[];
|
extern LPCWSTR ExceptionName[];
|
||||||
|
|
||||||
VOID DisplayMessage(LPCWSTR Format, ...);
|
VOID DisplayMessage(LPCWSTR Format, ...);
|
||||||
BOOLEAN BiosInitialize();
|
|
||||||
VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress);
|
#endif
|
||||||
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);
|
|
||||||
|
|
||||||
/* EOF */
|
/* 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