[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_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);

View file

@ -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)

View file

@ -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;

View file

@ -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;

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 ***********************************************************/
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)

View file

@ -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);

View file

@ -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();

View file

@ -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 */

View file

@ -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_

View file

@ -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 */

View file

@ -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_