X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=subsystems%2Fntvdm%2Femulator.c;h=ab2fabed94b9b641edd18fee7fc63adb994afdab;hp=2caed1c17951e9f77bb319c00dc779d69d45bf4f;hb=b152f5cf439d28bf0aad8689e3457ad020608b87;hpb=3d36bf445140d5ccbeee9bb73bcf7fb1019e842f diff --git a/subsystems/ntvdm/emulator.c b/subsystems/ntvdm/emulator.c index 2caed1c1795..ab2fabed94b 100644 --- a/subsystems/ntvdm/emulator.c +++ b/subsystems/ntvdm/emulator.c @@ -11,22 +11,51 @@ #define NDEBUG #include "emulator.h" -#include "bios/bios.h" +#include "callback.h" + +#include "clock.h" +#include "bios/rom.h" +#include "hardware/cmos.h" +#include "hardware/pic.h" +#include "hardware/ps2.h" +#include "hardware/speaker.h" +#include "hardware/timer.h" +#include "hardware/vga.h" + #include "bop.h" #include "vddsup.h" #include "io.h" -#include "registers.h" -#include "hardware/vga.h" -#include "hardware/pic.h" -// HACK -typedef INT VDM_MODE; +#include /* PRIVATE VARIABLES **********************************************************/ FAST486_STATE EmulatorContext; +BOOLEAN CpuSimulate = FALSE; + +/* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */ +const static INT MaxCpuCallLevel = 32; +static INT CpuCallLevel = 0; + +LPVOID BaseAddress = NULL; +BOOLEAN VdmRunning = TRUE; + +static BOOLEAN A20Line = FALSE; +static BYTE Port61hState = 0x00; + +static HANDLE InputThread = NULL; -static BOOLEAN A20Line = FALSE; +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" +}; /* BOP Identifiers */ #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app @@ -37,6 +66,10 @@ VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer { UNREFERENCED_PARAMETER(State); + // BIG HACK!!!! To make BIOS images working correctly, + // until Aleksander rewrites memory management!! + if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000; + /* If the A20 line is disabled, mask bit 20 */ if (!A20Line) Address &= ~(1 << 20); @@ -53,20 +86,24 @@ VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress()); DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress()) - VgaAddress + 1; - LPBYTE DestBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress); + LPBYTE DestBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress); /* Read from the VGA memory */ VgaReadMemory(VgaAddress, DestBuffer, ActualSize); } /* Read the data from the virtual address space and store it in the buffer */ - RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size); + RtlCopyMemory(Buffer, REAL_TO_PHYS(Address), Size); } VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size) { UNREFERENCED_PARAMETER(State); + // BIG HACK!!!! To make BIOS images working correctly, + // until Aleksander rewrites memory management!! + if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000; + /* If the A20 line is disabled, mask bit 20 */ if (!A20Line) Address &= ~(1 << 20); @@ -77,7 +114,7 @@ VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffe 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 */ - RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size); + RtlCopyMemory(REAL_TO_PHYS(Address), Buffer, Size); /* * Check if we modified the VGA memory. @@ -88,7 +125,7 @@ VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffe DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress()); DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress()) - VgaAddress + 1; - LPBYTE SrcBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress); + LPBYTE SrcBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress); /* Write to the VGA memory */ VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize); @@ -103,19 +140,218 @@ UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State) return PicGetInterrupt(); } -VOID WINAPI EmulatorDebugBreak(LPWORD Stack) +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]); + + /* Stop the VDM */ + VdmRunning = FALSE; + return; +} + +// FIXME: This function assumes 16-bit mode!!! +VOID EmulatorExecute(WORD Segment, WORD Offset) +{ + /* Tell Fast486 to move the instruction pointer */ + Fast486ExecuteAt(&EmulatorContext, Segment, Offset); +} + +VOID EmulatorStep(VOID) +{ + /* Dump the state for debugging purposes */ + // Fast486DumpState(&EmulatorContext); + + /* Execute the next instruction */ + Fast486StepInto(&EmulatorContext); +} + +VOID EmulatorSimulate(VOID) +{ + if (CpuCallLevel > MaxCpuCallLevel) + { + DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)", + CpuCallLevel, MaxCpuCallLevel); + + /* Stop the VDM */ + VdmRunning = FALSE; + return; + } + CpuCallLevel++; + + CpuSimulate = TRUE; + while (VdmRunning && CpuSimulate) ClockUpdate(); + + CpuCallLevel--; + if (CpuCallLevel < 0) CpuCallLevel = 0; + + /* This takes into account for reentrance */ + CpuSimulate = TRUE; +} + +VOID EmulatorUnsimulate(VOID) +{ + /* Stop simulation */ + CpuSimulate = FALSE; +} + +VOID EmulatorInterrupt(BYTE Number) +{ + /* Call the Fast486 API */ + Fast486Interrupt(&EmulatorContext, Number); +} + +VOID EmulatorInterruptSignal(VOID) +{ + /* Call the Fast486 API */ + Fast486InterruptSignal(&EmulatorContext); +} + +VOID EmulatorSetA20(BOOLEAN Enabled) +{ + A20Line = Enabled; +} + +VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack) { DPRINT1("NTVDM: BOP_DEBUGGER\n"); DebugBreak(); } +VOID WINAPI EmulatorUnsimulateBop(LPWORD Stack) +{ + EmulatorUnsimulate(); +} + +static BYTE WINAPI Port61hRead(ULONG Port) +{ + return Port61hState; +} + +static VOID WINAPI Port61hWrite(ULONG Port, BYTE Data) +{ + // BOOLEAN SpeakerChange = FALSE; + BYTE OldPort61hState = Port61hState; + + /* Only the four lowest bytes can be written */ + Port61hState = (Port61hState & 0xF0) | (Data & 0x0F); + + if ((OldPort61hState ^ Port61hState) & 0x01) + { + DPRINT("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off"); + // SpeakerChange = TRUE; + } + + PitSetGate(2, !!(Port61hState & 0x01)); + + if ((OldPort61hState ^ Port61hState) & 0x02) + { + /* There were some change for the speaker... */ + DPRINT("Speaker %s\n", Port61hState & 0x02 ? "on" : "off"); + // SpeakerChange = TRUE; + } + // if (SpeakerChange) SpeakerChange(); + SpeakerChange(); +} + +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) +{ + // BYTE OldPort61hState = Port61hState; + +#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 + DPRINT("Speaker PIT out\n"); + // if ((OldPort61hState ^ Port61hState) & 0x20) + // SpeakerChange(); +} + /* PUBLIC FUNCTIONS ***********************************************************/ -BOOLEAN EmulatorInitialize(VOID) +DWORD WINAPI PumpConsoleInput(LPVOID Parameter); + +BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) { /* Allocate memory for the 16-bit address space */ BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS); - if (BaseAddress == NULL) return FALSE; + if (BaseAddress == NULL) + { + wprintf(L"FATAL: Failed to allocate VDM memory.\n"); + return FALSE; + } + + /* Initialize I/O ports */ + /* Initialize RAM */ + + /* Initialize the internal clock */ + if (!ClockInitialize()) + { + wprintf(L"FATAL: Failed to initialize the clock\n"); + return FALSE; + } /* Initialize the CPU */ Fast486Initialize(&EmulatorContext, @@ -128,58 +364,78 @@ BOOLEAN EmulatorInitialize(VOID) EmulatorIntAcknowledge, NULL /* TODO: Use a TLB */); - /* Enable interrupts */ - setIF(1); + /* Initialize DMA */ + + /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */ + PicInitialize(); + PitInitialize(); + CmosInitialize(); + SpeakerInitialize(); + + /* 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); + + /* Initialize the PS2 port */ + PS2Initialize(ConsoleInput); + + /* Set the console input mode */ + // 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 */); + + /* Start the input thread */ + InputThread = CreateThread(NULL, 0, &PumpConsoleInput, ConsoleInput, 0, NULL); + // if (InputThread == NULL) return FALSE; + + /* Initialize the VGA */ + // if (!VgaInitialize(ConsoleOutput)) return FALSE; + VgaInitialize(ConsoleOutput); + + /* Initialize the software callback system and register the emulator BOPs */ + InitializeCallbacks(); + RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop); + RegisterBop(BOP_UNSIMULATE, EmulatorUnsimulateBop); /* Initialize VDD support */ VDDSupInitialize(); - /* Register the DebugBreak BOP */ - RegisterBop(BOP_DEBUGGER, EmulatorDebugBreak); - return TRUE; } VOID EmulatorCleanup(VOID) { - /* Free the memory allocated for the 16-bit address space */ - if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress); -} + VgaCleanup(); -// FIXME: This function assumes 16-bit mode!!! -VOID EmulatorExecute(WORD Segment, WORD Offset) -{ - /* Tell Fast486 to move the instruction pointer */ - Fast486ExecuteAt(&EmulatorContext, Segment, Offset); -} + /* Close the input thread handle */ + if (InputThread != NULL) CloseHandle(InputThread); + InputThread = NULL; -VOID EmulatorInterrupt(BYTE Number) -{ - /* Call the Fast486 API */ - Fast486Interrupt(&EmulatorContext, Number); -} + PS2Cleanup(); -VOID EmulatorInterruptSignal(VOID) -{ - /* Call the Fast486 API */ - Fast486InterruptSignal(&EmulatorContext); -} + SpeakerCleanup(); + CmosCleanup(); + // PitCleanup(); + // PicCleanup(); -VOID EmulatorStep(VOID) -{ - /* Dump the state for debugging purposes */ - // Fast486DumpState(&EmulatorContext); + // Fast486Cleanup(); - /* Execute the next instruction */ - Fast486StepInto(&EmulatorContext); + /* Free the memory allocated for the 16-bit address space */ + if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress); } -VOID EmulatorSetA20(BOOLEAN Enabled) -{ - A20Line = Enabled; -} +VOID +WINAPI +VDDSimulate16(VOID) +{ + EmulatorSimulate(); +} VOID WINAPI @@ -227,7 +483,7 @@ VdmMapFlat(IN USHORT Segment, return SEG_OFF_TO_PTR(Segment, Offset); } -BOOL +BOOL WINAPI VdmFlushCache(IN USHORT Segment, IN ULONG Offset,