PAGE	60,132

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; MIDIINT.ASM -- Assembler subroutines for MIDIEX patch exchange utility
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

; COPYRIGHT (C) 1986 John Bailin, Cantus Corporation
;
; Date:        01/02/86
;
; Changed 08/24/86 Jim Bergsten to function using the Microsoft "C"
; compiler. Summary of changes:
;     1. PAGE statement added for better output listing.
;     2. Entry point names prefixed with "_".
;     3. GROUP, PROG, SEGMENT statements changed to match Microsoft
;	 small model standards.
;     4. Unnecessary stack subtracts and adds removed.
;     5. Other corrections, simplifications...
;
; Changed 11/14/86 by Michael Geary - fixed several bugs
;
;
; Note: Linkage conventions assume the Microsoft C compiler small model.

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; equates

mpudata equ	330h			; mpu data port address.
mpustat equ	331h			; mpu status port address.
mpucmd	equ	331h			; mpu command port address.
mpudsr	equ	80h			; mpu data set ready, active low.
mpudrr	equ	40h			; mpu data read ready, active low.

loop_delay	equ	0ffffh		; delay for timeouts.

BUFMAX	equ	8192			; **MUST MATCH C CODE**

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_TEXT	SEGMENT  BYTE PUBLIC 'CODE'
_TEXT	ENDS
CONST	SEGMENT  PARA PUBLIC 'CONST'
CONST	ENDS
_BSS	SEGMENT  WORD PUBLIC 'BSS'
_BSS	ENDS
_DATA	SEGMENT  WORD PUBLIC 'DATA'
_DATA	ENDS
DGROUP	GROUP	CONST, _BSS, _DATA

	ASSUME	cs:_TEXT, ds:DGROUP, es:nothing, ss:nothing

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; data segment

_DATA	segment word public 'DATA'

	public	_recv_data_count,_recv_buf,_recv_buf_ptr
	public	_fetch_buf_ptr
	public	_wait_char

oldInt2 	label	dword
int_2_save	dw	?,?

_recv_buf	db	BUFMAX dup (0)
_recv_buf_end	label	byte
_recv_data_count dw	 0
_recv_buf_ptr	dw	0
_fetch_buf_ptr	dw	0

_wait_char	 db	 0

_DATA	ends

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; code segment

_TEXT	segment byte public 'CODE'

	public	_set_mpu_vector,_reset_mpu_vector,mpu_int,_int10
	public	_write_mpu_command,_write_mpu_data,_get_char
	public	_queue_mpu_data

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Set the interrupt vector for the MPU/401

_set_mpu_vector  proc	 near

	mov	ax,350ah		; get current int 2
	int	21h			; dos does it.
	mov	int_2_save,bx		; save the vector offset.
	mov	int_2_save+2,es 	; save the vector segment.
	push	ds			; save data segment.
	mov	dx,offset mpu_int	; offset for dos call.
	mov	ax,seg mpu_int		; segment for dos call
	mov	ds,ax			;
	mov	ax,250ah			; set vector call, int 2.
	int	21h			; dos installs vector.
	pop	ds
	cli				; interrupts off.
	in	al,21h			; read 8259 address.
	jmp	short $+2
	and	al,0fbh 		; enable irq2.
	out	21h,al			;
	call	_mpu_reset		; reset the mpu, return code in AX
	sti				; interrupts back on.
	ret				; return to caller.

_set_mpu_vector  endp

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Reset the interrupt vector for the MPU/401

_reset_mpu_vector	 proc	 near

	push	ds			; save registers.
	cli				; interrupts off.
	in	al,21h			; read 8259.
	jmp	short $+2
	or	al,4			; disable irq2.
	out	21h,al			; done.
	sti				; interrupts back on.
	mov	ax,250ah			; restore old int 2 vector.
	mov	dx,int_2_save		; get old offset into dx.
	mov	ds,int_2_save+2 	; get old segment into ds.
	int	21h			; dos sets the vector.
	pop	ds			; restore registers.
	ret				; return to caller.

_reset_mpu_vector	 endp

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Reset the MPU/401

_mpu_reset	 proc	 near
	cli				; disable interrupts.

	mov	dx,mpustat		; mpu status port.
	mov	cx,loop_delay		; delay counter.
mr10:	in	al,dx			; get the status.
	and	al,mpudrr		; test for data receive ready.
	jz	mr15			; ready.
	loop	mr10			; not ready.
	xor	ax,ax			; timed out.
	jmp	short mr40		; leave.
mr15:	mov	ax,0ffh 		; get the command.
	out	dx,al			; send to mpu.
	mov	cx,loop_delay		; delay counter.
mr20:	in	al,dx			; get the status.
	and	al,mpudsr		; test for data set ready.
	jz	mr30			; ready.
	loop	mr20			; not ready.
	xor	ax,ax			; timed out.
	jmp	short mr40		; leave.
mr30:	mov	dx,mpudata		; mpu data port.
	in	al,dx			; read data.
	mov	ax, 1
mr40:
	sti				; interrupts back on.
	ret				; return to caller.

_mpu_reset	 endp

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Write a command to the MPU
; BOOL write_mpu_command( command-byte );

_write_mpu_command	 proc	 near

	push	bp			; save caller's base pointer
	mov	bp,sp			; establish new base pointer.
	mov	dx,mpustat		; mpu status port.
	mov	cx,loop_delay		; loop counter for no response.
wmc10:	in	al,dx			; get the status.
	and	al,mpudrr		; test for data receive ready.
	jz	wmc15			; ready.
	loop	wmc10			; not ready.
	xor	ax,ax			; timed out.
	jmp	short wmc30		; leave.
wmc15:	cli				; interrupts off.
	mov	ax,[bp+4]		       ; get the command.
	out	dx,al			; send to mpu.
	mov	cx,loop_delay		; loop counter for no response.
wmc20:	in	al,dx			; get the status.
	and	al,mpudsr		; test for data set ready.
	jz	wmc25			; ready.
	loop	wmc20			; not ready.
	xor	ax,ax			; timed out.
	jmp	short wmc30		; leave.
wmc25:	mov	dx,mpudata		; mpu data port.
	in	al,dx			; read data.
	cmp	al,0FEh 		; acknowledge?
	je	wmc29			; yes.
	call	_queue_mpu_data 	; no. queue the data.
wmc29:	mov	ax,1			; normal return
wmc30:	sti				; interrupts back on.
	pop	bp			; get base pointer back.
	ret				; return to caller.

_write_mpu_command	 endp

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Write data to the MPU
; BOOL write_mpu_data( data-byte );

_write_mpu_data  proc	 near

	push	bp			; save caller's base pointer
	mov	bp,sp			; establish new base pointer.
	mov	dx,mpustat		; mpu status port.
	mov	cx,loop_delay		; loop counter for no response.
wmd10:	in	al,dx			; get the status.
	and	al,mpudrr		; test for data receive ready.
	jz	wmd20			; ready.
	loop	wmd10			; not ready.
	xor	ax,ax			; timed out.
	jmp	short wmd30		; leave.
wmd20:	mov	dx,mpudata		; mpu data port.
	mov	ax,[bp+4]		; get the byte to send.
	out	dx,al			; send to mpu.
	mov	ax,1			; normal return
wmd30:	pop	bp			; get base pointer back.
	ret				; return to caller.

_write_mpu_data  endp

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; MPU interrupt handler (IRQ2, INT 0Ah)

mpu_int proc	far

	push	ax			; save regs.
	push	dx
	pushf
	mov	dx,mpustat		; mpu status port
	in	al,dx			; read status
	rol	al,1			; bit 7 into cy.
	jb	mi90			; not generated by mpu.
	popf
	sti				; allow other interrupts
	push	ds
	mov	ax,DGROUP
	mov	ds,ax
	mov	dx,mpudata		; ready. now get data port.
	in	al,dx			; get data.
	cmp	al,0FEh 		; acknowledge?
	je	mi30			; yes. don't queue data.
	cmp	_wait_char,0		; waiting for a particular char?
	je	mi20			; no.
	cmp	al,_wait_char		; the char we're waiting for?
	jne	mi20			; no.
	mov	_wait_char,0		; yes. reset flag.
mi20:	call	_queue_mpu_data 	; queue it up.
mi30:	mov	al,20h			; eoi to send to 8259.
	out	20h,al			; send it.
	pop	ds			; restore regs.
	pop	dx
	pop	ax
	iret				; return to caller.

mi90:	popf
	pop	dx
	pop	ax
	jmp	oldInt2

mpu_int endp

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; queue received mpu data
; input -- al = received data

_queue_mpu_data  proc	 near

	push	bx			; save regs
	cli				; interrupts off.
	mov	bx,_recv_buf_ptr	; buffer ptr.
	mov	_recv_buf[bx],al	; store data.
	inc	_recv_buf_ptr		; increment the buffer pointer.
	inc	_recv_data_count	; increment the data count.
	cmp	_recv_buf_ptr,BUFMAX	; incremented past the end?
	jb	qmd10			; not yet.
	mov	_recv_buf_ptr,0 	; yes. wrap around to zero.
qmd10:	sti				; interrupts back on.
	pop	bx
	ret				; return to caller.

_queue_mpu_data  endp

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; fetch received mpu data (*not used*)
;
; input -- none
; output -- ah = 1 if data received, 0 if not.	al = data.

IF 0

_fetch_mpu_data  proc	 near

	xor	ax, ax
	cli				; interrupts off.
	mov	bx,_fetch_buf_ptr	; buffer ptr.
	cmp	bx,_recv_buf_ptr	; any data?
	je	fmd10			; no, return
	mov	al,_recv_buf[bx]	; fetch data.
	mov	ah, 1
	inc	_fetch_buf_ptr		; increment the buffer pointer.
	cmp	_fetch_buf_ptr,_recv_buf_end  ; incremented past the end?
	jb	fmd10			; not yet.
	mov	_fetch_buf_ptr,0	; yes. wrap around to zero.
fmd10:	sti				; interrupts back on.
	ret				; return to caller.

_fetch_mpu_data  endp

ENDIF

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; bios int 16h call.
;
; calling convention get_char( mode ) ;
;
; input -- mode = 0 -- read next char, return result in al, scan code in ah.

;	-- mode = 1 -- returns 1 if a char is available, 0 if not.
;	-- mode = 2 -- returns shift flags in al.

_get_char	 proc	 near

	push	bp			; save caller's base pointer
	mov	bp,sp			; establish new base pointer.
	mov	ax,[bp+4]			; get mode.
	xchg	ah,al			; turn it around for bios call.
	int	16h			; do the bios call.
	pushf				; save flags.
	cmp	word ptr [bp+4],1		; check for char available?
	jne	gc10			; no. just return what bios returns.

	xor	ax,ax			; assume no char.
	popf				; get flags back.
	jz	gc20			; no char available.
	mov	ax,1			; signal char available.
	jmp	short gc20		; continue.
gc10:	popf				; sync.
gc20:	pop	bp			; get base pointer back.
	ret				; return to caller.

_get_char	 endp

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; bios int 10h c level call.
;
; calling convention int10( ax, bx, cx, dx )
;

_int10	 proc	 near

	push	bp			; save caller's base pointer
	mov	bp,sp			; establish new base pointer.
	mov	ax,[bp+4]		; get register calling value.
	mov	bx,[bp+6]		; get register calling value.
	mov	cx,[bp+8]		; get register calling value.
	mov	dx,[bp+10]		; get register calling value.
	int	10h			; do the bios call.
	pop	bp			; get base pointer back.
	ret				; return to caller.

_int10	 endp

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_TEXT	ends

	end
