Big PIT fix Part 1/X
Currently, sound frequency is fixed (in Advanced NetWars, Rover, Dave)

svn path=/branches/ntvdm/; revision=61810
This commit is contained in:
Hermès Bélusca-Maïto 2014-01-25 17:00:09 +00:00
parent f878b8abae
commit 2bbb94f82b
2 changed files with 222 additions and 85 deletions

View file

@ -2,8 +2,10 @@
* COPYRIGHT: GPL - See COPYING in the top level directory * COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine * PROJECT: ReactOS Virtual DOS Machine
* FILE: timer.c * FILE: timer.c
* PURPOSE: Programmable Interval Timer emulation * PURPOSE: Programmable Interval Timer emulation -
* i82C54/8254 compatible
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/ */
/* INCLUDES *******************************************************************/ /* INCLUDES *******************************************************************/
@ -22,25 +24,139 @@ PPIT_CHANNEL PitChannel2 = &PitChannels[2];
/* PRIVATE FUNCTIONS **********************************************************/ /* PRIVATE FUNCTIONS **********************************************************/
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;
/* HACK!! */BYTE NullCount = 0;/* HACK!! */
StatusLatch = PitChannels[Channel].Out << 7 | NullCount << 6;
StatusLatch |= (PitChannels[Channel].ReadWriteMode & 0x03) << 4;
StatusLatch |= (PitChannels[Channel].Mode & 0x07) << 1;
StatusLatch |= (PitChannels[Channel].Bcd & 0x01);
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)
{
PitChannels[Channel].ReadStatus = PitChannels[Channel].ReadWriteMode;
/* Convert the current value to BCD if needed */
PitChannels[Channel].OutputLatch = READ_PIT_VALUE(PitChannels[Channel],
PitChannels[Channel].CurrentValue);
}
}
static VOID PitSetOut(PPIT_CHANNEL Channel, BOOLEAN State)
{
if (State == Channel->Out) return;
/* Set the new state of the OUT pin */
Channel->Out = State;
// /* Call the callback */
// if (Channel->OutFunction) Channel->OutFunction(Channel->OutParam, State);
}
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;
}
}
static VOID PitWriteCommand(BYTE Value) static VOID PitWriteCommand(BYTE Value)
{ {
BYTE Channel = Value >> 6; BYTE Channel = (Value >> 6) & 0x03;
BYTE ReadWriteMode = (Value >> 4) & 0x03;
BYTE Mode = (Value >> 1) & 0x07; BYTE Mode = (Value >> 1) & 0x07;
BOOLEAN IsBcd = Value & 0x01;
/* Check if this is a counter latch command */ /*
if (((Value >> 4) & 3) == 0) * 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)
{ {
PitChannels[Channel].LatchSet = TRUE; if ((Value & 0x20) == 0) // Bit 5 (Count) == 0: We latch multiple counters' counts
PitChannels[Channel].LatchedValue = PitChannels[Channel].CurrentValue; {
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; return;
} }
/* Set the access mode and reset flip-flops */ /* Check if this is a counter latch command... */
PitChannels[Channel].AccessMode = (Value >> 4) & 3; if (ReadWriteMode == 0)
{
PitLatchChannelCount(Channel);
return;
}
/* ... otherwise, set the modes and reset flip-flops */
PitChannels[Channel].ReadWriteMode = ReadWriteMode;
PitChannels[Channel].LatchStatusSet = FALSE;
PitChannels[Channel].StatusLatch = 0x00;
PitChannels[Channel].ReadStatus = 0x00;
PitChannels[Channel].WriteStatus = 0x00;
PitChannels[Channel].CountRegister = 0x00;
PitChannels[Channel].OutputLatch = 0x00;
PitChannels[Channel].Pulsed = FALSE; PitChannels[Channel].Pulsed = FALSE;
PitChannels[Channel].LatchSet = FALSE;
PitChannels[Channel].InputFlipFlop = FALSE;
PitChannels[Channel].OutputFlipFlop = FALSE; // PitChannels[Channel].Out = FALSE; // <-- unneeded, see the PitInitCounter call below.
/* Fix the current value if we switch to BCD counting */
PitChannels[Channel].Bcd = IsBcd;
if (IsBcd && PitChannels[Channel].CurrentValue > 9999)
PitChannels[Channel].CurrentValue = 9999;
switch (Mode) switch (Mode)
{ {
@ -56,96 +172,92 @@ static VOID PitWriteCommand(BYTE Value)
} }
case 6: case 6:
{
PitChannels[Channel].Mode = PIT_MODE_RATE_GENERATOR;
break;
}
case 7: case 7:
{ {
PitChannels[Channel].Mode = PIT_MODE_SQUARE_WAVE; /*
* Modes 6 and 7 become PIT_MODE_RATE_GENERATOR
* and PIT_MODE_SQUARE_WAVE respectively.
*/
PitChannels[Channel].Mode = Mode - 4;
break; break;
} }
} }
PitInitCounter(&PitChannels[Channel]);
} }
static BYTE PitReadData(BYTE Channel) static BYTE PitReadData(BYTE Channel)
{ {
WORD CurrentValue = PitChannels[Channel].CurrentValue; LPBYTE ReadWriteMode = NULL;
BYTE AccessMode = PitChannels[Channel].AccessMode; LPWORD CurrentValue = NULL;
/* Check if the value was latched */ /*
if (PitChannels[Channel].LatchSet) * If the status was latched, the first read operation
* will return the latched status, whichever the count
* value or the status was latched first.
*/
if (PitChannels[Channel].LatchStatusSet)
{ {
CurrentValue = PitChannels[Channel].LatchedValue; PitChannels[Channel].LatchStatusSet = FALSE;
return PitChannels[Channel].StatusLatch;
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 */ /* To be able to read the count asynchronously, latch it first if needed */
if (AccessMode == 3) if (PitChannels[Channel].ReadStatus == 0) PitLatchChannelCount(Channel);
{
AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
/* Check if this was the last read for the latched value */ /* The count is now latched */
if (!PitChannels[Channel].InputFlipFlop) ASSERT(PitChannels[Channel].ReadStatus != 0);
ReadWriteMode = &PitChannels[Channel].ReadStatus ;
CurrentValue = &PitChannels[Channel].OutputLatch;
if (*ReadWriteMode & 1)
{ {
/* Yes, the latch value was read as two bytes */ /* Read LSB */
PitChannels[Channel].LatchSet = FALSE; *ReadWriteMode &= ~1;
} return LOBYTE(*CurrentValue);
} }
switch (AccessMode) if (*ReadWriteMode & 2)
{ {
case 1: /* Read MSB */
{ *ReadWriteMode &= ~2;
/* Low byte */ return HIBYTE(*CurrentValue);
return CurrentValue & 0x00FF;
}
case 2:
{
/* High byte */
return CurrentValue >> 8;
}
} }
/* Shouldn't get here */ /* Shouldn't get here */
ASSERT(FALSE);
return 0; return 0;
} }
static VOID PitWriteData(BYTE Channel, BYTE Value) static VOID PitWriteData(BYTE Channel, BYTE Value)
{ {
BYTE AccessMode = PitChannels[Channel].AccessMode; LPBYTE ReadWriteMode = NULL;
/* Use the flip-flop for access mode 3 */ if (PitChannels[Channel].WriteStatus == 0x00)
if (PitChannels[Channel].AccessMode == 3)
{ {
AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2; PitChannels[Channel].WriteStatus = PitChannels[Channel].ReadWriteMode;
PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
} }
switch (AccessMode) ReadWriteMode = &PitChannels[Channel].WriteStatus;
if (*ReadWriteMode & 1)
{ {
case 1: /* Write LSB */
{ *ReadWriteMode &= ~1;
/* Low byte */
PitChannels[Channel].ReloadValue &= 0xFF00; PitChannels[Channel].ReloadValue &= 0xFF00;
PitChannels[Channel].ReloadValue |= Value; PitChannels[Channel].ReloadValue |= Value;
break; return;
} }
else if (*ReadWriteMode & 2)
case 2:
{ {
/* High byte */ /* Write MSB */
*ReadWriteMode &= ~2;
PitChannels[Channel].ReloadValue &= 0x00FF; PitChannels[Channel].ReloadValue &= 0x00FF;
PitChannels[Channel].ReloadValue |= Value << 8; PitChannels[Channel].ReloadValue |= Value << 8;
} return;
} }
} }
@ -302,11 +414,11 @@ VOID PitDecrementCount(DWORD Count)
/* Toggle the flip-flop if the number of reloads was odd */ /* Toggle the flip-flop if the number of reloads was odd */
if (ReloadCount & 1) if (ReloadCount & 1)
{ {
PitChannels[i].OutputFlipFlop = !PitChannels[i].OutputFlipFlop; PitChannels[i].Out = !PitChannels[i].Out;
} }
/* Was there any rising edge on channel 0 ? */ /* Was there any rising edge on channel 0 ? */
if (((PitChannels[i].OutputFlipFlop && (ReloadCount == 1)) if (((PitChannels[i].Out && (ReloadCount == 1))
|| (ReloadCount > 1)) || (ReloadCount > 1))
&& (i == 0)) && (i == 0))
{ {

View file

@ -2,8 +2,10 @@
* COPYRIGHT: GPL - See COPYING in the top level directory * COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine * PROJECT: ReactOS Virtual DOS Machine
* FILE: timer.h * FILE: timer.h
* PURPOSE: Programmable Interval Timer emulation * PURPOSE: Programmable Interval Timer emulation -
* i82C54/8254 compatible
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/ */
#ifndef _TIMER_H_ #ifndef _TIMER_H_
@ -20,7 +22,13 @@
#define PIT_DATA_PORT(x) (0x40 + (x)) #define PIT_DATA_PORT(x) (0x40 + (x))
#define PIT_COMMAND_PORT 0x43 #define PIT_COMMAND_PORT 0x43
enum #define WRITE_PIT_VALUE(PitChannel, Value) \
(PitChannel).Bcd ? BCD_TO_BINARY(Value) : (Value)
#define READ_PIT_VALUE(PitChannel, Value) \
(PitChannel).Bcd ? BINARY_TO_BCD(Value) : (Value)
typedef enum _PIT_MODE
{ {
PIT_MODE_INT_ON_TERMINAL_COUNT, PIT_MODE_INT_ON_TERMINAL_COUNT,
PIT_MODE_HARDWARE_ONE_SHOT, PIT_MODE_HARDWARE_ONE_SHOT,
@ -28,19 +36,36 @@ enum
PIT_MODE_SQUARE_WAVE, PIT_MODE_SQUARE_WAVE,
PIT_MODE_SOFTWARE_STROBE, PIT_MODE_SOFTWARE_STROBE,
PIT_MODE_HARDWARE_STROBE PIT_MODE_HARDWARE_STROBE
}; } PIT_MODE, *PPIT_MODE;
typedef struct _PIT_CHANNEL typedef struct _PIT_CHANNEL
{ {
WORD ReloadValue;
WORD CurrentValue;
WORD LatchedValue;
INT Mode;
BOOLEAN Pulsed; BOOLEAN Pulsed;
BOOLEAN LatchSet;
BOOLEAN InputFlipFlop;
BOOLEAN OutputFlipFlop; /* PIT Status members */
BYTE AccessMode; PIT_MODE Mode;
BOOLEAN Bcd;
BYTE ReadWriteMode; // 0 --> Counter Latch ; 1 --> LSB R/W ; 2 --> MSB R/W ; 3 --> LSB then MSB R/W
/* Reading the PIT status byte */
BOOLEAN LatchStatusSet;
BYTE StatusLatch;
/* For interleaving reading and writing in 2-byte RW mode */
BYTE ReadStatus; // Same convention as ReadWriteMode
BYTE WriteStatus; // Same convention as ReadWriteMode
/**/WORD CountRegister;/**/ // Our ReloadValue ???
WORD OutputLatch;
/*******************************/
WORD ReloadValue; // Max value of the counter
WORD CurrentValue; // Real value of the counter
/* PIT Output */
BOOLEAN Out; // 0: Low ; 1: High
} PIT_CHANNEL, *PPIT_CHANNEL; } PIT_CHANNEL, *PPIT_CHANNEL;
extern PPIT_CHANNEL PitChannel2; // Needed for PC Speaker extern PPIT_CHANNEL PitChannel2; // Needed for PC Speaker