summaryrefslogtreecommitdiffstats
path: root/private/mvdm/vdd
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/mvdm/vdd
downloadNT4.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')
-rw-r--r--private/mvdm/vdd/dirs24
-rw-r--r--private/mvdm/vdd/samples/adlibvdd/adlibvdd.def8
-rw-r--r--private/mvdm/vdd/samples/adlibvdd/makefile1
-rw-r--r--private/mvdm/vdd/samples/adlibvdd/sources24
-rw-r--r--private/mvdm/vdd/samples/adlibvdd/vdd.c457
-rw-r--r--private/mvdm/vdd/samples/adlibvdd/vdd.h37
-rw-r--r--private/mvdm/vdd/samples/dirs29
-rw-r--r--private/mvdm/vdd/samples/hpscan/hpscan16.asm138
-rw-r--r--private/mvdm/vdd/samples/hpscan/hpscan16.inc29
-rw-r--r--private/mvdm/vdd/samples/hpscan/makefile44
-rw-r--r--private/mvdm/vdd/samples/mscdex/dirs23
-rw-r--r--private/mvdm/vdd/samples/mscdex/readme.txt22
-rw-r--r--private/mvdm/vdd/samples/mscdex/tsr/makefile49
-rw-r--r--private/mvdm/vdd/samples/mscdex/tsr/messages/usa/messages.inc2
-rw-r--r--private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.asm246
-rw-r--r--private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.inc30
-rw-r--r--private/mvdm/vdd/samples/mscdex/vdd/makefile11
-rw-r--r--private/mvdm/vdd/samples/mscdex/vdd/mscdex.h313
-rw-r--r--private/mvdm/vdd/samples/mscdex/vdd/sources27
-rw-r--r--private/mvdm/vdd/samples/mscdex/vdd/vcdex.c1474
-rw-r--r--private/mvdm/vdd/samples/mscdex/vdd/vcdex.def8
-rw-r--r--private/mvdm/vdd/samples/mscdex/vdd/vcdex.h91
-rw-r--r--private/mvdm/vdd/samples/mscdex/vdd/vcdex.rc48
-rw-r--r--private/mvdm/vdd/samples/readme11
-rw-r--r--private/mvdm/vdd/samples/sample1/dirs1
-rw-r--r--private/mvdm/vdd/samples/sample1/fax16/fax16.asm348
-rw-r--r--private/mvdm/vdd/samples/sample1/fax16/makefile41
-rw-r--r--private/mvdm/vdd/samples/sample1/fax32/fax32.c248
-rw-r--r--private/mvdm/vdd/samples/sample1/fax32/fax32.def8
-rw-r--r--private/mvdm/vdd/samples/sample1/fax32/fax32.h29
-rw-r--r--private/mvdm/vdd/samples/sample1/fax32/makefile11
-rw-r--r--private/mvdm/vdd/samples/sample1/fax32/sources21
-rw-r--r--private/mvdm/vdd/samples/sample2/16bits/16bits.asm217
-rw-r--r--private/mvdm/vdd/samples/sample2/16bits/16bits.inc43
-rw-r--r--private/mvdm/vdd/samples/sample2/16bits/makefile42
-rw-r--r--private/mvdm/vdd/samples/sample2/dirs1
-rw-r--r--private/mvdm/vdd/samples/sample2/vdd/makefile11
-rw-r--r--private/mvdm/vdd/samples/sample2/vdd/sources23
-rw-r--r--private/mvdm/vdd/samples/sample2/vdd/vdd.c385
-rw-r--r--private/mvdm/vdd/samples/sample2/vdd/vdd.def6
-rw-r--r--private/mvdm/vdd/samples/sample2/vdd/vdd.h67
-rw-r--r--private/mvdm/vdd/samples/vsbd/makefile1
-rw-r--r--private/mvdm/vdd/samples/vsbd/sources25
-rw-r--r--private/mvdm/vdd/samples/vsbd/vdd.c1328
-rw-r--r--private/mvdm/vdd/samples/vsbd/vsbd.def8
-rw-r--r--private/mvdm/vdd/vddserv.doc791
46 files changed, 6801 insertions, 0 deletions
diff --git a/private/mvdm/vdd/dirs b/private/mvdm/vdd/dirs
new file mode 100644
index 000000000..44cabd854
--- /dev/null
+++ b/private/mvdm/vdd/dirs
@@ -0,0 +1,24 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+History:
+ Created 27-Mar-91 by Jeff Parsons (jeffpar)
+ from template created 12-Apr-1990 by Steve Wood (stevewo)
+
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS= samples
diff --git a/private/mvdm/vdd/samples/adlibvdd/adlibvdd.def b/private/mvdm/vdd/samples/adlibvdd/adlibvdd.def
new file mode 100644
index 000000000..f1aa6a272
--- /dev/null
+++ b/private/mvdm/vdd/samples/adlibvdd/adlibvdd.def
@@ -0,0 +1,8 @@
+LIBRARY ADLIBVDD
+
+DESCRIPTION 'Ad Lib virtual device driver'
+
+EXETYPE WINDOWS
+
+EXPORTS
+ DllEntryPoint
diff --git a/private/mvdm/vdd/samples/adlibvdd/makefile b/private/mvdm/vdd/samples/adlibvdd/makefile
new file mode 100644
index 000000000..5acbbd24c
--- /dev/null
+++ b/private/mvdm/vdd/samples/adlibvdd/makefile
@@ -0,0 +1 @@
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/mvdm/vdd/samples/adlibvdd/sources b/private/mvdm/vdd/samples/adlibvdd/sources
new file mode 100644
index 000000000..ca966c9b0
--- /dev/null
+++ b/private/mvdm/vdd/samples/adlibvdd/sources
@@ -0,0 +1,24 @@
+##
+# Create an Ad Lib virtual device driver
+#
+
+MAJORCOMP=vdd
+MINORCOMP=adlibvdd
+
+TARGETNAME=adlibvdd
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+UMTYPE=windows
+
+# Define libs we need and where to find them
+
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntvdm.lib
+
+C_DEFINES=-DUNICODE
+
+SOURCES=vdd.c
+
+DLLENTRY=DllEntryPoint
+
+DLLBASE=0x6BD00000
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;
+ }
+ }
+
+
diff --git a/private/mvdm/vdd/samples/adlibvdd/vdd.h b/private/mvdm/vdd/samples/adlibvdd/vdd.h
new file mode 100644
index 000000000..989c02dc2
--- /dev/null
+++ b/private/mvdm/vdd/samples/adlibvdd/vdd.h
@@ -0,0 +1,37 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+
+Copyright (c) 1992, 1993 Microsoft Corporation
+
+Module Name:
+
+ vdd.h
+
+Abstract:
+
+ This header file is a grossly cut down version of that which exists
+ in the SYNTH driver project. I have only extracted those pieces required
+ for the AdLib VDD.
+
+Author:
+
+ Mike Tricker (MikeTri) 27-Jan-93 (after Robin Speed (RobinSp) 20-Oct-92)
+
+Revision History:
+
+--*/
+
+#define STR_ADLIB_DEVICENAME L"\\Device\\adlib.mid"
+
+/*
+ * Stucture for passing synth data
+ */
+
+typedef struct {
+ unsigned short IoPort;
+ unsigned short PortData;
+} SYNTH_DATA, *PSYNTH_DATA;
+
+
+#define AD_MASK (0x004)
+#define AD_NEW (0x105)
diff --git a/private/mvdm/vdd/samples/dirs b/private/mvdm/vdd/samples/dirs
new file mode 100644
index 000000000..048cd12ac
--- /dev/null
+++ b/private/mvdm/vdd/samples/dirs
@@ -0,0 +1,29 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+History:
+ Created 27-Mar-91 by Jeff Parsons (jeffpar)
+ from template created 12-Apr-1990 by Steve Wood (stevewo)
+
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=mscdex
+
+OPTIONAL_DIRS=sample1 \
+ sample2 \
+ adlibvdd \
+ hpscan \
+ vsbd
diff --git a/private/mvdm/vdd/samples/hpscan/hpscan16.asm b/private/mvdm/vdd/samples/hpscan/hpscan16.asm
new file mode 100644
index 000000000..5e121e969
--- /dev/null
+++ b/private/mvdm/vdd/samples/hpscan/hpscan16.asm
@@ -0,0 +1,138 @@
+.MODEL small
+;*************************************************
+; Filename: hpscan16.asm
+; Purpose: Stub DOS Device Driver. Pass device
+; "HPSCAN" requests to the VDD, hpscan32.dll.
+; Environment: MSDOS, Windows NT.
+; (C) Hewlett-Packard Company 1993.
+;*************************************************
+INCLUDE hpscan16.inc ;private
+INCLUDE isvbop.inc ;NT DDK
+
+SUBTTL Segment and data definitions
+ ASSUME CS:CSEG,DS:NOTHING,ES:NOTHING
+CSEG SEGMENT
+
+;-------------------------------------------------
+; Resident data area - variables needed after init
+;-------------------------------------------------
+
+;**--- Device Header, must be at offset zero ---**
+SCAN_HEADER:
+ dd -1 ;becomes ptr to next req hdr
+ dw 0C000H ;character, supports IOCTL
+ dw offset STRAT ;Strategy routine
+ dw offset IDVR ;Interrupt routine
+DH_NAME db 'HPSCAN ' ;char device name
+
+;**---- Request Header addr, saved by STRAT ----**
+RH_PTRA LABEL DWORD
+RH_PTRO dw ? ;offset
+RH_PTRS dw ? ;segment
+
+;**------------- Define Stack Space ------------**
+STK_SEG dw ? ;Save original stack segment
+STK_PTR dw ? ;Save original stack pointer
+STACK dw 200 DUP (0) ;Local stack
+TOP_STK dw ? ;Top of local stack
+
+;**--------------- VDD information -------------**
+VDD_DllName db "HPSCAN32.DLL", 0
+VDD_InitFunc db "VDDInit", 0
+VDD_DispFunc db "VDDDispatch", 0
+VDD_hVDD dw ?
+
+;**-------------- Copyright Info ---------------**
+ db '(C) Copyright Hewlett-Packard Company 1993.'
+ db 'All rights reserved.'
+
+SUBTTL Device Strategy & Interrupt entry points
+
+;**--------------- STRAT routine ---------------**
+STRAT proc far ;Strategy routine
+ mov cs:RH_PTRO,bx ;save offset address
+ mov cs:RH_PTRS,es ;save segment address
+ ret ;end Strategy routine
+STRAT endp
+
+;**--------------- IDVR routine ---------------**
+IDVR proc far ;Interrupt routine
+ push ds ;save all modified registers
+ push es ;DOS has stack for 20 pushes
+ push ax
+ push bx
+ push cx
+ push dx
+ push di
+ push si
+ push bp
+
+ mov cs:STK_PTR,sp ;save original stack ptr
+ mov cs:STK_SEG,ss ;save original stack seg
+ cli ;disable for stack ops
+ mov ax,cs ;setup new stack ptr
+ mov ss,ax ;setup new stack seg
+ mov sp,offset TOP_STK
+ sti ;restore flags back
+ cld ;all moves are forward
+
+ les bx,cs:RH_PTRA ;load req hdr adr in es:bx
+ mov al,RH.RHC_CMD
+ cmp al,0 ;check for init command
+ je BOOTUP ;command 0 = init
+
+ xor dx,dx ;some other command
+ mov dl,RH.RHC_CMD ;dx = command code
+ mov cx,RH.RHC_CNT ;cx = count
+ mov ax,RH.RHC_SEG ;es:bx = addr of data
+ mov bx,RH.RHC_OFF
+ mov es,ax ;finally, load VDD handle
+ mov ax,word ptr cs:[VDD_hVDD]
+ DispatchCall ;call Dispatch in VDD
+ ;returns with status in di
+EXIT:
+ les bx,cs:RH_PTRA ;restore ES:BX
+ or di,STAT_DONE ;add "DONE" bit to status
+ mov RH.RHC_STA,di ;save status in requ hdr
+ cli ;disable ints for stack op
+ mov ss,cs:STK_SEG ;restore stack seg
+ mov sp,cs:STK_PTR ;restore stack ptr
+ sti ;re-enable interrupts
+
+ pop bp ;restore registers
+ pop si
+ pop di
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ pop es
+ pop ds
+ ret ;far return
+IDVR endp
+
+;**--------- jump here for Init Command --------**
+BOOTUP:
+ mov ax,offset EndDriver
+ mov RH.RHC_OFF,ax ;address of end of driver
+ mov RH.RHC_SEG,CS ;reference from code seg
+
+ mov si,offset VDD_DllName ;load regs for VDD
+ mov di,offset VDD_InitFunc
+ mov bx,offset VDD_DispFunc
+ mov ax,ds
+ mov es,ax
+ RegisterModule ;calls the VDD
+ jnc save_hVDD ;if NC then success
+ mov di,STAT_GF ;set failure status
+ jmp EXIT ;return via common exit
+
+save_hVDD:
+ mov [VDD_hVDD],ax ;save handle in ax
+ mov di,STAT_OK ;load OK status
+ jmp EXIT ;return via common exit
+
+EndDriver db ?
+CSEG ENDS
+ END SCAN_HEADER ;REQUIRED BY EXE2BIN
+ \ No newline at end of file
diff --git a/private/mvdm/vdd/samples/hpscan/hpscan16.inc b/private/mvdm/vdd/samples/hpscan/hpscan16.inc
new file mode 100644
index 000000000..1bc4d07ff
--- /dev/null
+++ b/private/mvdm/vdd/samples/hpscan/hpscan16.inc
@@ -0,0 +1,29 @@
+;*************************************************
+; Name: HPSCAN16.INC
+; Description: Defines for HPSCAN16.ASM
+;*************************************************
+
+;**----------- Segment Declarations ------------**
+CSEG segment word public 'CODE'
+CSEG ends ;header segment
+
+;**-------------- Status Values ----------------**
+STAT_OK equ 0000h ;ok
+STAT_DONE equ 0100h ;function complete
+STAT_GF equ 800Ch ;general failure
+
+RH EQU ES:[BX] ;request header
+
+;**------ Common Request Header Structure ------**
+RHC struc ;common to all commands
+ db ? ;length of request header
+ db ? ;unit code of device
+RHC_CMD db ? ;command code
+RHC_STA dw ? ;completion status, 16-bits
+ dq ? ;reserved for DOS
+ db ? ;this field varies with command
+RHC_OFF dw ? ;offset of data
+RHC_SEG dw ? ;segment of data
+RHC_CNT dw ? ;byte count (length) of data
+RHC ends ;end of common portion
+ \ No newline at end of file
diff --git a/private/mvdm/vdd/samples/hpscan/makefile b/private/mvdm/vdd/samples/hpscan/makefile
new file mode 100644
index 000000000..a8860fb1f
--- /dev/null
+++ b/private/mvdm/vdd/samples/hpscan/makefile
@@ -0,0 +1,44 @@
+# Makefile for hpscan16 stub device driver
+
+!IFNDEF BUILDMSG
+BUILDMSG=
+!ENDIF
+
+########## Path definition so we find 16 bit tools ##########
+# Also works around stupid bug in RC 3.1 that doesn't allow rcpp.err to be
+# in a directory that is greater than 128 chars down the path, even if
+# rc 3.1 is running as an OS/2 app.
+
+PATH = $(_NTBINDIR)\private\mvdm\tools16;$(PATH)
+
+.SUFFIXES:
+.SUFFIXES: .c .obj .lst .exe .exc .exs .com .sal .cod .sil .inc .skl .cla .cl1 .ctl .asm .idx .msg
+
+MAKE =nmake
+asm =masm
+awarn =-W1
+aflags =-Mx -t $(awarn) $(extasw)
+ainc =-I. -I$(_NTBINDIR)\public\sdk\inc
+
+
+.asm.obj:
+ $(asm) $(ainc) $(aflags) $*.asm;
+
+.asm.lst:
+ $(asm) -l $(ainc) $(aflags) $*.asm;
+
+all: hpscan16.sys
+ -binplace hpscan16.sys
+
+clean: cleanup all
+
+cleanup:
+ if exist *.obj del *.obj
+ if exist *.exe del *.exe
+ if exist *.map del *.map
+ if exist *.sym del *.sym
+ if exist *.sys del *.sys
+
+hpscan16.sys: hpscan16.obj
+ link16 hpscan16;
+ exe2bin hpscan16.exe hpscan16.sys
diff --git a/private/mvdm/vdd/samples/mscdex/dirs b/private/mvdm/vdd/samples/mscdex/dirs
new file mode 100644
index 000000000..faaa9370e
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/dirs
@@ -0,0 +1,23 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+History:
+ Created 27-Mar-91 by Jeff Parsons (jeffpar)
+ from template created 12-Apr-1990 by Steve Wood (stevewo)
+
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=vdd
diff --git a/private/mvdm/vdd/samples/mscdex/readme.txt b/private/mvdm/vdd/samples/mscdex/readme.txt
new file mode 100644
index 000000000..f2256daa8
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/readme.txt
@@ -0,0 +1,22 @@
+
+Hi Sudeep,
+
+Here are the MSCDEX files. There is a TSR and a VDD. I normally build the
+TSR with 16-bit tools.
+
+The only installation changes required would be that the TSR (MSCDEXNT.EXE)
+be added to the Autoexec.nt. This line in autoexec.nt needs to be before the
+"dosx" line, per a bug in dosx.
+
+The VCDEX.DLL needs to be in the path. The MSCDEXNT.TSR does a
+RegisterModule() to it.
+
+
+I have a couple of CD player apps in the TEST directory. CDP is a win16
+app, and CDPLYR is a DOS app (from the soundblaster disks). Both of them
+use MSCDEX exclusively.
+
+
+I hope that does it. Let me know if you run into any problems.
+
+-Neil
diff --git a/private/mvdm/vdd/samples/mscdex/tsr/makefile b/private/mvdm/vdd/samples/mscdex/tsr/makefile
new file mode 100644
index 000000000..8879a627f
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/tsr/makefile
@@ -0,0 +1,49 @@
+!IFNDEF BUILDMSG
+BUILDMSG=
+!ENDIF
+
+########## Path definition so we find 16 bit tools ##########
+# Also works around stupid bug in RC 3.1 that doesn't allow rcpp.err to be
+# in a directory that is greater than 128 chars down the path, even if
+# rc 3.1 is running as an OS/2 app.
+
+PATH = $(_NTBINDIR)\private\mvdm\tools16;$(PATH)
+
+.SUFFIXES:
+.SUFFIXES: .c .obj .lst .exe .exc .exs .com .sal .cod .sil .inc .skl .cla .cl1 .ctl .asm .idx .msg
+
+MAKE =nmake
+asm =masm
+awarn =-W1
+aflags =-Mx -t $(awarn) $(extasw)
+ainc =-I. -I$(_NTBINDIR)\public\sdk\inc
+
+!IFNDEF COUNTRY
+COUNTRY=usa
+!ENDIF
+
+.asm.obj:
+ $(asm) $(ainc) $(aflags) $*.asm;
+
+.asm.lst:
+ $(asm) -l $(ainc) $(aflags) $*.asm;
+
+all: mscdexnt.exe
+ -binplace mscdexnt.exe
+
+clean: cleanup all
+
+cleanup:
+ if exist *.obj del *.obj
+ if exist *.exe del *.exe
+ if exist *.map del *.map
+ if exist *.sym del *.sym
+ if exist messages.inc del messages.inc
+
+messages.inc : messages\$(COUNTRY)\messages.inc
+ copy messages\$(COUNTRY)\messages.inc .
+
+mscdexnt.obj: mscdexnt.asm mscdexnt.inc messages.inc
+
+mscdexnt.exe: mscdexnt.obj
+ link16 /CP:1 mscdexnt.obj, mscdexnt;
diff --git a/private/mvdm/vdd/samples/mscdex/tsr/messages/usa/messages.inc b/private/mvdm/vdd/samples/mscdex/tsr/messages/usa/messages.inc
new file mode 100644
index 000000000..94ea20e1a
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/tsr/messages/usa/messages.inc
@@ -0,0 +1,2 @@
+Message1 db CR,LF,'MSCDEXNT Will Run Only Under Windows NT.',CR,LF,'$'
+Message2 db CR,LF,'MSCDEXNT is already installed.',CR,LF,'$'
diff --git a/private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.asm b/private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.asm
new file mode 100644
index 000000000..ca0ff46a1
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.asm
@@ -0,0 +1,246 @@
+ name mscdexnt
+;
+; MSCDEXNT
+;
+; Author: Neil Sandlin (neilsa)
+;
+; Description:
+;
+; This TSR implements the v86 mode portion of MSCDEX support under
+; NT. Basically, all this piece does is hook INT2F and watch for
+; MSCDEX calls. When the first one occurs, it tries to load VCDEX.DLL.
+; If that succeeds, it passes the call (and all subsequent calls)
+; to VCDEX for processing.
+;
+ include isvbop.inc
+ include mscdexnt.inc
+
+_TEXT segment word public 'CODE'
+ assume cs:_TEXT,ds:_TEXT,es:_TEXT
+
+;*----------------------- TSR Code --------------------------*
+
+DrvStrat proc far ; Strategy Routine
+ ret
+DrvStrat endp
+
+DrvIntr proc far ; INterrupt routine
+ ret
+DrvIntr endp
+
+;******************************************************************************
+;
+; Int2FHook
+;
+;
+;******************************************************************************
+Int2FHook proc near
+
+ cmp ah, MSCDEX_ID ;MSCDEX?
+ jnz int2fchain ;no
+ cmp al, MAX_MSCDEX_CMD ;command too high?
+ ja int2fchain ;yes
+
+ cmp word ptr cs:[hVDD], 0 ;zero is an invalid module handle
+ jnz callvdd ;registered ok
+
+ cmp byte ptr cs:[fVDDChecked],1
+ jz vddfailed
+
+ call RegisterVDD
+ jc vddfailed ;didn't get it
+
+callvdd:
+ push ax ;put ax on stack
+ mov ax, word ptr cs:[hVDD]
+ DispatchCall
+ add sp, 2 ;vdd has set ax accordingly
+ iret ;svc handled, return to caller
+
+vddfailed:
+ or al,al
+ jnz try_0b
+ xor bx,bx
+ jmp short int2f_done
+try_0b:
+ cmp al,0bh
+ jne int2f_done
+;; williamh - June 1 1993 - if unable to load VDD, we should tell
+;; the caller that the drive is NOT a cd rom.
+ xor ax, ax
+ mov bx,0adadh
+int2f_done:
+ iret
+
+int2fchain:
+ jmp dword ptr cs:[oldint]
+
+Int2FHook endp
+
+;****************************************************************************
+;
+; RegisterVDD
+;
+;****************************************************************************
+RegisterVDD proc near
+
+ push ax
+ push bx
+ push cx
+ push dx
+ push si
+ push di
+ push ds
+ push es
+
+
+ mov ax, cs
+ mov ds, ax
+ mov es, ax
+ ; Load vcdex.dll
+ mov si, offset DllName ; ds:si = dll name
+ mov di, offset InitFunc ; es:di = init routine
+ mov bx, offset DispFunc ; ds:bx = dispatch routine
+
+ push cs ; pass far pointer to headers
+ pop cx ; in cx:dx
+ mov dx, offset drive_header
+
+ RegisterModule
+ jc errorexit ; jif error
+ mov cs:[hVDD],ax ; save handle
+
+errorexit:
+ mov byte ptr cs:[fVDDChecked],1
+ pop es
+ pop ds
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+RegisterVDD endp
+
+;*----------------------- TSR Data Area ---------------------*
+oldint dd 0
+hVDD DW 0
+
+fVDDChecked DB 0 ; 0 - VDD never called. 1 - VDD once called.
+
+DllName DB "VCDEX.DLL",0
+InitFunc DB "VDDRegisterInit",0
+DispFunc DB "VDDDispatch",0
+
+
+ ALIGN 16
+drive_header:
+ DrvHd 'MSCDEX00'
+
+ ALIGN 16
+Init_Fence:
+;*-------------------------- Initialization Code ----------------------*
+
+mscdexnt proc far
+
+ ; at this point es,ds -> PSP
+ ; SS:SP points to stack
+
+ ; first check that we are running under NT
+
+ mov ax, GET_NT_VERSION
+ int 21h
+ cmp bl, NT_MAJOR_VERSION
+ je cdx_chk_more
+ jmp cdx_badver
+cdx_chk_more:
+ cmp bh, NT_MINOR_VERSION
+ je cdx_ver_ok
+ jmp cdx_badver
+
+cdx_ver_ok:
+ ; Now check that this TSR is'nt already installed
+ mov ah,MSCDEX_ID
+ mov al,0bh ; call function 0b
+ int MPX_INT ; int 2f
+
+ cmp bx,0adadh
+ jne cdx_chks_done
+ jmp cdx_installed
+
+cdx_chks_done:
+
+ ; free the env segment
+
+ push es
+ push ds
+ mov es, es:[2ch]
+ mov ah, 49h
+ int 21h
+
+ mov ah, DOS_GET_VECTOR
+ mov al, MPX_INT ; 2f
+ int 21h ; get old vector
+ mov WORD PTR cs:oldint,bx ; save old vector here
+ mov WORD PTR cs:oldint+2,es
+
+ mov dx, offset Int2FHook
+ push cs ; get current code segment
+ pop ds
+ mov ah, DOS_SET_VECTOR
+ mov al, MPX_INT ; vector to hook
+ int 21h ; hook that vector
+
+;
+; Compute size of TSR area
+;
+ pop ds
+ pop es
+ mov dx, offset Init_Fence ; end of fixed TSR code
+ mov cl, 4 ; divide by 16
+ shr dx, cl
+ add dx, 16 ; add in PSP
+;
+; Terminate and stay resident
+;
+ mov ah, DOS_TSR ; TSR
+ mov al, 0
+ int 21h ; TSR
+
+cdx_badver:
+ mov dx, offset Message1
+ push cs
+ pop ds
+ mov ah,09h
+ int 21h
+ jmp short cdx_exit
+
+cdx_installed:
+ mov dx, offset Message2
+ push cs
+ pop ds
+ mov ah,09h
+ int 21h
+
+cdx_exit:
+ mov ax,4c00h ; Exit
+ int 21h
+
+mscdexnt endp
+
+ include messages.inc
+
+_TEXT ends
+
+InitStack segment para stack 'STACK'
+
+ dw 256 dup (?)
+
+top_of_stack equ $
+
+InitStack ends
+
+ end mscdexnt
+
diff --git a/private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.inc b/private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.inc
new file mode 100644
index 000000000..7cee3d55e
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.inc
@@ -0,0 +1,30 @@
+;----------------------------- E Q U A T E S ----------------------------
+DOS_SET_VECTOR equ 25h
+DOS_GET_VECTOR equ 35h
+DOS_TSR equ 31h
+
+MPX_INT equ 2fh
+MSCDEX_ID equ 15h
+MAX_MSCDEX_CMD equ 10h
+
+GET_NT_VERSION equ 3306h
+NT_MAJOR_VERSION equ 05
+NT_MINOR_VERSION equ 50
+
+CR equ 0dh
+LF equ 0Ah
+
+;----------------------------- M A C R O S ------------------------------
+DrvHd MACRO name
+ DD -1
+ DW 0c840h
+ DW 0
+ DW 0
+ DB name
+ dw 0
+ db 0
+ db 1
+ db 10 dup (0)
+ ENDM
+
+
diff --git a/private/mvdm/vdd/samples/mscdex/vdd/makefile b/private/mvdm/vdd/samples/mscdex/vdd/makefile
new file mode 100644
index 000000000..85f1ecf36
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/vdd/makefile
@@ -0,0 +1,11 @@
+# Sample VDD makefile
+#
+# Copyright (c) 1991, Microsoft Corporation
+#
+
+
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/mvdm/vdd/samples/mscdex/vdd/mscdex.h b/private/mvdm/vdd/samples/mscdex/vdd/mscdex.h
new file mode 100644
index 000000000..8d86db882
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/vdd/mscdex.h
@@ -0,0 +1,313 @@
+/*
+ The following definitions were derived from the "CD-ROM Programmer's
+ Guide for MS-DOS CD-ROM Extensions, Version 2.21" January 1992
+ */
+#define MSCDEX_VERSION 0x0215
+
+#define CDSTAT_ERROR 0X8000
+#define CDSTAT_BUSY 0X0200
+#define CDSTAT_DONE 0X0100
+
+#define CDERR_WRITE_PROTECT 0
+#define CDERR_UNKNOWN_UNIT 1
+#define CDERR_NOT_READY 2
+#define CDERR_UNKNOWN_CMD 3
+#define CDERR_CRC 4
+#define CDERR_STRUCT_LENGTH 5
+#define CDERR_SEEK 6
+#define CDERR_UNKNOWN_MEDIA 7
+#define CDERR_SECT_NOTFOUND 8
+#define CDERR_WRITE_FAULT 10
+#define CDERR_READ_FAULT 11
+#define CDERR_GENERAL 12
+#define CDERR_PARAMETER 13 // Per mscdex spec
+#define CDERR_DISK_CHANGE 15
+
+#define DEVICE_INIT 0
+#define IOCTL_READ 3
+#define INPUT_FLUSH 7
+#define OUTPUT_FLUSH 11
+#define IOCTL_WRITE 12
+#define DEVICE_OPEN 13
+#define DEVICE_CLOSE 14
+#define READ_LONG 128
+#define READ_LONG_PREFETCH 130
+#define SEEK 131
+#define PLAY_AUDIO 132
+#define STOP_AUDIO 133
+#define WRITE_LONG 134
+#define WRITE_LONG_VERIFY 135
+#define RESUME_AUDIO 136
+
+
+#define IOCTLR_RADDR 0
+#define IOCTLR_LOCHEAD 1
+#define IOCTLR_ERRSTAT 3
+#define IOCTLR_AUDINFO 4
+#define IOCTLR_DRVBYTES 5
+#define IOCTLR_DEVSTAT 6
+#define IOCTLR_SECTSIZE 7
+#define IOCTLR_VOLSIZE 8
+#define IOCTLR_MEDCHNG 9
+#define IOCTLR_DISKINFO 10
+#define IOCTLR_TNOINFO 11
+#define IOCTLR_QINFO 12
+#define IOCTLR_SUBCHANINFO 13
+#define IOCTLR_UPCCODE 14
+#define IOCTLR_AUDSTAT 15
+
+#define IOCTLW_EJECT 0
+#define IOCTLW_LOCKDOOR 1
+#define IOCTLW_RESETDRV 2
+#define IOCTLW_AUDINFO 3
+#define IOCTLW_DRVBYTES 4
+#define IOCTLW_CLOSETRAY 5
+
+#define MODE_HSG 0
+#define MODE_REDBOOK 1
+
+typedef union _SECTOR_ADDR {
+ BYTE b[4];
+ ULONG dw;
+} SECTOR_ADDR;
+
+
+#pragma pack(1)
+
+typedef struct _REQUESTHEADER {
+ BYTE rhLength;
+ BYTE rhUnit;
+ BYTE rhFunction;
+ WORD rhStatus;
+ BYTE rhReserved[8];
+
+ BYTE irwrData;
+ LPBYTE irwrBuffer;
+ WORD irwrBytes;
+} REQUESTHEADER, *LPREQUESTHEADER;
+
+typedef struct _DEVICE_HEADER {
+ DWORD link;
+ WORD attributes;
+ WORD strategy;
+ WORD interrupt;
+ BYTE name[8];
+ WORD reserved;
+ BYTE drive;
+ BYTE numunits;
+ BYTE reserved2[10];
+} DEVICE_HEADER, *PDEVICE_HEADER;
+
+typedef struct _DRIVE_DEVICE_LIST {
+ BYTE Unit;
+ DWORD DeviceHeader;
+} DRIVE_DEVICE_LIST, *PDRIVE_DEVICE_LIST;
+
+
+typedef struct _IOCTLR_RADDR_BLOCK {
+ BYTE ctlcode; // 0
+ DWORD devheader;
+} IOCTLR_RADDR_BLOCK, *PIOCTLR_RADDR_BLOCK;
+
+
+typedef struct _IOCTLR_LOCHEAD_BLOCK {
+ BYTE ctlcode; // 1
+ BYTE addrmode;
+ SECTOR_ADDR headlocation;
+} IOCTLR_LOCHEAD_BLOCK, *PIOCTLR_LOCHEAD_BLOCK;
+
+
+typedef struct _IOCTLR_ERRSTAT_BLOCK {
+ BYTE ctlcode; // 3
+ BYTE statistics; // array of undefined length
+} IOCTLR_ERRSTAT_BLOCK, *PIOCTLR_ERRSTAT_BLOCK;
+
+
+typedef struct _IOCTLR_AUDINFO_BLOCK {
+ BYTE ctlcode; // 4
+ BYTE chan0;
+ BYTE vol0;
+ BYTE chan1;
+ BYTE vol1;
+ BYTE chan2;
+ BYTE vol2;
+ BYTE chan3;
+ BYTE vol3;
+} IOCTLR_AUDINFO_BLOCK, *PIOCTLR_AUDINFO_BLOCK;
+
+
+typedef struct _IOCTLR_DRVBYTES_BLOCK {
+ BYTE ctlcode; // 5
+ BYTE numbytes;
+ BYTE buffer[128];
+} IOCTLR_DRVBYTES_BLOCK, *PIOCTLR_DRVBYTES_BLOCK;
+
+
+typedef struct _IOCTLR_DEVSTAT_BLOCK {
+ BYTE ctlcode; // 6
+ DWORD devparms;
+} IOCTLR_DEVSTAT_BLOCK, *PIOCTLR_DEVSTAT_BLOCK;
+
+#define DEVSTAT_DOOR_OPEN 0X00000001
+#define DEVSTAT_DOOR_UNLOCKED 0X00000002
+#define DEVSTAT_SUPPORTS_COOKED 0X00000004
+#define DEVSTAT_READ_WRITE 0X00000008
+#define DEVSTAT_PLAYS_AV 0X00000010
+#define DEVSTAT_SUPPORTS_ILEAVE 0X00000020
+#define DEVSTAT_SUPPORTS_PRFTCH 0X00000080
+#define DEVSTAT_SUPPORTS_CHMAN 0X00000100
+#define DEVSTAT_SUPPORTS_RBOOK 0X00000200
+#define DEVSTAT_NO_DISC 0X00000800
+#define DEVSTAT_SUPPORTS_RWSCH 0X00001000
+
+
+typedef struct _IOCTLR_SECTSIZE_BLOCK {
+ BYTE ctlcode; // 7
+ BYTE readmode;
+ WORD sectsize;
+} IOCTLR_SECTSIZE_BLOCK, *PIOCTLR_SECTSIZE_BLOCK;
+
+
+typedef struct _IOCTLR_VOLSIZE_BLOCK {
+ BYTE ctlcode; // 8
+ DWORD size;
+} IOCTLR_VOLSIZE_BLOCK, *PIOCTLR_VOLSIZE_BLOCK;
+
+
+typedef struct _IOCTLR_MEDCHNG_BLOCK {
+ BYTE ctlcode; // 9
+ BYTE medbyte;
+} IOCTLR_MEDCHNG_BLOCK, *PIOCTLR_MEDCHNG_BLOCK;
+
+#define MEDCHNG_NOT_CHANGED 1
+#define MEDCHNG_DONT_KNOW 0
+#define MEDCHNG_CHANGED 0XFF
+
+
+typedef struct _IOCTLR_DISKINFO_BLOCK {
+ BYTE ctlcode; // 10
+ BYTE tracklow;
+ BYTE trackhigh;
+ SECTOR_ADDR startleadout;
+} IOCTLR_DISKINFO_BLOCK, *PIOCTLR_DISKINFO_BLOCK;
+
+
+typedef struct _IOCTLR_TNOINFO_BLOCK {
+ BYTE ctlcode; // 11
+ BYTE trknum;
+ SECTOR_ADDR start;
+ BYTE trkctl;
+} IOCTLR_TNOINFO_BLOCK, *PIOCTLR_TNOINFO_BLOCK;
+
+
+typedef struct _IOCTLR_QINFO_BLOCK {
+ BYTE ctlcode; // 12
+ BYTE ctladr;
+ BYTE trknum;
+ BYTE pointx;
+ BYTE min;
+ BYTE sec;
+ BYTE frame;
+ BYTE zero;
+ BYTE apmin;
+ BYTE apsec;
+ BYTE apframe;
+} IOCTLR_QINFO_BLOCK, *PIOCTLR_QINFO_BLOCK;
+
+
+typedef struct _IOCTLR_SUBCHANINFO_BLOCK {
+ BYTE ctlcode; // 13
+ SECTOR_ADDR startsect;
+ DWORD transaddr;
+ DWORD numsect;
+} IOCTLR_SUBCHANINFO_BLOCK, *PIOCTLR_SUBCHANINFO_BLOCK;
+
+
+typedef struct _IOCTLR_UPCCODE_BLOCK {
+ BYTE ctlcode; // 14
+ BYTE ctladr;
+ BYTE upcean[7];
+ BYTE zero;
+ BYTE aframe;
+} IOCTLR_UPCCODE_BLOCK, *PIOCTLR_UPCCODE_BLOCK;
+
+
+typedef struct _IOCTLR_AUDSTAT_BLOCK {
+ BYTE ctlcode; // 15
+ WORD audstatbits;
+ SECTOR_ADDR startloc;
+ SECTOR_ADDR endloc;
+} IOCTLR_AUDSTAT_BLOCK, *PIOCTLR_AUDSTAT_BLOCK;
+
+#define AUDSTAT_PAUSED 1
+
+
+typedef struct _IOCTLW_LOCKDOOR_BLOCK {
+ BYTE ctlcode; // 1
+ BYTE lockfunc;
+} IOCTLW_LOCKDOOR_BLOCK, *PIOCTLW_LOCKDOOR_BLOCK;
+
+
+typedef struct _IOCTLW_AUDINFO_BLOCK {
+ BYTE ctlcode; // 3
+ BYTE chan0;
+ BYTE vol0;
+ BYTE chan1;
+ BYTE vol1;
+ BYTE chan2;
+ BYTE vol2;
+ BYTE chan3;
+ BYTE vol3;
+} IOCTLW_AUDINFO_BLOCK, *PIOCTLW_AUDINFO_BLOCK;
+
+
+typedef struct _IOCTLW_DRVBYTES_BLOCK {
+ BYTE ctlcode; // 4
+ BYTE buffer;
+} IOCTLW_DRVBYTES_BLOCK, *PIOCTLW_DRVBYTES_BLOCK;
+
+
+
+typedef struct _READ_LONG_BLOCK {
+ BYTE header[13];
+ BYTE addrmode;
+ DWORD transaddr;
+ WORD numsect;
+ SECTOR_ADDR startsect;
+ BYTE readmode;
+ BYTE ileavesize;
+ BYTE ileaveskip;
+} READ_LONG_BLOCK, *PREAD_LONG_BLOCK;
+
+
+typedef struct _SEEK_BLOCK {
+ BYTE header[13];
+ BYTE addrmode;
+ DWORD transaddr;
+ WORD numsect;
+ SECTOR_ADDR startsect;
+} SEEK_BLOCK, *PSEEK_BLOCK;
+
+
+typedef struct _PLAY_AUDIO_BLOCK {
+ BYTE header[13];
+ BYTE addrmode;
+ SECTOR_ADDR startsect;
+ DWORD numsect;
+} PLAY_AUDIO_BLOCK, *PPLAY_AUDIO_BLOCK;
+
+
+typedef struct _WRITE_LONG_BLOCK {
+ BYTE header[13];
+ BYTE addrmode;
+ DWORD transaddr;
+ WORD numsect;
+ SECTOR_ADDR startsect;
+ BYTE readmode;
+ BYTE ileavesize;
+ BYTE ileaveskip;
+} WRITE_LONG_BLOCK, *PWRITE_LONG_BLOCK;
+
+
+#pragma pack()
+
diff --git a/private/mvdm/vdd/samples/mscdex/vdd/sources b/private/mvdm/vdd/samples/mscdex/vdd/sources
new file mode 100644
index 000000000..c9a9f09c5
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/vdd/sources
@@ -0,0 +1,27 @@
+INDENTED_DIRECTIVES=1
+
+MAJORCOMP=vdd
+MINORCOMP=vcdex
+
+TARGETNAME=vcdex
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+TARGETLIBS=$(_NTDRIVE)\nt\public\sdk\lib\*\kernel32.lib \
+ $(_NTDRIVE)\nt\public\sdk\lib\*\user32.lib \
+ $(_NTDRIVE)\nt\public\sdk\lib\*\ntvdm.lib
+
+DLLENTRY=VDDInitialize
+DLLBASE=0x2000000
+
+# we don't use any CRT
+USE_NOLIBS=1
+
+SOURCES=vcdex.c VCDEX.RC
+
+C_DEFINES=-DWIN_32
+
+UMTYPE=windows
+UMTEST=
+UMLIBS=
+UMRES=Obj\*\VCDEX.Res
+
diff --git a/private/mvdm/vdd/samples/mscdex/vdd/vcdex.c b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.c
new file mode 100644
index 000000000..0d7895855
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.c
@@ -0,0 +1,1474 @@
+/*++
+
+Copyright (c) 1991, Microsoft Corporation
+
+Module Name:
+
+ vcdex.c
+
+Abstract:
+
+ Virtual Device Driver for MSCDEX
+
+Environment:
+
+ NT-MVDM (User Mode VDD)
+
+Author:
+
+ Neil Sandlin (neilsa), 3/20/93
+
+Notes:
+
+ Implementation Restrictions-
+
+ Currently, the starting and ending locations returned by the mscdex
+ "audio status info" are not returned by NT drivers. This makes it
+ difficult to maintain these values when the calling applications
+ exit, or when multiple applications are controlling a single drive.
+
+ Currently, this implementation does not validate the length argument of
+ the IOCTL calls. This needs to be added for robustness, but will not
+ affect well-behaved apps.
+
+
+Revision History:
+
+
+
+--*/
+
+//
+// Include files.
+//
+
+#include "windows.h"
+#include "winerror.h"
+#include <vddsvc.h>
+#include <mscdex.h>
+#include "devioctl.h"
+#include "ntddcdrm.h"
+#include "ntdddisk.h"
+#include "vcdex.h"
+
+//
+// Global variables.
+//
+
+PFNSVC apfnSVC [] = {
+ ApiGetNumberOfCDROMDrives,
+ ApiGetCDROMDriveList,
+ ApiGetCopyrightFileName,
+ ApiGetAbstractFileName,
+ ApiGetBDFileName,
+ ApiReadVTOC,
+ ApiReserved,
+ ApiReserved,
+ ApiAbsoluteDiskRead,
+ ApiAbsoluteDiskWrite,
+ ApiReserved,
+ ApiCDROMDriveCheck,
+ ApiMSCDEXVersion,
+ ApiGetCDROMDriveLetters,
+ ApiGetSetVolDescPreference,
+ ApiGetDirectoryEntry,
+ ApiSendDeviceRequest
+};
+
+PDRIVE_INFO DrivePointers[MAXDRIVES];
+PDRIVE_INFO DrvInfo;
+LPREQUESTHEADER VdmReq; // for "send device request"
+USHORT NumDrives = 0, FirstDrive = 0xff;
+DWORD DeviceHeader; // for "get CDROM drive list"
+BYTE LastRealStatus = AUDIO_STATUS_NO_STATUS;
+
+#define IS_DRIVE_CDROM(drive) \
+ (drive < MAXDRIVES && DrivePointers[drive] != NULL)
+
+
+HANDLE hVdd;
+HANDLE hProcessHeap;
+
+
+
+VOID UserBlockHook(VOID);
+VOID UserTerminateHook(USHORT);
+
+
+
+BOOL
+VDDInitialize(
+ HANDLE hDll,
+ DWORD dwReason,
+ LPVOID lpReserved
+ )
+
+/*++
+
+Routine Description:
+
+ The entry point for the Vdd which handles intialization and termination.
+
+Arguments:
+
+ hVdd - The handle to the VDD
+
+ Reason - flag word thatindicates why Dll Entry Point was invoked
+
+ lpReserved - Unused
+
+Return Value:
+ BOOL bRet - if (dwReason == DLL_PROCESS_ATTACH)
+ TRUE - Dll Intialization successful
+ FALSE - Dll Intialization failed
+ else
+ always returns TRUE
+--*/
+
+{
+ int i;
+
+ switch ( dwReason ) {
+
+ case DLL_PROCESS_ATTACH:
+ hVdd = hDll;
+ hProcessHeap = GetProcessHeap();
+
+ DisableThreadLibraryCalls(hDll);
+
+ DebugPrint (DEBUG_MOD, "VCDEX: Process Attach\n");
+ break;
+
+ case DLL_PROCESS_DETACH:
+
+ for (i=0; i<MAXDRIVES; i++)
+
+ if (DrivePointers[i] != NULL) {
+
+ if (DrivePointers[i]->Handle != INVALID_HANDLE_VALUE) {
+ CloseHandle(DrivePointers[i]->Handle);
+ }
+ HeapFree(hProcessHeap, 0, DrivePointers[i]);
+
+ }
+
+ DebugPrint (DEBUG_MOD, "VCDEX: Process Detach\n");
+ break;
+
+ default:
+
+ break;
+
+ }
+
+ return TRUE;
+
+}
+
+
+
+VOID
+VDDRegisterInit(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when the MSCDEXNT TSR makes its RegisterModule
+ call. Most of the initialization is done here instead of in the
+ VDDInitialize routine to improve performance in the case where the
+ VDD is loaded, but not used.
+
+ The main point of this routine is to search for CDROM drives and set
+ up an array of pointers to point to DRIVE_INFO structures. The array
+ is a fixed size array, one for each possible DOS drive. The structures
+ are allocated only if a CDROM drive exists at the corresponding drive
+ letter in the array.
+
+ By doing a CreateFile() to the drive letter of the drive, a handle to
+ the SCSI CDROM class driver is returned. This handle is used for all
+ communication with the drive.
+
+
+
+Return Value:
+
+ SUCCESS - Client carry is clear
+ Client CX = # of CDROM drives
+
+ FAILURE - Client carry is set
+
+--*/
+
+
+{
+ CHAR chRoot [] = "?:\\";
+ USHORT i;
+ HANDLE hDriver;
+ PDRIVE_INFO drvp;
+ static BOOLEAN Initialized = FALSE;
+
+ if (Initialized) {
+ setCF(0);
+ return;
+ }
+
+
+ // Make far pointer with offset zero (DX is para aligned)
+ DeviceHeader = (DWORD) ((getCX() << 16) + (getDX() << 12));
+
+ for (i=0; i<MAXDRIVES; i++) {
+
+ chRoot[0] = i + 'A';
+
+ if (GetDriveType((LPSTR) chRoot) == DRIVE_CDROM) {
+
+ hDriver = OpenPhysicalDrive(i);
+
+ if (hDriver != INVALID_HANDLE_VALUE) {
+
+ drvp = (PDRIVE_INFO)HeapAlloc(hProcessHeap,
+ 0,
+ sizeof(DRIVE_INFO)
+ );
+
+ if(drvp == NULL) {
+ DebugPrint (DEBUG_MOD, "VCDEX: Out of memory on initializetion\n");
+ Initialized = FALSE;
+ setCF(1);
+ return;
+ }
+
+ DrivePointers[i] = drvp;
+ drvp->Handle = hDriver;
+ drvp->DriveNum = i;
+ drvp->ValidVTOC = FALSE;
+ drvp->MediaStatus = MEDCHNG_CHANGED;
+
+ drvp->PlayStart.dw = 0;
+ drvp->PlayCount = 0;
+ GetAudioStatus (drvp);
+
+ NumDrives++;
+ if (FirstDrive == 0xff)
+ FirstDrive = i;
+
+
+ //
+ // Keep the handle close until app really wants to use it
+ //
+ drvp->Handle = INVALID_HANDLE_VALUE;
+ CloseHandle(hDriver);
+
+
+ } else {
+ DrivePointers[i] = NULL;
+ }
+
+ }
+
+ }
+
+ if (NumDrives == 0) {
+
+ setCF(1);
+
+ } else {
+ PDEVICE_HEADER pDev = (PDEVICE_HEADER) GetVDMPointer(
+ ((ULONG)getCX()<<16)+getDX(),
+ 1, FALSE);
+
+ // Put the first valid cdrom drive number in the device header
+ pDev->drive = FirstDrive+1;
+
+ VDDInstallUserHook(hVdd, NULL, UserTerminateHook, UserBlockHook, NULL);
+
+ DebugPrint (DEBUG_MOD, "VCDEX: Initialized\n");
+ Initialized = TRUE;
+
+ setCF(0);
+
+ }
+
+ return;
+}
+
+
+VOID UserTerminateHook(USHORT Pdb)
+{
+ UserBlockHook();
+}
+
+
+VOID UserBlockHook(VOID)
+{
+
+ int DrvNum;
+
+ DrvNum = MAXDRIVES;
+ while (DrvNum--) {
+ if (DrivePointers[DrvNum] &&
+ DrivePointers[DrvNum]->Handle != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle(DrivePointers[DrvNum]->Handle);
+ DrivePointers[DrvNum]->Handle = INVALID_HANDLE_VALUE;
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+VOID
+VDDDispatch(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This is the main MSCDEX api function dispatcher. When this routine
+ is entered, an int2f has just been executed. Client registers are
+ set to what they were at the time of the call with the exception
+ of AX, which must contain a handle for the DispatchCall(). The
+ value of AX was pushed on the stack. So, this routine restores it,
+ and uses AL to index into the function call table apfnSVC[].
+
+
+Return Value:
+
+ SUCCESS - Client carry is clear
+ FAILURE - Client carry is set
+
+--*/
+
+{
+
+ LPWORD VdmWordPtr;
+ WORD VdmAX;
+ ULONG VdmAddress;
+
+ //
+ // The TSR pushes AX on the stack. Pick up the value here
+ //
+
+ VdmAddress = ( getSS() << 16 ) | getSP();
+
+ VdmWordPtr = (LPWORD) GetVDMPointer ( VdmAddress, 2, FALSE);
+
+ VdmAX = *VdmWordPtr;
+
+ //
+ // AL has the MSCDEX function code
+ //
+ setAX (VdmAX); //restore AX
+
+ (apfnSVC [VdmAX & 0xFF])();
+
+ return;
+}
+
+
+
+/****************************************************************************
+
+ MSCDEX API SUBROUTINES
+
+ The following routines perform the individual functions specified by
+ the MSCDEX extensions.
+
+
+ ****************************************************************************/
+VOID
+ApiReserved(
+ VOID
+ )
+
+{
+
+ DebugFmt (DEBUG_API, "VCDEX: Reserved Function call, ax=%.4X\n", getAX());
+
+}
+
+
+VOID
+ApiGetNumberOfCDROMDrives(
+ VOID
+ )
+
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Get # of CDROM drives\n");
+
+ setBX (NumDrives);
+
+ if (NumDrives)
+ setCX (FirstDrive);
+
+}
+
+
+VOID
+ApiGetCDROMDriveList(
+ VOID
+ )
+
+{
+
+ PDRIVE_DEVICE_LIST devlist, devlist0;
+ ULONG VdmAddress;
+ USHORT Drive;
+ BYTE Unit;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get CDROM drive list\n");
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ devlist = devlist0 = (PDRIVE_DEVICE_LIST) GetVDMPointer (VdmAddress,
+ MAXDRIVES*sizeof(DRIVE_DEVICE_LIST),
+ FALSE);
+
+ for (Drive=0, Unit=0; Drive<MAXDRIVES; Drive++)
+ if (DrivePointers[Drive] != NULL) {
+ devlist->Unit = Unit;
+ devlist->DeviceHeader = DeviceHeader;
+ devlist++;
+ Unit++;
+ }
+
+ FreeVDMPointer (VdmAddress,
+ MAXDRIVES*sizeof(DRIVE_DEVICE_LIST),
+ devlist0,
+ FALSE);
+
+
+}
+
+VOID
+ApiGetCopyrightFileName(
+ VOID
+ )
+{
+ ULONG VdmAddress;
+ LPBYTE fnBuffer;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get Copyright File Name\n");
+
+ if (!IS_DRIVE_CDROM(getCX())) { // Is drive CDROM?
+ setAX (15); // no
+ setCF (1);
+ }
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ fnBuffer = (LPBYTE) GetVDMPointer (VdmAddress, 38, FALSE);
+
+ *fnBuffer = 0; // currently not implemented
+
+ FreeVDMPointer (VdmAddress, 38, fnBuffer, FALSE);
+
+}
+
+VOID
+ApiGetAbstractFileName(
+ VOID
+ )
+{
+
+ ULONG VdmAddress;
+ LPBYTE fnBuffer;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get Abstract File Name\n");
+
+ if (!IS_DRIVE_CDROM(getCX())) { // Is drive CDROM?
+ setAX (15); // no
+ setCF (1);
+ }
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ fnBuffer = (LPBYTE) GetVDMPointer (VdmAddress, 38, FALSE);
+
+ *fnBuffer = 0; // currently not implemented
+
+ FreeVDMPointer (VdmAddress, 38, fnBuffer, FALSE);
+
+}
+
+
+VOID
+ApiGetBDFileName(
+ VOID
+ )
+{
+
+ ULONG VdmAddress;
+ LPBYTE fnBuffer;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get Bibliographic Doc File Name\n");
+
+ if (!IS_DRIVE_CDROM(getCX())) { // Is drive CDROM?
+ setAX (15); // no
+ setCF (1);
+ }
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ fnBuffer = (LPBYTE) GetVDMPointer (VdmAddress, 38, FALSE);
+
+ *fnBuffer = 0; // currently not implemented
+
+ FreeVDMPointer (VdmAddress, 38, fnBuffer, FALSE);
+
+}
+
+VOID
+ApiReadVTOC(
+ VOID
+ )
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Read VTOC\n");
+ setCF(1); // currently not implemented
+
+}
+
+
+
+VOID
+ApiAbsoluteDiskRead(
+ VOID
+ )
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Absolute Disk Read\n");
+ setCF(1); // currently not implemented
+
+}
+
+VOID
+ApiAbsoluteDiskWrite(
+ VOID
+ )
+{
+ DebugPrint (DEBUG_API, "VCDEX: Absolute Disk Write\n");
+ setCF(1); // read only
+}
+
+
+VOID
+ApiCDROMDriveCheck(
+ VOID
+ )
+
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: CDROM drive check\n");
+
+ setBX (0xADAD); // MSCDEX Signature
+
+ if (IS_DRIVE_CDROM(getCX())) // is CD ROM drive
+ setAX (1); // yes
+ else
+ setAX (0); // no
+
+}
+
+VOID
+ApiMSCDEXVersion(
+ VOID
+ )
+
+{
+ DebugPrint (DEBUG_API, "VCDEX: MSCDEX Version\n");
+ setBX (MSCDEX_VERSION); // MSCDEX Version #
+
+}
+
+VOID
+ApiGetCDROMDriveLetters(
+ VOID
+ )
+
+{
+ ULONG VdmAddress;
+ LPBYTE VdmPtr, VdmPtr0;
+ USHORT Drive;
+
+ DebugPrint (DEBUG_API, "VCDEX: Get CDROM Drive Letters\n");
+
+ VdmAddress = (getES() << 16) | getBX();
+ VdmPtr = VdmPtr0 = (LPBYTE) GetVDMPointer (VdmAddress, MAXDRIVES, FALSE);
+
+ for (Drive=0; Drive<MAXDRIVES; Drive++)
+ if (DrivePointers[Drive] != NULL)
+ *VdmPtr++ = (BYTE) Drive;
+
+ FreeVDMPointer (VdmAddress, MAXDRIVES, VdmPtr0, FALSE);
+
+}
+
+
+VOID
+ApiGetSetVolDescPreference(
+ VOID
+ )
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Set Volume Descriptor Preference\n");
+ setCF(1); // currently not implemented
+
+}
+
+VOID
+ApiGetDirectoryEntry(
+ VOID
+ )
+{
+
+ DebugPrint (DEBUG_API, "VCDEX: Get Directory Entry\n");
+ setCF(1); // currently not implemented
+
+}
+
+
+
+VOID
+ApiSendDeviceRequest(
+ VOID
+ )
+{
+
+ ULONG VdmAddress;
+ BOOL Success;
+ DWORD BytesReturned;
+ DWORD absStart, absEnd;
+ int DrvNum;
+
+ VdmAddress = ( getES() << 16 ) | getBX();
+ VdmReq = (LPREQUESTHEADER) GetVDMPointer (VdmAddress,
+ sizeof (REQUESTHEADER),
+ FALSE);
+
+
+ DebugFmt (DEBUG_IO, ">RQ %d ", (DWORD) VdmReq->rhFunction);
+
+ DrvNum = getCX();
+
+ if (!IS_DRIVE_CDROM(DrvNum)) {
+ VdmReq->rhStatus = CDSTAT_ERROR | CDSTAT_DONE | CDERR_UNKNOWN_UNIT;
+ return;
+
+ }
+
+ DrvInfo = DrivePointers[DrvNum];
+
+ if (DrvInfo->Handle == INVALID_HANDLE_VALUE) {
+ DrvInfo->Handle = OpenPhysicalDrive(DrvInfo->DriveNum);
+ if (DrvInfo->Handle == INVALID_HANDLE_VALUE) {
+ VdmReq->rhStatus = CDSTAT_ERROR | CDSTAT_DONE | CDERR_UNKNOWN_UNIT;
+ HeapFree(hProcessHeap, 0, DrvInfo);
+ DrivePointers[DrvNum] = NULL;
+ NumDrives--;
+ if (FirstDrive == DrvNum) {
+ FirstDrive = 0xff;
+ while (++DrvNum < MAXDRIVES) {
+ if (DrivePointers[DrvNum]) {
+ FirstDrive = DrvNum;
+ break;
+ }
+ }
+ }
+
+ return;
+ }
+ }
+
+
+ GetAudioStatus (DrvInfo);
+
+ if (DrvInfo->Playing)
+ VdmReq->rhStatus |= CDSTAT_BUSY;
+
+ switch (VdmReq->rhFunction) {
+
+ case IOCTL_READ:
+
+ IOCTLRead();
+
+ break;
+
+ case IOCTL_WRITE:
+
+ IOCTLWrite();
+
+ break;
+
+ case INPUT_FLUSH:
+ case OUTPUT_FLUSH:
+ case DEVICE_OPEN:
+ case DEVICE_CLOSE:
+ case READ_LONG:
+ case READ_LONG_PREFETCH:
+ case SEEK:
+ DebugPrint (DEBUG_API, "Unsupported MSCDEX Device Request\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ break;
+
+ case PLAY_AUDIO: {
+
+ CDROM_PLAY_AUDIO_MSF PlayAudioMSF;
+ PPLAY_AUDIO_BLOCK playreq = (PPLAY_AUDIO_BLOCK) VdmReq;
+
+ if (playreq->addrmode == MODE_HSG) {
+
+ absStart = playreq->startsect.dw;
+ PlayAudioMSF.StartingM = (BYTE) (absStart / (75 * 60));
+ PlayAudioMSF.StartingS = (BYTE) ((absStart / 75) % 60);
+ PlayAudioMSF.StartingF = (BYTE) (absStart % 75);
+
+ } else if (playreq->addrmode == MODE_REDBOOK) {
+
+ PlayAudioMSF.StartingM = playreq->startsect.b[2];
+ PlayAudioMSF.StartingS = playreq->startsect.b[1];
+ PlayAudioMSF.StartingF = playreq->startsect.b[0];
+
+ absStart = (PlayAudioMSF.StartingM * 75 * 60) +
+ (PlayAudioMSF.StartingS * 75) +
+ (PlayAudioMSF.StartingF);
+ } else {
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_PARAMETER;
+ break;
+
+ }
+
+ absEnd = absStart + playreq->numsect - 1;
+
+ PlayAudioMSF.EndingM = (BYTE) (absEnd / (75 * 60));
+ PlayAudioMSF.EndingS = (BYTE) ((absEnd / 75) % 60);
+ PlayAudioMSF.EndingF = (BYTE) (absEnd % 75);
+
+ DebugPrint (DEBUG_IO, "Play ");
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_PLAY_AUDIO_MSF,
+ (LPVOID) &PlayAudioMSF,
+ sizeof (CDROM_PLAY_AUDIO_MSF),
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+
+ ProcessError (DrvInfo, PLAY_AUDIO,0);
+
+ else {
+
+ DrvInfo->Playing = TRUE;
+ DrvInfo->Paused = FALSE;
+ DrvInfo->PlayStart.dw = playreq->startsect.dw;
+ DrvInfo->PlayCount = playreq->numsect;
+
+ }
+
+ break;
+ }
+
+ case STOP_AUDIO:
+
+ if (DrvInfo->Playing) {
+
+ DebugPrint (DEBUG_IO, "Pause ");
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_PAUSE_AUDIO,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+
+ ProcessError (DrvInfo, STOP_AUDIO,0);
+
+ else {
+ DrvInfo->Playing = FALSE;
+ DrvInfo->Paused = TRUE;
+ }
+
+ } else {
+
+ DebugPrint (DEBUG_IO, "Stop ");
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_STOP_AUDIO,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ // Fake out GetAudioStatus to simulate stop
+ DrvInfo->Playing = FALSE;
+ DrvInfo->Paused = FALSE;
+ LastRealStatus = AUDIO_STATUS_PLAY_COMPLETE;
+
+ if (!Success) {
+ DWORD dwErr;
+
+ dwErr = GetLastError();
+ if (dwErr == ERROR_MR_MID_NOT_FOUND ||
+ dwErr == ERROR_NO_MEDIA_IN_DRIVE )
+ {
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ }
+
+ break;
+
+ case WRITE_LONG:
+ case WRITE_LONG_VERIFY:
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_WRITE_PROTECT;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ break;
+
+ case RESUME_AUDIO:
+
+ if (DrvInfo->Paused) {
+
+ DebugPrint (DEBUG_IO, "Resume ");
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_RESUME_AUDIO,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+
+ ProcessError (DrvInfo, RESUME_AUDIO,0);
+
+ } else {
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_GENERAL;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+
+ break;
+
+ default:
+ DebugPrint (DEBUG_API, "Invalid MSCDEX Device Request\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ }
+
+ VdmReq->rhStatus |= CDSTAT_DONE;
+
+ DebugFmt (DEBUG_IO, ": %.4X ", VdmReq->rhStatus);
+
+}
+
+VOID
+IOCTLRead(
+ VOID
+ )
+
+{
+
+ LPBYTE Buffer;
+ BOOL Success;
+ DWORD BytesReturned;
+
+ Buffer = GetVDMPointer ((ULONG)VdmReq->irwrBuffer, 16, FALSE);
+
+ DebugFmt (DEBUG_IO, "iord %d ", (DWORD) *Buffer);
+
+ switch (*Buffer) {
+
+ case IOCTLR_AUDINFO: {
+
+ PIOCTLR_AUDINFO_BLOCK audinfo = (PIOCTLR_AUDINFO_BLOCK) Buffer;
+ VOLUME_CONTROL VolumeControl;
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_GET_VOLUME,
+ (LPVOID) NULL, 0,
+ (LPVOID) &VolumeControl,
+ sizeof (VOLUME_CONTROL),
+ &BytesReturned, (LPVOID) NULL);
+
+ if (Success) {
+
+ // no support for input=>output channel manipulation
+ audinfo->chan0 = 0;
+ audinfo->chan1 = 1;
+ audinfo->chan2 = 2;
+ audinfo->chan3 = 3;
+
+ audinfo->vol0 = VolumeControl.PortVolume[0];
+ audinfo->vol1 = VolumeControl.PortVolume[1];
+ audinfo->vol2 = VolumeControl.PortVolume[2];
+ audinfo->vol3 = VolumeControl.PortVolume[3];
+
+ } else {
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+
+ break;
+ }
+
+ case IOCTLR_DEVSTAT: {
+
+ PIOCTLR_DEVSTAT_BLOCK devstat = (PIOCTLR_DEVSTAT_BLOCK) Buffer;
+
+ devstat->devparms = DEVSTAT_DOOR_UNLOCKED |
+ DEVSTAT_SUPPORTS_RBOOK;
+
+
+ if (!DrvInfo->StatusAvailable) {
+
+ DrvInfo->MediaStatus = MEDCHNG_CHANGED;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ switch (DrvInfo->LastError) {
+ case ERROR_NO_MEDIA_IN_DRIVE:
+ devstat->devparms |= DEVSTAT_NO_DISC |
+ DEVSTAT_DOOR_OPEN;
+
+ DebugFmt (DEBUG_STATUS, ":%.4X ", devstat->devparms);
+
+ break;
+ //BUGBUG case for recently inserted (80000016) - see below
+ }
+
+ break;
+ }
+
+ if (!(DrvInfo->current.Control & AUDIO_DATA_TRACK))
+ devstat->devparms |= DEVSTAT_PLAYS_AV;
+
+ break;
+ }
+
+ case IOCTLR_VOLSIZE: {
+
+ PIOCTLR_VOLSIZE_BLOCK volsize = (PIOCTLR_VOLSIZE_BLOCK) Buffer;
+ PTRACK_DATA Track;
+ PCDROM_TOC cdromtoc;
+
+ if ((cdromtoc = ReadTOC (DrvInfo))!=NULL) {
+
+ Track = &cdromtoc->TrackData[cdromtoc->LastTrack];
+
+ volsize->size = (DWORD) ( (Track->Address[1]*60*75) +
+ (Track->Address[2]*75) +
+ Track->Address[3] );
+
+ }
+ break;
+ }
+
+ case IOCTLR_MEDCHNG: {
+
+ PIOCTLR_MEDCHNG_BLOCK medptr = (PIOCTLR_MEDCHNG_BLOCK) Buffer;
+ BYTE status = DrvInfo->MediaStatus;
+
+ if (status == MEDCHNG_NOT_CHANGED) {
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_CHECK_VERIFY,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (Success)
+
+ medptr->medbyte = MEDCHNG_NOT_CHANGED;
+
+ else {
+
+ medptr->medbyte = MEDCHNG_CHANGED;
+ DrvInfo->MediaStatus = MEDCHNG_CHANGED;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+
+ } else
+ medptr->medbyte = DrvInfo->MediaStatus;
+
+ break;
+ }
+
+ case IOCTLR_DISKINFO: {
+
+ PIOCTLR_DISKINFO_BLOCK diskinfo = (PIOCTLR_DISKINFO_BLOCK) Buffer;
+ PTRACK_DATA Track;
+ PCDROM_TOC cdromtoc;
+
+ if ((cdromtoc = ReadTOC (DrvInfo))!=NULL) {
+ diskinfo->tracklow = cdromtoc->FirstTrack;
+ diskinfo->trackhigh = cdromtoc->LastTrack;
+
+ Track = &cdromtoc->TrackData[cdromtoc->LastTrack];
+
+ diskinfo->startleadout.b[0] = Track->Address[3];
+ diskinfo->startleadout.b[1] = Track->Address[2];
+ diskinfo->startleadout.b[2] = Track->Address[1];
+ diskinfo->startleadout.b[3] = Track->Address[0];
+
+ } else {
+
+ // zeroes apparently needed when not there physically
+ diskinfo->tracklow = 0;
+ diskinfo->trackhigh = 0;
+ diskinfo->startleadout.dw = 0;
+
+ }
+ break;
+ }
+
+ case IOCTLR_TNOINFO: {
+
+ PIOCTLR_TNOINFO_BLOCK tnoinfo = (PIOCTLR_TNOINFO_BLOCK) Buffer;
+ PTRACK_DATA Track;
+ PCDROM_TOC cdromtoc;
+
+ if ((cdromtoc = ReadTOC (DrvInfo))!=NULL) {
+
+ if (tnoinfo->trknum > cdromtoc->LastTrack) {
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_SECT_NOTFOUND;
+ break;
+ }
+
+ Track = &cdromtoc->TrackData[tnoinfo->trknum-1];
+ tnoinfo->start.b[0] = Track->Address[3];
+ tnoinfo->start.b[1] = Track->Address[2];
+ tnoinfo->start.b[2] = Track->Address[1];
+ tnoinfo->start.b[3] = Track->Address[0];
+
+ tnoinfo->trkctl = Track->Control;
+ }
+
+ break;
+ }
+
+ case IOCTLR_QINFO: {
+
+ PIOCTLR_QINFO_BLOCK qinfo = (PIOCTLR_QINFO_BLOCK) Buffer;
+
+ if (DrvInfo->StatusAvailable) {
+
+ qinfo->ctladr = DrvInfo->current.Control | DrvInfo->current.ADR<<4;
+ qinfo->trknum = DrvInfo->current.TrackNumber;
+ qinfo->pointx = DrvInfo->current.IndexNumber;
+ qinfo->min = DrvInfo->current.TrackRelativeAddress[1];
+ qinfo->sec = DrvInfo->current.TrackRelativeAddress[2];
+ qinfo->frame = DrvInfo->current.TrackRelativeAddress[3];
+
+ qinfo->zero = DrvInfo->current.AbsoluteAddress[0];
+ qinfo->apmin = DrvInfo->current.AbsoluteAddress[1];
+ qinfo->apsec = DrvInfo->current.AbsoluteAddress[2];
+ qinfo->apframe = DrvInfo->current.AbsoluteAddress[3];
+
+ } else {
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ }
+
+ break;
+ }
+
+ case IOCTLR_UPCCODE: {
+
+ PIOCTLR_UPCCODE_BLOCK upccode = (PIOCTLR_UPCCODE_BLOCK) Buffer;
+ SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog;
+ static CDROM_SUB_Q_DATA_FORMAT subqfmt = {IOCTL_CDROM_MEDIA_CATALOG};
+ int i;
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_READ_Q_CHANNEL,
+ (LPVOID) &subqfmt,
+ sizeof (CDROM_SUB_Q_DATA_FORMAT),
+ (LPVOID) &MediaCatalog,
+ sizeof (SUB_Q_MEDIA_CATALOG_NUMBER),
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+
+ ProcessError (DrvInfo, IOCTL_READ, IOCTLR_UPCCODE);
+
+ else {
+
+ if (MediaCatalog.Mcval) {
+
+ // The author is uncertain that this is the correct method,
+ // but it appears to work empirically.
+ for (i=0; i<7; i++)
+ upccode->upcean[i] = MediaCatalog.MediaCatalog[i];
+
+ } else
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_SECT_NOTFOUND;
+
+ }
+
+ break;
+ }
+
+ case IOCTLR_AUDSTAT: {
+ PIOCTLR_AUDSTAT_BLOCK audstat = (PIOCTLR_AUDSTAT_BLOCK) Buffer;
+
+ audstat->audstatbits = 0;
+
+ if (DrvInfo->Paused)
+ audstat->audstatbits |= AUDSTAT_PAUSED;
+
+ audstat->startloc.dw = DrvInfo->PlayStart.dw;
+ audstat->endloc.dw = DrvInfo->PlayCount;
+
+ break;
+ }
+
+
+ case IOCTLR_RADDR:
+ case IOCTLR_LOCHEAD:
+ case IOCTLR_ERRSTAT:
+ case IOCTLR_DRVBYTES:
+ case IOCTLR_SECTSIZE:
+ case IOCTLR_SUBCHANINFO:
+ DebugPrint (DEBUG_API, "Unsupported MSCDEX IOCTL Read\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ break;
+
+ default:
+ DebugPrint (DEBUG_API, "Invalid MSCDEX IOCTL Read\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ }
+}
+
+VOID
+IOCTLWrite(
+ VOID
+ )
+
+{
+ LPBYTE Buffer;
+ BOOL Success;
+ DWORD BytesReturned;
+
+ Buffer = GetVDMPointer ((ULONG)VdmReq->irwrBuffer, 16, FALSE);
+
+ DebugFmt (DEBUG_IO, "iowt %d ", (DWORD) *Buffer);
+
+ switch (*Buffer) {
+
+ case IOCTLW_EJECT:
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_EJECT_MEDIA,
+ (LPVOID) NULL, 0,
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+ ProcessError (DrvInfo, IOCTL_WRITE, IOCTLW_EJECT);
+ break;
+
+ case IOCTLW_LOCKDOOR: {
+
+ PREVENT_MEDIA_REMOVAL MediaRemoval;
+ PIOCTLW_LOCKDOOR_BLOCK lockdoor = (PIOCTLW_LOCKDOOR_BLOCK) Buffer;
+
+ MediaRemoval.PreventMediaRemoval = (BOOLEAN) lockdoor->lockfunc;
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_MEDIA_REMOVAL,
+ (LPVOID) &MediaRemoval,
+ sizeof(PREVENT_MEDIA_REMOVAL),
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+ ProcessError (DrvInfo, IOCTL_WRITE, IOCTLW_LOCKDOOR);
+ break;
+ }
+
+ case IOCTLW_AUDINFO: {
+ PIOCTLW_AUDINFO_BLOCK audinfo = (PIOCTLW_AUDINFO_BLOCK) Buffer;
+ VOLUME_CONTROL VolumeControl;
+
+ // note: no support for input=>output channel manipulation
+ VolumeControl.PortVolume[0] = audinfo->vol0;
+ VolumeControl.PortVolume[1] = audinfo->vol1;
+ VolumeControl.PortVolume[2] = audinfo->vol2;
+ VolumeControl.PortVolume[3] = audinfo->vol3;
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_SET_VOLUME,
+ (LPVOID) &VolumeControl,
+ sizeof (VOLUME_CONTROL),
+ (LPVOID) NULL, 0,
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success)
+ ProcessError (DrvInfo, IOCTL_WRITE, IOCTLW_AUDINFO);
+ break;
+ }
+
+
+
+ case IOCTLW_RESETDRV:
+ case IOCTLW_DRVBYTES:
+ case IOCTLW_CLOSETRAY:
+ DebugPrint (DEBUG_API, "Unsupported MSCDEX IOCTL Write\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+ break;
+
+ default:
+ DebugPrint (DEBUG_API, "Invalid MSCDEX IOCTL Write\n");
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_UNKNOWN_CMD;
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ }
+
+
+}
+
+
+/**************************************************************************
+
+ INTERNAL UTILITY ROUTINES
+
+ **************************************************************************/
+
+PCDROM_TOC
+ReadTOC (
+ PDRIVE_INFO DrvInfo
+ )
+/*++
+
+Routine Description:
+
+ Because several MSCDEX functions return information that is in the
+ Volume Table Of Contents (VTOC), this routine is called to cache
+ the TOC in the DRIVE_INFO structure. Subsequent operations that
+ request information from the VTOC will not have to get it from
+ the drive.
+
+Return Value:
+
+ DWORD value from GetLastError()
+
+--*/
+
+{
+ BOOL Success = TRUE;
+ DWORD BytesReturned;
+
+ if ((DrvInfo->ValidVTOC) &&
+ (DrvInfo->MediaStatus == MEDCHNG_NOT_CHANGED))
+ return(&DrvInfo->VTOC);
+
+ Success = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_READ_TOC,
+ (LPVOID) NULL, 0,
+ (LPVOID) &DrvInfo->VTOC, sizeof (CDROM_TOC),
+ &BytesReturned, (LPVOID) NULL);
+
+ if (!Success) {
+ DrvInfo->ValidVTOC = FALSE;
+ ProcessError (DrvInfo, 0, 0);
+ return (NULL);
+ }
+
+ DrvInfo->ValidVTOC = TRUE;
+ DrvInfo->MediaStatus = MEDCHNG_NOT_CHANGED;
+ return (&DrvInfo->VTOC);
+
+
+}
+
+BOOLEAN
+GetAudioStatus(
+ PDRIVE_INFO DrvInfo
+ )
+
+/*++
+
+ Because the AudioStatus byte does not statically reflect the difference
+ between paused and stopped, we have to try to watch for the transition
+ from one state to another to keep track of it.
+
+--*/
+{
+
+ static CDROM_SUB_Q_DATA_FORMAT subqfmt = {IOCTL_CDROM_CURRENT_POSITION};
+ DWORD BytesReturned;
+ BYTE AudStat;
+
+ DrvInfo->Paused = FALSE;
+ DrvInfo->Playing = FALSE;
+
+ DrvInfo->StatusAvailable = DeviceIoControl (DrvInfo->Handle,
+ (DWORD) IOCTL_CDROM_READ_Q_CHANNEL,
+ (LPVOID) &subqfmt,
+ sizeof (CDROM_SUB_Q_DATA_FORMAT),
+ (LPVOID) &DrvInfo->current,
+ sizeof (SUB_Q_CURRENT_POSITION),
+ &BytesReturned, (LPVOID) NULL);
+
+ if (DrvInfo->StatusAvailable) {
+
+ AudStat = DrvInfo->current.Header.AudioStatus;
+
+ DebugFmt (DEBUG_STATUS, "+%.2X ", AudStat);
+
+ switch (AudStat) {
+
+ case AUDIO_STATUS_IN_PROGRESS:
+
+ DrvInfo->Paused = FALSE;
+ DrvInfo->Playing = TRUE;
+ LastRealStatus = AudStat;
+ break;
+
+ case AUDIO_STATUS_PAUSED:
+
+ if (LastRealStatus == AUDIO_STATUS_IN_PROGRESS) {
+
+ DrvInfo->Playing = FALSE;
+ DrvInfo->Paused = TRUE;
+
+ }
+ break;
+
+ case AUDIO_STATUS_PLAY_ERROR:
+ case AUDIO_STATUS_PLAY_COMPLETE:
+
+ DrvInfo->Paused = FALSE;
+ DrvInfo->Playing = FALSE;
+ LastRealStatus = AudStat;
+ break;
+
+ }
+
+ } else {
+ DrvInfo->LastError = GetLastError();
+ }
+
+ return (DrvInfo->StatusAvailable);
+
+}
+
+
+DWORD
+ProcessError(
+ PDRIVE_INFO DrvInfo,
+ USHORT Command,
+ USHORT Subcmd
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when a DeviceIoControl() fails. The extended
+ error code is retrieved, and status bits are set according to the
+ operation that was in progress.
+
+ The DriveInfo Handle is closed
+
+
+ BUGBUG: At the time of this writing, NT fails to remap status 80000016,
+ (when a disc is re-inserted into the drive). So the case for MID_NOT_FOUND
+ was added below. This should be changed to the appropriate code when
+ this error status is properly mapped.
+
+
+Return Value:
+
+ DWORD value from GetLastError()
+
+--*/
+
+
+{
+ DWORD err;
+
+ err = GetLastError();
+
+ DebugFmt (DEBUG_ERROR, "Err! %d, ", Command);
+ DebugFmt (DEBUG_ERROR, "%d: ", Subcmd);
+ DebugFmt (DEBUG_ERROR, "%.8X\n", err);
+
+ switch (err) {
+
+ case ERROR_MR_MID_NOT_FOUND: // To cover unmapped 80000016
+ case ERROR_NO_MEDIA_IN_DRIVE:
+
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_NOT_READY;
+ DrvInfo->MediaStatus = MEDCHNG_CHANGED;
+ break;
+
+ default:
+ VdmReq->rhStatus = CDSTAT_ERROR | CDERR_GENERAL;
+
+ }
+
+ CloseHandle(DrvInfo->Handle);
+ DrvInfo->Handle = INVALID_HANDLE_VALUE;
+
+ return (err);
+
+}
+
+
+
+HANDLE
+OpenPhysicalDrive(
+ int DriveNum
+ )
+/*++
+
+Routine Description:
+
+ int DriveNum; Zero based (0 = A, 1 = B, 2 = C ...)
+
+Return Value:
+
+ HANDLE Drive Handle as returned from CreateFile
+
+--*/
+{
+ HANDLE hDrive;
+ CHAR chDrive [] = "\\\\.\\?:";
+
+ chDrive[4] = DriveNum + 'A';
+
+ hDrive = CreateFile (chDrive,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ (LPSECURITY_ATTRIBUTES) NULL,
+ OPEN_EXISTING,
+ 0,
+ (HANDLE) NULL);
+
+
+ return hDrive;
+}
diff --git a/private/mvdm/vdd/samples/mscdex/vdd/vcdex.def b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.def
new file mode 100644
index 000000000..7ce282195
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.def
@@ -0,0 +1,8 @@
+LIBRARY VCDEX
+
+DESCRIPTION 'MSCDEX Translation VDD for NT-MVDM'
+
+EXPORTS
+ VDDInitialize
+ VDDRegisterInit
+ VDDDispatch
diff --git a/private/mvdm/vdd/samples/mscdex/vdd/vcdex.h b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.h
new file mode 100644
index 000000000..db8633b5f
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.h
@@ -0,0 +1,91 @@
+
+#define MAXDRIVES 26
+
+typedef VOID (*PFNSVC)(VOID);
+
+typedef struct _DRIVE_INFO {
+ HANDLE Handle;
+ USHORT DriveNum;
+ USHORT LogicalBlocksPerSecond;
+ BOOLEAN Playing;
+ BOOLEAN Paused;
+ BOOLEAN ValidVTOC;
+ BOOLEAN StatusAvailable;
+ DWORD LastError;
+ BYTE MediaStatus;
+ SECTOR_ADDR PlayStart; //BUGBUG zero on reset, new disc, play complete
+ DWORD PlayCount;
+ SUB_Q_CURRENT_POSITION current;
+ CDROM_TOC VTOC;
+} DRIVE_INFO, *PDRIVE_INFO;
+
+
+VOID ApiReserved (VOID);
+VOID ApiGetNumberOfCDROMDrives (VOID);
+VOID ApiGetCDROMDriveList (VOID);
+VOID ApiGetCopyrightFileName (VOID);
+VOID ApiGetAbstractFileName (VOID);
+VOID ApiGetBDFileName (VOID);
+VOID ApiReadVTOC (VOID);
+VOID ApiAbsoluteDiskRead (VOID);
+VOID ApiAbsoluteDiskWrite (VOID);
+VOID ApiCDROMDriveCheck (VOID);
+VOID ApiMSCDEXVersion (VOID);
+VOID ApiGetCDROMDriveLetters (VOID);
+VOID ApiGetSetVolDescPreference (VOID);
+VOID ApiGetDirectoryEntry (VOID);
+VOID ApiSendDeviceRequest (VOID);
+VOID IOCTLRead (VOID);
+VOID IOCTLWrite (VOID);
+
+PCDROM_TOC ReadTOC (PDRIVE_INFO DrvInfo);
+BOOLEAN GetAudioStatus (PDRIVE_INFO DrvInfo);
+
+DWORD
+ProcessError(
+ PDRIVE_INFO DrvInfo,
+ USHORT Command,
+ USHORT Subcmd
+ );
+
+HANDLE
+OpenPhysicalDrive(
+ int DriveNum
+ );
+
+
+
+
+#define DEBUG_MOD 0x01
+#define DEBUG_API 0x02
+#define DEBUG_IO 0x04
+#define DEBUG_STATUS 0x08
+#define DEBUG_ERROR 0x80
+
+#ifdef DEBUG
+
+USHORT DebugLevel = 0;
+
+#define DebugPrint(LEVEL,STRING) \
+ { \
+ if (DebugLevel & LEVEL) \
+ OutputDebugString (STRING); \
+ }
+
+#define DebugFmt(LEVEL,STRING, PARM) \
+ { \
+ char szBuffer[80]; \
+ if (DebugLevel & LEVEL) { \
+ sprintf (szBuffer, STRING, PARM); \
+ OutputDebugString (szBuffer); \
+ } \
+ }
+
+#else
+
+#define DebugPrint(LEVEL,STRING) {}
+#define DebugFmt(LEVEL,STRING,PARM) {}
+
+#endif
+
+
diff --git a/private/mvdm/vdd/samples/mscdex/vdd/vcdex.rc b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.rc
new file mode 100644
index 000000000..f338bb2b1
--- /dev/null
+++ b/private/mvdm/vdd/samples/mscdex/vdd/vcdex.rc
@@ -0,0 +1,48 @@
+/*
+** VCDEX.RC
+**
+** Ntverp.h defines several global values that don't need to be
+** changed except for official releases such as betas, sdk updates, etc.
+**
+** Common.ver has the actual version resource structure that all these
+** #defines eventually initialize.
+*/
+
+#include <windows.h>
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* the following lines are specific to this file */
+/*-----------------------------------------------*/
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+#define VER_FILETYPE VFT_DLL
+/* possible values: VFT_UNKNOWN
+ VFT_APP
+ VFT_DLL
+ VFT_DRV
+ VFT_FONT
+ VFT_VXD
+ VFT_STATIC_LIB
+*/
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+/* possible values VFT2_UNKNOWN
+ VFT2_DRV_PRINTER
+ VFT2_DRV_KEYBOARD
+ VFT2_DRV_LANGUAGE
+ VFT2_DRV_DISPLAY
+ VFT2_DRV_MOUSE
+ VFT2_DRV_NETWORK
+ VFT2_DRV_SYSTEM
+ VFT2_DRV_INSTALLABLE
+ VFT2_DRV_SOUND
+ VFT2_DRV_COMM
+*/
+#define VER_FILEDESCRIPTION_STR "32-bit MSCDEX Virtual Device Driver"
+#define VER_INTERNALNAME_STR "VCDEX.DLL"
+#define VER_ORIGINALFILENAME_STR "VCDEX.DLL"
+
+#include "common.ver"
diff --git a/private/mvdm/vdd/samples/readme b/private/mvdm/vdd/samples/readme
new file mode 100644
index 000000000..cac67605e
--- /dev/null
+++ b/private/mvdm/vdd/samples/readme
@@ -0,0 +1,11 @@
+SAMPLE1 - This directory contains a rudimentary structure for application
+ based-intercept approach for writing a VDD. The details for this
+ approach are given in the Virtual Device Driver chapter. "fax16"
+ is a sub-directory, which has a minimal FAX stub device driver.
+ FAX32 directory has the sources for a FAX VDD. {fax16.sys,fax32.dll}
+
+SAMPLE2 - This directory contains a rudimentary structure for NTVDM
+ based-intercept approach for writing a VDD. The details for this
+ approach are given in the Virtual Device Driver chapter. "16bits"
+ is a sub-directory, which has a test device driver. VDD
+ directory has the sources for VDD. {16bits.exe,vdd.dll)
diff --git a/private/mvdm/vdd/samples/sample1/dirs b/private/mvdm/vdd/samples/sample1/dirs
new file mode 100644
index 000000000..083484f16
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample1/dirs
@@ -0,0 +1 @@
+DIRS=fax32
diff --git a/private/mvdm/vdd/samples/sample1/fax16/fax16.asm b/private/mvdm/vdd/samples/sample1/fax16/fax16.asm
new file mode 100644
index 000000000..0d8a6d86a
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample1/fax16/fax16.asm
@@ -0,0 +1,348 @@
+ name faxdrv
+ title 'FAX16 - Stub driver for Application based intercept under NT'
+
+;
+; fax16.asm: This is a very simple DOS stub device driver for NTVDM.
+; It shows how to use application based intercept services
+; provided by NTVDM. FAX32.dll is its DLL which will be loaded
+; in the NTVDM process by this stub device driver.
+;
+; This driver only has meaningful code for init,read and write.
+; Rest all command codes always succeed. We are assuming here
+; that the 16 bit fax application for this stub device driver
+; opens this device and just make read and write calls. The
+; meaning of read is to get a fax message and write means
+; send a message
+
+_TEXT segment byte public 'CODE'
+
+ assume cs:_TEXT,ds:_TEXT,es:NOTHING
+
+ org 0
+
+ include isvbop.inc
+
+MaxCmd equ 24 ; Maximum allowed command
+
+; VDD Command codes
+
+OpGet equ 1 ; Read a FAX
+OpSend equ 2 ; Send a FAX
+
+Header: ; Fax Device Header
+ DD -1
+ DW 0c840h
+ DW FaxStrat
+ DW FaxIntr
+ DB 'FAXDRV00'
+
+RHPtr DD ? ; Pointer to Request Header
+
+Dispatch: ; Interrupt routine command code
+ DW Init
+ DW MediaChk
+ DW BuildBPB
+ DW IoctlRd
+ DW Read
+ DW NdRead
+ DW InpStat
+ DW InpFlush
+ DW Write
+ DW WriteVfy
+ DW OutStat
+ DW OutFlush
+ DW IoctlWt
+ DW DevOpen
+ DW DevClose
+ DW RemMedia
+ DW OutBusy
+ DW Error
+ DW Error
+ DW GenIOCTL
+ DW Error
+ DW Error
+ DW Error
+ DW GetLogDev
+ DW SetLogDev
+
+DllName DB "FAX32.DLL",0
+InitFunc DB "FAXVDDRegisterInit",0
+DispFunc DB "FAXVDDDispatch",0
+
+F32Mes DB "We are called from 32 staff", 10, 13, "$"
+
+hVDD DW ?
+
+FaxStrat proc far ; Strategy Routine
+
+ mov word ptr cs:[RhPtr],bx
+ mov word ptr cs:[RhPtr+2],es
+ ret
+
+FaxStrat endp
+
+FaxIntr proc far ; INterrupt routine
+
+ push ax ; Save registers
+ push bx
+ push cx
+ push dx
+ push ds
+ push es
+ push di
+ push si
+ push bp
+
+ push cs
+ pop ds ; DS = CS
+
+ les di,[RHPtr] ; ES:DI = request header
+
+ mov bl,es:[di+2]
+ xor bh,bh ; BX = command code
+ cmp bx,MaxCmd
+ jle FIntr1
+
+ call Error ; Unknown command
+ jmp FIntr2
+
+FIntr1:
+ shl bx,1
+ call word ptr [bx+Dispatch] ; call command routine
+ les di,[RhPtr] ; ES:DI = request header
+
+FIntr2:
+ or ax,0100h ; Set Done bit in the status
+ mov es:[di+3],ax ; Store the status
+
+ pop bp ; restore registers
+ pop si
+ pop di
+ pop es
+ pop ds
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+
+ ret
+
+
+MediaChk proc near
+ xor ax,ax
+ ret
+MediaChk endp
+
+BuildBPB proc near
+ xor ax,ax
+ ret
+BuildBPB endp
+
+IoctlRd proc near
+ xor ax,ax
+ ret
+IoctlRd endp
+
+Read proc near
+ push es
+ push di ; Save Request Header add
+
+ mov bx,word ptr es:[di+14] ; buffer offset
+ mov ax,word ptr es:[di+16] ; buffer segment
+ mov cx,word ptr es:[di+18] ; buffer length
+
+ mov es,ax ; es:bx is the buffer where
+ ; fax has to be read from
+ ; the NT device driver
+
+ mov ax,word ptr cs:[hVDD] ; VDD handle returned by
+ ; register module
+ mov dx,OpGet ; Read the fax command
+
+ DispatchCall
+
+ pop di
+ pop es
+
+ jnc rOK ; NC -> Success and CX has
+ ; the count read.
+
+ call Error ; Operation Failed
+ ret
+
+rOK:
+ mov word ptr es:[di+12],cx ; return in header how much
+ ; was read
+ xor ax,ax
+ ret
+Read endp
+
+NdRead proc near
+ xor ax,ax
+ ret
+NdRead endp
+
+InpStat proc near
+ xor ax,ax
+ ret
+InpStat endp
+
+InpFlush proc near
+ xor ax,ax
+ ret
+InpFlush endp
+
+Write proc near
+ push es
+ push di ; Save Request Header add
+
+ mov bx,word ptr es:[di+14] ; buffer offset
+ mov ax,word ptr es:[di+16] ; buffer segment
+ mov cx,word ptr es:[di+18] ; buffer length
+
+ mov es,ax ; es:bx is the FAX message where
+ ; to be send by NT device
+ ; driver
+
+ mov ax,word ptr cs:[hVDD] ; VDD handle returned by
+ ; register module
+ mov dx,OpSend ; Send the fax command
+
+ DispatchCall
+
+ pop di
+ pop es
+
+ jnc wOK ; NC -> Success and CX has
+ ; the count read.
+
+ call Error ; Operation Failed
+ ret
+
+wOK:
+ mov word ptr es:[di+12],cx ; return in header how much
+ ; was actually written
+ xor ax,ax
+ ret
+Write endp
+
+WriteVfy proc near
+ xor ax,ax
+ ret
+WriteVfy endp
+
+OutStat proc near
+ xor ax,ax
+ ret
+OutStat endp
+
+OutFlush proc near
+ xor ax,ax
+ ret
+OutFlush endp
+
+IoctlWt proc near
+ xor ax,ax
+ ret
+IoctlWt endp
+
+DevOpen proc near
+ xor ax,ax
+ ret
+DevOpen endp
+
+DevClose proc near
+ xor ax,ax
+ ret
+DevClose endp
+
+RemMedia proc near
+ xor ax,ax
+ ret
+RemMedia endp
+
+OutBusy proc near
+ xor ax,ax
+ ret
+OutBusy endp
+
+GenIOCTL proc near
+ xor ax,ax
+ ret
+GenIOCTL endp
+
+GetLogDev proc near
+ xor ax,ax
+ ret
+GetLogDev endp
+
+SetLogDev proc near
+ xor ax,ax
+ ret
+SetLogDev endp
+
+Error proc near
+ mov ax,8003h ; Bad Command Code
+ ret
+Error endp
+;
+;
+; This function is a sample sub that calling from 32-bits part of VDD
+;
+From32Sub proc near
+
+ push cs
+ pop ds
+ mov dx, offset F32mes
+ mov ah, 09h
+ int 21h
+ VDDUnSimulate16
+ ret
+
+From32Sub endp
+
+Init proc near
+ push es
+ push di ; Save Request Header add
+
+ push ds
+ pop es
+
+ ; Load fax32.dll
+ mov si, offset DllName ; ds:si = fax32.dll
+ mov di, offset InitFunc ; es:di = init routine
+ mov bx, offset DispFunc ; ds:bx = dispatch routine
+ mov ax, offset From32Sub ; ds:ax = From32Sub
+
+
+ RegisterModule
+ jnc saveHVDD ; NC -> Success
+
+ call Error ; Indicate failure
+
+ pop di
+ pop es
+ mov byte ptr es:[di+13],0 ; unit supported 0
+ mov word ptr es:[di+14],offset Header ; Unload this device
+ mov word ptr es:[di+16],cs
+ mov si, offset Header
+ and [si+4],8FFFh ; clear bit 15 for failure
+ ret
+
+saveHVDD:
+ mov [hVDD],ax
+
+ pop di
+ pop es
+ mov word ptr es:[di+14],offset Init ; Free Memory address
+ mov word ptr es:[di+16],cs
+
+ xor ax,ax ; return success
+ ret
+Init endp
+
+FaxIntr endp
+
+_TEXT ends
+
+ end
diff --git a/private/mvdm/vdd/samples/sample1/fax16/makefile b/private/mvdm/vdd/samples/sample1/fax16/makefile
new file mode 100644
index 000000000..640ae45d7
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample1/fax16/makefile
@@ -0,0 +1,41 @@
+# Makefile for fax16 stub device driver
+
+!IFNDEF BUILDMSG
+BUILDMSG=
+!ENDIF
+
+########## Path definition so we find 16 bit tools ##########
+# Also works around stupid bug in RC 3.1 that doesn't allow rcpp.err to be
+# in a directory that is greater than 128 chars down the path, even if
+# rc 3.1 is running as an OS/2 app.
+
+PATH = $(_NTBINDIR)\private\mvdm\tools16;$(PATH)
+
+.SUFFIXES:
+.SUFFIXES: .c .obj .lst .exe .exc .exs .com .sal .cod .sil .inc .skl .cla .cl1 .ctl .asm .idx .msg
+
+MAKE =nmake
+asm =masm
+awarn =-W1
+aflags =-Mx -t $(awarn) $(extasw)
+ainc =-I. -I$(_NTBINDIR)\public\sdk\inc
+
+
+.asm.obj:
+ $(asm) $(ainc) $(aflags) $*.asm;
+
+.asm.lst:
+ $(asm) -l $(ainc) $(aflags) $*.asm;
+
+all: fax16.sys
+
+clean:
+ if exist *.obj del *.obj
+ if exist *.exe del *.exe
+ if exist *.map del *.map
+ if exist *.sym del *.sym
+ if exist *.sys del *.sys
+
+fax16.sys: fax16.obj
+ link16 fax16;
+ exe2bin fax16.exe fax16.sys
diff --git a/private/mvdm/vdd/samples/sample1/fax32/fax32.c b/private/mvdm/vdd/samples/sample1/fax32/fax32.c
new file mode 100644
index 000000000..cb162b10e
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample1/fax32/fax32.c
@@ -0,0 +1,248 @@
+/*++
+ *
+ * VDD v1.0
+ *
+ * Copyright (c) 1991, Microsoft Corporation
+ *
+ * VDD.C - Sample VDD for NT-MVDM
+ *
+--*/
+#include "fax32.h"
+#include "vddsvc.h"
+
+
+USHORT Sub16CS;
+USHORT Sub16IP;
+
+BOOL
+VDDInitialize(
+ IN PVOID DllHandle,
+ IN ULONG Reason,
+ IN PCONTEXT Context OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ DllHandle - Not Used
+
+ Reason - Attach or Detach
+
+ Context - Not Used
+
+Return Value:
+
+ SUCCESS - TRUE
+ FAILURE - FALSE
+
+--*/
+
+{
+
+ switch ( Reason ) {
+
+ case DLL_PROCESS_ATTACH:
+ // Allocate VDD's local heap if needed. Check that NT FAX driver
+ // is available by opening that device.
+ //....
+ // Install user hook for callback service.
+
+ if(!VDDInstallUserHook (DllHandle,&FAXVDDCreate, &FAXVDDTerminate,
+ &FAXVDDBlock, &FAXVDDResume))
+ OutputDebugString("FAX32: UserHook not installed\n");
+ else
+ OutputDebugString("FAX32: UserHook installed!\n");
+
+ // UserHook # 2
+ if(!VDDInstallUserHook (DllHandle,&FAXVDDCreate, NULL,
+ NULL, &FAXVDDResume))
+ OutputDebugString("FAX32: UserHook #2 not installed\n");
+ else
+ OutputDebugString("FAX32: UserHook #2 installed!\n");
+
+ break;
+
+ case DLL_PROCESS_DETACH:
+ // Deallocate VDD's local heap if needed
+ // communicate to appropriate Device driver about your departure
+ //...
+ // Deinstall user hook for callback service.
+ if(!VDDDeInstallUserHook (DllHandle))
+ OutputDebugString("FAX32: UserHook not deinstalled\n");
+ else
+ OutputDebugString("FAX32: UserHook deinstalled!\n");
+
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+// Sample function
+VOID FAXVDDTerminate(USHORT usPDB)
+{
+ USHORT uSaveCS, uSaveIP;
+
+ OutputDebugString("FAX32: Terminate message\n");
+
+ // VDDHostSimulate
+
+ uSaveCS = getCS();
+ uSaveIP = getIP();
+ setCS(Sub16CS);
+ setIP(Sub16IP);
+ VDDSimulate16();
+ setCS(uSaveCS);
+ setIP(uSaveIP);
+
+}
+
+// Sample function
+VOID FAXVDDCreate(USHORT usPDB)
+{
+ OutputDebugString("FAX32: Create Message\n");
+}
+
+// Sample function
+VOID FAXVDDBlock(VOID)
+{
+ OutputDebugString("FAX32: Block Message\n");
+}
+
+// Sample function
+VOID FAXVDDResume(VOID)
+{
+ OutputDebugString("FAX32: Resume Message\n");
+}
+
+
+VOID
+FAXVDDTerminateVDM(
+ VOID
+ )
+/*++
+
+Arguments:
+
+Return Value:
+
+ SUCCESS - TRUE
+ FAILURE - FALSE
+
+--*/
+
+
+{
+
+ // Cleanup any resource taken for this vdm
+
+
+ return;
+}
+
+
+VOID
+FAXVDDRegisterInit(
+ VOID
+ )
+/*++
+
+Arguments:
+
+Return Value:
+
+ SUCCESS - TRUE
+ FAILURE - FALSE
+
+--*/
+
+
+{
+ // Save addresses for fax16
+ Sub16CS = getDS();
+ Sub16IP = getAX();
+
+ OutputDebugString("FAX32: GET_ADD\n");
+
+ // Called from the BOP manager. If VDDInitialize has done all the
+ // checking and resources alloaction, just return success.
+
+ setCF(0);
+ return;
+}
+
+
+#define GET_A_FAX 1
+#define SEND_A_FAX 2
+
+VOID
+FAXVDDDispatch(
+ VOID
+ )
+/*++
+
+Arguments:
+ Client (DX) = Command code
+ 01 - get a message from NT device driver
+ 02 - send a message through NT device driver
+ 03 - address of 16 bit routine
+
+ Client (ES:BX) = Message Buffer
+ Client (CX) = Buffer Size
+
+Return Value:
+
+ SUCCESS - Client Carry Clear and CX has the count transferred
+ FAILURE - Client Carry Set
+
+--*/
+
+
+{
+PCHAR Buffer;
+USHORT cb;
+USHORT uCom;
+BOOL Success = TRUE; // In this sample operation always succeeds
+
+ uCom = getDX();
+
+ cb = getCX();
+ Buffer = (PCHAR) GetVDMPointer ((ULONG)((getES() << 16)|getBX()),cb,FALSE);
+ switch (uCom) {
+ case GET_A_FAX:
+ // Make a DeviceIOControl or ReadFile on NT FAX driver with
+ // cb and Buffer.Then set CX if success.
+
+ if (Success) {
+ setCX(cb);
+ setCF(0);
+ }
+ else
+ setCF(1);
+
+ break;
+
+
+ case SEND_A_FAX:
+ // Make a DeviceIOControl or WriteFile on NT FAX driver with
+ // cb and Buffer.Then set CX if success.
+
+ if (Success) {
+ setCX(cb);
+ setCF(0);
+ }
+ else
+ setCF(1);
+
+ break;
+ default:
+ setCF(1);
+ }
+ return;
+}
diff --git a/private/mvdm/vdd/samples/sample1/fax32/fax32.def b/private/mvdm/vdd/samples/sample1/fax32/fax32.def
new file mode 100644
index 000000000..826d5579a
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample1/fax32/fax32.def
@@ -0,0 +1,8 @@
+LIBRARY VDD
+
+DESCRIPTION 'Sample ISV VDD for NT-MVDM'
+
+EXPORTS
+ FAXVDDDispatch
+ FAXVDDTerminateVDM
+ FAXVDDRegisterInit
diff --git a/private/mvdm/vdd/samples/sample1/fax32/fax32.h b/private/mvdm/vdd/samples/sample1/fax32/fax32.h
new file mode 100644
index 000000000..fc5c45650
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample1/fax32/fax32.h
@@ -0,0 +1,29 @@
+/* vdd.h - main include file for the VDD
+ *
+ */
+
+
+
+#ifdef WIN_32
+#define WIN
+#define FLAT_32
+#define TRUE_IF_WIN32 1
+#else
+#define TRUE_IF_WIN32 0
+#endif
+
+#ifdef WIN
+#define _WINDOWS
+#include "windows.h"
+#endif
+
+
+
+BOOL VDDInitialize(PVOID,ULONG,PCONTEXT);
+VOID FAXVDDTerminateVDM(VOID);
+VOID FAXVDDInit (VOID);
+VOID FAXVDDDispatch (VOID);
+VOID FAXVDDTerminate(USHORT usPDB);
+VOID FAXVDDCreate(USHORT usPDB);
+VOID FAXVDDBlock(VOID);
+VOID FAXVDDResume(VOID);
diff --git a/private/mvdm/vdd/samples/sample1/fax32/makefile b/private/mvdm/vdd/samples/sample1/fax32/makefile
new file mode 100644
index 000000000..85f1ecf36
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample1/fax32/makefile
@@ -0,0 +1,11 @@
+# Sample VDD makefile
+#
+# Copyright (c) 1991, Microsoft Corporation
+#
+
+
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/mvdm/vdd/samples/sample1/fax32/sources b/private/mvdm/vdd/samples/sample1/fax32/sources
new file mode 100644
index 000000000..942d66469
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample1/fax32/sources
@@ -0,0 +1,21 @@
+INDENTED_DIRECTIVES=1
+
+MAJORCOMP=vdd
+MINORCOMP=fax32
+
+TARGETNAME=fax32
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntvdm.lib
+
+DLLENTRY=VDDInitialize
+DLLBASE=0x2000000
+
+SOURCES=fax32.c
+
+C_DEFINES=-DWIN_32 -DDEBUG
+
+UMTYPE=windows
+UMTEST=
+UMLIBS=
diff --git a/private/mvdm/vdd/samples/sample2/16bits/16bits.asm b/private/mvdm/vdd/samples/sample2/16bits/16bits.asm
new file mode 100644
index 000000000..093e69e91
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample2/16bits/16bits.asm
@@ -0,0 +1,217 @@
+ TITLE Sample 16 Bits DOS application
+;---------------------------------------------------------------;
+;
+ include 16bits.inc
+
+ DOSSEG
+ .MODEL SMALL
+
+ .STACK 100h
+
+ .DATA
+
+
+DMAWriteBuffer label byte
+ db 64 dup (?)
+DMA_BUFFER_SIZE equ $ - DMAWriteBuffer
+
+DMAReadBuffer label byte
+ db DMA_BUFFER_SIZE dup (?)
+
+MIOPattern label byte
+ db 00, 0FFh, 0AAh, 055h
+MIOPATTERN_SIZE equ $ - MIOPattern
+
+public start
+
+ .CODE
+start:
+ jmp short RealStart
+OldVector label dword
+ dd ?
+DMACompleted db ?
+
+RealStart:
+ mov ax,@DATA
+ mov ds,ax
+ mov es,ax
+ assume ds:@DATA, es:@DATA
+
+;Hook interrupt(DMA terminate count notification)
+ push ds
+ mov al, DMA_INTERRUPT
+ mov ah, 35h
+ int 21h
+ mov word ptr cs:OldVector, bx
+ mov word ptr cs:OldVector + 2, es
+ mov dx, offset ISRDMACompleted
+ mov ax, cs
+ mov ds, ax
+ mov al, DMA_INTERRUPT
+ mov ah, 25h
+ int 21h
+ pop ds
+
+
+;VDD operation.
+;(1). Hook the I/O port.
+;(2). Keep the port status up-to-date if a write operation is performed
+; by 16 bits application(this program).
+;(3). Simulate DMA operation and generate a fake interrupt for 16bits
+; applicatiion if the DMA operation reaches its TC.
+;
+;
+;16bits application
+;(1). Output one byte to the port and then request DMA operation.
+;(2). Wait for DMA operation completed.
+;(3). goto step (1) if there are more data to be transferred.
+;Note that the given I/O must be a R/W port.
+
+;Here we do a DMA write operation upon I/O mapped I/O
+ mov cx, DMA_BUFFER_SIZE
+ mov si, offset DMAWriteBuffer
+DMATransferLoop_Fast:
+ mov dx, IO_PORT_DMA
+ mov al, cl
+ out dx, al ;write I/O the current count
+ cli
+ mov cs:DMACompleted, FALSE ;reset TC flag
+ sti
+;channel #1, write op, no auto init, addr inc, single transfer
+ mov al, 01000101B
+ call SetupDMAOperation
+;Fire the DMA WRITE operation, this will cause VDD to gain control
+;and start DMA operation.
+ mov dx, IO_PORT_FIRE_DMA_FAST
+ out dx, al
+;In real world(there is a real hardware adapter), we won't do this
+;idle loop, rather, we can do something useful(like, read the DMA current
+;count and display the progress and so forth)provided that we can regain
+;control while DMA operation is in progress(VDD spawns a new thread to
+;handle DMA operation and returns to us immediately)
+;
+;Since we are simulating DMA operation without hardware, we always
+;start the DMA operation with transfer count set to 1 byte. In reality
+;this is should not be the case because it will slow down the data transfer.
+
+WaitForDMA_Fast:
+ cmp cs:DMACompleted, TRUE
+ jnz WaitForDMA_Fast
+ inc si
+ loop DMATransferLoop_Fast
+;
+;Now do a DMA read operation
+ mov cx, DMA_BUFFER_SIZE
+ mov si, offset DMAWriteBuffer
+ mov di, offset DMAReadBuffer
+DMATransferLoop_Slow:
+;channel #1, read op, no auto init, addr inc, single transfer
+ mov al, 01001001B
+ cli
+ mov cs:DMACompleted, FALSE
+ sti
+ call SetupDMAOperation
+;Fire the DMA READ operation
+ mov dx, IO_PORT_FIRE_DMA_SLOW
+ out dx, al
+WaitForDMA_Slow:
+ cmp cs:DMACompleted, TRUE
+ jne WaitForDMA_Slow
+ mov dx, IO_PORT_DMA
+ in al, dx
+ mov [di], al
+ inc di ;advance our buffer
+ inc si ;and the DMA buffer
+ loop DMATransferLoop_Slow
+;
+;The DMAWriteBuffer and DMAReadBuffer should have the same contents.
+;If they don't, it failed. ....
+
+
+;Memory mapped I/O
+ mov ax, MIO_SEGMENT
+ mov es, ax
+ mov bx, MIOPATTERN_SIZE
+ mov si, offset MIOPattern
+
+MIO_Loop:
+ cld
+ lodsb ;get next pattern
+ mov cx, MIO_PORT_RANGE
+ mov di, MIO_PORT_FIRST
+rep stosb ;fill all I/O with the pattern
+ mov cx, MIO_PORT_RANGE
+ dec di
+ std
+repe scasb ;
+ je @F
+; call ErrorMIO ;if any i/o failed,
+@@:
+ dec bx ;next pattern
+ jnz MIO_Loop
+
+;Before terminate, retsore everything we have touched
+ push ds
+ lds dx, cs:OldVector
+ mov al, DMA_INTERRUPT
+ mov ah, 25h
+ int 21h
+ pop ds
+ mov ah, 04Ch
+ int 21h
+
+;-------------------------------------------------------;
+;Setup DMA operation
+;Input: ds:si = seg:offset of memeory address
+; al = DMA mode
+;output: NONE
+;Modified: AX, DX
+;-------------------------------------------------------;
+SetupDMAOperation proc
+ push cx
+ mov dx, DMA_PORT_FLIPFLOP
+ out dx, al
+ mov dx, DMA_PORT_MODE ;more register
+ out dx, al
+ mov ax, ds ;transfer address -> page:offset
+ mov cl, 4 ;page: A16 ~ A19, offset A0 ~ A15
+ rol ax, cl
+ mov ch, al
+ and al, 0F0h
+ add ax, si
+ jnc @F
+ inc ch
+@@:
+ mov dx, DMA_PORT_ADDR
+ out dx, al ;offset lower byte
+ mov al, ah
+ out dx, al ;and higher byte
+ mov dx, DMA_PORT_PAGE ;page register
+ mov al, ch
+ and al, 0Fh ;only 4 bits allowed
+ out dx, al
+ mov al, 1 ;single transfer, one byte
+ mov dx, DMA_PORT_COUNT
+ out dx, al
+ dec al
+ out dx, al ;higher byte set to 0
+ mov dx, DMA_PORT_REQUEST ;request channel #1
+ mov al, 00000101B
+ out dx, al
+ mov dx, DMA_PORT_SNGLE_MASK ;start DMA transfer
+ mov al, 00000001B
+ out dx, al
+ pop cx
+ ret
+SetupDMAOperation endp
+
+
+ISRDMACompleted proc far
+ mov byte ptr DMACompleted, TRUE
+ mov al, 20h
+ out 20h, al
+ out 0A0h, al
+ iret
+ISRDMACompleted endp
+
+END start
diff --git a/private/mvdm/vdd/samples/sample2/16bits/16bits.inc b/private/mvdm/vdd/samples/sample2/16bits/16bits.inc
new file mode 100644
index 000000000..dd4fd0115
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample2/16bits/16bits.inc
@@ -0,0 +1,43 @@
+;---------------------------------------------------;
+; Include file for 16bits sample application
+; Copyright (C) 1992, Microsoft Corporation
+;---------------------------------------------------;
+
+TRUE equ 0FFh
+FALSE equ NOT(TRUE)
+
+;
+;I/O mapped I/O port equates
+;
+IO_PORT_FIRST equ 790h ;The first I/O port addr
+IO_PORT_LAST equ 793h ;The last I/O port addr
+IO_PORT_FIRE_DMA_SLOW equ IO_PORT_FIRST ;Port addr to trigger DMA
+IO_PORT_FIRE_DMA_FAST equ IO_PORT_FIRST + 1 ;port to trigger DMA
+IO_PORT_DMA equ IO_PORT_FIRST+2 ;port connnected to DMA channel
+;
+;Memory mapped I/O port equates ;segment address
+;
+MIO_SEGMENT equ 0C000h
+MIO_PORT_FIRST equ 0 ;The first port addr(offset)
+MIO_PORT_LAST equ 07 ;the last port addr(offset)
+MIO_PORT_RANGE equ MIO_PORT_LAST - MIO_PORT_FIRST + 1
+MIO_PORT_FIRE_DMA equ MIO_PORT_FIRST ;Port to trigger DMA
+MIO_PORT_DMA equ MIO_PORT_FIRST+1;port connected to DMA channel
+;
+;DMA equates
+;
+DMA_INTERRUPT equ 70h + 2 ;slave PIC, line #2(0 based)
+DMA_CHANNEL equ 01h ;DMA #1, channel #1(0 based)
+
+DMA_PORT_BASE equ 00
+DMA_PORT_PAGE equ 083h
+DMA_PORT_ADDR equ DMA_PORT_BASE + 2
+DMA_PORT_COUNT equ DMA_PORT_BASE + 3
+DMA_PORT_CMD equ DMA_PORT_BASE + 8
+DMA_PORT_REQUEST equ DMA_PORT_BASE + 9
+DMA_PORT_SNGLE_MASK equ DMA_PORT_BASE + 10
+DMA_PORT_MODE equ DMA_PORT_BASE + 11
+DMA_PORT_FLIPFLOP equ DMA_PORT_BASE + 12
+DMA_PORT_TEMP equ DMA_PORT_BASE + 13
+DMA_PORT_CLEARMASK equ DMA_PORT_BASE + 14
+DMA_PORT_WRTEMASK equ DMA_PORT_BASE + 15
diff --git a/private/mvdm/vdd/samples/sample2/16bits/makefile b/private/mvdm/vdd/samples/sample2/16bits/makefile
new file mode 100644
index 000000000..d394d22f1
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample2/16bits/makefile
@@ -0,0 +1,42 @@
+# Makefile for 16bits module of third part apps making bop calls
+#
+
+########## Path definition so we find 16 bit tools ##########
+# Also works around stupid bug in RC 3.1 that doesn't allow rcpp.err to be
+# in a directory that is greater than 128 chars down the path, even if
+# rc 3.1 is running as an OS/2 app.
+
+PATH = $(_NTBINDIR)\private\mvdm\tools16;$(PATH)
+
+.SUFFIXES:
+.SUFFIXES: .c .obj .lst .exe .exc .exs .com .sal .cod .sil .inc .skl .cla .cl1 .ctl .asm .idx .msg
+
+MAKE =nmake
+asm =masm
+awarn =-W1
+aflags =-Mx -t $(awarn) $(extasw)
+ainc =-I.
+link_opts = /MAP
+LINK =link
+exelink =/E
+
+
+.asm.obj:
+ $(asm) $(ainc) $(aflags) $*.asm;
+
+.asm.lst:
+ $(asm) -l $(ainc) $(aflags) $*.asm;
+
+all: 16bits.exe
+
+clean:
+ if exist *.obj del *.obj
+ if exist *.exe del *.exe
+ if exist *.map del *.map
+ if exist *.sym del *.sym
+ if exist *.exe del *.exe
+
+16bits.exe: 16bits.obj
+ link16 $(link_opts) 16bits.obj, 16bits.exe, 16bits.map;
+
+16bits.obj: 16bits.inc
diff --git a/private/mvdm/vdd/samples/sample2/dirs b/private/mvdm/vdd/samples/sample2/dirs
new file mode 100644
index 000000000..19983aa36
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample2/dirs
@@ -0,0 +1 @@
+DIRS=vdd
diff --git a/private/mvdm/vdd/samples/sample2/vdd/makefile b/private/mvdm/vdd/samples/sample2/vdd/makefile
new file mode 100644
index 000000000..85f1ecf36
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample2/vdd/makefile
@@ -0,0 +1,11 @@
+# Sample VDD makefile
+#
+# Copyright (c) 1991, Microsoft Corporation
+#
+
+
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/mvdm/vdd/samples/sample2/vdd/sources b/private/mvdm/vdd/samples/sample2/vdd/sources
new file mode 100644
index 000000000..8622d9d42
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample2/vdd/sources
@@ -0,0 +1,23 @@
+INDENTED_DIRECTIVES=1
+
+MAJORCOMP=vdd
+MINORCOMP=vdd
+
+TARGETNAME=vdd
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntvdm.lib
+
+DLLENTRY=VDDInitialize
+DLLBASE=0x2000000
+
+INCLUDES=.
+
+SOURCES=vdd.c
+
+C_DEFINES=-DWIN_32 -DDEBUG
+
+UMTYPE=windows
+UMTEST=
+UMLIBS=
diff --git a/private/mvdm/vdd/samples/sample2/vdd/vdd.c b/private/mvdm/vdd/samples/sample2/vdd/vdd.c
new file mode 100644
index 000000000..53c22d2bb
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample2/vdd/vdd.c
@@ -0,0 +1,385 @@
+/*++
+ *
+ * VDD v1.0
+ *
+ * Copyright (c) 1991, Microsoft Corporation
+ *
+ * VDD.C - Sample VDD for NT-MVDM
+ *
+--*/
+
+#include "vdd.h"
+
+/** Global variables **/
+
+ HANDLE hVDD; /* VDD module handle */
+ HANDLE hVddHeap; /* VDD local heap */
+ PBYTE IOBuffer; /* buffer to simulate I/O Read and Write */
+ ULONG MIOAddress; /* memory mapped I/O linear address */
+ PVOID BaseAddress; /* memory mapped I/O virtual address */
+ BOOLEAN IOHook; /* true if we installed a I/O hooked */
+ BOOLEAN MIOHook; /* true if we installed a memory hook */
+ static VDD_IO_PORTRANGE PortRange;
+
+
+BOOL
+VDDInitialize(
+ HANDLE hVdd,
+ DWORD dwReason,
+ LPVOID lpReserved)
+
+/*++
+
+Routine Description:
+
+ The DllEntryPoint for the Vdd which handles intialization and termination
+
+Arguments:
+
+ hVdd - The handle to the VDD
+
+ Reason - flag word thatindicates why Dll Entry Point was invoked
+
+ lpReserved - Unused
+
+Return Value:
+ BOOL bRet - if (dwReason == DLL_PROCESS_ATTACH)
+ TRUE - Dll Intialization successful
+ FALSE - Dll Intialization failed
+ else
+ always returns TRUE
+--*/
+
+{
+ int i;
+ VDD_IO_HANDLERS IOHandlers;
+
+
+
+/**
+ keep a copy of VDD handle in global variable so the other functions
+ can see it
+**/
+ hVDD = hVdd;
+
+ switch ( dwReason ) {
+
+ case DLL_PROCESS_ATTACH:
+
+ // Allocate VDD's local heap
+ hVddHeap = HeapCreate(0, 0x1000, 0x10000);
+
+ if (!hVddHeap) {
+ OutputDebugString("VDD: Can't create local heap");
+ return FALSE;
+ }
+
+ IOBuffer = (PBYTE)HeapAlloc(hVddHeap,0,IO_PORT_RANGE);
+
+ if (!IOBuffer) {
+ OutputDebugString("VDD: Can't allocate IO buffer from heap");
+ HeapDestroy(hVddHeap);
+ return FALSE;
+ }
+
+ // communicate to appropriate Device driver about your arrival
+
+ // Set emulated I/O to floating
+ for (i = 0 ; i < IO_PORT_RANGE; i++)
+ IOBuffer[i] = FLOATING_IO;
+
+
+ IOHandlers.inb_handler = MyInB;
+ IOHandlers.inw_handler = NULL;
+ IOHandlers.insb_handler = NULL;
+ IOHandlers.insw_handler = NULL;
+ IOHandlers.outb_handler = MyOutB;
+ IOHandlers.outw_handler = NULL;
+ IOHandlers.outsb_handler = NULL;
+ IOHandlers.outsw_handler = NULL;
+ PortRange.First = IO_PORT_FIRST;
+ PortRange.Last = IO_PORT_LAST;
+
+ // hook I/O mapped I/O
+ IOHook = VDDInstallIOHook(hVDD, (WORD) 1, &PortRange, &IOHandlers);
+
+ // get 32 bits linear address of memory mapped I/O
+ MIOAddress = (ULONG) GetVDMPointer(MIO_ADDRESS, MIO_PORT_RANGE, 0);
+ // hook memory mapped I/O
+ MIOHook = VDDInstallMemoryHook(hVDD, (PVOID) MIOAddress, MIO_PORT_RANGE,
+ (PVDD_MEMORY_HANDLER)MyMIOHandler);
+ break;
+
+ case DLL_PROCESS_DETACH:
+
+ // communicate to appropriate Device driver about your departure
+ if (IOHook)
+ VDDDeInstallIOHook(hVDD, 1, &PortRange);
+ if (MIOHook) {
+ VDDDeInstallMemoryHook(hVDD, (PVOID) MIOAddress, MIO_PORT_RANGE);
+ if (BaseAddress) {
+ VDDFreeMem(BaseAddress, PAGE_SIZE, MEM_DECOMMIT);
+ }
+ }
+
+ // Deallocate VDD's local heap if needed
+ HeapDestroy(hVddHeap);
+ break;
+
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+
+VOID
+MyInB(
+WORD Port,
+PBYTE Buffer
+)
+
+{
+// Simply provide the data from our buffer
+ *Buffer = IOBuffer[Port - IO_PORT_FIRST];
+}
+
+VOID
+MyOutB(
+WORD Port,
+BYTE Data
+)
+
+{
+ // update our local buffer.
+ // In real application, the VDD might want to call its associated
+ // device driver to update the change.
+
+ IOBuffer[Port - IO_PORT_FIRST] = (BYTE)Data;
+
+ // If the IO port is the one to trigger DMA operation, do it
+ // To demonstarte the two options in handling DMA operation, we
+ // use two ports here to trigger different DMS operation schemes.
+
+ if (Port == IO_PORT_FIRE_DMA_FAST) {
+ FastDMA();
+ }
+ else {
+ if(Port == IO_PORT_FIRE_DMA_SLOW) {
+ SlowDMA();
+ }
+ }
+}
+
+
+VOID
+MyMIOHandler(
+ULONG Address, // faulting linear address
+ULONG RWFlags // 1 if write opertion, 0 if read
+)
+{
+
+ // map the memory for the memory mapped I/O so that we won't
+ // get page fault on our memory mapped I/O after this.
+ // We may reserve the memory during DLL_PROCESS_ATTACH (by using
+ // MEM_RESERVE rather than MEM_COMMIT we did here).
+ // The solution applied here is not the best solution(it is the
+ // simplest solution though). A better way to handle memory mapped
+ // I/O is to hook the page fault as we did here and decode the faulting
+ // instruction, simulate its operation and advance 16 bits application
+ // program counter(getIP and setIP).
+
+ BaseAddress = VDDAllocMem ((LPVOID) MIOAddress, PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE);
+ if (!BaseAddress) {
+ OutputDebugString("VDD: Can't allocate virtual memory");
+ }
+}
+
+
+/** DMA operation support
+
+Facts:
+ - All DMA I/O ports are trapped and maintained by MVDM.
+ - VDD provides necessary buffers and calls its associated device
+ driver to carry out the real work(in this case, device driver
+ will perform real DMA operation with the buffer provided by
+ VDD as the source(DMA READ) or destination(DMA WRITE).
+ -The device driver has full knowledge of which I/O port(s) are
+ connectted to the DMA request channel.
+Therefore, the responsibilities of VDD are:
+ (1). Allocate necessary buffers
+ (2). if it is a DMA write operation(data from I/O to memory)
+ - calls device driver to perform the DMA operation with
+ newly allocated memory as the target buffer for DMA operation.
+ - calls MVDM DMA support routine to transfer data from
+ local buffer to 16bits application buffer.
+ - Simulate an interrupt to the 16 bits applications to notify
+ the completion.
+ if it is a DMA read operation(data from memory to I/O)
+ - calls MVDM DMA service to copy data from 16bits application
+ to the newly allocated buffer.
+ - calls the device driver to carry out DMA operation with
+ the allocated buffer as the source of the operation.
+ - Simulate an interrupt to the 16bits application to notify
+ the completion.
+The SlowDMA simulates a DMA READ operation by using VDDRequestDMA service.
+The FastDMA simulates a DMA WRITE operation by using VDDQueryDMA and VDDSetDMA
+services.
+
+** NOTE **
+We run the DMA in the same thread here so that before we return, there is no
+way for 16 bits application to regain control. In real world, it would
+be appropriate for VDDs to create a thread to do the actual DMA transfer
+(interactes with device driver) so that it won't block the 16bits
+application from running which will allow 16 bits application to provide
+useful information to the users(the application can read DMA registers and
+display the progress to the user and so forth)
+**/
+
+
+// This function perform DMA READ operation by using VDDRequestDMA service
+BOOLEAN
+SlowDMA()
+{
+ PBYTE DMABuffer, CurDMABuffer;
+ DWORD BufferLen;
+ DWORD PacketLen;
+
+ //first find out how big the buffer we need and then allocate
+ //the buffer from local heap.
+ //give 0 for buffer length to ask buffer length
+ //the length returned from VDDRequestDMA is the number of byte
+ //the DMA operation has to carry out. If the DMA channel is a 16bits
+ //the returned length will be 2 times the count value set to the
+ // DMA count register.
+
+ BufferLen = VDDRequestDMA(hVDD, DMA_CHANNEL, 0, 0);
+
+ CurDMABuffer = DMABuffer = (PBYTE)HeapAlloc(hVddHeap, 0, BufferLen);
+ if (!DMABuffer) {
+ OutputDebugString("VDD: Can't allocate heap memory for VDDRequestDMA");
+ return(FALSE);
+ }
+
+ // Since this is a DMA read operation(memory -> I/O), 16bits application
+ // should have provided necessary data in its local memory and the memory
+ // address can be derived from DMA base address register and page register.
+ // We don't want to deal with DMA register in this DMS operation scheme,
+ // therefore, we ask MVDM to copy the application data to our local buffer.
+ // It may be the case that applications requested a very big data transfer,
+ // 128K bytes for example, and we may not allocate enough buffer from our
+ // local heap. To overcome the problem, we have to break the data transfer
+ // to multiple subblocks and transfer each subblock by calling our device
+ // driver. Here we assume we can get the enough buffer from local heap.
+ //
+ // The device driver should take care of 64k wrap problem of DMA operation.
+
+ BufferLen = VDDRequestDMA(hVDD, DMA_CHANNEL, CurDMABuffer, BufferLen);
+
+ // MVDM updates DMA registers on each VDDReauestDMA. Therefore, after we
+ // made this call, the DMA registers maintained by MVDM have been set
+ // to the states as the DMA operation has been done(and it is not yet).
+ // It may be the case that the 16bits application regains control(as we
+ // create a different thread to do the operation) and issues another
+ // DMA operation before we(and the device driver) complete the current
+ // DMA operation. Keep away from using global variables in this
+ // case.
+
+ // We have source data in our local buffer, time to ask device
+ // driver to transfer the data to I/O. In case that the device driver
+ // can not transfer the whole buffer in a single service call, we
+ // call the device driver repeatly until we consume the entire buffer
+
+ while( BufferLen > 0) {
+ PacketLen = FakeDD_DMARead(CurDMABuffer, (WORD)BufferLen);
+ CurDMABuffer += PacketLen;
+ BufferLen -= PacketLen;
+ }
+
+
+ // DMA transfer completed; we simulate an interrupt to the 16bits
+ // application. Note that the DMA I/O ports should have been set
+ // accordingly by MVDM(through VDDReauestDMA).
+ // A real VDD may not do thing this way(VDD has to wait for device
+ // driver to finish the data transfer before returning to 16bits application
+ // Instead, the VDD and its associated device driver can keep synchronized
+ // by using semaphore or other events so that they can keep running in
+ // parallel and when receiving a event signal(triggered by the device driver to
+ // to notify completion of DMA operation) the VDD can then simulate the
+ // interrupt to the 16bits application
+
+ VDDSimulateInterrupt(DMA_INTERRUPT_PIC, DMA_INTERRUPT_LINE, 1);
+ HeapFree(hVddHeap, 0, DMABuffer);
+ return (TRUE);
+
+}
+
+ // This function simulate a DMA write(I/O to memory) operation
+ // It uses VDDQueryDMA and VDDSetDMA services to gain speed
+
+BOOLEAN
+FastDMA()
+{
+ ULONG DMAAddress;
+ DWORD Size, PacketLen;
+
+ VDD_DMA_INFO DMAInfo;
+
+ // Get the current DMA registers setting
+ VDDQueryDMA(hVDD, DMA_CHANNEL, &DMAInfo);
+
+ // if the DMA channel is not 1 16bits channel, adjust the size
+ Size = (DMAInfo.count << DMA_SHIFT_COUNT);
+
+ // seg:off of the DMA transfer address
+ DMAAddress = (((ULONG)DMAInfo.page) << (12 + 16))
+ + (DMAInfo.addr >> DMA_SHIFT_COUNT);
+
+ // Get DMA transfer 32bits linear address
+ DMAAddress = (ULONG) GetVDMPointer(DMAAddress, Size, 0);
+
+ while(Size) {
+ PacketLen = FakeDD_DMAWrite((PBYTE)DMAAddress, (WORD)Size);
+ DMAAddress += PacketLen;
+ Size -= PacketLen;
+ DMAInfo.addr += PacketLen;
+ DMAInfo.count -= (PacketLen >> DMA_SHIFT_COUNT);
+
+ // We have to upate the DMA registers even though we are not done
+ // the transfer yet. It is a good practice to update the DMA
+ // register each time we have really transferred data so that
+ // if the 16bits application regains control before DMA operation
+ // totally completed, it can get the partial result states and
+ // report to users.
+
+ VDDSetDMA(hVDD, DMA_CHANNEL, VDD_DMA_ADDR | VDD_DMA_COUNT,
+ &DMAInfo);
+ }
+
+ // see note on SlowDMA
+ VDDSimulateInterrupt(DMA_INTERRUPT_PIC, DMA_INTERRUPT_LINE, 1);
+ return(TRUE);
+}
+
+ // This function is a fake service which should be provided by device
+ // drivers in the real world. The thing we do here is to simulate
+ // a DMA operation transferring data from the given buffer to
+ // to the pre-defined memory mapped I/O port(DMA READ).
+
+WORD FakeDD_DMARead(PBYTE Buffer, WORD Size)
+{
+ IOBuffer[IO_PORT_DMA - IO_PORT_FIRST] = Buffer[0];
+ return (1);
+}
+
+ //This function calls device driver services to start DMA operation
+ //and requests the device driver to write the data to the given buffer.
+ //Since we are simulating the operation, we simply fill the buffer by
+ //reading the predefined I/O port
+
+WORD FakeDD_DMAWrite(PBYTE Buffer, WORD Size)
+{
+ *Buffer = IOBuffer[IO_PORT_DMA - IO_PORT_FIRST];
+ return(1);
+}
diff --git a/private/mvdm/vdd/samples/sample2/vdd/vdd.def b/private/mvdm/vdd/samples/sample2/vdd/vdd.def
new file mode 100644
index 000000000..d4bfcb805
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample2/vdd/vdd.def
@@ -0,0 +1,6 @@
+LIBRARY VDD
+
+DESCRIPTION 'Sample ISV VDD for NT-MVDM'
+
+EXPORTS
+ VDDInitialize
diff --git a/private/mvdm/vdd/samples/sample2/vdd/vdd.h b/private/mvdm/vdd/samples/sample2/vdd/vdd.h
new file mode 100644
index 000000000..fca8b1871
--- /dev/null
+++ b/private/mvdm/vdd/samples/sample2/vdd/vdd.h
@@ -0,0 +1,67 @@
+/* vdd.h - main include file for the VDD
+ *
+ */
+
+
+#include "windows.h"
+
+// VDD services header
+#include <vddsvc.h>
+
+// private macro definitions
+
+
+#define PAGE_SIZE 0x1000
+/*disconnected I/O value */
+#define FLOATING_IO 0xFF
+#define FLOATING_MIO 0xFF
+
+// I/O mapped I/O
+#define IO_PORT_FIRST 0x790
+#define IO_PORT_LAST 0x793
+#define IO_PORT_FIRE_DMA_SLOW IO_PORT_FIRST
+#define IO_PORT_FIRE_DMA_FAST IO_PORT_FIRST + 1
+#define IO_PORT_DMA IO_PORT_FIRST + 2
+#define IO_PORT_RANGE IO_PORT_LAST - IO_PORT_FIRST + 1
+
+// memory mapped I/O
+#define MIO_SEGMENT 0xC000
+#define MIO_PORT_FIRST 0
+#define MIO_PORT_LAST 7
+#define MIO_PORT_FIRE_DMA MIO_PORT_FIRST
+#define MIO_PORT_DMA MIO_PORT_FIRST + 1
+#define MIO_PORT_RANGE MIO_PORT_LAST - MIO_PORT_FIRST + 1
+#define MIO_ADDRESS ((((ULONG)MIO_SEGMENT) << 16) | MIO_PORT_FIRST)
+
+// DMA
+#define DMA_CHANNEL 1
+#define DMA_PORT_BASE 0
+#define DMA_SHIFT_COUNT 0
+#define DMA_INTERRUPT_LINE 2
+#define DMA_INTERRUPT_PIC ICA_SLAVE
+#define DMA_PORT_PAGE 0x83
+#define DMA_PORT_ADDR DMA_PORT_BASE + 2
+#define DMA_PORT_COUNT DMA_PORT_BASE + 3
+#define DMA_PORT_CMD DMA_PORT_BASE + 8
+#define DMA_PORT_REQUEST DMA_PORT_BASE + 9
+#define DMA_PORT_SNGLE_MASK DMA_PORT_BASE + 10
+#define DMA_PORT_MODE DMA_PORT_BASE + 11
+#define DMA_PORT_FLIPFLOP DMA_PORT_BASE + 12
+#define DMA_PORT_TEMP DMA_PORT_BASE + 13
+#define DMA_PORT_CLEARMASK DMA_PORT_BASE + 14
+#define DMA_PORT_WRTEMASK DMA_PORT_BASE + 15
+
+
+/* function prototype definitions */
+
+/* entry function of VDD */
+BOOL VddDllEntry(HANDLE hVdd, DWORD dwReason, LPVOID lpReserved);
+
+/* private functions */
+VOID MyInB(WORD, PBYTE);
+VOID MyOutB(WORD, BYTE);
+VOID MyMIOHandler(ULONG, ULONG);
+WORD FakeDD_DMARead(PBYTE, WORD);
+WORD FakeDD_DMAWrite(PBYTE, WORD);
+BOOLEAN SlowDMA();
+BOOLEAN FastDMA();
diff --git a/private/mvdm/vdd/samples/vsbd/makefile b/private/mvdm/vdd/samples/vsbd/makefile
new file mode 100644
index 000000000..5acbbd24c
--- /dev/null
+++ b/private/mvdm/vdd/samples/vsbd/makefile
@@ -0,0 +1 @@
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/mvdm/vdd/samples/vsbd/sources b/private/mvdm/vdd/samples/vsbd/sources
new file mode 100644
index 000000000..c7b3fd62b
--- /dev/null
+++ b/private/mvdm/vdd/samples/vsbd/sources
@@ -0,0 +1,25 @@
+##
+# Create an Ad Lib virtual device driver
+#
+
+MAJORCOMP=windows
+MINORCOMP=vsbd
+
+TARGETNAME=vsbd
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+UMTYPE=windows
+
+# Define libs we need and where to find them
+
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\winmm.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntvdm.lib
+
+C_DEFINES=-DUNICODE
+
+SOURCES=vdd.c
+
+DLLENTRY=DllEntryPoint
+
+DLLBASE=0x6BD00000
diff --git a/private/mvdm/vdd/samples/vsbd/vdd.c b/private/mvdm/vdd/samples/vsbd/vdd.c
new file mode 100644
index 000000000..bfba222b9
--- /dev/null
+++ b/private/mvdm/vdd/samples/vsbd/vdd.c
@@ -0,0 +1,1328 @@
+/****************************************************************************
+ *
+ * vdd.c
+ *
+ * Copyright (c) 1991 Microsoft Corporation. All Rights Reserved.
+ *
+ ***************************************************************************/
+
+
+#include <windows.h> // The VDD is just a win32 DLL
+#include <mmsystem.h> // Multi-media APIs
+#include <vddsvc.h> // Definition of VDD calls
+#include <stdio.h>
+
+/*
+ * Debugging
+ */
+
+#if DBG
+
+ int VddDebugLevel = 1;
+ int VddDebugCount = 0;
+ #define DEBUG_START 0
+
+
+ /***************************************************************************
+
+ Generate debug output in printf type format
+
+ ****************************************************************************/
+
+ void VddDbgOut(LPSTR lpszFormat, ...)
+ {
+ char buf[256];
+ va_list va;
+
+ if (++VddDebugCount < DEBUG_START) {
+ return;
+ }
+ OutputDebugStringA("Sound blaster 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 RESET_PORT 0x06
+ #define READ_DATA 0x0A
+ #define WRITE_PORT 0x0C // Data or command
+ #define WRITE_STATUS 0x0C
+ #define READ_STATUS 0x0E
+
+ #define SB_VERSION 0x200 // We pretend to be DSP version 2
+ #define SB_INTERRUPT 0x07 // Interrupt 7
+ #define SB_DMA_CHANNEL 0x01 // DMA Channel 1
+
+/*
+ * DSP commands
+ */
+
+ #define DSP_CARD_IDENTIFY 0xE0 // Doing card identification
+ #define DSP_GET_VERSION 0xE1 // dsp version command
+ #define DSP_SPEAKER_ON 0xD1 // speaker on command
+ #define DSP_SPEAKER_OFF 0xD3 // speaker off command
+ #define DSP_SET_SAMPLE_RATE 0x40 // set the sample rate
+ #define DSP_SET_BLOCK_SIZE 0x48 // set dma block size
+ #define DSP_WRITE 0x14 // Start non-auto DMA
+ #define DSP_WRITE_AUTO 0x1C // auto init output mode
+ #define DSP_READ 0x24 // Start non-auto read
+ #define DSP_READ_AUTO 0x2C // auto init mode input
+ #define DSP_HALT_DMA 0xD0 // stop dma
+ #define DSP_CONTINUE_DMA 0xD4 // continue halted dma
+ #define DSP_STOP_AUTO 0xDA // exit from auto init mode
+ #define DSP_MIDI_READ 0x31 // Interrupt driver midi input
+ #define DSP_MIDI_READ_UART 0x35 // Interrupt driver midi input (uart mode)
+ #define DSP_MIDI_TS_READ 0x37 // Midi time-stamped read
+ #define DSP_MIDI_WRITE 0x38 // Midi output
+ #define DSP_GENERATE_INT 0xF2 // Special code to generate a interrupt
+ #define DSP_DIRECT_WAVE_OUT 0x10 // polled output
+
+/*
+ * State machines
+ */
+
+ enum {
+ ResetNotStarted = 1,
+ Reset1Written
+ }
+ ResetState = ResetNotStarted;
+
+ enum {
+ WriteCommand = 1, // Initial state and after RESET
+ SetTimeConstant,
+ BlockSizeFirstByte,
+ BlockSizeSecondByte,
+ BlockSizeFirstByteWrite,
+ BlockSizeSecondByteWrite,
+ BlockSizeFirstByteRead,
+ BlockSizeSecondByteRead,
+ MidiWrite,
+ DirectWaveOut,
+ CardIdent
+ }
+ DSPWriteState = WriteCommand;
+
+ enum {
+ NothingToRead = 1,
+ Reset,
+ FirstVersionByte,
+ SecondVersionByte,
+ SpeakerStatus,
+ DirectWaveIn,
+ MidiRead,
+ ReadIdent
+ }
+ DSPReadState = NothingToRead;
+
+ BYTE IdentByte;
+
+ BOOL SpeakerOn = FALSE;
+ BOOL GetVersionFirst = TRUE; // First time getting version after RESET?
+ // (we emulate Thunderboard).
+
+ BOOL InterruptAcknowleged = TRUE;
+
+/*
+ * What gets read from the write status port
+ */
+
+ BYTE WriteStatus = 0x7F;
+
+/*
+ * Auto init setting
+ */
+ BOOL Auto;
+
+/*
+ * Initial settings
+ */
+
+ DWORD SBBlockSize = 0x800;
+ DWORD BlockSize = 0x800;
+ DWORD TimeConstant = 256 - 1000000 / 11025;
+
+/*
+ * 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};
+
+/*
+ * Globals
+ */
+
+ WORD BasePort; // Where the card is mapped
+ UINT WaveInDevice, WaveOutDevice;
+ CRITICAL_SECTION WaveDeviceCritSec; // Synchronize with call backs
+ HINSTANCE GlobalhInstance;
+ VDD_DMA_INFO DmaInfo;
+
+
+
+/*
+ * Function prototypes
+ */
+
+
+ #define NO_DEVICE_FOUND 0xFFFF
+
+ UINT FindDevice(BOOL Input);
+ BOOL SetupWave(PVOID TransferAddress);
+ BOOL StartTransfer(BOOL InputOrOutput, BOOL Auto);
+ BOOL TestWaveFormat(DWORD SamplesRate);
+ BOOL OpenWaveDevice(void);
+ void WaveCallback(HWAVEOUT hWave, UINT msg, DWORD dwUser, DWORD dw1,
+ DWORD dw2);
+ void CloseWaveDevice(void);
+ void KillWaveDevice(void);
+ void Continue(void);
+ void Pause(void);
+ PBYTE GetTransferAddress(void);
+ void SetDMAPosition(DWORD Position);
+ void GenerateInterrupt(void);
+ void SetSpeaker(BOOL);
+ void StopAuto(void);
+ DWORD GetSamplingRate(void);
+ void SetTerminalCount(BOOL);
+
+
+
+/*
+ * Send a command to the DSP
+ */
+
+ void WriteCommandByte(BYTE command)
+ {
+ switch (command) {
+ case DSP_GET_VERSION:
+ dprintf2(("Command - Get Version"));
+ DSPReadState = FirstVersionByte;
+ break;
+
+ case DSP_CARD_IDENTIFY:
+ dprintf2(("Command - Identify"));
+ DSPWriteState = CardIdent;
+ break;
+
+ case DSP_SPEAKER_ON:
+ dprintf2(("Command - Speaker ON"));
+ SetSpeaker(TRUE);
+ Pause();
+ break;
+
+ case DSP_SPEAKER_OFF:
+ dprintf2(("Command - Speaker OFF"));
+ SetSpeaker(FALSE);
+ Pause();
+ break;
+
+ case DSP_SET_SAMPLE_RATE:
+ DSPWriteState = SetTimeConstant;
+ break;
+
+ case DSP_SET_BLOCK_SIZE:
+ DSPWriteState = BlockSizeFirstByte;
+ break;
+
+ case DSP_WRITE:
+ dprintf2(("Command - Write - non Auto"));
+ DSPWriteState = BlockSizeFirstByteWrite;
+ break;
+
+
+ case DSP_DIRECT_WAVE_OUT:
+ dprintf2(("Command - Direct output"));
+ DSPWriteState = DirectWaveOut;
+ break;
+
+ case DSP_WRITE_AUTO:
+ dprintf2(("Command - Write - Auto"));
+ StartTransfer(FALSE, TRUE);
+ break;
+
+ case DSP_READ:
+ dprintf2(("Command - Read - non Auto"));
+ DSPWriteState = BlockSizeFirstByteRead;
+ break;
+
+ case DSP_READ_AUTO:
+ dprintf2(("Command - Read - Auto"));
+ StartTransfer(TRUE, TRUE);
+ break;
+
+ case DSP_HALT_DMA:
+ dprintf2(("Command - Halt DMA"));
+ Pause();
+ break;
+
+ case DSP_CONTINUE_DMA:
+ dprintf2(("Command - Continue DMA"));
+ Continue();
+ break;
+
+ case DSP_STOP_AUTO:
+ dprintf2(("Command - Stop DMA"));
+ StopAuto();
+ break;
+
+ case DSP_GENERATE_INT:
+ dprintf2(("Command - Generate interrupt DMA"));
+ GenerateInterrupt();
+ break;
+
+ default:
+ dprintf2(("Unrecognized DSP command %2X", command));
+ }
+ }
+
+/*
+ * Map a write to a port
+ */
+
+
+ void MyByteOut(WORD port, BYTE data)
+ {
+ dprintf3(("Received write to Port %4X, Data %2X", port, data));
+
+ switch (port - BasePort) {
+ case RESET_PORT:
+ if (data == 1) {
+ ResetState = Reset1Written;
+ } else {
+ if (ResetState == Reset1Written && data == 0) {
+ ResetState = ResetNotStarted;
+
+ /*
+ * OK - reset everything
+ */
+
+ CloseWaveDevice();
+
+ /*
+ * Reset state machines
+ */
+
+ DSPReadState = Reset;
+ DSPWriteState = WriteCommand;
+ GetVersionFirst = TRUE;
+ }
+ }
+ break;
+
+ case WRITE_PORT:
+ /*
+ * Use the state to see if it's data
+ */
+
+
+ switch (DSPWriteState) {
+ case WriteCommand:
+ WriteCommandByte(data);
+ break;
+
+ case CardIdent:
+ IdentByte = data;
+ DSPReadState = ReadIdent;
+ DSPWriteState = WriteCommand;
+ break;
+
+ case SetTimeConstant:
+ TimeConstant = (DWORD)data;
+ dprintf2(("Set sampling rate %d", GetSamplingRate()));
+ DSPWriteState = WriteCommand;
+ break;
+
+ case BlockSizeFirstByte:
+ SBBlockSize = (DWORD)data;
+ DSPWriteState = BlockSizeSecondByte;
+ break;
+
+ case BlockSizeSecondByte:
+ SBBlockSize = SBBlockSize + ((DWORD)data << 8) + 1;
+ DSPWriteState = WriteCommand;
+ dprintf2(("Block size set to 0x%x", SBBlockSize));
+ break;
+
+ case BlockSizeFirstByteWrite:
+ SBBlockSize = (DWORD)data;
+ DSPWriteState = BlockSizeSecondByteWrite;
+ break;
+
+ case BlockSizeSecondByteWrite:
+ SBBlockSize = SBBlockSize + ((DWORD)data << 8) + 1;
+ DSPWriteState = WriteCommand;
+ StartTransfer(FALSE, FALSE);
+ break;
+
+ case BlockSizeFirstByteRead:
+ SBBlockSize = (DWORD)data;
+ DSPWriteState = BlockSizeSecondByteRead;
+ break;
+
+ case BlockSizeSecondByteRead:
+ SBBlockSize = SBBlockSize + ((DWORD)data << 8) + 1;
+ DSPWriteState = WriteCommand;
+ StartTransfer(TRUE, FALSE);
+ break;
+
+ case DirectWaveOut:
+ case MidiWrite:
+ /*
+ * Just discard for now
+ */
+ DSPWriteState = WriteCommand;
+ break;
+
+ }
+ break;
+
+ }
+ }
+
+
+/*
+ * 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;
+
+ /*
+ * If we fail simulate nothing at the port
+ */
+
+ *data = 0xFF;
+
+ switch (port - BasePort) {
+ case WRITE_STATUS:
+
+ /*
+ * Can always write
+ */
+
+ *data = WriteStatus;
+ WriteStatus = 0x7F;
+ break;
+
+ case READ_STATUS:
+ /*
+ * See if we think there is something to read
+ */
+
+ InterruptAcknowleged = TRUE;
+ *data = DSPReadState != NothingToRead ? 0xFF : 0x7F;
+ break;
+
+ case READ_DATA:
+ /*
+ * The only useful things they can read are :
+ * 0xAA after RESET
+ *
+ * The DSP version
+ */
+
+ switch (DSPReadState) {
+ case NothingToRead:
+ *data = 0xFF;
+ break;
+
+ case ReadIdent:
+ *data = ~IdentByte;
+ DSPReadState = NothingToRead;
+ break;
+
+ case Reset:
+ *data = 0xAA;
+ DSPReadState = NothingToRead;
+ break;
+
+ case FirstVersionByte:
+ if (GetVersionFirst) {
+ *data = SB_VERSION / 256;
+ } else {
+ *data = 0x01; // Thunderboard version
+ }
+ DSPReadState = SecondVersionByte;
+ break;
+
+ case SecondVersionByte:
+ if (GetVersionFirst) {
+ *data = SB_VERSION % 256;
+ } else {
+ *data = 0x21; // Thunderboard version
+ }
+ GetVersionFirst = FALSE;
+ break;
+ }
+ }
+
+ dprintf3(("Received read from Port %4X, Returned Data %2X", port, *data));
+
+ }
+
+/*
+ * See if we can map ourselves somewhere
+ * Sets global BasePort if successful
+ */
+
+ BOOL InstallIoHook(HINSTANCE hInstance)
+ {
+ int i;
+ static WORD Ports[] = { 0x220, 0x210, 0x230, 0x240, 0x250, 0x260, 0x270 };
+
+ for (i = 0; i < sizeof(Ports) / sizeof(Ports[0]); i++ ) {
+ VDD_IO_PORTRANGE PortRange[2];
+
+ PortRange[0].First = Ports[i];
+ PortRange[0].Last = Ports[i] + 0x07;
+
+ PortRange[1].First = Ports[i] + 0x0A;
+ PortRange[1].Last = Ports[i] + 0x0F;
+
+ if (VDDInstallIOHook((HANDLE)hInstance, 2, PortRange, &handlers)) {
+
+ dprintf2(("Device installed at %3X", Ports[i]));
+ BasePort = Ports[i];
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+
+/*
+ * Remove our hook
+ */
+
+ VOID DeInstallIoHook(HINSTANCE hInstance)
+ {
+ VDD_IO_PORTRANGE PortRange[2];
+
+ PortRange[0].First = BasePort;
+ PortRange[0].Last = BasePort + 0x07;
+
+ PortRange[1].First = BasePort + 0x0A;
+ PortRange[1].Last = BasePort + 0x0F;
+
+ VDDDeInstallIOHook((HANDLE)hInstance, 2, PortRange);
+ }
+
+/*
+ * Standard DLL entry point routine.
+ */
+
+ BOOL DllEntryPoint(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
+ {
+ switch (Reason) {
+ case DLL_PROCESS_ATTACH:
+ GlobalhInstance = hInstance;
+ InitializeCriticalSection(&WaveDeviceCritSec);
+
+ /*
+ * Find suitable wave devices
+ */
+
+ WaveInDevice = FindDevice(TRUE);
+ WaveOutDevice = FindDevice(FALSE);
+
+ /*
+ * Must at least have a wave out device!
+ */
+
+ if (WaveOutDevice == NO_DEVICE_FOUND) {
+ return FALSE;
+ }
+
+ if (!InstallIoHook(hInstance)) {
+ dprintf2(("VDD failed to load"));
+ return FALSE;
+ } else {
+
+ dprintf2(("Loaded at port %3X", BasePort));
+ return TRUE;
+ }
+
+ case DLL_PROCESS_DETACH:
+ dprintf2(("Process detaching"));
+ CloseWaveDevice();
+ DeInstallIoHook(hInstance);
+ DeleteCriticalSection(&WaveDeviceCritSec);
+
+ return TRUE;
+
+ case DLL_THREAD_ATTACH:
+ dprintf2(("Connecting to thread %X", GetCurrentThreadId()));
+ return TRUE;
+
+ case DLL_THREAD_DETACH:
+ dprintf2(("Sound blaster VDD detaching from thread %X", GetCurrentThreadId()));
+ return TRUE;
+
+ default:
+ return TRUE;
+ }
+ }
+
+
+/*****************************************************************************
+ *
+ * Device manipulation and control routines
+ *
+ *****************************************************************************/
+
+/*
+ * Find a suitable device - either input or output. Returns
+ * 0xFFFF if none
+ */
+ UINT FindDevice(BOOL Input)
+ {
+ UINT NumDev;
+ UINT Device;
+
+ NumDev = (Input ? waveInGetNumDevs() : waveOutGetNumDevs());
+
+ for (Device = 0; Device < NumDev; Device++) {
+ if (Input) {
+ WAVEINCAPS wc;
+
+ if (MMSYSERR_NOERROR ==
+ waveInGetDevCaps(Device, &wc, sizeof(wc))) {
+
+ /*
+ * Need 11025 for input
+ */
+ if (wc.dwFormats & WAVE_FORMAT_1M08) {
+ return Device;
+ }
+ }
+ } else {
+ /* Output */
+
+ WAVEOUTCAPS wc;
+
+ if (MMSYSERR_NOERROR ==
+ waveOutGetDevCaps(Device, &wc, sizeof(wc))) {
+
+ /*
+ * Need 11025 and 22050 for output
+ */
+ if ((wc.dwFormats &
+ (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08)) ==
+ (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08)) {
+ return Device;
+ }
+ }
+ }
+ }
+
+ return NO_DEVICE_FOUND;
+ }
+
+/*
+ * Get sampling rate for time constant
+ */
+
+ DWORD GetSamplingRate(void)
+ {
+ /*
+ * Sampling rate = 1000000 / (256 - Time constant)
+ */
+
+ if (TimeConstant == 256 - 1000000 / 11025) {
+ return 11025;
+ } else {
+ if (TimeConstant == 256 - 1000000 / 22050) {
+ return 22050;
+ } else {
+ return 1000000 / (256 - TimeConstant);
+ }
+ }
+ }
+
+/*
+ * Reflect the virtual DMA counter back
+ */
+
+ void SetDMAPosition(DWORD Position)
+ {
+ VDD_DMA_INFO CurrentInfo;
+
+ CurrentInfo = DmaInfo;
+
+ CurrentInfo.count = (WORD)(((Auto ? BlockSize * 2 : BlockSize) - Position) - 1);
+ if ( Auto && CurrentInfo.count == 0xFFFF) {
+ CurrentInfo.count = (WORD)(BlockSize * 2 - 1);
+ }
+ CurrentInfo.addr += (DWORD)BlockSize;
+
+ dprintf2(("Dma Position = %x, count = %x", CurrentInfo.addr,
+ CurrentInfo.count));
+
+ VDDSetDMA((HANDLE)GlobalhInstance,
+ SB_DMA_CHANNEL,
+ VDD_DMA_COUNT | VDD_DMA_ADDR,
+ &CurrentInfo);
+ }
+
+/*
+ * Generate the device interrupt
+ */
+
+ void GenerateInterrupt(void)
+ {
+ /*
+ * Generate 1 interrupt on the master controller
+ * (how do devices normally 'detect' the hardware? and
+ * how can this VDD find a spare interrupt to 'install'
+ * the device?).
+ */
+
+ if (/*InterruptAcknowleged*/TRUE) {
+
+ /*
+ * Set to FALSE FIRST to avoid race conditions
+ */
+
+ InterruptAcknowleged = FALSE;
+ dprintf2(("Generating interrupt"));
+ VDDSimulateInterrupt(ICA_MASTER, SB_INTERRUPT, 1);
+ } else {
+ dprintf1(("Missed interrupt !"));
+ }
+
+ /*
+ * Set the status to see if more apps will work
+ */
+
+ WriteStatus = 0xFF;
+ }
+/*
+ * Get DMA transfer address
+ */
+
+ PBYTE GetTransferAddress(void)
+ {
+ PBYTE Address;
+
+ if (VDDQueryDMA((HANDLE)GlobalhInstance, SB_DMA_CHANNEL, &DmaInfo)) {
+ dprintf2(("DMA Info : addr %4X, count %4X, page %4X, status %2X, mode %2X, mask %2X",
+ DmaInfo.addr, DmaInfo.count, DmaInfo.page, DmaInfo.status,
+ DmaInfo.mode, DmaInfo.mask));
+
+// #if 0
+ BlockSize = (DWORD)DmaInfo.count + 1;
+
+ /*
+ * This optimization appears to fix some cases where apps miss
+ * interrupts
+ */
+
+ if (DmaInfo.count == 0) {
+ SetTerminalCount(FALSE);
+ GenerateInterrupt();
+ return (PBYTE)(-1L);
+ }
+// #endif
+
+ /*
+ * Don't continue if masked off or at terminal count
+ */
+
+#if 0 // What do we care?
+ if ((DmaInfo.mask & (1 << SB_DMA_CHANNEL)) ||
+ (DmaInfo.status & (1 << SB_DMA_CHANNEL))) {
+ dprintf2(("Wrong channel"));
+ return (PBYTE)(-1L);
+ }
+#endif
+
+ Address = GetVDMPointer(0, 0, 0);
+
+ dprintf3(("VDM starts at %8X", Address));
+ Address += (DWORD)DmaInfo.addr +
+ ((DWORD)DmaInfo.page << 16);
+
+ dprintf3(("Transfer address = %8X", (DWORD)Address));
+
+ return Address;
+ } else {
+ dprintf2(("Could not retrieve DMA Info"));
+ return (PBYTE)(-1L);
+ }
+ }
+
+
+
+/****************************************************************************
+ *
+ * Wave device control globals
+ *
+ ****************************************************************************/
+
+ HWAVE hWave;
+ BOOL WaveActive;
+ BOOL Input;
+ int Half;
+ int InterruptHalf;
+
+/*
+ * Auto init mode
+ */
+
+ HANDLE AutoThread = NULL;
+ HANDLE AutoEvent = NULL;
+ int BuffersToPlay = 0;
+
+ WAVEHDR WaveHdr[2];
+ PCMWAVEFORMAT WaveFormat = { { WAVE_FORMAT_PCM, 1, 0, 0, 1 }, 8};
+
+
+ DWORD AutoThreadEntry(PVOID Context)
+ {
+ int NumberOfBuffers;
+ Half = 0;
+ InterruptHalf = 0;
+
+ dprintf3(("Auto thread starting"));
+
+ for (; ; ) {
+ WaitForSingleObject(AutoEvent, INFINITE);
+
+ dprintf3(("Got event"));
+
+ EnterCriticalSection(&WaveDeviceCritSec);
+
+ dprintf2(("Playing %d buffers", BuffersToPlay));
+
+ NumberOfBuffers = BuffersToPlay;
+ BuffersToPlay = 0;
+
+ LeaveCriticalSection(&WaveDeviceCritSec);
+
+ while (NumberOfBuffers-- > 0) {
+
+ UINT rc;
+
+ rc =
+ (Input ? waveInAddBuffer : waveOutWrite)
+ (hWave, &WaveHdr[Half], sizeof(WAVEHDR));
+
+ if (rc != 0) {
+ dprintf1(("Got bad return on Add Buffer %x", rc));
+ }
+
+ Half = 1 - Half;
+ }
+ }
+
+ return 0;
+ }
+
+ void QuiesceAuto(void)
+ {
+ EnterCriticalSection(&WaveDeviceCritSec);
+
+ BuffersToPlay = 0;
+
+ LeaveCriticalSection(&WaveDeviceCritSec);
+ }
+
+/*
+ * Stop Auto mode
+ */
+
+ void StopAuto(void)
+ {
+ QuiesceAuto();
+
+ Auto = FALSE;
+ }
+
+/*
+ * Set the speaker state - BUGBUG for now we assume they're just doing it
+ * to avoid clicks so we don't do anything
+ */
+
+ void SetSpeaker(BOOL On)
+ {
+ return;
+ }
+
+/*
+ * Start a transfer (if possible)
+ */
+
+ BOOL StartTransfer(BOOL InputOrOutput, BOOL NewAuto)
+ {
+ PBYTE DMATransferAddress;
+
+ /*
+ * Set the status to see if more apps will work
+ */
+
+ WriteStatus = 0xFF;
+
+ /*
+ * We find where the data is - we know how long it is from
+ * the block size
+ */
+
+ DMATransferAddress = GetTransferAddress();
+
+ dprintf2(("Starting transfer from %8X", (DWORD)DMATransferAddress));
+
+ if (DMATransferAddress == (PBYTE)(-1L)) {
+ return FALSE;
+ }
+
+#if DBG
+
+ if (VddDebugLevel >= 3) {
+ int i;
+ for (i = 0; i < 64; i+= 8) {
+ dprintf(("Data : %2X %2X %2X %2X %2X %2X %2X %2X",
+ ((PBYTE)DMATransferAddress)[i],
+ ((PBYTE)DMATransferAddress)[i + 1],
+ ((PBYTE)DMATransferAddress)[i + 2],
+ ((PBYTE)DMATransferAddress)[i + 3],
+ ((PBYTE)DMATransferAddress)[i + 4],
+ ((PBYTE)DMATransferAddress)[i + 5],
+ ((PBYTE)DMATransferAddress)[i + 6],
+ ((PBYTE)DMATransferAddress)[i + 7]));
+ }
+ }
+
+#endif //DBG
+
+ /*
+ * If we're changing our type of device
+ */
+
+ if (InputOrOutput != Input) {
+
+ dprintf3(("Direction changed - close device"));
+ CloseWaveDevice();
+ Input = InputOrOutput;
+ }
+ /*
+ * Start the device if possible
+ */
+
+ KillWaveDevice();
+
+ Auto = NewAuto;
+
+ if (Auto) {
+ BlockSize /= 2;
+ }
+
+ if (SetupWave(DMATransferAddress)) {
+ /*
+ * Set the device as requesting
+ */
+
+ SetTerminalCount(TRUE);
+ }
+ }
+
+/*
+ * Pause
+ */
+
+ void Pause(void)
+ {
+ DWORD Position;
+ MMTIME mmTime;
+
+ QuiesceAuto();
+
+ if (hWave) {
+ (Input ? waveInStop : waveOutPause)(hWave);
+ } else {
+ return;
+ }
+
+ /*
+ * See where we've got to and reflect it in the DMA position
+ */
+
+ EnterCriticalSection(&WaveDeviceCritSec);
+
+ mmTime.wType = TIME_BYTES;
+
+ (Input ? waveInGetPosition : waveOutGetPosition)
+ ( hWave, &mmTime, sizeof(MMTIME));
+
+ Position = (mmTime.u.cb - 1) % (Auto ? WaveHdr[0].dwBufferLength * 2 :
+ WaveHdr[0].dwBufferLength) + 1;
+
+
+ LeaveCriticalSection(&WaveDeviceCritSec);
+
+ /*
+ * Reflect back the DMA position etc
+ */
+
+ SetDMAPosition(Position);
+ }
+
+/*
+ * Continue
+ */
+
+ void Continue(void)
+ {
+ if (hWave) {
+ (Input ? waveInStart : waveOutRestart)(hWave);
+ }
+ }
+
+/*
+ * Stop our wave device
+ */
+
+ void KillWaveDevice(void)
+ {
+ /*
+ * No synchrnoization required
+ */
+
+ if (hWave != NULL && WaveActive) {
+ (Input ? waveInReset : waveOutReset)(hWave);
+ WaveActive = FALSE;
+ }
+ }
+
+/*
+ * Shut down our wave device
+ */
+
+ void CloseWaveDevice(void)
+ {
+
+ dprintf3(("Closeing wave device"));
+
+ QuiesceAuto();
+
+ if (hWave != NULL) {
+ (Input ? waveInReset : waveOutReset)(hWave);
+
+ WaveActive = FALSE;
+
+ (Input ? waveInClose : waveOutClose)(hWave);
+
+ hWave = NULL;
+ }
+ }
+
+/*
+ * Set the terminal count bit
+ */
+
+ void SetTerminalCount(BOOL Start)
+ {
+ VDD_DMA_INFO CurrentInfo;
+
+ CurrentInfo.status = DmaInfo.status;
+
+ if (Start) {
+ CurrentInfo.status &= ~(1 << SB_DMA_CHANNEL); // Terminal count
+ CurrentInfo.status |= (0x10 << SB_DMA_CHANNEL); // Request
+ } else {
+ CurrentInfo.status |= (1 << SB_DMA_CHANNEL);
+ CurrentInfo.status &= ~(0x10 << SB_DMA_CHANNEL);
+ }
+ VDDSetDMA((HANDLE)GlobalhInstance, SB_DMA_CHANNEL,
+ VDD_DMA_STATUS,
+ &CurrentInfo);
+ }
+
+
+
+ void WaveCallback(HWAVEOUT hWave, UINT msg, DWORD dwUser, DWORD dw1,
+ DWORD dw2)
+ {
+ switch (msg) {
+
+ case MM_WOM_DONE:
+ case MM_WIM_DATA:
+
+ dprintf3(("Buffer complete"));
+
+ //
+ // If we use the critical section here we deadlock ourselves because
+ // the call sits behind us on the thread!
+ //
+
+ EnterCriticalSection(&WaveDeviceCritSec);
+
+ SetDMAPosition((!Auto || !InterruptHalf) ? BlockSize : BlockSize * 2);
+ if (Auto) {
+
+ UINT rc;
+
+ BuffersToPlay++;
+
+ SetEvent(AutoEvent);
+ InterruptHalf = 1 - InterruptHalf;
+ } else {
+ WaveActive = FALSE;
+ SetTerminalCount(FALSE);
+ }
+ GenerateInterrupt();
+ LeaveCriticalSection(&WaveDeviceCritSec);
+
+
+ break;
+ }
+ }
+
+ BOOL TestWaveFormat(DWORD SampleRate)
+ {
+ PCMWAVEFORMAT Format;
+
+ Format = WaveFormat;
+ Format.wf.nSamplesPerSec = SampleRate;
+ Format.wf.nAvgBytesPerSec = SampleRate;
+
+ return MMSYSERR_NOERROR ==
+ (Input ? waveInOpen : waveOutOpen)
+ (NULL,
+ (UINT)(Input ? WaveInDevice : WaveOutDevice),
+ &Format.wf,
+ 0,
+ 0,
+ WAVE_FORMAT_QUERY);
+ }
+
+ BOOL OpenWaveDevice(void)
+ {
+ UINT rc;
+
+ rc = (Input ? waveInOpen : waveOutOpen)
+ (&hWave,
+ (UINT)(Input ? WaveInDevice : WaveOutDevice),
+ &WaveFormat.wf,
+ (DWORD)WaveCallback,
+ 0,
+ CALLBACK_FUNCTION);
+
+ if (rc != MMSYSERR_NOERROR) {
+ dprintf1(("Failed to open wave device - code %d", rc));
+ }
+
+ return MMSYSERR_NOERROR == rc;
+ }
+
+ BOOL SetupWave(PVOID TransferAddress)
+ {
+ DWORD SampleRate;
+ UINT rc;
+
+ /*
+ * Make sure we've got a device - we may have one which does
+ * not match the current sampling rate.
+ */
+
+ if (TimeConstant != 0xFFFF) {
+ SampleRate = GetSamplingRate();
+ if (SampleRate != WaveFormat.wf.nSamplesPerSec) {
+
+ /*
+ * Search for a suitable format
+ */
+
+ if (!TestWaveFormat(SampleRate)) {
+ /*
+ * If this did not work it may be too fast
+ * or slow so move it into our compass
+ */
+
+ if (SampleRate > 22050) {
+ SampleRate = 22050;
+ } else {
+ if (SampleRate < 11025) {
+ SampleRate = 11025;
+ }
+ }
+
+ /*
+ * Device may only support discrete rates
+ */
+
+ if (!TestWaveFormat(SampleRate)) {
+ if (SampleRate > (11025 + 22050) / 2) {
+ SampleRate == 22050;
+ } else {
+ SampleRate = 11025;
+ }
+ }
+ }
+
+ /*
+ * Open the device with the new format if it's changed
+ */
+
+ if (SampleRate != WaveFormat.wf.nSamplesPerSec) {
+
+ dprintf3(("Format changed"));
+
+ CloseWaveDevice();
+
+ WaveFormat.wf.nSamplesPerSec = SampleRate;
+ WaveFormat.wf.nAvgBytesPerSec = SampleRate;
+
+ dprintf2(("Setting %d samples per second", SampleRate));
+
+ }
+ }
+ TimeConstant = 0xFFFF;
+ }
+
+ if (hWave == NULL) {
+ dprintf3(("Opening wave device"));
+ OpenWaveDevice();
+ } else {
+
+ dprintf3(("Resetting wave device prior to play"));
+ (Input ? waveInReset : waveOutReset)(hWave);
+ }
+
+ /*
+ * Set up any wave buffers etc if necessary
+ */
+
+ if (hWave) {
+ if (WaveHdr[0].lpData != (LPSTR)TransferAddress ||
+ BlockSize != WaveHdr[0].dwBufferLength) {
+
+ (Input ? waveInUnprepareHeader : waveOutUnprepareHeader)
+ (hWave, &WaveHdr[0], sizeof(WAVEHDR));
+
+ if (WaveHdr[1].dwFlags & WHDR_PREPARED) {
+ (Input ? waveInUnprepareHeader : waveOutUnprepareHeader)
+ (hWave, &WaveHdr[1], sizeof(WAVEHDR));
+ }
+
+ }
+
+ WaveHdr[0].lpData = (LPSTR)TransferAddress;
+ WaveHdr[0].dwBufferLength = BlockSize;
+ WaveHdr[1].lpData = (LPSTR)TransferAddress + BlockSize;
+ WaveHdr[1].dwBufferLength = BlockSize;
+
+ if (Auto && AutoThread == NULL) {
+
+ dprintf3(("Creating event"));
+
+ if (AutoEvent == NULL) {
+ AutoEvent = CreateEvent(NULL, 0, 0, NULL);
+
+ if (AutoEvent != NULL) {
+ DWORD Id;
+
+ dprintf2(("Creating thread"));
+
+ AutoThread = CreateThread(NULL,
+ 300,
+ AutoThreadEntry,
+ NULL,
+ 0,
+ &Id);
+ if (AutoThread == NULL) {
+ dprintf2(("Create thread failed code %d",
+ GetLastError()));
+ }
+ } else {
+ dprintf2(("Create event failed code %d",
+ GetLastError()));
+ }
+ }
+ }
+
+ if (!(WaveHdr[0].dwFlags & WHDR_PREPARED)) {
+
+ (Input ? waveInPrepareHeader : waveOutPrepareHeader)
+ (hWave, &WaveHdr[0], sizeof(WAVEHDR));
+ }
+
+ if (Auto) {
+ if (!(WaveHdr[1].dwFlags & WHDR_PREPARED)) {
+ (Input ? waveInPrepareHeader : waveOutPrepareHeader)
+ (hWave, &WaveHdr[1], sizeof(WAVEHDR));
+ }
+ }
+
+ /*
+ * Actually do it!
+ */
+
+ dprintf2(("Writing %d bytes to wave device",
+ WaveHdr[0].dwBufferLength));
+
+ rc = (Input ? waveInAddBuffer : waveOutWrite)
+ (hWave, &WaveHdr[0], sizeof(WAVEHDR));
+
+ if (rc != MMSYSERR_NOERROR) {
+ dprintf1(("Failed to write to /read from wave device - %d", rc));
+ }
+
+ if (Auto) {
+ (Input ? waveInAddBuffer : waveOutWrite)
+ (hWave, &WaveHdr[1], sizeof(WAVEHDR));
+ }
+
+ if (Input) {
+ waveInStart(hWave);
+ }
+ }
+
+ return TRUE;
+ }
diff --git a/private/mvdm/vdd/samples/vsbd/vsbd.def b/private/mvdm/vdd/samples/vsbd/vsbd.def
new file mode 100644
index 000000000..b48bfa573
--- /dev/null
+++ b/private/mvdm/vdd/samples/vsbd/vsbd.def
@@ -0,0 +1,8 @@
+LIBRARY VSBD
+
+DESCRIPTION 'SoundBlaster virtual device driver'
+
+EXETYPE WINDOWS
+
+EXPORTS
+ DllEntryPoint
diff --git a/private/mvdm/vdd/vddserv.doc b/private/mvdm/vdd/vddserv.doc
new file mode 100644
index 000000000..175fa7b7f
--- /dev/null
+++ b/private/mvdm/vdd/vddserv.doc
@@ -0,0 +1,791 @@
+
+
+Installable Virtual Device Driver (VDD) Support For NTVDM
+==========================================================
+
+Problem:
+--------
+There is a class of DOS applications which run on their
+own custom hardware. Generally these applications will
+have a pluggable card and a 16bit device driver for their
+card. As these cards are beyond the scope of normal PC
+architecture, NTVDM cannot virtualize them in a secure
+manner. So all such applications are not supported under
+NTVDM.
+
+Solution:
+---------
+
+If an ISV is writing an NT native device driver for such a
+card, it is technically quite simple to provide the support
+such that the DOS application runs unmodified and in a secure
+fashion. The vendor has to write a Virtual Device Driver (VDD)
+which will virtualize the card for the DOS application by calling
+the native device driver.
+
+
+ -----------------------
+ | |
+ | DOS Application | V86 mode
+ | |
+ -----------------------
+ I/O-map | ^ |Mem-Map | DMA
+ IO | | |IO |
+ -------------|-----------------------------
+ | | | |
+ V | V V
+ -----------------------
+ | | DMA |
+ | NTVDM -------| NT User mode code
+ | |
+ -----------------------
+ ^ |
+ | | Dispatches the event to VDD
+ | V
+ -----------------------
+ | |
+ | VDD | VDD is a DLL attached to NTVDM
+ | |
+ -----------------------
+ ^ |
+ | | Call the real driver
+ -------------|-----------------------------
+ | |
+ | V
+ -----------------------
+ | |
+ | NT Device Driver | Kernel mode
+ | |
+ -----------------------
+ ^ |
+ | | Carries out the operation with its card
+ | V
+ -----------------------
+ | |
+ | Plugged Card |
+ | |
+ -----------------------
+
+Following are the main work items to achieve the above solution:
+
+ a. Loading the VDD
+ b. Support for I/O mapped I/O
+ c. Support for Memory mapped I/O
+ d. Support for DMA operations
+ e. Register manipulation services
+ f. Memory accessing services
+ g. Interrupt simulation services
+ h. Miscelleneous services
+
+
+a. Loading the VDD:
+
+ The system administrator will add the command lines in the
+ \\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\CONTROL\"VirtualDeviceDrivers"
+ section of the registry for all the VDDs to be loaded in the
+ VDM process. The command line format is REG_MULT_SZ (i.e. ASCIIZZ).
+ This key will laways be present and install program just need to
+ add their VDD name.
+
+ [VirtualDeviceDrivers]
+ VDD = <full-path of the VDD1.DLL>\0<full-path of the VDD2>\0\0
+
+ VDD ACTION :
+
+ NTVDM will load all these VDDs from the registry at the VDM process
+ initialization time and call their initialization routine. VDDs should
+ make sure at this time that their respective NT device driver is
+ present and make all the resource allocations. The VDD handle passed
+ in this initialization routine will be the Id of the VDD when calling
+ the VDD services as described below.
+
+
+b. Support for I/O mapped I/O:
+
+ Following services will be provided for VDDs to deal with IO
+ ports:
+
+ BOOL VDDInstallIOHook (HANDLE, IO_PORT_RANGE, IO_PORT_HANDLERS);
+ BOOL VDDDeInstallIOHook (HANDLE, IO_PORT_RANGE);
+
+ The HANDLE is the one passed to the VDD in its DLLInit routine.
+ Only one IO hook may be installed for a given port, all subsequent
+ requests will fail. On DeInstalling, the default IO hook will be
+ placed, which means no one is hooked on those IO ports. IO_PORT_HANDLERS
+ should atleast provide a byte read and a byte write handler. In addition,
+ word and string handlers can also be provided. In the absense of word
+ or string handlers, these will be emulated using byte handlers.
+ A port has to be hooked for both read and write. VDDs should not hook
+ DMA ports as these are virtualized by NTVDM and services are provided
+ for VDDs to access DMA.
+
+ VDD Action: On an IO access (or on a series of IO accesses) the VDD
+ can check if it needs to start the DMA. If so it can call
+ the DMA services given in the following section. If it
+ does'nt require the DMA or if the DMA has transfered the
+ buffer, then the VDD can call its NT device driver to
+ complete the desired request. (Its also possible that the
+ VDD first requests the device driver and than using DMA
+ services copies the contents to the VDM buffer).
+
+
+c. Support for Memory mapped I/O:
+
+ Following services will be provided for VDDs to deal with their
+ memory mapped addresses:
+
+ BOOL VDDInstallMemoryHooks (HANDLE, ADDR_RANGE, MEMORY_HANDLER);
+ BOOL VDDDeInstallMemoryHooks (HANDLE, ADDR_RANGE);
+
+ The addr_range should be a valid range i.e. above RMSIZE and below system
+ rom. Only one Memory hook may be installed for a given range, all subsequent
+ requests will fail. On deinstalling the range, VDD will no longer get page
+ fault on those ranges. Memory_handler will
+ tell on which address the fault occured and whether it was a
+ read or write fault. On its return from the memory handler it will be
+ assumed that the fault was handled.
+
+
+ A port-range will actually result in a whole page to be reserved. That
+ means the page will not remain available to EMM for EMM page frames and
+ UMBs.
+
+ VDD Action: It can map the normal memory and let the app write to it.
+ Later they can use the WIN32 API's or its device driver
+ as per the case to deal the whole memory range.
+
+d. Support for DMA operations
+
+ Following DMA service will be provided for the VDD:
+
+ DWORD VDDRequestDMA (HANDLE, DMA_CHANNEL, BUFFER, TRANSFER_BYTES);
+ BOOL VDDQueryDMA (HANDLE, DMA_CHANNEL, DMA_INFO_BUFFER);
+ BOOL VDDSetDma (HANDLE, DMA_CHANNEL, INDEX, DMA_INFO_BUFFER);
+
+ NTVDM will control all the DMA ports and will maintain all the
+ information on per channel basis. There are two flavors in which
+ a VDD can carry-out the DMA operations. It can call VDDRequestDMA
+ and this service will interpret the DMA registers and do the
+ DMA transfer. It should be clear at this point that this will
+ involve two buffer copyings. For example, the VDD will ask the device
+ driver to trasnfer some data in a buffer (allocated by VDD) then it will
+ call this service to transfer this buffer to the address DOS application
+ has asked for (through DMA programming).
+
+ On the other hand, a VDD can collect all the DMA registers using
+ VDDQueryDMA and figure out where the DOS application has asked the
+ DMA to take place, in what mode and how much to transfer. Then it can
+ call the NT device driver with same address as asked by DOS app. In this
+ case there will be only one copying. VDD should use VDDSetDMA after
+ such an operation to update the DMA state.
+
+e. Register manipulation services:
+
+ See the reference section for details. There is a get and a set service
+ for each V86 registers.
+
+
+f: Memory Accessing services:
+
+ Following services are provided for Manipulating VDM's memory.
+
+ PVOID GetVDMPointer(ULONG Address, ULONG Size, BOOL ProtectedMode);
+ BOOL FreeVDMPointer(ULONG Address, ULONG Size, BOOL ProtectedMode);
+ BOOL FlushVDMPointer(ULONG Addr,
+ ULONG Size, PVOID Buffer, BOOL ProtectedMode);
+
+g: Interrupt simulation services
+
+ Following services is provided for simulating an interrupt to the VDM.
+
+ VOID VDDSimulateInterrupt (BYTE ms, BYTE line, WORD count);
+
+
+h. Miscellaneous Services
+
+ Following services will be provided for memory allocation/deallocation.
+
+ VDDAllocMem (HANDLE, ADDRESS, PAGES);
+ VDDFreeMem (HANDLE, ADDRESS, PAGES);
+
+ VDD will use VDDAllocMem when it gets a page fault on a page which
+ it has hooked using VDDInstallMemoryHook. Later it can free this memory
+ using VDDFreeMem. We are asking VDDs to use WIN32 API for all their needs
+ and here also a VDD could have used VirtualAlloc and VirtualFree. The
+ problem is that on a non-x86 machine this would'nt have worked because
+ on such a plateform the VDM memory is under the emulator's control and it
+ has to be told of such memory changes.
+
+ Following service will be provided for VDM termination.
+
+ VDDTerminateVDM (VOID);
+
+ VDD will use this service to terminate the VDM. For instance if a VDD
+ fails to allocate memory on a memory mapped IO, it may want to terminate
+ the VDM as its state is inconsistenet.
+
+ VDDs should always use these services to achieve plateform independence.
+
+
+
+---------------- REFERENCE SECTION -------------------------
+
+/** Basic typedefs of VDD IO hooks **/
+
+typedef VOID (*PFNVDD_INB) (WORD iport,BYTE * data);
+typedef VOID (*PFNVDD_INW) (WORD iport,WORD * data);
+typedef VOID (*PFNVDD_INSB) (WORD iport,BYTE * data,WORD count);
+typedef VOID (*PFNVDD_INSW) (WORD iport,WORD * data,WORD count);
+typedef VOID (*PFNVDD_OUTB) (WORD iport,BYTE data);
+typedef VOID (*PFNVDD_OUTW) (WORD iport,WORD data);
+typedef VOID (*PFNVDD_OUTSB) (WORD iport,BYTE * data,WORD count);
+typedef VOID (*PFNVDD_OUTSW) (WORD iport,WORD * data,WORD count);
+
+/** Array of handlers for VDD IO hooks. **/
+
+typedef struct _VDD_IO_HANDLERS {
+ PFNVDD_INB inb_handler;
+ PFNVDD_INW inw_handler;
+ PFNVDD_INSB insb_handler;
+ PFNVDD_INSW insw_handler;
+ PFNVDD_OUTB outb_handler;
+ PFNVDD_OUTW outw_handler;
+ PFNVDD_OUTSB outsb_handler;
+ PFNVDD_OUTSW outsw_handler;
+} VDD_IO_HANDLERS, *PVDD_IO_HANDLERS;
+
+/** Port Range structure **/
+typedef struct _VDD_IO_PORTRANGE {
+ WORD First;
+ WORD Last;
+} VDD_IO_PORTRANGE, *PVDD_IO_PORTRANGE;
+
+/** Memory mapped I/O handler. **/
+
+typedef VOID (*PVDD_MEMORY_HANDLER) (PVOID FaultAddress, ULONG RWMode);
+
+/** Buffer for returning DMA information **/
+typedef struct _VDD_DMA_INFO {
+ WORD addr;
+ WORD count;
+ WORD page;
+ BYTE status;
+ BYTE mode;
+ BYTE mask;
+} VDD_DMA_INFO, *PVDD_DMA_INFO;
+
+/*** VDDInstallIOHook - This service is provided for VDDs to hook the
+ * IO ports they are responsible for.
+ *
+ * INPUT:
+ * hVDD ; VDD Handle
+ * cPortRange; Number of VDD_IO_PORTRANGE structures
+ * pPortRange; Pointer to array of VDD_IO_PORTRANGE
+ * IOhandler : VDD handler for the ports.
+ *
+ * OUTPUT
+ * SUCCESS : Returns TRUE
+ * FAILURE : Returns FALSE
+ * GetLastError has the extended error information.
+ *
+ * NOTES:
+ * 1. The first one to hook a port will get control. Subsequent
+ * requests will be failed. There is no concept of chaining
+ * the hooks.
+ *
+ * 2. IOHandler must atleast provide a byte read and a byte write
+ * handler. Others can be NULL.
+ *
+ * 3. If word or string handlers are not provided, their effect
+ * will be emulated using byte handlers.
+ *
+ * 4. VDDs should not hook DMA ports. NTVDM manages it for all
+ * the clients and services are provided to perform DMA
+ * operations and to access and modify DMA data.
+ *
+ * 5. VDDs should not hook video ports as well. Such a hooking
+ * will succeed but there is no gurantee that the IO handler will
+ * get called.
+ *
+ * 6. Each Vdd is allowed to install only one set of IO hooks
+ * at a time.
+ *
+ * 7. Extended Error codes:
+ *
+ * ERROR_ACCESS_DENIED - One of the requested ports is already hooked
+ * ERROR_ALREADY_EXISTS - Vdd already has active IO port handlers
+ * ERROR_OUTOFMEMORY - Insufficient resources for additional VDD
+ * Port handler set.
+ * ERROR_INVALID_ADDRESS - One of the IO port handlers has an invalid
+ * address.
+ */
+BOOL VDDInstallIOHook (
+ HANDLE hVdd,
+ WORD cPortRange,
+ PVDD_IO_PORTRANGE pPortRange,
+ PVDD_IO_HANDLERS pIOFn
+);
+
+/*** VDDDeInstallIOHook - This service is provided for VDDs to unhook the
+ * IO ports they have hooked.
+ *
+ * INPUT:
+ * hVDD : VDD Handle
+ *
+ * OUTPUT
+ * None
+ *
+ * NOTES
+ *
+ * 1. On Deinstalling a hook, the defult hook is placed back on
+ * those ports. Default hook returns 0xff on reading
+ * and ignores the write operations.
+ *
+ */
+VOID VDDDeInstallIOHook (
+ HANDLE hVdd,
+ WORD cPortRange,
+ PVDD_IO_PORTRANGE pPortRange
+);
+
+/*** VDDInstallMemoryHook - This service is provided for VDDs to hook the
+ * Memory Mapped IO addresses they are resposible
+ * for.
+ *
+ * INPUT:
+ * hVDD : VDD Handle
+ * addr : Starting linear address
+ * count : Number of bytes
+ * MemoryHandler : VDD handler for the memory addresses
+ *
+ *
+ * OUTPUT
+ * SUCCESS : Returns TRUE
+ * FAILURE : Returns FALSE
+ * GetLastError has the extended error information.
+ *
+ * NOTES
+ * 1. The first one to hook an address will get the control. There
+ * is no concept of chaining the hooks. VDD should grab the
+ * memory range in its initialization routine. After all
+ * the VDDs are loaded, EMM will eat up all the remaining
+ * memory ranges for UMB support.
+ *
+ * 2. Memory handler will be called with the address on which the
+ * page fault occured and with a falg telling whether it was a
+ * read or write operation.
+ *
+ * 3. On returning from the hook handler it will be assumed that
+ * the page fault was handled and the return will go back to the
+ * VDM.
+ *
+ * 4. Installing a hook on a memory range will result in the
+ * consumption of memory based upon page boundaries. The Starting
+ * address is rounded down, and the count is rounded up to the
+ * next page boundary. The VDD's memory hook handler will be
+ * invoked for all addreses within the page(s) hooked. The page(s)
+ * will be set aside as mapped reserved sections, and will no
+ * longer be available for use by NTVDM or other VDDs. The VDD is
+ * permitted to manipulate the memory (commit, free, etc) as needed.
+ *
+ * 5. After calling the MemoryHandler, NTVDM will return to the
+ * faulting cs:ip in the 16bit app. If the VDD does'nt want
+ * that to happen it should adjust cs:ip appropriatly by using
+ * setCS and setIP.
+ *
+ * 6. Only one VDD will be allowed to have memory hooks in a page.
+ * In other words a page si owned by a VDD exclusively.
+ *
+ * 7. Extended Error codes:
+ *
+ * ERROR_ACCESS_DENIED - One of the requested ports is already hooked
+ * ERROR_OUTOFMEMORY - Insufficient resources.
+ */
+BOOL VDDInstallMemoryHook (
+ HANDLE hVDD,
+ PVOID pStart,
+ DWORD count,
+ PVDD_MEMORY_HANDLER MemoryHandler
+);
+
+/*** VDDDeInstallMemoryHook - This service is provided for VDDs to unhook the
+ * Memory Mapped IO addresses.
+ *
+ * INPUT:
+ * hVDD : VDD Handle
+ * addr : Starting linear address
+ * count : Number of addresses
+ *
+ * OUTPUT
+ * None
+ *
+ * NOTES
+ * 1. On Deinstalling a hook, the memory range becomes invalid.
+ * VDM's access of this memory range will cause a page fault.
+ *
+ * 2. Extended Error codes:
+ * ERROR_INVALID_PARAMETER - One of the parameter is invalid.
+ */
+BOOL VDDDeInstallMemoryHook (
+ HANDLE hVDD,
+ PVOID pStart,
+ DWORD count
+);
+
+/*** VDDRequestDMA - This service is provided for VDDs to request a DMA
+ * transfer.
+ *
+ * INPUT:
+ * hVDD VDD Handle
+ * iChannel DMA Channel on which the operation to take place
+ * Buffer Buffer where to or from transfer to take place
+ * length Transfer Count (in bytes)
+ * If Zero, returns the Current VDMA transfer count
+ * in bytes.
+ *
+ * OUTPUT
+ * DWORD returns bytes transferred
+ * if Zero and GetLastError is set means operation failed.
+ * if Zero and GetLastError is clear means VDMA transfer count
+ * was zero.
+ *
+ * NOTES
+ * 1. This service is intended for those VDDs which do not want to
+ * carry on the DMA operation on their own. Carrying on a DMA
+ * operation involves understanding all the DMA registers and
+ * figuring out what has to be copied, from where and how much.
+ *
+ * 2. This service will be slower than using VDDQueryDMA/VDDSetDMA and
+ * doing the transfer on your own.
+ *
+ * 3. Extended Error codes:
+ *
+ * ERROR_ALREADY_EXISTS - Vdd already has active IO port handlers
+ * ERROR_OUTOFMEMORY - Insufficient resources for additional VDD
+ * Port handler set.
+ * ERROR_INVALID_ADDRESS - One of the IO port handlers has an invalid
+ * address.
+ *
+ */
+DWORD VDDRequestDMA (
+ HANDLE hVDD,
+ WORD iChannel,
+ PVOID Buffer,
+ DWORD length
+);
+
+/*** VDDQueryDMA - This service is provided for VDDs to collect all the DMA
+ * data.
+ *
+ * INPUT:
+ * hVDD VDD Handle
+ * iChannel DMA Channel for which to query
+ * Buffer Buffer where information will be returned
+ *
+ * OUTPUT
+ * SUCCESS : Returns TRUE
+ * FAILURE : Returns FALSE
+ * GetLastError has the extended error information.
+ *
+ *
+ * NOTES
+ * 1. This service is intended for those VDD which are doing
+ * performance critical work. These VDD can do their own DMA
+ * transfers and avoid one extra buffer copying which is a
+ * overhead in using VDDRequestDMA.
+ *
+ * 2. VDDs should use VDDSetDMA to properly update the state of
+ * DMA after carrying on the operation.
+ *
+ * 3. Extended Error codes:
+ *
+ * ERROR_INVALID_ADDRESS - Invalid channel
+ *
+ */
+BOOL VDDQueryDMA (
+ HANDLE hVDD,
+ WORD iChannel,
+ PVDD_DMA_INFO pDmaInfo
+);
+
+/*** VDDSetDMA - This service is provided for VDDs to set the DMA data.
+ *
+ * INPUT:
+ * hVDD VDD Handle
+ * iChannel DMA Channel for which to query
+ * fDMA Bit Mask indicating which DMA data fields are to be set
+ * VDD_DMA_ADDR
+ * VDD_DMA_COUNT
+ * VDD_DMA_PAGE
+ * VDD_DMA_STATUS
+ * VDD_DMA_ALL (all Above)
+ * Buffer Buffer with DMA data
+ *
+ * OUTPUT
+ * SUCCESS : Returns TRUE
+ * FAILURE : Returns FALSE
+ * GetLastError has the extended error information.
+ *
+ * NOTES
+ *
+ * 1. Extended Error codes:
+ *
+ * ERROR_INVALID_ADDRESS - Invalid channel
+ *
+ */
+BOOL VDDSetDMA (
+ HANDLE hVDD,
+ WORD iChannel,
+ WORD fDMA,
+ PVDD_DMA_INFO pDmaInfo
+);
+
+/** VDDAllocMem - Allocates memory at a given virtual address.
+ *
+ * INPUT
+ * hVDD : VDD
+ * Address: Address where memory is to be allocated (between 640k and 1Mb)
+ * nBytes : Number of bytes to allocate
+ *
+ * OUTPUT
+ * SUCCESS : Returns TRUE
+ * FAILURE : Returns FALSE
+ * GetLastError has the extended error information.
+ * Notes:
+ * 1. VDDs have to use this service instead of WIN32 VirtualAlloc
+ * to be plateform independent. On non-x86 machines VDDAllocMem
+ * tells the CPU emulator about this memory allocation.
+ *
+ * 2. The address will be made page aligned downwards and nBytes will
+ * be streched upward to be page aligned.
+ *
+ * 3. Extended Error codes:
+ * ERROR_OUTOFMEMORY - Insufficient memory
+ * ERROR_INVALID_ADDRESS - Invalid address
+ */
+VOID VDDAllocMem (
+ HANDLE hVDD,
+ PVOID Address,
+ ULONG nBytes
+);
+
+/** VDDFreeMem - Free memory at a given virtual address.
+ *
+ * INPUT
+ * hVDD : VDD
+ * Address: Address where memory is to be freed
+ * nBytes : Number of bytes to free
+ *
+ * OUTPUT
+ * SUCCESS : Returns TRUE
+ * FAILURE : Returns FALSE
+ * GetLastError has the extended error information.
+ *
+ * Notes:
+ * 1. Extended Error codes:
+ * ERROR_INVALID_ADDRESS - Invalid address
+ *
+ * 2. The address will be made page aligned downwards and nBytes will
+ * be streched upward to be page aligned.
+ *
+ */
+VOID VDDFreeMem (
+ HANDLE hVDD,
+ PVOID Address,
+ ULONG nBytes
+);
+
+/** VDDTerminateVDM - Terminate the VDM.
+ *
+ * INPUT
+ * None
+ *
+ * OUTPUT
+ * None
+ *
+ * Notes:
+ * 1. VDD should call this service on encoutering a fatal error,
+ * such that VDDs state is inconsistent.
+ *
+ * 2. VDD can use MessageBox WIN32 API to putup a popup before
+ * terminating the VDM.
+ */
+VOID VDDTerminateVDM (
+ VOID
+);
+
+/** Register Manipulation services
+ *
+ */
+
+ULONG getEAX(VOID);
+USHORT getAX(VOID);
+UCHAR getAL(VOID);
+UCHAR getAH(VOID);
+ULONG getEBX(VOID);
+USHORT getBX(VOID);
+UCHAR getBL(VOID);
+UCHAR getBH(VOID);
+ULONG getECX(VOID);
+USHORT getCX(VOID);
+UCHAR getCL(VOID);
+UCHAR getCH(VOID);
+ULONG getEDX(VOID);
+USHORT getDX(VOID);
+UCHAR getDL(VOID);
+UCHAR getDH(VOID);
+ULONG getESP(VOID);
+USHORT getSP(VOID);
+ULONG getEBP(VOID);
+USHORT getBP(VOID);
+ULONG getESI(VOID);
+USHORT getSI(VOID);
+ULONG getEDI(VOID);
+USHORT getDI(VOID);
+ULONG getEIP(VOID);
+USHORT getIP(VOID);
+USHORT getCS(VOID);
+USHORT getSS(VOID);
+USHORT getDS(VOID);
+USHORT getES(VOID);
+USHORT getFS(VOID);
+USHORT getGS(VOID);
+ULONG getCF(VOID);
+ULONG getPF(VOID);
+ULONG getAF(VOID);
+ULONG getZF(VOID);
+ULONG getSF(VOID);
+ULONG getIF(VOID);
+ULONG getDF(VOID);
+ULONG getOF(VOID);
+USHORT getMSW(VOID);
+
+VOID setEAX(ULONG);
+VOID setAX(USHORT);
+VOID setAH(UCHAR);
+VOID setAL(UCHAR);
+VOID setEBX(ULONG);
+VOID setBX(USHORT);
+VOID setBH(UCHAR);
+VOID setBL(UCHAR);
+VOID setECX(ULONG);
+VOID setCX(USHORT);
+VOID setCH(UCHAR);
+VOID setCL(UCHAR);
+VOID setEDX(ULONG);
+VOID setDX(USHORT);
+VOID setDH(UCHAR);
+VOID setDL(UCHAR);
+VOID setESP(ULONG);
+VOID setSP(USHORT);
+VOID setEBP(ULONG);
+VOID setBP(USHORT);
+VOID setESI(ULONG);
+VOID setSI(USHORT);
+VOID setEDI(ULONG);
+VOID setDI(USHORT);
+VOID setEIP(ULONG);
+VOID setIP(USHORT);
+VOID setCS(USHORT);
+VOID setSS(USHORT);
+VOID setDS(USHORT);
+VOID setES(USHORT);
+VOID setFS(USHORT);
+VOID setGS(USHORT);
+VOID setCF(ULONG);
+VOID setPF(ULONG);
+VOID setAF(ULONG);
+VOID setZF(ULONG);
+VOID setSF(ULONG);
+VOID setIF(ULONG);
+VOID setDF(ULONG);
+VOID setOF(ULONG);
+VOID setMSW(USHORT);
+
+/** GetVDMPointer - Findout the linear address of a given VDM address
+ *
+ * INPUT
+ * Address - seg/sel:off (hi word has segment or selector and loword
+ * is offset)
+ * Size - Range of the pointer
+ * ProtectMode - If protectmode == TRUE its sel:off
+ * If protectmode == FALSE its seg:off
+ *
+ * OUTPUT
+ * Returns Linear address.
+ *
+ * NOTES:
+ * 1. VDDs should use this service to convert the address rather
+ * than shifting the seg by 4 and adding the offset. This makes
+ * them plateform independent. On non-x86 machine VDM's 0 and
+ * the process's 0 are different and the actual adddress conversion
+ * is provided by the CPU emulator.
+ */
+PVOID GetVDMPointer(
+ ULONG Address,
+ ULONG Size,
+ BOOL ProtectedMode
+);
+
+/** FlushVDMPointer - Flushes the contents (required because of emulator)
+ *
+ * INPUT
+ * Address - seg/sel:off (hi word has segment or selector and loword
+ * is offset)
+ * Size - Range of the pointer
+ * Buffer - Address returned by GetVDMPointer.
+ * ProtectMode - If protecmeode == TRUE its sel:off
+ * If protecmeode == FALSE its seg:off
+ *
+ * OUTPUT
+ * Returns Linear address.
+ *
+ * NOTES:
+ * 1. VDDs should use this service to make sure that on non-x86
+ * machines, the CPU emulator gets a chance to flush any data
+ * associated with a memory range.
+ */
+BOOL FlushVDMPointer(
+ ULONG Addr,
+ ULONG Size,
+ PVOID Buffer,
+ BOOL ProtectedMode
+);
+
+/** FreeVDMPointer - Frees a pointer previously returned by GetVDMPointer
+ *
+ * INPUT
+ * Address - seg/sel:off (hi word has segment or selector and loword
+ * is offset)
+ * Size - Range of the pointer
+ * ProtectMode - If protecmeode == TRUE its sel:off
+ * If protecmeode == FALSE its seg:off
+ *
+ * OUTPUT
+ * None
+ *
+ * NOTES:
+ * 1. FreeVDMPointer does FlushVDMPointer as well.
+ */
+BOOL FreeVDMPointer(
+ ULONG Address,
+ ULONG Size,
+ BOOL ProtectedMode
+);
+
+/** VDDSimulateInterrupt - Simulates an interrupt to the VDM.
+ *
+ * INPUT
+ * ms - Is either ICA_MASTER or ICA_SLAVE as appropriate
+ * line - Interrupt line
+ * count - allows a batch of interrupts to be delivered but will usually
+ * be 1.
+ *
+ * OUTPUT
+ * None
+ */
+VOID VDDSimulateInterrupt (
+ BYTE ms,
+ BYTE line,
+ WORD count
+);