diff --git a/include/reactos/libs/fast486/fast486.h b/include/reactos/libs/fast486/fast486.h index c2501f7729a..618a29122a3 100644 --- a/include/reactos/libs/fast486/fast486.h +++ b/include/reactos/libs/fast486/fast486.h @@ -145,6 +145,13 @@ typedef enum _FAST486_EXCEPTIONS FAST486_EXCEPTION_MC = 0x12 } FAST486_EXCEPTIONS, *PFAST486_EXCEPTIONS; +typedef enum _FAST486_INT_STATUS +{ + FAST486_INT_NONE = 0, + FAST486_INT_EXECUTE = 1, + FAST486_INT_SIGNAL = 2 +} FAST486_INT_STATUS, *PFAST486_INT_STATUS; + typedef BOOLEAN (NTAPI *FAST486_MEM_READ_PROC) @@ -200,6 +207,13 @@ VOID USHORT BopCode ); +typedef +UCHAR +(NTAPI *FAST486_INT_ACK_PROC) +( + PFAST486_STATE State +); + typedef union _FAST486_REG { union @@ -352,6 +366,7 @@ struct _FAST486_STATE FAST486_IO_WRITE_PROC IoWriteCallback; FAST486_IDLE_PROC IdleCallback; FAST486_BOP_PROC BopCallback; + FAST486_INT_ACK_PROC IntAckCallback; FAST486_REG GeneralRegs[FAST486_NUM_GEN_REGS]; FAST486_SEG_REG SegmentRegs[FAST486_NUM_SEG_REGS]; FAST486_REG InstPtr, SavedInstPtr; @@ -362,7 +377,7 @@ struct _FAST486_STATE ULONG ExceptionCount; ULONG PrefixFlags; FAST486_SEG_REGS SegmentOverride; - BOOLEAN HardwareInt; + FAST486_INT_STATUS IntStatus; UCHAR PendingIntNum; }; @@ -396,6 +411,10 @@ VOID NTAPI Fast486Interrupt(PFAST486_STATE State, UCHAR Number); +VOID +NTAPI +Fast486InterruptSignal(PFAST486_STATE State); + VOID NTAPI Fast486ExecuteAt(PFAST486_STATE State, USHORT Segment, ULONG Offset); diff --git a/lib/fast486/fast486.c b/lib/fast486/fast486.c index d271a865cba..04a87760670 100644 --- a/lib/fast486/fast486.c +++ b/lib/fast486/fast486.c @@ -61,11 +61,23 @@ Fast486ExecutionControl(PFAST486_STATE State, INT Command) { State->SavedInstPtr = State->InstPtr; - /* Check if interrupts are enabled and there is an interrupt pending */ - if (State->Flags.If && State->HardwareInt) + /* + * Check if there is an interrupt to execute, or a hardware interrupt signal + * while interrupts are enabled. + */ + if ((State->IntStatus == FAST486_INT_EXECUTE) + || (State->Flags.If + && (State->IntAckCallback != NULL) + && (State->IntStatus == FAST486_INT_SIGNAL))) { FAST486_IDT_ENTRY IdtEntry; + if (State->IntStatus == FAST486_INT_SIGNAL) + { + /* Acknowledge the interrupt to get the number */ + State->PendingIntNum = State->IntAckCallback(State); + } + /* Get the interrupt vector */ if (Fast486GetIntVector(State, State->PendingIntNum, &IdtEntry)) { @@ -76,8 +88,8 @@ Fast486ExecutionControl(PFAST486_STATE State, INT Command) IdtEntry.Type); } - /* Clear the interrupt pending flag */ - State->HardwareInt = FALSE; + /* Clear the interrupt status */ + State->IntStatus = FAST486_INT_NONE; } } @@ -243,6 +255,7 @@ Fast486Reset(PFAST486_STATE State) FAST486_IO_WRITE_PROC IoWriteCallback = State->IoWriteCallback; FAST486_IDLE_PROC IdleCallback = State->IdleCallback; FAST486_BOP_PROC BopCallback = State->BopCallback; + FAST486_INT_ACK_PROC IntAckCallback = State->IntAckCallback; /* Clear the entire structure */ RtlZeroMemory(State, sizeof(*State)); @@ -278,17 +291,26 @@ Fast486Reset(PFAST486_STATE State) State->IoWriteCallback = IoWriteCallback; State->IdleCallback = IdleCallback; State->BopCallback = BopCallback; + State->IntAckCallback = IntAckCallback; } VOID NTAPI Fast486Interrupt(PFAST486_STATE State, UCHAR Number) { - /* Set the hardware interrupt flag */ - State->HardwareInt = TRUE; + /* Set the interrupt status and the number */ + State->IntStatus = FAST486_INT_EXECUTE; State->PendingIntNum = Number; } +VOID +NTAPI +Fast486InterruptSignal(PFAST486_STATE State) +{ + /* Set the interrupt status */ + State->IntStatus = FAST486_INT_SIGNAL; +} + VOID NTAPI Fast486ExecuteAt(PFAST486_STATE State, USHORT Segment, ULONG Offset) diff --git a/lib/fast486/opcodes.c b/lib/fast486/opcodes.c index db9c90dda98..083c349fda6 100644 --- a/lib/fast486/opcodes.c +++ b/lib/fast486/opcodes.c @@ -876,7 +876,7 @@ FAST486_OPCODE_HANDLER(Fast486OpcodeHalt) } /* Halt */ - while (!State->HardwareInt) State->IdleCallback(State); + while (State->IntStatus != FAST486_INT_SIGNAL) State->IdleCallback(State); /* Return success */ return TRUE; diff --git a/subsystems/ntvdm/bios.c b/subsystems/ntvdm/bios.c index 0005b65382a..61e6beb8c1b 100644 --- a/subsystems/ntvdm/bios.c +++ b/subsystems/ntvdm/bios.c @@ -1092,39 +1092,40 @@ VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack) BYTE ScanCode, VirtualKey; WORD Character; - /* Check if there is a scancode available */ - if (!(KeyboardReadStatus() & 1)) break; - - /* Get the scan code and virtual key code */ - ScanCode = KeyboardReadData(); - VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK); - - /* Check if this is a key press or release */ - if (!(ScanCode & (1 << 7))) + /* Loop while there is a scancode available */ + while (KeyboardReadStatus() & 1) { - /* Key press */ - if (VirtualKey == VK_NUMLOCK - || VirtualKey == VK_CAPITAL - || VirtualKey == VK_SCROLL) - { - /* For toggle keys, toggle the lowest bit in the keyboard map */ - BiosKeyboardMap[VirtualKey] ^= ~(1 << 0); - } + /* Get the scan code and virtual key code */ + ScanCode = KeyboardReadData(); + VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK); - /* Set the highest bit */ - BiosKeyboardMap[VirtualKey] |= (1 << 7); - - /* Find out which character this is */ - if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) > 0) + /* Check if this is a key press or release */ + if (!(ScanCode & (1 << 7))) { - /* Push it onto the BIOS keyboard queue */ - BiosKbdBufferPush((ScanCode << 8) | (Character & 0xFF)); + /* Key press */ + if (VirtualKey == VK_NUMLOCK + || VirtualKey == VK_CAPITAL + || VirtualKey == VK_SCROLL) + { + /* For toggle keys, toggle the lowest bit in the keyboard map */ + BiosKeyboardMap[VirtualKey] ^= ~(1 << 0); + } + + /* Set the highest bit */ + BiosKeyboardMap[VirtualKey] |= (1 << 7); + + /* Find out which character this is */ + if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) > 0) + { + /* Push it onto the BIOS keyboard queue */ + BiosKbdBufferPush((ScanCode << 8) | (Character & 0xFF)); + } + } + else + { + /* Key release, unset the highest bit */ + BiosKeyboardMap[VirtualKey] &= ~(1 << 7); } - } - else - { - /* Key release, unset the highest bit */ - BiosKeyboardMap[VirtualKey] &= ~(1 << 7); } break; diff --git a/subsystems/ntvdm/emulator.c b/subsystems/ntvdm/emulator.c index fb0e643d72c..7081c58982b 100644 --- a/subsystems/ntvdm/emulator.c +++ b/subsystems/ntvdm/emulator.c @@ -329,6 +329,14 @@ static VOID WINAPI EmulatorBiosOperation(PFAST486_STATE State, WORD Code) } } +static BYTE WINAPI EmulatorIntAcknowledge(PFAST486_STATE State) +{ + UNREFERENCED_PARAMETER(State); + + /* Get the interrupt number from the PIC */ + return PicGetInterrupt(); +} + /* PUBLIC FUNCTIONS ***********************************************************/ BOOLEAN EmulatorInitialize() @@ -342,7 +350,8 @@ BOOLEAN EmulatorInitialize() EmulatorContext.MemWriteCallback = (FAST486_MEM_WRITE_PROC)EmulatorWriteMemory; EmulatorContext.IoReadCallback = (FAST486_IO_READ_PROC)EmulatorReadIo; EmulatorContext.IoWriteCallback = (FAST486_IO_WRITE_PROC)EmulatorWriteIo; - EmulatorContext.BopCallback = (FAST486_BOP_PROC)EmulatorBiosOperation; + EmulatorContext.BopCallback = EmulatorBiosOperation; + EmulatorContext.IntAckCallback = EmulatorIntAcknowledge; /* Reset the CPU */ Fast486Reset(&EmulatorContext); @@ -371,10 +380,10 @@ VOID EmulatorInterrupt(BYTE Number) Fast486Interrupt(&EmulatorContext, Number); } -VOID EmulatorExternalInterrupt(BYTE Number) +VOID EmulatorInterruptSignal(VOID) { /* Call the Fast486 API */ - Fast486Interrupt(&EmulatorContext, Number); + Fast486InterruptSignal(&EmulatorContext); } ULONG EmulatorGetRegister(ULONG Register) diff --git a/subsystems/ntvdm/emulator.h b/subsystems/ntvdm/emulator.h index 0906c7d2a41..fcfb0712e96 100644 --- a/subsystems/ntvdm/emulator.h +++ b/subsystems/ntvdm/emulator.h @@ -88,7 +88,7 @@ BOOLEAN EmulatorInitialize(); VOID EmulatorSetStack(WORD Segment, DWORD Offset); VOID EmulatorExecute(WORD Segment, WORD Offset); VOID EmulatorInterrupt(BYTE Number); -VOID EmulatorExternalInterrupt(BYTE Number); +VOID EmulatorInterruptSignal(VOID); ULONG EmulatorGetRegister(ULONG Register); ULONG EmulatorGetProgramCounter(VOID); VOID EmulatorSetRegister(ULONG Register, ULONG Value); diff --git a/subsystems/ntvdm/ntvdm.c b/subsystems/ntvdm/ntvdm.c index 72123098c70..60d78986e84 100644 --- a/subsystems/ntvdm/ntvdm.c +++ b/subsystems/ntvdm/ntvdm.c @@ -79,12 +79,12 @@ INT wmain(INT argc, WCHAR *argv[]) INT i; CHAR CommandLine[DOS_CMDLINE_LENGTH]; DWORD CurrentTickCount; - DWORD LastTickCount = GetTickCount(); DWORD Cycles = 0; DWORD LastCyclePrintout = GetTickCount(); DWORD LastVerticalRefresh = GetTickCount(); LARGE_INTEGER Frequency, LastTimerTick, Counter; LONGLONG TimerTicks; + HANDLE InputThread = NULL; /* Set the handler routine */ SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); @@ -141,6 +141,9 @@ INT wmain(INT argc, WCHAR *argv[]) DisplayMessage(L"Could not start program: %S", CommandLine); return -1; } + + /* Start the input thread */ + InputThread = CreateThread(NULL, 0, &InputThreadProc, NULL, 0, NULL); /* Set the last timer tick to the current time */ QueryPerformanceCounter(&LastTimerTick); @@ -162,13 +165,6 @@ INT wmain(INT argc, WCHAR *argv[]) for (i = 0; i < TimerTicks; i++) PitDecrementCount(); LastTimerTick = Counter; - /* Check for console input events every millisecond */ - if (CurrentTickCount != LastTickCount) - { - CheckForInputEvents(); - LastTickCount = CurrentTickCount; - } - /* Check for vertical retrace */ if ((CurrentTickCount - LastVerticalRefresh) >= 16) { @@ -198,6 +194,7 @@ INT wmain(INT argc, WCHAR *argv[]) VgaRefreshDisplay(); Cleanup: + if (InputThread != NULL) CloseHandle(InputThread); BiosCleanup(); EmulatorCleanup(); diff --git a/subsystems/ntvdm/pic.c b/subsystems/ntvdm/pic.c index 51d2febbb73..de7a0a119b5 100644 --- a/subsystems/ntvdm/pic.c +++ b/subsystems/ntvdm/pic.c @@ -35,8 +35,8 @@ BYTE PicReadCommand(BYTE Port) } else { - /* The IRR is always 0, as the emulated CPU receives the interrupt instantly */ - return 0; + /* Read the interrupt request register */ + return Pic->IntRequestRegister; } } @@ -167,9 +167,9 @@ VOID PicInterruptRequest(BYTE Number) /* 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); + /* Set the appropriate bit in the IRR and interrupt the CPU */ + MasterPic.IntRequestRegister |= 1 << Number; + EmulatorInterruptSignal(); } else if (Number >= 8 && Number < 16) { @@ -187,7 +187,7 @@ VOID PicInterruptRequest(BYTE Number) /* Check if any of the higher-priorirty interrupts are busy */ if (MasterPic.InServiceRegister != 0) return; - for (i = 0; i <= Number ; i++) + for (i = 0; i <= Number; i++) { if (SlavePic.InServiceRegister & (1 << Number)) return; } @@ -196,12 +196,57 @@ VOID PicInterruptRequest(BYTE Number) if (SlavePic.MaskRegister & (1 << Number)) return; /* Set the IRQ 2 bit in the master ISR */ - if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << 2; + 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); + /* Set the appropriate bit in the IRR and interrupt the CPU */ + SlavePic.IntRequestRegister |= 1 << Number; + EmulatorInterruptSignal(); } } +BYTE PicGetInterrupt(VOID) +{ + INT i, j; + + /* Search interrupts by priority */ + for (i = 0; i < 8; i++) + { + /* Check if this line is cascaded to the slave PIC */ + if ((i == 2) + && MasterPic.CascadeRegister & (1 << 2) + && SlavePic.Slave + && (SlavePic.CascadeRegister == 2)) + { + /* Search the slave PIC interrupts by priority */ + for (j = 0; j < 8; j++) if ((j != 1) && SlavePic.IntRequestRegister & (1 << j)) + { + /* Clear the IRR flag */ + SlavePic.IntRequestRegister &= ~(1 << j); + + /* Set the ISR flag, unless AEOI is enabled */ + if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= (1 << j); + + /* Return the interrupt number */ + return SlavePic.IntOffset + j; + } + } + + if (MasterPic.IntRequestRegister & (1 << i)) + { + /* Clear the IRR flag */ + MasterPic.IntRequestRegister &= ~(1 << i); + + /* Set the ISR flag, unless AEOI is enabled */ + if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << i); + + /* Return the interrupt number */ + return MasterPic.IntOffset + i; + } + } + + /* Spurious interrupt */ + if (MasterPic.InServiceRegister & (1 << 2)) return SlavePic.IntOffset + 7; + else return MasterPic.IntOffset + 7; +} + /* EOF */ diff --git a/subsystems/ntvdm/pic.h b/subsystems/ntvdm/pic.h index adc47e477cd..ef60b61137b 100644 --- a/subsystems/ntvdm/pic.h +++ b/subsystems/ntvdm/pic.h @@ -34,6 +34,7 @@ typedef struct _PIC { BOOLEAN Initialization; BYTE MaskRegister; + BYTE IntRequestRegister; BYTE InServiceRegister; BYTE IntOffset; BYTE ConfigRegister; @@ -51,6 +52,7 @@ VOID PicWriteCommand(BYTE Port, BYTE Value); BYTE PicReadData(BYTE Port); VOID PicWriteData(BYTE Port, BYTE Value); VOID PicInterruptRequest(BYTE Number); +BYTE PicGetInterrupt(VOID); #endif // _PIC_H_ diff --git a/subsystems/ntvdm/ps2.c b/subsystems/ntvdm/ps2.c index 276cce7d836..ed8e82acde2 100644 --- a/subsystems/ntvdm/ps2.c +++ b/subsystems/ntvdm/ps2.c @@ -265,49 +265,64 @@ VOID KeyboardWriteData(BYTE Data) // TODO: Implement PS/2 device commands } -VOID CheckForInputEvents() +DWORD WINAPI InputThreadProc(LPVOID Parameter) { - PINPUT_RECORD Buffer; + INT i; HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE); - DWORD i, j, Count, TotalEvents; - BYTE ScanCode; - BOOLEAN Interrupt = FALSE; + INPUT_RECORD InputRecord; + DWORD Count; - /* 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++) + while (VdmRunning) { - /* Check if this is a key event */ - if (Buffer[i].EventType != KEY_EVENT) continue; - - /* Get the scan code */ - ScanCode = (BYTE)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++) + /* Wait for an input record */ + if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count)) { - KeyboardQueuePush(ScanCode); + DPRINT1("Error reading console input\n"); + return GetLastError(); + } - Interrupt = TRUE; + ASSERT(Count != 0); + + /* Check the event type */ + switch (InputRecord.EventType) + { + case KEY_EVENT: + { + BYTE ScanCode = (BYTE)InputRecord.Event.KeyEvent.wVirtualScanCode; + + /* If this is a key release, set the highest bit in the scan code */ + if (!InputRecord.Event.KeyEvent.bKeyDown) ScanCode |= 0x80; + + /* Push the scan code onto the keyboard queue */ + for (i = 0; i < InputRecord.Event.KeyEvent.wRepeatCount; i++) + { + KeyboardQueuePush(ScanCode); + } + + /* Keyboard IRQ */ + PicInterruptRequest(1); + + break; + } + + case MOUSE_EVENT: + { + // TODO: NOT IMPLEMENTED + UNIMPLEMENTED; + + break; + } + + default: + { + /* Ignored */ + break; + } + } } - if (Interrupt) PicInterruptRequest(1); - -Cleanup: - HeapFree(GetProcessHeap(), 0, Buffer); + return 0; } /* EOF */ diff --git a/subsystems/ntvdm/ps2.h b/subsystems/ntvdm/ps2.h index 94d68a11e55..67799a67b2c 100644 --- a/subsystems/ntvdm/ps2.h +++ b/subsystems/ntvdm/ps2.h @@ -28,7 +28,7 @@ BYTE KeyboardReadStatus(); VOID KeyboardWriteCommand(BYTE Command); BYTE KeyboardReadData(); VOID KeyboardWriteData(BYTE Data); -VOID CheckForInputEvents(); +DWORD WINAPI InputThreadProc(LPVOID Parameter); #endif // _PS2_H_