[FAST486]

Separate external interrupts from interrupt signals (which are
interrupts whose number is not known until they can be serviced,
just like hardware interrupts on a real CPU).
[NTVDM]
Improve the PIC emulation code (IRQ priorities, etc...).
Instead of checking for interrupts in the main loop, move the
PS/2 input parsing to a different thread.
Improve BIOS keyboard IRQ handling.


svn path=/branches/ntvdm/; revision=60761
This commit is contained in:
Aleksandar Andrejevic 2013-10-27 00:37:01 +00:00
parent fcbe98e6b6
commit f5d3c9254c
11 changed files with 204 additions and 94 deletions

View file

@ -145,6 +145,13 @@ typedef enum _FAST486_EXCEPTIONS
FAST486_EXCEPTION_MC = 0x12 FAST486_EXCEPTION_MC = 0x12
} FAST486_EXCEPTIONS, *PFAST486_EXCEPTIONS; } 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 typedef
BOOLEAN BOOLEAN
(NTAPI *FAST486_MEM_READ_PROC) (NTAPI *FAST486_MEM_READ_PROC)
@ -200,6 +207,13 @@ VOID
USHORT BopCode USHORT BopCode
); );
typedef
UCHAR
(NTAPI *FAST486_INT_ACK_PROC)
(
PFAST486_STATE State
);
typedef union _FAST486_REG typedef union _FAST486_REG
{ {
union union
@ -352,6 +366,7 @@ struct _FAST486_STATE
FAST486_IO_WRITE_PROC IoWriteCallback; FAST486_IO_WRITE_PROC IoWriteCallback;
FAST486_IDLE_PROC IdleCallback; FAST486_IDLE_PROC IdleCallback;
FAST486_BOP_PROC BopCallback; FAST486_BOP_PROC BopCallback;
FAST486_INT_ACK_PROC IntAckCallback;
FAST486_REG GeneralRegs[FAST486_NUM_GEN_REGS]; FAST486_REG GeneralRegs[FAST486_NUM_GEN_REGS];
FAST486_SEG_REG SegmentRegs[FAST486_NUM_SEG_REGS]; FAST486_SEG_REG SegmentRegs[FAST486_NUM_SEG_REGS];
FAST486_REG InstPtr, SavedInstPtr; FAST486_REG InstPtr, SavedInstPtr;
@ -362,7 +377,7 @@ struct _FAST486_STATE
ULONG ExceptionCount; ULONG ExceptionCount;
ULONG PrefixFlags; ULONG PrefixFlags;
FAST486_SEG_REGS SegmentOverride; FAST486_SEG_REGS SegmentOverride;
BOOLEAN HardwareInt; FAST486_INT_STATUS IntStatus;
UCHAR PendingIntNum; UCHAR PendingIntNum;
}; };
@ -396,6 +411,10 @@ VOID
NTAPI NTAPI
Fast486Interrupt(PFAST486_STATE State, UCHAR Number); Fast486Interrupt(PFAST486_STATE State, UCHAR Number);
VOID
NTAPI
Fast486InterruptSignal(PFAST486_STATE State);
VOID VOID
NTAPI NTAPI
Fast486ExecuteAt(PFAST486_STATE State, USHORT Segment, ULONG Offset); Fast486ExecuteAt(PFAST486_STATE State, USHORT Segment, ULONG Offset);

View file

@ -61,11 +61,23 @@ Fast486ExecutionControl(PFAST486_STATE State, INT Command)
{ {
State->SavedInstPtr = State->InstPtr; 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; 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 */ /* Get the interrupt vector */
if (Fast486GetIntVector(State, State->PendingIntNum, &IdtEntry)) if (Fast486GetIntVector(State, State->PendingIntNum, &IdtEntry))
{ {
@ -76,8 +88,8 @@ Fast486ExecutionControl(PFAST486_STATE State, INT Command)
IdtEntry.Type); IdtEntry.Type);
} }
/* Clear the interrupt pending flag */ /* Clear the interrupt status */
State->HardwareInt = FALSE; State->IntStatus = FAST486_INT_NONE;
} }
} }
@ -243,6 +255,7 @@ Fast486Reset(PFAST486_STATE State)
FAST486_IO_WRITE_PROC IoWriteCallback = State->IoWriteCallback; FAST486_IO_WRITE_PROC IoWriteCallback = State->IoWriteCallback;
FAST486_IDLE_PROC IdleCallback = State->IdleCallback; FAST486_IDLE_PROC IdleCallback = State->IdleCallback;
FAST486_BOP_PROC BopCallback = State->BopCallback; FAST486_BOP_PROC BopCallback = State->BopCallback;
FAST486_INT_ACK_PROC IntAckCallback = State->IntAckCallback;
/* Clear the entire structure */ /* Clear the entire structure */
RtlZeroMemory(State, sizeof(*State)); RtlZeroMemory(State, sizeof(*State));
@ -278,17 +291,26 @@ Fast486Reset(PFAST486_STATE State)
State->IoWriteCallback = IoWriteCallback; State->IoWriteCallback = IoWriteCallback;
State->IdleCallback = IdleCallback; State->IdleCallback = IdleCallback;
State->BopCallback = BopCallback; State->BopCallback = BopCallback;
State->IntAckCallback = IntAckCallback;
} }
VOID VOID
NTAPI NTAPI
Fast486Interrupt(PFAST486_STATE State, UCHAR Number) Fast486Interrupt(PFAST486_STATE State, UCHAR Number)
{ {
/* Set the hardware interrupt flag */ /* Set the interrupt status and the number */
State->HardwareInt = TRUE; State->IntStatus = FAST486_INT_EXECUTE;
State->PendingIntNum = Number; State->PendingIntNum = Number;
} }
VOID
NTAPI
Fast486InterruptSignal(PFAST486_STATE State)
{
/* Set the interrupt status */
State->IntStatus = FAST486_INT_SIGNAL;
}
VOID VOID
NTAPI NTAPI
Fast486ExecuteAt(PFAST486_STATE State, USHORT Segment, ULONG Offset) Fast486ExecuteAt(PFAST486_STATE State, USHORT Segment, ULONG Offset)

View file

@ -876,7 +876,7 @@ FAST486_OPCODE_HANDLER(Fast486OpcodeHalt)
} }
/* Halt */ /* Halt */
while (!State->HardwareInt) State->IdleCallback(State); while (State->IntStatus != FAST486_INT_SIGNAL) State->IdleCallback(State);
/* Return success */ /* Return success */
return TRUE; return TRUE;

View file

@ -1092,9 +1092,9 @@ VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
BYTE ScanCode, VirtualKey; BYTE ScanCode, VirtualKey;
WORD Character; WORD Character;
/* Check if there is a scancode available */ /* Loop while there is a scancode available */
if (!(KeyboardReadStatus() & 1)) break; while (KeyboardReadStatus() & 1)
{
/* Get the scan code and virtual key code */ /* Get the scan code and virtual key code */
ScanCode = KeyboardReadData(); ScanCode = KeyboardReadData();
VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK); VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
@ -1126,6 +1126,7 @@ VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack)
/* Key release, unset the highest bit */ /* Key release, unset the highest bit */
BiosKeyboardMap[VirtualKey] &= ~(1 << 7); BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
} }
}
break; break;
} }

View file

@ -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 ***********************************************************/ /* PUBLIC FUNCTIONS ***********************************************************/
BOOLEAN EmulatorInitialize() BOOLEAN EmulatorInitialize()
@ -342,7 +350,8 @@ BOOLEAN EmulatorInitialize()
EmulatorContext.MemWriteCallback = (FAST486_MEM_WRITE_PROC)EmulatorWriteMemory; EmulatorContext.MemWriteCallback = (FAST486_MEM_WRITE_PROC)EmulatorWriteMemory;
EmulatorContext.IoReadCallback = (FAST486_IO_READ_PROC)EmulatorReadIo; EmulatorContext.IoReadCallback = (FAST486_IO_READ_PROC)EmulatorReadIo;
EmulatorContext.IoWriteCallback = (FAST486_IO_WRITE_PROC)EmulatorWriteIo; EmulatorContext.IoWriteCallback = (FAST486_IO_WRITE_PROC)EmulatorWriteIo;
EmulatorContext.BopCallback = (FAST486_BOP_PROC)EmulatorBiosOperation; EmulatorContext.BopCallback = EmulatorBiosOperation;
EmulatorContext.IntAckCallback = EmulatorIntAcknowledge;
/* Reset the CPU */ /* Reset the CPU */
Fast486Reset(&EmulatorContext); Fast486Reset(&EmulatorContext);
@ -371,10 +380,10 @@ VOID EmulatorInterrupt(BYTE Number)
Fast486Interrupt(&EmulatorContext, Number); Fast486Interrupt(&EmulatorContext, Number);
} }
VOID EmulatorExternalInterrupt(BYTE Number) VOID EmulatorInterruptSignal(VOID)
{ {
/* Call the Fast486 API */ /* Call the Fast486 API */
Fast486Interrupt(&EmulatorContext, Number); Fast486InterruptSignal(&EmulatorContext);
} }
ULONG EmulatorGetRegister(ULONG Register) ULONG EmulatorGetRegister(ULONG Register)

View file

@ -88,7 +88,7 @@ BOOLEAN EmulatorInitialize();
VOID EmulatorSetStack(WORD Segment, DWORD Offset); VOID EmulatorSetStack(WORD Segment, DWORD Offset);
VOID EmulatorExecute(WORD Segment, WORD Offset); VOID EmulatorExecute(WORD Segment, WORD Offset);
VOID EmulatorInterrupt(BYTE Number); VOID EmulatorInterrupt(BYTE Number);
VOID EmulatorExternalInterrupt(BYTE Number); VOID EmulatorInterruptSignal(VOID);
ULONG EmulatorGetRegister(ULONG Register); ULONG EmulatorGetRegister(ULONG Register);
ULONG EmulatorGetProgramCounter(VOID); ULONG EmulatorGetProgramCounter(VOID);
VOID EmulatorSetRegister(ULONG Register, ULONG Value); VOID EmulatorSetRegister(ULONG Register, ULONG Value);

View file

@ -79,12 +79,12 @@ INT wmain(INT argc, WCHAR *argv[])
INT i; INT i;
CHAR CommandLine[DOS_CMDLINE_LENGTH]; CHAR CommandLine[DOS_CMDLINE_LENGTH];
DWORD CurrentTickCount; DWORD CurrentTickCount;
DWORD LastTickCount = GetTickCount();
DWORD Cycles = 0; DWORD Cycles = 0;
DWORD LastCyclePrintout = GetTickCount(); DWORD LastCyclePrintout = GetTickCount();
DWORD LastVerticalRefresh = GetTickCount(); DWORD LastVerticalRefresh = GetTickCount();
LARGE_INTEGER Frequency, LastTimerTick, Counter; LARGE_INTEGER Frequency, LastTimerTick, Counter;
LONGLONG TimerTicks; LONGLONG TimerTicks;
HANDLE InputThread = NULL;
/* Set the handler routine */ /* Set the handler routine */
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
@ -142,6 +142,9 @@ INT wmain(INT argc, WCHAR *argv[])
return -1; return -1;
} }
/* Start the input thread */
InputThread = CreateThread(NULL, 0, &InputThreadProc, NULL, 0, NULL);
/* Set the last timer tick to the current time */ /* Set the last timer tick to the current time */
QueryPerformanceCounter(&LastTimerTick); QueryPerformanceCounter(&LastTimerTick);
@ -162,13 +165,6 @@ INT wmain(INT argc, WCHAR *argv[])
for (i = 0; i < TimerTicks; i++) PitDecrementCount(); for (i = 0; i < TimerTicks; i++) PitDecrementCount();
LastTimerTick = Counter; LastTimerTick = Counter;
/* Check for console input events every millisecond */
if (CurrentTickCount != LastTickCount)
{
CheckForInputEvents();
LastTickCount = CurrentTickCount;
}
/* Check for vertical retrace */ /* Check for vertical retrace */
if ((CurrentTickCount - LastVerticalRefresh) >= 16) if ((CurrentTickCount - LastVerticalRefresh) >= 16)
{ {
@ -198,6 +194,7 @@ INT wmain(INT argc, WCHAR *argv[])
VgaRefreshDisplay(); VgaRefreshDisplay();
Cleanup: Cleanup:
if (InputThread != NULL) CloseHandle(InputThread);
BiosCleanup(); BiosCleanup();
EmulatorCleanup(); EmulatorCleanup();

View file

@ -35,8 +35,8 @@ BYTE PicReadCommand(BYTE Port)
} }
else else
{ {
/* The IRR is always 0, as the emulated CPU receives the interrupt instantly */ /* Read the interrupt request register */
return 0; return Pic->IntRequestRegister;
} }
} }
@ -167,9 +167,9 @@ VOID PicInterruptRequest(BYTE Number)
/* Check if the interrupt is masked */ /* Check if the interrupt is masked */
if (MasterPic.MaskRegister & (1 << Number)) return; if (MasterPic.MaskRegister & (1 << Number)) return;
/* Set the appropriate bit in the ISR and interrupt the CPU */ /* Set the appropriate bit in the IRR and interrupt the CPU */
if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << Number; MasterPic.IntRequestRegister |= 1 << Number;
EmulatorExternalInterrupt(MasterPic.IntOffset + Number); EmulatorInterruptSignal();
} }
else if (Number >= 8 && Number < 16) else if (Number >= 8 && Number < 16)
{ {
@ -196,12 +196,57 @@ VOID PicInterruptRequest(BYTE Number)
if (SlavePic.MaskRegister & (1 << Number)) return; if (SlavePic.MaskRegister & (1 << Number)) return;
/* Set the IRQ 2 bit in the master ISR */ /* 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 */ /* Set the appropriate bit in the IRR and interrupt the CPU */
if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= 1 << Number; SlavePic.IntRequestRegister |= 1 << Number;
EmulatorExternalInterrupt(SlavePic.IntOffset + 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 */ /* EOF */

View file

@ -34,6 +34,7 @@ typedef struct _PIC
{ {
BOOLEAN Initialization; BOOLEAN Initialization;
BYTE MaskRegister; BYTE MaskRegister;
BYTE IntRequestRegister;
BYTE InServiceRegister; BYTE InServiceRegister;
BYTE IntOffset; BYTE IntOffset;
BYTE ConfigRegister; BYTE ConfigRegister;
@ -51,6 +52,7 @@ VOID PicWriteCommand(BYTE Port, BYTE Value);
BYTE PicReadData(BYTE Port); BYTE PicReadData(BYTE Port);
VOID PicWriteData(BYTE Port, BYTE Value); VOID PicWriteData(BYTE Port, BYTE Value);
VOID PicInterruptRequest(BYTE Number); VOID PicInterruptRequest(BYTE Number);
BYTE PicGetInterrupt(VOID);
#endif // _PIC_H_ #endif // _PIC_H_

View file

@ -265,49 +265,64 @@ VOID KeyboardWriteData(BYTE Data)
// TODO: Implement PS/2 device commands // TODO: Implement PS/2 device commands
} }
VOID CheckForInputEvents() DWORD WINAPI InputThreadProc(LPVOID Parameter)
{ {
PINPUT_RECORD Buffer; INT i;
HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE); HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD i, j, Count, TotalEvents; INPUT_RECORD InputRecord;
BYTE ScanCode; DWORD Count;
BOOLEAN Interrupt = FALSE;
/* Get the number of input events */ while (VdmRunning)
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 */ /* Wait for an input record */
if (Buffer[i].EventType != KEY_EVENT) continue; if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count))
{
DPRINT1("Error reading console input\n");
return GetLastError();
/* Get the scan code */ }
ScanCode = (BYTE)Buffer[i].Event.KeyEvent.wVirtualScanCode;
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 this is a key release, set the highest bit in the scan code */
if (!Buffer[i].Event.KeyEvent.bKeyDown) ScanCode |= 0x80; if (!InputRecord.Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
/* Push the scan code onto the keyboard queue */ /* Push the scan code onto the keyboard queue */
for (j = 0; j < Buffer[i].Event.KeyEvent.wRepeatCount; j++) for (i = 0; i < InputRecord.Event.KeyEvent.wRepeatCount; i++)
{ {
KeyboardQueuePush(ScanCode); KeyboardQueuePush(ScanCode);
} }
Interrupt = TRUE; /* Keyboard IRQ */
PicInterruptRequest(1);
break;
} }
if (Interrupt) PicInterruptRequest(1); case MOUSE_EVENT:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
Cleanup: break;
HeapFree(GetProcessHeap(), 0, Buffer); }
default:
{
/* Ignored */
break;
}
}
}
return 0;
} }
/* EOF */ /* EOF */

View file

@ -28,7 +28,7 @@ BYTE KeyboardReadStatus();
VOID KeyboardWriteCommand(BYTE Command); VOID KeyboardWriteCommand(BYTE Command);
BYTE KeyboardReadData(); BYTE KeyboardReadData();
VOID KeyboardWriteData(BYTE Data); VOID KeyboardWriteData(BYTE Data);
VOID CheckForInputEvents(); DWORD WINAPI InputThreadProc(LPVOID Parameter);
#endif // _PS2_H_ #endif // _PS2_H_