
title TRI - 8088 routine CALLed from BASIC   Steve Muenter

comment *This routine produces polyphonic music with three
	voices.  The frequencies and durations of the notes
	are passed to this routine in an integer array.

	Called from BASIC via:

		CALL TRI(TUNE%(0))

	Where:

		TUNE% is an integer array

	TUNE% is treated by TRI as data words with the three most
	significant bits identifying the type of data.	000 causes
	the routine to terminate and return to BASIC.  001 sets
	the duration of play of the voices before being updated.
	010 sets the tempo of the playback.  011 is unused.  100
	sets the period for voice 1 which is slightly louder than
	the rest.  101 sets the period for voice 2.  110 sets the
	period for voice 3.
	*

cseg	segment
	assume CS:cseg, DS:cseg, ES:nothing
;	db	0FDH		;indicates BLOAD file
;	dw	0		;segment--BASIC will use default
;	dw	0		;offset--specify in BLOAD command
;	dw	rtn_len 	;length of the routine

spkr_io equ	61H

tri	proc	far
	push	bp
	mov	bp,sp
	mov	ax,[bp+6]	;get address of TUNE%(0) off stack
	sub	ax,2		;required for first time in sort
	push	ax		;save on stack
	mov	ax,1FFFH	;default tempo data
	push	ax		;save on stack
	push	ax		;initialize tempo counter
	xor	ax,ax		;must write 0 into voice count and
	mov	bx,ax		; data registers
	mov	dx,ax
	mov	si,ax
	mov	bp,ax
	mov	di,ax
	mov	es,ax
;
;This section sorts the tune data into the appropriate registers
;Voice period data is stored in ES for voice 1, DI for voice 2,
;and SI for voice 3.  The voice period count is stored in BX for
;voice 1, DX for voice 2, and BP for voice 3.
;
sort:	push	bx
	push	dx
	push	bp

sort_1: mov	bp,sp
	mov	bx,[bp+10]	;get tune pointer
	add	bx,2		;point to next tune data
	mov	[bp+10],bx	;restore updated tune pointer
	mov	ax,[bx] 	;get tune data in AX
	mov	dx,ax		;make a copy in DX
	and	dx,1FFFH	;strip off 3 data type bits
	shl	ax,1		;move msb into carry bit
	jnc	sort_4		;jump if end, duration, or tempo data
	shl	ax,1		;move 2nd msb into carry bit
	jc	sort_3		;jump if voice 3 data
	shl	ax,1		;move 3rd msb into carry bit
	jc	sort_2		;jump if voice 2 data
	mov	es,dx		;store voice 1 data in ES
	jmp	sort_1
sort_2: mov	di,dx		;store voice 2 data in DI
	jmp	sort_1
sort_3: mov	si,dx		;store voice 3 data in SI
	jmp	sort_1		;ignore
sort_4: shl	ax,1		;move 2nd msb into carry bit
	jnc	sort_6		;jump if end or duration data
	shl	ax,1
	jnc	sort_5		;jump if tempo data
	jmp	sort_1		;ignore
sort_5: mov	[bp+8],dx	;store tempo
	mov	[bp+6],dx	;initialize tempo counter
	jmp	sort_1
sort_6: shl	ax,1		;move 3rd msb into carry
	jc	sort_7		;jump if duration
	jmp	end
sort_7: mov	cx,dx		;store duration data
	pop	bp
	pop	dx
	pop	bx
	cli			;turn off interrupts during play
loop:	pop	ax		;get current tempo count
	dec	ax		;subtract 1
	push	ax		;restore tempo count
	jnz	play		;jump if some tempo remains
	pop	ax		;reset tempo counter
	pop	ax
	push	ax
	push	ax
	loop	play		;decrement duration counter
	sti			;turn ints back on during sort
	jmp	sort		;get new tune data if duration up
;
;This routine plays the notes until the duration counter in CX
;reaches zero.	At that time, the new data is sorted.
;
play:	add	bx,si		;add voice 3 data to count
	rol	bx,1		;get msb of voice 3 count
	mov	al,12H		;keeps keyboard clk on and
	rcl	al,1		; casette motor relay off
	rcl	al,1
	out	spkr_io,al	;output voice 3 state to speaker
	ror	bx,1		;restore bx to original state
	add	dx,di		;add voice 2 data to count
	rol	dx,1
	mov	al,12H
	rcl	al,1
	rcl	al,1
	out	spkr_io,al
	ror	dx,1
	mov	ax,es
	add	bp,ax		;add voice 1 data to count
	rol	bp,1
	mov	al,12H
	rcl	al,1
	rcl	al,1
	out	spkr_io,al
	ror	bp,1
	jmp	loop
end:	add	sp,12
	pop	bp
	mov	ax,cs
	mov	es,ax
	ret	2
tri	endp
rtn_len equ	$-tri		;length of routine for header
cseg	ends
	end	tri

	bp
	mov	ax,cs
	mov	es,ax
	ret	2
tri	endp
rtn_len equ	$-tri		;le