diff --git a/subsystems/ntvdm/dos.c b/subsystems/ntvdm/dos.c index 5f91e79de22..1ba037a7fd2 100644 --- a/subsystems/ntvdm/dos.c +++ b/subsystems/ntvdm/dos.c @@ -9,6 +9,7 @@ #include "ntvdm.h" WORD CurrentPsp = SYSTEM_PSP, LastError = 0; +DWORD DiskTransferArea; static VOID DosCombineFreeBlocks(WORD StartBlock) { @@ -433,6 +434,7 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock) /* Execute */ CurrentPsp = Segment; + DiskTransferArea = MAKELONG(0x80, Segment); EmulatorExecute(Segment + Header->e_cs, sizeof(DOS_PSP) + Header->e_ip); Success = TRUE; @@ -466,6 +468,7 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock) /* Execute */ CurrentPsp = Segment; + DiskTransferArea = MAKELONG(0x80, Segment); EmulatorExecute(Segment, 0x100); Success = TRUE; @@ -635,6 +638,13 @@ VOID DosInt21h(WORD CodeSegment) break; } + /* Set Disk Transfer Area */ + case 0x1A: + { + DiskTransferArea = MAKELONG(LOWORD(Edx), DataSegment); + break; + } + /* Set Interrupt Vector */ case 0x25: { @@ -721,6 +731,15 @@ VOID DosInt21h(WORD CodeSegment) break; } + /* Get Disk Transfer Area */ + case 0x2F: + { + EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(DiskTransferArea)); + EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(DiskTransferArea)); + + break; + } + /* Get Interrupt Vector */ case 0x35: { diff --git a/subsystems/ntvdm/emulator.c b/subsystems/ntvdm/emulator.c index 265acd9abae..80dfac9d1ee 100644 --- a/subsystems/ntvdm/emulator.c +++ b/subsystems/ntvdm/emulator.c @@ -14,9 +14,13 @@ softx86_ctx EmulatorContext; softx87_ctx FpuEmulatorContext; +static BOOLEAN A20Line = FALSE; static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size) { + /* If the A20 line is disabled, mask bit 20 */ + if (!A20Line) Address &= ~(1 << 20); + /* Make sure the requested address is valid */ if ((Address + Size) >= MAX_ADDRESS) return; @@ -35,6 +39,9 @@ static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT S static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size) { + /* If the A20 line is disabled, mask bit 20 */ + if (!A20Line) Address &= ~(1 << 20); + /* Make sure the requested address is valid */ if ((Address + Size) >= MAX_ADDRESS) return; @@ -71,6 +78,26 @@ static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size) *Buffer = PicReadData(Address); break; } + + case PIT_DATA_PORT(0): + case PIT_DATA_PORT(1): + case PIT_DATA_PORT(2): + { + *Buffer = PitReadData(Address - PIT_DATA_PORT(0)); + break; + } + + case PS2_CONTROL_PORT: + { + *Buffer = KeyboardReadStatus(); + break; + } + + case PS2_DATA_PORT: + { + *Buffer = KeyboardReadData(); + break; + } } } @@ -107,6 +134,18 @@ static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size PicWriteData(Address, Byte); break; } + + case PS2_CONTROL_PORT: + { + KeyboardWriteCommand(Byte); + break; + } + + case PS2_DATA_PORT: + { + KeyboardWriteData(Byte); + break; + } } } @@ -187,6 +226,16 @@ static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number) } } +static VOID EmulatorHardwareInt(PVOID Context, BYTE Number) +{ + /* Do nothing */ +} + +static VOID EmulatorHardwareIntAck(PVOID Context, BYTE Number) +{ + /* Do nothing */ +} + /* PUBLIC FUNCTIONS ***********************************************************/ BOOLEAN EmulatorInitialize() @@ -196,7 +245,7 @@ BOOLEAN EmulatorInitialize() if (BaseAddress == NULL) return FALSE; /* Initialize the softx86 CPU emulator */ - if (!softx86_init(&EmulatorContext, SX86_CPULEVEL_80186)) + if (!softx86_init(&EmulatorContext, SX86_CPULEVEL_80286)) { HeapFree(GetProcessHeap(), 0, BaseAddress); return FALSE; @@ -220,10 +269,15 @@ BOOLEAN EmulatorInitialize() /* Set interrupt callbacks */ EmulatorContext.callbacks->on_sw_int = EmulatorSoftwareInt; + EmulatorContext.callbacks->on_hw_int = EmulatorHardwareInt; + EmulatorContext.callbacks->on_hw_int_ack = EmulatorHardwareIntAck; /* Connect the emulated FPU to the emulated CPU */ softx87_connect_to_CPU(&EmulatorContext, &FpuEmulatorContext); + /* Enable interrupts */ + EmulatorSetFlag(EMULATOR_FLAG_IF); + return TRUE; } @@ -252,6 +306,12 @@ VOID EmulatorInterrupt(BYTE Number) softx86_make_simple_interrupt_call(&EmulatorContext, &Segment, &Offset); } +VOID EmulatorExternalInterrupt(BYTE Number) +{ + /* Call the softx86 API */ + softx86_ext_hw_signal(&EmulatorContext, Number); +} + ULONG EmulatorGetRegister(ULONG Register) { if (Register < EMULATOR_REG_ES) @@ -294,7 +354,11 @@ VOID EmulatorClearFlag(ULONG Flag) VOID EmulatorStep() { /* Call the softx86 API */ - softx86_step(&EmulatorContext); + if (!softx86_step(&EmulatorContext)) + { + /* Invalid opcode */ + EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE); + } } VOID EmulatorCleanup() @@ -307,4 +371,9 @@ VOID EmulatorCleanup() softx87_free(&FpuEmulatorContext); } +VOID EmulatorSetA20(BOOLEAN Enabled) +{ + A20Line = Enabled; +} + /* EOF */ diff --git a/subsystems/ntvdm/hardware.c b/subsystems/ntvdm/hardware.c index 428294d9875..904d4991292 100644 --- a/subsystems/ntvdm/hardware.c +++ b/subsystems/ntvdm/hardware.c @@ -53,6 +53,9 @@ 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) { @@ -73,7 +76,6 @@ static BOOLEAN KeyboardQueuePush(BYTE ScanCode) return TRUE; } -#if 0 static BOOLEAN KeyboardQueuePop(BYTE *ScanCode) { /* Make sure the keyboard queue is not empty */ @@ -94,7 +96,6 @@ static BOOLEAN KeyboardQueuePop(BYTE *ScanCode) return TRUE; } -#endif /* PUBLIC FUNCTIONS ***********************************************************/ @@ -248,7 +249,7 @@ VOID PicInterruptRequest(BYTE Number) /* Set the appropriate bit in the ISR and interrupt the CPU */ if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << Number; - EmulatorInterrupt(MasterPic.IntOffset + Number); + EmulatorExternalInterrupt(MasterPic.IntOffset + Number); } else if (Number >= 8 && Number < 16) { @@ -279,7 +280,7 @@ VOID PicInterruptRequest(BYTE Number) /* Set the appropriate bit in the ISR and interrupt the CPU */ if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= 1 << Number; - EmulatorInterrupt(SlavePic.IntOffset + Number); + EmulatorExternalInterrupt(SlavePic.IntOffset + Number); } } @@ -497,6 +498,203 @@ VOID PitDecrementCount() } } +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; diff --git a/subsystems/ntvdm/ntvdm.h b/subsystems/ntvdm/ntvdm.h index 071fb7534e0..3709d7dc053 100644 --- a/subsystems/ntvdm/ntvdm.h +++ b/subsystems/ntvdm/ntvdm.h @@ -74,6 +74,9 @@ #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) @@ -92,6 +95,18 @@ #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, @@ -226,9 +241,14 @@ 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); @@ -237,5 +257,7 @@ BOOLEAN EmulatorGetFlag(ULONG Flag); BOOLEAN EmulatorInitialize(); VOID EmulatorStep(); VOID EmulatorCleanup(); +VOID EmulatorHalt(); +VOID EmulatorSetA20(BOOLEAN Enabled); /* EOF */