mirror of
https://github.com/reactos/reactos.git
synced 2025-02-22 08:25:03 +00:00
[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. svn path=/trunk/; revision=65333
This commit is contained in:
parent
7cfea2fbb6
commit
6990cd2172
7 changed files with 236 additions and 61 deletions
|
@ -31,6 +31,9 @@
|
|||
#include "hardware/pic.h"
|
||||
#include "hardware/timer.h"
|
||||
|
||||
/* Extra PSDK/NDK Headers */
|
||||
#include <ndk/kefuncs.h>
|
||||
|
||||
/* PRIVATE VARIABLES **********************************************************/
|
||||
|
||||
CALLBACK16 BiosContext;
|
||||
|
@ -166,9 +169,11 @@ static VOID WINAPI BiosMiscService(LPWORD Stack)
|
|||
* See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
|
||||
* for more information.
|
||||
*/
|
||||
LARGE_INTEGER TimeOut;
|
||||
TimeOut.QuadPart = MAKELONG(getDX(), getCX()) * -10LL;
|
||||
|
||||
// HACK: For now, use the Win32 API (that takes time in milliseconds).
|
||||
Sleep(MAKELONG(getDX(), getCX()) / 1000);
|
||||
// HACK: For now, use the NT API (time in hundreds of nanoseconds).
|
||||
NtDelayExecution(FALSE, &TimeOut);
|
||||
|
||||
/* Clear CF */
|
||||
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
|
||||
|
|
|
@ -239,11 +239,6 @@ DWORD DosStartProcess(IN LPCSTR ExecutablePath,
|
|||
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_
|
||||
|
|
|
@ -293,8 +293,8 @@ static VOID WINAPI Port61hWrite(USHORT Port, BYTE Data)
|
|||
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 @@ static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State)
|
|||
if ((OldPort61hState ^ Port61hState) & 0x20)
|
||||
{
|
||||
DPRINT("PitChan2Out -- Port61hState changed\n");
|
||||
SpeakerChange();
|
||||
SpeakerChange(Port61hState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
static
|
||||
VOID
|
||||
MakeBeep(ULONG Frequency,
|
||||
ULONG Duration)
|
||||
{
|
||||
/* Adapted from kernel32:Beep() */
|
||||
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 @@ VOID PlaySound(DWORD Frequency,
|
|||
0);
|
||||
}
|
||||
|
||||
VOID SpeakerChange(VOID)
|
||||
static
|
||||
VOID PulseSample(VOID)
|
||||
{
|
||||
BYTE Port61hState = IOReadB(CONTROL_SYSTEM_PORT61H);
|
||||
BOOLEAN IsConnectedToPITChannel2 = !!(Port61hState & 0x01);
|
||||
BOOLEAN SpeakerDataOn = !!(Port61hState & 0x02);
|
||||
static ULONG Pulses = 0, CountStartTick = 0, LastPulsesFreq = 0;
|
||||
ULONG LastPulseTickCount, CurrPulsesFreq;
|
||||
LARGE_INTEGER Counter;
|
||||
LONGLONG Elapsed;
|
||||
|
||||
if (PitChannel2 && IsConnectedToPITChannel2 && SpeakerDataOn)
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
/* Start beeping */
|
||||
CountStart.QuadPart = 0;
|
||||
Pulses = 0;
|
||||
FreqPulses = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD Frequency, Duration;
|
||||
/* We have closely spaced pulses. Start counting. */
|
||||
if (CountStart.QuadPart == 0)
|
||||
{
|
||||
NtQueryPerformanceCounter(&CountStart, NULL);
|
||||
CountStartTick = PulseTickCount;
|
||||
Pulses = 0;
|
||||
FreqPulses = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD PitChannel2ReloadValue = PitChannel2->ReloadValue;
|
||||
if (PitChannel2ReloadValue == 0) PitChannel2ReloadValue = 65536;
|
||||
/* A pulse is ongoing */
|
||||
++Pulses;
|
||||
|
||||
DPRINT("(1) PitChannel2(Mode = %d ; ReloadValue = %d)\n", PitChannel2->Mode, PitChannel2ReloadValue);
|
||||
/* We require some pulses to have some statistics */
|
||||
if (PulseTickCount - CountStartTick <= (SPEAKER_RESPONSE >> 1)) return;
|
||||
|
||||
if (OldMode == PitChannel2->Mode && OldReloadValue == PitChannel2ReloadValue)
|
||||
return;
|
||||
/* Get count time */
|
||||
NtQueryPerformanceCounter(&Counter, NULL);
|
||||
|
||||
OldMode = PitChannel2->Mode;
|
||||
OldReloadValue = PitChannel2ReloadValue;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
DPRINT("(2) PitChannel2(Mode = %d ; ReloadValue = %d)\n", PitChannel2->Mode, PitChannel2ReloadValue);
|
||||
/* Update counting for next pulses */
|
||||
CountStart = Counter;
|
||||
CountStartTick = PulseTickCount;
|
||||
|
||||
Frequency = (PIT_BASE_FREQUENCY / PitChannel2ReloadValue);
|
||||
Duration = INFINITE;
|
||||
// HACKHACK!! I need to check why we need to double the number
|
||||
// of pulses in order to have the correct frequency...
|
||||
Pulses <<= 1;
|
||||
|
||||
PlaySound(Frequency, Duration);
|
||||
/* 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 */
|
||||
// FreqPulses = ((FreqPulses << 2) + LastPulsesFreq + CurrPulsesFreq) / 6;
|
||||
FreqPulses = ((FreqPulses << 1) + LastPulsesFreq + CurrPulsesFreq) >> 2;
|
||||
}
|
||||
|
||||
OldMode = 0;
|
||||
OldReloadValue = 0;
|
||||
/* Round the pulses frequency up and align */
|
||||
if ((FreqPulses & 0x0F) > 7) FreqPulses += 0x10;
|
||||
FreqPulses &= ~0x0F;
|
||||
|
||||
PlaySound(0x00, 0x00);
|
||||
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 @@ VOID SpeakerInitialize(VOID)
|
|||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
IO_STATUS_BLOCK IoStatusBlock;
|
||||
|
||||
/* Adapted from kernel32:Beep() */
|
||||
/* 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 device */
|
||||
/* Open the BEEP device */
|
||||
RtlInitUnicodeString(&BeepDevice, L"\\Device\\Beep");
|
||||
InitializeObjectAttributes(&ObjectAttributes, &BeepDevice, 0, NULL, NULL);
|
||||
Status = NtCreateFile(&hBeep,
|
||||
|
@ -125,7 +291,8 @@ VOID SpeakerInitialize(VOID)
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
VOID SpeakerChange(VOID);
|
||||
VOID SpeakerChange(UCHAR Port61hValue);
|
||||
|
||||
VOID SpeakerInitialize(VOID);
|
||||
VOID SpeakerCleanup(VOID);
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
/* PRIVATE VARIABLES **********************************************************/
|
||||
|
||||
static PIT_CHANNEL PitChannels[PIT_CHANNELS];
|
||||
PPIT_CHANNEL PitChannel2 = &PitChannels[2];
|
||||
|
||||
/* PRIVATE FUNCTIONS **********************************************************/
|
||||
|
||||
|
@ -471,22 +470,19 @@ VOID PitSetGate(BYTE Channel, BOOLEAN State)
|
|||
PitChannels[Channel].Gate = State;
|
||||
}
|
||||
|
||||
VOID PitClock(DWORD Count)
|
||||
WORD PitGetReloadValue(BYTE Channel)
|
||||
{
|
||||
UINT i;
|
||||
if (Channel >= PIT_CHANNELS) return 0xFFFF;
|
||||
|
||||
if (Count == 0) return;
|
||||
|
||||
for (i = 0; i < PIT_CHANNELS; i++)
|
||||
{
|
||||
// if (!PitChannels[i].Counting) continue;
|
||||
PitDecrementCount(&PitChannels[i], Count);
|
||||
}
|
||||
if (PitChannels[Channel].ReloadValue == 0)
|
||||
return 0xFFFF;
|
||||
else
|
||||
return PitChannels[Channel].ReloadValue;
|
||||
}
|
||||
|
||||
DWORD PitGetResolution(VOID)
|
||||
{
|
||||
INT i;
|
||||
UCHAR i;
|
||||
DWORD MinReloadValue = 65536;
|
||||
|
||||
for (i = 0; i < PIT_CHANNELS; i++)
|
||||
|
@ -503,6 +499,19 @@ DWORD PitGetResolution(VOID)
|
|||
return PIT_BASE_FREQUENCY / MinReloadValue;
|
||||
}
|
||||
|
||||
VOID PitClock(DWORD Count)
|
||||
{
|
||||
UCHAR i;
|
||||
|
||||
if (Count == 0) return;
|
||||
|
||||
for (i = 0; i < PIT_CHANNELS; i++)
|
||||
{
|
||||
// if (!PitChannels[i].Counting) continue;
|
||||
PitDecrementCount(&PitChannels[i], Count);
|
||||
}
|
||||
}
|
||||
|
||||
VOID PitInitialize(VOID)
|
||||
{
|
||||
/* Set up the timers to their default value */
|
||||
|
|
|
@ -73,15 +73,14 @@ typedef struct _PIT_CHANNEL
|
|||
|
||||
} 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);
|
||||
|
||||
VOID PitClock(DWORD Count);
|
||||
DWORD PitGetResolution(VOID);
|
||||
VOID PitClock(DWORD Count);
|
||||
|
||||
VOID PitInitialize(VOID);
|
||||
|
||||
|
|
Loading…
Reference in a new issue