diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/mvdm/vdd/samples/adlibvdd/vdd.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/mvdm/vdd/samples/adlibvdd/vdd.c')
-rw-r--r-- | private/mvdm/vdd/samples/adlibvdd/vdd.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/private/mvdm/vdd/samples/adlibvdd/vdd.c b/private/mvdm/vdd/samples/adlibvdd/vdd.c new file mode 100644 index 000000000..7a027ebb4 --- /dev/null +++ b/private/mvdm/vdd/samples/adlibvdd/vdd.c @@ -0,0 +1,457 @@ +/**************************************************************************** + * + * 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 screwed 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; + } + } + + |