mirror of
https://github.com/reactos/reactos.git
synced 2024-09-09 12:20:21 +00:00
237 lines
6.2 KiB
C
237 lines
6.2 KiB
C
/*
|
|
* 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 <theflash AT sdf DOT lonestar DOT org>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#define NDEBUG
|
|
|
|
#include "timer.h"
|
|
#include "pic.h"
|
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
static PIT_CHANNEL PitChannels[PIT_CHANNELS];
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID PitDecrementCount()
|
|
{
|
|
INT i;
|
|
|
|
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;
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* EOF */
|