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?r…
==============================================================================
--- 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?…
==============================================================================
--- 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);