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
This commit is contained in:
Aleksandar Andrejevic
2013-06-21 13:55:31 +00:00
parent 03b2c06c97
commit f3aa7c7145
4 changed files with 218 additions and 79 deletions
+3 -1
View 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;
}
+185 -77
View 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;
BOOLEAN Pulsed;
BOOLEAN FlipFlop;
BYTE AccessMode;
WORD ReloadValue;
WORD CurrentValue;
WORD LatchedValue;
INT Mode;
BOOLEAN Pulsed;
BOOLEAN LatchSet;
BOOLEAN InputFlipFlop;
BOOLEAN OutputFlipFlop;
BYTE AccessMode;
} 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 4:
{
PitChannels[Channel].RateGenerator = FALSE;
break;
}
case 1:
case 2:
case 3:
case 4:
case 5:
{
PitChannels[Channel].RateGenerator = TRUE;
PitChannels[Channel].Mode = Mode;
break;
}
case 6:
{
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;
}
}
}
}
+28 -1
View 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();
+2
View 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);