/* * COPYRIGHT: GPL - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: timer.c * PURPOSE: Programmable Interval Timer emulation * PROGRAMMERS: Aleksandar Andrejevic */ /* INCLUDES *******************************************************************/ #define NDEBUG #include "emulator.h" #include "io.h" #include "timer.h" #include "pic.h" /* PRIVATE VARIABLES **********************************************************/ static PIT_CHANNEL PitChannels[PIT_CHANNELS]; PPIT_CHANNEL PitChannel2 = &PitChannels[2]; /* PUBLIC FUNCTIONS ***********************************************************/ VOID PitWriteCommand(BYTE Value) { BYTE Channel = Value >> 6; BYTE Mode = (Value >> 1) & 0x07; /* 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].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].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) { AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2; PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop; } switch (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; } } } static BYTE WINAPI PitReadPort(ULONG Port) { switch (Port) { case PIT_DATA_PORT(0): case PIT_DATA_PORT(1): case PIT_DATA_PORT(2): { return PitReadData(Port - PIT_DATA_PORT(0)); } } return 0; } static VOID WINAPI PitWritePort(ULONG Port, BYTE Data) { switch (Port) { case PIT_COMMAND_PORT: { PitWriteCommand(Data); break; } case PIT_DATA_PORT(0): case PIT_DATA_PORT(1): case PIT_DATA_PORT(2): { PitWriteData(Port - PIT_DATA_PORT(0), Data); break; } } } VOID PitDecrementCount(DWORD Count) { INT i; for (i = 0; i < PIT_CHANNELS; i++) { switch (PitChannels[i].Mode) { case PIT_MODE_INT_ON_TERMINAL_COUNT: { /* Decrement the value */ if (Count > PitChannels[i].CurrentValue) { /* The value does not reload in this case */ PitChannels[i].CurrentValue = 0; } else PitChannels[i].CurrentValue -= Count; /* 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: { BOOLEAN Reloaded = FALSE; while (Count) { if ((Count > PitChannels[i].CurrentValue) && (PitChannels[i].CurrentValue != 0)) { /* Decrease the count */ Count -= PitChannels[i].CurrentValue; /* Reload the value */ PitChannels[i].CurrentValue = PitChannels[i].ReloadValue; /* Set the flag */ Reloaded = TRUE; } else { /* Decrease the value */ PitChannels[i].CurrentValue -= Count; /* Clear the count */ Count = 0; /* Did it fall to zero? */ if (PitChannels[i].CurrentValue == 0) { PitChannels[i].CurrentValue = PitChannels[i].ReloadValue; Reloaded = TRUE; } } } /* If there was a reload on channel 0, raise IRQ 0 */ if ((i == 0) && Reloaded) PicInterruptRequest(0); break; } case PIT_MODE_SQUARE_WAVE: { INT ReloadCount = 0; WORD ReloadValue = PitChannels[i].ReloadValue; /* The reload value must be even */ ReloadValue &= ~1; while (Count) { if (((Count * 2) > PitChannels[i].CurrentValue) && (PitChannels[i].CurrentValue != 0)) { /* Decrease the count */ Count -= PitChannels[i].CurrentValue / 2; /* Reload the value */ PitChannels[i].CurrentValue = ReloadValue; /* Increment the reload count */ ReloadCount++; } else { /* Decrease the value */ PitChannels[i].CurrentValue -= Count * 2; /* Clear the count */ Count = 0; /* Did it fall to zero? */ if (PitChannels[i].CurrentValue == 0) { /* Reload the value */ PitChannels[i].CurrentValue = ReloadValue; /* Increment the reload count */ ReloadCount++; } } } if (ReloadCount == 0) break; /* Toggle the flip-flop if the number of reloads was odd */ if (ReloadCount & 1) { PitChannels[i].OutputFlipFlop = !PitChannels[i].OutputFlipFlop; } /* Was there any rising edge on channel 0 ? */ if (((PitChannels[i].OutputFlipFlop && (ReloadCount == 1)) || (ReloadCount > 1)) && (i == 0)) { /* Yes, IRQ 0 */ PicInterruptRequest(0); } 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; } } } } DWORD PitGetResolution(VOID) { INT i; DWORD MinReloadValue = 65536; for (i = 0; i < PIT_CHANNELS; i++) { DWORD ReloadValue = PitChannels[i].ReloadValue; /* 0 means 65536 */ if (ReloadValue == 0) ReloadValue = 65536; if (ReloadValue < MinReloadValue) MinReloadValue = ReloadValue; } /* Return the frequency resolution */ return PIT_BASE_FREQUENCY / MinReloadValue; } BOOLEAN PitInitialize(VOID) { /* Register the I/O Ports */ RegisterIoPort(PIT_COMMAND_PORT, NULL , PitWritePort); RegisterIoPort(PIT_DATA_PORT(0), PitReadPort, PitWritePort); RegisterIoPort(PIT_DATA_PORT(1), PitReadPort, PitWritePort); RegisterIoPort(PIT_DATA_PORT(2), PitReadPort, PitWritePort); return TRUE; } /* EOF */