You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
472 lines
11 KiB
472 lines
11 KiB
/***************************************************************************
|
|
*
|
|
* vsb.c
|
|
*
|
|
* Copyright (c) 1991-1996 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* This code provides VDD support for SB 2.0 sound output, specifically:
|
|
* DSP 2.01+ (excluding SB-MIDI port)
|
|
* Mixer Chip CT1335 (not strictly part of SB 2.0, but apps seem to like it)
|
|
* FM Chip OPL2 (a.k.a. Adlib)
|
|
*
|
|
***************************************************************************/
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* #includes
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <windows.h> // The VDD is a win32 DLL
|
|
#include <mmsystem.h> // Multi-media APIs
|
|
#include <vddsvc.h> // Definition of VDD calls
|
|
#include <vsb.h>
|
|
#include <dsp.h>
|
|
#include <mixer.h>
|
|
#include <fm.h>
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Globals
|
|
*
|
|
*****************************************************************************/
|
|
|
|
//
|
|
// Definitions for MM api entry points. The functions will be linked
|
|
// dynamically to avoid bringing winmm.dll in before wow32.
|
|
//
|
|
SETVOLUMEPROC SetVolumeProc;
|
|
GETNUMDEVSPROC GetNumDevsProc;
|
|
GETDEVCAPSPROC GetDevCapsProc;
|
|
OPENPROC OpenProc;
|
|
RESETPROC ResetProc;
|
|
CLOSEPROC CloseProc;
|
|
GETPOSITIONPROC GetPositionProc;
|
|
WRITEPROC WriteProc;
|
|
PREPAREHEADERPROC PrepareHeaderProc;
|
|
UNPREPAREHEADERPROC UnprepareHeaderProc;
|
|
|
|
BOOL bWaveOutActive = FALSE;
|
|
BOOL bWinmmLoaded = FALSE;
|
|
BOOL LoadWinmm(VOID);
|
|
BOOL InitDevices(VOID);
|
|
HINSTANCE hWinmm;
|
|
|
|
/*
|
|
* General globals
|
|
*/
|
|
|
|
HINSTANCE GlobalHInstance; // handle passed to dll entry point
|
|
WORD BasePort; // Where the card is mapped
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* General Functions
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
/*
|
|
* DLL entry point routine.
|
|
* Returns TRUE on success.
|
|
*/
|
|
|
|
BOOL WINAPI
|
|
DllEntryPoint(
|
|
HINSTANCE hInstance,
|
|
DWORD reason,
|
|
LPVOID reserved
|
|
)
|
|
{
|
|
|
|
switch (reason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
DisableThreadLibraryCalls(hInstance);
|
|
// save instance
|
|
GlobalHInstance = hInstance;
|
|
|
|
// Hook i/o ports
|
|
if (!InstallIoHook(hInstance)) {
|
|
dprintf1(("VDD failed to load"));
|
|
#if 0
|
|
MessageBoxA(NULL, "Unable to load wave out device",
|
|
"Sound Blaster VDD", MB_OK | MB_ICONEXCLAMATION);
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
if (!DspProcessAttach()) {
|
|
DeInstallIoHook(hInstance);
|
|
return FALSE;
|
|
}
|
|
|
|
#if 0
|
|
{
|
|
char buf[256];
|
|
wsprintfA(buf, "Sound blaster VDD loaded at port %3X, IRQ %d, DMA Channel %d, %s OPL2/Adlib support.",
|
|
BasePort, SB_INTERRUPT, SB_DMA_CHANNEL,
|
|
(FMActive ? "with" : "without"));
|
|
MessageBoxA(NULL, buf, "Sound Blaster VDD", MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
#endif // DBG
|
|
return TRUE;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
DspProcessDetach();
|
|
DeInstallIoHook(hInstance);
|
|
|
|
if (bWaveOutActive) {
|
|
CloseFMDevice();
|
|
SetSpeaker(TRUE);
|
|
}
|
|
|
|
if (bWinmmLoaded) {
|
|
FreeLibrary(hWinmm);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************/
|
|
//
|
|
// LoadWinmm()
|
|
//
|
|
// This function dynamically loads the "waveOutxxx" entry points. This
|
|
// is done because there is code in WINMM which does certain things in a
|
|
// WOW vdm. If we do static links, then winmm may get loaded way before
|
|
// WOW32, in which case it can't do the things it should.
|
|
//
|
|
BOOL
|
|
LoadWinmm(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
if (!(hWinmm = LoadLibrary(L"WINMM.DLL"))) {
|
|
return FALSE;
|
|
}
|
|
|
|
//BUGBUG: Should check for error return from getprocaddress
|
|
//
|
|
SetVolumeProc = (SETVOLUMEPROC) GetProcAddress(hWinmm, "waveOutSetVolume");
|
|
GetNumDevsProc = (GETNUMDEVSPROC) GetProcAddress(hWinmm, "waveOutGetNumDevs");
|
|
GetDevCapsProc = (GETDEVCAPSPROC) GetProcAddress(hWinmm, "waveOutGetDevCapsW");
|
|
OpenProc = (OPENPROC) GetProcAddress(hWinmm, "waveOutOpen");
|
|
ResetProc = (RESETPROC) GetProcAddress(hWinmm, "waveOutReset");
|
|
CloseProc = (CLOSEPROC) GetProcAddress(hWinmm, "waveOutClose");
|
|
GetPositionProc = (GETPOSITIONPROC) GetProcAddress(hWinmm, "waveOutGetPosition");
|
|
WriteProc = (WRITEPROC) GetProcAddress(hWinmm, "waveOutWrite");
|
|
PrepareHeaderProc = (PREPAREHEADERPROC) GetProcAddress(hWinmm, "waveOutPrepareHeader");
|
|
UnprepareHeaderProc = (UNPREPAREHEADERPROC) GetProcAddress(hWinmm, "waveOutUnprepareHeader");
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
//
|
|
// InitDevices()
|
|
//
|
|
// This function tries to get handles to the waveout and FM devices.
|
|
//
|
|
BOOL
|
|
InitDevices(
|
|
VOID
|
|
)
|
|
{
|
|
static BOOL bTriedLoadAndFailed = FALSE;
|
|
|
|
if (bWaveOutActive) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (bTriedLoadAndFailed) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bWinmmLoaded) {
|
|
if (!LoadWinmm()) {
|
|
bTriedLoadAndFailed = TRUE;
|
|
return FALSE;
|
|
}
|
|
bWinmmLoaded = TRUE;
|
|
}
|
|
|
|
if (!FindWaveDevice()) {
|
|
return FALSE;
|
|
}
|
|
bWaveOutActive = TRUE;
|
|
OpenFMDevice();
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* Hooks i/o ports with i/o handlers.
|
|
* Sets BasePort and returns TRUE if successful.
|
|
*/
|
|
|
|
BOOL
|
|
InstallIoHook(
|
|
HINSTANCE hInstance
|
|
)
|
|
{
|
|
int i;
|
|
WORD ports[] = { 0x220, 0x210, 0x230, 0x240, 0x250, 0x260, 0x270 };
|
|
VDD_IO_HANDLERS handlers = {
|
|
VsbByteIn,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
VsbByteOut,
|
|
NULL,
|
|
NULL,
|
|
NULL};
|
|
|
|
// try each base port until success is achieved
|
|
for (i = 0; i < sizeof(ports) / sizeof(ports[0]); i++ ) {
|
|
VDD_IO_PORTRANGE PortRange[5];
|
|
|
|
PortRange[0].First = ports[i] + 0x04;
|
|
PortRange[0].Last = ports[i] + 0x06;
|
|
|
|
PortRange[1].First = ports[i] + 0x08;
|
|
PortRange[1].Last = ports[i] + 0x0A;
|
|
|
|
PortRange[2].First = ports[i] + 0x0C;
|
|
PortRange[2].Last = ports[i] + 0x0C;
|
|
|
|
PortRange[3].First = ports[i] + 0x0E;
|
|
PortRange[3].Last = ports[i] + 0x0E;
|
|
|
|
PortRange[4].First = 0x388;
|
|
PortRange[4].Last = 0x389;
|
|
|
|
if (VDDInstallIOHook((HANDLE)hInstance, 5, PortRange, &handlers)) {
|
|
dprintf2(("Device installed at %X", ports[i]));
|
|
BasePort = ports[i];
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* Remove our i/o hook.
|
|
*/
|
|
|
|
VOID
|
|
DeInstallIoHook(
|
|
HINSTANCE hInstance
|
|
)
|
|
{
|
|
VDD_IO_PORTRANGE PortRange[5];
|
|
|
|
PortRange[0].First = BasePort + 0x04;
|
|
PortRange[0].Last = BasePort + 0x06;
|
|
|
|
PortRange[1].First = BasePort + 0x08;
|
|
PortRange[1].Last = BasePort + 0x0A;
|
|
|
|
PortRange[2].First = BasePort + 0x0C;
|
|
PortRange[2].Last = BasePort + 0x0C;
|
|
|
|
PortRange[3].First = BasePort + 0x0E;
|
|
PortRange[3].Last = BasePort + 0x0E;
|
|
|
|
PortRange[4].First = 0x388;
|
|
PortRange[4].Last = 0x389;
|
|
|
|
VDDDeInstallIOHook((HANDLE)hInstance, 5, PortRange);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* Gets called when the application reads from port.
|
|
* Returns results to application in data.
|
|
*/
|
|
VOID
|
|
VsbByteIn(
|
|
WORD port,
|
|
BYTE * data
|
|
)
|
|
{
|
|
// If we fail simulate nothing at the port
|
|
*data = 0xFF;
|
|
|
|
//
|
|
// make sure we are linked in with winmm
|
|
//
|
|
if (!bWaveOutActive) {
|
|
if (!InitDevices()) {
|
|
// no wave device, forget it
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (port - BasePort) {
|
|
case READ_STATUS:
|
|
DspReadStatus(data);
|
|
break;
|
|
|
|
case READ_DATA:
|
|
DspReadData(data);
|
|
break;
|
|
|
|
case WRITE_STATUS:
|
|
// Can always write
|
|
*data = 0x7F;
|
|
break;
|
|
|
|
case MIXER_ADDRESS:
|
|
// apps sometimes read from this port??
|
|
break;
|
|
|
|
case MIXER_DATA:
|
|
MixerDataRead(data);
|
|
break;
|
|
|
|
case 0x8:
|
|
// remap to ADLIB_STATUS_PORT
|
|
port = ADLIB_STATUS_PORT;
|
|
break;
|
|
}
|
|
|
|
switch(port) {
|
|
case ADLIB_STATUS_PORT:
|
|
FMStatusRead(data);
|
|
break;
|
|
}
|
|
|
|
dprintf4(("Read %4X, <= %2X", port, *data));
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* Gets called when an application writes data to port.
|
|
*/
|
|
|
|
VOID
|
|
VsbByteOut(
|
|
WORD port,
|
|
BYTE data
|
|
)
|
|
{
|
|
//
|
|
// make sure we are linked in with winmm
|
|
//
|
|
if (!bWaveOutActive) {
|
|
if (!InitDevices()) {
|
|
// no wave device, forget it
|
|
return;
|
|
}
|
|
}
|
|
|
|
dprintf4(("Write %4X, => %2X", port, data));
|
|
|
|
switch (port - BasePort) {
|
|
case RESET_PORT:
|
|
DspResetWrite(data);
|
|
break;
|
|
|
|
case WRITE_PORT:
|
|
DspWrite(data);
|
|
break;
|
|
|
|
case MIXER_ADDRESS:
|
|
MixerAddrWrite(data);
|
|
break;
|
|
|
|
case MIXER_DATA:
|
|
MixerDataWrite(data);
|
|
break;
|
|
|
|
case 0x8:
|
|
// remap to ADLIB_REGISTER_SELECT_PORT
|
|
port = ADLIB_REGISTER_SELECT_PORT;
|
|
break;
|
|
|
|
case 0x9:
|
|
// remap to ADLIB_DATA_PORT
|
|
port = ADLIB_DATA_PORT;
|
|
break;
|
|
}
|
|
|
|
switch(port) {
|
|
case ADLIB_REGISTER_SELECT_PORT:
|
|
FMRegisterSelect(data);
|
|
break;
|
|
|
|
case ADLIB_DATA_PORT:
|
|
FMDataWrite(data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* Reset all devices
|
|
*/
|
|
|
|
VOID
|
|
ResetAll(
|
|
VOID
|
|
)
|
|
{
|
|
dprintf2(("Resetting"));
|
|
ResetDSP();
|
|
ResetFM();
|
|
ResetMixer();
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* Debug
|
|
*/
|
|
|
|
#if DBG
|
|
int VddDebugLevel = 3;
|
|
int VddDebugCount = 0;
|
|
|
|
#define DEBUG_START 0
|
|
|
|
/*
|
|
* Generate debug output in printf type format.
|
|
*/
|
|
|
|
void VddDbgOut(LPSTR lpszFormat, ...)
|
|
{
|
|
char buf[256];
|
|
char buf2[300] = "VSBD: ";
|
|
va_list va;
|
|
|
|
if (++VddDebugCount < DEBUG_START) {
|
|
return;
|
|
}
|
|
|
|
va_start(va, lpszFormat);
|
|
wvsprintfA(buf, lpszFormat, va);
|
|
va_end(va);
|
|
|
|
strcat(buf2, buf);
|
|
strcat(buf2, "\r\n");
|
|
OutputDebugStringA(buf2);
|
|
|
|
}
|
|
#endif
|
|
|