From: Aleksandar Andrejevic Date: Fri, 21 Jun 2013 13:55:31 +0000 (+0000) Subject: [NTVDM] X-Git-Tag: backups/0.3.17@66124~1365^2~589 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=f3aa7c71456f714d9f622ab85cb557a96043a36f [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 --- diff --git a/subsystems/ntvdm/bios.c b/subsystems/ntvdm/bios.c index 2dad12699c0..1f82d88fb47 100644 --- a/subsystems/ntvdm/bios.c +++ b/subsystems/ntvdm/bios.c @@ -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; } diff --git a/subsystems/ntvdm/hardware.c b/subsystems/ntvdm/hardware.c index 784cff6c24a..3f61ccf083d 100644 --- a/subsystems/ntvdm/hardware.c +++ b/subsystems/ntvdm/hardware.c @@ -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; + } + } + } } + diff --git a/subsystems/ntvdm/ntvdm.c b/subsystems/ntvdm/ntvdm.c index 40a5cddfd83..0050d3dc041 100644 --- a/subsystems/ntvdm/ntvdm.c +++ b/subsystems/ntvdm/ntvdm.c @@ -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(); diff --git a/subsystems/ntvdm/ntvdm.h b/subsystems/ntvdm/ntvdm.h index 0edfb8be0f0..fdf331cc7df 100644 --- a/subsystems/ntvdm/ntvdm.h +++ b/subsystems/ntvdm/ntvdm.h @@ -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);