[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 21 Jun 2013 00:47:07 +0000 (00:47 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 21 Jun 2013 00:47:07 +0000 (00:47 +0000)
Implement Programmable Interval Timer (PIT) emulation. Fix bugs in interrupts and PIC emulation.

svn path=/branches/ntvdm/; revision=59271

subsystems/ntvdm/bios.c
subsystems/ntvdm/emulator.c
subsystems/ntvdm/hardware.c
subsystems/ntvdm/ntvdm.h

index 72f3f67..2dad126 100644 (file)
@@ -71,6 +71,12 @@ BOOLEAN BiosInitialize()
     /* 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 BiosVideoService()
     }
 }
 
+VOID BiosHandleIrq(BYTE IrqNumber)
+{
+    PicWriteCommand(PIC_MASTER_CMD, PIC_OCW2_EOI);
+}
+
 /* EOF */
index 313067c..bce9afb 100644 (file)
@@ -56,12 +56,58 @@ static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT
 
 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)
@@ -101,6 +147,18 @@ static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
             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 EmulatorExecute(WORD Segment, WORD Offset)
 
 VOID EmulatorInterrupt(BYTE Number)
 {
-    LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress);
+    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
     UINT Segment, Offset;
 
     /* Get the segment and offset */
index 751c833..fc36cd8 100644 (file)
@@ -24,7 +24,64 @@ typedef struct _PIC
     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 @@ VOID PicInterruptRequest(BYTE Number)
 {
     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 @@ VOID PicInterruptRequest(BYTE Number)
             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;
+        if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << 2;
         
-        /* Check if the interrupt is busy or in a cascade */
+        /* 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);
+}
index 44555c8..0edfb8b 100644 (file)
@@ -208,11 +208,15 @@ VOID DosInt20h(WORD CodeSegment);
 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);