274 lines
7.4 KiB
C
274 lines
7.4 KiB
C
/***************************************************************************
|
|
*
|
|
* fm.c
|
|
*
|
|
* Copyright (c) 1991-1996 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* This code provides VDD support for SB 2.0 sound output, specifically:
|
|
* 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 <fm.h>
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Globals
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HANDLE HFM = NULL; // current open FM device
|
|
BOOL FMActive = FALSE; // indicates whether we have an FM device
|
|
BYTE AdlibRegister = 0x00; // register currently selected
|
|
int Position = 0; // position in PortData array
|
|
SYNTH_DATA PortData[BATCH_SIZE]; // batched data to be written to OPL2 device
|
|
BOOL Timer1Started = FALSE; // if a timer interrupts then it's stopped
|
|
BOOL Timer2Started = FALSE; // if a timer interrupts then it's stopped
|
|
BYTE Status = 0x06; // or 0x00, see sb programming book page xi
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* FM device routines
|
|
*
|
|
****************************************************************************/
|
|
|
|
VOID
|
|
ResetFM(
|
|
VOID
|
|
)
|
|
{
|
|
AdlibRegister = 0x00; // register currently selected
|
|
Position = 0;
|
|
Timer1Started = FALSE;
|
|
Timer2Started = FALSE;
|
|
Status = 0x06;
|
|
}
|
|
|
|
VOID
|
|
FMStatusRead(
|
|
BYTE *data
|
|
)
|
|
{
|
|
#if 0 // This should work but doesn't (ReadFile fails)
|
|
// Are we expecting a state change ?
|
|
|
|
if (Timer1Started || Timer2Started) {
|
|
// Read the status port from the driver - this is how the
|
|
// driver interprets read.
|
|
// Well, actually don't because the WSS driver doesn't work!
|
|
|
|
if (!ReadFile(HFM, &Status, 1, &bytesRead, NULL)) {
|
|
#if DBG
|
|
FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(char *) &lpMsgBuf, 0, NULL);
|
|
dprintf1(("FM read port failed: %d bytes of data read, error message: %s",
|
|
bytesRead, lpMsgBuf));
|
|
LocalFree( lpMsgBuf ); // Free the buffer.
|
|
#endif DBG
|
|
break;
|
|
}
|
|
else {
|
|
// Look for state change
|
|
|
|
if (Status & 0x40) {
|
|
Timer1Started = FALSE;
|
|
dprintf2(("Timer 1 finished"));
|
|
}
|
|
|
|
if (Status & 0x20) {
|
|
Timer2Started = FALSE;
|
|
dprintf2(("Timer 2 finished"));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
*data = Status;
|
|
}
|
|
|
|
VOID
|
|
FMRegisterSelect(
|
|
BYTE data
|
|
)
|
|
{
|
|
AdlibRegister = data;
|
|
}
|
|
|
|
VOID
|
|
FMDataWrite(
|
|
BYTE data
|
|
)
|
|
{
|
|
if(AdlibRegister==AD_NEW) {
|
|
data &=0xFE; // don't enter opl3 mode
|
|
}
|
|
|
|
// put data in PortData array
|
|
if(Position <= BATCH_SIZE-2) {
|
|
PortData[Position].IoPort = ADLIB_REGISTER_SELECT_PORT;
|
|
PortData[Position].PortData = AdlibRegister;
|
|
PortData[Position + 1].IoPort = ADLIB_DATA_PORT;
|
|
PortData[Position + 1].PortData = data;
|
|
Position += 2;
|
|
} else {
|
|
dprintf1(("Attempting to write beyond end of PortData array"));
|
|
}
|
|
|
|
if (Position == BATCH_SIZE ||
|
|
AdlibRegister>=0xB0 && AdlibRegister<=0xBD ||
|
|
AdlibRegister == AD_MASK) {
|
|
// PortData full or note-on/off command or changing status
|
|
if (!FMPortWrite()) {
|
|
dprintf1(("Failed to write to device!"));
|
|
} else {
|
|
// Work out what status change may have occurred
|
|
if (AdlibRegister == AD_MASK) {
|
|
// Look for RST and starting timers
|
|
if (data & 0x80) {
|
|
Status = 0x00; // reset both timers
|
|
}
|
|
|
|
// We ignore starting of timers if their interrupt
|
|
// flag is set because the timer status will have to
|
|
// be set again to make the status for this timer change
|
|
|
|
if ((data & 1) && !(Status & 0x40)) {
|
|
dprintf2(("Timer 1 started"));
|
|
#if 0
|
|
Timer1Started = TRUE;
|
|
#else
|
|
Status |= 0xC0; // simulate immediate expiry of timer1
|
|
#endif
|
|
} else {
|
|
Timer1Started = FALSE;
|
|
}
|
|
|
|
if ((data & 2) && !(Status & 0x20)) {
|
|
dprintf2(("Timer 2 started"));
|
|
#if 0
|
|
Timer2Started = TRUE;
|
|
#else
|
|
Status |= 0xA0; // simulate immediate expiry of timer2
|
|
#endif
|
|
Timer2Started = TRUE;
|
|
} else {
|
|
Timer2Started = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Opens opl2 device adlib.mid or adlib.mid0 as a file handle.
|
|
* Returns TRUE on success.
|
|
*/
|
|
|
|
BOOL
|
|
OpenFMDevice(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD dwBytesReturned;
|
|
LPVOID lpMsgBuf;
|
|
|
|
// attempt to open device file adlib.mid or adlib.mid0
|
|
HFM = CreateFile(L"\\\\.\\adlib.mid", GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
|
if (HFM == INVALID_HANDLE_VALUE) {
|
|
HFM = CreateFile(L"\\\\.\\adlib.mid0", GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
|
}
|
|
|
|
if (HFM == INVALID_HANDLE_VALUE) {
|
|
#if DBG
|
|
FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &lpMsgBuf, 0, NULL);
|
|
dprintf1(("Create FM out failed, error message: %s", lpMsgBuf));
|
|
LocalFree( lpMsgBuf ); // Free the buffer.
|
|
#endif // DBG
|
|
return FALSE;
|
|
}
|
|
FMActive = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* Closes our FM device.
|
|
*/
|
|
|
|
VOID
|
|
CloseFMDevice(
|
|
VOID
|
|
)
|
|
{
|
|
dprintf2(("Closing FM device"));
|
|
|
|
if (HFM) {
|
|
CloseHandle(HFM);
|
|
HFM = NULL;
|
|
FMActive = FALSE;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* Sends FM data to the card.
|
|
* Returns TRUE on success.
|
|
*/
|
|
|
|
BOOL
|
|
FMPortWrite(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD bytesWritten = 0;
|
|
LPVOID lpMsgBuf;
|
|
|
|
if(FMActive) {
|
|
dprintf4(("Writing %d bytes of data to port",
|
|
Position * sizeof(PortData[0])));
|
|
if(!WriteFile(HFM, &PortData, Position * sizeof(PortData[0]),
|
|
&bytesWritten, NULL)) {
|
|
#if DBG
|
|
FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &lpMsgBuf,
|
|
0, NULL);
|
|
|
|
dprintf1(("FM write failed: %d bytes of data written, error message: %s",
|
|
bytesWritten, lpMsgBuf));
|
|
LocalFree( lpMsgBuf ); // Free the buffer.
|
|
#endif //DBG
|
|
return FALSE;
|
|
}
|
|
}
|
|
Position = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
|
|
|
|
|
|
|