Author: aandrejevic Date: Fri Jun 21 00:47:07 2013 New Revision: 59271
URL: http://svn.reactos.org/svn/reactos?rev=59271&view=rev Log: [NTVDM] Implement Programmable Interval Timer (PIT) emulation. Fix bugs in interrupts and PIC emulation.
Modified: branches/ntvdm/subsystems/ntvdm/bios.c branches/ntvdm/subsystems/ntvdm/emulator.c branches/ntvdm/subsystems/ntvdm/hardware.c branches/ntvdm/subsystems/ntvdm/ntvdm.h
Modified: branches/ntvdm/subsystems/ntvdm/bios.c URL: http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/bios.c?re... ============================================================================== --- branches/ntvdm/subsystems/ntvdm/bios.c [iso-8859-1] (original) +++ branches/ntvdm/subsystems/ntvdm/bios.c [iso-8859-1] Fri Jun 21 00:47:07 2013 @@ -71,6 +71,12 @@ /* Make sure the PIC is in 8086 mode */ PicWriteData(PIC_MASTER_DATA, PIC_ICW4_8086); PicWriteData(PIC_SLAVE_DATA, PIC_ICW4_8086); + + /* Clear the masks for both PICs */ + PicWriteData(PIC_MASTER_DATA, 0x00); + PicWriteData(PIC_SLAVE_DATA, 0x00); + + PitInitialize();
return TRUE; } @@ -251,4 +257,9 @@ } }
+VOID BiosHandleIrq(BYTE IrqNumber) +{ + PicWriteCommand(PIC_MASTER_CMD, PIC_OCW2_EOI); +} + /* EOF */
Modified: branches/ntvdm/subsystems/ntvdm/emulator.c URL: http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/emulator.... ============================================================================== --- branches/ntvdm/subsystems/ntvdm/emulator.c [iso-8859-1] (original) +++ branches/ntvdm/subsystems/ntvdm/emulator.c [iso-8859-1] Fri Jun 21 00:47:07 2013 @@ -56,12 +56,58 @@
static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size) { - // TODO: NOT IMPLEMENTED! + switch (Address) + { + case PIC_MASTER_CMD: + case PIC_SLAVE_CMD: + { + *Buffer = PicReadCommand(Address); + break; + } + + case PIC_MASTER_DATA: + case PIC_SLAVE_DATA: + { + *Buffer = PicReadData(Address); + break; + } + } }
static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size) { - // TODO: NOT IMPLEMENTED! + BYTE Byte = *Buffer; + + switch (Address) + { + case PIT_COMMAND_PORT: + { + PitWriteCommand(Byte); + break; + } + + case PIT_DATA_PORT(0): + case PIT_DATA_PORT(1): + case PIT_DATA_PORT(2): + { + PitWriteData(Address - PIT_DATA_PORT(0), Byte); + break; + } + + case PIC_MASTER_CMD: + case PIC_SLAVE_CMD: + { + PicWriteCommand(Address, Byte); + break; + } + + case PIC_MASTER_DATA: + case PIC_SLAVE_DATA: + { + PicWriteData(Address, Byte); + break; + } + } }
static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number) @@ -100,6 +146,18 @@ /* Stop the VDM */ VdmRunning = FALSE; return; + } + + /* Check if this was an PIC IRQ */ + if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8) + { + /* It was an IRQ from the master PIC */ + BiosHandleIrq(IntNum - BIOS_PIC_MASTER_INT); + } + else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8) + { + /* It was an IRQ from the slave PIC */ + BiosHandleIrq(IntNum - BIOS_PIC_SLAVE_INT + 8); }
switch (IntNum) @@ -183,7 +241,7 @@
VOID EmulatorInterrupt(BYTE Number) { - LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress); + LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress); UINT Segment, Offset;
/* Get the segment and offset */
Modified: branches/ntvdm/subsystems/ntvdm/hardware.c URL: http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/hardware.... ============================================================================== --- branches/ntvdm/subsystems/ntvdm/hardware.c [iso-8859-1] (original) +++ branches/ntvdm/subsystems/ntvdm/hardware.c [iso-8859-1] Fri Jun 21 00:47:07 2013 @@ -24,7 +24,64 @@ BOOLEAN ReadIsr; } PIC, *PPIC;
+typedef struct _PIT_CHANNEL +{ + BOOLEAN RateGenerator; + BOOLEAN Pulsed; + BOOLEAN FlipFlop; + BYTE AccessMode; + WORD ReloadValue; +} PIT_CHANNEL, *PPIT_CHANNEL; + static PIC MasterPic, SlavePic; +static PIT_CHANNEL PitChannels[PIT_CHANNELS]; + +static DWORD WINAPI PitThread(PVOID Parameter) +{ + LARGE_INTEGER Frequency, CurrentTime, LastTickTime; + LONGLONG Elapsed, Milliseconds, TicksNeeded; + UNREFERENCED_PARAMETER(Parameter); + + /* Get the performance counter frequency */ + if (!QueryPerformanceFrequency(&Frequency)) return EXIT_FAILURE; + if (!QueryPerformanceCounter(&LastTickTime)) return EXIT_FAILURE; + + while (VdmRunning) + { + if (!QueryPerformanceCounter(&CurrentTime)) return EXIT_FAILURE; + + /* Calculate the elapsed time, in PIT ticks */ + Elapsed = ((CurrentTime.QuadPart - LastTickTime.QuadPart) + * PIT_BASE_FREQUENCY) + / Frequency.QuadPart; + + /* A reload value of 0 indicates 65536 */ + if (PitChannels[0].ReloadValue) TicksNeeded = PitChannels[0].ReloadValue; + else TicksNeeded = 65536; + + if (Elapsed < TicksNeeded) + { + /* Get the number of milliseconds */ + Milliseconds = (Elapsed * 1000LL) / PIT_BASE_FREQUENCY; + + /* If this number is non-zero, put the thread in the waiting state */ + if (Milliseconds > 0LL) Sleep((DWORD)Milliseconds); + + continue; + } + + LastTickTime = CurrentTime; + + /* Do the IRQ */ + if (PitChannels[0].RateGenerator || !PitChannels[0].Pulsed) + { + PitChannels[0].Pulsed = TRUE; + PicInterruptRequest(0); + } + } + + return EXIT_SUCCESS; +}
/* PUBLIC FUNCTIONS ***********************************************************/
@@ -165,14 +222,15 @@ { if (Number >= 0 && Number < 8) { - /* Check if the interrupt is busy or in a cascade */ + /* Check if the interrupt is busy, in a cascade or masked */ if (MasterPic.CascadeRegister & (1 << Number) - || MasterPic.InServiceRegister & (1 << Number)) + || MasterPic.InServiceRegister & (1 << Number) + || MasterPic.MaskRegister & (1 << Number)) { return; }
- MasterPic.InServiceRegister |= 1 << Number; + if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << Number; EmulatorInterrupt(MasterPic.IntOffset + Number); } else if (Number >= 8 && Number < 16) @@ -189,20 +247,96 @@ return; }
- /* Check the if the slave PIC is busy */ - if (MasterPic.InServiceRegister & (1 << 2)) return; + /* Check the if the slave PIC is busy or masked */ + if (MasterPic.InServiceRegister & (1 << 2) + || MasterPic.MaskRegister & (1 << 2)) return;
/* Set the IRQ 2 bit in the master ISR */ - MasterPic.InServiceRegister |= 1 << 2; - - /* Check if the interrupt is busy or in a cascade */ + if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << 2; + + /* Check if the interrupt is busy, in a cascade or masked */ if (SlavePic.CascadeRegister & (1 << Number) - || SlavePic.InServiceRegister & (1 << Number)) + || SlavePic.InServiceRegister & (1 << Number) + || SlavePic.MaskRegister & (1 << Number)) { return; }
- SlavePic.InServiceRegister |= 1 << Number; + if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= 1 << Number; EmulatorInterrupt(SlavePic.IntOffset + Number); } } + +VOID PitWriteCommand(BYTE Value) +{ + BYTE Channel = Value >> 6; + BYTE Mode = (Value >> 1) & 0x07; + + /* Set the access mode and reset flip-flop */ + // TODO: Support latch command! + PitChannels[Channel].AccessMode = (Value >> 4) & 3; + PitChannels[Channel].FlipFlop = FALSE; + + switch (Mode) + { + case 0: + case 4: + { + PitChannels[Channel].RateGenerator = FALSE; + break; + } + + case 2: + case 3: + { + PitChannels[Channel].RateGenerator = TRUE; + break; + } + } +} + +VOID PitWriteData(BYTE Channel, BYTE Value) +{ + /* Use the flip-flop for access mode 3 */ + if (PitChannels[Channel].AccessMode == 3) + { + PitChannels[Channel].AccessMode = PitChannels[Channel].FlipFlop ? 1 : 2; + PitChannels[Channel].FlipFlop = !PitChannels[Channel].FlipFlop; + } + + switch (PitChannels[Channel].AccessMode) + { + case 1: + { + /* Low byte */ + PitChannels[Channel].ReloadValue &= 0xFF00; + PitChannels[Channel].ReloadValue |= Value; + break; + } + + case 2: + { + /* High byte */ + PitChannels[Channel].ReloadValue &= 0x00FF; + PitChannels[Channel].ReloadValue |= Value << 8; + } + } +} + +VOID PitInitialize() +{ + HANDLE ThreadHandle; + + /* Set up channel 0 */ + PitChannels[0].ReloadValue = 0; + PitChannels[0].RateGenerator = TRUE; + PitChannels[0].Pulsed = FALSE; + PitChannels[0].AccessMode = 3; + PitChannels[0].FlipFlop = FALSE; + + /* Create the PIT timer thread */ + ThreadHandle = CreateThread(NULL, 0, PitThread, NULL, 0, NULL); + + /* We don't need the handle */ + CloseHandle(ThreadHandle); +}
Modified: branches/ntvdm/subsystems/ntvdm/ntvdm.h URL: http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/ntvdm.h?r... ============================================================================== --- branches/ntvdm/subsystems/ntvdm/ntvdm.h [iso-8859-1] (original) +++ branches/ntvdm/subsystems/ntvdm/ntvdm.h [iso-8859-1] Fri Jun 21 00:47:07 2013 @@ -208,11 +208,15 @@ VOID DosInt21h(WORD CodeSegment); VOID DosBreakInterrupt(); VOID BiosVideoService(); +VOID BiosHandleIrq(BYTE IrqNumber); BYTE PicReadCommand(BYTE Port); VOID PicWriteCommand(BYTE Port, BYTE Value); BYTE PicReadData(BYTE Port); VOID PicWriteData(BYTE Port, BYTE Value); VOID PicInterruptRequest(BYTE Number); +VOID PitInitialize(); +VOID PitWriteCommand(BYTE Value); +VOID PitWriteData(BYTE Channel, BYTE Value); VOID EmulatorSetStack(WORD Segment, WORD Offset); VOID EmulatorExecute(WORD Segment, WORD Offset); VOID EmulatorInterrupt(BYTE Number);