mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-09-02 04:13:34 +00:00
[NTVDM]
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:
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user