page
name	SDA_USER_ROUTINES

code	segment public 'code'
	assume cs:code,ds:code


;******************************************************************
;*	
;*	 		RELEASE 2.0
;*	
;*  CHANGES TO PRE-EXISTING (RELEASE 1.0) ROUTINES:
;*
;*  1.	_CHECK4_SDA now alters reg. CX.
;*
;*	This routine has been modified to turn off the tempo updates
;*	that are active if the TSR program is invoked out of Promidi's
;*	Play/Record screen.
;*
;*  2.	_PREP_RTN must be called before the TSR program terminates
;*	  and returns.  This will re-activate the tempo updates if 
;*	  necessary.
;*
;*  These changes affect only the routines in this source module.
;*  Release 1.0 User Routines will continue to work (unmodified)
;*  with ALL Promidi software (ver. C11 & above).
;*
;*
;*
;*			NEW FEATURES
;*
;*  Routines have been added that allow TSR programs to put the
;*  Midicard in UART Mode and access the interface directly
;*  through its I/O space.
;*
;*  The UART Mode send and receive routines (_U_SNDB, _U_RCVB) are
;*  many times faster than _SDA_GETB and _SDA_PUTB.  They are 
;*  intended to be used for "longer" uploads and downloads (say more
;*  than 100 bytes or so) where the ability to send and receive
;*  while Promidi is playing or recording are not as important
;*  as the time required for the operations.
;*
;*  In UART Mode, there is no Midi Thru capability in the
;*  Midicard.  Any passing of data through the interface 
;*  (Midi In to Midi Out) must be done by the TSR software in this mode.
;*
;*  UART Mode may be entered and exited on a message by message
;*  basis, using _SDA_GETB and _SDA_PUTB for the shorter messages;
;*  and _U_RCVB and _U_SNDB for longer ones.  
;*
;*  *** DANGER ***
;*
;*     Only the following User Routines may be called while
;*     in UART Mode:
;*
;*		_U_RCVB		Get a byte from the Midicard
;*
;*		_U_SNDB		Send a byte to the Midicard
;*
;*		_SDA_UOFF	Leave UART Mode
;*
;*		_SDA_EXEC	Call Promidi's EXEC
;*
;*  To enter UART Mode, the TSR software must first call '_SDA_UON'.
;*  This routine returns 0 (FAIL) if Promidi is recording or playing,
;*  or the TSR software is recording ('_SDA_RECON').  In the case
;*  of Promidi recording or playing, it is suggested that the 
;*  software notify the user that the type of operation requested
;*  can not be performed while playing or recording.  The user may
;*  then flip back to Promidi to stop, and then return to the TSR
;*  program.
;*
;******************************************************************


;******************************************************************
;*
;* See companion file 'SDA_USER.DOC' for more information on 
;* these subroutines.
;*
;******************************************************************




;******************************************************************
;*
;* This software is intended to provide access to a running
;* Promidi system from co-resident software which receives
;* control via INT 16 (function 1).  Two prerequisites are
;* necessary before the service routines may be called:
;*  1. The interrupting routine must immediately save the Promid's
;*     DS register in _SDA_DSEG.
;*  2. The routine _CHECK4_SDA must be called successfully
;*     (RET = 1).  This routine binds the two programs together.
;*
;* If it is desired to have Promidi continue to read from the disk
;* (as when playing a file/track), the routine _SDA_EXEC must be
;* called periodically.
;*
;******************************************************************


;*************************************************************************
;*
;* The source file 'SDA_USER.ASM' is provided free of charge by Systems 
;* Design Associates, Inc.  SDA does not in any way warranty this software 
;* nor is it liable for any damages resulting from its use.
;*
;*************************************************************************





PUBLIC	_SDA_DSEG


_SDA_DSEG	DW	0	; SDA's Data Segment
				; Must be saved by the user upon
				;  entrance via INT 16.

SDA_SS		DW	0	; SAVED IN SDAKI BEFORE THE INT 16.
SDA_SP		DW	0
SDA_BP		DW	0
SDA_SI		DW	0
SDA_DI		DW	0

USER_SS		DW	0	; SAVED IN THE USER ROUTINES
USER_DS		DW	0	;  THAT CALL EXEC.
USER_SP		DW	0
USER_BP		DW	0
USER_DI		DW	0
USER_ES		DW	0

IRLOC2		dw	(8 + 2) * 4	; Midicard Interrupt locations.
IRLOC7		dw	(8 + 7) * 4
IRLOC5		dw	(8 + 5) * 4
IRLOC4		dw	(8 + 4) * 4

PTA		dw	?		; Midicard Port Addresses
PTB		dw	?
PTC		dw	?
PTCNTL		dw	?

UREC_MODE	db	0		; In User Record Mode Flag
TUP_STATUS	db	0		; Tempo Update Status Flag.


PUBLIC	_CHECK4_SDA,_SDA_GETB,_SDA_PUTB,_SDA_EXEC,_SDA_RECON
PUBLIC	_SDA_RECOFF,_U_SNDB,_U_RCVB,_PREP_RET,_SDA_UON,_SDA_UOFF
page



; _CHECK4_SDA
;
; *** DANGER *** THIS ROUTINE MUST BE CALLED SUCCESSFULLY (RET = 1)
;		 IN ORDER TO USE ANY OF THE SDA USER FUNCTIONS.
;
; If SDA interface (Midicard) interrupt handler is
;  in place, this routine fills SDA_SEG & SDA_OFST with the
;  addr. of USER_CALL.
;
;	Returns:	ax = 0		Handler not present.
;			ax = 1		Is present - data filled.
;
;	Regs altered - ax,bx,cx,dx

_CHECK4_SDA proc far
	push	es
	push	si
	push	bp

	mov	si,[IRLOC2]
	call	SDA_MATCH
	cmp	ax,1
	je	GOT_MATCH

	mov	si,[IRLOC7]
	call	SDA_MATCH
	cmp	ax,1
	je	GOT_MATCH

	mov	si,[IRLOC5]
	call	SDA_MATCH
	cmp	ax,1
	je	GOT_MATCH

	mov	si,[IRLOC4]
	call	SDA_MATCH
	cmp	ax,1
	je	GOT_MATCH

	xor	ax,ax 		; No match - don't use card.
	jmp	CHKRET

GOT_MATCH:

; si = int. vector offset.

	xor	ax,ax
	mov	es,ax

	mov	ax,es:[si + 2]		; Get segment of int. rout.
	mov	bx,es:[si]		; Get offset   "    "
	sub	bx,8			;  minus 8.
	mov	es,ax
	mov	ax,es:[bx + 2]	; Get USER_CALL segment
	mov	dx,es:[bx]	; Get offset.

	mov	cs:[SDA_SEG],ax
	mov	cs:[SDA_OFST],dx

	mov	si,11			; Get tempo update status.
	call	DOUC
	mov	TUP_STATUS,al

	cmp	al,0			; Are tempo status updates active ?
	jz	UPNA			; No

	mov	si,8			; Yes - Turn them off.
	call	DOUC
UPNA:
	mov	si,6			; Get port addresses of Midicard
	call	DOUC

	mov	PTA,ax
	inc	ax
	mov	PTB,ax
	inc	ax
	mov	PTC,ax
	inc	ax
	mov	PTCNTL,ax

	mov	ax,1
CHKRET:
	pop	bp
	pop	si
	pop	es
	ret
_CHECK4_SDA endp



; SDA_MATCH
;
; This routine looks for the string "SDA " above the interrupt
; location indicated by the vector offset in 'si'.
;
;	Returns:	ax = 0		No match
;			ax = 1		Match
;
;	Regs altered	ax,bx,es

SDA_MATCH proc near
	xor	ax,ax
	mov	es,ax
	mov	bx,si
	mov	ax, word ptr es:[bx] + 2
	mov	bx, word ptr es:[bx]
	mov	es,ax

	cmp	byte ptr es:[bx] - 4,'S'
	jne	matret

	cmp	byte ptr es:[bx] - 3,'D'
	jne	matret

	cmp	byte ptr es:[bx] - 2,'A'
	jne	matret

	cmp	byte ptr es:[bx] - 1,' '
	jne	matret
	mov	ax,1
matret:	
	ret
SDA_MATCH endp
page



; _PREP_RET  -  Prepare to terminate program and return to Promidi.
;
;  *******	THIS ROUTINE MUST BE CALLED BEFORE TSR PROGRAM TERMINATION
;		
;
;  c  call	prep_ret();
;
;  asm  call	call	_prep_ret
;    (far)
;
;	Regs Altered: ax,bx,cx,dx

_PREP_RET	proc	far

	cmp	TUP_STATUS,0	; Was tempo status update active ?
	jz	RETRET		; No

	push	si
	mov	si,7		; Yes - re-enable tempo updates.
	call	DOUC
	pop	si
RETRET:
	ret
_PREP_RET	endp
page




; _SDA_GETB  -  If in sda record mode, get a byte from midi in.
;
;  c  call	c = sda_getb();
;
;  asm  call	call	_sda_getb	; Byte or ret. code in ax.  
;    (far)
;
;	RETURNS:	byte
;			100H		Buffer empty
;			2nnH		Buffer overflow (byte in LSB).
;
;	Regs Altered: ax,bx,cx,dx

_SDA_GETB	proc	far

	push	si
	mov	si,1
	call	DOUC
	pop	si
	ret
_SDA_GETB	endp
page




; _SDA_PUTB  -  send a byte (of a complete midi msg.) out midi out.
;
;  c call	sda_putb(c);
;
;  asm call	push	ax		; Push byte on stack
;   (far)	call	_sda_putb
;		add	sp,2
;
;	Regs Altered: ax,bx,cx,dx


_SDA_PUTB	proc	far

	push	bp
	mov	bp,sp
	push	si
	mov	cx,[bp+6]	; Get parameter.
	mov	si,2
	call	DOUC
	pop	si
	pop	bp
	ret
_SDA_PUTB	endp
page



; _SDA_RECON  -  Go into SDA record mode.
;
;  c  call	sda_recon();
;
;  asm  call	call	_sda_recon
;    (far)
;
;	Returns:	non-0	No. of bytes allocated to receive buffer.
;			0	Fail - already recording or 
;				not enough memory.
;
;	Regs Altered: ax,bx,cx,dx


_SDA_RECON	proc	far

	mov	UREC_MODE,1
	push	si
	mov	si,3
	call	DOUC
	pop	si
	ret
_SDA_RECON	endp
page



; _SDA_RECOFF  -  Leave SDA record mode.
;
;  c  call	sda_recoff();
;
;  asm  call	call	_sda_recoff
;    (far)
;
;	Regs Altered: ax,bx,cx,dx


_SDA_RECOFF	proc	far

	mov	UREC_MODE,0
	push	si
	mov	si,4
	call	DOUC
	pop	si
	ret
_SDA_RECOFF	endp
page





; _U_SNDB  -  Send a byte to the Midicard.
;
;  c call	u_sndb(c);
;
;  asm call	push	ax		; Push byte on stack
;   (far)	call	_u_sndb
;		add	sp,2
;
;
;	Regs Altered: ax,cx,dx

_U_SNDB	proc	far
	push	bp
	mov	bp,sp
	mov	cx,[bp+6]		; Get parameter.
USND1:
	mov	dx,PTC
	in	al,dx
	test	al,80H			; Output buffer empty ?
	jz	USND1			; No - keep trying.

	mov	dx,PTB
	in	al,dx
	test	al,40H			; Can we send Midi data ?
	jnz	USND1			; No - keep trying.

	mov	dx,PTCNTL		; Say its Midi data
	mov	al,4			;   (not a command)
	out	dx,al

	mov	dx,PTA			; Send byte.
	mov	al,cl
	out	dx,al

	pop	bp
	ret
_U_SNDB	endp
page




; _U_RCVB  -  If a byte is present, receive a byte from the Midicard
;
;  c  call	c = u_rcvb();
;
;  asm  call	call	_u_rcvb	; Byte or ret. code in ax.  
;    (far)
;
;	RETURNS:	byte
;			100H		No input received
;
;	Regs Altered: ax,dx

_U_RCVB	proc	far
	mov	dx,PTC
	in	al,dx
	test	al,20H			; Look for input
	jnz	IBFULL
	mov	ax,100H			; None
	jmp	URCRET
IBFULL:
	mov	dx,PTA			; Get byte
	in	al,dx
	xor	ah,ah
URCRET:
	ret
_U_RCVB	endp
page




; _U_TRCV  -  Test if a byte is present from the Midicard
;
;  c  call	rc = u_trcv();
;
;  asm  call	call	_u_trcv  ; Ret. code in ax.  
;    (far)
;
;	RETURNS:	0		No input.
;			1		Input present.
;
;	Regs Altered: ax,dx

_U_TRCV	proc	far
	mov	dx,PTC
	in	al,dx
	test	al,20H			; Look for input
	jnz	UTR1
	xor	ax,ax
	jmp	UTRRET
UTR1:
	mov	ax,1
UTRRET:
	ret
_U_TRCV	endp
page




; _SDA_UON  -  Enter Uart Mode
;
;  c  call	rc = sda_uon();
;
;  asm  call	call	_sda_uoff	; Return code in ax
;    (far)
;
;	RETURNS:  0  -  FAIL, Promidi is recording or playing, or
;			  we are in User Record Mode.
;		  1  -  SUCCESS  -  in Uart Mode.
;
;
;	Regs Altered: ax,bx,cx,dx

_SDA_UON	proc	far

	cmp	UREC_MODE,0
	jnz	UONFAIL

	push	si
	mov	si,9
	call	DOUC
	pop	si

	cmp	ax,0
	jnz	UON2
UONFAIL:
	xor	ax,ax
	jmp	UONRET
UON2:
	mov	dx,PTC
	in	al,dx
	test	al,80H
	jz	UON2

	mov	dx,PTCNTL
	mov	al,5
	out	dx,al

	mov	dx,PTA
	mov	al,35
	out	dx,al
	mov	ax,1
UONRET:
	ret
_SDA_UON	endp
page




; _SDA_UOFF  -  Leave Uart Mode
;
;  c  call	sda_uoff();
;
;  asm  call	call	_sda_uoff
;    (far)
;
;	Regs Altered: ax,bx,cx,dx

_SDA_UOFF	proc	far

	push	si
	mov	si,10
	call	DOUC
	pop	si
	ret
_SDA_UOFF	endp
page




; _SDA_EXEC  -  Call Promidi's multi-tasking exec.
;		This gives Promidi a chance to replenish
;		its disk buffers when playing.
;
;  c  call	sda_exec();
;
;  asm  call	call	_sda_exec
;    (far)
;
;	Regs Altered: ax,bx,cx,dx


_SDA_EXEC	proc	far

	push	si
	mov	si,5
	call	DOUC
	pop	si
	ret
_SDA_EXEC	endp
page




; The DOUC sub-routine vectors into the Promidi software.
; The 2-word address must have been set up via a successful
;  call to _check4_sda.

DOUC		proc	near

		mov	ax,[_SDA_DSEG]	; Get the SDA data seg.
		db	9AH
SDA_OFST	dw	0	; USER_CALL offset.
SDA_SEG		dw	0	; USER_CALL segment.
		ret
DOUC		endp

	code	ends

	END
