[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 21 Jun 2013 13:55:31 +0000 (13:55 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 21 Jun 2013 13:55:31 +0000 (13:55 +0000)
Change the PIT code to use only one thread, to avoid race conditions.
Implement PIT counter reading and latch command.

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

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

index 2dad126..1f82d88 100644 (file)
@@ -76,7 +76,9 @@ BOOLEAN BiosInitialize()
     PicWriteData(PIC_MASTER_DATA, 0x00);
     PicWriteData(PIC_SLAVE_DATA, 0x00);
     
-    PitInitialize();
+    PitWriteCommand(0x34);
+    PitWriteData(0, 0x00);
+    PitWriteData(0, 0x00);
 
     return TRUE;
 }
index 784cff6..3f61ccf 100644 (file)
@@ -24,65 +24,32 @@ typedef struct _PIC
     BOOLEAN ReadIsr;
 } PIC, *PPIC;
 
+enum
+{
+    PIT_MODE_INT_ON_TERMINAL_COUNT,
+    PIT_MODE_HARDWARE_ONE_SHOT,
+    PIT_MODE_RATE_GENERATOR,
+    PIT_MODE_SQUARE_WAVE,
+    PIT_MODE_SOFTWARE_STROBE,
+    PIT_MODE_HARDWARE_STROBE
+};
+
 typedef struct _PIT_CHANNEL
 {
-    BOOLEAN RateGenerator;
+    WORD ReloadValue;
+    WORD CurrentValue;
+    WORD LatchedValue;
+    INT Mode;
     BOOLEAN Pulsed;
-    BOOLEAN FlipFlop;
+    BOOLEAN LatchSet;
+    BOOLEAN InputFlipFlop;
+    BOOLEAN OutputFlipFlop;
     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 ***********************************************************/
 
 BYTE PicReadCommand(BYTE Port)
@@ -275,39 +242,110 @@ 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!
+    /* Check if this is a counter latch command */
+    if (((Value >> 4) & 3) == 0)
+    {
+        PitChannels[Channel].LatchSet = TRUE;
+        PitChannels[Channel].LatchedValue = PitChannels[Channel].CurrentValue;
+        return;
+    }
+    
+    /* Set the access mode and reset flip-flops */
     PitChannels[Channel].AccessMode = (Value >> 4) & 3;
-    PitChannels[Channel].FlipFlop = FALSE;
+    PitChannels[Channel].Pulsed = FALSE;
+    PitChannels[Channel].LatchSet = FALSE;
+    PitChannels[Channel].InputFlipFlop = FALSE;
+    PitChannels[Channel].OutputFlipFlop = FALSE;
     
     switch (Mode)
     {
         case 0:
+        case 1:
+        case 2:
+        case 3:
         case 4:
+        case 5:
         {
-            PitChannels[Channel].RateGenerator = FALSE;
+            PitChannels[Channel].Mode = Mode;
             break;
         }
-            
-        case 2:
-        case 3:
+        
+        case 6:
         {
-            PitChannels[Channel].RateGenerator = TRUE;
+            PitChannels[Channel].Mode = PIT_MODE_RATE_GENERATOR;
+            break;
+        }
+        
+        case 7:
+        {
+            PitChannels[Channel].Mode = PIT_MODE_SQUARE_WAVE;
             break;
         }
     }
 }
 
+BYTE PitReadData(BYTE Channel)
+{
+    WORD CurrentValue = PitChannels[Channel].CurrentValue;
+    BYTE AccessMode = PitChannels[Channel].AccessMode;
+
+    /* Check if the value was latched */
+    if (PitChannels[Channel].LatchSet)
+    {
+        CurrentValue = PitChannels[Channel].LatchedValue;
+        
+        if (AccessMode == 1 || AccessMode == 2)
+        {
+            /* The latched value was read as one byte */
+            PitChannels[Channel].LatchSet = FALSE;
+        }
+    }
+    
+    /* Use the flip-flop for access mode 3 */
+    if (AccessMode == 3)
+    {
+        AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
+        PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
+        
+        /* Check if this was the last read for the latched value */
+        if (!PitChannels[Channel].InputFlipFlop)
+        {
+            /* Yes, the latch value was read as two bytes */
+            PitChannels[Channel].LatchSet = FALSE;
+        }
+    }
+    
+    switch (AccessMode)
+    {
+        case 1:
+        {
+            /* Low byte */
+            return CurrentValue & 0x00FF;
+        }
+        
+        case 2:
+        {
+            /* High byte */
+            return CurrentValue >> 8;
+        }
+    }
+    
+    /* Shouldn't get here */
+    return 0;
+}
+
 VOID PitWriteData(BYTE Channel, BYTE Value)
 {
+    BYTE AccessMode = PitChannels[Channel].AccessMode;
+    
     /* 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;
+        AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
+        PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
     }
     
-    switch (PitChannels[Channel].AccessMode)
+    switch (AccessMode)
     {
         case 1:
         {
@@ -326,20 +364,90 @@ VOID PitWriteData(BYTE Channel, BYTE Value)
     }
 }
 
-VOID PitInitialize()
+VOID PitDecrementCount()
 {
-    HANDLE ThreadHandle;
+    INT i;
     
-    /* Set up channel 0 */
-    PitChannels[0].ReloadValue = 0;
-    PitChannels[0].RateGenerator = TRUE;
-    PitChannels[0].Pulsed = FALSE;
-    PitChannels[0].AccessMode = 3;
-    PitChannels[0].FlipFlop = FALSE;
+    for (i = 0; i < PIT_CHANNELS; i++)
+    {
+        switch (PitChannels[i].Mode)
+        {
+            case PIT_MODE_INT_ON_TERMINAL_COUNT:
+            {
+                /* Decrement the value */
+                PitChannels[i].CurrentValue--;
+                
+                /* Did it fall to the terminal count? */
+                if (PitChannels[i].CurrentValue == 0 && !PitChannels[i].Pulsed)
+                {
+                    /* Yes, raise the output line */
+                    if (i == 0) PicInterruptRequest(0);
+                    PitChannels[i].Pulsed = TRUE;
+                }
+                break;
+            }
+            
+            case PIT_MODE_RATE_GENERATOR:
+            {
+                /* Decrement the value */
+                PitChannels[i].CurrentValue--;
+                
+                /* Did it fall to zero? */
+                if (PitChannels[i].CurrentValue != 0) break;
 
-    /* Create the PIT timer thread */
-    ThreadHandle = CreateThread(NULL, 0, PitThread, NULL, 0, NULL);
-    
-    /* We don't need the handle */
-    CloseHandle(ThreadHandle);
+                /* Yes, raise the output line and reload */
+                if (i == 0) PicInterruptRequest(0);
+                PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
+                
+                break;
+            }
+            
+            case PIT_MODE_SQUARE_WAVE:
+            {
+                /* Decrement the value by 2 */
+                PitChannels[i].CurrentValue -= 2;
+                
+                /* Did it fall to zero? */
+                if (PitChannels[i].CurrentValue != 0) break;
+                
+                /* Yes, toggle the flip-flop */
+                PitChannels[i].OutputFlipFlop = !PitChannels[i].OutputFlipFlop;
+                    
+                /* Did this create a rising edge in the signal? */
+                if (PitChannels[i].OutputFlipFlop)
+                {
+                    /* Yes, IRQ 0 if this is channel 0 */
+                    if (i == 0) PicInterruptRequest(0);
+                }
+                    
+                /* Reload the value, but make sure it's even */
+                if (PitChannels[i].ReloadValue % 2)
+                {
+                    /* It's odd, reduce it by 1 */
+                    PitChannels[i].CurrentValue = PitChannels[i].ReloadValue - 1;
+                }
+                else
+                {
+                    /* It was even */
+                    PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
+                }
+                
+                break;
+            }
+            
+            case PIT_MODE_SOFTWARE_STROBE:
+            {
+                // TODO: NOT IMPLEMENTED
+                break;
+            }
+            
+            case PIT_MODE_HARDWARE_ONE_SHOT:
+            case PIT_MODE_HARDWARE_STROBE:
+            {
+                /* These modes do not work on x86 PCs */
+                break;
+            }
+        }
+    }
 }
+
index 40a5cdd..0050d3d 100644 (file)
@@ -57,6 +57,8 @@ INT wmain(INT argc, WCHAR *argv[])
     INT i;
     BOOLEAN PrintUsage = TRUE;
     CHAR CommandLine[128];
+    LARGE_INTEGER Frequency, LastTimerTick, Counter;
+    LONGLONG TimerTicks;
 
     /* Set the handler routine */
     SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
@@ -96,6 +98,13 @@ INT wmain(INT argc, WCHAR *argv[])
     }
 
     if (!EmulatorInitialize()) return 1;
+    
+    /* Initialize the performance counter (needed for hardware timers) */
+    if (!QueryPerformanceFrequency(&Frequency))
+    {
+        wprintf(L"FATAL: Performance counter not available\n");
+        goto Cleanup;
+    }
 
     /* Initialize the system BIOS */
     if (!BiosInitialize())
@@ -117,9 +126,27 @@ INT wmain(INT argc, WCHAR *argv[])
         DisplayMessage(L"Could not start program: %S", CommandLine);
         return -1;
     }
+    
+    /* Set the last timer tick to the current time */
+    QueryPerformanceCounter(&LastTimerTick);
 
     /* Main loop */
-    while (VdmRunning) EmulatorStep();
+    while (VdmRunning)
+    {
+        /* Get the current time */
+        QueryPerformanceCounter(&Counter);
+        
+        /* Get the number of PIT ticks that have passed */
+        TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
+                     * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
+        
+        /* Update the PIT */
+        for (i = 0; i < TimerTicks; i++) PitDecrementCount();
+        LastTimerTick = Counter;
+        
+        /* Continue CPU emulation */
+        EmulatorStep();
+    }
 
 Cleanup:
     EmulatorCleanup();
index 0edfb8b..fdf331c 100644 (file)
@@ -216,7 +216,9 @@ VOID PicWriteData(BYTE Port, BYTE Value);
 VOID PicInterruptRequest(BYTE Number);
 VOID PitInitialize();
 VOID PitWriteCommand(BYTE Value);
+BYTE PitReadData(BYTE Channel);
 VOID PitWriteData(BYTE Channel, BYTE Value);
+VOID PitDecrementCount();
 VOID EmulatorSetStack(WORD Segment, WORD Offset);
 VOID EmulatorExecute(WORD Segment, WORD Offset);
 VOID EmulatorInterrupt(BYTE Number);