2013-06-26 22:58:41 +00:00
|
|
|
/*
|
|
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
|
|
* FILE: timer.c
|
2014-01-25 17:00:09 +00:00
|
|
|
* PURPOSE: Programmable Interval Timer emulation -
|
|
|
|
* i82C54/8254 compatible
|
2013-06-26 22:58:41 +00:00
|
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
2014-01-25 17:00:09 +00:00
|
|
|
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
|
2013-06-26 22:58:41 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
2013-07-22 13:51:26 +00:00
|
|
|
#define NDEBUG
|
|
|
|
|
2013-11-26 20:20:51 +00:00
|
|
|
#include "emulator.h"
|
|
|
|
#include "io.h"
|
2013-06-26 22:58:41 +00:00
|
|
|
#include "timer.h"
|
|
|
|
#include "pic.h"
|
|
|
|
|
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
|
|
|
|
static PIT_CHANNEL PitChannels[PIT_CHANNELS];
|
2013-11-03 20:31:19 +00:00
|
|
|
PPIT_CHANNEL PitChannel2 = &PitChannels[2];
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-11 21:45:01 +00:00
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 17:00:09 +00:00
|
|
|
static VOID PitLatchChannelStatus(BYTE Channel)
|
|
|
|
{
|
|
|
|
if (Channel >= PIT_CHANNELS) return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A given counter can be latched only one time until it gets unlatched.
|
|
|
|
* If the counter is latched and then is latched again later before the
|
|
|
|
* value is read, then this last latch command is ignored and the value
|
|
|
|
* will be the value at the time the first command was issued.
|
|
|
|
*/
|
|
|
|
if (PitChannels[Channel].LatchStatusSet == FALSE)
|
|
|
|
{
|
|
|
|
BYTE StatusLatch = 0;
|
2014-01-28 20:24:24 +00:00
|
|
|
/** HACK!! **/BYTE NullCount = 0;/** HACK!! **/
|
2014-01-25 17:00:09 +00:00
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
StatusLatch = PitChannels[Channel].Out << 7 | NullCount << 6;
|
2014-01-25 17:00:09 +00:00
|
|
|
StatusLatch |= (PitChannels[Channel].ReadWriteMode & 0x03) << 4;
|
|
|
|
StatusLatch |= (PitChannels[Channel].Mode & 0x07) << 1;
|
|
|
|
StatusLatch |= (PitChannels[Channel].Bcd & 0x01);
|
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Latch the counter's status */
|
2014-01-25 17:00:09 +00:00
|
|
|
PitChannels[Channel].LatchStatusSet = TRUE;
|
|
|
|
PitChannels[Channel].StatusLatch = StatusLatch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static VOID PitLatchChannelCount(BYTE Channel)
|
|
|
|
{
|
|
|
|
if (Channel >= PIT_CHANNELS) return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A given counter can be latched only one time until it gets unlatched.
|
|
|
|
* If the counter is latched and then is latched again later before the
|
|
|
|
* value is read, then this last latch command is ignored and the value
|
|
|
|
* will be the value at the time the first command was issued.
|
|
|
|
*/
|
|
|
|
if (PitChannels[Channel].ReadStatus == 0x00)
|
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Latch the counter's value */
|
2014-01-25 17:00:09 +00:00
|
|
|
PitChannels[Channel].ReadStatus = PitChannels[Channel].ReadWriteMode;
|
|
|
|
|
|
|
|
/* Convert the current value to BCD if needed */
|
2014-01-28 20:24:24 +00:00
|
|
|
PitChannels[Channel].OutputLatch =
|
|
|
|
READ_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CurrentValue);
|
2014-01-25 17:00:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static VOID PitSetOut(PPIT_CHANNEL Channel, BOOLEAN State)
|
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
/** HACK!! **\ if (State == Channel->Out) return; \** HACK!! **/
|
2014-01-25 17:00:09 +00:00
|
|
|
|
|
|
|
/* Set the new state of the OUT pin */
|
|
|
|
Channel->Out = State;
|
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Call the callback */
|
2014-09-27 15:31:12 +00:00
|
|
|
if (!Channel->Gate) return; // HACK: This is a HACK until gates are properly used (needed for the speaker to work properly).
|
2014-01-28 20:24:24 +00:00
|
|
|
if (Channel->OutFunction) Channel->OutFunction(Channel->OutParam, State);
|
2014-01-25 17:00:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static VOID PitInitCounter(PPIT_CHANNEL Channel)
|
|
|
|
{
|
|
|
|
switch (Channel->Mode)
|
|
|
|
{
|
|
|
|
case PIT_MODE_INT_ON_TERMINAL_COUNT:
|
|
|
|
PitSetOut(Channel, FALSE);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PIT_MODE_HARDWARE_ONE_SHOT:
|
|
|
|
case PIT_MODE_RATE_GENERATOR:
|
|
|
|
case PIT_MODE_SQUARE_WAVE:
|
|
|
|
case PIT_MODE_SOFTWARE_STROBE:
|
|
|
|
case PIT_MODE_HARDWARE_STROBE:
|
|
|
|
PitSetOut(Channel, TRUE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-11 21:45:01 +00:00
|
|
|
static VOID PitWriteCommand(BYTE Value)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 17:00:09 +00:00
|
|
|
BYTE Channel = (Value >> 6) & 0x03;
|
|
|
|
BYTE ReadWriteMode = (Value >> 4) & 0x03;
|
|
|
|
BYTE Mode = (Value >> 1) & 0x07;
|
|
|
|
BOOLEAN IsBcd = Value & 0x01;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for valid PIT channel - Possible values: 0, 1, 2.
|
|
|
|
* A value of 3 is for Read-Back Command.
|
|
|
|
*/
|
|
|
|
if (Channel > PIT_CHANNELS) return;
|
|
|
|
|
|
|
|
/* Read-Back Command */
|
|
|
|
if (Channel == PIT_CHANNELS)
|
|
|
|
{
|
|
|
|
if ((Value & 0x20) == 0) // Bit 5 (Count) == 0: We latch multiple counters' counts
|
|
|
|
{
|
|
|
|
if (Value & 0x02) PitLatchChannelCount(0);
|
|
|
|
if (Value & 0x04) PitLatchChannelCount(1);
|
|
|
|
if (Value & 0x08) PitLatchChannelCount(2);
|
|
|
|
}
|
|
|
|
if ((Value & 0x10) == 0) // Bit 4 (Status) == 0: We latch multiple counters' statuses
|
|
|
|
{
|
|
|
|
if (Value & 0x02) PitLatchChannelStatus(0);
|
|
|
|
if (Value & 0x04) PitLatchChannelStatus(1);
|
|
|
|
if (Value & 0x08) PitLatchChannelStatus(2);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 17:00:09 +00:00
|
|
|
/* Check if this is a counter latch command... */
|
|
|
|
if (ReadWriteMode == 0)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 17:00:09 +00:00
|
|
|
PitLatchChannelCount(Channel);
|
2013-06-26 22:58:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-25 17:00:09 +00:00
|
|
|
/* ... otherwise, set the modes and reset flip-flops */
|
|
|
|
PitChannels[Channel].ReadWriteMode = ReadWriteMode;
|
2014-01-28 20:24:24 +00:00
|
|
|
PitChannels[Channel].ReadStatus = 0x00;
|
|
|
|
PitChannels[Channel].WriteStatus = 0x00;
|
2014-01-25 17:00:09 +00:00
|
|
|
|
|
|
|
PitChannels[Channel].LatchStatusSet = FALSE;
|
|
|
|
PitChannels[Channel].StatusLatch = 0x00;
|
|
|
|
|
|
|
|
PitChannels[Channel].CountRegister = 0x00;
|
|
|
|
PitChannels[Channel].OutputLatch = 0x00;
|
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
/** HACK!! **/PitChannels[Channel].FlipFlop = FALSE;/** HACK!! **/
|
2014-01-25 17:00:09 +00:00
|
|
|
|
|
|
|
/* Fix the current value if we switch to BCD counting */
|
|
|
|
PitChannels[Channel].Bcd = IsBcd;
|
|
|
|
if (IsBcd && PitChannels[Channel].CurrentValue > 9999)
|
|
|
|
PitChannels[Channel].CurrentValue = 9999;
|
2013-06-26 22:58:41 +00:00
|
|
|
|
|
|
|
switch (Mode)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
case 5:
|
|
|
|
{
|
|
|
|
PitChannels[Channel].Mode = Mode;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
case 7:
|
|
|
|
{
|
2014-01-25 17:00:09 +00:00
|
|
|
/*
|
|
|
|
* Modes 6 and 7 become PIT_MODE_RATE_GENERATOR
|
|
|
|
* and PIT_MODE_SQUARE_WAVE respectively.
|
|
|
|
*/
|
|
|
|
PitChannels[Channel].Mode = Mode - 4;
|
2013-06-26 22:58:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-01-25 17:00:09 +00:00
|
|
|
|
|
|
|
PitInitCounter(&PitChannels[Channel]);
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-11 21:45:01 +00:00
|
|
|
static BYTE PitReadData(BYTE Channel)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 17:00:09 +00:00
|
|
|
LPBYTE ReadWriteMode = NULL;
|
|
|
|
LPWORD CurrentValue = NULL;
|
|
|
|
|
|
|
|
/*
|
2014-01-28 20:24:24 +00:00
|
|
|
* If the status was latched, the first read operation will return the
|
|
|
|
* latched status, whichever value (count or status) was latched first.
|
2014-01-25 17:00:09 +00:00
|
|
|
*/
|
|
|
|
if (PitChannels[Channel].LatchStatusSet)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 17:00:09 +00:00
|
|
|
PitChannels[Channel].LatchStatusSet = FALSE;
|
|
|
|
return PitChannels[Channel].StatusLatch;
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-25 17:00:09 +00:00
|
|
|
/* To be able to read the count asynchronously, latch it first if needed */
|
|
|
|
if (PitChannels[Channel].ReadStatus == 0) PitLatchChannelCount(Channel);
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 17:00:09 +00:00
|
|
|
/* The count is now latched */
|
|
|
|
ASSERT(PitChannels[Channel].ReadStatus != 0);
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 17:00:09 +00:00
|
|
|
ReadWriteMode = &PitChannels[Channel].ReadStatus ;
|
|
|
|
CurrentValue = &PitChannels[Channel].OutputLatch;
|
|
|
|
|
|
|
|
if (*ReadWriteMode & 1)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 17:00:09 +00:00
|
|
|
/* Read LSB */
|
|
|
|
*ReadWriteMode &= ~1;
|
|
|
|
return LOBYTE(*CurrentValue);
|
|
|
|
}
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 17:00:09 +00:00
|
|
|
if (*ReadWriteMode & 2)
|
|
|
|
{
|
|
|
|
/* Read MSB */
|
|
|
|
*ReadWriteMode &= ~2;
|
|
|
|
return HIBYTE(*CurrentValue);
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Shouldn't get here */
|
2014-01-25 17:00:09 +00:00
|
|
|
ASSERT(FALSE);
|
2013-06-26 22:58:41 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-11 21:45:01 +00:00
|
|
|
static VOID PitWriteData(BYTE Channel, BYTE Value)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 17:00:09 +00:00
|
|
|
LPBYTE ReadWriteMode = NULL;
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 17:00:09 +00:00
|
|
|
if (PitChannels[Channel].WriteStatus == 0x00)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 17:00:09 +00:00
|
|
|
PitChannels[Channel].WriteStatus = PitChannels[Channel].ReadWriteMode;
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
ASSERT(PitChannels[Channel].WriteStatus != 0);
|
|
|
|
|
2014-01-25 17:00:09 +00:00
|
|
|
ReadWriteMode = &PitChannels[Channel].WriteStatus;
|
|
|
|
|
|
|
|
if (*ReadWriteMode & 1)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 17:00:09 +00:00
|
|
|
/* Write LSB */
|
|
|
|
*ReadWriteMode &= ~1;
|
2014-01-25 18:56:12 +00:00
|
|
|
PitChannels[Channel].CountRegister &= 0xFF00;
|
|
|
|
PitChannels[Channel].CountRegister |= Value;
|
2014-01-25 17:00:09 +00:00
|
|
|
}
|
|
|
|
else if (*ReadWriteMode & 2)
|
|
|
|
{
|
|
|
|
/* Write MSB */
|
|
|
|
*ReadWriteMode &= ~2;
|
2014-01-25 18:56:12 +00:00
|
|
|
PitChannels[Channel].CountRegister &= 0x00FF;
|
|
|
|
PitChannels[Channel].CountRegister |= Value << 8;
|
|
|
|
}
|
2014-01-25 17:00:09 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
/* ReadWriteMode went to zero: we are going to load the new count */
|
|
|
|
if (*ReadWriteMode == 0x00)
|
|
|
|
{
|
|
|
|
if (PitChannels[Channel].CountRegister == 0x0000)
|
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Wrap around to the highest count */
|
2014-01-25 18:56:12 +00:00
|
|
|
if (PitChannels[Channel].Bcd)
|
|
|
|
PitChannels[Channel].CountRegister = 9999;
|
|
|
|
else
|
|
|
|
PitChannels[Channel].CountRegister = 0xFFFF; // 0x10000; // 65536
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert the current value from BCD if needed */
|
2014-01-28 20:24:24 +00:00
|
|
|
PitChannels[Channel].CountRegister =
|
|
|
|
WRITE_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CountRegister);
|
2014-01-25 18:56:12 +00:00
|
|
|
PitChannels[Channel].ReloadValue = PitChannels[Channel].CountRegister;
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-23 12:17:44 +00:00
|
|
|
static BYTE WINAPI PitReadPort(USHORT Port)
|
2013-11-26 20:20:51 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-10-23 12:17:44 +00:00
|
|
|
static VOID WINAPI PitWritePort(USHORT Port, BYTE Data)
|
2013-11-26 20:20:51 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
if (Count == 0) return;
|
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
switch (Channel->Mode)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 18:56:12 +00:00
|
|
|
case PIT_MODE_INT_ON_TERMINAL_COUNT:
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 18:56:12 +00:00
|
|
|
/* Decrement the value */
|
|
|
|
if (Count > Channel->CurrentValue)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 18:56:12 +00:00
|
|
|
/* The value does not reload in this case */
|
|
|
|
Channel->CurrentValue = 0;
|
|
|
|
}
|
|
|
|
else Channel->CurrentValue -= Count;
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
/* Did it fall to the terminal count? */
|
2014-01-28 20:24:24 +00:00
|
|
|
if (Channel->CurrentValue == 0 && !Channel->Out)
|
2014-01-25 18:56:12 +00:00
|
|
|
{
|
|
|
|
/* Yes, raise the output line */
|
2014-01-28 20:24:24 +00:00
|
|
|
PitSetOut(Channel, TRUE);
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
2014-01-25 18:56:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PIT_MODE_RATE_GENERATOR:
|
|
|
|
{
|
|
|
|
BOOLEAN Reloaded = FALSE;
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
while (Count)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 18:56:12 +00:00
|
|
|
if ((Count > Channel->CurrentValue)
|
|
|
|
&& (Channel->CurrentValue != 0))
|
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Decrement the count */
|
2014-01-25 18:56:12 +00:00
|
|
|
Count -= Channel->CurrentValue;
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
/* Reload the value */
|
|
|
|
Channel->CurrentValue = Channel->ReloadValue;
|
|
|
|
|
|
|
|
/* Set the flag */
|
|
|
|
Reloaded = TRUE;
|
|
|
|
}
|
|
|
|
else
|
2013-11-01 01:57:40 +00:00
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Decrement the value */
|
2014-01-25 18:56:12 +00:00
|
|
|
Channel->CurrentValue -= Count;
|
2013-11-01 01:57:40 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
/* Clear the count */
|
|
|
|
Count = 0;
|
2013-11-01 01:57:40 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
/* Did it fall to zero? */
|
|
|
|
if (Channel->CurrentValue == 0)
|
2013-11-01 01:57:40 +00:00
|
|
|
{
|
2014-01-25 18:56:12 +00:00
|
|
|
Channel->CurrentValue = Channel->ReloadValue;
|
|
|
|
Reloaded = TRUE;
|
2013-11-01 01:57:40 +00:00
|
|
|
}
|
|
|
|
}
|
2014-01-25 18:56:12 +00:00
|
|
|
}
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
/* If there was a reload, raise the output line */
|
|
|
|
if (Reloaded) PitSetOut(Channel, TRUE);
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PIT_MODE_SQUARE_WAVE:
|
|
|
|
{
|
|
|
|
INT ReloadCount = 0;
|
|
|
|
WORD ReloadValue = Channel->ReloadValue;
|
|
|
|
|
|
|
|
/* The reload value must be even */
|
|
|
|
ReloadValue &= ~1;
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
while (Count)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-25 18:56:12 +00:00
|
|
|
if (((Count * 2) > Channel->CurrentValue)
|
|
|
|
&& (Channel->CurrentValue != 0))
|
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Decrement the count */
|
2014-01-25 18:56:12 +00:00
|
|
|
Count -= Channel->CurrentValue / 2;
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
/* Reload the value */
|
|
|
|
Channel->CurrentValue = ReloadValue;
|
2013-06-26 22:58:41 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
/* Increment the reload count */
|
|
|
|
ReloadCount++;
|
|
|
|
}
|
|
|
|
else
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Decrement the value */
|
2014-01-25 18:56:12 +00:00
|
|
|
Channel->CurrentValue -= Count * 2;
|
2013-11-01 01:57:40 +00:00
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
/* Clear the count */
|
|
|
|
Count = 0;
|
|
|
|
|
|
|
|
/* Did it fall to zero? */
|
|
|
|
if (Channel->CurrentValue == 0)
|
|
|
|
{
|
2013-11-01 01:57:40 +00:00
|
|
|
/* Reload the value */
|
2014-01-25 18:56:12 +00:00
|
|
|
Channel->CurrentValue = ReloadValue;
|
2013-11-01 01:57:40 +00:00
|
|
|
|
|
|
|
/* Increment the reload count */
|
|
|
|
ReloadCount++;
|
|
|
|
}
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
if (ReloadCount == 0) break;
|
|
|
|
|
|
|
|
/* Toggle the flip-flop if the number of reloads was odd */
|
|
|
|
if (ReloadCount & 1)
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
Channel->FlipFlop = !Channel->FlipFlop;
|
2014-09-27 15:31:12 +00:00
|
|
|
PitSetOut(Channel, !Channel->Out);
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Was there any rising edge? */
|
|
|
|
if ((Channel->FlipFlop && (ReloadCount == 1)) || (ReloadCount > 1))
|
2013-06-26 22:58:41 +00:00
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Yes, raise the output line */
|
|
|
|
PitSetOut(Channel, TRUE);
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
2014-01-25 18:56:12 +00:00
|
|
|
|
|
|
|
break;
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
2014-01-25 18:56:12 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
|
2014-01-28 20:24:24 +00:00
|
|
|
VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction)
|
|
|
|
{
|
|
|
|
if (Channel >= PIT_CHANNELS) return;
|
|
|
|
|
|
|
|
PitChannels[Channel].OutParam = Param;
|
|
|
|
PitChannels[Channel].OutFunction = OutFunction;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID PitSetGate(BYTE Channel, BOOLEAN State)
|
|
|
|
{
|
|
|
|
if (Channel >= PIT_CHANNELS) return;
|
|
|
|
if (State == PitChannels[Channel].Gate) return;
|
|
|
|
|
|
|
|
/* UNIMPLEMENTED */
|
|
|
|
PitChannels[Channel].Gate = State;
|
|
|
|
}
|
|
|
|
|
2014-01-25 18:56:12 +00:00
|
|
|
VOID PitClock(DWORD Count)
|
|
|
|
{
|
|
|
|
UINT i;
|
|
|
|
|
|
|
|
if (Count == 0) return;
|
|
|
|
|
|
|
|
for (i = 0; i < PIT_CHANNELS; i++)
|
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
// if (!PitChannels[i].Counting) continue;
|
2014-01-25 18:56:12 +00:00
|
|
|
PitDecrementCount(&PitChannels[i], Count);
|
2013-06-26 22:58:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-28 02:25:54 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
VOID PitInitialize(VOID)
|
2013-11-26 20:20:51 +00:00
|
|
|
{
|
2014-01-28 20:24:24 +00:00
|
|
|
/* Set up the timers to their default value */
|
|
|
|
PitSetOutFunction(0, NULL, NULL);
|
|
|
|
PitSetGate(0, TRUE);
|
|
|
|
PitSetOutFunction(1, NULL, NULL);
|
|
|
|
PitSetGate(1, TRUE);
|
|
|
|
PitSetOutFunction(2, NULL, NULL);
|
|
|
|
PitSetGate(2, FALSE);
|
|
|
|
|
2013-11-26 20:20:51 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
/* EOF */
|