Author: hbelusca Date: Sat Nov 8 21:45:20 2014 New Revision: 65333
URL: http://svn.reactos.org/svn/reactos?rev=65333&view=rev Log: [NTVDM] - For the BIOS wait hack, use the NT API instead of the Win32 one. - Remove unneeded DOS-exported functions. - Use a PitGetReloadValue function for retrieving the reload value of a given PIT channel, instead of directly reading the reload value member, for functions *outside* of the PIT module. - Implement basic Pulse-Width Modulation code for the PC speaker emulation.
Modified: trunk/reactos/subsystems/ntvdm/bios/bios32/bios32.c trunk/reactos/subsystems/ntvdm/dos/dos32krnl/dos.h trunk/reactos/subsystems/ntvdm/emulator.c trunk/reactos/subsystems/ntvdm/hardware/speaker.c trunk/reactos/subsystems/ntvdm/hardware/speaker.h trunk/reactos/subsystems/ntvdm/hardware/timer.c trunk/reactos/subsystems/ntvdm/hardware/timer.h
Modified: trunk/reactos/subsystems/ntvdm/bios/bios32/bios32.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/ntvdm/bios/bios3... ============================================================================== --- trunk/reactos/subsystems/ntvdm/bios/bios32/bios32.c [iso-8859-1] (original) +++ trunk/reactos/subsystems/ntvdm/bios/bios32/bios32.c [iso-8859-1] Sat Nov 8 21:45:20 2014 @@ -30,6 +30,9 @@ #include "hardware/cmos.h" #include "hardware/pic.h" #include "hardware/timer.h" + +/* Extra PSDK/NDK Headers */ +#include <ndk/kefuncs.h>
/* PRIVATE VARIABLES **********************************************************/
@@ -166,9 +169,11 @@ * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm * for more information. */ - - // HACK: For now, use the Win32 API (that takes time in milliseconds). - Sleep(MAKELONG(getDX(), getCX()) / 1000); + LARGE_INTEGER TimeOut; + TimeOut.QuadPart = MAKELONG(getDX(), getCX()) * -10LL; + + // HACK: For now, use the NT API (time in hundreds of nanoseconds). + NtDelayExecution(FALSE, &TimeOut);
/* Clear CF */ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
Modified: trunk/reactos/subsystems/ntvdm/dos/dos32krnl/dos.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/ntvdm/dos/dos32k... ============================================================================== --- trunk/reactos/subsystems/ntvdm/dos/dos32krnl/dos.h [iso-8859-1] (original) +++ trunk/reactos/subsystems/ntvdm/dos/dos32krnl/dos.h [iso-8859-1] Sat Nov 8 21:45:20 2014 @@ -239,11 +239,6 @@ VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode); BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle);
-VOID WINAPI DosInt20h(LPWORD Stack); -VOID WINAPI DosInt21h(LPWORD Stack); -VOID WINAPI DosBreakInterrupt(LPWORD Stack); -VOID WINAPI DosInt2Fh(LPWORD Stack); - BOOLEAN DosKRNLInitialize(VOID);
#endif // _DOS_H_
Modified: trunk/reactos/subsystems/ntvdm/emulator.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/ntvdm/emulator.c... ============================================================================== --- trunk/reactos/subsystems/ntvdm/emulator.c [iso-8859-1] (original) +++ trunk/reactos/subsystems/ntvdm/emulator.c [iso-8859-1] Sat Nov 8 21:45:20 2014 @@ -293,8 +293,8 @@ DPRINT("Speaker %s\n", Port61hState & 0x02 ? "on" : "off"); // SpeakerStateChange = TRUE; } - // if (SpeakerStateChange) SpeakerChange(); - SpeakerChange(); + // if (SpeakerStateChange) SpeakerChange(Port61hState); + SpeakerChange(Port61hState); }
static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State) @@ -347,7 +347,7 @@ if ((OldPort61hState ^ Port61hState) & 0x20) { DPRINT("PitChan2Out -- Port61hState changed\n"); - SpeakerChange(); + SpeakerChange(Port61hState); } }
Modified: trunk/reactos/subsystems/ntvdm/hardware/speaker.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/ntvdm/hardware/s... ============================================================================== --- trunk/reactos/subsystems/ntvdm/hardware/speaker.c [iso-8859-1] (original) +++ trunk/reactos/subsystems/ntvdm/hardware/speaker.c [iso-8859-1] Sat Nov 8 21:45:20 2014 @@ -12,7 +12,6 @@
#include "emulator.h" #include "speaker.h" -#include "io.h" #include "timer.h"
/* Extra PSDK/NDK Headers */ @@ -20,6 +19,9 @@ #include <ndk/obfuncs.h> #include <ndk/rtlfuncs.h>
+/* Extra PSDK/NDK Headers */ +#include <ndk/kefuncs.h> + /* DDK Driver Headers */ #include <ntddbeep.h>
@@ -27,26 +29,56 @@
static HANDLE hBeep = NULL;
+static LARGE_INTEGER FreqCount, CountStart; +static ULONG PulseTickCount = 0, FreqPulses = 0; + +#define SPEAKER_RESPONSE 200 // in milliseconds + +#define MIN_AUDIBLE_FREQ 20 // BEEP_FREQUENCY_MINIMUM +#define MAX_AUDIBLE_FREQ 20000 // BEEP_FREQUENCY_MAXIMUM +#define CLICK_FREQ 100 + + /* PRIVATE FUNCTIONS **********************************************************/
-static DWORD OldReloadValue = 0; -static PIT_MODE OldMode = 0; - -/* PUBLIC FUNCTIONS ***********************************************************/ - -VOID PlaySound(DWORD Frequency, - DWORD Duration) -{ - /* Adapted from kernel32:Beep() */ +static +VOID +MakeBeep(ULONG Frequency, + ULONG Duration) +{ + static ULONG LastFrequency = 0, LastDuration = 0;
IO_STATUS_BLOCK IoStatusBlock; BEEP_SET_PARAMETERS BeepSetParameters;
- /* Set beep data */ + /* + * Do nothing if we are replaying exactly the same sound + * (this avoids hiccups due to redoing the same beeps). + */ + if (Frequency == LastFrequency && Duration == LastDuration) return; + + /* A null frequency means we stop beeping */ + if (Frequency == 0) Duration = 0; + + /* + * For small durations we automatically reset the beep so + * that we can replay short beeps like clicks immediately. + */ + if (Duration < 10) + { + LastFrequency = 0; + LastDuration = 0; + } + else + { + LastFrequency = Frequency; + LastDuration = Duration; + } + + /* Set the data and do the beep */ BeepSetParameters.Frequency = Frequency; BeepSetParameters.Duration = Duration;
- /* Send the beep */ NtDeviceIoControlFile(hBeep, NULL, NULL, @@ -59,44 +91,173 @@ 0); }
-VOID SpeakerChange(VOID) -{ - BYTE Port61hState = IOReadB(CONTROL_SYSTEM_PORT61H); - BOOLEAN IsConnectedToPITChannel2 = !!(Port61hState & 0x01); - BOOLEAN SpeakerDataOn = !!(Port61hState & 0x02); - - if (PitChannel2 && IsConnectedToPITChannel2 && SpeakerDataOn) - { - /* Start beeping */ - - DWORD Frequency, Duration; - - DWORD PitChannel2ReloadValue = PitChannel2->ReloadValue; - if (PitChannel2ReloadValue == 0) PitChannel2ReloadValue = 65536; - - DPRINT("(1) PitChannel2(Mode = %d ; ReloadValue = %d)\n", PitChannel2->Mode, PitChannel2ReloadValue); - - if (OldMode == PitChannel2->Mode && OldReloadValue == PitChannel2ReloadValue) - return; - - OldMode = PitChannel2->Mode; - OldReloadValue = PitChannel2ReloadValue; - - DPRINT("(2) PitChannel2(Mode = %d ; ReloadValue = %d)\n", PitChannel2->Mode, PitChannel2ReloadValue); - - Frequency = (PIT_BASE_FREQUENCY / PitChannel2ReloadValue); - Duration = INFINITE; - - PlaySound(Frequency, Duration); +static +VOID PulseSample(VOID) +{ + static ULONG Pulses = 0, CountStartTick = 0, LastPulsesFreq = 0; + ULONG LastPulseTickCount, CurrPulsesFreq; + LARGE_INTEGER Counter; + LONGLONG Elapsed; + + /* + * Check how far away was the previous pulse and + * if it was >= 200ms away then restart counting. + */ + LastPulseTickCount = PulseTickCount; + PulseTickCount = GetTickCount(); + if (PulseTickCount - LastPulseTickCount >= SPEAKER_RESPONSE) + { + CountStart.QuadPart = 0; + Pulses = 0; + FreqPulses = 0; + return; + } + + /* We have closely spaced pulses. Start counting. */ + if (CountStart.QuadPart == 0) + { + NtQueryPerformanceCounter(&CountStart, NULL); + CountStartTick = PulseTickCount; + Pulses = 0; + FreqPulses = 0; + return; + } + + /* A pulse is ongoing */ + ++Pulses; + + /* We require some pulses to have some statistics */ + if (PulseTickCount - CountStartTick <= (SPEAKER_RESPONSE >> 1)) return; + + /* Get count time */ + NtQueryPerformanceCounter(&Counter, NULL); + + /* + * Get the number of speaker hundreds of microseconds that have passed + * since we started counting. + */ + Elapsed = (Counter.QuadPart - CountStart.QuadPart) * 10000 / FreqCount.QuadPart; + if (Elapsed == 0) ++Elapsed; + + /* Update counting for next pulses */ + CountStart = Counter; + CountStartTick = PulseTickCount; + + // HACKHACK!! I need to check why we need to double the number + // of pulses in order to have the correct frequency... + Pulses <<= 1; + + /* Get the current pulses frequency */ + CurrPulsesFreq = 10000 * Pulses / Elapsed; + + /* Round the current pulses frequency up and align */ + if ((CurrPulsesFreq & 0x0F) > 7) CurrPulsesFreq += 0x10; + CurrPulsesFreq &= ~0x0F; + + /* Reinitialize frequency counters if necessary */ + if (LastPulsesFreq == 0) LastPulsesFreq = CurrPulsesFreq; + if (FreqPulses == 0) FreqPulses = LastPulsesFreq; + + /* Fix up the current pulses frequency if needed */ + if (LastPulsesFreq != 0 && CurrPulsesFreq == 0) + CurrPulsesFreq = LastPulsesFreq; + + /* + * Magic begins there... + */ +#ifndef ABS +#define ABS(x) ((x) < 0 ? -(x) : (x)) +#endif + if (ABS(CurrPulsesFreq - LastPulsesFreq) > 7) + { + /* + * This can be a "large" fluctuation so ignore it for now, but take + * it into account if it happens to be a real frequency change. + */ + CurrPulsesFreq = (CurrPulsesFreq + LastPulsesFreq) >> 1; } else { - /* Stop beeping */ - - OldMode = 0; - OldReloadValue = 0; - - PlaySound(0x00, 0x00); + // FreqPulses = ((FreqPulses << 2) + LastPulsesFreq + CurrPulsesFreq) / 6; + FreqPulses = ((FreqPulses << 1) + LastPulsesFreq + CurrPulsesFreq) >> 2; + } + + /* Round the pulses frequency up and align */ + if ((FreqPulses & 0x0F) > 7) FreqPulses += 0x10; + FreqPulses &= ~0x0F; + + DPRINT("FreqPulses = %d, LastPulsesFreq = %d, CurrPulsesFreq = %d, Pulses = %d, Elapsed = %d\n", + FreqPulses, LastPulsesFreq, CurrPulsesFreq, Pulses, Elapsed); + + LastPulsesFreq = CurrPulsesFreq; + Pulses = 0; +} + + +/* PUBLIC FUNCTIONS ***********************************************************/ + +// SpeakerPulse +VOID SpeakerChange(UCHAR Port61hValue) +{ + static BOOLEAN OldSpeakerOff = TRUE; + + BOOLEAN Timer2Gate = !!(Port61hValue & 0x01); + BOOLEAN SpeakerOn = !!(Port61hValue & 0x02); + + DPRINT("SpeakerChange -- Timer2Gate == %s ; SpeakerOn == %s\n", + Timer2Gate ? "true" : "false", SpeakerOn ? "true" : "false"); + + if (Timer2Gate) + { + if (SpeakerOn) + { + /* Start beeping */ + ULONG Frequency = (PIT_BASE_FREQUENCY / PitGetReloadValue(2)); + if (Frequency < MIN_AUDIBLE_FREQ || MAX_AUDIBLE_FREQ < Frequency) + Frequency = 0; + + MakeBeep(Frequency, INFINITE); + } + else + { + /* Stop beeping */ + MakeBeep(0, 0); + } + } + else + { + if (SpeakerOn) + { + if (OldSpeakerOff) + { + OldSpeakerOff = FALSE; + PulseSample(); + } + + if (FreqPulses >= MIN_AUDIBLE_FREQ) + MakeBeep(FreqPulses, INFINITE); + else if (CountStart.QuadPart != 0) + MakeBeep(CLICK_FREQ, 1); /* Click */ + else + MakeBeep(0, 0); /* Stop beeping */ + } + else + { + OldSpeakerOff = TRUE; + + /* + * Check how far away was the previous pulse and if + * it was >= (200 + eps) ms away then stop beeping. + */ + if (GetTickCount() - PulseTickCount >= SPEAKER_RESPONSE + (SPEAKER_RESPONSE >> 3)) + { + CountStart.QuadPart = 0; + FreqPulses = 0; + + /* Stop beeping */ + MakeBeep(0, 0); + } + } } }
@@ -107,9 +268,14 @@ OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock;
- /* Adapted from kernel32:Beep() */ - - /* Open the device */ + /* Retrieve the performance frequency and initialize the timer ticks */ + NtQueryPerformanceCounter(&CountStart, &FreqCount); + if (FreqCount.QuadPart == 0) + { + wprintf(L"FATAL: Performance counter not available\n"); + } + + /* Open the BEEP device */ RtlInitUnicodeString(&BeepDevice, L"\Device\Beep"); InitializeObjectAttributes(&ObjectAttributes, &BeepDevice, 0, NULL, NULL); Status = NtCreateFile(&hBeep, @@ -125,7 +291,8 @@ 0); if (!NT_SUCCESS(Status)) { - DPRINT1("Failed to open Beep driver, Status 0x%08lx\n", Status); + DPRINT1("Failed to open the Beep driver, Status 0x%08lx\n", Status); + // hBeep = INVALID_HANDLE_VALUE; } }
Modified: trunk/reactos/subsystems/ntvdm/hardware/speaker.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/ntvdm/hardware/s... ============================================================================== --- trunk/reactos/subsystems/ntvdm/hardware/speaker.h [iso-8859-1] (original) +++ trunk/reactos/subsystems/ntvdm/hardware/speaker.h [iso-8859-1] Sat Nov 8 21:45:20 2014 @@ -17,7 +17,7 @@
/* FUNCTIONS ******************************************************************/
-VOID SpeakerChange(VOID); +VOID SpeakerChange(UCHAR Port61hValue);
VOID SpeakerInitialize(VOID); VOID SpeakerCleanup(VOID);
Modified: trunk/reactos/subsystems/ntvdm/hardware/timer.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/ntvdm/hardware/t... ============================================================================== --- trunk/reactos/subsystems/ntvdm/hardware/timer.c [iso-8859-1] (original) +++ trunk/reactos/subsystems/ntvdm/hardware/timer.c [iso-8859-1] Sat Nov 8 21:45:20 2014 @@ -20,7 +20,6 @@ /* PRIVATE VARIABLES **********************************************************/
static PIT_CHANNEL PitChannels[PIT_CHANNELS]; -PPIT_CHANNEL PitChannel2 = &PitChannels[2];
/* PRIVATE FUNCTIONS **********************************************************/
@@ -471,9 +470,38 @@ PitChannels[Channel].Gate = State; }
+WORD PitGetReloadValue(BYTE Channel) +{ + if (Channel >= PIT_CHANNELS) return 0xFFFF; + + if (PitChannels[Channel].ReloadValue == 0) + return 0xFFFF; + else + return PitChannels[Channel].ReloadValue; +} + +DWORD PitGetResolution(VOID) +{ + UCHAR 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; +} + VOID PitClock(DWORD Count) { - UINT i; + UCHAR i;
if (Count == 0) return;
@@ -482,25 +510,6 @@ // if (!PitChannels[i].Counting) continue; PitDecrementCount(&PitChannels[i], Count); } -} - -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; }
VOID PitInitialize(VOID)
Modified: trunk/reactos/subsystems/ntvdm/hardware/timer.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/ntvdm/hardware/t... ============================================================================== --- trunk/reactos/subsystems/ntvdm/hardware/timer.h [iso-8859-1] (original) +++ trunk/reactos/subsystems/ntvdm/hardware/timer.h [iso-8859-1] Sat Nov 8 21:45:20 2014 @@ -73,15 +73,14 @@
} PIT_CHANNEL, *PPIT_CHANNEL;
-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); +WORD PitGetReloadValue(BYTE Channel);
+DWORD PitGetResolution(VOID); VOID PitClock(DWORD Count); -DWORD PitGetResolution(VOID);
VOID PitInitialize(VOID);