windows-nt/Source/XPSP1/NT/base/mvdm/vdd/samples/adlibvdd/vdd.c

458 lines
11 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************
*
* config.c
*
* Copyright (c) 1991 Microsoft Corporation. All Rights Reserved.
*
***************************************************************************/
/*
* Definition of interface to kernel driver (synth.sys)
*
* The kernel driver's Dos device name is assumed fixed and known
*
* adlib.mid or adlib.mid0
*
* The kernel driver is opened in read/write mode.
*
* Writing to the driver sends a list of SYNTH_DATA structures
* to the driver. The port number MUST be 0x388 or 0x389.
*
*
* Reading always reads just 1 byte - the status port.
*/
#include <windows.h> // The VDD is just a win32 DLL
#include <vddsvc.h> // Definition of VDD calls
#include "vdd.h" // Common data with kernel driver
#include <stdio.h>
/*
* Debugging
*/
#if DBG
int VddDebugLevel = 1;
/***************************************************************************
Generate debug output in printf type format
****************************************************************************/
void VddDbgOut(LPSTR lpszFormat, ...)
{
char buf[256];
va_list va;
OutputDebugStringA("Ad Lib VDD: ");
va_start(va, lpszFormat);
vsprintf(buf, lpszFormat, va);
va_end(va);
OutputDebugStringA(buf);
OutputDebugStringA("\r\n");
}
#define dprintf( _x_ ) VddDbgOut _x_
#define dprintf1( _x_ ) if (VddDebugLevel >= 1) VddDbgOut _x_
#define dprintf2( _x_ ) if (VddDebugLevel >= 2) VddDbgOut _x_
#define dprintf3( _x_ ) if (VddDebugLevel >= 3) VddDbgOut _x_
#define dprintf4( _x_ ) if (VddDebugLevel >= 4) VddDbgOut _x_
#else
#define dprintf(x)
#define dprintf1(x)
#define dprintf2(x)
#define dprintf3(x)
#define dprintf4(x)
#endif // DBG
/*
* Symbolic names for port addresses
*/
#define ADLIB_DATA_PORT 0x389
#define ADLIB_REGISTER_SELECT_PORT 0x388
#define ADLIB_STATUS_PORT 0x388
/*
* Batch data to the device - for true Adlib use a size of 2
*/
#define BATCH_SIZE 40
int Position = 0;
SYNTH_DATA PortData[BATCH_SIZE];
/*
* Internal Routines
*/
void MyByteIn(WORD port, BYTE *data);
void MyByteOut(WORD port, BYTE data);
/*
* IO handler table.
*
* There's no point in providing string handlers because the chip
* can't respond very quickly (need gaps of at least 23 microseconds
* between writes).
*/
VDD_IO_HANDLERS handlers = {
MyByteIn,
NULL,
NULL,
NULL,
MyByteOut,
NULL,
NULL,
NULL};
/*
* Note that we rely on the kernel driver to pretend the device is
* at address 388 even the driver supports it somewhere else.
*/
VDD_IO_PORTRANGE ports[] = {
{
0x228,
0x229
},
{
0x388,
0x389
}
};
/*
* Globals
*/
//
// Track timers. The basic rule is that if no timer is started then
// the only way the status register can change is via the reset bit
// in which case we know what will happen.
//
// If a timer interrupts then it's 'stopped'
//
BOOL Timer1Started;
BOOL Timer2Started;
BYTE Status;
/*
* Current device handle
*
* NULL if device is (potentially) free
* INVALID_HANDLE_VALUE if device was not obtainable
*/
HANDLE DeviceHandle;
HANDLE OpenDevice(PWSTR DeviceName)
{
WCHAR DosDeviceName[MAX_PATH];
/*
* Make up a string suitable for opening a Dos device
*/
wcscpy(DosDeviceName, TEXT("\\\\."));
wcscat(DosDeviceName, DeviceName +
wcslen(TEXT("\\Device")));
/*
* Open the device with GENERIC_READ and GENERIC_WRITE
* Also use FILE_SHARE_WRITE so other applications can
* set the device volume
*/
return CreateFile(DosDeviceName,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
}
/*
* Open our device is it can be opened and we haven't tried before
*
* Returns FALSE if device can't be acquired.
*/
BOOL CheckDeviceAccess(void)
{
/*
* If we don't have a handle (valid or invalid) already try
* opening the device
*/
if (DeviceHandle == NULL) {
DeviceHandle = OpenDevice(STR_ADLIB_DEVICENAME);
if (DeviceHandle == INVALID_HANDLE_VALUE) {
DeviceHandle = OpenDevice(STR_ADLIB_DEVICENAME L"0");
}
Position = 0;
}
return DeviceHandle != INVALID_HANDLE_VALUE;
}
/*
* Map a write to a port
*
* How are we going to simulate timer stuff?
* Answer: Allow reading of the status port.
*
* This is optimized to only write when we get a data port write
*/
void MyByteOut(WORD port, BYTE data)
{
//
// Remember what register is selected
//
static BYTE AdlibRegister;
//
// Just package the stuff up and call write file
//
DWORD BytesWritten;
dprintf3(("Received write to Port %4X, Data %2X", port, data));
port = (port & 1) | ADLIB_REGISTER_SELECT_PORT;
/*
* Check for special values - don't let them switch to
* OPL3 mode.
*/
#if 0
if (port == ADLIB_DATA_PORT && AdlibRegister == AD_NEW) {
data &= 0xFE;
}
#endif
if (port == ADLIB_REGISTER_SELECT_PORT) {
/*
* Just remember which register is supposed to be selected
* to cut down the number of times we go to the device driver
*/
AdlibRegister = data;
} else {
/*
* Write this one to the device
*/
PortData[Position].IoPort = ADLIB_REGISTER_SELECT_PORT;
PortData[Position].PortData = AdlibRegister;
PortData[Position + 1].IoPort = port;
PortData[Position + 1].PortData = data;
Position += 2;
if (Position == BATCH_SIZE ||
AdlibRegister >= 0xA0 && AdlibRegister <= 0xBF ||
AdlibRegister == AD_MASK) {
/*
* See if we have the device
*/
if (CheckDeviceAccess()) {
if (!WriteFile(DeviceHandle,
&PortData,
Position * sizeof(PortData[0]),
&BytesWritten,
NULL)) {
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 = 0;
}
/*
* 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;
#endif
} else {
Timer1Started = FALSE;
}
if ((data & 2) && !(Status & 0x20)) {
dprintf2(("Timer 2 started"));
#if 0
Timer2Started = TRUE;
#else
Status |= 0xA0;
#endif
Timer2Started = TRUE;
} else {
Timer2Started = FALSE;
}
}
}
}
Position = 0;
}
}
}
/*
* Gets called when the application reads from one of our ports.
* We know the device only returns interesting things in the status port.
*/
void MyByteIn(WORD port, BYTE *data)
{
DWORD BytesRead;
dprintf4(("Received read from Port %4X", port));
port = (port & 1) | ADLIB_STATUS_PORT;
/*
* If we fail simulate nothing at the port
*/
*data = 0xFF;
/*
* Say there's nothing there if we didn't get the device driver or
* it's not the status port
*/
if (port != ADLIB_STATUS_PORT || !CheckDeviceAccess()) {
return;
}
#if 0 // WSS interrupt messed this up
/*
* 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(DeviceHandle,
&Status,
1,
&BytesRead,
NULL)) {
dprintf1(("Failed to read from device - code %d", GetLastError()));
} else {
/*
* Look for state change
*/
if (Status & 0x40) {
Timer1Started = FALSE;
dprintf2(("Timer 1 finished"));
}
if (Status & 0x20) {
Timer2Started = FALSE;
dprintf2(("Timer 2 finished"));
}
}
}
#endif
dprintf3(("Data read was %2X", Status));
*data = Status;
}
/*
* Standard DLL entry point routine.
*/
BOOL DllEntryPoint(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
{
switch (Reason) {
case DLL_PROCESS_ATTACH:
if (!VDDInstallIOHook(hInstance, 2, ports, &handlers)) {
dprintf2(("Ad Lib VDD failed to load - error in VDDInstallIoHook"));
return FALSE;
} else {
dprintf2(("Ad Lib VDD loaded OK"));
return TRUE;
}
case DLL_PROCESS_DETACH:
VDDDeInstallIOHook(hInstance, 2, ports);
/*
* Note that this event corresponds to FreeLibrary on our DLL,
* NOT termination of the process - so we can't rely on process
* termination to close our device handle.
*
*/
if (DeviceHandle) {
CloseHandle(DeviceHandle);
DeviceHandle = NULL; // Redundant but neater.
}
return TRUE;
default:
return TRUE;
}
}