From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/mvdm/vdd/dirs | 24 + private/mvdm/vdd/samples/adlibvdd/adlibvdd.def | 8 + private/mvdm/vdd/samples/adlibvdd/makefile | 1 + private/mvdm/vdd/samples/adlibvdd/sources | 24 + private/mvdm/vdd/samples/adlibvdd/vdd.c | 457 ++++++ private/mvdm/vdd/samples/adlibvdd/vdd.h | 37 + private/mvdm/vdd/samples/dirs | 29 + private/mvdm/vdd/samples/hpscan/hpscan16.asm | 138 ++ private/mvdm/vdd/samples/hpscan/hpscan16.inc | 29 + private/mvdm/vdd/samples/hpscan/makefile | 44 + private/mvdm/vdd/samples/mscdex/dirs | 23 + private/mvdm/vdd/samples/mscdex/readme.txt | 22 + private/mvdm/vdd/samples/mscdex/tsr/makefile | 49 + .../samples/mscdex/tsr/messages/usa/messages.inc | 2 + private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.asm | 246 ++++ private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.inc | 30 + private/mvdm/vdd/samples/mscdex/vdd/makefile | 11 + private/mvdm/vdd/samples/mscdex/vdd/mscdex.h | 313 +++++ private/mvdm/vdd/samples/mscdex/vdd/sources | 27 + private/mvdm/vdd/samples/mscdex/vdd/vcdex.c | 1474 ++++++++++++++++++++ private/mvdm/vdd/samples/mscdex/vdd/vcdex.def | 8 + private/mvdm/vdd/samples/mscdex/vdd/vcdex.h | 91 ++ private/mvdm/vdd/samples/mscdex/vdd/vcdex.rc | 48 + private/mvdm/vdd/samples/readme | 11 + private/mvdm/vdd/samples/sample1/dirs | 1 + private/mvdm/vdd/samples/sample1/fax16/fax16.asm | 348 +++++ private/mvdm/vdd/samples/sample1/fax16/makefile | 41 + private/mvdm/vdd/samples/sample1/fax32/fax32.c | 248 ++++ private/mvdm/vdd/samples/sample1/fax32/fax32.def | 8 + private/mvdm/vdd/samples/sample1/fax32/fax32.h | 29 + private/mvdm/vdd/samples/sample1/fax32/makefile | 11 + private/mvdm/vdd/samples/sample1/fax32/sources | 21 + private/mvdm/vdd/samples/sample2/16bits/16bits.asm | 217 +++ private/mvdm/vdd/samples/sample2/16bits/16bits.inc | 43 + private/mvdm/vdd/samples/sample2/16bits/makefile | 42 + private/mvdm/vdd/samples/sample2/dirs | 1 + private/mvdm/vdd/samples/sample2/vdd/makefile | 11 + private/mvdm/vdd/samples/sample2/vdd/sources | 23 + private/mvdm/vdd/samples/sample2/vdd/vdd.c | 385 +++++ private/mvdm/vdd/samples/sample2/vdd/vdd.def | 6 + private/mvdm/vdd/samples/sample2/vdd/vdd.h | 67 + private/mvdm/vdd/samples/vsbd/makefile | 1 + private/mvdm/vdd/samples/vsbd/sources | 25 + private/mvdm/vdd/samples/vsbd/vdd.c | 1328 ++++++++++++++++++ private/mvdm/vdd/samples/vsbd/vsbd.def | 8 + private/mvdm/vdd/vddserv.doc | 791 +++++++++++ 46 files changed, 6801 insertions(+) create mode 100644 private/mvdm/vdd/dirs create mode 100644 private/mvdm/vdd/samples/adlibvdd/adlibvdd.def create mode 100644 private/mvdm/vdd/samples/adlibvdd/makefile create mode 100644 private/mvdm/vdd/samples/adlibvdd/sources create mode 100644 private/mvdm/vdd/samples/adlibvdd/vdd.c create mode 100644 private/mvdm/vdd/samples/adlibvdd/vdd.h create mode 100644 private/mvdm/vdd/samples/dirs create mode 100644 private/mvdm/vdd/samples/hpscan/hpscan16.asm create mode 100644 private/mvdm/vdd/samples/hpscan/hpscan16.inc create mode 100644 private/mvdm/vdd/samples/hpscan/makefile create mode 100644 private/mvdm/vdd/samples/mscdex/dirs create mode 100644 private/mvdm/vdd/samples/mscdex/readme.txt create mode 100644 private/mvdm/vdd/samples/mscdex/tsr/makefile create mode 100644 private/mvdm/vdd/samples/mscdex/tsr/messages/usa/messages.inc create mode 100644 private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.asm create mode 100644 private/mvdm/vdd/samples/mscdex/tsr/mscdexnt.inc create mode 100644 private/mvdm/vdd/samples/mscdex/vdd/makefile create mode 100644 private/mvdm/vdd/samples/mscdex/vdd/mscdex.h create mode 100644 private/mvdm/vdd/samples/mscdex/vdd/sources create mode 100644 private/mvdm/vdd/samples/mscdex/vdd/vcdex.c create mode 100644 private/mvdm/vdd/samples/mscdex/vdd/vcdex.def create mode 100644 private/mvdm/vdd/samples/mscdex/vdd/vcdex.h create mode 100644 private/mvdm/vdd/samples/mscdex/vdd/vcdex.rc create mode 100644 private/mvdm/vdd/samples/readme create mode 100644 private/mvdm/vdd/samples/sample1/dirs create mode 100644 private/mvdm/vdd/samples/sample1/fax16/fax16.asm create mode 100644 private/mvdm/vdd/samples/sample1/fax16/makefile create mode 100644 private/mvdm/vdd/samples/sample1/fax32/fax32.c create mode 100644 private/mvdm/vdd/samples/sample1/fax32/fax32.def create mode 100644 private/mvdm/vdd/samples/sample1/fax32/fax32.h create mode 100644 private/mvdm/vdd/samples/sample1/fax32/makefile create mode 100644 private/mvdm/vdd/samples/sample1/fax32/sources create mode 100644 private/mvdm/vdd/samples/sample2/16bits/16bits.asm create mode 100644 private/mvdm/vdd/samples/sample2/16bits/16bits.inc create mode 100644 private/mvdm/vdd/samples/sample2/16bits/makefile create mode 100644 private/mvdm/vdd/samples/sample2/dirs create mode 100644 private/mvdm/vdd/samples/sample2/vdd/makefile create mode 100644 private/mvdm/vdd/samples/sample2/vdd/sources create mode 100644 private/mvdm/vdd/samples/sample2/vdd/vdd.c create mode 100644 private/mvdm/vdd/samples/sample2/vdd/vdd.def create mode 100644 private/mvdm/vdd/samples/sample2/vdd/vdd.h create mode 100644 private/mvdm/vdd/samples/vsbd/makefile create mode 100644 private/mvdm/vdd/samples/vsbd/sources create mode 100644 private/mvdm/vdd/samples/vsbd/vdd.c create mode 100644 private/mvdm/vdd/samples/vsbd/vsbd.def create mode 100644 private/mvdm/vdd/vddserv.doc (limited to 'private/mvdm/vdd') 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 // The VDD is just a win32 DLL +#include // Definition of VDD calls +#include "vdd.h" // Common data with kernel driver +#include + +/* + * 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 +#include +#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; iHandle != 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; iHandle = 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; DriveUnit = 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; DriveRQ %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 +#include + +/*-----------------------------------------------*/ +/* 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 + +// 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 // The VDD is just a win32 DLL +#include // Multi-media APIs +#include // Definition of VDD calls +#include + +/* + * 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 = \0\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 +); -- cgit v1.2.3