;$Author:   DCODY  $
;$Date:   24 Sep 1992 08:54:22  $
;$Header:   X:/sccs/mvsound/mvsound.asv   1.6   24 Sep 1992 08:54:22   DCODY  $
;$Log:   X:/sccs/mvsound/mvsound.asv  $
;  
;     Rev 1.6   24 Sep 1992 08:54:22   DCODY
;  changed MVGetHardware to mvGetHardware
;  
;     Rev 1.5   12 Aug 1992 17:13:18   DCODY
;  made call to mvgethardware use a zero (0) parameter to use the current base.
;  
;     Rev 1.4   17 Jul 1992 14:18:22   DCODY
;  moved TheDMAchannel and TheIRQchannel out to another module. Made the
;  hardware I/O relocatable. Made initmvsound recallable without ill effects.
;  
;     Rev 1.3   09 Jul 1992 10:07:50   DCODY
;  corrected MV101 bit test and jumps
;  
;     Rev 1.2   26 Jun 1992 14:13:48   DCODY
;  added the parameter to GetHWVersion call
;  
;     Rev 1.1   23 Jun 1992 17:06:04   DCODY
;  PAS2 update
;  
;     Rev 1.0   15 Jun 1992 09:43:46   BCRANE
;  Initial revision.
;$Logfile:   X:/sccs/mvsound/mvsound.asv  $
;$Modtimes$
;$Revision:   1.6  $
;$Workfile:   mvsound.asm  $ 

        page    64,131
	Title	MVSOUND  --  Pro Audio Spectrum Sound Support Code

;   /*\
;---|*|----====< MVSOUND >====----
;---|*|
;---|*|  This module contains the code for supporting PCM I/O.
;---|*|
;---|*|  Media Vision, Inc. Copyright (c) 1991,1992. All Rights Reserved.
;---|*|
;   \*/

;
; The following set of equates are used to control what routines are compiled
; by doing this, MVSOUND.ASM can contain all the necessary code in one file,
; but generate separate, linkable objects with just the necessary code.
;
ALLOBJS     = 0 	; individual objects are being built
DATAOBJ     = 0 	; data code to be compiled
MISC1OBJ    = 0 	; minimum code needed from the library
MISC2OBJ    = 0 	; other miscellaneous code
PCMOBJ	    = 0 	; PCM code to be compiled

ifdef BUILDDATA
  DATAOBJ   = 1 	; data code to be compiled
endif

ifdef BUILDMISC1
  MISC1OBJ  = 1 	; misc module #1 code to be compiled
endif

ifdef BUILDMISC2
  MISC2OBJ  = 1 	; misc module #2 code to be compiled
endif

ifdef BUILDPCM
  PCMOBJ    = 1 	; data code to be compiled
endif

ifdef BUILDALL
  ALLOBJS   = 1 	; all objects are being built
  DATAOBJ   = 1 	; data code to be compiled
  MISC1OBJ  = 1 	; miscellaneous code to be compiled
  MISC2OBJ  = 1 	; miscellaneous code to be compiled
  PCMOBJ    = 1 	; PCM code to be compiled
endif

	.xlist
	include model.inc
        include masm.inc
	include target.inc
	include state.inc
	include common.inc
	.list

;
; structure for pointing to the above table of DMA addresses
;

dmaaddr struc
_dmach		db	?	; DMA channel selected
_dmardstat      db      ?       ; DMA read status
_dmawrcntrl	db	?	; DMA write command register
_dmawreq	db	?	; DMA write request register
_dmawrsmr	db	?	; DMA write single mask register
_dmawrmode	db	?	; DMA write mode register
_dmaclear	db	?	; DMA clear low/high flip-flop
_dmardtemp	db	?	; DMA read temp register
_dmawrclr	db	?	; DMA write master clear
_dmaclrmsk	db	?	; DMA clear mask register
_dmawrall	db	?	; DMA write all mask register bits
dmaaddr ends

;
;---------------------------========================---------------------------
;---------------------------====< DATA SECTION >====---------------------------
;---------------------------========================---------------------------
;
if MODELSIZE eq 0
	.code
else
	.data
endif

if DATAOBJ                              ; include if we are building DATA.OBJ

;
; These variables are DMA/Buffer control variables
;
TheIRQMask	db	INT7MSK 	; 8259 interrupt mask

TheDMAMode	db	00h		; 44h for input, 48h for output
LinearPtr	dd	0		; holds linear address
SegmentedPtr	dd	0		; holds segmented address (real mode)
DMABuffLength	dw	0		; DMA Length (0 based) (MUST BE EVEN!)
DMABuffDivides	db	0		; # of splits in the DMA buffer

TheSampleRate	dd	0		; 3k through 88k
StereoMono	db	0		; ff for mono, 00 for stereo
PCMDirection	db	0		; bit mask for the DMA controller
SampleSize	db	0		; sample size: 0=8,1=12,2=16

	public	DMAAutoInit		; TRUE(0ffh) or FALSE(000h)
DMAAutoInit	db	0ffh		; to allow auto-init on DMA access

UserRoutine     dd      0               ; User Code

StatusWord      dw      0               ; Holds current status:
;					;   0 = inactive, available
;					;   1 = currently playing
;					;   2 = currently recording

OldIRQRoutine   dd      0               ; holds the original routine

polledmask	equ	bICsamprate+bICsampbuff ; polled mask
dmamask 	equ	bICsampbuff		; dma mask

DMAOUTPUT	equ	0+dmamask	; DMA is used to drive the output
POLLEDOUTPUT	equ	0+polledmask	; Polling routine drives output
DMAINPUT	equ	1+dmamask	; DMA is used to read input
POLLEDINPUT	equ	1+polledmask	; Polling routine reads input
TypeOfSetup	db	0		; polled/dma interrupt masks & stuff..

	public	NumberOfInterrupts
NumberOfInterrupts      dw      0       ; number of interrupts that have occured
;
; These variables direct our code to the proper DMA channel
;
OurDMAPageReg	dw	CH1PAGEREG	; default to DMA channel 1 page reg
OurDMAddress	dw	DMAC1ADDR	; default to DMA channel 1 address reg

;
; table of address pointers to the various DMA 2 addresses
;
        public  DMA1AddrTable
DMA1AddrTable	label	byte
	db	DEFAULTDMA	; DMA channel selected
        db      DMARDSTAT       ; DMA read status
	db	DMAWRCNTRL	; DMA write command register
	db	DMAWREQ 	; DMA write request register
	db	DMAWRSMR	; DMA write single mask register
	db	DMAWRMODE	; DMA write mode register
	db	DMACLEAR	; DMA clear low/high flip-flop
	db	DMARDTEMP	; DMA read temp register
	db	DMAWRCLR	; DMA write master clear
	db	DMACLRMSK	; DMA clear mask register
	db	DMAWRALL	; DMA write all mask register bits
;
; table of address pointers to the various DMA 2 addresses
;
	public	DMA2AddrTable
DMA2AddrTable   label   byte
        db      DEFAULTDMA      ; DMA channel selected
	db	DMA2RDSTAT	; DMA read status
	db	DMA2WRCNTRL	; DMA write command register
	db	DMA2WREQ	; DMA write request register
	db	DMA2WRSMR	; DMA write single mask register
	db	DMA2WRMODE	; DMA write mode register
	db	DMA2CLEAR	; DMA clear low/high flip-flop
	db	DMA2RDTEMP	; DMA read temp register
	db	DMA2WRCLR	; DMA write master clear
	db	DMA2CLRMSK	; DMA clear mask register
	db	DMA2WRALL	; DMA write all mask register bits

	public	DMAPointer
DMAPointer      dw      offset DMA1AddrTable    ; default to channel 1 table
;
; working variables
;
;
	public	TheIRQMask		; 8259 interrupt mask
	public	TheDMAMode		; 55h for input, 59h for output
	public	LinearPtr		; holds linear address
	public	SegmentedPtr		; holds segmented address (real mode)
	public	DMABuffLength		; DMA Length (0 based) (MUST BE EVEN!)
	public	DMABuffDivides		; # of splits in the DMA buffer
	public	TheSampleRate		; 3k through 88k
	public	StereoMono		; ff for mono, 00 for stereo
	public	PCMDirection		; bit mask for the DMA controller
	public	SampleSize		; sample size: 0=8,1=12,2=16
	public	DMAAutoInit		; TRUE/FALSE flag for DMA autoinit bit
	public	UserRoutine		; User Code
	public	StatusWord		; Holds current status:
	public	OldIRQRoutine		; holds the original routine
polledmask	equ	bICsamprate+bICsampbuff ; polled mask
dmamask 	equ	bICsampbuff		; dma mask
DMAOUTPUT	equ	0+dmamask	; DMA is used to drive the output
POLLEDOUTPUT	equ	0+polledmask	; Polling routine drives output
DMAINPUT	equ	1+dmamask	; DMA is used to read input
POLLEDINPUT	equ	1+polledmask	; Polling routine reads input
	public	TypeOfSetup		; polled/dma interrupt masks & stuff..
	public	NumberOfInterrupts	; number of interrupts that have occured
	public	OurDMAPageReg		; default to DMA channel 1 page reg
	public	OurDMAddress		; default to DMA channel 1 address reg

else	; not building DATA.OBJ, declare all publics

	extrn	DMAPointer	:word	; pointer to the DMA address table
	extrn	DMA1AddrTable	:byte	; 1st DMA controller table
	extrn	DMA2AddrTable	:byte	; 2nd DMA controller table
	extrn	TheIRQMask	:byte	; 8259 interrupt mask
	extrn	TheDMAMode	:byte	; 55h for input, 59h for output
	extrn	LinearPtr	:dword	; holds linear address
	extrn	SegmentedPtr	:dword	; holds segmented address (real mode)
	extrn	DMABuffLength	:word	; DMA Length (0 based) (MUST BE EVEN!)
	extrn	DMABuffDivides	:byte	; # of splits in the DMA buffer
	extrn	TheSampleRate	:dword	; 3k through 88k
	extrn	StereoMono	:byte	; ff for mono, 00 for stereo
	extrn	PCMDirection	:byte	; bit mask for the DMA controller
	extrn	SampleSize	:byte	; sample size: 0=8,1=12,2=16
	extrn	DMAAutoInit	:byte	; TRUE/FALSE flag for DMA autoinit bit
	extrn	UserRoutine	:dword	; User Code
	extrn	StatusWord	:word	; Holds current status:
	extrn	OldIRQRoutine	:dword	; holds the original routine
	extrn	TypeOfSetup	:byte	; polled/dma interrupt masks & stuff..
	extrn	NumberOfInterrupts:word ; number of interrupts that have occured
	extrn	OurDMAPageReg	:word	; default to DMA channel 1 page reg
	extrn	OurDMAddress	:word	; default to DMA channel 1 address reg


polledmask	equ	bICsamprate+bICsampbuff ; polled mask
dmamask 	equ	bICsampbuff		; dma mask
DMAOUTPUT	equ	0+dmamask	; DMA is used to drive the output
POLLEDOUTPUT	equ	0+polledmask	; Polling routine drives output
DMAINPUT	equ	1+dmamask	; DMA is used to read input
POLLEDINPUT	equ	1+polledmask	; Polling routine reads input
SHADOWTABLELEN  equ     28              ; 28 entries in the shadow data table

endif

	extrn	TheDMAChannel	  :byte ; defaults to channel 1
	extrn	TheIRQChannel	  :byte ; defaults to IRQ 7

        extrn   mvhwShadowPointer :dword; a common variable for all
	extrn	_MVHWVersionBits  :word ; hardware state bits
	extrn	_MVTranslateCode   :word

	.code

    externADDR	MVInitStatePtr		; pointer to the state table init code
    externADDR	mvGetHWVersion		; returns the hardware bits
;
; TEXT SEGMENT externals for the different modules
;

if PCMOBJ AND (NOT ALLOBJS)		; added for independent PCMOBJ module
    externADDR	SelectIRQ		; PCM uses "SelectIRQ"
    externADDR	_unloadirqvector	; PCM uses "_unloadirqvector"
    externADDR	_getirqoffset		; PCM uses "_getirqoffset"
endif

;
;---------------------------========================---------------------------
;---------------------------====< CODE SECTION >====---------------------------
;---------------------------========================---------------------------
;
;   /*\
;---|*|
;---|*|---------------====< Prototypes >====---------------
;---|*|
;---|*| void far *DMABuffer(char far *, int, int )
;---|*|
;---|*|     Passes in a pointer and length to the buffer.
;---|*|     Also flushes the buffer. Returns 0 or true DMA buffer ptr.
;---|*|
;---|*| int EnablePCMPlay()
;---|*|
;---|*|     Sets up the PCM hardware for polled output
;---|*|
;---|*| int EnablePCMRecord()
;---|*|
;---|*|     Sets up the PCM hardware for polled input
;---|*|
;---|*| char huge *FindDMABuffer(char huge *, int )
;---|*|
;---|*|     Takes a memory address & return the next 64k boundary
;---|*|
;---|*| MVState far *InitMVSound()
;---|*|
;---|*|     Initializes the int 2F interface & some global variables.
;---|*|
;---|*| int InitPCM()
;---|*|
;---|*|     Initializes the PCM code.
;---|*|
;---|*| void PausePCM()
;---|*|
;---|*|     Temporarily stops the PCM I/O by disabling the timers
;---|*|
;---|*| int PCMInfo( long ,int, int, int )
;---|*|
;---|*|     Sets up the transfer rate & stereo/mono/compression/data size
;---|*|
;---|*| int PCMPlay()
;---|*|
;---|*|     Starts the DMA feeding the DAC
;---|*|
;---|*| int PCMRecord()
;---|*|
;---|*|     Starts the DMA reading the ADC
;---|*|
;---|*| void RemovePCM()
;---|*|
;---|*|     kills the PCM code.
;---|*|
;---|*| void ResumePCM()
;---|*|
;---|*|     Restarts the PCM I/O by enabling the timers
;---|*|
;---|*| int SelectDMA( int )
;---|*|
;---|*|     Selects the DMA channel 1, or 3
;---|*|
;---|*| int SelectIRQ( int )
;---|*|
;---|*|     Selects the IRQ line for DMA control
;---|*|
;---|*| void StopPCM()
;---|*|
;---|*|     Turn off the PCM timers, interrupts, and state machine.
;---|*|
;---|*| void UserFunc((*long)())
;---|*|
;---|*|     Call back routine when Half way buffer is full/empty.
;---|*|
;   \*/

if PCMOBJ

;   /*\
;---|*|------====< void far * DMABuffer( char far *, int, int ) >====------
;---|*|
;---|*| Passes in a pointer and length to the buffer
;---|*|
;---|*| Entry Conditions:
;---|*|     dParm1 points to DMA buffer
;---|*|     wParm3 points to the length (wParm3 * 1024 is the total length )
;---|*|     wParm4 is the # of divisions of the DMA buffer
;---|*|
;---|*| Exit Conditions:
;---|*|     DX:AX = 0 - bad buffer
;---|*|     DX:AX = returns buffer pointer
;---|*|
;   \*/

	public	DMABuffer
DMABuffer	proc
        push    bp
	mov	bp,sp
	push	es
	push	di

	mov	cx,wParm3		; get the dma size (4/8/16/32/64)
	cmp	cx,64			; too high?
	ja	dmabbad 		; yes, bomb out

        mov     ax,1024
	cwd
	mul	cx			; get the length - 1
	dec	ax
	mov	[DMABuffLength],ax	; and save for later use

	les	di,dParm1		; get the far pointer to the buffer

        mov     ax,di
	mov	bx,es			; convert it to a linear address
	mov	dx,es
	mov	cl,4
	rol	dx,cl
	and	dx,000fh
	shl	bx,cl			; add offset portion of seg to offset
	add	ax,bx
	jc	dmabbad 		; bad if extends over a 64k boundary

	mov	wptr [LinearPtr+0],ax	; save the linear address
	mov	wptr [LinearPtr+2],dx
	mov	wptr [SegmentedPtr+0],di
	mov	wptr [SegmentedPtr+2],es

	mov	bx,wParm4		; get the # of DMA buffer divisions
	mov	[DMABuffDivides],bl

	push	ax			; save the buffer offset

	mov	cx,[DMABuffLength]	; get the clearing length
        mov     al,80h                  ; PCM silent code
	cld
	rep stosb			; flush the buffer length...
	stosb				; + 1

	mov	dx,es
        pop     ax                      ; return dx:ax
	jmp	short dmabdone
;
dmabbad:
	sub	ax,ax
	cwd
;
dmabdone:
        pop     di
	pop	es
	pop	bp
	ret

DMABuffer	endp


;   /*\
;---|*|---------------====< EnablePCMPlay() >====---------------
;---|*|
;---|*| Enabled the DAC hardware for polled output (no interrupts)
;---|*|
;---|*| Entry Conditions:
;---|*|     None
;---|*|
;---|*| Exit Conditions:
;---|*|     AX =  0 good start
;---|*|     AX = -1 not started, problem occured
;   \*/

	public	EnablePCMPlay
EnablePCMPlay	proc
	push	es

	mov	ax,wptr [TheSampleRate+0] ; Validate the sample rate
	or	ax,wptr [TheSampleRate+2]
	jz	enaPCMPlay_bad		  ; zero sample rate, bomb out...

	call	FFAR ptr EnablePlaying	  ; go for it...
	mov	ax,0
	jmp	short enaPCMPlay_exit
;
enaPCMPlay_bad:
	mov	ax,-1
;
enaPCMPlay_exit:
	pop	es
        ret

EnablePCMPlay	endp


;   /*\
;---|*|---------------====< EnablePCMRecord() >====---------------
;---|*|
;---|*| Enabled the ADC hardware for polled input (no interrupts)
;---|*|
;---|*| Entry Conditions:
;---|*|     None
;---|*|
;---|*| Exit Conditions:
;---|*|     AX =  0 recording has started
;---|*|     AX = -1 recording did not start
;---|*|
;   \*/

	public	EnablePCMRecord
EnablePCMRecord proc
        push    es

	mov	ax,wptr [TheSampleRate+0] ; Validate the sample rate
	or	ax,wptr [TheSampleRate+2]
	jz	enaPCMRec_bad

	call	FFAR ptr EnableRecording  ; go for it...
	mov	ax,0
	jmp	short enaPCMRec_exit
;
enaPCMRec_bad:
	mov	ax,-1
;
enaPCMRec_exit:
	pop	es
        ret

EnablePCMRecord endp


;   /*\
;---|*|------====< char far *FindDMABuffer(char far *,int ) >====------
;---|*|
;---|*| Finds the next 64k boundary starting with dParm1 address
;---|*|
;---|*| Entry Conditions:
;---|*|     dParm1 points to a buffer twice the size needed for the DMA
;---|*|     wParm2 is the size of the DMA buffer (4/8/16/32/64)
;---|*|
;---|*| Exit Conditions:
;---|*|     DX:AX = returns buffer pointer on a 64k boundary
;---|*|     DX:AX = 0 if boundary wraps beyond 1 meg.
;---|*|
;   \*/

	public	FindDMABuffer
FindDMABuffer	proc
	push	bp
	mov	bp,sp

	sub	ax,ax			; default to bad...
	cwd

        mov     cx,wParm3               ; get the length
	cmp	cx,64			; 64k buffer requested?
	ja	fdb_exit		; too high, exit
	jz	fdb_64k 		; yes, special case it...
	cmp	cl,4
	jb	fdb_exit		; too low, exit

	mov	ax,1024 		; calculate the length of the DMA buffer
	mul	cx
	mov	bx,ax			; bx holds the length

	mov	dx,wptr dParm1+2
	mov	ax,dx			; accumulate the offset in ax
	and	ax,0fffh		; get the offset portion of the segment
	and	dx,0f000h		; make DX a 64k segment register
        mov     cl,4
	shl	ax,cl			; make the segment an offset
	add	ax,wptr dParm1+0	; accumulate the rest of the offset
	add	bx,ax			; does the buffer cross a 64k boundary?
	cmc				; carry set if 64k crossing
	sbb	bx,bx			; bx = ffff if no crossing, 0 if crossed
	and	ax,bx			; clear ax if crossed
	not	bx			; bx = ffff if crossed, 0000 if not
	and	bx,1000h
	add	dx,bx			; advance dx if we crossed a 64k boundary
	jmp	short fdb_exit
;
fdb_64k:
	mov	dx,wptr dParm1+2	; get the segment
        sub     ax,ax                   ; offset is zero
	and	dx,0f000h		; find the next segment
	add	dx,1000h		; move to next segment
	jnc	fdb_exit		; if doesn't wraps past 1 meg, it's good
        cwd                             ; bad, flush it...
;
fdb_exit:
	pop	bp
	ret

FindDMABuffer	endp
;
endif	; PCMOBJ
if MISC1OBJ

;
;   /*\
;---|*|---------------====< InitMVSound() >====---------------
;---|*|
;---|*| Initializes this body of code. It will try to find the int 2F DOS
;---|*| interface to the MVPROAS device. Once found, the new state table
;---|*| pointer will be loaded.
;---|*|
;---|*| Entry Conditions:
;---|*|     None
;---|*|
;---|*| Exit Conditions:
;---|*|     DX:AX point to the state table
;   \*/

	public	InitMVSound
InitMVSound	proc
	push	es
	push	di
	mov	ax,ds
	mov	es,ax
;
; find the hardware. Should be installed for this to run
;
	call	MVInitStatePtr		; initialize the state table ptr

	cmp	_MVHWVersionBits,-1	; initialized yet?
	jnz	@F			; yes, continue on...
	mov	ax,USE_ACTIVE_ADDR	; pass in the base address
	push	ax
	call	mvGetHWVersion		; no, checkout the hardware
	pop	ax
    ;
    @@:
	mov	ax,wptr [mvhwShadowPointer+0]
	mov	dx,wptr [mvhwShadowPointer+2]

	pop	di
	pop	es
	ret

InitMVSound	endp
;
endif	; MISC1OBJ
if	PCMOBJ
;
;   /*\
;---|*|---------------====< InitPCM() >====---------------
;---|*|
;---|*| Initializes the PCM code
;---|*|
;---|*| Entry Conditions:
;---|*|     None
;---|*|
;---|*| Exit Conditions:
;---|*|     AX =  Version of this Code
;   \*/

	public	InitPCM
InitPCM proc
	push	es

	mov	[TypeOfSetup],polledmask	; flushes both interrupts
						; for upcoming calls
	mov	al,TheDMAChannel		; initialize the DMA now...
	cbw
	push	ax
	call	FFAR ptr SelectDMA
	pop	ax

	mov	al,TheIRQChannel		; initialize the IRQ now...
	cbw
	push	ax
	call	FFAR ptr SelectIRQ		; also removes the IRQ
	pop	ax

        call    FFAR ptr StopPCM                ; kill the Audio Spectrum board

	mov	dx,INTRCTLRST			; flush any pending PCM irq
	xor	dx,[_MVTranslateCode]		; xlate the board address
	out	dx,al

	sub	ax,ax
	mov	[TypeOfSetup],al		; unknown type

	mov	wptr [UserRoutine+0],ax
	mov	wptr [UserRoutine+2],ax

	mov	wptr [LinearPtr+0],ax
	mov	wptr [LinearPtr+2],ax
	mov	wptr [SegmentedPtr+0],ax
        mov     wptr [SegmentedPtr+2],ax

	mov	wptr [DMABuffLength],ax

	not	al
	mov	[StereoMono],al 		; mono

	mov	ax,11025
	cwd

	call	FFAR ptr _calcsamplerate	; setup the default rate
	mov	wptr [TheSampleRate+0],ax
	mov	wptr [TheSampleRate+2],dx

	mov	ax,0003h			; for compatibility, version 00.03

	pop	es
	ret

InitPCM endp
;
endif	; PCMOBJ
if PCMOBJ
;
;
;   /*\
;---|*|---------------====< PausePCM >====---------------
;---|*|
;---|*| Turn off the h/w timer enables to effectively stop pcm temporarily.
;---|*|
;---|*| Entry Conditions:
;---|*|     None
;---|*|
;---|*| Exit Conditions:
;---|*|     Nothing
;---|*|
;   \*/
;
	public	PausePCM
PausePCM	proc
	push	es
	push	di
;
; Setup the audio filter sample bits
;
	les	di,[mvhwShadowPointer]
        mov     dx,AUDIOFILT
	xor	dx,[_MVTranslateCode]	   ; xlate the board address

        disable

        mov     al,es:[di._audiofilt]
	and	al,not bFIsrate 	   ; flush the enable bits
	mov	es:[di._audiofilt],al
	out	dx,al

	enable

        pop     di
	pop	es
	ret

PausePCM	endp

;
;   /*\
;---|*|---------------====< PCMInfo ( long, int, int, int ) >====---------------
;---|*|
;---|*| Selects xfer rate & stereo/mono
;---|*|
;---|*| Entry Conditions:
;---|*|     Parm #1 (wParm1, wParm2) is the rate
;---|*|     Parm #2 (wParm3)	     is the stereo(1) or mono (0)
;---|*|     Parm #3 (wParm4)	     is the compression flag
;---|*|     Parm #4 (wParm5)	     is the data size (8/12/16 bits per sample)
;---|*|
;---|*| Exit Conditions:
;---|*|     AX =  0, good data
;---|*|     AX = -1, bum transfer rate
;---|*|
;   \*/

	public	PCMInfo
PCMInfo 	proc
	push	bp
	mov	bp,sp

	mov	al,wParm4+2		; get the data size
	cbw
	cmp	al,8			; 8 bit channel?
	jz	@F

        inc     ah
	cmp	al,12			; 12 bit channel
	jz	@F

	inc	ah
	cmp	al,16			; 16 bit channel?
	jnz	pcinf_bad		; no, bomb out...
    ;
    @@:
	mov	[SampleSize],ah

        mov     cx,wParm3               ; get the stereo flag
	shr	cx,1
	jnz	pcinf_bad		; exit bad if not zero

	cmc				; set for mono, cleared for stereo
	sbb	cx,cx			; make a full bit mask
	mov	[StereoMono],cl 	; save only valid values

        mov     ax,dParm1+0             ; get the sample rate
	mov	dx,dParm1+2

	or	cx,cx			; is it mono?
	jnz	@F			; yes, skip the doubling
	shl	ax,1			; no, stereo, so double the sample rate
	adc	dx,dx
    ;
    @@:
	call	FFAR ptr _calcsamplerate
	jc	pcinf_bad
	mov	wptr [TheSampleRate+0],ax
	mov	wptr [TheSampleRate+2],dx

	sub	ax,ax
	jmp	short pcinf_exit
;
pcinf_bad:
	mov	ax,-1
;
pcinf_exit:
        pop     bp
	ret

PCMInfo 	endp
;
;
;   /*\
;---|*|---------------====< PCMPlay() >====---------------
;---|*|
;---|*| Starts the DMA feeding the DAC
;---|*|
;---|*| Entry Conditions:
;---|*|     None
;---|*|
;---|*| Exit Conditions:
;---|*|     AX =  0 good start
;---|*|     AX = -1 not started, problem occured
;   \*/

	public	PCMPlay
PCMPlay proc
	push	es

	les	ax,LinearPtr		  ; Validate the buffer pointer
	mov	bx,es
	or	ax,bx
	jz	PCMPlay_bad

	mov	ax,[DMABuffLength]	  ; Validate the buffer count
	or	ax,ax
	jz	PCMPlay_bad

	mov	ax,wptr [TheSampleRate+0] ; Validate the sample rate
	or	ax,wptr [TheSampleRate+2]
	jz	PCMPlay_bad		  ; zero sample rate, bomb out...

	call	FFAR ptr StartPlaying	  ; go for it...
	sub	ax,ax
	jmp	short PCMPlay_exit
;
PCMPlay_bad:
	mov	ax,-1
;
PCMPlay_exit:
	pop	es
        ret

PCMPlay endp
;
;
;   /*\
;---|*|---------------====< PCMRecord() >====---------------
;---|*|
;---|*| Starts the DMA reading the ADC
;---|*|
;---|*| Entry Conditions:
;---|*|     None
;---|*|
;---|*| Exit Conditions:
;---|*|     AX =  0 recording has started
;---|*|     AX = -1 recording did not start
;---|*|
;   \*/

	public	PCMRecord
PCMRecord proc
        push    es

	les	ax,LinearPtr		  ; Validate the buffer pointer
	mov	bx,es
	or	ax,bx
	jz	PCMRec_bad

	mov	ax,[DMABuffLength]	  ; Validate the buffer count
	or	ax,ax
	jz	PCMRec_bad

	mov	ax,wptr [TheSampleRate+0] ; Validate the sample rate
	or	ax,wptr [TheSampleRate+2]
	jz	PCMRec_bad

	call	FFAR ptr StartRecording   ; go for it...
	mov	ax,0
	jmp	short PCMRec_exit
;
PCMRec_bad:
	mov	ax,-1
;
PCMRec_exit:
	pop	es
        ret

PCMRecord	endp
;
;
;   /*\
;---|*|---------------====< RemovePCM () >====---------------
;---|*|
;---|*| Remove our code from the system
;---|*|
;---|*| Entry Conditions:
;---|*|     None
;---|*|
;---|*| Exit Conditions:
;---|*|     None
;   \*/

	public	RemovePCM
RemovePCM proc

	call	FFAR ptr StopPCM		; kill the Audio Spectrum board

        mov     dx,INTRCTLRST                   ; flush any pending PCM irq
        xor     dx,[_MVTranslateCode]           ; xlate the board address
        out     dx,al

	call	FFAR ptr _unloadirqvector	; restore the original vector

        ret

RemovePCM endp
;
;
;   /*\
;---|*|---------------====< ResumePCM >====---------------
;---|*|
;---|*| Turn on the h/w from making interrupt and DMA requests. This assumes
;---|*| the hardware has already been setup and is ready to go...
;---|*|
;---|*| Entry Conditions:
;---|*|     None
;---|*|
;---|*| Exit Conditions:
;---|*|     Nothing
;---|*|
;   \*/
;
	public	ResumePCM
ResumePCM	proc
	push	es
	push	di
;
; Setup the audio filter sample bits
;
	les	di,[mvhwShadowPointer]
	call	FFAR ptr _ASloadtimer0

        disable

        mov     dx,AUDIOFILT
	xor	dx,[_MVTranslateCode]	; xlate the board address
	mov	al,es:[di._audiofilt]
	or	al,bFIsrate		; enable sample rate and buffer timers
	mov	es:[di._audiofilt],al
	out	dx,al

	enable

        pop     di
	pop	es
	ret

ResumePCM       endp
;
;
;
;   /*\
;---|*|---------------====< SelectDMA( int ) >====---------------
;---|*|
;---|*| Selects the DMA channel 0 - 7, excluding 4
;---|*|
;---|*| Entry Conditions:
;---|*|     wParm1 points to DMA number (0, 1, 2, 3, 5, 6, 7 )
;---|*|
;---|*| Exit Conditions:
;---|*|     AX =  0, good buffer, all okay
;---|*|     AX = -1, good buffer, all okay
;---|*|
;   \*/

        public  SelectDMA
SelectDMA	proc
	push	bp
	mov	bp,sp

        mov     ax,wParm1               ; get the DMA #

	and	ax,0111b		; save the channels

        mov     bx,ax                   ; get some of the I/O addreses
	shl	bx,1			; into DX
	jnz	seldma_02		; not channel 0, go use it...
	push	sp			; 8088/86 class machine?
	pop	cx
	cmp	cx,sp
	jnz	seldma_bad		; yes, can't do channel 0 dma
    ;
    seldma_02:
        mov     dx,cs:[dmatable+bx]
	or	dx,dx			; valid entry?
	jz	seldma_bad		; no, bomb out...

        mov     TheDMAChannel,al        ; select the channel.
	mov	bptr OurDMAPageReg,dh	; ...the page register,
	mov	bptr OurDMAddress,dl	; ...the address register,

	lea	bx,DMA1AddrTable	; get the DMA channel addresses
	cmp	al,4
	jl	seldma_05
	lea	bx,DMA2AddrTable	; get the DMA channel addresses
	sub	al,4			; make it zero based
    ;
    seldma_05:
	mov	[bx._dmach],al		; save the adjusted dma channel #
	mov	[DMAPointer],bx 	; save the pointer to all DMA addrs
	sub	ax,ax
	pop	bp
	ret
;
seldma_bad:
	mov	ax,-1
	pop	bp
	ret
;
; dma channels, etc
;
dmatable	label	word
	dw	(CH0PAGEREG SHL 8) + DMAC0ADDR
        dw      (CH1PAGEREG SHL 8) + DMAC1ADDR
	dw	(CH2PAGEREG SHL 8) + DMAC2ADDR
	dw	(CH3PAGEREG SHL 8) + DMAC3ADDR
        dw      0
	dw	(CH5PAGEREG SHL 8) + DMA2C5ADDR
	dw	(CH6PAGEREG SHL 8) + DMA2C6ADDR
	dw	(CH7PAGEREG SHL 8) + DMA2C7ADDR

SelectDMA       endp

endif	; PCMOBJ
if MISC2OBJ

;   /*\
;---|*|---------------====< SelectIRQ( int ) >====---------------
;---|*|
;---|*| Selects the IRQ line for DMA control
;---|*|
;---|*| Entry Conditions:
;---|*|     wParm1 points to IRQ number (3,5,6,7)
;---|*|
;---|*| Exit Conditions:
;---|*|     AX =  0, good buffer, all okay
;---|*|     AX = -1, bad IRQ #
;---|*|
;   \*/

        public  SelectIRQ
SelectIRQ	proc
	push	bp
	mov	bp,sp

	call	FFAR ptr _unloadirqvector ; attempt to restore original vector

	sub	ax,ax			; flush ax for a bad return

	mov	cx,wParm1		; get the irq # (2-7,10-12,14-15)
	and	cl,0fh			; save only the valid bits
	mov	bx,01
	shl	bx,cl

	and	bx,1001110010111100b	; save the mask bit only if the irq
        jz      seirqbad                ; is a valid selection

	mov	TheIRQChannel,cl	; save the channel number

	cmp	cl,8			; 2nd interrupt controller?
	jb	@F			; no, skip
	xchg	bh,bl
    ;
    @@:
	mov	TheIRQMask,bl
	call	FFAR ptr _loadirqvector ; load the new vector
	mov	ax,1
;
seirqbad:
	dec	ax
	pop	bp
	ret

SelectIRQ       endp
;
endif	; MISC2OBJ
if PCMOBJ

;
;   /*\
;---|*|--------------------====< void StopPCM() >====--------------------
;---|*|
;---|*| Turn off the PCM timers, interrupts, and state machine.
;---|*|
;---|*| Entry Conditions:
;---|*|
;---|*| Exit  Conditions:
;---|*|
;---|*|
;   \*/

	public	StopPCM
StopPCM proc
	push	es
	push	di
	les	di,[mvhwShadowPointer]
;
; clear the audio filter sample bits
;
	mov	dx,AUDIOFILT
	xor	dx,[_MVTranslateCode]	; xlate the board address
	disable 			; drop dead...
	mov	al,es:[di._audiofilt]	; get the state
	and	al,not (bFIsrate+bFIsbuff) ; flush the sample timer bits
	mov	es:[di._audiofilt],al	; save the new state
	out	dx,al
;
; clear the PCM enable bit
;
	mov	al,es:[di._crosschannel]; get the current cross channel
	and	al,not bCCenapcm	; clear the PCM enable bit
	or	al,bCCdac
        mov     dx,CROSSCHANNEL
	xor	dx,[_MVTranslateCode]	; xlate the board address
	out	dx,al			; end to the hardware
	mov	es:[di._crosschannel],al
;
; disable the 16 bit stuff
;
	test	[_MVHWVersionBits],bMV101  ; 101 chip?
	jz	stpc02			   ; no, don't touch this...
	mov	dx,SYSCONFIG2
	xor	dx,[_MVTranslateCode]	   ; xlate the board address
	in	al,dx
	and	al,not bSC216bit+bSC212bit ; flush the 16 bit stuff
	out	dx,al
    ;
    stpc02:
;
; clear the appropriate Interrupt Control Register bit
;
	mov	ah,TypeOfSetup
	and	ah,bICsamprate+bICsampbuff
	not	ah
	mov	dx,INTRCTLR
	xor	dx,[_MVTranslateCode]	; xlate the board address
	in	al,dx
	and	al,ah			; kill sample timer interrupts
	out	dx,al
	mov	es:[di._intrctlr],al
;
; clear the system interrupt mask only if no other ints are used
;
	test	al,fICintmaskbits XOR (bICsamprate+bICsampbuff)
	jnz	stpc10
;
; select the correct IRQ controller, then mask it...
;
	cmp	TheIRQChannel,2 	; Chained IRQ channel?
	jz	stpc10			; yes, leave it open...

        mov     dx,IRQ1MASKREG
	cmp	TheIRQChannel,8 	; 2nd IRQ controller?
	jl	stpc05
	mov	dl,IRQ2MASKREG
    ;
    stpc05:
	in	al,dx
	or	al,[TheIRQMask]
	out	dx,al
	enable				; start again...
;
stpc10:
	call	FFAR ptr KillDMA	; stop the DMA too...

	mov	[StatusWord],0		; inactive, available

	pop	di
	pop	es
	ret

StopPCM endp

;
;   /*\
;---|*|---------------====< UserFunc((*long)()) >====---------------
;---|*|
;---|*| Call back routine when Half way buffer is full/empty.
;---|*|
;---|*| Entry Conditions:
;---|*|     dParm1 points to the user routine
;---|*|
;---|*| Exit Conditions:
;---|*|     Nothing
;---|*|
;   \*/

	public	UserFunc
UserFunc proc
	push	bp
	mov	bp,sp

        push    es

	les	ax,dParm1		; get the routine
	mov	dx,es
	or	dx,ax
	jz	usfu_bad

	mov	wptr [UserRoutine+0],ax
	mov	wptr [UserRoutine+2],es
;
usfu_bad:
	pop	es
	pop	bp
	ret

UserFunc endp

endif	; PCMOBJ

;
;
;----------------------------========================--------------------------
;----------------------------========================--------------------------
;----------------------------========================--------------------------
;
;
;			PPPPPPPP      CCCCCC   MMM	 MMM
;			PPPPPPPP     CCCCCCC   MMMM	MMMM
;			PPP   PPP   CCC        MMMMM   MMMMM
;			PPP   PPP   CCC        MMMMMM MMMMMM
;			PPPPPPPP    CCC        MMM MMMMM MMM
;			PPPPPPPP    CCC        MMM  MMM  MMM
;			PPP	    CCC        MMM   M	 MMM
;			PPP	    CCC        MMM	 MMM
;			PPP	     CCCCCCC   MMM	 MMM
;			PPP	      CCCCCC   MMM	 MMM
;
;	 rrrrr	  oooo	 uu  uu  tttttt  iiiiii  nn  nn   eeeee   sssss
;	 rr  rr  oo  oo  uu  uu    tt	   ii	 nnn nn  ee	 ss
;	 rrrrrr  oo  oo  uu  uu    tt	   ii	 nnnnnn  eeeeee   ssss
;	 rr rr	 oo  oo  uu  uu    tt	   ii	 nn nnn  ee	     ss
;	 rr  rr   oooo	  uuuuuu   tt	 iiiiii  nn  nn   eeeee  sssss
;
;
;
;----------------------------========================--------------------------
;----------------------------========================--------------------------
;----------------------------========================--------------------------

if PCMOBJ

;
;----------------------------====< _ASloadtimer0 >====-------------------------
;
;
; Setup the Sample Timer (T0 & square wave output)
;
;   Entry Conditions:
;	ES:DI point to the state table.
;   Exit Conditions:
;	The timer is loaded with the new sample rate
;
	public	_ASloadtimer0
_ASloadtimer0	proc

        mov     al,00110110b            ; 36h Timer 0 & square wave
	mov	dx,TMRCTLR
	xor	dx,[_MVTranslateCode]	; xlate the board address

	pushf
	cli

	out	dx,al			; setup the mode, etc
        mov     es:[di._tmrctlr],al

	mov	ax,es:[di._samplerate]	; pre-calculated & saved in prior code
	mov	dx,SAMPLERATE
	xor	dx,[_MVTranslateCode]	; xlate the board address
	out	dx,al			; output the timer value

        pause

        xchg    ah,al
	out	dx,al

        popf
	ret

_ASloadtimer0   endp

;
;----------------------------====< _ASloadtimer1 >====-------------------------
;
        public  _ASloadtimer1
_ASloadtimer1   proc
	push	es
	push	di
	les	di,[mvhwShadowPointer]

        push    dx                      ; do not disturb any register
	push	ax

        mov     al,01110100b            ; 74h Timer 1 & rate generator
	mov	dx,TMRCTLR
	xor	dx,[_MVTranslateCode]	; xlate the board address

        disable

        out     dx,al
	mov	es:[di._tmrctlr],al	; local timer control register

	pop	ax

        mov     dx,SAMPLECNT
	xor	dx,[_MVTranslateCode]	; xlate the board address
	mov	es:[di._samplecnt],ax

        out     dx,al
        pause
	xchg	ah,al
	out	dx,al

	enable

	xchg	ah,al

	pop	dx
	pop	di
	pop	es
	ret

_ASloadtimer1	endp
;
;
;--------------------------====< _calcsamplerate >====-------------------------
;
;  Calculate the H/W timer value (internal routine)
;
; Entry Conditions:
;	DX:AX hold the users requested sample rate
;
; Exit Conditions:
;	carry SET if bad value
;	No registers modified
;
;
	public	_calcsamplerate
_calcsamplerate proc
	push	es
	push	di
	les	di,[mvhwShadowPointer]

	push	ax
	push	bx
	push	cx
	push	dx
;
; make sure sample rate does not exceed 88200
;
        mov     cx,ax                   ; do 32 bit subtraction
	sub	cx,05888H+1		; 157C0 is decimal 88200
	mov	cx,dx			; too high?
	sbb	cx,00001H		;  over 88200 khz is bad
	jnc	CaSaRa_bad		; bomb out greater than 88200
;
; load 1193180 in bx:cx for 32x32 bit division
;
	mov	bx,0012h
	mov	cx,34dch		; load bx:cx with 1193180

	xchg	bx,dx			; dx:ax = 1193180
	xchg	cx,ax			; bx:cx = sample rate
;
; since we don't have 32x32 bit division, we'll cheat here. No great loss.
;
	or	bx,bx			; is value over 64k?
	jz	@F			; no, continue on...
	shr	bx,1			; yes, divide all by 2
	rcr	cx,1			; to allow division of 32x16 bits
	shr	dx,1
	rcr	ax,1
;
@@:
	div	cx
	mov	es:[di._samplerate],ax	; save just the low order
	clc
	jmp	short CaSaRa_exit
;
CaSaRa_bad:
	stc
;
CaSaRa_exit:
	pop	dx
	pop	cx
	pop	bx
	pop	ax

        pop     di
	pop	es
	ret

_calcsamplerate endp
;
;
;----------------------------====< EnableRecording >====-----------------------
;
	public	EnableRecording
EnableRecording  proc
;
; Save the type of setup. It contains the interrupt masks needed
;
	mov	[TypeOfSetup],POLLEDINPUT
	mov	[PCMDirection],0	; bit 6 is cleared (DAC enable)
;
; enable the onboard sampling timers, disable interrupts
;
	call	FFAR ptr SetupPCMPolledIO ; Setup the MV Hardware
	mov	StatusWord,2		; set the global status word
        ret

EnableRecording  endp
;
;
;----------------------------====< EnablePlaying >====-------------------------
;
	public	EnablePlaying
EnablePlaying    proc
;
; Save the type of setup. It contains the interrupt masks needed
;
	mov	[TypeOfSetup],POLLEDOUTPUT
        mov     [PCMDirection],bCCdac   ; bit d6 of interrupt control register
;
; enable the onboard sampling timers, etc.
;
	call	FFAR ptr SetupPCMPolledIO ; Setup the MV Hardware
	mov	StatusWord,1		; set the global status word
 
        ret

EnablePlaying	 endp
;
endif	; PCMOBJ
if MISC2OBJ
;
;---------------------------====< _getirqoffset >====--------------------------
;
; takes the IRQ # & converts to offset in vector table
;
;
	public	_getirqoffset
_getirqoffset   proc
        sub     bh,bh
	shl	bx,1
	mov	bx,cs:[irqoffsets+bx]
	ret

irqoffsets	label	word
	dw	(8+0)*4 		; IRQ 0 clock timer
	dw	(8+1)*4 		; IRQ 1 keyboard
	dw	(8+2)*4 		; IRQ 2 available
	dw	(8+3)*4 		; IRQ 3 COM2
	dw	(8+4)*4 		; IRQ 4 COM1
	dw	(8+5)*4 		; IRQ 5 available
	dw	(8+6)*4 		; IRQ 6 floppy controller
	dw	(8+7)*4 		; IRQ 7 LPT
	dw	(70h+0)*4		; IRQ 8 real time clock
	dw	(70h+1)*4		; IRQ 9 Re-Directed IRQ2
	dw	(70h+2)*4		; IRQ a available
	dw	(70h+3)*4		; IRQ b available
	dw	(70h+4)*4		; IRQ c available
	dw	(70h+5)*4		; IRQ d coprocessor
	dw	(70h+6)*4		; IRQ e Hard Disk
	dw	(70h+7)*4		; IRQ f available

_getirqoffset   endp
;
endif	; MISC2OBJ
if PCMOBJ
;
;------------------------------====< KillDMA >====-----------------------------
;
; KillDMA  -- flush our settings
;
; Entry Conditions:
;
KillDMA proc
	push	es
	push	di

        cmp     StatusWord,0            ; is there any activity?
	jz	kidm_none

	mov	di,[DMAPointer] 	; get the DMA pointer
        sub     dx,dx                   ; clear out the high byte
;
; mask out the DMA to stop it
;
	disable
	mov	al,[di._dmach]		; get the adjusted dma channel #
        or      al,0100b                ; disable the DMA
	mov	dl,[di._dmawrsmr]
	out	dx,al
;
; remove control on the DRQ line
;
        les     di,[mvhwShadowPointer]

        mov     al,es:[di._crosschannel]; get the state
        mov     dx,CROSSCHANNEL
        xor     dx,[_MVTranslateCode]           ; xlate the board address
	and	al,not bCCdrq		; clear the DRQ bit
	out	dx,al

        mov     es:[di._crosschannel],al; and save the new state
	enable
;
kidm_none:
	pop	di
	pop	es
	ret

KillDMA endp
;
endif	; PCMOBJ
if PCMOBJ
;
;------------------------------====< LoadDMA >====-----------------------------
;
; LoadDMA  -- Load the DMA controller to read/write data to the MV board
;
; Entry Conditions:
;
;
	public	LoadDMA
LoadDMA proc
	push	es
	push	di
	push	si

        les     di,[mvhwShadowPointer]

	mov	si,[DMAPointer] 	; point to the DMA controller table
	sub	dx,dx			; clear out the high byte
;
; kill the dma until all programming is done
;
	mov	al,[si._dmach]		; get the adjusted dma channel #
        or      al,0100b                ; causes all DMA to be suspended
	mov	dl,[si._dmawrsmr]
	out	dx,al
;
; program the mode
;
        mov     al,[TheDMAmode]         ; get the app's desired mode
	or	al,[si._dmach]		; merge the adjusted dma channel #
	mov	dl,[si._dmawrmode]
        out     dx,al
;
; adjust the address for a 16 bit channel
;
	mov	ax,wptr [LinearPtr+2]	; get the page #
;
; setup the page register
;
	mov	dx,[OurDMAPageReg]
	out	dx,al
	mov	bl,al
;
; reset the flip-flop, then output the address, then count
;
	mov	dl,[si._dmaclear]	; dh is still clear...
        out     dx,al                   ; flush...

	mov	ax,wptr [LinearPtr+0]	; get the low 16 bits
	cmp	si,offset DMA1AddrTable ; 1st DMA controller?
	jz	@F			; yes, continue on...
	shr	bl,1			; no, divide the buffer in half
	rcr	ax,1			; by shifting 17 bits
    ;
    @@:
	mov	dx,[OurDMAddress]
	out	dx,al
	pause
	xchg	ah,al
	out	dx,al

	mov	ax,[DMABuffLength]
	cmp	si,offset DMA1AddrTable ; is this the 2nd dma controller?
	jz	lodma03 		; no, use the full length
	shr	ax,1
        inc     dx                      ; move to next port address
    ;
    lodma03:
	inc	dx			; move to next port address
	out	dx,al
	pause
	xchg	ah,al
	out	dx,al
;
; before we enable the DMA, let's make sure the DRQ is controlled, not floating
;
	mov	al,es:[di._crosschannel]; get the state
        mov     dx,CROSSCHANNEL
        xor     dx,[_MVTranslateCode]           ; xlate the board address
	or	al,bCCdrq		; set the DRQ bit to control it
	out	dx,al
	mov	es:[di._crosschannel],al; and save the new state
;
; re-enable the dma now that all programming is done
;
	mov	al,[si._dmach]		; get the adjusted dma channel #
        sub     dx,dx                   ; clear dh
	mov	dl,[si._dmawrsmr]
	out	dx,al			; & let'er loose (not moving though...)
;
; all done, return home...
;
	pop	si
	pop	di
	pop	es
	ret

LoadDMA endp
;
endif	; PCMOBJ
if MISC2OBJ
;
;--------------------------====< _loadirqvector >====--------------------------
;
; Restore the original vector
;
; Entry Conditions:
;
; Exit Conditions:
;     Nothing

	public	_loadirqvector
_loadirqvector  proc
	push	es

        les     ax,OldIRQRoutine
	mov	bx,es
	or	bx,ax
	jnz	@F

	lea	ax,OurIntVector
	mov	dx,cs

	mov	bl,[TheIRQChannel]
	call	FFAR ptr _getirqoffset

	push	ds
	sub	cx,cx
	mov	ds,cx

	disable
	xchg	ds:[bx+0],ax
	xchg	ds:[bx+2],dx
	enable

	pop	ds

	mov	wptr [OldIRQRoutine+0],ax
	mov	wptr [OldIRQRoutine+2],dx
;
@@:
	pop	es
	ret

_loadirqvector	endp
;
endif	; MISC2OBJ
if MISC2OBJ
;
;---------------------------====< OurIntVector >====---------------------------
;
; OurIntVector	-- process DMA interrupts
;

intsemaphore	db	-1		; -1 free, 0+ locked

	public	OurIntVector
OurIntVector    proc
	push	dx
	push	ax
        push    ds

if MODELSIZE eq 0
	mov	ax,cs			; tiny (.com) model uses cs segment
else
	mov	ax,@data		; all others use their data segments
endif
	mov	ds,ax

	mov	dx,INTRCTLRST		; clear the interrupt
        xor     dx,[_MVTranslateCode]           ; xlate the board address
	in	al,dx

        test    al,bISsampbuff          ; our interrupt?
	jz	skip_int		; no, continue on...

	out	dx,al			; yes, flush it...

	mov	al,EOI			; clear the interrupt
	cmp	TheIRQChannel,8 	; 2nd IRQ controller?
	jb	@F
	out	IRQ2ACKREG,al
    ;
    @@:
	out	IRQ1ACKREG,al

        inc     [NumberOfInterrupts]

	inc	cs:[intsemaphore]
        jnz     oiv_done

	sti				; let interrupts go while we work

	cmp	wptr [UserRoutine+2],0	; call the user function?
	jz	oiv_done		; no, just exit

	call	dword ptr [UserRoutine]
;
oiv_done:
	dec	cs:[intsemaphore]
;
exit_int:
	pop	ds
	pop	ax
	pop	dx
	iret
;
skip_int:
	pushf
	call	dword ptr ds:[OldIRQRoutine] ; perform the old interrupt
	jmp	short exit_int

OurIntVector    endp
;
endif	; MISC2OBJ
if PCMOBJ
;
;---------------------------====< SetupPCMDMAIO >====--------------------------
;
; SetupPCMDMAIO  --  Setup to output to the DAC
;
	public	SetupPCMDMAIO
SetupPCMDMAIO   proc
	push	es
	push	di
	les	di,[mvhwShadowPointer]
;
; setup the sample rate timer
;
        call    _ASloadtimer0
;
; Setup the Sample Buffer Counter Timer (T1 & rate generator)
;
        mov     ax,[DMABuffLength]      ; get the count
	sub	dx,dx
	add	ax,1			; make it 1 based (1 - 64k)
	adc	dx,dx
	mov	cl,[DMABUffDivides]	; get the buffer size
	sub	ch,ch
	div	cx			; now, we must be flexible

	mov	bl,[TheDMAChannel]	; is this a 16 bit channel?
	mov	bh,[SampleSize] 	; CX = sample size, channel

	sub	cx,cx			; ch = multiplier, cl=divider
	cmp	bx,0003h		; 8 bits on 8 bit channel?
	jbe	@F			; yes, continue on...

	inc	cx			; divide by 2
	cmp	bx,0007h		; 8 bits on 16 bit channel?
	jbe	@F			; yes, continue on...

	xchg	ch,cl			; multiply by 2
	cmp	bx,0203h		; 16 bits on 8 bit channel?
	jbe	@F			; yes, continue on...
	sub	cx,cx			; no multiply or divide
    ;
    @@:
        shr     ax,cl                   ; if 8 on 16 divide by 2
	xchg	ch,cl
	shl	ax,cl			; if 16 on 8 multiply by 2

	sub	cx,cx			; The buffer size is # of bytes, so
	neg	bh			; we must convert it to the data size
	adc	cx,cx
	shr	ax,cl

        call    FFAR ptr _ASloadtimer1
;
; Setup the system interrupt mask (IRQ mask)
;
	mov	dx,IRQ1MASKREG
	cmp	TheIRQChannel,8 	; 2nd IRQ controller?
	jl	@F
	mov	dl,IRQ2MASKREG
    ;
    @@:
	in	al,dx			; get the mask
	mov	ah,TheIRQMask
	not	ah
	and	al,ah			; unmask the correct IRQ
	out	dx,al			; then let the system know
;
; Setup the Interrupt Control Register
;
	mov	dx,INTRCTLRST		; flush any pending interrupts
	xor	dx,[_MVTranslateCode]	; xlate the board address
	out	dx,al			; of the PCM circuitry

        mov     dx,INTRCTLR
        xor     dx,[_MVTranslateCode]           ; xlate the board address
	in	al,dx			; get the real mask
	or	al,bICsampbuff		; interrupt on sample buffer count
	out	dx,al			; send it..
	mov	es:[di._intrctlr],al	; save it..
;
; enable the 12/16 bit stuff
;
	test	[_MVHWVersionBits],bMV101 ; 101 chip?
	jz	sdhpas1_05		  ; no, don't touch this...

	mov	cx,((NOT(bSC216bit+bSC212bit))*256) + bSC216bit+bSC212bit
	cmp	[SampleSize],1		; 12 bit?
	jz	@F
	mov	cx,((NOT(bSC216bit+bSC212bit))*256) + bSC216bit
	cmp	[SampleSize],2		; 16 bit?
	jz	@F
	mov	cx,((NOT(bSC216bit+bSC212bit))*256) + 0
    ;
    @@:
	mov	dx,SYSCONFIG2
	xor	dx,[_MVTranslateCode]	; xlate the board address
	in	al,dx
	and	al,ch			; clear the bits
	or	al,cl			; set the appropriate bits
	out	dx,al
    ;
    sdhpas1_05:
;
; setup the direction, stereo/mono and DMA enable bits
;
	mov	al,bCCmono		; get the stereo/mono mask bit
	and	al,[StereoMono] 	; al = bCCmono if in mono mode
	or	al,[PCMDirection]	; get the direction bit mask
	or	al,bCCenapcm		; enable the PCM state machine
        mov     dx,CROSSCHANNEL
	xor	dx,[_MVTranslateCode]	; xlate the board address
	mov	ah,0fh + bCCdrq 	; get a mask to load non PCM bits
	and	ah,es:[di._crosschannel]; grab all but PCM/DRQ/MONO/DIRECTION
	or	al,ah			; merge the two states
	xor	al,bCCenapcm		; disable the PCM bit
	out	dx,al			; send to the hardware
	xor	al,bCCenapcm		; enable the PCM bit
	out	dx,al			; send to the hardware
	mov	es:[di._crosschannel],al; and save the new state
;
; Setup the audio filter sample bits
;
	mov	al,es:[di._audiofilt]
	or	al,(bFIsrate+bFIsbuff)	; enable the sample count/buff counters
	mov	dx,AUDIOFILT
	xor	dx,[_MVTranslateCode]	; xlate the board address
	out	dx,al
	mov	es:[di._audiofilt],al

        mov     NumberOfInterrupts,0

        enable                          ; Fly, baby Fly!!!

	pop	di
	pop	es
	ret

SetupPCMDMAIO	endp
;
;
;---------------------------====< SetupPCMPolledIO >====-----------------------
;
; SetupPCMPolledIO  --	Setup to read/write to the ADC/DAC in a polled fashion
;
	public	SetupPCMPolledIO
SetupPCMPolledIO        proc
	push	es
	push	di
	les	di,[mvhwShadowPointer]
;
; Setup the Sample Timer (T0 & square wave output)
;
	call	_ASloadTimer0
;
; Setup the Sample Buffer Counter Timer (T1 & rate generator)
;
	mov	ax,1			; get the count
	call	FFAR ptr _ASloadtimer1
;
; Setup the Interrupt Control Register
;
        mov     dx,INTRCTLR
	xor	dx,[_MVTranslateCode]	   ; xlate the board address
	in	al,dx			   ; get the real mask
	or	al,bICsampbuff+bICsamprate ; enable both sample timer ints
	out	dx,al			   ; send it...
	mov	es:[di._intrctlr],al	   ; save it...
;
; enable the 16 bit stuff
;
	test	[_MVHWVersionBits],bMV101 ; 101 chip?
	jz	sphpas1_05		  ; no, don't touch this...

	mov	cx,((NOT(bSC216bit+bSC212bit))*256) + bSC216bit+bSC212bit
	cmp	[SampleSize],1		; 12 bit?
	jz	@F
	mov	cx,((NOT(bSC216bit+bSC212bit))*256) + bSC216bit
	cmp	[SampleSize],2		; 16 bit?
	jz	@F
	mov	cx,((NOT(bSC216bit+bSC212bit))*256) + 0
    ;
    @@:
	mov	dx,SYSCONFIG2
	xor	dx,[_MVTranslateCode]	; xlate the board address
	in	al,dx
	and	al,ch			; clear the bits
	or	al,cl			; set the appropriate bits
	out	dx,al
    ;
    sphpas1_05:
;
; setup the direction, stereo/mono and DMA enable bits
;
	mov	al,bCCmono		; get the stereo/mono mask bit
	and	al,[StereoMono] 	; al = bCCmono if in mono mode
	or	al,[PCMDirection]	; get the direction bit mask
	or	al,bCCenapcm + bCCdrq	; enable the PCM & set DRQ for NOT mask
        mov     dx,CROSSCHANNEL
	xor	dx,[_MVTranslateCode]	; xlate the board address
	mov	ah,0fh			; get a mask to load the non-PCM bits
	and	ah,es:[di._crosschannel]; grab all but PCM/DRQ/MONO/DIRECTION
	or	al,ah			; merge the two states
	xor	al,bCCenapcm + bCCdrq	; disable the PCM & DRQ
	out	dx,al			; send to the hardware
	xor	al,bCCenapcm		; enable the PCM
	out	dx,al
	mov	es:[di._crosschannel],al; and save the new state
;
; Setup the audio filter sample bits to enable sample timer/count
;
	mov	al,es:[di._audiofilt]
	or	al,(bFIsrate+bFIsbuff)	; enable the sample timer/counter
	mov	dx,AUDIOFILT
	xor	dx,[_MVTranslateCode]	; xlate the board address
	out	dx,al
	mov	es:[di._audiofilt],al

        sub     ax,ax
	mov	NumberOfInterrupts,ax

        enable

	pop	di
	pop	es
	ret

SetupPCMPolledIO   endp
;
;
;----------------------------====< StartPlaying >====--------------------------
;
	public	StartPlaying
StartPlaying    proc
;
; Save the type of setup. It contains the interrupt masks needed
;
	mov	[TypeOfSetup],DMAOUTPUT
	mov	[PCMDirection],bCCdac	; bit d6 of interrupt control register
;
; Select the DMA mode for playing
;
	mov	al,10h			; auto init bit on 8237 chip
	and	al,[DMAAutoInit]	; al may have the auto-init bit
	or	al,48h			; merge in the rest of the DMA bits
	mov	TheDMAMode,al		; save the mode
;
; Program the DMA, then our board circuitry to start the interrupts
;
        call    LoadDMA                 ; setup the DMA controller
	call	SetupPCMDMAIO		; Setup the MV Hardware

	mov	StatusWord,1		; set the global status word
 
        ret

StartPlaying	endp
;
;
;---------------------------====< StartRecording >====-------------------------
;
	public	StartRecording
StartRecording	proc
;
; Save the type of setup. It contains the interrupt masks needed
;
	mov	[TypeOfSetup],DMAINPUT
	mov	[PCMDirection],00h	; bit d6 of interrupt control register
;
; Select the DMA mode for recording
;
	mov	al,10h			; auto init bit on 8237 chip
	and	al,[DMAAutoInit]	; al may have the auto-init bit
	or	al,44h			; merge in the rest of the DMA bits
	mov	TheDMAMode,al		; save the mode
;
; Program the DMA, then our board circuitry to start the interrupts
;
        call    LoadDMA                 ; setup the DMA controller
	call	SetupPCMDMAIO		; Setup the MV Hardware

	mov	StatusWord,2		; set the global status word
        ret

StartRecording	endp
;
endif	; PCMOBJ
if MISC2OBJ
;
;---------------------====< _unloadirqvector >====---------------
;
; Restore the original vector
;
; Entry Conditions:
;     dParm1 points to the user routine
;
; Exit Conditions:
;     Nothing
;
;
	public	_unloadirqvector
_unloadirqvector	proc
	push	es

        les     ax,OldIRQRoutine
	mov	bx,es
	or	bx,ax
	jz	@F

	mov	bl,[TheIRQChannel]
	call	_getirqoffset

	push	ds
	sub	cx,cx
	mov	ds,cx

        disable
	mov	ds:[bx+0],ax
	mov	ds:[bx+2],es
        enable

	pop	ds

        sub     ax,ax
	mov	wptr [OldIRQRoutine+0],ax
	mov	wptr [OldIRQRoutine+2],ax
;
@@:
	pop	es
	ret

_unloadirqvector	endp

endif	; MISC2OBJ

	end

