/*************************************************************************** * * 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 // The VDD is a win32 DLL #include // Multi-media APIs #include // Definition of VDD calls #include #include #include #include /***************************************************************************** * * 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