Source file: /~heha/argon/multimed.zip/SNDBLST/INITA.ASM

        page 60, 132
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   inita.asm
;
;   Copyright (c) 1991-1992 Microsoft Corporation.  All rights reserved.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        .286

        .xlist
        include cmacros.inc                   
        include windows.inc
        include mmsystem.inc
        include mmddk.inc
        include sndblst.inc
        include vsbd.inc
        .list

VDS_VERSION_REQD    equ 0200h           ; V2.00 of VDS for auto-DMA support

        ?PLM=1                          ; Pascal calling convention
        ?WIN=0                          ; NO! Windows prolog/epilog code

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   extrn declarations
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 

        externA <__WinFlags>                ; kernel

        externD <glpVSBDEntry>              ; commona.asm
        externFP <vsbdAcquireSoundBlaster>  ; commona.asm
        externFP <vsbdReleaseSoundBlaster>  ; commona.asm

        externFP <timeGetTime>              ; MMSystem

        externFP <GlobalAlloc>              ; Windows
        externFP <GlobalFree>               ; Windows
        externFP <GetSelectorBase>          ; Windows
        externFP <GlobalPageLock>           ; Windows
        externFP <GlobalPageUnlock>         ; Windows

        externFP <dspRead>                  ; sndblst.asm
        externFP <dspWrite>                 ; sndblst.asm
        externFP <dspISR>                   ; sndblst.asm

        externFP <dspReset>                 ; commona.asm
        externFP <dspSpeakerOn>             ; commona.asm

        externB <gbDMAPhysPage>             ; sndblst.asm
        externW <gwDMAOffset>               ; sndblst.asm
        externW <gwDMAPhysAddr>             ; sndblst.asm
        externW <gwDMASelector>             ; sndblst.asm
        externW <gwMidiOutDelay>            ; sndblst.asm
        externB <gbIntUsed>                 ; sndblst.asm

        externW <gbMidiOutFlags>            ; midia.asm
        externW <gbMidiInFlags>             ; midia.asm

        externW <gbWaveOutFlags>            ; wavea.asm
        externW <gbWaveInFlags>             ; wavea.asm

        externB <_gfWaveOutPaused>          ; wavefix.c

        externB <_gfVerifyInt>              ; initc.c

        externFP <widStop>                  ; wavea.asm
        externFP <widStart>                 ; wavea.asm
        externFP <wodPause>                 ; wavea.asm
        externFP <wodResume>                ; wavea.asm
        externFP <midStop>                  ; midia.asm
        externFP <midStart>                 ; midia.asm

        externFP <widAcquireHardware>       ; wavea.asm
        externFP <widReleaseHardware>       ; wavea.asm
        externFP <wodAcquireHardware>       ; wavea.asm
        externFP <wodReleaseHardware>       ; wavea.asm

        externFP <midAcquireHardware>       ; midia.asm
        externFP <midReleaseHardware>       ; midia.asm
        externFP <modAcquireHardware>       ; midia.asm
        externFP <modReleaseHardware>       ; midia.asm

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   segmentation
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

IFNDEF SEGNAME
        SEGNAME equ <_TEXT>
ENDIF

createSeg %SEGNAME, CodeSeg, word, public, CODE

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   data segment
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

sBegin Data
        globalD gdwOldISR,           0
        globalW gwPort,             -1  ; gwPort gbInt and gbDMAChannel *MUST*
        globalB gbInt,              -1  ; ...be initialized to -1, -1, and 1!!!
        globalB gbIntMask,           0  ; saved interrupt mask
        globalB gbDMAChannel,        1
        globalB gbDMAPageReg,        0
        globalW gwEOICommands,       0
        globalW gwDSPVersion,        0  ; DSP version of card we are ENABLED for
        globalW gwMidiInPersistence, 0
        globalB _gfEnabled,          0

        globalB gbIntCount,          0
        globalD isrTestNext,         0
sEnd Data

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   code segment
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

sBegin CodeSeg
        assumes cs, CodeSeg
        assumes ds, Data
        assumes es, nothing

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL
;
; @asm WORD | CheckForMCA | This function returns non-zero if the 
;     machine is an MCA machine.
;
; @rdesc If the machine architecture is Micro-Channel, then non-zero is
;   returned.  Otherwise, zero is returned.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

GETSYSTEMCONFIG     equ 0C0h            ; System config BIOS function

SysDescStruc STRUC
SD_len      dw  ?
SD_model    db  ?
SD_submodel db  ?
SD_ROM_rev  db  ?
SD_feature1 db  ?
SD_feature2 db  ?
SD_feature3 db  ?
SD_feature4 db  ?
SD_feature5 db  ?
SysDescStruc ENDS

; Feature byte 1 bits assignments:

SF1_FD_uses_DMA3        = 10000000b
SF1_PIC_2_present       = 01000000b
SF1_RealTimeClock       = 00100000b
SF1_INT15s_called       = 00010000b
SF1_ExtEventWait        = 00001000b
SF1_EBIOS_allocated     = 00000100b
SF1_MicroChnPresent     = 00000010b

        assumes ds, nothing
        assumes es, nothing

cProc CheckForMCA <NEAR, PASCAL, PUBLIC> <>
cBegin

        xor     cx, cx
        stc                           ; set this in case BIOS doesn't
        mov     ah, GETSYSTEMCONFIG
        int     15h
        jc      CFM_NoMicroChannel    ; successful call?
        or      ah, ah                ; AH = 0 on all modern PC's if call worked
        jnz     CFM_NoMicroChannel
        test    es:[bx.SD_feature1], SF1_MicroChnPresent
        jz      CFM_NoMicroChannel
        inc     cx                    ; return true

CFM_NoMicroChannel:

        mov     ax, cx        

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   WORD NEAR PASCAL vdmadGetVersion(void)
;
;   DESCRIPTION:
;       This function gets the version number of the Virtual DMA Services
;       provided by VDMAD.  This is used to determine if it is safe to do
;       the auto-init DMA.  Under pre 3.1.54 builds of enhanced mode
;       Windows, drivers that programmed auto-init DMA transfers needed to
;       use VADMAD.386.
;
;       We return the 'implementation version number' which should be
;       >= 0103h if VDMAD supports auto-init DMA.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc vdmadGetVersion <NEAR, PASCAL, PUBLIC> <>
        LocalW  wVersion
cBegin

        pusha                       ; int 4Bh trashes a bunch of registers

        mov     ax, 8102h           ; VDS Get Version
        xor     dx, dx              ; flags should be zero
        int     4Bh                 ; fire it off
        jnc     gvExit              ; carry clear if no error

        xor     cx, cx              ; show that there was an error

gvExit:
        mov     wVersion, cx        ; implementation version (needs to be 0200h)
        popa

        mov     ax, wVersion        ; return version number

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL
;
; @asm dspAbsRead | Read a byte from the DSP at port base given.
;
; @rdesc If time out occurs before the DSP is ready then the carry flag
;     is set and no value is returned otherwise the carry flag is cleared
;     and the data value returned in AL.
;
; @parm WORD | wPort | port base to read from.
;
; @comm The timeout value is very machine dependent.
;
; @comm USES: FLAGS, AL--all other registers are preserved.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, nothing
        assumes es, nothing

cProc dspAbsRead <NEAR, PASCAL, PUBLIC> <>
        ParmW   wPort
cBegin
        push    cx
        push    dx

        mov     dx, wPort
        add     dx, DSP_PORT_DATAAVAIL  ; point to data available status port
        mov     cx, 1000                ; set time out value

abs_Read_Wait_Data:
        in      al, dx                  ; get status
        test    al, 80h                 ; Q: MSB set?
        jnz     abs_Read_Data_Avail     ;   Y: jump if data ready
        loop    abs_Read_Wait_Data

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   timed out
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        stc 
        jmp     abs_Read_Exit

abs_Read_Data_Avail:
        sub     dx, (DSP_PORT_DATAAVAIL - DSP_PORT_RDDATA)  ; base + A
        in      al, dx                                      ; get data byte
        clc

abs_Read_Exit:
        pop     dx
        pop     cx

cEnd dspAbsRead

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL
;
; @asm dspAbsWrite | Write a command or data byte to the DSP at the port
;       base given.
;
; @parm WORD | wPort | Port base to write byte to.
;
; @parm BYTE | bByte | The byte to be written.
;
; @comm Carry is set on exit if it times out. The reason the timeout loop
;    is so big is that the SpeakerOff command can take up to 220 msec
;    to execute - if you time out before that the next few DSP commands
;    you write won't work.
;
; @comm Uses only FLAGS--all other registers are preserved.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, nothing
        assumes es, nothing

cProc dspAbsWrite <NEAR, PASCAL, PUBLIC> <ax, cx, dx>
        ParmW   wPort
        ParmB   bByte
cBegin
        mov     dx, wPort
        add     dx, DSP_PORT_WRBUSY     ; point to data status port

        xor     cx, cx                  ; timeout loop counters
        mov     ah, 10

abs_Write_Wait:

        in      al, dx
        test    al, 80h                 ; test busy bit
        jz      abs_Write_Ready         ; exit if not busy

        loop    abs_Write_Wait          ; loop if not timed out
        dec     ah
        jnz     abs_Write_Wait

        stc                             ; set carry to show time out
        jmp     abs_Write_Exit

abs_Write_Ready:

        mov     al, bByte
        out     dx, al                  ; write it (same port)
        .errnz (DSP_PORT_WRBUSY - DSP_PORT_WRDATA)
        clc                             ; no error

abs_Write_Exit:

cEnd dspAbsWrite

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   BOOL NEAR PASCAL vsbdGetEntryPoint(void)
;
;   DESCRIPTION:
;       This function is responsible for setting up the entry point to 
;       the pmode API of VSBD.386 _if_ it is installed.  If VSBD is not
;       installed, zero is returned; a non-zero return means that VSBD
;       is around and responding.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc vsbdGetEntryPoint <NEAR, PASCAL, PUBLIC> <si, di>
cBegin

        xor     di, di                  ; zero ES:DI before call
        mov     es, di
        mov     ax, 1684h               ; get device API entry point
        mov     bx, VSBD_Device_ID      ; virtual device ID
        int     2Fh                     ; call WIN/386 INT 2F API
        mov     ax, es                  ; return farptr
        or      ax, di
        jz      gvsbd_exit
        mov     [glpVSBDEntry].off, di
        mov     [glpVSBDEntry].sel, es

gvsbd_exit:
cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   WORD NEAR PASCAL vsbdGetSoundBlasterInfo(void)
;
;   DESCRIPTION:
;       This function calls VSBD to get the configuration of the SB
;       card that it is virtualizing.  If VSBD is not installed, the carry
;       is set and AX is zero.
;
;   ENTRY:
;       Nothing.
;
;   EXIT:
;       IF Carry Clear
;           AX = DSP Version, major = high byte, minor = low byte
;           BX = Flags
;           CH = IRQ
;           CL = DMA Channel
;           DX = Port base being virtualized
;       ELSE
;           carry set, AX = 0 (VSBD not around)
;
;   USES:
;       Flags, AX, BX, CX, DX
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc vsbdGetSoundBlasterInfo <NEAR, PASCAL, PUBLIC> <>
cBegin
        mov     ax, [glpVSBDEntry].off      ; Q: is VSBD installed?
        or      ax, [glpVSBDEntry].sel
        stc                                 ;   (assume failure)
        jz      vsbd_Get_Info_Done          ;   N: then leave (return 0)

        mov     dx, SB_API_Get_Sound_Blaster_Info
        call    [glpVSBDEntry]

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Return (carry clear, success):
;       AX = DSP version
;       BX = Flags: fSB_ASB_Acquire_DSP, fSB_ASB_Acquire_AdLib_Synth
;       CH = IRQ
;       CL = DMA channel
;       DX = Base port of SB
;   Return (carry set, fail):
;       AX = 0
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        D1 <VSBD_Info: DSP #AX  Flags #BX  IRQ/DMA #CX  Port #DX>
        clc

vsbd_Get_Info_Done:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   DWORD FAR PASCAL InitGetConfiguration(void)
;
;   DESCRIPTION:
;       This function simply gets the current configuration _after_
;       the driver has been Enabled.  This is used by the configuration
;       dialog (which is the only C code that requires access to this
;       hardware dependent info).
;
;   ENTRY:
;       Nothing.
;
;   EXIT:
;       AX = port base
;       DL = int
;       DH = DMA Channel
;
;       NOTE: if the driver is not Enabled, AX will be -1, DL will be -1,
;       and DH will be the default DMA channel (1).
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc InitGetConfiguration <FAR, PUBLIC> <>
cBegin

        mov     ax, [gwPort]
        mov     dl, [gbInt]
        mov     dh, [gbDMAChannel]

        D1 <InitGetConfiguration: wPort:#AX  bInt:#DL  bDMAChannel:#DH>

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   WORD NEAR PASCAL InitPreliminary(void)
;
;   DESCRIPTION:
;       This function is called before allowing the driver to install.  It
;       is responsible for checking for critical errors that keep this 
;       driver from working.  For example, this Sound Blaster driver will
;       not work correctly on an MCA machine--so we do NOT allow it to
;       load on an MCA.
;
;       The following checks are made:
;           o check for MCA
;           o check for auto-init support in VDMAD.386 (>= V2.00 of VDS)
;
;       In addition, communication with VSBD is setup and the CPU is
;       profiled for software timing loops.
;
;   ENTRY:
;       Nothing.
;
;   EXIT:
;       If success,
;           zero is returned.
;       else failure,
;           the non-zero IDS_xxx value is returned. If this function
;           fails, it is assumed to be *critical* error and the driver
;           should NOT load.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc InitPreliminary <NEAR, PUBLIC> <si, di>
cBegin

        D1 <InitPreliminary>

        cCall   CheckForMCA
        or      ax, ax                      ; Q: are we running on MCA?
        jz      ipre_Not_MCA                ;   N: good!

        mov     ax, IDS_ERRMCANOTSUPPORTED
        jmp     ipre_Exit

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   If we are running in Enhanced mode, we need to get the entry point to
;   VSBD (if it is installed) and verify that VDMAD is new enough to support
;   auto-init DMA.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

ipre_Not_MCA:

        mov     cx, [__WinFlags]
        test    cx, WF_ENHANCED             ; Q: are we running in enh mode?
        jz      ipre_Profile_CPU            ;   N: then profile CPU for MIDI
        
        cCall   vsbdGetEntryPoint           ; get entry if installed

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Starting with Win 3.1.55, auto-init DMA is implemented correctly.
;   The implementation version number of VDS is 2.00 for this new
;   VDMAD.  This driver will not work on an old version of VDMAD!
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   vdmadGetVersion
        cmp     ax, VDS_VERSION_REQD        ; Q: VDMAD support auto-init?
        jae     ipre_Profile_CPU

        mov     ax, IDS_ERROLDVDMAD
        jmp     ipre_Exit                   ; return error

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Due to a timing bug in the Sound Blaster 2.x and 3.x DSP's, we need to
;   be able to delay >= 400us between MIDI data bytes if MIDI input and 
;   output are going at the same time.  This gives rough estimate of 500us.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

ipre_Profile_CPU:

;BUG <ipre_Profile_CPU: should only do this for MIDI...>

        cCall   timeGetTime
        mov     di, dx
        mov     si, ax

        xor     cx, cx
        loop    $

        cCall   timeGetTime
        sub     ax, si
        sbb     dx, di                     ; DX:AX == num milliseconds
        mov     bx, ax
        shl     bx, 1                      ; assume not > ~32 seconds
        jz      ipre_Profile_CPU_Uh_Oh     ; if 0 then timeGetTime not accurate!
        mov     ax, -1
        div     bx

ipre_Profile_CPU_Uh_Oh:

        mov     [gwMidiOutDelay], ax

        D1 <MODELAY: #AX>

        xor     ax, ax                      ; succeed!
        errn$ ipre_Exit

ipre_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   DWORD FAR PASCAL InitVerifyConfiguration(wPort, bInt, bDMAChannel)
;
;   DESCRIPTION:
;       This function is responsible for verifying that the configuration
;       information is correct.  We attempt to detect the hardware and
;       gaurantee that the configuration information is valid.  This function
;       should be written in such a way that it can be called _at any time_
;       (enabled or disabled) to verify settings.  Believe it or not, it is
;       ok (but NOT desirable) to crash here... if we DO crash, the .INI
;       settings will NOT be written, so the user can reboot their machine.
;
;       DO EVERYTHING POSSIBLE TO MAKE THIS FUNCTION AS SAFE AND CRASH
;       PROOF AS POSSIBLE!  DO NOT DO STUPID THINGS!  THIS IS *VERIFYING*
;       SETTINGS-->NOT AUTO-DETECTING THE HARDWARE.
;
;   ENTRY:
;       ParmW   wPort       :   Port base to verify
;       ParmB   bInt        :   IRQ to verify
;       ParmB   bDMAChannel :   DMA channel to verify
;
;   EXIT:
;       If success,
;           AX = 0
;           DX = DSP Version
;       else failure,
;           AX = non-zero IDS_xxx error
;           DX = DSP Version (invalid for bad port)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc InitVerifyConfiguration <FAR, PUBLIC> <>
        ParmW   wPort
        ParmB   bInt
        ParmB   bDMAChannel

        LocalW  wDSPVersion
cBegin
        xor     ax, ax
        mov     wDSPVersion, ax

        mov     dx, wPort
        mov     al, bInt
        mov     ah, bDMAChannel
        D1      <VerifyConfig: wPort:#DX  bInt:#AL  bDMAChannel:#AH>

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   if the driver is enabled then verify by checking globals
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        test    [_gfEnabled], 0FFh
        jz      VerifyConfig_Not_Enabled

VerifyConfig_Enabled:
        mov     ax, IDS_ERRBADPORT      ; return improper port!
        mov     dx, wPort
        cmp     dx, [gwPort]            ; the same as enabled value?
        jne     VerifyConfig_Exit       ; no.. fail with error
        mov     dx, [gwDSPVersion]
        mov     wDSPVersion, dx 

        mov     ax, IDS_ERRBADINT       ; return improper int!
        mov     dl, bInt
        cmp     dl, [gbInt]             ; is the int the same?
        jne     VerifyConfig_Exit       ; not the same, it is a error

        xor     ax, ax
        jmp     short VerifyConfig_Exit

VerifyConfig_Not_Enabled:

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   if we cannot acquire it, we won't be hurting anything--we either own
;   it which we deal with or another VM owns it and we sorta deal with it.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        mov     ax, [glpVSBDEntry].off      ; Q: is VSBD installed?
        or      ax, [glpVSBDEntry].sel
        jz      VerifyConfig_Acquire_Continue

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   AX = Base of SB to acquire (for example, 0240h)
;   BX = Flags: fSB_ASB_Acquire_DSP, fSB_ASB_Acquire_AdLib_Synth
;   DX = SB_API_Acquire_Sound_Blaster (1)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        mov     ax, wPort                   ; base port to acquire
        mov     bx, fSB_ASB_Acquire_DSP+fSB_ASB_Auto_Reset_DSP
        mov     dx, SB_API_Acquire_Sound_Blaster
        call    [glpVSBDEntry]

VerifyConfig_Acquire_Continue:
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        cCall   InitVerifyPort, <wPort>
        or      ax, ax
        jnz     VerifyConfig_Exit_Release
        mov     wDSPVersion, dx         ; save this

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        cCall   InitVerifyInt, <wPort, bInt>
        or      ax, ax
        jnz     VerifyConfig_Exit_Release

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        cCall   InitVerifyDMA, <wPort, bDMAChannel>
        or      ax, ax
        jnz     VerifyConfig_Exit_Release

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
VerifyConfig_Valid:
        errn$   VerifyConfig_Exit_Release

VerifyConfig_Exit_Release:
        mov     bx, [glpVSBDEntry].off      ; Q: is VSBD installed?
        or      bx, [glpVSBDEntry].sel
        jz      VerifyConfig_Exit

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   AX = Base of SB to release (for example, 0240h)
;   BX = Flags: fSB_ASB_Acquire_DSP, fSB_ASB_Acquire_AdLib_Synth
;   DX = SB_API_Release_Sound_Blaster (2)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        push    ax
        mov     ax, wPort                   ; base port to release
        mov     bx, fSB_ASB_Acquire_DSP     ; just the DSP
        mov     dx, SB_API_Release_Sound_Blaster
        call    [glpVSBDEntry]
        pop     ax

VerifyConfig_Exit:
        mov     dx, wDSPVersion
        D1      <VerifyConfig: Error=#AX  DSP Version=#DX>
cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   WORD NEAR PASCAL InitVerifyPort(wPort)
;
;   DESCRIPTION:
;
;       Verify that the hardware is found at the given port.
;
;   ENTRY:
;       ParmW   wPort       :   Port base to verify
;
;   EXIT:
;       If success,
;           AX = 0
;           DX = DSP Version
;       else failure,
;           AX = non-zero IDS_xxx error
;           DX = DSP Version (zero if no card found)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc InitVerifyPort <NEAR, PUBLIC> <>
        ParmW   wPort
cBegin
        cmp     wPort, -1
        je      VerifyPort_Invalid

        call    vsbdGetSoundBlasterInfo
        jc      VerifyPort_VSBD_Not_Installed

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   VSBD.386 is installed, use the info it has
;
;   If carry clear, VSBD is installed and:
;       AX = DSP version
;       BX = Flags: fSB_ASB_Acquire_DSP, fSB_ASB_Acquire_AdLib_Synth
;       CH = IRQ
;       CL = DMA channel
;       DX = Base port of SB
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        xchg    ax, dx
        cmp     wPort, ax               ; Q: same port?
        je      VerifyPort_Valid        ;   Y: success

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   VSBD.386 is either *not* installed, or is virtualizeing another port.
;   so try to verify the port without its help.
;
;   NOTE: if VSBD.386 is installed and is working with same card that we
;   are trying to verify, we won't get here!
;
;   We are not enabled or the port is not the same as the current one, so
;   anything is game!  We don't need to worry about a VxD cuz that is taken
;   care of above...
;
;   At this point, the port could be valid, so check the hardware
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
VerifyPort_VSBD_Not_Installed:
        mov     dx, wPort
        add     dx, DSP_PORT_RESET      ; point to reset port
        mov     al, 1
        out     dx, al                  ; reset active

        mov     cx, 100                 ; wait 3 microseconds for DSP reset
        loop    $                       ; ...to take a good hold

        xor     al, al
        out     dx, al                  ; clear the reset
        sub     dx, DSP_PORT_RESET      ; point back to base

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   read the data port to confirm 0AAH is there
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        mov     cx, 25                  ; lots of tries

VerifyPort_Reset_Wait:
        cCall   dspAbsRead, <dx>
        jc      VerifyPort_Reset_Not_Done ; jump if it timed out

        cmp     al, 0AAh                ; correct return value ?
        je      VerifyPort_Get_Version  ; jump if it is

VerifyPort_Reset_Not_Done:
        loop    VerifyPort_Reset_Wait

VerifyPort_Invalid:
        mov     ax, IDS_ERRBADPORT
        xor     dx, dx
        jmp     short VerifyPort_Exit

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   at this point, there *should* be a valid card located at wPort... now
;   grab the DSP version
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

VerifyPort_Get_Version:
        cCall   dspAbsWrite, <dx, GETDSPVER>
        jc      VerifyPort_Invalid      ; jump if timed out (shouldn't happen here)

        cCall   dspAbsRead, <dx>
        jc      VerifyPort_Invalid

        mov     ah, al                  ; save major version
        cCall   dspAbsRead, <dx>
        jc      VerifyPort_Invalid

        mov     bx, ax                  ; we have the DSP version

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Check for a Media Vision Thunder Board.  The DSP version will be 
;   different the second time we query.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   dspAbsWrite, <dx, GETDSPVER>
        jc      VerifyPort_Invalid      ; jump if timed out (shouldn't happen here)

        cCall   dspAbsRead, <dx>
        jc      VerifyPort_Invalid

        mov     ah, al                  ; save major version
        cCall   dspAbsRead, <dx>
        jc      VerifyPort_Invalid

        mov     dx, bx
        cmp     dx, ax                  ; Q: Thunder Board?
        je      VerifyPort_Valid        ;   N: then continue normally...

        D1      <THUNDER BOARD DETECTED>
        or      dh, 80h                 ; flag we have a Thunder Board
        errn$   VerifyPort_Valid

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   we have found the hardware at the specified port
;
;       DX = DSP version (high bit set in DH if Thunder Board...)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
VerifyPort_Valid:
        xor     ax, ax                  ; show no error
        mov     [gwDSPVersion], dx      ; save this.

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
VerifyPort_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   WORD NEAR PASCAL InitVerifyInt(wPort, bInt)
;
;   DESCRIPTION:
;
;       Verify that the hardware is using the specifed interrupt.
;
;   ENTRY:
;       ParmW   wPort       :   port (assumed correct)
;       ParmW   bInt        :   interrupt to verify
;
;   EXIT:
;       If success,
;           AX = 0
;       else failure,
;           AX = non-zero IDS_xxx error code
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

NUM_INTS_TO_VERIFY = 10

cProc InitVerifyInt <NEAR, PUBLIC> <si, di>
        ParmW   wPort
        ParmB   bInt
cBegin

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   int must be from 1-15, or we know it is bad.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        mov     al, bInt
        or      al, al
        jz      VerifyInt_Invalid
        cmp     al, 0Fh
        jbe     VerifyInt_Verify

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
VerifyInt_Invalid:
        mov     ax, IDS_ERRBADINT
        jmp     VerifyInt_Exit

VerifyInt_Valid:
        xor     ax, ax
        jmp     VerifyInt_Exit

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Hook the interrupt vector and tell the DSP to fire an interrupt.
;   See if it happens, and do it a few times just to be sure.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
VerifyInt_Verify:

        test    [_gfVerifyInt], 0FFh        ; over-ride just in case
        jz      VerifyInt_Valid

        cCall   GlobalPageLock, <cs>        ; just for fun!

        mov     dx, cs                      ; !!! seg isrTest
        mov     ax, offset isrTest
        mov     cl, bInt

        EnterCrit

        cCall   InitSetInterruptVector, <cx, dx, ax>

        mov     [isrTestNext].off, ax
        mov     [isrTestNext].sel, dx       ; set oldISR, save

        LeaveCrit

        cCall   InitSetIntMask, <bInt, 0>   ; unmask the IRQ
        push    ax                          ; save old state

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        mov     cx, NUM_INTS_TO_VERIFY
        mov     dx, wPort                   ; get port
        mov     si, IDS_ERRBADINT           ; assume failure
        mov     bl, [gbIntCount]            ; get current int count
        add     bl, cl                      ; this is what we expect
    
VerifyInt_Loop:
        pusha       
        cCall   InitSetIntMask, <bInt, 0>   ; unmask the IRQ!
        popa

        mov     al, [gbIntCount]

        cCall   dspAbsWrite, <dx, GENERATEINT>
        jc      VerifyInt_BadInt_dspWrite

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   wait for the interrupt to fire; time out after 64k tries
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        push    cx
        xor     cx, cx
@@:     cmp     [gbIntCount], al
        loope   @b
        pop     cx
        je      VerifyInt_BadInt_TimeOut    ; we never got an interrupt

        inc     al
        cmp     [gbIntCount], al
        jne     VerifyInt_BadInt            ; we got too many

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;
;   HACK HACK HACK!
;
;   On some machines the default handler for IRQ7 does not do an EOI
;   so we will check for this and do the EOI for it.
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        push    bx
        EnterCrit
        mov     al, 0Bh                     ; read the ISR
        out     20h, al
        pause
        in      al, 20h

        or      al, al                      ; are any IRQ left un-EOIed?
        jz      @F

        D1      <ISR = #al, doing non-specific EOI>

        mov     al, 20h                     ; send non-specific EOI
        out     20h, al
@@:
        LeaveCrit
        pop     bx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   tell the DSP we did the interrupt.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        add     dx, DSP_PORT_DATAAVAIL      ; point to data avail port
        in      al, dx                      ; read status (reset interrupt)
        sub     dx, DSP_PORT_DATAAVAIL      ; point to data avail port

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        loop    VerifyInt_Loop              ; try some more, just to be sure.

        cmp     bl, [gbIntCount]            ; did we get exactly NUM_INTS???
        jne     VerifyInt_BadInt            ; ...we got more/less card not found

VerifyInt_GoodInt:
        D1      <VerifyInt: IRQ appears to be correct>
        xor     si, si                      ; show success
        jmp     short VerifyInt_Done

VerifyInt_BadInt_TimeOut:
        D1      <VerifyInt: interrupt TIME OUT!>
        jmp     short VerifyInt_Done

VerifyInt_BadInt_dspWrite:
        D1      <VerifyInt: dspWrite TIME OUT!>
        jmp     short VerifyInt_Done

VerifyInt_BadInt:
        D1      <VerifyInt: IRQ not valid>
        errn$   VerifyInt_Done

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
VerifyInt_Done:
        pop     ax                          ; restore PIC
        cCall   InitSetIntMask, <bInt, ax>

        mov     cl, bInt
        cCall   InitSetInterruptVector, <cx, isrTestNext>

        cCall   GlobalPageUnlock, <cs>      ; more fun!

        mov     ax, si                      ; get error code
        errn$   VerifyInt_Exit              ; and exit

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

VerifyInt_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   isrTest
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

public isrTest
isrTest proc far

        push    ds

        push    DGROUP                  ; set up local DS
        pop     ds
        assumes ds, Data

        inc     [gbIntCount]            ; we got an interrupt

        D1      <TEST-INTERRUPT>

        push    [isrTestNext].sel
        push    [isrTestNext].off

        push    bp                      ; restore DS from stack
        mov     bp, sp
        mov     ds, [bp + 6]            ; stack: [ds] [gdwOldISR].sel [gdwOldISR].off [bp]
        assumes ds, nothing
        pop     bp
        retf    2                       ; chain!

isrTest endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   WORD NEAR PASCAL InitVerifyDMA(wPort, bDMA)
;
;   DESCRIPTION:
;
;       Verify that the hardware is using the specifed DMA channel.
;
;   ENTRY:
;       ParmW   wPort       :   Port (assumed correct)
;       ParmW   bDMA        :   DMA channel to verify
;
;   EXIT:
;       If success,
;           AX = 0
;       else failure,
;           AX = non-zero IDS_xxx error code
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc InitVerifyDMA <NEAR, PUBLIC> <>
        ParmW   wPort
        ParmB   bDMA
cBegin
        xor     ax, ax                  ; always valid
cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   BOOL NEAR PASCAL InitSetConfiguration(wPort, bInt, bDMAChannel, wMidiInPersistence)
;
;   DESCRIPTION:
;       This routine is used to initialize all variables needed to handle
;       the hardware based on INI settings.  It is _assumed_ that the IRQ
;       and Port Base are valid!
;
;   ENTRY:
;       ParmW wPort             :   The port base to be 'set.'
;       ParmB bInt              :   The IRQ to be 'set.'
;       ParmB bDMAChannel       :   The DMA channel to be 'set.'
;       ParmW wMidiInPersistence:   MIDI input persistence
;
;   EXIT:
;       FALSE if failed; non-zero if success
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc InitSetConfiguration <NEAR, PUBLIC> <>
        ParmW   wPort
        ParmB   bInt
        ParmB   bDMAChannel
        ParmW   wMidiInPersistence
cBegin
        AssertT [_gfEnabled]             ; !!! this would be very bad

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   set the port base and MIDI input persistence
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        mov     ax, wPort
        mov     [gwPort], ax
        mov     ax, wMidiInPersistence
        mov     [gwMidiInPersistence], ax
        mov     al, bInt
        mov     [gbInt], al             ; set 'gbInt' to return value

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;  set the IRQ specific variables appropriately...
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cmp     al, 8
        jae     isc_Slave_IRQ

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   IRQ is for 'master' PIC--set global variables for IRQ appropriately
;   AL = bInt
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        mov     ah, al
        or      ah, 60h                 ; specific EOI command for master
        xor     al, al                  ; no slave command
        jmp     short isc_Save_IRQ_Info

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   IRQ is for 'slave' PIC--set global variables for IRQ appropriately
;   AL = bInt
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

isc_Slave_IRQ:
        and     al, 07h                 ; map to slave IRQ
        or      al, 60h                 ; specific EOI for slave
        mov     ah, 62h                 ; specific EOI (#2) command for master

isc_Save_IRQ_Info:

        mov     [gwEOICommands], ax     ; save EOI commands for ISR
        errn$ isc_Set_DMA_Channel

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Now compute the DMA channel stuff
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

isc_Set_DMA_Channel:

        mov     al, bDMAChannel
        or      al, al
        jne     @F
        mov     ah, 87h                 ; DMA channel 0 page register
        jmp     isc_Save_DMA_Info

@@:     cmp     al, 2
        jne     @F
        mov     ah, 81h                 ; DMA channel 2 page register
        jmp     isc_Save_DMA_Info

@@:     cmp     al, 3
        jne     @F
        mov     ah, 82h                 ; DMA channel 3 page register
        jmp     isc_Save_DMA_Info

@@:     mov     ax, 8301h               ; default to DMA channel 1

isc_Save_DMA_Info:

        mov     [gbDMAChannel], al      ; save info...
        mov     [gbDMAPageReg], ah

        errn$ isc_Exit

isc_Exit:

ifdef DEBUG
        mov     ax, [gwPort]
        D1      <gwPort:#AX>
        mov     al, [gbInt]
        D2      <gbInt:#AL>
        mov     al, [gbDMAChannel]
        D1      <gbDMAChannel:#AL>
        mov     al, [gbDMAPageReg]
        D2      <gbDMAPageReg:#AL>
endif
        mov     ax, 1                   ; return success

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL
;
; @asm dmaAllocateBuffer | This function allocates a page locked DMA buffer
;     which is guaranteed not to contain a physical page boundary.
;
; @parm wBufferSize | The size of the buffer in bytes.  It must be <= 4k!!!
;
; @rdesc The return value is non-zero if successful, and zero on failure.
;
;     The buffer selector and offset are stored in global variables:
;
;       gwDMASelector   : pmode selector of DMA buffer
;       gwDMAOffset     : offset to even page boundary
;       gwDMAPhysAddr   : for DMA address register
;       gbDMAPhysPage   : for DMA page register
;
; @comm
;     The algorithm used here is very simple.  It first attempts
;     to allocate the desired buffer size.  If this fails,
;     it returns with an out of memory error.  If the buffer
;     is allocated, it checks to see if it contains a physical
;     page boundary.  If not, it saves the selector and
;     offset of the buffer.  If the block contains a physical
;     page boundary then it frees the block and attempts to
;     allocate a block of twice the size.  If the allocation
;     fails it returns an out of memory error.  If successful, it
;     tests the first half of the block for a physical page 
;     boundary.  If none is found, it sets the selector and
;     offset.  If it contains a boundary, it sets the
;     same selector but the offset is set to the upper half
;     of the block which is assumed to be boundary free.
;
;     This is obviously a bit simple, but there is currently no
;     way to move a block to a better place.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc dmaAllocateBuffer <NEAR, PASCAL, PUBLIC> <>
        ParmW   wBufferSize
cBegin

        D2 <dmaAllocateBuffer>

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   This code assumes the caller wants <= 4kb, so fail if this is not the
;   case.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        mov     ax, wBufferSize
        cmp     ax, P_SIZE              ; Q: size <= 4kb?
        jbe     alloc_Continue          ;   Y: then attempt allocation

        xor     ax, ax                  ;   N: fail!
        jmp     alloc_Exit

alloc_Continue:

        xor     dx, dx
        mov     [gwDMAOffset], dx       ; assume offset is zero for now
        cCall   GlobalAlloc, <GMEM_FIXED+GMEM_SHARE, dx, ax>

        mov     [gwDMASelector], ax
        or      ax, ax                  ; Q: failed?
        jz      alloc_Exit              ;   Y: hmm... bad news!

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   check if it contains a page boundary (it almost ALWAYS contains a page
;   boundary...)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   GetSelectorBase, <ax>   ; returns DX:AX = linear base
        or      dh, dh                  ; Q: is the base > 16Mb?
        jnz     alloc_Bad_Mem           ;   Y: yes it is not usable!

        test    ax, (P_SIZE - 1)        ; Q: even page boundary?
        jz      alloc_Success           ;   Y: DX:AX == phys addr...

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   this block is no good so free it and try a bigger chunk...
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   dmaFreeBuffer           ; free previous block
                
        mov     ax, (P_SIZE * 2)        ; try 2 pages...
        xor     dx, dx
        cCall   GlobalAlloc, <GMEM_FIXED+GMEM_SHARE, dx, ax>

        mov     [gwDMASelector], ax
        or      ax, ax                  ; Q: failed?
        jz      alloc_Exit              ;   Y: hmm... bad news!

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   calculate offset into double size block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   GetSelectorBase, <ax>
        or      dh, dh                  ; Q: is the base > 16Mb?
        jnz     alloc_Bad_Mem           ;   Y: yes it is not usable!

        mov     bx, ax
        neg     bx                  
        and     bx, (P_SIZE - 1)        ; BX contains offset into linear pg.

        mov     [gwDMAoffset], bx
        add     ax, bx
        adc     dl, dh
        jmp     alloc_Success           ; DX:AX == phys addr...

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
; couldn't allocate a physically contiguous block of memory... fail!
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

alloc_Bad_Mem:

        cCall   dmaFreeBuffer           ; free bogus memory...
        xor     ax, ax                  ; fail!
        jmp     alloc_Exit

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   DX:AX contains linear base of DMA buffer
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

alloc_Success:

        mov     [gbDMAPhysPage], dl     ; save it
        mov     [gwDMAPhysAddr], ax

ifdef DEBUG
        mov     cx, [gwDMASelector]
        mov     bx, [gwDMAOffset]
        D1      <DMA BUFFER: DMA=#cx:#bx, addr=#dl#ax>
endif
        mov     ax, 1

alloc_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL
;
; @api BOOL | dmaFreeBuffer | Frees a DMA buffer allocated with previous call
;       to dmaAllocateBuffer.
;
; @rdesc The return value is non-zero if successful, and zero on failure.
;
; @xref dmaAllocateBuffer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc dmaFreeBuffer <NEAR, PASCAL, PUBLIC> <>
cBegin

        D2 <dmaFreeBuffer>

        mov     ax, [gwDMASelector]
        or      ax, ax
        jz      free_Exit

        cCall   GlobalFree, <ax>
        xor     ax, ax
        mov     [gwDMASelector], ax
        mov     [gwDMAOffset], ax
        mov     [gwDMAPhysAddr], ax
        mov     [gbDMAPhysPage], al

free_Exit:

        inc     ax                      ; succeed!

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   BOOL NEAR PASCAL InitResetAdapter(bMode)
;
;   DESCRIPTION:
;       This function is responsible for putting the adapter into a known
;       state from a random state.  It is should ONLY be called during 
;       Enable and Disable.
;
;   ENTRY:
;       ParmB bMode:  IRA_MODE_POWERON - back to power on state (Disable)
;                     IRA_MODE_DEFAULT - to normal 'sndblst.drv' state (Enable)
;
;   EXIT:
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc InitResetAdapter <NEAR, PUBLIC> <>
        ParmB   bMode
cBegin

        D2 <InitResetAdapter>

        cCall   vsbdAcquireSoundBlaster
        or      ax, ax
        jnz     ira_Exit

        call    dspReset
        or      ax, ax
        jnz     ira_Release_Hardware

        cmp     bMode, IRA_MODE_DEFAULT
        jne     ira_Release_Hardware

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   If VSBD is installed, use the DSP version that it already knows.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        call    vsbdGetSoundBlasterInfo
        jnc     ira_Save_DSP_Version

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Get the DSP version.  We just did a fresh reset, so this is perfectly
;   valid.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        mov     al, GETDSPVER
        call    dspWrite
        jc      ira_Fail_And_Release    ; jump if timed out (shouldn't happen here)

        call    dspRead
        jc      ira_Fail_And_Release

        mov     dh, al                  ; save major version
        call    dspRead
        jc      ira_Fail_And_Release

        mov     ah, dh                  ; AL has minor, AH has major

ira_Save_DSP_Version:

        mov     [gwDSPVersion], ax
        D1 <DSP VERSION: #AX>

        cCall   dspSpeakerOn
        xor     ax, ax
        jz      ira_Release_Hardware

ira_Fail_And_Release:

        mov     ax, -1                  ; fail! (but release hardware)

ira_Release_Hardware:

        cCall   vsbdReleaseSoundBlaster

ira_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   BOOL NEAR PASCAL InitSetIntMask(bIRQ, bMask)
;
;   DESCRIPTION:
;       This function sets or unsets interrupt vector mask.
;
;   ENTRY:
;       ParmB   bIRQ        :   The IRQ (0 - 15) to mask/unmask
;       ParmB   bMask       :   The mask
;
;   EXIT:
;       AX    :   The return value is the previous interrupt mask.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc InitSetIntMask <NEAR, PUBLIC> <>
        ParmB   bIRQ
        ParmB   bMask
cBegin
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   see if we need to talk to the slave or master PIC
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        mov     cl, bIRQ
        mov     dx, PIC_IMR_MASTER
        cmp     cl, 8
        jb      SetIntMask_Master
        and     cl, 07h
        mov     dx, PIC_IMR_SLAVE
SetIntMask_Master:

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   compute the interrupt mask.
;       DX = slave or master mask register
;       CL = 0-7 bit to set/clear
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        mov     ch, 1                   ; CH = 1
        shl     ch, cl                  ; CH = int mask

        mov     cl, bMask               ; get mask
        or      cl, cl
        jz      SetIntMask_UnMask
        mov     cl, ch
SetIntMask_UnMask:

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   CH  = PIC mask         (1 << (bInt&7))
;   CL  = wanted mask      bMask ? ch : 0
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        not     ch                      ; we need inverse of mask

        EnterCrit                       ; !!! Trashes BX !!!
        in      al, dx                  ; grab current mask
        mov     ah, al                  ; save it
        and     al, ch                  ; clear bit
        or      al, cl                  ; clear or set based on bMask
        cmp     al, ah                  ; don't set the same state again!
        je      SetIntMask_Same
        out     dx, al                  ; enable/disable ints...
SetIntMask_Same:
        LeaveCrit                       ; !!! Trashes BX !!!

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   we have set/cleared the PIC, now return the old state.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        not     ch                      ; return previous mask state
        mov     al, ah
        and     al, ch
        xor     ah, ah
cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   LPFUNC NEAR PASCAL InitSetInterruptVector(bIRQ, lpNewISR)
;
;   DESCRIPTION:
;       This function takes the IRQ and sets the appropriate interrupt
;       vector; and returns the pointer to the previous handler of the IRQ.
;
;   ENTRY:
;       ParmB   bIRQ        :   The IRQ (0 - 15) to install handler for.
;       ParmD   lpNewISR    :   The handler
;
;   EXIT:
;       DX:AX   :   The return value is the previous interrupt handler.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc InitSetInterruptVector <NEAR, PUBLIC> <>
        ParmB   bIRQ
        ParmD   lpNewISR
cBegin

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   convert IRQ to interrupt vector.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        mov     al, bIRQ
        mov     ah, 08h
        cmp     al, ah                  ; Q: slave or master IRQ?
        jl      isv_Continue

        mov     ah, (70h - 08h)         ;   slave

isv_Continue:

        add     al, ah                  ; AL = interrupt vector

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   get old interrupt vector (AL == interrupt vector)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        mov     ah, 35h
        int     21h                     ; get the old vector in es:bx

        push    es                      ; save for a bit
        push    bx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   set new interrupt vector (AL == interrupt vector)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        mov     ah, 25h
        push    ds
        lds     dx, lpNewISR
        assumes ds, nothing
        int     21h                     ; set the new vector
        pop     ds

        pop     ax                      ; restore old ISR for return value
        pop     dx                      ; ... DX:AX is old handler

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @api BOOL | widSuspend | This function suspends wave input.
;
; @rdesc The return is zero if successful, and non-zero on failure.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc widSuspend <NEAR, PUBLIC> <>
cBegin

        D2 <widSuspend>

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Release the hardware if we own it.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        xor     ax, ax                         ; assume success

        test    [gbWaveInFlags], WIF_ALLOCATED
        jz      wid_Suspend_Exit               ; exit if hardware not allocated

        test    [gbWaveInFlags], WIF_STARTED   ; Q: currently active?
        jz      wid_Suspend_Release            ;   N: then just release hardware

        call    widStop
        or      [gbWaveInFlags], WIF_RESTART   ; flag we need to restart...

wid_Suspend_Release:

        cCall   widReleaseHardware             ; AX == 0 if success...
        or      [gbWaveInFlags], WIF_SUSPENDED

wid_Suspend_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @api BOOL | widReactivate | This function resumes paused wave input.
;
; @rdesc The return is zero if successful, and non-zero on failure.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc widReactivate <NEAR, PUBLIC> <>
cBegin

        D2 <widReactivate>

        test    [gbWaveInFlags], WIF_SUSPENDED
        jz      wid_Reactivate_Success  ; succeed if not suspended...

        D2 <reactivating>

        cCall   widAcquireHardware
        or      ax, ax                  ; Q: we get it?
        jnz     wid_Reactivate_Exit     ;   N: fail!

        test    [gbWaveInFlags], WIF_RESTART
        jz      wid_Reactivate_Success  ; succeed if wasn't started...

        call    widStart                ; start it back up

wid_Reactivate_Success:

        and     [gbWaveInFlags], not (WIF_RESTART or WIF_SUSPENDED)
        xor     ax, ax                  ; success

wid_Reactivate_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @api BOOL | wodSuspend | This function suspends wave output.
;
; @rdesc The return is zero if successful, and non-zero on failure.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        assumes ds, Data
        assumes es, nothing

cProc wodSuspend <NEAR, PUBLIC> <>
cBegin

        D2 <wodSuspend>

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Release the hardware if we own it.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        xor     ax, ax                          ; assume success

        test    [gbWaveOutFlags], WOF_ALLOCATED
        jz      wod_Suspend_Exit                ; exit if hardware not allocated

        test    [_gfWaveOutPaused], 0FFh        ; Q: currently paused?
        jnz     wod_Suspend_Release             ;   Y: then DON'T flag restart!

        call    wodPause
        or      [gbWaveOutFlags], WOF_RESTART   ; flag we need to restart...

wod_Suspend_Release:

        cCall   wodReleaseHardware              ; AX == 0 if success...
        or      [gbWaveOutFlags], WOF_SUSPENDED

wod_Suspend_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @api BOOL | wodReactivate | This function resumes suspended wave output.
;
; @rdesc The return is zero if successful, and non-zero on failure.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc wodReactivate <NEAR, PUBLIC> <>
cBegin

        D2 <wodReactivate>

        test    [gbWaveOutFlags], WOF_SUSPENDED
        jz      wod_Reactivate_Success  ; succeed if not suspended...

        D2 <reactivating>

        cCall   wodAcquireHardware
        or      ax, ax                  ; Q: we get it?
        jnz     wod_Reactivate_Exit     ;   N: fail!

        test    [gbWaveOutFlags], WOF_RESTART
        jz      wod_Reactivate_Success  ; succeed if wasn't started...

        call    wodResume               ; start it back up

wod_Reactivate_Success:

        and     [gbWaveOutFlags], not (WOF_RESTART or WOF_SUSPENDED)
        xor     ax, ax                  ; success

wod_Reactivate_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @api BOOL | midSuspend | This function suspends midi input.
;
; @rdesc The return is zero if successful, and non-zero on failure.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc midSuspend <NEAR, PUBLIC> <>
cBegin

        D2 <midSuspend>

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Release the hardware if we own it.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        xor     ax, ax                         ; assume success

        test    [gbMidiInFlags], MIF_ALLOCATED
        jz      mid_Suspend_Exit               ; exit if hardware not allocated

        test    [gbMidiInFlags], MIF_STARTED   ; Q: currently active?
        jz      mid_Suspend_Release            ;   N: then just release hardware

        call    midStop
        or      [gbMidiInFlags], MIF_RESTART   ; flag we need to restart...

mid_Suspend_Release:

        cCall   midReleaseHardware             ; AX == 0 if success...
        or      [gbMidiInFlags], MIF_SUSPENDED

mid_Suspend_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @api BOOL | midReactivate | This function resumes suspended midi input.
;
; @rdesc The return is zero if successful, and non-zero on failure.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc midReactivate <NEAR, PUBLIC> <>
cBegin

        D2 <midReactivate>

        test    [gbMidiInFlags], MIF_SUSPENDED
        jz      mid_Reactivate_Success  ; succeed if not suspended...

        D2 <reactivating>

        cCall   midAcquireHardware
        or      ax, ax                  ; Q: we get it?
        jnz     mid_Reactivate_Exit     ;   N: fail!

        test    [gbMidiInFlags], MIF_RESTART
        jz      mid_Reactivate_Success  ; succeed if wasn't started...
        call    midStart                ; start it back up

mid_Reactivate_Success:

        and     [gbMidiInFlags], not (MIF_RESTART or MIF_SUSPENDED)
        xor     ax, ax                  ; success

mid_Reactivate_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @api BOOL | modSuspend | This function suspends midi output.
;
; @rdesc The return is zero if successful, and non-zero on failure.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc modSuspend <NEAR, PUBLIC> <>
cBegin

        D2 <modSuspend>

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Release the hardware if we own it.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        xor     ax, ax
        test    [gbMidiOutFlags], MOF_ALLOCATED
        jz      mod_Suspend_Exit

        cCall   modReleaseHardware
        or      [gbMidiOutFlags], MOF_SUSPENDED

mod_Suspend_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @api BOOL | modReactivate | This function resumes suspended midi output.
;
; @rdesc The return is zero if successful, and non-zero on failure.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc modReactivate <NEAR, PUBLIC> <>
cBegin

        D2 <modReactivate>

        xor     ax, ax                  ; assume success
        test    [gbMidiOutFlags], MOF_SUSPENDED
        jz      mod_Reactivate_Exit

        D2 <reactivating>

        cCall   modAcquireHardware
        or      ax, ax                  ; Q: can we get hardware?
        jnz     mod_Reactivate_Exit     ;   N: bummer (stay suspended!)

        and     [gbMidiOutFlags], not MOF_SUSPENDED
        xor     ax, ax                  ; success

mod_Reactivate_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @asm WORD | Enable | This function enables the driver.  It will allocate
;     DMA buffers, hook interrupts and validate the hardware.
;
; @rdesc The return value is zero if the call is successful; otherwise
;     an error code is returned.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc Enable <FAR, PASCAL, PUBLIC> <>
cBegin
         
        D1 <Enable>

        mov     al, [gbInt]             ; if not configured, DON'T enable!
        cbw     
        or      ax, [gwPort]
        cmp     ax, -1
        jne     enable_We_Are_Configured


enable_Fail:

        mov     ax, -1                  ; return error!
        jmp     enable_Exit 

        
enable_We_Are_Configured:

        test    [_gfEnabled], 0FFh      ; Q: are we already enabled?
        jnz     enable_Success          ;   Y: then succeed

        cCall   dmaAllocateBuffer, <DMAPHYSBUFSIZE>
        or      ax, ax
        jz      enable_Fail             ; ...fail if we can't alloc DMA buffer!


;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   reset the adapter because we don't know what state it is in
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   InitResetAdapter, <IRA_MODE_DEFAULT>
        or      ax, ax
        jnz     enable_Fail

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   set our ISR--save old interrupt vector
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        mov     dx, seg dspISR
        mov     ax, offset dspISR
        mov     bl, [gbInt]
        cCall   InitSetInterruptVector, <bx, dx, ax>

        mov     [gdwOldISR].sel, dx
        mov     [gdwOldISR].off, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   enable interrupts for the Sound Blaster
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   InitSetIntMask, <gbInt, 0>
        mov     [gbIntMask], al   ; save old state

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Reactivate MIDI and/or Wave if was going during Disable
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   widReactivate
        cCall   wodReactivate
        cCall   midReactivate
        cCall   modReactivate
        errn$ enable_Success

enable_Success:

        D1      <Enable ok>
        inc     [_gfEnabled]            ; mark as being enabled
        xor     ax, ax                  ; no error

enable_Exit:

cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; @doc INTERNAL 
;
; @asm WORD | Disable | This function disables the driver.  It disables the
;     the hardware, unhooks interrupts and frees any memory allocated.
;
; @rdesc The return value is zero if the call is successful; otherwise
;     an error code is returned.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        assumes ds, Data
        assumes es, nothing

cProc Disable <FAR, PASCAL, PUBLIC> <>
cBegin

        D1 <Disable>

        test    [_gfEnabled], 0FFh      ; Q: are we currently enabled?
        jz      disable_Exit            ;   N: then exit (success)


;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   Suspend MIDI and/or Wave if it is going
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   widSuspend
        cCall   wodSuspend
        cCall   midSuspend
        cCall   modSuspend

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   restore PIC to original state
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

        cCall   InitSetIntMask, <gbInt, gbIntMask>

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
;   restore hardware and the old interrupt vector
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;

disable_Success:

        cCall   InitResetAdapter, <IRA_MODE_POWERON>

        mov     bl, [gbInt]
        mov     ax, [gdwOldISR].off
        mov     dx, [gdwOldISR].sel

        cCall   InitSetInterruptVector, <bx, dx, ax>
        cCall   dmaFreeBuffer           ; free the DMA buffer

disable_Exit:

        xor     ax, ax                  ; no error
        mov     [_gfEnabled], al        ; flag we are disabled now

cEnd

sEnd CodeSeg

        end
Detected encoding: ASCII (7 bit)2