From 03d8479401ec83c15a5c6814d82a09d6697a0399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Tue, 28 Jan 2014 20:24:24 +0000 Subject: [PATCH] [NTVDM] Part 2 of PIT + sound fix. - Move port 61h management from speaker.c to the emulator.c module; - Add PIT OUT callbacks support; - Add (unimplemented) PitSetGate function (will be used later on). Still WIP. svn path=/branches/ntvdm/; revision=61866 --- subsystems/ntvdm/emulator.c | 82 ++++++++++++++++++++++++ subsystems/ntvdm/emulator.h | 4 ++ subsystems/ntvdm/hardware/speaker.c | 49 +++------------ subsystems/ntvdm/hardware/speaker.h | 4 +- subsystems/ntvdm/hardware/timer.c | 97 ++++++++++++++++++----------- subsystems/ntvdm/hardware/timer.h | 24 ++++--- 6 files changed, 173 insertions(+), 87 deletions(-) diff --git a/subsystems/ntvdm/emulator.c b/subsystems/ntvdm/emulator.c index c8b2c1366f7..bc3f1017665 100644 --- a/subsystems/ntvdm/emulator.c +++ b/subsystems/ntvdm/emulator.c @@ -31,6 +31,7 @@ LPVOID BaseAddress = NULL; BOOLEAN VdmRunning = TRUE; static BOOLEAN A20Line = FALSE; +static BYTE Port61hState = 0x00; LPCWSTR ExceptionName[] = { @@ -125,6 +126,79 @@ VOID WINAPI EmulatorDebugBreak(LPWORD Stack) DebugBreak(); } + +static BYTE WINAPI Port61hRead(ULONG Port) +{ + return Port61hState; +} + +static VOID WINAPI Port61hWrite(ULONG Port, BYTE Data) +{ + BYTE OldPort61hState = Port61hState; + + /* Only the four lowest bytes can be written */ + Port61hState = (Port61hState & 0xF0) | (Data & 0x0F); + + if ((OldPort61hState ^ Port61hState) & 0x01) + { + DPRINT1("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off"); + } + + PitSetGate(2, !!(Port61hState & 0x01)); + + if ((OldPort61hState ^ Port61hState) & 0x02) + { + /* There were some change for the speaker... */ + DPRINT1("Speaker %s\n", Port61hState & 0x02 ? "on" : "off"); + } +} + +static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State) +{ + if (State) + { + DPRINT("PicInterruptRequest\n"); + PicInterruptRequest(0); // Raise IRQ 0 + } + // else < Lower IRQ 0 > +} + +static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State) +{ +#if 0 + if (State) + { + /* Set bit 4 of Port 61h */ + Port61hState |= 1 << 4; + } + else + { + /* Clear bit 4 of Port 61h */ + Port61hState &= ~(1 << 4); + } +#else + Port61hState = (Port61hState & 0xEF) | (State << 4); +#endif +} + +static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State) +{ +#if 0 + if (State) + { + /* Set bit 5 of Port 61h */ + Port61hState |= 1 << 5; + } + else + { + /* Clear bit 5 of Port 61h */ + Port61hState &= ~(1 << 5); + } +#else + Port61hState = (Port61hState & 0xDF) | (State << 5); +#endif +} + /* PUBLIC FUNCTIONS ***********************************************************/ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) @@ -160,6 +234,14 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) CmosInitialize(); SpeakerInitialize(); + /* Set output functions */ + PitSetOutFunction(0, NULL, PitChan0Out); + PitSetOutFunction(1, NULL, PitChan1Out); + PitSetOutFunction(2, NULL, PitChan2Out); + + /* Register the I/O Ports */ + RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite); + /* Initialize the PS2 port */ PS2Initialize(ConsoleInput); diff --git a/subsystems/ntvdm/emulator.h b/subsystems/ntvdm/emulator.h index 24749688fee..49aff1e43ed 100644 --- a/subsystems/ntvdm/emulator.h +++ b/subsystems/ntvdm/emulator.h @@ -61,6 +61,10 @@ #define BCD_TO_BINARY(x) (((x) >> 12) * 1000 + ((x) >> 8) * 100 + ((x) >> 4) * 10 + ((x) & 0x0F)) +/* System I/O ports */ +#define CONTROL_SYSTEM_PORT61H 0x61 + + enum { EMULATOR_EXCEPTION_DIVISION_BY_ZERO, diff --git a/subsystems/ntvdm/hardware/speaker.c b/subsystems/ntvdm/hardware/speaker.c index 4bd9af8e40e..26af44da373 100644 --- a/subsystems/ntvdm/hardware/speaker.c +++ b/subsystems/ntvdm/hardware/speaker.c @@ -25,37 +25,19 @@ /* PRIVATE VARIABLES **********************************************************/ -static BYTE Port61hState = 0x00; -HANDLE hBeep = NULL; +static HANDLE hBeep = NULL; /* PRIVATE FUNCTIONS **********************************************************/ -static BYTE SpeakerReadStatus(VOID) +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID SpeakerPool(VOID) { - return Port61hState; -} + BYTE Port61hState = IOReadB(CONTROL_SYSTEM_PORT61H); + BOOLEAN IsConnectedToPITChannel2 = !!(Port61hState & 0x01); + BOOLEAN SpeakerDataOn = !!(Port61hState & 0x02); -static VOID SpeakerWriteCommand(BYTE Value) -{ - BOOLEAN IsConnectedToPITChannel2; - UCHAR SpeakerData; - - Port61hState = Value; - IsConnectedToPITChannel2 = ((Port61hState & 0x01) != 0); - SpeakerData = (Port61hState & 0x02); - - if (PitChannel2 && IsConnectedToPITChannel2) - { - /* Set bit 5 of Port 61h */ - Port61hState |= 1 << 5; - } - else - { - /* Clear bit 5 of Port 61h */ - Port61hState &= ~(1 << 5); - } - - if (PitChannel2 && IsConnectedToPITChannel2 && (SpeakerData != 0)) + if (PitChannel2 && IsConnectedToPITChannel2 && SpeakerDataOn) { /* Start beeping - Adapted from kernel32:Beep() */ NTSTATUS Status; @@ -121,18 +103,6 @@ static VOID SpeakerWriteCommand(BYTE Value) } } -static BYTE WINAPI SpeakerReadPort(ULONG Port) -{ - return SpeakerReadStatus(); -} - -static VOID WINAPI SpeakerWritePort(ULONG Port, BYTE Data) -{ - SpeakerWriteCommand(Data); -} - -/* PUBLIC FUNCTIONS ***********************************************************/ - VOID SpeakerInitialize(VOID) { NTSTATUS Status; @@ -165,9 +135,6 @@ VOID SpeakerInitialize(VOID) { DPRINT1("Failed to open Beep driver, Status 0x%08lx\n", Status); } - - /* Register the I/O Ports */ - RegisterIoPort(SPEAKER_CONTROL_PORT, SpeakerReadPort, SpeakerWritePort); } VOID SpeakerCleanup(VOID) diff --git a/subsystems/ntvdm/hardware/speaker.h b/subsystems/ntvdm/hardware/speaker.h index 166c63e0528..207b501ee03 100644 --- a/subsystems/ntvdm/hardware/speaker.h +++ b/subsystems/ntvdm/hardware/speaker.h @@ -15,10 +15,10 @@ /* DEFINES ********************************************************************/ -#define SPEAKER_CONTROL_PORT 0x61 - /* FUNCTIONS ******************************************************************/ +VOID SpeakerPool(VOID); + VOID SpeakerInitialize(VOID); VOID SpeakerCleanup(VOID); diff --git a/subsystems/ntvdm/hardware/timer.c b/subsystems/ntvdm/hardware/timer.c index 153e744affa..87f68634479 100644 --- a/subsystems/ntvdm/hardware/timer.c +++ b/subsystems/ntvdm/hardware/timer.c @@ -37,13 +37,14 @@ static VOID PitLatchChannelStatus(BYTE Channel) if (PitChannels[Channel].LatchStatusSet == FALSE) { BYTE StatusLatch = 0; - /* HACK!! */BYTE NullCount = 0;/* HACK!! */ + /** HACK!! **/BYTE NullCount = 0;/** HACK!! **/ - StatusLatch = PitChannels[Channel].Out << 7 | NullCount << 6; + StatusLatch = PitChannels[Channel].Out << 7 | NullCount << 6; StatusLatch |= (PitChannels[Channel].ReadWriteMode & 0x03) << 4; StatusLatch |= (PitChannels[Channel].Mode & 0x07) << 1; StatusLatch |= (PitChannels[Channel].Bcd & 0x01); + /* Latch the counter's status */ PitChannels[Channel].LatchStatusSet = TRUE; PitChannels[Channel].StatusLatch = StatusLatch; } @@ -61,23 +62,24 @@ static VOID PitLatchChannelCount(BYTE Channel) */ if (PitChannels[Channel].ReadStatus == 0x00) { + /* Latch the counter's value */ 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); + PitChannels[Channel].OutputLatch = + READ_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CurrentValue); } } static VOID PitSetOut(PPIT_CHANNEL Channel, BOOLEAN State) { - if (State == Channel->Out) return; + /** HACK!! **\ if (State == Channel->Out) return; \** HACK!! **/ /* Set the new state of the OUT pin */ Channel->Out = State; - // /* Call the callback */ - // if (Channel->OutFunction) Channel->OutFunction(Channel->OutParam, State); + /* Call the callback */ + if (Channel->OutFunction) Channel->OutFunction(Channel->OutParam, State); } static VOID PitInitCounter(PPIT_CHANNEL Channel) @@ -138,20 +140,16 @@ static VOID PitWriteCommand(BYTE Value) /* ... otherwise, set the modes and reset flip-flops */ PitChannels[Channel].ReadWriteMode = ReadWriteMode; + PitChannels[Channel].ReadStatus = 0x00; + PitChannels[Channel].WriteStatus = 0x00; 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].Out = FALSE; // <-- unneeded, see the PitInitCounter call below. + /** HACK!! **/PitChannels[Channel].FlipFlop = FALSE;/** HACK!! **/ /* Fix the current value if we switch to BCD counting */ PitChannels[Channel].Bcd = IsBcd; @@ -192,9 +190,8 @@ static BYTE PitReadData(BYTE Channel) LPWORD CurrentValue = NULL; /* - * 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 the status was latched, the first read operation will return the + * latched status, whichever value (count or status) was latched first. */ if (PitChannels[Channel].LatchStatusSet) { @@ -239,6 +236,8 @@ static VOID PitWriteData(BYTE Channel, BYTE Value) PitChannels[Channel].WriteStatus = PitChannels[Channel].ReadWriteMode; } + ASSERT(PitChannels[Channel].WriteStatus != 0); + ReadWriteMode = &PitChannels[Channel].WriteStatus; if (*ReadWriteMode & 1) @@ -261,6 +260,7 @@ static VOID PitWriteData(BYTE Channel, BYTE Value) { if (PitChannels[Channel].CountRegister == 0x0000) { + /* Wrap around to the highest count */ if (PitChannels[Channel].Bcd) PitChannels[Channel].CountRegister = 9999; else @@ -268,8 +268,8 @@ static VOID PitWriteData(BYTE Channel, BYTE Value) } /* Convert the current value from BCD if needed */ - PitChannels[Channel].CountRegister = WRITE_PIT_VALUE(PitChannels[Channel], - PitChannels[Channel].CountRegister); + PitChannels[Channel].CountRegister = + WRITE_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CountRegister); PitChannels[Channel].ReloadValue = PitChannels[Channel].CountRegister; } } @@ -311,6 +311,8 @@ static VOID WINAPI PitWritePort(ULONG Port, BYTE Data) static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) { + if (Count == 0) return; + switch (Channel->Mode) { case PIT_MODE_INT_ON_TERMINAL_COUNT: @@ -324,11 +326,10 @@ static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) else Channel->CurrentValue -= Count; /* Did it fall to the terminal count? */ - if (Channel->CurrentValue == 0 && !Channel->Pulsed) + if (Channel->CurrentValue == 0 && !Channel->Out) { /* Yes, raise the output line */ - if (Channel == &PitChannels[0]) PicInterruptRequest(0); - Channel->Pulsed = TRUE; + PitSetOut(Channel, TRUE); } break; } @@ -342,7 +343,7 @@ static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) if ((Count > Channel->CurrentValue) && (Channel->CurrentValue != 0)) { - /* Decrease the count */ + /* Decrement the count */ Count -= Channel->CurrentValue; /* Reload the value */ @@ -353,7 +354,7 @@ static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) } else { - /* Decrease the value */ + /* Decrement the value */ Channel->CurrentValue -= Count; /* Clear the count */ @@ -368,8 +369,8 @@ static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) } } - /* If there was a reload on channel 0, raise IRQ 0 */ - if ((Channel == &PitChannels[0]) && Reloaded) PicInterruptRequest(0); + /* If there was a reload, raise the output line */ + if (Reloaded) PitSetOut(Channel, TRUE); break; } @@ -387,7 +388,7 @@ static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) if (((Count * 2) > Channel->CurrentValue) && (Channel->CurrentValue != 0)) { - /* Decrease the count */ + /* Decrement the count */ Count -= Channel->CurrentValue / 2; /* Reload the value */ @@ -398,7 +399,7 @@ static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) } else { - /* Decrease the value */ + /* Decrement the value */ Channel->CurrentValue -= Count * 2; /* Clear the count */ @@ -421,16 +422,15 @@ static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) /* Toggle the flip-flop if the number of reloads was odd */ if (ReloadCount & 1) { - Channel->Out = !Channel->Out; + Channel->FlipFlop = !Channel->FlipFlop; + // PitSetOut(Channel, !Channel->Out); } - /* Was there any rising edge on channel 0 ? */ - if (((Channel->Out && (ReloadCount == 1)) - || (ReloadCount > 1)) - && (Channel == &PitChannels[0])) + /* Was there any rising edge? */ + if ((Channel->FlipFlop && (ReloadCount == 1)) || (ReloadCount > 1)) { - /* Yes, IRQ 0 */ - PicInterruptRequest(0); + /* Yes, raise the output line */ + PitSetOut(Channel, TRUE); } break; @@ -453,6 +453,23 @@ static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) /* PUBLIC FUNCTIONS ***********************************************************/ +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; +} + VOID PitClock(DWORD Count) { UINT i; @@ -461,7 +478,7 @@ VOID PitClock(DWORD Count) for (i = 0; i < PIT_CHANNELS; i++) { - // if (!PitChannels[i].Couting) continue; + // if (!PitChannels[i].Counting) continue; PitDecrementCount(&PitChannels[i], Count); } } @@ -487,6 +504,14 @@ DWORD PitGetResolution(VOID) VOID PitInitialize(VOID) { + /* 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); + /* Register the I/O Ports */ RegisterIoPort(PIT_COMMAND_PORT, NULL , PitWritePort); RegisterIoPort(PIT_DATA_PORT(0), PitReadPort, PitWritePort); diff --git a/subsystems/ntvdm/hardware/timer.h b/subsystems/ntvdm/hardware/timer.h index fcd0fcf2e19..21f7f8ee379 100644 --- a/subsystems/ntvdm/hardware/timer.h +++ b/subsystems/ntvdm/hardware/timer.h @@ -38,23 +38,25 @@ typedef enum _PIT_MODE PIT_MODE_HARDWARE_STROBE } PIT_MODE, *PPIT_MODE; +typedef VOID (WINAPI *PIT_OUT_FUNCTION)(LPVOID Param, BOOLEAN State); + typedef struct _PIT_CHANNEL { - BOOLEAN Pulsed; - - - /* PIT Status members */ + /* PIT Status fields */ 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 */ + /* For interleaved reading and writing in 2-byte RW mode */ + BYTE ReadStatus; // Same convention as ReadWriteMode + BYTE WriteStatus; // Same convention as ReadWriteMode + + /* For 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 + /* Counting */ + BOOLEAN Gate; /**/WORD CountRegister;/**/ // Our ReloadValue ??? WORD OutputLatch; @@ -65,6 +67,9 @@ typedef struct _PIT_CHANNEL /* PIT Output */ BOOLEAN Out; // 0: Low ; 1: High + /** HACK!! **/BOOLEAN FlipFlop;/** HACK!! **/ + LPVOID OutParam; + PIT_OUT_FUNCTION OutFunction; } PIT_CHANNEL, *PPIT_CHANNEL; @@ -72,6 +77,9 @@ extern PPIT_CHANNEL PitChannel2; // Needed for PC Speaker /* FUNCTIONS ******************************************************************/ +VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction); +VOID PitSetGate(BYTE Channel, BOOLEAN State); + VOID PitClock(DWORD Count); DWORD PitGetResolution(VOID);