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)
USHORT BopCode
);
+typedef
+UCHAR
+(NTAPI *FAST486_INT_ACK_PROC)
+(
+ PFAST486_STATE State
+);
+
typedef union _FAST486_REG
{
union
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;
ULONG ExceptionCount;
ULONG PrefixFlags;
FAST486_SEG_REGS SegmentOverride;
- BOOLEAN HardwareInt;
+ FAST486_INT_STATUS IntStatus;
UCHAR PendingIntNum;
};
NTAPI
Fast486Interrupt(PFAST486_STATE State, UCHAR Number);
+VOID
+NTAPI
+Fast486InterruptSignal(PFAST486_STATE State);
+
VOID
NTAPI
Fast486ExecuteAt(PFAST486_STATE State, USHORT Segment, ULONG Offset);
{
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))
{
IdtEntry.Type);
}
- /* Clear the interrupt pending flag */
- State->HardwareInt = FALSE;
+ /* Clear the interrupt status */
+ State->IntStatus = FAST486_INT_NONE;
}
}
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));
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)
}
/* Halt */
- while (!State->HardwareInt) State->IdleCallback(State);
+ while (State->IntStatus != FAST486_INT_SIGNAL) State->IdleCallback(State);
/* Return success */
return TRUE;
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)
+ /* 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)))
{
- /* For toggle keys, toggle the lowest bit in the keyboard map */
- BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
+ /* 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));
+ }
}
-
- /* Set the highest bit */
- BiosKeyboardMap[VirtualKey] |= (1 << 7);
-
- /* Find out which character this is */
- if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) > 0)
+ else
{
- /* Push it onto the BIOS keyboard queue */
- BiosKbdBufferPush((ScanCode << 8) | (Character & 0xFF));
+ /* Key release, unset the highest bit */
+ BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
}
}
- else
- {
- /* Key release, unset the highest bit */
- BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
- }
break;
}
}
}
+static BYTE WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
+{
+ UNREFERENCED_PARAMETER(State);
+
+ /* Get the interrupt number from the PIC */
+ return PicGetInterrupt();
+}
+
/* PUBLIC FUNCTIONS ***********************************************************/
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);
Fast486Interrupt(&EmulatorContext, Number);
}
-VOID EmulatorExternalInterrupt(BYTE Number)
+VOID EmulatorInterruptSignal(VOID)
{
/* Call the Fast486 API */
- Fast486Interrupt(&EmulatorContext, Number);
+ Fast486InterruptSignal(&EmulatorContext);
}
ULONG EmulatorGetRegister(ULONG Register)
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);
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);
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);
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)
{
VgaRefreshDisplay();
Cleanup:
+ if (InputThread != NULL) CloseHandle(InputThread);
BiosCleanup();
EmulatorCleanup();
}
else
{
- /* The IRR is always 0, as the emulated CPU receives the interrupt instantly */
- return 0;
+ /* Read the interrupt request register */
+ return Pic->IntRequestRegister;
}
}
/* 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)
{
/* 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;
}
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 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;
+ }
+ }
- /* Set the appropriate bit in the ISR and interrupt the CPU */
- if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= 1 << Number;
- EmulatorExternalInterrupt(SlavePic.IntOffset + Number);
+ 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 */
{
BOOLEAN Initialization;
BYTE MaskRegister;
+ BYTE IntRequestRegister;
BYTE InServiceRegister;
BYTE IntOffset;
BYTE ConfigRegister;
BYTE PicReadData(BYTE Port);
VOID PicWriteData(BYTE Port, BYTE Value);
VOID PicInterruptRequest(BYTE Number);
+BYTE PicGetInterrupt(VOID);
#endif // _PIC_H_
// 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;
+ while (VdmRunning)
+ {
+ /* Wait for an input record */
+ if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count))
+ {
+ DPRINT1("Error reading console input\n");
+ return GetLastError();
+
+ }
- /* Allocate the buffer */
- Buffer = (PINPUT_RECORD)HeapAlloc(GetProcessHeap(), 0, Count * sizeof(INPUT_RECORD));
- if (Buffer == NULL) return;
+ ASSERT(Count != 0);
- /* Peek the input events */
- if (!ReadConsoleInput(ConsoleInput, Buffer, Count, &TotalEvents)) goto Cleanup;
+ /* Check the event type */
+ switch (InputRecord.EventType)
+ {
+ case KEY_EVENT:
+ {
+ BYTE ScanCode = (BYTE)InputRecord.Event.KeyEvent.wVirtualScanCode;
- for (i = 0; i < TotalEvents; i++)
- {
- /* Check if this is a key event */
- if (Buffer[i].EventType != KEY_EVENT) continue;
+ /* If this is a key release, set the highest bit in the scan code */
+ if (!InputRecord.Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
- /* Get the scan code */
- ScanCode = (BYTE)Buffer[i].Event.KeyEvent.wVirtualScanCode;
+ /* Push the scan code onto the keyboard queue */
+ for (i = 0; i < InputRecord.Event.KeyEvent.wRepeatCount; i++)
+ {
+ KeyboardQueuePush(ScanCode);
+ }
- /* If this is a key release, set the highest bit in the scan code */
- if (!Buffer[i].Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
+ /* Keyboard IRQ */
+ PicInterruptRequest(1);
- /* Push the scan code onto the keyboard queue */
- for (j = 0; j < Buffer[i].Event.KeyEvent.wRepeatCount; j++)
- {
- KeyboardQueuePush(ScanCode);
- }
+ break;
+ }
- Interrupt = TRUE;
- }
+ case MOUSE_EVENT:
+ {
+ // TODO: NOT IMPLEMENTED
+ UNIMPLEMENTED;
+
+ break;
+ }
- if (Interrupt) PicInterruptRequest(1);
+ default:
+ {
+ /* Ignored */
+ break;
+ }
+ }
+ }
-Cleanup:
- HeapFree(GetProcessHeap(), 0, Buffer);
+ return 0;
}
/* EOF */
VOID KeyboardWriteCommand(BYTE Command);
BYTE KeyboardReadData();
VOID KeyboardWriteData(BYTE Data);
-VOID CheckForInputEvents();
+DWORD WINAPI InputThreadProc(LPVOID Parameter);
#endif // _PS2_H_