;#>

;$Author:   DCODY  $
;$Date:   23 Sep 1992 10:34:44  $
;$Header:   X:/sccs/midi/midia.asv   1.6   23 Sep 1992 10:34:44   DCODY  $
;$Log:   X:/sccs/midi/midia.asv  $
;  
;     Rev 1.6   23 Sep 1992 10:34:44   DCODY
;  changed MVGetH... to mvGetH...
;  
;     Rev 1.5   04 Sep 1992 16:55:12   DCODY
;  NEAR external choked large model programs
;  
;     Rev 1.4   20 Jul 1992 11:42:44   DCODY
;  call to MVGetHWVersion requests active I/O detection. Found some
;  I/O that was not relocatable, and now performs XOR relocation.
;  
;     Rev 1.3   17 Jul 1992 13:57:30   DCODY
;  make I/O addresses relocatable.
;  
;     Rev 1.2   27 Jun 1992 15:44:56   DCODY
;  removed debug output code.
;  
;     Rev 1.1   25 Jun 1992 21:48:46   DCODY
;  PAS2 update
;  
;     Rev 1.0   15 Jun 1992 10:42:58   BCRANE
;  Initial revision.
;$Logfile:   X:/sccs/midi/midia.asv  $
;$Modtimes$
;$Revision:   1.6  $
;$Workfile:   midia.asm  $ 

;#<

        Title   MIDIA.ASM  --  Media Vision 3802 Midi Hardware Dependent Module
	Subttl	Copyright (c) 1991,1992. Media Vision Inc. All Rights Reserved.
	page	64,131

;   /*\
;---|*|-----------------------====< MIDIA.ASM >====------------------------
;---|*|
;---|*| Low Level MIDI I/O routines for the Pro Audio Spectrum cards.
;---|*|
;   \*/

	.xlist
	include model.inc
	include masm.inc
	include mvmidi.inc
	include common.inc
	include target.inc
        .list

;
;   /*\
;---|*|----====< int mvMIDIEnable >====----
;---|*|
;---|*| mvMIDIEnable - Initialize the MIDI interface
;---|*|
;---|*|   Entry Conditions:
;---|*|      wParm1 is a bit field for process control
;---|*| 	 D0 = 1 to enable input interrupts
;---|*| 	 D1 = 1 to enable output interrupts
;---|*|
;---|*|   Exit Conditions:
;---|*|      AX = -1 if cannot init the MIDI hardware.
;---|*|
;---|*|----====< void mvMIDIDisable >====----
;---|*|
;---|*| mxdDisable - MIDI shutdown
;---|*|
;---|*| Entry Conditions
;---|*|     None
;---|*|
;---|*| Exit Conditions
;---|*|     There is no return value.
;---|*|
;---|*|----====< int mvMIDIGetBuff >====----
;---|*|
;---|*| mvMIDIGetBuff - get data byte from MIDI channel
;---|*|
;---|*| Entry Conditions
;---|*|     wParm1 holds the count to move
;---|*|     dParm2 points to the target buffer
;---|*|
;---|*| Exit Conditions
;---|*|     AX = 0, no data, # of bytes moved
;---|*|
;---|*|----====< int mvMIDIGetByte >====----
;---|*|
;---|*| mvMIDIGetByte - get/check one byte from MIDI channel buffer
;---|*|
;---|*| Entry Conditions
;---|*|     None
;---|*|
;---|*| Exit Conditions
;---|*|     AX = byte, or -1 if no data
;---|*|
;---|*|----====< void mvMIDISendBuff >====----
;---|*|
;---|*| mvMIDISendBuff - send a buffer of data
;---|*|
;---|*| Entry Conditions
;---|*|     wParm1 = integer of count to load
;---|*|     dParm1 = far pointer to the buffer
;---|*|
;---|*| Exit Conditions
;---|*|     There is no return value.
;---|*|
;---|*|----====< void mvMIDISendByte >====----
;---|*|
;---|*| mvMIDISendByte - send data byte out MIDI channel
;---|*|
;---|*| Entry Conditions
;---|*|     wParm1  datum
;---|*|
;---|*| Exit Conditions
;---|*|     There is no return value.
;---|*|
;   \*/
;
	.data
;
;   /*\
;---|*|------------====< Global Data Definitions >====------------
;   \*/
;
	extrn	_MVTranslateCode:word
	extrn	_MVHWVersionBits:word
;
; The overflow count will always be here, but we won't make it public
; except for debugging purposes
;

ifdef DEBUG
        public  _overflow
endif
_overflow       dw      0               ; overflow count

;
; MidiInFilter allows certain types of MIDI messages to be tossed out...
;
;   NOTE: this currently works for active sense ONLY.
;
	public	_MidiInFilter
_MidiInFilter	dw	MF_ACTSENSE	; default ignores active sense

;
;   /*\
;---|*|------------====< Private Data Definitions >====------------
;   \*/
;

;
; This midi code uses interrupt driven I/O, so a circular buffer is used
; to support the data flow.
;

MAXQUEUE	equ	128

;
; MIDI input circular buffer
;
iCircularQueue	db	MAXQUEUE	dup (0)

MIDIiQueueCnt	dw	0

lpiMIQueue	dw	iCircularQueue
lpiMOQueue	dw	iCircularQueue

;
; MIDI output circular buffer
;
oCircularQueue	db	MAXQUEUE	dup (0)

MIDIoQueueCnt	dw	0

lpoMIQueue	dw	oCircularQueue
lpoMOQueue	dw	oCircularQueue

;
; Interrupt channel number and mask
;

_IRQNumb        db      -1
_IRQMask	db	0

;
; Interrupt Service Routine re-entrancy sema-phore
;
ISRbusy         db      -1              ; ISR semaphore

;
; Internal process control to handle polled or interrupt driven data I/O
;
ProcessControl  db      0               ; Interrupt or polled I/O
INTINPUT	equ	00000001b
INTOUTPUT	equ	00000010b	; (currently not implemented)


;
;   /*\
;---|*|------------====< Yamaha 3802 midi interface variables >====------------
;   \*/
;

MDctrl		db	0	; MDSYSCTLR shadow for write only hardware
ISRenab 	db	0	; R06 TX and RX int enable bits

; r02/r03/r06 bit definitions
TxIRQ	equ	01000000b	; Tx FIFO interrupt
RxIRQ	equ	00100000b	; Rx FIFO interrupt

; r34 bit definitions
RxRDY	equ	10000000b	; Rx FIFO non-empty

; r54 bit definitions
TxEMP	equ	10000000b	; Tx FIFO empty
TxRDY	equ	01000000b	; Tx FIFO non-full

TIMER1	equ	2173

;
;------------------------================================----------------------
;------------------------====< MIDI Common Routines >====----------------------
;------------------------================================----------------------
;
        .code
	assume	ds:@data,es:nothing

    externADDR	mvGetHWVersion		; determine the installed hardware type
;
; We will save the old IRQ vector here to guarrantee access at all times.
;

OldISR          dd      0               ; holds the original IRQ vector

;
;    Toggle the MIDI interrupt to stimulate latent IRQs
;
ToggleMV101mask macro

        mov     dx,INTRCTLR
	xor	dx,[_MVTranslateCode]

	in	al,dx			
	xor	al,bICmidi	; disable MIDI interrupts
	out	dx,al
	pause
	xor	al,bICmidi	; enable MIDI interrupts
	out	dx,al

	endm

;
;   /*\
;---|*|----====< int mvMIDIEnable >====----
;---|*|
;---|*| Initialize the MIDI interface
;---|*|
;---|*| Entry Conditions:
;---|*|       wParm1 is a bit field for process control:
;---|*| 	 D0 = 1 to enable input interrupts.
;---|*| 	 D1 = 1 to enable output interrupts.
;---|*|
;---|*| Exit Conditions:
;---|*|     AX = -1 if cannot init the MIDI hardware.
;---|*|
;   \*/
;
        public  mvMIDIEnable
mvMIDIEnable   proc
	push	bp			; frame the stack for our 1 parameter
	mov	bp,sp
;
; reset the MIDI buffering system
;
	mov	[lpiMIQueue],OFFSET iCircularQueue
	mov	[lpiMOQueue],OFFSET iCircularQueue
	mov	[lpoMIQueue],OFFSET oCircularQueue
	mov	[lpoMOQueue],OFFSET oCircularQueue
	mov	[MIDIoQueueCnt],0
	mov	[MIDIiQueueCnt],0
	mov	[_overflow],0
	mov	[_MidiInFilter],MF_ACTSENSE
;
; If the hardware bits have not been determined, do it...
;
	cmp	_MVHWVersionBits,-1	; version bits = -1 if the function
	jnz	@F			; hasn't been called yet.
	mov	ax,USE_ACTIVE_ADDR	; do the call
	push	ax			; to setup the hardware bits, etc.
	call	mvGetHWVersion
	pop	ax
    ;
    @@:
;
; determine the IRQ for the Pro Audio card
;
	call	whereirq		; looks for MVSOUND.SYS
;
; convert each bit to a full mask. Send this mask to each routine.
;
	mov	ax,wParm1		; load the bit flags
	mov	[ProcessControl],al	; save as our process control
	ror	ax,1			; ah holds input bit, al holds output
	neg	ah
	sbb	ah,ah			; ah = FF to enable ints, else 0
	neg	al
	sbb	al,al			; al = FF to enable ints, else 0
;
; perform the proper init
;
	test	[_MVHWVersionBits],bMV101; MV101 version of MIDI?
	jz	@F			 ; no, go do yamaha
;
; initialize the MV101 MIDI device
;
        call    enable_mv101
	pop	bp
        ret
;
@@:
;
; initialize the Yamaha 3802
;
        call    enable_yamaha
	pop	bp
        ret

mvMIDIEnable   endp

;
;   /*\
;---|*|----====< void mvMIDIDisable >====----
;---|*|
;---|*| MIDI device shutdown.
;---|*|
;---|*| Entry Conditions:
;---|*|     None.
;---|*|
;---|*| Exit Conditions:
;---|*|     There is no return value.
;---|*|
;   \*/
;
        public  mvMIDIDisable
mvMIDIDisable  proc
;
; remove ourselves from the chain
;
        call    unhook_interrupt
;
; perform the proper init
;
	test	[_MVHWVersionBits],bMV101; MV101 version of MIDI?
	jz	@F			 ; no, go do yamaha

	call	disable_MV101		; disable the MV101 MIDI
        ret
    ;
    @@:
	call	disable_yamaha		; disable the Yamaha MIDI
;
midblexit:
        ret

mvMIDIDisable  endp

;
;   /*\
;---|*|----====< int mvMIDIGetBuff >====----
;---|*|
;---|*| Get data byte from MIDI channel.
;---|*|
;---|*| Entry Conditions
;---|*|     wParm1 holds the count to move.
;---|*|     dParm2 points to the target buffer.
;---|*|
;---|*| Exit Conditions
;---|*|     AX = 0, no data, # of bytes moved.
;---|*|
;   \*/
;
        public  mvMIDIGetBuff
mvMIDIGetBuff  proc
	push	bp
	mov	bp,sp
;
; save the C criticals
;
        push    es
	push	si
	push	di
;
; load the parameters
;
        mov     cx,wParm1               ; get the number of bytes to move
	les	di,wParm2		; get the target pointer
	mov	si,[lpiMOqueue] 	; get the source pointer
	sub	dx,dx			; dx holds the # of bytes moved
	cld
;
; using interrupt driven input?
;
        test    [ProcessControl],INTINPUT
	jz	mvigb_20		; polled, check each h/w
;
mvigb_05:
;
; read as many bytes out of the queues until one is empty, or full.
;
	cmp	[MIDIiQueueCnt],0	; all done?
	jz	mvigb_10		; yes, return # of bytes moved
	movsb
	dec	[MIDIiQueueCnt] 	; one less byte here...
	inc	dx
	cmp	si,offset iCircularQueue+MAXQUEUE
	jl	@F
	mov	si,offset iCircularQueue
    ;
    @@:
	loop	mvigb_05		; get it all...
;
mvigb_10:
	mov	[lpiMOQueue],si 	; save the pointer
	mov	ax,dx
;
mvigb_ret:
	pop	di
	pop	si
	pop	es
	pop	bp
        ret
;
mvigb_20:
;
; This is a little slow, but avoids messy code duplication
;
	push	si			; save si
	mov	si,cx			; si will be our loop counter
	mov	bp,cx			; bp will hold the total count
;
mvigb_25:
	call	FFAR ptr mvMIDIGetByte	; get the next byte
	cmp	ax,-1			; any data?
	jz	mvigb_30		; nope, we're done...
	stosb				; save in the callers buffer
	dec	si			; all done?
	jnz	mvigb_25		; nope, continue looping
;
mvigb_30:
        sub     bp,si                   ; bp holds the total read count
	mov	ax,bp
	pop	si
	jmp	short mvigb_ret

mvMIDIGetBuff  endp

;
;   /*\
;---|*|----====< int mvMIDIGetByte >====----
;---|*|
;---|*| Get one byte from MIDI channel buffer.
;---|*|
;---|*| Entry Conditions
;---|*|     None.
;---|*|
;---|*| Exit Conditions
;---|*|     AX = -1 if no data.
;---|*|     AH =  0 if data in AL.
;---|*|
;   \*/
;
        public  mvMIDIGetByte
mvMIDIGetByte  proc
;
; If the code is running as interrupt input, then we can check the queue
;
        test    [ProcessControl],INTINPUT
	jz	mvgbt_20		; polled, check each h/w

	cmp	[MIDIiQueueCnt],0	; any data?
	jz	mvgbt_none		; no, just return now

	mov	bx,[lpiMOqueue] 	; get the source pointer
	sub	ah,ah			; clear out the top
	mov	al,[bx] 		; load the bottom

	dec	[MIDIiQueueCnt] 	; one less byte here...
	inc	bx
	cmp	bx,offset iCircularQueue+MAXQUEUE
	jl	mvgbt_05
	mov	bx,offset iCircularQueue
;
mvgbt_05:
	mov	[lpiMOqueue],bx 	; save the new pointer
        ret
;
mvgbt_none:
	mov	ax,-1			; bad, so exit with error flag
	ret
;
mvgbt_20:
;
; This is the polled mode of MIDI input - go check the correct chip FIFO
;
        test    [_MVHWVersionBits],bMV101; MV101 version of MIDI?
	jz	mvgbt_yamaha		 ; no, go do yamaha
;
; read the MV101 MIDI fifo
;
	mov	dx,MIDISTATUS		; check data available
	xor	dx,[_MVTranslateCode]	; translated I/O addr

	in	al,dx			; isolate the data available bit
	and	ax,bMSRififo
	jz	mvgbt_none		; no data available, go report it...

	mov	dx,MIDIDATA
        xor     dx,[_MVTranslateCode]   ; translated I/O addr

	in	al,dx			; return the byte in al, ah = 0

	ret
;
mvgbt_yamaha:
;
; read the Yamaha 3802 MIDI fifo.
;
        mov     al, 3                   ; 3x
	mov	dx, MDSYSCTLR		; set the 3802 global reg set index
	mov	[MDctrl],al		; shadow...
	out	dx, al

        mov     dx, MDGROUP4            ; 34 = FIFO-Rx status
	in	al, dx
	and	ax, RxRDY		; RxRDY bit?
        jz      mvgbt_none              ; no data available, go report it...

	mov	dx, MDGROUP6		; 36 = FIFO-Rx data

	in	al, dx			; return the byte in al, ah = 0

	ret

mvMIDIGetByte  endp

;
;   /*\
;---|*|----====< void mvMIDISendByte >====----
;---|*|
;---|*| mvMIDISendByte - send a MIDI data byte out the MIDI channel.
;---|*|
;---|*| Entry Conditions
;---|*|     wParm1 - MIDI byte to be sent.
;---|*|
;---|*| Exit Conditions
;---|*|     There is no return value.
;   \*/
;
	public	mvMIDISendByte
mvMIDISendByte	proc
;
; send the byte out directly if polled output, else queue it up to keep order
;
        push    bp
	mov	bp,sp

	mov	al,wParm1		; grab the byte

	test	[ProcessControl],INTOUTPUT ; interrupt output?
	jnz	mvsnd_ints		   ; yes, we will queue it...

        call    dosendbyte              ; send it out straight

        pop     bp
	ret
;
mvsnd_ints:
;
; Interrupt driven output requires the byte be queue to keep the byte order.
;
        mov     bx,[lpoMIQueue]         ; get the input-to-buffer pointer
;
@@:
	cmp	[MIDIoQueueCnt],MAXQUEUE; is the internal queue full?
	jnz	@F			; no, go load another byte
	call	primexmit		; make sure the xmitter is working
	jmp	short @B		; go wait for data to move
;
@@:
	mov	[bx],al
	inc	bx
	cmp	bx,offset oCircularQueue+MAXQUEUE ; wrapped?
	jnz	@F
	mov	bx,offset oCircularQueue	  ; yes, go back to the beginning
;
@@:
        mov     [lpoMIQueue],bx         ; save the new input-to-buffer pointer
	inc	[MIDIoQueueCnt] 	; add one more into the FIFO

        call    primexmit               ; make sure the xmitter is working
	pop	bp
	ret

mvMIDISendByte endp

;
;   /*\
;---|*|----====< void mvMIDISendBuff >====----
;---|*|
;---|*| mvMIDISendBuff - send a buffer of data.
;---|*|
;---|*| Entry Conditions
;---|*|     wParm1 = integer of count to load.
;---|*|     dParm1 = far pointer to the buffer.
;---|*|
;---|*| Exit Conditions
;---|*|     Returns when all the bytes are loaded in the circular queue.
;   \*/
;
	public	mvMIDISendBuff
mvMIDISendBuff	proc
        push    bp
	mov	bp,sp			; 'C' frame

	push	es			; save the 'C' criticals
	push	si
;
; we will load the entire block into the FIFO. This even means waiting for the
; transmitter to empty enough data until we're done.
;
	mov	cx,wParm1		; get the buffer counter
	les	si,wParm2		; get the far pointer
	mov	bx,[lpoMIQueue] 	; get the input-to-buffer pointer
;
queueloop:
	cmp	[MIDIoQueueCnt],MAXQUEUE; is the internal queue full?
	jnz	@F			; no, go load another byte
	call	primexmit		; make sure the xmitter is working
	jmp	short queueloop
;
@@:
	lods	byte ptr es:[si]	; get the next MIDI byte
	mov	[bx],al
	inc	bx
	cmp	bx,offset oCircularQueue+MAXQUEUE ; wrapped?
	jnz	@F
	mov	bx,offset oCircularQueue	  ; yes, go back to the beginning
;
@@:
	inc	[MIDIoQueueCnt] 	; add one more into the FIFO
	loop	queueloop		; load all the bytes into the queue
	mov	[lpoMIQueue],bx 	; save the new input-to-buffer pointer
;
; We've loaded the FIFO. if polling, call the output routine till done.
;
	test	[ProcessControl],INTOUTPUT ; interrupt driven output?
	jnz	sendbuffints		   ; yes, force the xmitter to work
;
; this byte goes out the MV101
;
    @@:
	call	primexmit		; make sure the xmitter is working
	cmp	[MIDIoQueueCnt],0	; any more queued up data?
	jnz	@B			; yes, stay here till sent...

	jmp	short sendbuffexit	; all done, return home
;
sendbuffints:
;
; make sure the xmitter is primed to generate interrupts
;
	call	primexmit
;
sendbuffexit:
	pop	si
	pop	es
	pop	bp
	ret

mvMIDISendBuff endp

;
;---------------------------==============================---------------------
;---------------------------====< Local Subroutines > ====---------------------
;---------------------------==============================---------------------
;

;
;   /*\
;---|*|----====< disable_MV101 >====----
;---|*|
;---|*| This routine will disable any MV101 MIDI
;---|*| interrupts and reset the tranceiver.
;---|*|
;---|*| Entry Conditions:
;---|*|     None.
;---|*|
;---|*| Exit Conditions:
;---|*|     AX, DX modified
;---|*|
;   \*/
;
disable_MV101	proc	near

	mov	dx,MIDICONTROL		; get the control address
	xor	dx,[_MVTranslateCode]	; translate the address

        sub     al,al                   ; flush all enables
	out	dx,al			; kill fifo irq

        ret

disable_MV101   endp

;
;   /*\
;---|*|----====< disable_yamaha >====----
;---|*|
;---|*| This routine will disable any Yamaha MIDI
;---|*| interrupts and reset the tranceiver.
;---|*|
;---|*| Entry Conditions:
;---|*|     None.
;---|*|
;---|*| Exit Conditions:
;---|*|     AX, DX modified
;---|*|
;   \*/
;
disable_yamaha  proc    near
;
; disable interrupts in the yamaha 3802
;
	sub	ax, ax
	mov	[MDctrl], al
	mov	dx, MDSYSCTLR
	out	dx, al			; 0x

        mov     dx, MDGROUP6            ; 06 = IRQ enable request
	out	dx, al

        .errnz  MDGROUP6-MDGROUP5-1

	dec	dx			; 05 = IRQ mode control
	out	dx, al

        ret

disable_yamaha	endp

;
;   /*\
;---|*|----====< dosendbyte >====----
;---|*|
;---|*| A generic routine to send one byte out to the hardware.
;---|*|
;---|*| Entry Conditions;
;---|*|     AL holds the byte to be sent.
;---|*|
;---|*| Exit Conditions:
;---|*|     AX,BX,CX,DX modified.
;---|*|     Byte sent, no return value.
;---|*|
;   \*/
;
dosendbyte      proc    near
;
; save the byte until we need it...
;
        mov     ah,al                   ; ah holds the byte to send
;
; select the proper chip.
;
        test    [_MVHWVersionBits],bMV101; MV101 version of MIDI?
	jz	send_yamaha		 ; no, go do yamaha
;
; this byte goes out the MV101
;
	mov	dx, MIDIFIFOS		; get the transmitter status
        xor     dx,[_MVTranslateCode]   ; translated I/O addr
;
; we will only load up to 15 bytes in the output FIFO since a zero count
; means either 16 bytes free or 0 bytes free. By only loading 15 bytes max,
; we assume a zero means 16 bytes free.
;
	in	al,dx			; get the FIFO output free room count
	and	al,bMFCofifo		; isolate the count
	jz	mvsendit		; 16 bytes free
	sub	cx,cx			; we will do a timeout
    ;
    @@:
	in	al, dx			; get the FIFO counts
	and	al, bMFCofifo		; any room in the output FIFO?
	cmp	al, 10h 		; one byte left in the FIFO?
	loopz	@b			; wait till there is more room
	jcxz	deadtx			; exit on a timeout
;
mvsendit:
	mov	dx, MIDIDATA		; get the data output port
        xor     dx,[_MVTranslateCode]   ; translated I/O addr

	mov	al, ah
	out	dx, al			; ship stuff...
;
deadtx:
        ret
;
send_yamaha:
;
; this byte goes out the yamaha 3802
;
        mov     al, 5                   ; 5x
	mov	[MDctrl], al		; shadow it...
	mov	dx, MDSYSCTLR		; get the port
	out	dx, al			; let'er rip...

	mov	dx, MDGROUP4		; 54 = FIFO-Tx status
;
@@:     in      al, dx
	test	al, TxRDY		; TxRDY bit?
	jz	@b			; ..jump if FIFO full

	mov	dl, LOW MDGROUP6	; 56 = FIFO-Tx data
	mov	al, ah
	out	dx, al			; ship datum

	ret

dosendbyte	endp

;
;   /*\
;---|*|----====< enable_mv101 >====----
;---|*|
;---|*| Enable the MV101 tranceiver, and any appropriate interrupts
;---|*|
;---|*| Entry Conditions:
;---|*|     AH = FF to enable input interrupts
;---|*|     AL = FF to enable output interrupts
;---|*|
;---|*| Exit Conditions:
;---|*|     AX,BX,CX,DX modified.
;---|*|
;   \*/
;
enable_mv101    proc    near
;
; build the correct mask bits in ah
;
	and	ax,(bMCRenafifoi SHL 8) + bMCRenafifoo
	or	ah,al			; ah holds the enable bits
;
; do something to the test register
;
;;;	mov	dx,MIDITEST
;;;	xor	dx,[_MVTranslateCode]	; translated I/O addr
;;;	mov	al,0FFh
;;;	out	dx,al
;
; setup the MIDI prescale
;
        mov     dx,MIDIPRESCALE
        xor     dx,[_MVTranslateCode]   ; translated I/O addr
	mov	al,10h			;
	out	dx,al
;
; reset the fifos
;
	mov	dx,MIDICONTROL
	xor	dx,[_MVTranslateCode]	; translated I/O addr
	mov	al,bMCRrstfifoi+bMCRrstfifoo
	out	dx,al			; send the reset bits
	mov	al,ah
	out	dx,al			; clear reset bits and set enables
;
; reset any present status bits
;
        mov     dx,MIDISTATUS
	xor	dx,[_MVTranslateCode]	; translated I/O addr
	mov	al,-1			; reset all midi status bits
	out	dx,al
;
; only enable the stuff if we're using interrupts
;
	test	[ProcessControl],INTINPUT+INTOUTPUT
	jz	enamv_done		; no interrupts, just exit now...
;
; install the interrupt handling now
;
	cli

	lea	ax,MV101_ISR
	call	hook_interrupt		; insert our routine

	sti
;
enamv_done:
        ret

enable_mv101    endp

;
;   /*\
;---|*|----====< enable_yamaha >====----
;---|*|
;---|*| Enable the Yamaha 3802 tranceiver, and any appropriate interrupts.
;---|*|
;---|*| Enable the 3802 Yamaha MIDI device
;---|*|     AH = FF to enable input interrupts.
;---|*|     AL = FF to enable output interrupts.
;---|*|
;---|*| Exit Conditions:
;---|*|     AX,BX,CX,DX modified.
;---|*|
;   \*/
;
enable_yamaha	proc	  near
;
; build the correct mask bits in ah
;
	and	ax,(RxIRQ SHL 8)+ TxIRQ
	or	al,ah			; al holds the enable bits
	push	ax			; save the interrupt enable bits
;
; reset the 3802 midi chip
;
	mov	al, 80h 		; master clear
	mov	[MDctrl], al
	mov	dx, MDSYSCTLR
	out	dx, al

	mov	cx, 100
@@:	in	al, dx			; wait 32 TCLK
	loop	@b

	xchg	ax,cx			; load zero
	out	dx,al			; flush the reset bit

	mov	al, 6
	mov	[MDctrl], al
	out	dx, al			; 6x
	mov	dx, MDGROUP6		; 66 = Click counter control
	mov	al, 00000010b		; Clock=1.0MHz, no click pulse
	out	dx, al

	mov	al, 4
	mov	[MDctrl], al
	mov	dx, MDSYSCTLR
	out	dx, al			; 4x
	mov	dx, MDGROUP4		; 44 = Tx communication rate
	mov	al, 00001000b		; CLKM/32 = 31,250 bps
	out	dx, al

	mov	al, 2
	mov	[MDctrl], al
	mov	dx, MDSYSCTLR
	out	dx, al			; 2x
	mov	dx, MDGROUP4		; 24 = Rx communication rate
	mov	al, 00001000b		; CLKM/32 = 31,250 bps
	out	dx, al

	mov	al, 5
	mov	[MDctrl], al
	mov	dx, MDSYSCTLR
	out	dx, al			; 5x
	mov	dx, MDGROUP5		; 55 = FIFO-Tx control
	mov	al, 10000101b		; TxE
	out	dx, al

	mov	al, 8
	mov	[MDctrl], al
	mov	dx, MDSYSCTLR
	out	dx, al			; 8x
	mov	dx, MDGROUP4		; 84 = General Timer LSB
	mov	ax, TIMER1
	out	dx, al
	mov	dx, MDGROUP5		; 85 = General Timer MSB
	xchg	ah, al
	out	dx, al
;
; clear the FIFO-Rx
;
	mov	al, 3			; 35 = RCR: FIFO-Rx control
	mov	[MDctrl], al		; bit 7 = clear FIFO-Rx
	mov	dx, MDSYSCTLR		; bit 6 = clear RxOV flag
	out	dx, al			; bit 4 = enable MIDI-clock filter
	mov	dx, MDGROUP5		; bit 3 = clear BRK flag
	mov	al, 11001101b		; bit 2 = clear RxOL flag
	out	dx, al			; bit 1 = enable Address-hunter
;
; only enable the stuff if we're using interrupts
;
	test	[ProcessControl],INTINPUT+INTOUTPUT
	jz	enayam_already		; no interrupts, just exit now...
;					; bit 0 = enable Receiver
; initialize the 3802 chip IRQ system
;
        sub     al, al
	mov	[MDctrl], al
	mov	dx, MDSYSCTLR
	out	dx, al			; 0x
	mov	dx, MDGROUP5		; 05 = IRQ mode control
	mov	al, 0011b		; VE+VM
	out	dx, al
;
; initialize the board and motherboard IRQ systems
;
        cli

        .errnz  MDGROUP6-MDGROUP5-1
        inc     dx

	pop	ax			; get the interrupt enable bits
        out     dx, al
	mov	[ISRenab], al

        mov     dx, MDGROUP4            ; flush any pending IRQs
	mov	al, 0ffh
	out	dx,al
;
; hook the actual vector
;
	lea	ax,Yamaha_ISR
        call    hook_interrupt

        sti
;
enaya_done:
        ret
;
enayam_already:
	pop	ax
	ret

enable_yamaha	endp

;
;   /*\
;---|*|----====< hook_interrupt >====----
;---|*|
;---|*| Hook the Pro Audio hardware interrupt,
;---|*| and enable the system interrupt.
;---|*|
;---|*| Entry Conditions:
;---|*|     None.
;---|*|
;---|*| Exit Conditions:
;---|*|     AX,BX,CX,DX modified.
;---|*|
;   \*/
;
hook_interrupt  proc    near
        push    ds
	push	es

	cmp	wptr cs:[OldISR+2],0	; any segment?
	jnz	midihooked		; yes, we've hooked already

	push	ax			; save the IRQ offset

	mov	ax, 3508h		; DOS : get vector
	cmp	[_IRQNumb],7		; 1st IRQ channel?
	jbe	@F
	mov	al,70h-8		; 2nd IRQs start at 70H
    ;
    @@:
	add	al, [_IRQNumb]		; IRQ
	push	ax			; save the IRQ #

	int	21h			; fetch the vector

	mov	wptr cs:[OldISR+0], bx	; save original value
	mov	wptr cs:[OldISR+2], es

	pop	ax
        pop     dx                      ; get the actual offset

        push    ds                      ; save ds over the int call
	push	cs
	pop	ds			; ds:dx point to the vector
	mov	ah, 25h 		; DOS : set vector
	int	21h
	pop	ds
;
; enable the Pro Audio IRQ channel
;
	pushf
	cli
;
; address the correct IRQ controller
;
	mov	dx,IRQ1MASKREG
	cmp	[_IRQNumb],7		; 1st IRQ controller?
	jbe	@F			; yes, continue on...
	mov	dx,IRQ2MASKREG		; no, use the 2nd interrupt
    ;
    @@:
;
; Enable the correct IRQ channel
;
        in      al, dx                  ; enable the system IRQ
	mov	ah, [_IRQMask]
	not	ah
	and	al, ah
	out	dx, al
;
; Enable the Pro Audio MIDI interrupt
;
	mov	dx, INTRCTLR		; Enable the PAS. This could signal
	xor	dx, [_MVTranslateCode]
	in	al, dx			; an interrupt immediately.
	or	al, bICmidi		; MIDI interrupt enable
	out	dx, al

	popf
    ;
    midihooked:

        pop     es
	pop	ds
	ret

hook_interrupt  endp

;
;   /*\
;---|*|----====< void primexmit >====----
;---|*|
;---|*| Prime the transmitter. If interrupt driven output, make sure the
;---|*| outbound FIFO has at least one byte loaded. If polled, send
;---|*| the next polled byte.
;---|*|
;---|*| Entry Conditions:
;---|*|    Working registers assumed to hold data.
;---|*|
;---|*| Exit Conditions:
;---|*|    No registers modified.
;---|*|
;   \*/
;
primexmit	proc	near
;
; save everything for the caller
;
	pushf				; save the flags so we can CLI
	push	ax			; don't mess with any other registers
	push	dx
        push    bx
	push	cx
;
; Interrupt driven output is a special case. We just have to prime the pump...
;
	test	[ProcessControl],INTOUTPUT ; interrupt driven output?
	jnz	prixmints		   ; yes, force the xmitter to work
;
prixprimeit:
;
; This is polled output mode. We just send one byte out. The caller will keep
; calling us till the queue is emptied.
;
	cmp	[MIDIoQueueCnt],0	; any data to send?
	jz	prixdone		; no, it's all loaded and gone..

        mov     bx,[lpoMOQueue]         ; get the out-of-buffer pointer
	mov	al,[bx]
	inc	bx

	cmp	bx,offset oCircularQueue+MAXQUEUE ; wrapped?
	jnz	@F
	mov	bx,offset oCircularQueue	  ; yes, wrap it...
;
@@:
	mov	[lpoMOQueue],bx 	; save the new input-to-buffer pointer
	dec	[MIDIoQueueCnt] 	; add one more into the FIFO

	call	dosendbyte		; send it via the polled routines
;
prixdone:
;
; return with nothing changed
;
        pop     cx
	pop	bx
	pop	dx
	pop	ax
	popf
	ret
;
prixmints:
;
; in case of some MIDI interrupts occuring when we don't expect it, (such
; as MIDI in interrupts) this section will be executed without interrupts
; to avoid double sending, or sending data from the queue out of order.
;
	cli				; no ints while we go to prime the pump
;
; Make sure the transmitter FIFO has data loaded. This guarrantees an interrupt
; when the FIFO is empty.
;
	test	[_MVHWVersionBits],bMV101; MV101 version of MIDI?
	jz	prixyamaha		 ; no, go do yamaha

	mov	dx, MIDIFIFOS		; get the transmitter FIFO status
        xor     dx,[_MVTranslateCode]   ; translated I/O addr

        in      al,dx                   ; get the FIFO output free room count
	and	al,bMFCofifo		; isolate the outbound count
	jnz	prixdone		; there is data, no need to prime it.
	jmp	short prixprimeit	; 16 bytes free - load one more...
;
prixyamaha:
;
; Yamaha 3802 - we go to TX register set 05 to find the TX empty bit
;
	mov	al, 5			; reg set 5x is for the xmitter
	mov	[MDctrl], al
	mov	dx, MDSYSCTLR
	out	dx, al			; 5x

        mov     dx, MDGROUP4            ; 84 = General Timer LSB
	in	al,dx
	test	al,TxEMP		; is the transmitter empty?
	jnz	prixprimeit		; yes, go load another byte
	jmp	short prixdone		; no, leave it alone

primexmit	endp

;
;   /*\
;---|*|----====< unhook_interrupt >====----
;---|*|
;---|*| If installed, disable the IRQ system, then unhook
;---|*| the Pro Audio IRQ handler from the chain.
;---|*|
;---|*| Entry Conditions:
;---|*|    None.
;---|*|
;---|*| Exit Conditions:
;---|*|    AX,BX,CX,DX may be modified.
;---|*|
;   \*/
;
unhook_interrupt        proc    near
;
; don't do this if there is no need
;
	cmp	wptr cs:[OldISR+2],0	; interrupt already unhooked?
	jz	unhooked		; yes, just exit
;
; disable system interrupt for the Pro Audio card
;
	pushf
        cli

	test	al,fICintmaskbits	; any other interrupts enabled
	jnz	mskint_done		; yes, leave the system mask alone

	mov	dx,IRQ1MASKREG
	cmp	[_IRQNumb],7		; 1st IRQ controller?
	jbe	@F			; yes, continue on...
	mov	dx,IRQ2MASKREG		; no, use the 2nd interrupt
    ;
    @@:
	cmp	[_IRQNumb],2		; hardware IRQ 2?
	jz	mskint_done		; yes, never mask it

	in	al,dx
	or	al,[_IRQMask]		; no, kill the system interrupts
	out	dx,al
;
mskint_done:
	mov	dx, INTRCTLR		; disable the Pro Audio MIDI IRQ
	xor	dx, [_MVTranslateCode]
	in	al, dx
	and	al, not bICmidi 	; MIDI interrupt disable
	out	dx, al

	popf
;
; calculate the correct vector and restore it.
;
	mov	ax, 2508h		; DOS : set vector
	cmp	[_IRQNumb],7		; 1st IRQ channel?
	jbe	@F
	mov	al,70h-8		; 2nd IRQs start at 70H
    ;
    @@:
	add	al, [_IRQNumb]		; IRQ
;
; do it through DOS...
;
        push    ds
	lds	dx, cs:[OldISR]
	int	21h
	pop	ds

	mov	wptr cs:[OldISR+2],0	; flush the segment of the vector
;
unhooked:
        ret

unhook_interrupt	endp

;
;   /*\
;---|*|----====< whereirq >====----
;---|*|
;---|*| This routine queries MVSOUND.SYS for the Pro Audio IRQ. Once
;---|*| determined, then an appropriate interrupt mask will be
;---|*| generated, and saved for future reference.
;---|*|
;---|*| Entry Conditions:
;---|*|    None.
;---|*|
;---|*| Exit Conditions:
;---|*|    AX,BX,CX,DX may be modified.
;---|*|
;   \*/
;
whereirq        proc    near
;
; Do this just once
;
	cmp	[_IRQNumb],-1		; have we been through here?
	jnz	IRQdone 		; yes, it's been found
;
; Call MVSOUND.SYS to get the DMA/IRQ
;
	mov	ax,0bc04h		; call MVSOUND.SYS for it...
	int	2fh
;
; register the IRQ if the call was good
;
	cmp	ax,'MV'                 ; was this a good call?
	jnz	IRQdone

	mov	[_IRQNumb],cl		; save the #
	mov	ax,1
	shl	ax,cl
	or	al,ah
	mov	[_IRQMask],al		; save the mask.
;
IRQdone:
	ret

whereirq        endp

;
;------------------------===================================-------------------
;------------------------====< MIDI Interrupt Service > ====-------------------
;------------------------===================================-------------------
;

;
;   /*\
;---|*|----====< MV101_ISR >====----
;---|*|
;---|*|  ISR service routine for the MV101 MIDI interrupts.
;---|*|
;   \*/
;
MV101_ISR	proc	far
	push	ds
	push	ax
	push	bx
	push	cx
	push	dx

	mov	ax,@data
	mov	ds,ax
;
; first time in, check for a MIDI interrupt
;
	mov	dx,INTRCTLRST
	xor	dx,[_MVTranslateCode]	; translate the address

	in	al,dx			; get the interrupt status
	test	al,bISmidi		; is this our interrupt?
	jnz	mv_is_midi_int		; yes, go process it...

	test	al,fISints		; are any of these interrupt legit?
	jjz	midiint_done		; no, flush this int.
;
; this was not our interrupt, chain on...
;
	pop	dx
	pop	cx
	pop	bx
        pop     ax
	pop	ds
	jmp	dword ptr cs:[OldISR]	; pass off to the other code.
;
mv_is_midi_int:
;
; check the receiver for data, or errors
;
	mov	dx,MIDISTATUS
	xor	dx,[_MVTranslateCode]		; translate the address
	in	al,dx
	mov	ah,al				; save a copy for xmitter

	and	al,bMSRframeerr+bMSRififoovr+bMSRofifoovr ; overrun or frame error?
	jz	mvmidi_int			; nope
	out	dx,al				; clear errors

	mov	dx,MIDICONTROL
	xor	dx,[_MVTranslateCode]		; translate the address

	in	al,dx
	or	al,bMCRrstfifoi+bMCRrstfifoo	; reset FIFO in ptr
        out     dx,al
	or	al,NOT(bMCRrstfifoi+bMCRrstfifoo)
	out	dx,al
;
mvmidi_int:
;
; get the status once again for the second chance loop
;
        mov     dx,MIDISTATUS
	xor	dx,[_MVTranslateCode]	; translate the address
	in	al,dx
;
; Only process the interrupt process wanted by the user
;
	test	[ProcessControl],INTINPUT ; interrupt driven input?
	jz	checktrans		; no, go check for int driven xmit

	test	al,bMSRififo		; any data in input FIFO?
	jz	checktrans		; no, go check the transmitter

	.errnz	MIDIFIFOS-MIDISTATUS-1

	inc	dx			; move to the status register

	in	al,dx
	mov	cl,al
	and	cx,bMFCififo		; get # of bytes in FIFO
	jnz	@F
	mov	cx,16
    ;
    @@:
	mov	dx,MIDIDATA		; move to the data register
	xor	dx,[_MVTranslateCode]	; translate the address
	mov	bx,[lpiMIQueue] 	; get  the input queue pointer
;
mvrxloop:
;
; read the data off the receiver
;
	in	al,dx			; fetch the received byte
;
; MIDI spec requires transmitter send something every 300 msec
; Active Sense (FE) is the standard "nothing for you" byte
;
	test	[_MidiInFilter],MF_ACTSENSE ; filter out active sense
	jz	@F			; no, keep it...
	cmp	al, 0FEh		; Active Sense?
	je	rxCont			; ..ignore it
    ;
    @@:
	cmp	[MIDIiQueueCnt],MAXQUEUE; is it full?
	jle	@F			; no, save it
	inc	[_overflow]		; yes, list it as an error
	jmp	short mvrxloop
    ;
    @@:
	mov	[bx],al
	inc	bx
	inc	[MIDIiQueueCnt] 	; one more in...
	cmp	bx,offset iCircularQueue+MAXQUEUE
	jb	rxCont
	mov	bx,offset iCircularQueue
    ;
    rxCont:
	loop	mvrxloop		; do all bytes listed in the queue
	mov	[lpiMIQueue],bx 	; save the input queue pointer
	jmp	second_check		; make sure FIFO is empty
;
checktrans:
;
; If we are using interrupt driven output, load the FIFO now...
;
	test	[ProcessControl],INTOUTPUT; interrupt driven output?
	jz	second_check		  ; no, just exit out

	; reset the interrupt now.

        mov     dx,MIDISTATUS
	xor	dx,[_MVTranslateCode]	; translate the address
	mov	al,bMSRofifo		; any data in output FIFO?
	out	dx,al

        ; check for any outgoing data

	cmp	[MIDIoQueueCnt],0	; any queued up data?
	jz	second_check		; no, just exit out

	mov	dx,MIDIFIFOS
	xor	dx,[_MVTranslateCode]	; translate the address

        in      al,dx
	and	ax,bMFCofifo		; get # of outgoing bytes
	mov	cl,4
	shr	al,cl			; right hand justify the number
	xchg	ax,cx
	cmp	cl,1			; down to 1 byte free?
	je	second_check		; yes, can't load it
	ja	@F			; we have 15 or less entries
	mov	cx,16			; 16 entires fit, but...
    ;
    @@:
	dec	cx			; we only load 15...
	mov	dx,MIDIDATA		; move to the data register
	xor	dx,[_MVTranslateCode]	; translate the address
	mov	bx,[lpoMOQueue] 	; get  the input queue pointer
    ;
    txLoop:
	mov	al,[bx] 		; pass the next byte
	out	dx,al

	inc	bx			; move the pointer
	cmp	bx,offset oCircularQueue+MAXQUEUE
	jb	@F
	mov	bx,offset oCircularQueue; it wrapped...
    ;
    @@:
	dec	[MIDIoQueueCnt] 	; any queued up data?
	loopnz	txLoop
	mov	[lpoMOQueue],bx
;
; all done with input and output processing. Check for more interrupts
;
second_check:
;
; in case we received more MIDI data during this routine, check again...
;
        mov     dx,INTRCTLRST
	xor	dx,[_MVTranslateCode]	; translate the address

	in	al,dx			; get the interrupt status
	test	al,bISmidi		; is the interrupt still active?
	jjnz	mvmidi_int		; yes, go process it...
;
midiint_done:
	mov	al,EOI			; acknowledge the interrupt.
	cmp	[_IRQNumb],7
	jbe	@F
	out	IRQ2ACKREG, al		; ack the 2nd IRQ controller
    ;
    @@:
	out	IRQ1ACKREG, al		; ack the 1st IRQ controller

	ToggleMV101mask 		; make sure the line stays active
;
busy_exit:
        pop     dx
	pop	cx
	pop	bx
        pop     ax
	pop	ds
        iret

MV101_ISR	endp

;
;   /*\
;---|*|----====< Yamaha_ISR >====----
;---|*|
;---|*| ISR service routine for the Yamaha 3802 interrupts.
;---|*|
;   \*/
;
Yamaha_ISR	proc	far
	push	ds			; minimal save
	push	dx
	push	ax

	mov	ax, @data		; establish our DS
	mov	ds, ax

	mov	dx, INTRCTLRST
	in	al, dx			; acknowledge interrupt
	test	al, bICmidi		; our MIDI interrupt?
	jnz	isourint		; yes, go do it...

	pushf
	call	dword ptr cs:[OldISR]	; perform the old interrupt
	jmp	WOOPS_exit
;
isourint:
;
; we can now process all MIDI interrupts here
;
	push	es
	push	di
        cld                             ; fatal to assume
;
; We will only process interrupt generated input if ProcessControl says so.
;
	test	[ProcessControl],INTINPUT ; Int input?
	jz	emptyRX 		  ; polled, check each h/w
;
; Check for Rx IRQ (means FIFO-Rx went non-empty) empty the FIFO
;
	mov	dx, MDSYSSTAT		; IRQ Status
	in	al, dx
	and	al, RxIRQ		; FIFO-RX non-empty?
	jz	emptyRX 		; ..jump if not

	.errnz	MDIRQCLR-MDSYSSTAT-1

	inc	dx
        out     dx,al                   ; flush it...
;
; get the input queue & load'er up!
;
        mov     ax,ds
	mov	es,ax
	mov	di,lpiMIQueue		; get the input to the queue
   ;
   rxISR_05:
	mov	al, 3			; 3x
	mov	dx, MDSYSCTLR
	out	dx, al

        mov     dx, MDGROUP4            ; 34 = FIFO-Rx status
	in	al, dx
	test	al, RxRDY		; RxRDY bit?
	jz	rxISR_10		; ..jump if FIFO empty

	mov	dx, MDGROUP6		; 36 = FIFO-Rx data
	in	al, dx			; fetch datum
;
; Eliminate some overhead! The	MIDI spec requires transmitter send something
; every 300 msec. Active Sense (FE) is the standard "nothing for you" byte
;
	test	[_MidiInFilter],MF_ACTSENSE ; filter out active sense
	jz	@F			    ; no, keep it...
	cmp	al, ACTV_SENSE		    ; Active Sense?
	je	rxISR_05		    ; ..ignore it
    ;
    @@:
	cmp	[MIDIiQueueCnt],MAXQUEUE; is it full?
	jle	@F			; no, save it
	inc	_overflow
        jmp     short rxISR_05
    ;
    @@:
        stosb
	inc	[MIDIiQueueCnt] 	; one more in...
	cmp	di,offset iCircularQueue+MAXQUEUE
	jb	rxISR_05
	mov	di,offset iCircularQueue

	jmp	rxISR_05
   ;
   rxISR_10:
	mov	[lpiMIQueue],di 	; get the input to the queue
;
emptyRX:
;
; Check for Transmitter Empyt IRQ here
;
	test	[ProcessControl],INTOUTPUT ; Int output?
	jz	endMIDIint		   ; no, exit out...

        mov     dx, MDSYSSTAT           ; IRQ Status
	in	al, dx
	and	al, TxIRQ		; FIFO-Tx became empty?
	jz	endMIDIint		; ..jump if not

        .errnz  MDIRQCLR-MDSYSSTAT-1

	inc	dx
        out     dx,al                   ; flush it...
;
; output any data till done.
;
	cmp	[MIDIoQueueCnt],0	; any data in the queue?
	jz	endMIDIint		; ..jump if not

        mov     di,lpoMOQueue           ; get the pointer to the queue
	mov	dx, MDSYSCTLR		; index to Xmitter
	mov	al,5
	out	dx,al
;
ytxLoop:
	mov	dx, MDGROUP4		; 54 = FIFO-Tx status
	in	al, dx
	test	al, TxRDY		; TxRDY bit?
	jz	ytxDone 		; ..jump if FIFO full

	mov	dx, MDGROUP6		; 56 = FIFO-Tx data
	mov	al, [di]
        out     dx, al                  ; ship datum

	inc	di			; move the pointer
	cmp	di,offset oCircularQueue+MAXQUEUE
	jb	@F
	mov	di,offset oCircularQueue; it wrapped...
    ;
    @@:
	dec	[MIDIoQueueCnt] 	; any queued up data?
	jnz	ytxLoop 		; do as much as possible
;
ytxDone:
	mov	[lpoMOQueue],di 	; save the ending pointer
;
endMIDIint:
;
; all int handling is done now, so restore the state & exit home...
;
        pop     di
	pop	es

        mov     al, [MDctrl]
        mov     dx, MDSYSCTLR
        out     dx, al                  ; restore incoming state
;
WOOPS_reenter:
;
; flush the data ready IRQ
;
	mov	al,EOI			; acknowledge at the system level
	cmp	_IRQNumb,7
	jbe	@F
	out	IRQ2ACKREG, al		; ack the 2nd IRQ controller
    ;
    @@:
	out	IRQ1ACKREG, al		; ack the 1st IRQ controller

	ToggleMV101mask 		; make sure the MV101 can generate ints
;
WOOPS_exit:
        pop     ax
        pop     dx
        pop     ds
	iret

Yamaha_ISR	endp

;   /*\
;---|*| end of MIDIA.ASM
;   \*/

	end

