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/bios…
==============================================================================
--- 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/dos32…
==============================================================================
--- 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.…
==============================================================================
--- 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/…
==============================================================================
--- 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/…
==============================================================================
--- 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/…
==============================================================================
--- 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/…
==============================================================================
--- 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);