2013-06-17 00:00:36 +00:00
|
|
|
/*
|
|
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
|
|
* FILE: emulator.c
|
|
|
|
* PURPOSE: Minimal x86 machine emulator for the VDM
|
|
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
2013-07-22 13:51:26 +00:00
|
|
|
#define NDEBUG
|
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
#include "emulator.h"
|
2014-09-30 23:47:23 +00:00
|
|
|
|
|
|
|
#include "cpu/callback.h"
|
|
|
|
#include "cpu/cpu.h"
|
|
|
|
#include "cpu/bop.h"
|
|
|
|
#include <isvbop.h>
|
|
|
|
|
|
|
|
#include "int32.h"
|
2014-01-11 20:59:27 +00:00
|
|
|
|
2014-02-01 16:32:20 +00:00
|
|
|
#include "clock.h"
|
2014-02-26 01:07:09 +00:00
|
|
|
#include "bios/rom.h"
|
2014-01-11 20:59:27 +00:00
|
|
|
#include "hardware/cmos.h"
|
2014-09-16 00:51:15 +00:00
|
|
|
#include "hardware/keyboard.h"
|
|
|
|
#include "hardware/mouse.h"
|
2014-01-11 20:59:27 +00:00
|
|
|
#include "hardware/pic.h"
|
2014-01-25 00:21:51 +00:00
|
|
|
#include "hardware/ps2.h"
|
2014-01-11 20:59:27 +00:00
|
|
|
#include "hardware/speaker.h"
|
|
|
|
#include "hardware/timer.h"
|
|
|
|
#include "hardware/vga.h"
|
|
|
|
|
2013-12-17 02:19:52 +00:00
|
|
|
#include "vddsup.h"
|
2013-11-26 20:20:51 +00:00
|
|
|
#include "io.h"
|
2013-12-17 23:03:23 +00:00
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
LPVOID BaseAddress = NULL;
|
|
|
|
BOOLEAN VdmRunning = TRUE;
|
2013-07-07 20:53:23 +00:00
|
|
|
|
2014-02-01 16:32:20 +00:00
|
|
|
static BOOLEAN A20Line = FALSE;
|
2014-01-28 20:24:24 +00:00
|
|
|
static BYTE Port61hState = 0x00;
|
2013-06-17 00:00:36 +00:00
|
|
|
|
2014-02-01 16:32:20 +00:00
|
|
|
static HANDLE InputThread = NULL;
|
|
|
|
|
2014-01-25 00:21:51 +00:00
|
|
|
LPCWSTR ExceptionName[] =
|
|
|
|
{
|
|
|
|
L"Division By Zero",
|
|
|
|
L"Debug",
|
|
|
|
L"Unexpected Error",
|
|
|
|
L"Breakpoint",
|
|
|
|
L"Integer Overflow",
|
|
|
|
L"Bound Range Exceeded",
|
|
|
|
L"Invalid Opcode",
|
|
|
|
L"FPU Not Available"
|
|
|
|
};
|
|
|
|
|
2013-12-16 23:57:35 +00:00
|
|
|
/* BOP Identifiers */
|
|
|
|
#define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
|
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
|
[NTVDM]
- Use a helper function for copying little chunks of memory (1, 2, 4 and 8 bytes) because a profiling of ntvdm showed that especially 1 and 2 bytes of memory were read the most, and calling RtlCopy/MoveMemory for intensively copying 1 or 2 bytes was shown to be inefficient. We also don't use directly intrinsics/builtins because the compiler cannot know in advance the size of the memory to be copied, it cannot perform the required optimizations. It was checked that using the builtin-memcpy or memmove of GCC when compiling the program in release+full optimization mode just embedded a call to _memcpy, and naively using the movsX intrinsics of MSVC does not do the job of "moving" memory taking into account for the possible overlaps. Therefore, for small sizes (<= 8 bytes), we use copy assignments, whereas for large sizes (and for 3, 5, 7, 9+ bytes) we use the regular method of calling RtlMoveMemory. We gain ~=10% speed with this optimization.
- Also I use >> and & for dividing by 4 and 2 instead of the regular / and % operations because they are not optimized otherwise by default by MSVC (they are however, if you explicitely enable optimizations).
svn path=/trunk/; revision=64792
2014-10-17 22:08:51 +00:00
|
|
|
static inline VOID
|
|
|
|
EmulatorMoveMemory(OUT VOID UNALIGNED *Destination,
|
|
|
|
IN const VOID UNALIGNED *Source,
|
|
|
|
IN SIZE_T Length)
|
|
|
|
{
|
|
|
|
#if 1
|
|
|
|
/*
|
|
|
|
* We use a switch here to detect small moves of memory, as these
|
|
|
|
* constitute the bulk of our moves.
|
|
|
|
* Using RtlMoveMemory for all these small moves would be slow otherwise.
|
|
|
|
*/
|
|
|
|
switch (Length)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return;
|
|
|
|
|
|
|
|
case sizeof(UCHAR):
|
|
|
|
*(PUCHAR)Destination = *(PUCHAR)Source;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case sizeof(USHORT):
|
|
|
|
*(PUSHORT)Destination = *(PUSHORT)Source;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case sizeof(ULONG):
|
|
|
|
*(PULONG)Destination = *(PULONG)Source;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case sizeof(ULONGLONG):
|
|
|
|
*(PULONGLONG)Destination = *(PULONGLONG)Source;
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
#if defined(__GNUC__)
|
|
|
|
__builtin_memmove(Destination, Source, Length);
|
|
|
|
#else
|
|
|
|
RtlMoveMemory(Destination, Source, Length);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#else // defined(_MSC_VER)
|
|
|
|
|
|
|
|
PUCHAR Dest = (PUCHAR)Destination;
|
|
|
|
PUCHAR Src = (PUCHAR)Source;
|
|
|
|
|
|
|
|
SIZE_T Count, NewSize = Length;
|
|
|
|
|
|
|
|
/* Move dword */
|
|
|
|
Count = NewSize >> 2; // NewSize / sizeof(ULONG);
|
|
|
|
NewSize = NewSize & 3; // NewSize % sizeof(ULONG);
|
|
|
|
__movsd(Dest, Src, Count);
|
|
|
|
Dest += Count << 2; // Count * sizeof(ULONG);
|
|
|
|
Src += Count << 2;
|
|
|
|
|
|
|
|
/* Move word */
|
|
|
|
Count = NewSize >> 1; // NewSize / sizeof(USHORT);
|
|
|
|
NewSize = NewSize & 1; // NewSize % sizeof(USHORT);
|
|
|
|
__movsw(Dest, Src, Count);
|
|
|
|
Dest += Count << 1; // Count * sizeof(USHORT);
|
|
|
|
Src += Count << 1;
|
|
|
|
|
|
|
|
/* Move byte */
|
|
|
|
Count = NewSize; // NewSize / sizeof(UCHAR);
|
|
|
|
// NewSize = NewSize; // NewSize % sizeof(UCHAR);
|
|
|
|
__movsb(Dest, Src, Count);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-11-09 17:16:04 +00:00
|
|
|
VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
|
2013-06-17 00:00:36 +00:00
|
|
|
{
|
2013-10-27 12:17:06 +00:00
|
|
|
UNREFERENCED_PARAMETER(State);
|
2013-08-07 19:56:28 +00:00
|
|
|
|
2014-02-24 00:33:21 +00:00
|
|
|
// BIG HACK!!!! To make BIOS images working correctly,
|
|
|
|
// until Aleksander rewrites memory management!!
|
|
|
|
if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
|
|
|
|
|
2013-06-26 17:15:45 +00:00
|
|
|
/* If the A20 line is disabled, mask bit 20 */
|
|
|
|
if (!A20Line) Address &= ~(1 << 20);
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
/* Make sure the requested address is valid */
|
|
|
|
if ((Address + Size) >= MAX_ADDRESS) return;
|
|
|
|
|
2013-12-23 18:09:18 +00:00
|
|
|
/*
|
|
|
|
* Check if we are going to read the VGA memory and
|
|
|
|
* copy it into the virtual address space if needed.
|
|
|
|
*/
|
2013-08-05 23:20:25 +00:00
|
|
|
if (((Address + Size) >= VgaGetVideoBaseAddress())
|
|
|
|
&& (Address < VgaGetVideoLimitAddress()))
|
|
|
|
{
|
|
|
|
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
|
2013-11-09 17:16:04 +00:00
|
|
|
DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
|
|
|
|
- VgaAddress + 1;
|
2014-02-24 00:33:21 +00:00
|
|
|
LPBYTE DestBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
|
2013-08-05 23:20:25 +00:00
|
|
|
|
|
|
|
/* Read from the VGA memory */
|
2013-12-23 18:09:18 +00:00
|
|
|
VgaReadMemory(VgaAddress, DestBuffer, ActualSize);
|
2013-08-05 23:20:25 +00:00
|
|
|
}
|
2013-12-23 18:09:18 +00:00
|
|
|
|
|
|
|
/* Read the data from the virtual address space and store it in the buffer */
|
[NTVDM]
- Use a helper function for copying little chunks of memory (1, 2, 4 and 8 bytes) because a profiling of ntvdm showed that especially 1 and 2 bytes of memory were read the most, and calling RtlCopy/MoveMemory for intensively copying 1 or 2 bytes was shown to be inefficient. We also don't use directly intrinsics/builtins because the compiler cannot know in advance the size of the memory to be copied, it cannot perform the required optimizations. It was checked that using the builtin-memcpy or memmove of GCC when compiling the program in release+full optimization mode just embedded a call to _memcpy, and naively using the movsX intrinsics of MSVC does not do the job of "moving" memory taking into account for the possible overlaps. Therefore, for small sizes (<= 8 bytes), we use copy assignments, whereas for large sizes (and for 3, 5, 7, 9+ bytes) we use the regular method of calling RtlMoveMemory. We gain ~=10% speed with this optimization.
- Also I use >> and & for dividing by 4 and 2 instead of the regular / and % operations because they are not optimized otherwise by default by MSVC (they are however, if you explicitely enable optimizations).
svn path=/trunk/; revision=64792
2014-10-17 22:08:51 +00:00
|
|
|
EmulatorMoveMemory(Buffer, REAL_TO_PHYS(Address), Size);
|
2013-06-17 00:00:36 +00:00
|
|
|
}
|
|
|
|
|
2013-11-09 17:16:04 +00:00
|
|
|
VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
|
2013-06-17 00:00:36 +00:00
|
|
|
{
|
2013-10-27 12:17:06 +00:00
|
|
|
UNREFERENCED_PARAMETER(State);
|
2013-08-07 19:56:28 +00:00
|
|
|
|
2014-02-24 00:33:21 +00:00
|
|
|
// BIG HACK!!!! To make BIOS images working correctly,
|
|
|
|
// until Aleksander rewrites memory management!!
|
|
|
|
if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
|
|
|
|
|
2013-06-26 17:15:45 +00:00
|
|
|
/* If the A20 line is disabled, mask bit 20 */
|
|
|
|
if (!A20Line) Address &= ~(1 << 20);
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
/* Make sure the requested address is valid */
|
|
|
|
if ((Address + Size) >= MAX_ADDRESS) return;
|
|
|
|
|
|
|
|
/* Make sure we don't write to the ROM area */
|
|
|
|
if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
|
|
|
|
|
|
|
|
/* Read the data from the buffer and store it in the virtual address space */
|
[NTVDM]
- Use a helper function for copying little chunks of memory (1, 2, 4 and 8 bytes) because a profiling of ntvdm showed that especially 1 and 2 bytes of memory were read the most, and calling RtlCopy/MoveMemory for intensively copying 1 or 2 bytes was shown to be inefficient. We also don't use directly intrinsics/builtins because the compiler cannot know in advance the size of the memory to be copied, it cannot perform the required optimizations. It was checked that using the builtin-memcpy or memmove of GCC when compiling the program in release+full optimization mode just embedded a call to _memcpy, and naively using the movsX intrinsics of MSVC does not do the job of "moving" memory taking into account for the possible overlaps. Therefore, for small sizes (<= 8 bytes), we use copy assignments, whereas for large sizes (and for 3, 5, 7, 9+ bytes) we use the regular method of calling RtlMoveMemory. We gain ~=10% speed with this optimization.
- Also I use >> and & for dividing by 4 and 2 instead of the regular / and % operations because they are not optimized otherwise by default by MSVC (they are however, if you explicitely enable optimizations).
svn path=/trunk/; revision=64792
2014-10-17 22:08:51 +00:00
|
|
|
EmulatorMoveMemory(REAL_TO_PHYS(Address), Buffer, Size);
|
2013-06-17 00:00:36 +00:00
|
|
|
|
2013-12-23 18:09:18 +00:00
|
|
|
/*
|
|
|
|
* Check if we modified the VGA memory.
|
|
|
|
*/
|
2013-08-05 23:20:25 +00:00
|
|
|
if (((Address + Size) >= VgaGetVideoBaseAddress())
|
|
|
|
&& (Address < VgaGetVideoLimitAddress()))
|
2013-06-17 00:00:36 +00:00
|
|
|
{
|
2013-08-05 23:20:25 +00:00
|
|
|
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
|
2013-11-09 17:16:04 +00:00
|
|
|
DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
|
|
|
|
- VgaAddress + 1;
|
2014-02-24 00:33:21 +00:00
|
|
|
LPBYTE SrcBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
|
2013-08-05 23:20:25 +00:00
|
|
|
|
|
|
|
/* Write to the VGA memory */
|
2013-12-23 18:09:18 +00:00
|
|
|
VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize);
|
2013-06-17 00:00:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-09 17:16:04 +00:00
|
|
|
UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
|
2013-10-27 00:37:01 +00:00
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(State);
|
|
|
|
|
|
|
|
/* Get the interrupt number from the PIC */
|
|
|
|
return PicGetInterrupt();
|
|
|
|
}
|
|
|
|
|
2014-02-01 16:32:20 +00:00
|
|
|
VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
|
|
|
|
{
|
|
|
|
WORD CodeSegment, InstructionPointer;
|
|
|
|
PBYTE Opcode;
|
|
|
|
|
|
|
|
ASSERT(ExceptionNumber < 8);
|
|
|
|
|
|
|
|
/* Get the CS:IP */
|
|
|
|
InstructionPointer = Stack[STACK_IP];
|
|
|
|
CodeSegment = Stack[STACK_CS];
|
|
|
|
Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
|
|
|
|
|
|
|
|
/* Display a message to the user */
|
|
|
|
DisplayMessage(L"Exception: %s occured at %04X:%04X\n"
|
|
|
|
L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
|
|
|
|
ExceptionName[ExceptionNumber],
|
|
|
|
CodeSegment,
|
|
|
|
InstructionPointer,
|
|
|
|
Opcode[0],
|
|
|
|
Opcode[1],
|
|
|
|
Opcode[2],
|
|
|
|
Opcode[3],
|
|
|
|
Opcode[4],
|
|
|
|
Opcode[5],
|
|
|
|
Opcode[6],
|
|
|
|
Opcode[7],
|
|
|
|
Opcode[8],
|
|
|
|
Opcode[9]);
|
|
|
|
|
2014-10-13 18:51:40 +00:00
|
|
|
Fast486DumpState(&EmulatorContext);
|
|
|
|
|
2014-02-01 16:32:20 +00:00
|
|
|
/* Stop the VDM */
|
2014-03-02 14:37:51 +00:00
|
|
|
EmulatorTerminate();
|
2014-02-01 16:32:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-03-02 14:37:51 +00:00
|
|
|
VOID EmulatorTerminate(VOID)
|
|
|
|
{
|
|
|
|
/* Stop the VDM */
|
2014-10-14 00:14:56 +00:00
|
|
|
CpuUnsimulate(); // Halt the CPU
|
2014-03-02 14:37:51 +00:00
|
|
|
VdmRunning = FALSE;
|
|
|
|
}
|
|
|
|
|
2014-02-01 16:32:20 +00:00
|
|
|
VOID EmulatorInterruptSignal(VOID)
|
|
|
|
{
|
|
|
|
/* Call the Fast486 API */
|
|
|
|
Fast486InterruptSignal(&EmulatorContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID EmulatorSetA20(BOOLEAN Enabled)
|
|
|
|
{
|
|
|
|
A20Line = Enabled;
|
|
|
|
}
|
|
|
|
|
2014-03-02 14:37:51 +00:00
|
|
|
static VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack)
|
2013-12-16 23:57:35 +00:00
|
|
|
{
|
|
|
|
DPRINT1("NTVDM: BOP_DEBUGGER\n");
|
|
|
|
DebugBreak();
|
|
|
|
}
|
|
|
|
|
2014-10-23 11:44:07 +00:00
|
|
|
static BYTE WINAPI Port61hRead(USHORT Port)
|
2014-01-28 20:24:24 +00:00
|
|
|
{
|
|
|
|
return Port61hState;
|
|
|
|
}
|
|
|
|
|
2014-10-23 11:44:07 +00:00
|
|
|
static VOID WINAPI Port61hWrite(USHORT Port, BYTE Data)
|
2014-01-28 20:24:24 +00:00
|
|
|
{
|
2014-09-27 15:33:27 +00:00
|
|
|
// BOOLEAN SpeakerStateChange = FALSE;
|
2014-01-28 20:24:24 +00:00
|
|
|
BYTE OldPort61hState = Port61hState;
|
|
|
|
|
|
|
|
/* Only the four lowest bytes can be written */
|
|
|
|
Port61hState = (Port61hState & 0xF0) | (Data & 0x0F);
|
|
|
|
|
|
|
|
if ((OldPort61hState ^ Port61hState) & 0x01)
|
|
|
|
{
|
2014-01-29 00:25:43 +00:00
|
|
|
DPRINT("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off");
|
2014-09-27 15:33:27 +00:00
|
|
|
PitSetGate(2, !!(Port61hState & 0x01));
|
|
|
|
// SpeakerStateChange = TRUE;
|
2014-01-28 20:24:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((OldPort61hState ^ Port61hState) & 0x02)
|
|
|
|
{
|
|
|
|
/* There were some change for the speaker... */
|
2014-01-29 00:25:43 +00:00
|
|
|
DPRINT("Speaker %s\n", Port61hState & 0x02 ? "on" : "off");
|
2014-09-27 15:33:27 +00:00
|
|
|
// SpeakerStateChange = TRUE;
|
2014-01-28 20:24:24 +00:00
|
|
|
}
|
2014-09-27 15:33:27 +00:00
|
|
|
// if (SpeakerStateChange) SpeakerChange();
|
2014-01-29 00:25:43 +00:00
|
|
|
SpeakerChange();
|
2014-01-28 20:24:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State)
|
|
|
|
{
|
|
|
|
if (State)
|
|
|
|
{
|
|
|
|
DPRINT("PicInterruptRequest\n");
|
|
|
|
PicInterruptRequest(0); // Raise IRQ 0
|
|
|
|
}
|
|
|
|
// else < Lower IRQ 0 >
|
|
|
|
}
|
|
|
|
|
|
|
|
static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
if (State)
|
|
|
|
{
|
|
|
|
/* Set bit 4 of Port 61h */
|
|
|
|
Port61hState |= 1 << 4;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Clear bit 4 of Port 61h */
|
|
|
|
Port61hState &= ~(1 << 4);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
Port61hState = (Port61hState & 0xEF) | (State << 4);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State)
|
|
|
|
{
|
2014-09-27 15:33:27 +00:00
|
|
|
BYTE OldPort61hState = Port61hState;
|
2014-01-29 00:25:43 +00:00
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
#if 0
|
|
|
|
if (State)
|
|
|
|
{
|
|
|
|
/* Set bit 5 of Port 61h */
|
|
|
|
Port61hState |= 1 << 5;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Clear bit 5 of Port 61h */
|
|
|
|
Port61hState &= ~(1 << 5);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
Port61hState = (Port61hState & 0xDF) | (State << 5);
|
|
|
|
#endif
|
2014-09-27 15:33:27 +00:00
|
|
|
|
|
|
|
if ((OldPort61hState ^ Port61hState) & 0x20)
|
|
|
|
{
|
|
|
|
DPRINT("PitChan2Out -- Port61hState changed\n");
|
|
|
|
SpeakerChange();
|
|
|
|
}
|
2014-01-28 20:24:24 +00:00
|
|
|
}
|
|
|
|
|
2014-09-16 00:51:15 +00:00
|
|
|
|
|
|
|
static DWORD
|
|
|
|
WINAPI
|
|
|
|
PumpConsoleInput(LPVOID Parameter)
|
|
|
|
{
|
|
|
|
HANDLE ConsoleInput = (HANDLE)Parameter;
|
|
|
|
INPUT_RECORD InputRecord;
|
|
|
|
DWORD Count;
|
|
|
|
|
|
|
|
while (VdmRunning)
|
|
|
|
{
|
|
|
|
/* Make sure the task event is signaled */
|
|
|
|
WaitForSingleObject(VdmTaskEvent, INFINITE);
|
|
|
|
|
|
|
|
/* Wait for an input record */
|
|
|
|
if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count))
|
|
|
|
{
|
|
|
|
DWORD LastError = GetLastError();
|
|
|
|
DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput, Count, LastError);
|
|
|
|
return LastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(Count != 0);
|
|
|
|
|
|
|
|
/* Check the event type */
|
|
|
|
switch (InputRecord.EventType)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Hardware events
|
|
|
|
*/
|
|
|
|
case KEY_EVENT:
|
|
|
|
KeyboardEventHandler(&InputRecord.Event.KeyEvent);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MOUSE_EVENT:
|
|
|
|
MouseEventHandler(&InputRecord.Event.MouseEvent);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WINDOW_BUFFER_SIZE_EVENT:
|
|
|
|
ScreenEventHandler(&InputRecord.Event.WindowBufferSizeEvent);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Interface events
|
|
|
|
*/
|
|
|
|
case MENU_EVENT:
|
|
|
|
MenuEventHandler(&InputRecord.Event.MenuEvent);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FOCUS_EVENT:
|
|
|
|
FocusEventHandler(&InputRecord.Event.FocusEvent);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VOID EnableExtraHardware(HANDLE ConsoleInput)
|
|
|
|
{
|
|
|
|
DWORD ConInMode;
|
|
|
|
|
|
|
|
if (GetConsoleMode(ConsoleInput, &ConInMode))
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
// GetNumberOfConsoleMouseButtons();
|
|
|
|
// GetSystemMetrics(SM_CMOUSEBUTTONS);
|
|
|
|
// GetSystemMetrics(SM_MOUSEPRESENT);
|
|
|
|
if (MousePresent)
|
|
|
|
{
|
|
|
|
#endif
|
|
|
|
/* Support mouse input events if there is a mouse on the system */
|
|
|
|
ConInMode |= ENABLE_MOUSE_INPUT;
|
|
|
|
#if 0
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Do not support mouse input events if there is no mouse on the system */
|
|
|
|
ConInMode &= ~ENABLE_MOUSE_INPUT;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SetConsoleMode(ConsoleInput, ConInMode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
|
2014-09-28 16:27:30 +00:00
|
|
|
static VOID
|
|
|
|
DumpMemoryRaw(HANDLE hFile)
|
2014-05-17 23:49:35 +00:00
|
|
|
{
|
2014-09-28 16:27:30 +00:00
|
|
|
PVOID Buffer;
|
|
|
|
SIZE_T Size;
|
2014-05-17 23:49:35 +00:00
|
|
|
|
2014-09-28 16:27:30 +00:00
|
|
|
/* Dump the VM memory */
|
|
|
|
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
|
|
|
|
Buffer = REAL_TO_PHYS(NULL);
|
|
|
|
Size = MAX_ADDRESS - (ULONG_PTR)(NULL);
|
|
|
|
WriteFile(hFile, Buffer, Size, &Size, NULL);
|
|
|
|
}
|
2014-05-17 23:49:35 +00:00
|
|
|
|
2014-09-28 16:27:30 +00:00
|
|
|
static VOID
|
|
|
|
DumpMemoryTxt(HANDLE hFile)
|
|
|
|
{
|
2014-05-17 23:49:35 +00:00
|
|
|
#define LINE_SIZE 75 + 2
|
|
|
|
ULONG i;
|
|
|
|
PBYTE Ptr1, Ptr2;
|
|
|
|
CHAR LineBuffer[LINE_SIZE];
|
|
|
|
PCHAR Line;
|
|
|
|
SIZE_T LineSize;
|
|
|
|
|
|
|
|
/* Dump the VM memory */
|
|
|
|
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
|
|
|
|
Ptr1 = Ptr2 = REAL_TO_PHYS(NULL);
|
|
|
|
while (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr1) > 0)
|
|
|
|
{
|
|
|
|
Ptr1 = Ptr2;
|
|
|
|
Line = LineBuffer;
|
|
|
|
|
|
|
|
/* Print the address */
|
|
|
|
Line += snprintf(Line, LINE_SIZE + LineBuffer - Line, "%08x ", PHYS_TO_REAL(Ptr1));
|
|
|
|
|
|
|
|
/* Print up to 16 bytes... */
|
|
|
|
|
|
|
|
/* ... in hexadecimal form first... */
|
|
|
|
i = 0;
|
|
|
|
while (i++ <= 0x0F && (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr1) > 0))
|
|
|
|
{
|
|
|
|
Line += snprintf(Line, LINE_SIZE + LineBuffer - Line, " %02x", *Ptr1);
|
|
|
|
++Ptr1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ... align with spaces if needed... */
|
|
|
|
RtlFillMemory(Line, 0x0F + 4 - i, ' ');
|
|
|
|
Line += 0x0F + 4 - i;
|
|
|
|
|
|
|
|
/* ... then in character form. */
|
|
|
|
i = 0;
|
|
|
|
while (i++ <= 0x0F && (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr2) > 0))
|
|
|
|
{
|
2014-05-17 23:55:14 +00:00
|
|
|
*Line++ = ((*Ptr2 >= 0x20 && *Ptr2 <= 0x7E) || (*Ptr2 >= 0x80 && *Ptr2 < 0xFF) ? *Ptr2 : '.');
|
2014-05-17 23:49:35 +00:00
|
|
|
++Ptr2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Newline */
|
|
|
|
*Line++ = '\r';
|
|
|
|
*Line++ = '\n';
|
|
|
|
|
|
|
|
/* Finally write the line to the file */
|
|
|
|
LineSize = Line - LineBuffer;
|
|
|
|
WriteFile(hFile, LineBuffer, LineSize, &LineSize, NULL);
|
|
|
|
}
|
2014-09-28 16:27:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID DumpMemory(BOOLEAN TextFormat)
|
|
|
|
{
|
|
|
|
static ULONG DumpNumber = 0;
|
|
|
|
|
|
|
|
HANDLE hFile;
|
|
|
|
WCHAR FileName[MAX_PATH];
|
|
|
|
|
|
|
|
/* Build a suitable file name */
|
|
|
|
_snwprintf(FileName, MAX_PATH,
|
|
|
|
L"memdump%lu.%s",
|
|
|
|
DumpNumber,
|
|
|
|
TextFormat ? L"txt" : L"dat");
|
|
|
|
++DumpNumber;
|
|
|
|
|
|
|
|
DPRINT1("Creating memory dump file '%S'...\n", FileName);
|
|
|
|
|
|
|
|
/* Always create the dump file */
|
|
|
|
hFile = CreateFileW(FileName,
|
|
|
|
GENERIC_WRITE,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
CREATE_ALWAYS,
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
|
|
|
|
FileName, GetLastError());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dump the VM memory in the chosen format */
|
|
|
|
if (TextFormat)
|
|
|
|
DumpMemoryTxt(hFile);
|
|
|
|
else
|
|
|
|
DumpMemoryRaw(hFile);
|
2014-05-17 23:49:35 +00:00
|
|
|
|
|
|
|
/* Close the file */
|
|
|
|
CloseHandle(hFile);
|
2014-09-14 00:43:43 +00:00
|
|
|
|
|
|
|
DPRINT1("Memory dump done\n");
|
2014-05-17 23:49:35 +00:00
|
|
|
}
|
|
|
|
|
2014-01-25 00:21:51 +00:00
|
|
|
BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
|
2013-06-17 00:00:36 +00:00
|
|
|
{
|
|
|
|
/* Allocate memory for the 16-bit address space */
|
2014-09-28 16:27:30 +00:00
|
|
|
BaseAddress = HeapAlloc(GetProcessHeap(), /*HEAP_ZERO_MEMORY*/ 0, MAX_ADDRESS);
|
2014-01-11 20:59:27 +00:00
|
|
|
if (BaseAddress == NULL)
|
|
|
|
{
|
|
|
|
wprintf(L"FATAL: Failed to allocate VDM memory.\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
2014-09-28 22:35:05 +00:00
|
|
|
/*
|
|
|
|
* For diagnostics purposes, we fill the memory with INT 0x03 codes
|
|
|
|
* so that if a program wants to execute random code in memory, we can
|
|
|
|
* retrieve the exact CS:IP where the problem happens.
|
|
|
|
*/
|
|
|
|
RtlFillMemory(BaseAddress, MAX_ADDRESS, 0xCC);
|
2014-01-11 20:59:27 +00:00
|
|
|
|
|
|
|
/* Initialize I/O ports */
|
|
|
|
/* Initialize RAM */
|
2013-06-17 00:00:36 +00:00
|
|
|
|
2014-09-30 23:47:23 +00:00
|
|
|
/* Initialize the CPU */
|
|
|
|
|
2014-02-01 16:32:20 +00:00
|
|
|
/* Initialize the internal clock */
|
|
|
|
if (!ClockInitialize())
|
|
|
|
{
|
|
|
|
wprintf(L"FATAL: Failed to initialize the clock\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2013-11-01 01:46:58 +00:00
|
|
|
/* Initialize the CPU */
|
2014-09-30 23:47:23 +00:00
|
|
|
CpuInitialize();
|
|
|
|
// Fast486Initialize(&EmulatorContext,
|
|
|
|
// EmulatorReadMemory,
|
|
|
|
// EmulatorWriteMemory,
|
|
|
|
// EmulatorReadIo,
|
|
|
|
// EmulatorWriteIo,
|
|
|
|
// NULL,
|
|
|
|
// EmulatorBiosOperation,
|
|
|
|
// EmulatorIntAcknowledge,
|
|
|
|
// NULL /* TODO: Use a TLB */);
|
2013-06-17 00:00:36 +00:00
|
|
|
|
2014-02-01 16:32:20 +00:00
|
|
|
/* Initialize DMA */
|
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
/* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
|
|
|
|
PicInitialize();
|
|
|
|
PitInitialize();
|
|
|
|
CmosInitialize();
|
|
|
|
SpeakerInitialize();
|
2013-12-17 02:19:52 +00:00
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Set output functions */
|
|
|
|
PitSetOutFunction(0, NULL, PitChan0Out);
|
|
|
|
PitSetOutFunction(1, NULL, PitChan1Out);
|
|
|
|
PitSetOutFunction(2, NULL, PitChan2Out);
|
|
|
|
|
|
|
|
/* Register the I/O Ports */
|
|
|
|
RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite);
|
|
|
|
|
2014-01-25 00:21:51 +00:00
|
|
|
/* Set the console input mode */
|
2014-02-28 21:21:41 +00:00
|
|
|
// FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
|
|
|
|
// upon console window events (screen buffer resize, ...).
|
|
|
|
SetConsoleMode(ConsoleInput, ENABLE_PROCESSED_INPUT /* | ENABLE_WINDOW_INPUT */);
|
2014-03-02 01:45:57 +00:00
|
|
|
// SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
|
|
|
|
|
2014-09-16 00:51:15 +00:00
|
|
|
/**/EnableExtraHardware(ConsoleInput);/**/
|
|
|
|
|
|
|
|
/* Initialize the PS/2 port */
|
|
|
|
PS2Initialize();
|
|
|
|
|
|
|
|
/* Initialize the keyboard and mouse and connect them to their PS/2 ports */
|
|
|
|
KeyboardInit(0);
|
|
|
|
MouseInit(1);
|
2014-01-25 00:21:51 +00:00
|
|
|
|
2014-05-11 19:25:09 +00:00
|
|
|
/**************** ATTACH INPUT WITH CONSOLE *****************/
|
2014-02-01 16:32:20 +00:00
|
|
|
/* Start the input thread */
|
|
|
|
InputThread = CreateThread(NULL, 0, &PumpConsoleInput, ConsoleInput, 0, NULL);
|
2014-05-11 19:25:09 +00:00
|
|
|
if (InputThread == NULL)
|
|
|
|
{
|
|
|
|
DisplayMessage(L"Failed to create the console input thread.");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/************************************************************/
|
2014-02-01 16:32:20 +00:00
|
|
|
|
2014-01-25 00:21:51 +00:00
|
|
|
/* Initialize the VGA */
|
2014-05-11 19:25:09 +00:00
|
|
|
if (!VgaInitialize(ConsoleOutput))
|
|
|
|
{
|
|
|
|
DisplayMessage(L"Failed to initialize VGA support.");
|
|
|
|
return FALSE;
|
|
|
|
}
|
2014-01-25 00:21:51 +00:00
|
|
|
|
2014-02-23 16:09:35 +00:00
|
|
|
/* Initialize the software callback system and register the emulator BOPs */
|
2014-09-30 23:47:23 +00:00
|
|
|
InitializeInt32();
|
2014-02-01 16:32:20 +00:00
|
|
|
RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop);
|
2014-09-30 23:47:23 +00:00
|
|
|
// RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
|
2013-12-16 23:57:35 +00:00
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
/* Initialize VDD support */
|
|
|
|
VDDSupInitialize();
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-11-01 01:46:58 +00:00
|
|
|
VOID EmulatorCleanup(VOID)
|
|
|
|
{
|
2014-03-01 20:58:42 +00:00
|
|
|
VgaCleanup();
|
2014-02-01 16:32:20 +00:00
|
|
|
|
|
|
|
/* Close the input thread handle */
|
|
|
|
if (InputThread != NULL) CloseHandle(InputThread);
|
|
|
|
InputThread = NULL;
|
|
|
|
|
2014-01-25 00:21:51 +00:00
|
|
|
PS2Cleanup();
|
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
SpeakerCleanup();
|
|
|
|
CmosCleanup();
|
|
|
|
// PitCleanup();
|
|
|
|
// PicCleanup();
|
|
|
|
|
2014-09-30 23:47:23 +00:00
|
|
|
CpuCleanup();
|
2014-01-11 20:59:27 +00:00
|
|
|
|
2013-11-01 01:46:58 +00:00
|
|
|
/* Free the memory allocated for the 16-bit address space */
|
|
|
|
if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
|
|
|
|
}
|
|
|
|
|
2013-06-26 17:15:45 +00:00
|
|
|
|
2013-08-31 19:18:12 +00:00
|
|
|
|
2014-02-01 16:32:20 +00:00
|
|
|
VOID
|
|
|
|
WINAPI
|
|
|
|
VDDSimulate16(VOID)
|
2013-06-26 17:15:45 +00:00
|
|
|
{
|
2014-09-30 23:47:23 +00:00
|
|
|
CpuSimulate();
|
2013-06-26 17:15:45 +00:00
|
|
|
}
|
|
|
|
|
2013-12-24 15:52:31 +00:00
|
|
|
VOID
|
|
|
|
WINAPI
|
|
|
|
VDDTerminateVDM(VOID)
|
|
|
|
{
|
|
|
|
/* Stop the VDM */
|
2014-03-02 14:37:51 +00:00
|
|
|
EmulatorTerminate();
|
2013-12-24 15:52:31 +00:00
|
|
|
}
|
|
|
|
|
2013-12-17 23:03:23 +00:00
|
|
|
PBYTE
|
|
|
|
WINAPI
|
2013-12-23 18:09:18 +00:00
|
|
|
Sim32pGetVDMPointer(IN ULONG Address,
|
|
|
|
IN BOOLEAN ProtectedMode)
|
2013-12-17 22:10:58 +00:00
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
UNREFERENCED_PARAMETER(ProtectedMode);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* HIWORD(Address) == Segment (if ProtectedMode == FALSE)
|
|
|
|
* or Selector (if ProtectedMode == TRUE )
|
|
|
|
* LOWORD(Address) == Offset
|
|
|
|
*/
|
2013-12-23 18:09:18 +00:00
|
|
|
return (PBYTE)FAR_POINTER(Address);
|
2013-12-17 22:10:58 +00:00
|
|
|
}
|
|
|
|
|
2013-12-17 23:03:23 +00:00
|
|
|
PBYTE
|
|
|
|
WINAPI
|
2013-12-23 18:09:18 +00:00
|
|
|
MGetVdmPointer(IN ULONG Address,
|
|
|
|
IN ULONG Size,
|
|
|
|
IN BOOLEAN ProtectedMode)
|
2013-12-17 22:10:58 +00:00
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(Size);
|
|
|
|
return Sim32pGetVDMPointer(Address, ProtectedMode);
|
|
|
|
}
|
|
|
|
|
2013-12-17 23:03:23 +00:00
|
|
|
PVOID
|
|
|
|
WINAPI
|
|
|
|
VdmMapFlat(IN USHORT Segment,
|
|
|
|
IN ULONG Offset,
|
|
|
|
IN VDM_MODE Mode)
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
UNREFERENCED_PARAMETER(Mode);
|
|
|
|
|
|
|
|
return SEG_OFF_TO_PTR(Segment, Offset);
|
|
|
|
}
|
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
BOOL
|
2013-12-17 23:03:23 +00:00
|
|
|
WINAPI
|
|
|
|
VdmFlushCache(IN USHORT Segment,
|
|
|
|
IN ULONG Offset,
|
|
|
|
IN ULONG Size,
|
|
|
|
IN VDM_MODE Mode)
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
VdmUnmapFlat(IN USHORT Segment,
|
|
|
|
IN ULONG Offset,
|
|
|
|
IN PVOID Buffer,
|
|
|
|
IN VDM_MODE Mode)
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
/* EOF */
|