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/bios32/bios32.c?rev=65333&r1=65332&r2=65333&view=diff
==============================================================================
--- 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/dos32krnl/dos.h?rev=65333&r1=65332&r2=65333&view=diff
==============================================================================
--- 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?rev=65333&r1=65332&r2=65333&view=diff
==============================================================================
--- 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/speaker.c?rev=65333&r1=65332&r2=65333&view=diff
==============================================================================
--- 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/speaker.h?rev=65333&r1=65332&r2=65333&view=diff
==============================================================================
--- 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/timer.c?rev=65333&r1=65332&r2=65333&view=diff
==============================================================================
--- 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/timer.h?rev=65333&r1=65332&r2=65333&view=diff
==============================================================================
--- 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);
 


Reply via email to