summaryrefslogtreecommitdiffstats
path: root/private/mvdm/vdd/samples/adlibvdd/vdd.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/mvdm/vdd/samples/adlibvdd/vdd.c')
-rw-r--r--private/mvdm/vdd/samples/adlibvdd/vdd.c457
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;
+ }
+ }
+
+