diff --git a/subsystems/ntvdm/CMakeLists.txt b/subsystems/ntvdm/CMakeLists.txt index eff132bc2ec..c812b03c5ac 100644 --- a/subsystems/ntvdm/CMakeLists.txt +++ b/subsystems/ntvdm/CMakeLists.txt @@ -12,6 +12,7 @@ list(APPEND SOURCE registers.c timer.c ps2.c + speaker.c vga.c ntvdm.c ntvdm.rc diff --git a/subsystems/ntvdm/emulator.c b/subsystems/ntvdm/emulator.c index df30150a8bd..cd03403144f 100644 --- a/subsystems/ntvdm/emulator.c +++ b/subsystems/ntvdm/emulator.c @@ -14,6 +14,7 @@ #include "bios.h" #include "bop.h" #include "dos.h" +#include "speaker.h" #include "vga.h" #include "pic.h" #include "ps2.h" @@ -125,6 +126,12 @@ static VOID WINAPI EmulatorReadIo(PFAST486_STATE State, ULONG Port, PVOID Buffer break; } + case SPEAKER_CONTROL_PORT: + { + *(Address++) = SpeakerReadStatus(); + break; + } + case VGA_AC_WRITE: case VGA_AC_READ: case VGA_SEQ_INDEX: @@ -204,6 +211,12 @@ static VOID WINAPI EmulatorWriteIo(PFAST486_STATE State, ULONG Port, PVOID Buffe break; } + case SPEAKER_CONTROL_PORT: + { + SpeakerWriteCommand(*(Address++)); + break; + } + case VGA_AC_WRITE: case VGA_AC_READ: case VGA_SEQ_INDEX: diff --git a/subsystems/ntvdm/ntvdm.c b/subsystems/ntvdm/ntvdm.c index f33001797e5..2fec5624057 100644 --- a/subsystems/ntvdm/ntvdm.c +++ b/subsystems/ntvdm/ntvdm.c @@ -13,6 +13,7 @@ #include "ntvdm.h" #include "emulator.h" #include "bios.h" +#include "speaker.h" #include "vga.h" #include "dos.h" #include "timer.h" @@ -117,6 +118,9 @@ INT wmain(INT argc, WCHAR *argv[]) goto Cleanup; } + /* Initialize the PC Speaker */ + SpeakerInitialize(); + /* Initialize the VDM DOS kernel */ if (!DosInitialize()) { @@ -133,7 +137,7 @@ INT wmain(INT argc, WCHAR *argv[]) /* Start the input thread */ InputThread = CreateThread(NULL, 0, &InputThreadProc, NULL, 0, NULL); - + /* Set the last timer tick to the current time */ QueryPerformanceCounter(&LastTimerTick); @@ -187,6 +191,7 @@ INT wmain(INT argc, WCHAR *argv[]) Cleanup: if (InputThread != NULL) CloseHandle(InputThread); + SpeakerCleanup(); BiosCleanup(); EmulatorCleanup(); diff --git a/subsystems/ntvdm/ntvdm.h b/subsystems/ntvdm/ntvdm.h index c5c8e233e5b..0ae0747d8b0 100644 --- a/subsystems/ntvdm/ntvdm.h +++ b/subsystems/ntvdm/ntvdm.h @@ -15,6 +15,7 @@ #include #include +#define WIN32_NO_STATUS #include #include diff --git a/subsystems/ntvdm/speaker.c b/subsystems/ntvdm/speaker.c new file mode 100644 index 00000000000..2499a7cdd26 --- /dev/null +++ b/subsystems/ntvdm/speaker.c @@ -0,0 +1,165 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: speaker.c + * PURPOSE: PC Speaker emulation + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "speaker.h" +#include "emulator.h" +#include "timer.h" + +/* Extra PSDK/NDK Headers */ +#include +#include +#include + +/* DDK Driver Headers */ +#include + +/* PRIVATE VARIABLES **********************************************************/ + +static BYTE Port61hState = 0x00; +HANDLE hBeep = NULL; + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID SpeakerInitialize(VOID) +{ + NTSTATUS Status; + UNICODE_STRING BeepDevice; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + + /* Adapted from kernel32:Beep() */ + + // + // On TS systems, we need to Load Winsta.dll and call WinstationBeepOpen + // after doing a GetProcAddress for it + // + + /* Open the device */ + RtlInitUnicodeString(&BeepDevice, L"\\Device\\Beep"); + InitializeObjectAttributes(&ObjectAttributes, &BeepDevice, 0, NULL, NULL); + Status = NtCreateFile(&hBeep, + FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + NULL, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN_IF, + 0, + NULL, + 0); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open Beep driver, Status 0x%08lx\n", Status); + } +} + +VOID SpeakerCleanup(VOID) +{ + NtClose(hBeep); +} + +BYTE SpeakerReadStatus(VOID) +{ + // DPRINT1("SpeakerReadStatus() == 0x%x\n", Port61hState); + return Port61hState; +} + +VOID SpeakerWriteCommand(BYTE Value) +{ + BOOLEAN IsConnectedToPITChannel2; + UCHAR SpeakerData; + + // DPRINT1("SpeakerWriteCommand(0x%x)\n", Value); + + 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)) + { + /* Start beeping - Adapted from kernel32:Beep() */ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + BEEP_SET_PARAMETERS BeepSetParameters; + + DWORD PitChannel2ReloadValue = PitChannel2->ReloadValue; + if (PitChannel2ReloadValue == 0) PitChannel2ReloadValue = 65536; + + /* Set beep data */ + BeepSetParameters.Frequency = (PIT_BASE_FREQUENCY / PitChannel2ReloadValue) * + (PitChannel2->Mode == PIT_MODE_SQUARE_WAVE ? 2 : 1); + BeepSetParameters.Duration = INFINITE; + + /* Send the beep */ + Status = NtDeviceIoControlFile(hBeep, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_BEEP_SET, + &BeepSetParameters, + sizeof(BeepSetParameters), + NULL, + 0); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Beep (%lu, %lu) failed, Status 0x%08lx\n", + BeepSetParameters.Frequency, + BeepSetParameters.Duration, + Status); + } + } + else + { + /* Stop beeping */ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + BEEP_SET_PARAMETERS BeepSetParameters; + + /* Set beep data */ + BeepSetParameters.Frequency = 0x00; + BeepSetParameters.Duration = 0x00; + + /* Send the beep */ + Status = NtDeviceIoControlFile(hBeep, + NULL, + NULL, + NULL, + &IoStatusBlock, + IOCTL_BEEP_SET, + &BeepSetParameters, + sizeof(BeepSetParameters), + NULL, + 0); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Beep (%lu, %lu) failed, Status 0x%08lx\n", + BeepSetParameters.Frequency, + BeepSetParameters.Duration, + Status); + } + } +} + +/* EOF */ diff --git a/subsystems/ntvdm/speaker.h b/subsystems/ntvdm/speaker.h new file mode 100644 index 00000000000..9f2dc107e75 --- /dev/null +++ b/subsystems/ntvdm/speaker.h @@ -0,0 +1,29 @@ +/* + * COPYRIGHT: GPL - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: speaker.h + * PURPOSE: PC Speaker emulation + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _SPEAKER_H_ +#define _SPEAKER_H_ + +/* INCLUDES *******************************************************************/ + +#include "ntvdm.h" + +/* DEFINES ********************************************************************/ + +#define SPEAKER_CONTROL_PORT 0x61 + +/* FUNCTIONS ******************************************************************/ + +VOID SpeakerInitialize(VOID); +VOID SpeakerCleanup(VOID); +BYTE SpeakerReadStatus(VOID); +VOID SpeakerWriteCommand(BYTE Value); + +#endif // _SPEAKER_H_ + +/* EOF */ diff --git a/subsystems/ntvdm/timer.c b/subsystems/ntvdm/timer.c index 0d7327cd98c..29d20d58b9f 100644 --- a/subsystems/ntvdm/timer.c +++ b/subsystems/ntvdm/timer.c @@ -16,6 +16,7 @@ /* PRIVATE VARIABLES **********************************************************/ static PIT_CHANNEL PitChannels[PIT_CHANNELS]; +PPIT_CHANNEL PitChannel2 = &PitChannels[2]; /* PUBLIC FUNCTIONS ***********************************************************/ diff --git a/subsystems/ntvdm/timer.h b/subsystems/ntvdm/timer.h index 637f4441fae..3fbd4a7a762 100644 --- a/subsystems/ntvdm/timer.h +++ b/subsystems/ntvdm/timer.h @@ -43,6 +43,8 @@ typedef struct _PIT_CHANNEL BYTE AccessMode; } PIT_CHANNEL, *PPIT_CHANNEL; +extern PPIT_CHANNEL PitChannel2; // Needed for PC Speaker + /* FUNCTIONS ******************************************************************/ VOID PitWriteCommand(BYTE Value);