


;	COMPOSER.ASM	A MUSICIAL PROGRAM BY C. W. CHATHAM
;			Copyright  Nov 1, 1982
;
; 	Permission is hereby granted for the non-commercial rep-
; roduction of this program.  All other rights reserved.
;
;	This program is designed to operate with AY-3-8910/8912 
; sound chips.  It requires a 2ms counter at memory location
; TICK (000Bh) as defined in the first equate.  This is used for
; all tempo regulation.  
;
;
;	Definitions:	PSGRx
;			0	Ch A Tone Period  8 bit LSB
;			1	"       "	  4 bit	MSB
;			2	Ch B Tone Period  8 bit LSB
;			3			  4 bit MSB
;			4	Ch C Tone Period  8 bit LSB
;			5			  4 bit MSB
;			6	Noise Period	  5 bits
;			7	N/Enable  port/noise/tone
;			8	Ch A Amplitude	  M +4 bits
;			9	Ch B Amp
;			10	Ch C Amp
;			11	Envelope Period   Fine (LSB)
;			12	  "        "      Coarse (MSB)
;			13	Envelope Shape/Cycle 4 bits     
; end of registers	14	CH 'A' ON TIME	  Fine (LSB)
;			15			  Coarse (MSB)
;			16		Tied?		1 bit
;			17	CH 'B' ON TIME	  Fine (LSB)
;			18			  Coarse (MSB)
;			19		Tied?		1 bit
;			20	CH 'C' ON TIME	  Fine (LSB)
;			21			  Coarse (MSB)
;			22		Tied?		1 bit
;			23
;			24
;			30
;-----------------------------------------------------------
TICK	EQU	0BH      ;MEMORY LOCATION OF 2ms TICK COUNTER
BDOS	EQU	5H
CONIN	EQU	1
CONOUT	EQU	2
PSTRING	EQU	9
RSTRING	EQU	10
OPENF	EQU	15
CLOSEF	EQU	16
FCB	EQU	5CH
CR	EQU	0DH	;CARRIAGE RTN
LF	EQU	0AH	;LINE FEED
HT	EQU	09H	;HOR TAB
BS	EQU	08H	;BACKSPACE
DEL	EQU	7FH	;DELETE
SPC	EQU	20H	;SPACE
ES	EQU	24H	;END STRING
;------------------------------Š	ORG	100H
	LXI	SP,STACK	;LOCAL STACK
	MVI	A,'1'	;GET PORT #1 ADDRESS
	CALL	GTPRT
CLGTFL	CALL	CLS	;CLEAR THE SCREEN
	CALL	NAME	;PRINT OPENING STATEMENT
	CALL	GETFIL	;GET THE STARTING MUSIC
	CALL	LOADFIL	;
NEWTMP	CALL	TEMPO
	CALL	BEAT0
STRTHR	CALL	START
	CALL	BEGIN
	CALL	0000H	;PAUSE
PSTAT	DB	0
MSURE	DS	4	;0000-9999 MEASURE #
BNOTE	DB	'4'	;NOTE TO GET ONE BEAT
BMSUR	DB	'4'	;# BEATS PER MEASURE
IMSUR	DS	2	;# INTERUPTS PER MEASURE
TIME1	DS	2	;# INTERUPTS PER WHOLE NOTE
BEAT	DS	2	;# INTERUPTS PER BEAT
FILPT	DS	2	;FILE POINTER
NDBUF	DS	2	;TOP OF MUSIC BUFFER SET BY LDFIL
TMPO	DB	120,49,50,48	;SET BY TEMPO
PSGN	DB	01H	;PSG IN USE
		;FOLLOWING SET BY GTPORT
PORT	DB	320Q	;BASE PORT FOR CURRENT USE
PORT2	DB	322Q	;PORT + 2
REGIS	DW	PSGR1	;BASE FOR CURRENT PSG REGISTERS
		;CHNL SET BY NOTE3
CHNL	DB	'A'	;CURRENT PSG CHANNEL
PSGB1	DB	322Q	;PSGA1-1  (PORT2)
PSGA1	DB	320Q	;PSGA1+0
PSGR1	DS	1EH
PSGB2	DB	0	;
PSGA2	DB	0	;PSGA1+20H
PSGR2	DS	1EH
PSGB3	DB	0
PSGA3	DB	0	;PSGA1+40H
PSGR3	DS	1EH
PSGB4	DB	0
PSGA4	DB	0	;PSGA1+60H
PSGR4	DS	1EH
PSGB5	DB	0
PSGA5	DB	0	;PSGA1+80H
PSGR5	DS	1EH
PSGB6	DB	0
PSGA6	DB	0	;PSGA1+A0H 
PSGR6	DS	1EH
;
;
SVOICE	DB	'1','A'	;START VOICE
;VOICE TABLE  1st Byte=R6 (N Freq)  2nd Byte=R7 (Not Enable)
; 3rd & 4th=R11 & R12 (Envelope period LSB & MSB)
;	5th Byte=R15 (Envelope Shape/Cycle)
VOICEA	DB	000Q,370Q,00H,90H,09	;SINGLE STRIKE DECAYŠVOICEB	DB	000Q,370Q,000,60H,09
VOICEC	DB	000Q,370Q,000,38H,09
VOICED	DB	000Q,370Q,000,20H,09
VOICEE	DB	000Q,370Q,000,14H,09
VOICEF	DB	000Q,370Q,000,0AH,09
VOICEG	DB	000Q,370Q,000,06H,09
VOICEH	DB	000Q,370Q,000,03H,09

VOICEI	DB	000Q,370Q,000,0AH,08	;MULT STRIKE DECAY
VOICEJ	DB	000Q,370Q,000,07H,08
VOICEK	DB	000Q,370Q,000,05H,08
VOICEL	DB	000Q,370Q,000,03H,08
VOICEM	DB	000Q,370Q,000,02H,08

VOICEN	DB	000Q,370Q,000,18H,0EH	;WARBLE
VOICEO	DB	000Q,370Q,000,0AH,0EH
VOICEP	DB	000Q,370Q,000,08H,0EH
VOICEQ	DB	000Q,370Q,000,06H,0EH
VOICER	DB	000Q,370Q,000,04H,0EH
VOICES	DB	000Q,370Q,000,03H,0EH
VOICET	DB	000Q,370Q,000,02H,0EH
VOICEU	DB	000Q,370Q,000,02H,0DH	;RAPID ATTACK 
	;SPECIAL EFFECTS IN CHANNEL A ONLY
VOICEV	DB	001Q,361Q,000,60H,09H	;DRUM 1
VOICEW	DB	377Q,361Q,000,14H,09H	;DRUM 2
VOICEX	DB	377Q,360Q,000,06H,0DH	;BREATHY WHISTLE
VOICEY	DB	017Q,361Q,000,020Q,00H	;GUNSHOT
VOICEZ	DB	001Q,361Q,000,070Q,00H	;EXPLOSION

ZERO	DB	'0'
TIMER	DB	0
;
; 
NAME	MVI	C,09H	;PRINT STRING FCN
	LXI	D,NAME1
	CALL	BDOS
	RET
NAME1	DB	LF,HT,HT,HT,HT,'COMPOSER.ASM',CR,LF,LF
	DB	HT,HT,'By C.W. Chatham    '
	DB	' Copyright 1 Nov 1982',CR,LF
	DB	LF,HT,HT,'A musical program using the AY 3-8910'
	DB	' PSG.',CR,LF,HT,LF,LF,LF,'$'

;------------------------
BEGIN	;START HERE FOR MUSIC PLAYING
	;JUMPS TO HERE UPON DETECTION OF CLOCK CHANGE
	;AT LOCATION TICK -- 000BH
	LXI	H,TICK
	MOV	A,M
	LXI	H,TIMER
	MOV	M,A	;PUT CURRENT TICK COUNT IN 
			;TIMER
	MVI	B,00H	;SET CHANNEL AVAIL TO ZERO
;-----------------------	
PRTCAL	LXI	H,PSGB1Š	CALL	PRTCLA
	NOP		;IS PORT ZERO?
	JZ	BEGIN4	;IF SO FINISHED
	CALL	BEGIN0
	LXI	H,PSGB2
	CALL	PRTCLA
	NOP
	JZ	BEGIN4
	CALL	BEGIN0
	LXI	H,PSGB3
	CALL	PRTCLA
	NOP
	JZ	BEGIN4
	CALL	BEGIN0
	LXI	H,PSGB4
	CALL	PRTCLA
			;ORA	A
	JZ	BEGIN4
	CALL	BEGIN0
	LXI	H,PSGB5
	CALL	PRTCLA
			;ORA	A
	JZ	BEGIN4
	CALL	BEGIN0
	LXI	H,PSGB6
	CALL	PRTCLA
			;ORA	A
	JZ	BEGIN4
	CALL	BEGIN0
	JMP	BEGIN4
;------------------------
PRTCLA	MOV	A,M
	ORA	A	;IS A PORT AVAIL?
	RZ		;NO PORT#
	LXI	D,PORT2	;POINT TO PORT2
	STAX	D	;STORE PORT2#
	INX	H	;LOAD PORT#
	MOV	A,M
	DCX	D	;POINT TO PORT
	STAX	D	;STORE PORT#
	RET
;------------------------
BEGIN0	MVI	C,0FH	;STEP TO PSGRx+14
	DAD	B	;ADD THE 15 TO HL
	MVI	C,03H	;PUT COUNTER IN C
BEGIN1	MOV	A,M	;CHECK FOR ZERO IN PSGRx 14/17/20
	MOV	E,A
	INX	H	;POINT TO COARSE BYTE
	MOV	D,M
	ORA	D	;IS THE TIME UP?
	JNZ	BEGIN3
	MVI	B,1	;SET B TO INDICATE AVAIL CH
	JMP	BEGIN2
BEGIN3	DCX	D	;DEC COUNT AND RETURN
	MOV	M,D	;IT TO MEMORYŠ	DCX	H	;SEND FINE BYTE
	MOV	M,E
	INX	H	;POINT TO COARSE BYTE
	MOV	A,E
	ORA	D
	JNZ	BEGIN2	;NO CHANGE NECESSARY
		;CHECK FOR TIE BYTE
	INX	H	;POINT TO TIE BYTE
	MOV	A,M
	DCX	H
	ORA	A	;IS IT ZERO?
	JNZ	BEGIN2	;IF NOT, PLAY ON
	
		;SEND 0 TO THIS CHANNEL AMPLITUDE
	MVI	A,11	;CH A AMP REGISTER + 3
	SUB	C	;SUBTRACT COUNTER
	PUSH	B
	PUSH	H
	LXI	H,PORT
	MOV	C,M
	CALL	OUTC	;Z80 OP OUT A,(C)
	XRA	A	;CLEAR A
	LXI	H,PORT2
	MOV	C,M
	CALL	OUTC	;SEND ZERO
	POP	H
	POP	B
BEGIN2	DCR	C
	RZ		;FINISHED THIS PSG
	INX	H
	INX	H	;POINT TO NEXT CHANNEL
	JMP	BEGIN1	;DO IT AGAIN
BEGIN4	DCR	B	;CHECK FOR 1 IN B (CH AVAIL)
	JM	BEGIN5
	CALL	RDFIL
BEGIN5	MVI	C,0BH
	CALL	BDOS	;IS CONSOLE READY?
	ORA	A
	JZ	BEGIN6	;NO CHARACTER READY
	MVI	C,01H
	CALL	BDOS
	CPI	'Q'	;PRESS Q TO QUIT
	JZ	NDPLA	;WRITE 0 AMP TO PSGS AND END
	CPI	'P'
	JZ	STRTHR
	CPI	'T'
	JZ	NEWTMP
	CPI	'S'
	JZ	CLGTFL
BEGIN6	LXI	H,TICK
	MOV	A,M	;CHECK FOR TIMER CHANGE
	LXI	H,TIMER
	CMP	M
	JZ	BEGIN5	;NO CHANGE SO WAIT
	JMP	BEGINŠ;-----------------------
START	CALL	SETUP	
	;SET UP PSG REGISTERS FOR PLAYING
	MVI	A,7	;PSG ENABLE
	CALL	OUTP
	MVI	A,370Q	;ENABLE TONES ONLY
	CALL	OUTP2
	MVI	A,11	;ENVELOPE PERIOD
	CALL	OUTP
	MVI	A,00	;FILL FINE VALUE
	CALL	OUTP2
	MVI	A,14
	CALL	OUTP	;COARSE VALUE
	MVI	A,35H
	CALL	OUTP2	;
	MVI	A,13	;ENVELOPE REGIS
	CALL	OUTP	
	MVI	A,09	;ENVELOPE SHAPE
	CALL	OUTP2
		;SET FILE POINTER (FILPT)
	LXI	H,MUSICB-1
	CALL	SVFLPT
	CALL	CLS
	CALL	CURS
	LXI	D,INSTR1
	MVI	C,PSTRING
	CALL	BDOS
	CALL	GTFLPT
	CALL	PRNT
	RET
;------------------------
OPTION	
	RET
;--------------------------
PLAY	LXI	H,CHNL	;GET CHANNEL A,B,OR C
	MOV	A,M
	LXI	H,REGIS	;GET BASE OF REGISTER IN HL
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG
	SUI	'A'	;CHANGE TO BINARY CHANNEL
	;	PLAY NOTE WITH HL POINT TO PSGR#
	;	CHANEL IN E IN FORM OF A=0H, B=1H, C=2H
	PUSH 	H
	MOV	E,A	;SAVE CHANEL IN E
	ORA	A	;CLEAR CARRY BIT
	RAL		;MULTIPLY BY TWO
	CALL	OUTP	;SEND REGISTER FOR FINE TUNE
	PUSH	PSW	;SAVE REGISTER POINTER
PLAY1	DCR	A	;CHECK CHANEL
	JM	PLAY2	;CHANEL SET
	INX	H	;IF NOT INCREMENT HL
	JMP	PLAY1	;CHECK IF FINISHED
PLAY2	MOV	A,M	;PUT FINE TUNE IN ACUMŠ	CALL	OUTP2	;SEND IT
	POP	PSW	;GET FINE TUNE POINTER
	INR	A
	CALL	OUTP	;SEND IT TO PSG
	INX	H
	MOV	A,M	;GET COARSE BYTE
	CALL	OUTP2	;SEND TO PSG
	MOV	A,E	;PUT CHANEL IN A
	ADI	8	;POINT TO AMPLITUDE REGISTER
	CALL	OUTP	
	POP	H
;-------
	PUSH	H	;FOR SENDING ENVELOPE
;-------
	ADD	L
	MOV	L,A
	JNC	PLAY3
	INR	H
PLAY3	MOV	A,M
	CALL	OUTP2
;-------------------------------
	ORI	10H	;SEND UPDATE TO PSG ENVELOPE
			;IF MODE CTL SELECTED AND
			;CH #A BEING PLAYED
	POP	H	;RESTORE PSGRx POINTER
	RZ		;RTN IF MODE CONTROL NOT USED

	XCHG		;SAVE H
	LXI	H,CHNL
	MVI	A,'A'
	CMP	M	;CHECK FOR ZERO	(CH #A)
	RNZ		;IF NOT (CH #A) RETURN
	XCHG	
	MVI	A,13	;SET FOR ENVELOPE CONT
	CALL	OUTP
	CALL	ADD6
	CALL	ADD6
	INX	H	;POINT TO PSGRx+13
	MOV	A,M	;GET MODE
	CALL	OUTP2	;SEND TO PORT2
;--------------------------------
	RET
;-----------------------------------
NDPLA			;END PLAY AND QUIT
	LXI	H,NDMSG
	CALL	SVFLPT
	JMP	BEGIN
NDMSG	DB	'\A,R,3,0\B,R,3,0\C,R,3,0\$'
	DB	'2A,R,3,0\2B,R,3,0\2C,R,3,0\'
	DB	'3A,R,3,0\3B,R,3,0\3C,R,3,0\'
	DB	'4A,R,3,0\4B,R,3,0\4C,R,3,0\'
	DB	'5A,R,3,0\5B,R,3,0\5C,R,3,0\'
	DB	'6A,R,3,0\6B,R,3,0\6C,R,3,0\$'
;-----------------------------------
OUTP	PUSH	HŠ	PUSH	B
	LXI	H,PORT	;LOAD ADDRESS OF PORT
	MOV	C,M	;GET PORT ADDRESS INTO C
	CALL	OUTC	;SEND TO PORT
	POP	B
	POP	H
	RET
OUTP2	PUSH	H
	PUSH	B
	LXI	H,PORT2
	MOV	C,M
	CALL	OUTC
	POP	B
	POP	H
	RET
	DS	1BH
;------------------------
GTPRT			;GET PORTS @ PUT INTO PORT AND PORT2
			;PSG# IN A IN FORM OF "1, 2, 3" ETC.
	PUSH	H
	PUSH	D
	SUI	'1'
	INR	A
	JM	GTPRT4
	CPI	7
	JP	GTPRT4
	LXI	H,PSGA1
	LXI	D,20H
GTPRT1	DCR	A	;POINT TO PSGAx ADDRESS
	JZ	GTPRT3
	DAD	D
	JMP	GTPRT1
GTPRT3	MOV	A,M
	PUSH	H	;SAVE PSGAx ADDRESS
	LXI	H,PORT	
	MOV	M,A	;PUT PORT# IN "PORT"
	POP	H
	PUSH	H	;GET PSGAx ADDRESS
	DCX	H	;PSGBx ADDRESS
	MOV	A,M
	LXI	H,PORT2
	MOV	M,A	;PUT PORT+2 IN PORT2
	POP	D	;ADDRESS OF PORT2
	INX	D	;ADDRESS OF PSGR#
	LXI	H,REGIS	;BASE OF PSG REGISTER
	MOV	M,E
	INX	H
	MOV	M,D
	POP	D
	POP	H
	RET
GTPRT4	POP	D
	POP	H
	JMP	RDFIL1
;ŠERROR	CALL	CLS	;CLEAR THE SCREEN
	LXI	D,ERR
	CALL	PRINT
	JMP	00H
ERR	DB	CR,LF,HT,'You have encountered a non-recoverable'
	DB	' program error.',CR,LF,'$'
;
;
GETFIL	LXI	D,GETP	;MUSIC INPUT
	MVI	C,PSTRING	;PRINT STRING
	CALL	BDOS

	CALL	GTFIL8	;LOAD BUFFER WITH SPACES
	LXI	H,MUSICB
	LXI	D,FCB
	LXI	B,12
	CALL	LDIR
	LXI	D,MUSICB
GTFIL0	MVI	A,0EH	;14 IS MAX NUMBER CHAR
	STAX	D
	MVI	C,0AH
	CALL	BDOS
MAKCAP	LXI	H,MUSICB+1	;CONVERT TO CAPS
	MVI	C,0EH
	MVI	B,'a'
MKCAP1	MOV	A,M	;GET BYTE
	CMP	B	;IS IT LESS THAN A?
	JC	MKCAP2
	SUI	20H	;CONVERT TO UP CASE
	MOV	M,A	;SAVE IT
MKCAP2	INX	H
	DCR	C
	JNZ	MKCAP1
GTFILA	LXI	H,MUSICB+3	;POINT TO 2ND BYTE
	LXI	D,FCB	;POINT TO FCB DESTINATION
	MOV	A,M
	CPI	':'	;IS THE 1ST BYTE DISK DRIVE
	JNZ	GTFIL1
	DCX	H	;POINT TO DISK IN BUFFER
	MOV	A,M	;PUT IN A
	SUI	40H
	CPI	1
	JM	ERROR	;CANT BE LESS THAN THIS
	XCHG		;POINT TO FCB WITH HL
	MOV	M,A	;LOAD DISK DRIVE
	XCHG
	INX	D	;POINT TO FCB+1 (BEGIN NAME)
	LXI	H,MUSICB+4	;POINT TO NAME IN BUFF
	JMP	GTFIL2
GTFIL1	LXI	H,FCB	;1ST BYTE NOT DISK NAME
	MVI	A,0
	MOV	M,A	;PUT 0 IN FCB+0
	XCHG		;POINT TO FCB WITH DE
	INX	D	;POINT TO FCB+1
	LXI	H,MUSICB+2	;POINT TO NAME IN BUFFŠGTFIL2	LXI	B,08H	;COUNT FOR XFR
	MOV	A,M
	CPI	'.'
	JZ	GTFIL7
	CPI	SPC
	JZ	GTFILB
	CALL	LDI
	MOV	A,C
	ORA	A
	JNZ	GTFIL2+2
GTFIL5	LXI	B,03H	;COUNTER
	MOV	A,M
	CPI	'.'
	JZ	GTFIL7
GTFIL6		;
	CALL	LDI	;SEND IT
	MOV	A,C
	ORA	A	;CHECK FOR 0
	JNZ	GTFIL6	;PLAY IT AGAIN SAM
	XCHG
GTFILB	LXI	H,FCB+12	;POINT TO FCB+12
	MVI	M,0	;SEND ZEROS TO 
	INX	H	;FCB+12,13, AND 14
	MVI	M,0
	INX	H
	MVI	M,0
	RET		;FINISHED

GTFIL7	INX	H	;POINT TO 1 PAST '.'
	LXI	D,FCB+9	;POINT TO 1ST EXT BYTE
	;GET IST EXT CHR IN A
	JMP	GTFIL5
GTFIL8	LXI	H,MUSICB+11H	;FILL BUFF WITH SPACES
	MVI	C,0FH
GTFIL9	MVI	M,SPC	;LOAD SPACE IN BUFF
	DCX	H
	DCR	C	;COUNTER
	JNZ	GTFIL9
	XCHG		;POINT TO BUFF WITH DE
	RET
GETP	DB	CR,LF,HT,HT,'Type the name of the music file.'
	DB	CR,LF,LF,LF,HT,HT,HT,HT,'$'
;--------------------------
LOADFIL		;OPEN THE FILE IN FCB AND LOAD IT INTO
		;MEMORY BUFFER AT ADDRESS MUSICB.
	LXI	D,OPSTR
	CALL	PRINT
	LXI	D,FCB
	MVI	C,15	;OPEN FILE FCN
	CALL	BDOS
	ADI	0FH	;CHECK IF A=255
	JC	OPNER	;CAN NOT OPEN
	LXI	H,FCB+32	;CURRENT RECORD#
	MVI	M,0	;SET TO ZERO
	CALL	CLSŠ	CALL	CURS
	LXI	D,LDSTR
	CALL	PRINT
	JMP	LDFIL1
OPSTR	DB	HT,HT,'Opening file.',CR,LF,'$'
LDSTR	DB	HT,HT,'Loading music program.',CR,LF,'$'
OPNERS	DB	HT,HT,'UNABLE TO OPEN FILE AS ENTERED.',CR,LF,'$'
CLSTR	DB	HT,HT,'Closing file.',CR,LF,ES
OPNER	LXI	D,OPNERS
	CALL	PRINT
	POP	H	;RETURN INST TO 'CALL LOADFIL'
	JMP	CLGTFL	;GO TRY AGAIN (GETFIL)
LDFIL1	LXI	D,MUSICB	;SET DMA ADDRESS
	MVI	C,1AH
	CALL	BDOS
LDFIL2	LXI	D,FCB	;READ SEQUENTIAL INTO BUFF
	MVI	C,14H	;20
	CALL	BDOS
	ORA	A	;IS A 0 RETURNED IN A?
	JZ	LDFIL3	;AINT FINISHED YET
	LXI	H,LDFIL1+1	;PREP FOR NEXT TIME
	LXI	D,MUSICB	;DMA ADDRESS
	MOV	M,E
	INX	H
	MOV	M,D
	CALL	CLS
	CALL	CURS
	LXI	D,CLSTR
	CALL	PRINT
	LXI	D,FCB	;CLOSE FILE
	MVI	C,16
	CALL	BDOS
	CALL	CLS
	CALL	CURS
	RET		;FINISHED
LDFIL3	LXI	H,LDFIL1+1	;GET LAST DMA ADDRESS
	MOV	A,M
	INX	H
	MOV	D,M
	ADI	128	;ADD SECTOR READ LENGTH TO DMA
	MOV	E,A
	JNC	LDFIL4
	INR	D	;ADD CARRY TO HIGH BIT
LDFIL4	LXI	H,NDBUF	;TOP OF BUFFER
	MOV	M,E
	INX	H
	MOV	M,D
	LXI	H,LDFIL1+1	;DMA ADDRESS
	MOV	M,E
	INX	H
	MOV	M,D
	JMP	LDFIL1	;SET DMA ADDRESS @ GO AGAIN
;---------------------------
;
TEMPO			;CALL FOR AND SET TEMPO COUNTERŠ			;FOR ONE BEAT
	CALL	CLS
	CALL	CURS			 
;TEMPO+6
	LXI	D,TEMP1	;ADDRESS OF PROMPT STRING
	MVI	C,09H	
	CALL	BDOS	;PRINT IT
TEMPO1	LXI	H,TMPO+1	;POINT TO STORE AREA
	LXI	B,03H
TEMPO2	MOV	A,M
	CALL	PUTCHR	;PRINT IT
	INX	H
	DCR	C	;PRINTED 3?
	JNZ	TEMPO2
	LXI	H,TMPO+1	;POINT TO 1ST CHAR
	MVI	C,3	;3 TIMES
TEMPO3	MVI	A,BS	;BACKSPACE
	CALL	PUTCHR	;SEND BACKSPACE
	DCR	C
	JNZ	TEMPO3	;3 TIMES?
;SETS CURSOR AT HUNDREDS POSITION
	MVI	C,3
TEMPO4	CALL	GETNUM
	ORA	A	;IS IT A ZERO?
	JZ	TEMPO5	;FINISHED INPUT?
	MOV	M,A
	INX	H
	DCR	C
	JNZ	TEMPO4	;GET ANOTHER CHAR
;FINISHED INPUT - CALCULATE @ PUT IN TMPO
TEMPO5	LXI	H,TMPO+1
	CALL	TEMPOA
	XRA	A
TEMPO6	DCR	C
	JM	TEMPO7
	ADI	100
	JC	TMPOER	;MAX VAL=256
	JMP	TEMPO6
TEMPO7	INX	H	;POINT TO TENS
	CALL	TEMPOA
TEMPO8	DCR	C
	JM	TEMPO9
	ADI	10
	JC	TMPOER	;MAX VAL=256
	JMP	TEMPO8
TEMPO9	INX	H	;POINT TO ONES
	CALL	TEMPOA
TMPO10	DCR	C
	JM	TMPO11	;VALUE COMPLETE
	INR	A
	CPI	255
	JZ	TMPO11	;VALUE = 255
	JMP	TMPO10
TMPO11 LXI	H,TMPO
	MOV	M,A	;STORE ITŠ	RET
TEMPOA	PUSH	PSW
	MOV	A,M	;GET NUMBER
	SUI	30H	;CONVERT TO BINARY
	JM	ERROR	;NOT ASCII NUMBER
	MOV	C,A
	POP	PSW
	RET
TMPOER	LXI	H,PSTAT	;PROGRAM STATUS
	MOV	A,M
	ANI	80H	;CHECK BIT 7
	RNZ		;DO NOTHING DURING PLAY
	CALL	CLS	;CLEAR SCREEN
	LXI	D,TERMSG
	MVI	C,PSTRING
	CALL	BDOS
	JMP	TEMPO+3
TERMSG	DB	CR,LF,LF,LF,HT,HT,'The tempo must be a '
	DB	'number between 30 and 255.',CR,LF
	DB	HT,HT,'Try one more time.$'  
TEMP1	DB	HT,HT,'Enter the tempo of the desired song.'
	DB	CR,LF,HT,HT,HT,HT,ES
;
BEAT0	LXI	H,TMPO	;GET TEMPO
	MOV	A,M
	CPI	30H
	JNC	BEAT1
	MVI	A,30H
BEAT1	LXI	H,7530H	;2MS INT/MIN (30000)
	LXI	D,0000H	;ZERO THE COUNTER
	MVI	B,0	;CLEAR D
	MOV	C,A	;SUBTRACTOR IN DE
BEAT2		;DEVIDE BY SUCCESSIVE SUBTRACTION
	CALL	SBCHB	;Z80 16BIT SBC HL,BC
	JC	BEAT3	;HL<0
	INX	D	;SUBTRACTED (BC) TIMES
			;WITHOUT CARRY
	JMP	BEAT2	;DO AGAIN
BEAT3	LXI	H,BEAT	;# INTERRUPTS/BEAT LOADED
	MOV	M,E
	INX	H
	MOV	M,D
	CALL	HOLNOT
	RET
;
GETNUM			;GET A NUMBER
	CALL	GETCHR
	CPI	08H	;DONT ACCEPT BACKSPACE
	JZ	GTNUM3
	CPI	127	;DONT ACCEPT DELETE
	JZ	GTNUM3
	CPI	48	;IS IT LESS THAN A NUMBER?
	JM	GTNUM1
	CPI	58	;IS IT GREATER THAN A NUMBER?
	JP	GTNUM1Š	RET
GTNUM1	CPI	CR
	JZ	GTNUM2
	MVI	A,127	;SEND A DELETE CHARACTER
	CALL	PUTCHR	;SEND IT TO CONSOLE
	JMP	GETNUM	;TRY AGAIN
GTNUM2	MVI	A,0
	RET
GTNUM3	MVI	A,20H	;SEND SPACE
	CALL	PUTCHR
	JMP	GETNUM
;----------------
TRIAD	;START WITH VALUE IN DE FOR BEAT TIME 
	;RETURN 2/3 VALUE IN DE FOR TRIAD NOTES
	CALL	SVFLPT
	XCHG	;HL=DE
	XRA	A	;CLEAR PSW
	MOV	D,A
	MOV	E,A	;CLEAR DE
	MOV	A,L	;HL=HL*2
	RAL
	MOV	L,A
	MOV	A,H
	RAL
	MOV	H,A
	LXI	B,03H	;DEVIDE BY 3
TRIAD1	CALL	SBCHB	;16 BIT SUBTRACT
	RC		;QUIT HL<0
	INX	D	;ADD 1 TO DE
	JMP	TRIAD1	;DO AGAIN
;---------------
;PUT HOLE NOTE VALUE IN TIME1
HOLNOT	LXI	H,BEAT	;GET 1 BEAT COUNT
	MOV	E,M	;PUT IN DE
	INX	H
	MOV	D,M
	LXI	H,BNOTE	;GET BEAT NOTE
	MOV	B,M	;PUT IN B
	MOV	A,M
	CPI	'1'
	JZ	HLNT1	;DONE
	CALL	SHFTDL	;SHIFT DE LEFT
	CPI	'2'
	JZ	HLNT1
	CALL	SHFTDL	;NOT HALF NOTE
	CPI	'4'
	JZ	HLNT1
	CALL	SHFTDL	;NOT QUARTER NOTE
	CPI	'8'
	JZ	HLNT1
	CALL	SHFTDL	;NOT EIGHTH NOTE
	CPI	'6'
	JZ	HLNT1
	CALL	SHFTDL	;NOT SIXTEENTH
	CPI	'3'Š	JZ	HLNT1
	JMP	ERROR
HLNT1	;PUT VALUE IN TIME1
	LXI	H,TIME1
	MOV	M,E
	INX	H
	MOV	M,D
	RET
;------------------
SHFTDL	;SHIFT DE LEFT 1 BIT
	PUSH	PSW
	XRA	A	;CLEAR CARRY
	MOV	A,E
	RAL
	MOV	E,A
	MOV	A,D
	RAL
	MOV	D,A
	POP	PSW
	RET
;----------------
NOTTIM	;GET SIXTEEN BIT COUNT FOR NOTE
	;NOTE VALUE IN 'A' RTN VAL IN DE
	LXI	H,TIME1
	MOV	B,A	;SAVE ASCII NOTE
	MOV	E,M	;GET WHOLE NOTE VAL
	INX	H
	MOV	D,M
	CPI	'1'
	RZ	;NO CHANGE NECESSARY
	CALL	SHFTDR	;SHIFT DE RIGHT
	CPI	'2'
	RZ
	CALL	SHFTDR
	CPI	'4'
	RZ
	CALL	SHFTDR
	CPI	'8'
	RZ
	CALL	SHFTDR
	CPI	'6'
	RZ
	CALL	SHFTDR
	RET
;-------------------------
RDFIL	;READS NEXT FILE ENTRY BEGINNING AT
	;LOCATION OF FILE POINTER 'FILPT'
	CALL	GTFLPT
	MOV	A,M
	CPI	CR
	JNZ	RDFIL0
	INX	H
	INX	H
	MOV	A,M
RDFIL0	CPI	'$'	;END?Š	JZ	QUIT
	CPI	','	;IF ',' THEN SET PSG=1
	JZ	NOTE0	;AND CHANNEL=A
	CPI	27H	;IS IT QUOTE?
	JZ	PRNT	;PRINT IT
	CPI	'\'
	JZ	RDFIL2
	CPI	'1'	;IS IT AS LARGE AS A NUMBER?
	JM	RDFIL2
	CPI	'6'+1	;6 OR LESS?
	JM	NOTE1	;SET CHIP=(A)
			;PLAY NOTE
	CPI	'A'
	JM	RDFIL2
	CPI	'C'+1	;C OR LESS?
	JM	NOTE0	;SET CHIP=1
			;PLAY NOTE
	CPI	'R'
	JZ	REST0	;REST
	CPI	'V'
	JZ	VOICE0	;PSG VOICE
	CPI	'M'
	JZ	MSUR0	;MEASURE
	CPI	'T'
	JZ	TMP0	;TEMPO
	CPI	'J'
	JZ	JUMP0	;JUMP TO MEASURE#
	CPI	'K'
	JZ	JUMP8
	CPI	'L'
	JZ	JUMP9
	CPI	'N'
	JZ	JUMP10
RDFIL1	CALL	GTFLPT
RDFIL2	MOV	A,M
	CPI	'\'
	JZ	RDFIL3
	INX	H
	JMP	RDFIL2	;THROW AWAY ALL TO '\'
RDFIL3	INX	H
	MOV	A,M
	CPI	'\'
	JZ	RDFIL4
	CALL	SVFLPT
	RET	;GO BACK TO MAIN PROGRAM
RDFIL4	
	DCX	H
	CALL	SVFLPT
	RET
;--------------------------
TMP0	;SETS NEW TEMPO WRITTEN INTO
	;MUSIC WITH HL POINTING TO THE
	;'T' FOLLOWING A '\'
	INX	H
	MOV	A,MŠ	CPI	' '
	JZ	TMP0
	CPI	','
	JZ	TMP0
	CPI	'\'
	JZ	RDFIL1	;GO TO NEXT FILE ENTRY
	XCHG
	LXI	H,TMPO+1	;POINT TO BUFFER
	MOV	M,A
	XCHG
	INX	H
	MOV	A,M	;GET 2ND BYTE
	CPI	'\'
	JZ	RDFIL1	;ABORT 
	XCHG	
	INX	H	;LOAD 2ND BYTE
	MOV	M,A
	XCHG
	INX	H
	MOV	A,M	;GET 3RD BYTE
	CPI	'\'
	CZ	TMP1	;LESS THAN 100
	XCHG
	INX	H
	MOV	M,A	;LOAD LAST BYTE
	CALL	TEMPO5	;COMPUTE 'TEMPO'
	CALL	BEAT0	;COMPUTE # INT/BEAT
			;LOAD INTO BEAT
			;PUT WHOLE NOTE
			;VALUE INTO TIME1
	JMP	RDFIL1	;FINISHED
	DS	20H
TMP1	XCHG		;POINT TO TMPO+2 (TENS)
	INX	H	;POINT TO TMPO+3 (ONES)
	MOV	M,A	;LOAD TENS
	LXI	H,TMPO+1	;GET 1ST BYTE (TENS)
	MOV	A,M
	INX	H	;PUT IT IN TENS POSITION
	MOV	M,A	;WHERE IT BELONGS
	MVI	A,'0'	;
	DCX	H
	MOV	M,A	;PUT ZERO IN TMPO+1 (HUNDREDS)
	INX	H	;POINT TO TENS
	XCHG		;CONFIGUR FOR RETURN
	RET
;----------------------
GTFLPT	;GET FILE POINTER (FILPT) WITH HL
	;RETURNED WITH VALUE OF POINTER
	PUSH	D
	LXI	H,FILPT
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG
	POP	DŠ	RET
;-----------------------
SVFLPT	;SAVE FILE POINTER (FILPT) WITH HL
	;POINTING TO LOCATION TO BE SAVED
	PUSH	PSW
	PUSH	B
	PUSH	D
	XCHG
	LXI	H,FILPT
	MOV	M,E
	INX	H
	MOV	M,D
	XCHG
	POP	D
	POP	B
	POP	PSW
	RET
;--------------------------
	DS	30H
NOTE0	;CHIP = #1
	DCX	H	;SET HL AT ONE
		;PRIOR TO CHANNEL #
	MVI	A,'1'	;PSG #1
NOTE1	CALL	SVFLPT	;SAVE FILE POINTER
	CALL	GTPRT	;SET PORT NUMBER
	CALL	GTFLPT	;POINT TO CHANNEL #
	INX	H
	MOV	A,M
	CPI	','
	JNZ	NOTE3
NOTE2	DCX	H	;SET POINTER JUST PRIOR
			;TO NOTE VALUE
	MVI	A,'A'	;LOAD DEFAULT CHANNEL #
NOTE3	XCHG
	JMP	TIMEUP	;CHECK IF CURRENT NOTE
			;IS FINISHED
NOTE3A	MOV	M,A
	XCHG
NOTE4	INX	H	;POINT TO CHANNEL# +1
	MVI	C,0
	MOV	A,M
	CPI	','	;IS IT NOTE VALUE?
	JZ	NOTE4
	;GET AND PROGRAM CURRENT PSG CHANNEL
	;FOR TONE COUNT
	CPI	'\'
	JZ	RDFIL1	;ABORT
	CPI	'0'
	JM	RDFIL1	;ABORT
	CPI	'8'
	JP	NOTE6	;NOT A NUMBER
	SUI	'0'	;CONVERT TO BINARY
	MOV	C,A	;OCTAVE COUNTER IN C
NOTE5	INX	H	;LOOK AT NOTE
	MOV	A,MŠNOTE6	CALL	SVFLPT
	CPI	'R'	;IS IT A REST?
	JZ	NOTER
	CPI	'A'
	JM	RDFIL1	;ABORT-NOT NOTE
	LXI	H,CNOTE	;POINT TO "C" VALUE
	CPI	'C'
	JZ	NOTE7	;LOAD VALUE
	CALL	ADD6	;INX H 6 TIMES
	CPI	'D'
	JZ	NOTE7
	CALL	ADD6
	CPI	'E'
	JZ	NOTE7
	CALL	ADD6
	CPI	'F'
	JZ	NOTE7
	CALL	ADD6
	CPI	'G'
	JZ	NOTE7
	CALL	ADD6
	CPI	'A'
	JZ	NOTE7
	CALL	ADD6
	CPI	'B'
	JZ	NOTE7
	JMP	RDFIL1	;ABORT NOT NOTE
NOTE7	;CHECK IF SHARP OR FLAT
	XCHG	;SAVE NOTE ADDRESS IN DE
	CALL	GTFLPT
	INX	H	;POINT TO NOTE + 1
	MOV	A,M
	CPI	'#'	;CHECK IF SHARP
	JZ	SHARP
	CPI	'+'
	JZ	SHARP
	CPI	'S'
	JZ	SHARP
	CPI	'F'	;CHECK IF FLAT
	JZ	FLAT
	CPI	'-'
	JZ	FLAT
	CPI	'B'
	JZ	FLAT
	DCX	H	;POINT TO NOTE
NOTE8	;GET TONE VAL AND CONVERT
	;FOR PROPER OCTAVE
	CALL	SVFLPT	;SAVE FILE POINTER
	XCHG	;POINT TO NOTE TABLE
	MOV	E,M	;LOAD VAL IN DE
	INX	H
	MOV	D,M
NOTE9	DCR	C	;DEC OCTAVE COUNTER
	JM	NOTE10
	MOV	A,E	;SAVE LSBŠ	CALL	SHFTDR	;SHIFT DE RIGHT
	JMP	NOTE9	;
NOTE10	ANI	01H	;IS RT BIT A '1'?
	JZ	NOTE11
	INX	D	;IF SO ADD 1 TO TONE
NOTE11		;PUT TONE VAL IN PSG FILE
	LXI	H,CHNL
	MOV	A,M	;PUT CHANNEL IN A
	PUSH	D
	LXI	H,REGIS
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG
	POP	D
	CPI	'A'	;IS IT 'A' CHANNEL?
	JZ	NOTE12	
	INX	H	;IF NOT POINT TO B
	INX	H
	CPI	'B'	;'B' CHANNEL?
	JZ	NOTE12
	INX	H	;IF NOT POINT TO C
	INX	H
NOTE12	MOV	M,E	;STORE LOW ORDER BYTE
	INX	H
	MOV	M,D	;STORE HIGH ORDER BYTE
NOTET	;GET NOTE TIME VALUE AND SET IN PSG FILE AS
	;A 16 BIT VALUE AT PSGRx+
	;A CHANNEL	14
	;B CHANNEL	17
	;C CHANNEL	20
	CALL	GTFLPT	;GET FILE POINTER
	INX	H	;POINT TO TIMEVALUE
	MOV	A,M
	CPI	','
	JNZ	NOTT1	;
	INX	H
NOTT1	MOV	A,M
	CPI	','	;IS IT SEPERATOR?
	JZ	NOTETA
	CPI	'/'	;IS IT SEPERATOR?
	JNZ	NOTET0
NOTETA	MVI	A,'4'	;DEFAULT TO QTR NOTE
	DCX	H	;POINT BACK ONE
	CALL	SVFLPT
	JMP	NOTET1
NOTET0	CPI	'1'
	JM	RDFIL1	;ERROR-ABORT
	CPI	'9'
	JP	RDFIL1	;ERROR
	CPI	'5'
	JZ	RDFIL1	;ERROR
	CPI	'7'
	JZ	RDFIL1	;ERROR
NOTET1	CALL	SVFLPT	;VALID NOTE DESIGNATOR IN A	Š	CALL	NOTTIM	;GET NOTE TIME
	CALL	GTFLPT	;GET FILE POINTER
	INX	H
	MOV	A,M
	CPI	','
	JZ	NOTETB
	CALL	SVFLPT	;SAVE POINTER
	CPI	'.'	;DOTTED NOTE
	CZ	DOTNOT	;DE=DE*1.5
	CPI	'T'	;TRIAD
	CZ	TRIAD	;DE=DE*2/3
	;PUT NOTE VALUE IN DE INTO REGISTER 
NOTETB	LXI	H,CHNL	;GET CHANNEL 
	PUSH	D	;SAVE NOTE DWELL
	MOV	C,M	;GET CHANNEL COUNTER
	LXI	H,REGIS	;POINT TO REGISTER
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG	;POINTING TO PSGRx
	LXI	D,14	;ADD OFFSET TO CHANNEL A
	DAD	D	;POINTING TO PSGRx+14
	POP	D	;RESTORE VALUE
	MOV	A,C
	CPI	'A'	;IS IT CHANNEL 'A'
	JZ	NOTET2
	CALL	ADD3
	CPI	'B'
	JZ	NOTET2
	CALL	ADD3
NOTET2	MOV	M,E	;PUT LOW BIT INTO
	INX	H	;REGISTER FILE
	MOV	M,D	;PUT HI BIT
	PUSH	H	;SAVE PSGRx+ LOCATION
		;CHECK FOR AND SET TIE BYTE
	CALL	GTFLPT
	INX	H	;LOOK AT NEXT BYTE
	MOV	A,M	;FOR TIE '-'
	CPI	'-'
	MVI	A,1
	JZ	NOTTA
	DCX	H
	XRA	A	;SEND 0 TO TIE BYTE
NOTTA	CALL	SVFLPT
	POP	H
	INX	H
	MOV	M,A	;SEND IT
	CALL	GTFLPT
	JMP	NOTE13
NOTER	CALL	SVFLPT	;REST - CONTINUE
	JMP	NOTET
		;GET AND STORE AMPLITUDE VALUE
NOTE13	INX	H	;POINT TO NOTE+1
	MOV	A,M
	CPI	','Š	JZ	NOTE13	;STEP AHEAD ONE
	CPI	'\'
	JZ	NOTE15	;USE MODE CONTROL
	CPI	'0'	;LESS THAN ZERO?
	JM	NOTE16	;USE DEFAULT
	CPI	'9'+1	;IS IT NUMBER?
	JM	NOTE14	;USE THE NUMBER
	CPI	'A'	;LESS THAN LETTER?
	JM	NOTE16	;WRONG, USE DEFAULT
	CPI	'F'+1
	JP	NOTE15	;>F SO SET MODE CONTROL
	SUI	55	;CONVERT ASCII A-F
			;TO HEX
	JMP	NOTE18
NOTE14		;SET NUMBER VALUE IN A
	SUI	'0'	;30H
	JMP	NOTE18
NOTE15	MVI	A,1FH	;SET MODE CONTROL
	JMP	NOTE18
NOTE16	MVI	A,08H	;USE DEFAULT VALUE

NOTE18			;PUT VALUE IN A INTO PSG CHANNEL
	PUSH	PSW	;SAVE VALUE
	LXI	H,CHNL
	MOV	A,M	;GET CHANNEL NUMBER
	SUI	'A'	;CONVERT TO BINARY
	MOV	C,A	;STORE IN COUNTER
	LXI	H,REGIS
	MOV	E,M	;GET LOW BYTE
	INX	H
	MOV	D,M	;GET HI BYTE
	XCHG		;POINT TO REG #
	CALL	ADD6	;POINT TO REG # +6
	INX	H
NOTE17	INX	H	;POINT TO REG # +8
	DCR	C
	JP	NOTE17
	POP	PSW
	MOV	M,A	;PUT IT IN REG
	CALL	PLAY	;PLAY THE NOTE
	JMP	RDFIL1	;
;-------------------------
TIMEUP	PUSH	PSW	;SAVE ENVIRONMENT
	PUSH	D
	LXI	H,CHNL	;GET CHANNEL
	PUSH	H	;SAVE FOR LATER
	MOV	M,A	;PUT CHANNEL IN A
	LXI	H,REGIS	;LOAD PSGRx IN DE
	MOV	E,M
	INX	H
	MOV	D,M
	LXI	H,14	;ADD OFFSET TO 1ST CHAN
	DAD	D	;TIME @ STORE IN HL
	SUI	'A'-1	;CONVERT TO BINARY 1,2,3
TMUP1	DCR	AŠ	JZ	TMUP2
	CALL	ADD3	;POINT TO NEXT CHAN TIME
	JMP	TMUP1
TMUP2	MOV	A,M	;GET FINE TIME TO GO
	INX	H
	MOV	D,M	;COARSE TIME TO GO
	ORA	D	;LAST NOTE FINISHED?
	JNZ	TMUP3	;NOT FINISHED
	POP	H
	POP	D
	POP	PSW
	JMP	NOTE3A	;GO BACK AND PLAY
TMUP3	POP	H
	POP	D
	POP	PSW
	CALL	GTFLPT
TMUP4	MOV	A,M	;SET FILPT AT '\' AND RTN
	CPI	'\'
	JZ	RDFIL2
	DCX	H
	JMP	TMUP4	;DO AGAIN
;------------------------
SETUP	;ZEROS PSG WORKING REGISTER
	LXI	H,PSGR1
	CALL	SETUP0
	LXI	H,PSGR2
	CALL	SETUP0
	LXI	H,PSGR3
	CALL	SETUP0
	LXI	H,PSGR4
	CALL	SETUP0
	LXI	H,PSGR5
	CALL	SETUP0
	LXI	H,PSGR6
	CALL	SETUP0
	RET
SETUP0	XRA	A	;CLEAR A
	MVI	C,1EH
SETUP1	MOV	M,A
	INX	H
	DCR	C
	JNZ	SETUP1
	RET
;------------------------
DOTNOT	;START WITH 16 BIT TIME VALUE IN DE
	;RETURN DE*1.5 IN DE
	CALL	SVFLPT
	PUSH	PSW
	PUSH	D	;SAVE VALUE
	CALL	SHFTDR	;DEVIDE BY TWO
	POP	H	
	MOV	A,L
	ADD	E
	MOV	E,A	;RESULT IN E
	MOV	A,HŠ	ADC	D	;ADD WITH CARRY
	MOV	D,A
	POP	PSW	;RESTORE A
	RET
;------------------------
ADD6	CALL	ADD3
	CALL	ADD3
	RET
ADD3	INX	H
	INX	H
	INX	H
	RET
;---------------------------
CFNOTE	DW	4146	;PSG TONE TABLE
CNOTE	DW	3914
CSNOTE	DW	3694
DFNOTE	DW	3694
DNOTE	DW	3487
DSNOTE	DW	3291
EFNOTE	DW	3291
ENOTE	DW	3107
ESNOTE	DW	2932	
FFNOTE	DW	3107
FNOTE	DW	2932
FSNOTE	DW	2768
GFNOTE	DW	2768
GNOTE	DW	2612
GSNOTE	DW	2466
AFNOTE	DW	2466
ANOTE	DW	2327
ASNOTE	DW	2197
BFNOTE	DW	2197
BNOTEE	DW	2073
BSNOTE	DW	1957	
;--------------------------
REST0	
	JMP	RDFIL1	
MSUR0	
	JMP	RDFIL1
;------------------------------
SHARP
	INX	D
	INX	D
	JMP	NOTE8
FLAT
	DCX	D
	DCX	D
	JMP	NOTE8
;------------------------------
VOICE0		;SEND UPDATE TO PSGRx VOICING 
	INX	H	;POINT TO PSG# IN FILE
	MOV	A,M
	CALL	SVFLPT
	CALL	GTPRT	;GET THE PORT #
	LXI	H,PORTŠ	MVI	A,0
	CMP	M
	JZ	RDFIL1	;THROW AWAY IF PORT=0
	CALL	GTFLPT
	INX	H	;POINT TO VOICE
	MOV	A,M
	SUI	'A'
	JM	RDFIL2	;WRONG VOICE SELCTED
	MOV	C,A	;AND PUT IN C
	INR	C
;---------------------------
;NUMBER OF VOICES AVAILABLE IN PROGRAM SET HERE
	CPI	26	;# OF OPTIONS AVAILABLE	
;---------------------------------
	JP	RDFIL2
	INR	A
	LXI	D,0005H	;# OF BYTES IN VOICEx
	LXI	H,VOICEA
VOICE1	DCR	C	;ADD TO SELECTED VOICE
	JZ	VOICE2
	DAD	D	;POINT TO NEXT VOICE
	JMP	VOICE1
VOICE2	;HL=VOICEx ADDRESS  SEND VOICING
	;TO PSGRx AND TO PORT
	PUSH	H	;SAVE VOICE ADDRESS
	LXI	H,REGIS	;GET PSGRx INTO DE
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG		;POINT WITH HL	
	CALL	ADD6	;POINT TO PSGRx+6
	POP	D	;GET VOICEx ADD
	XCHG		;POINT TO VOICEx IN HL
	MVI	A,6
	CALL	VOICE3
	CALL	LDI
	MVI	A,7	;SEND TO REG #7
	CALL	VOICE3
	CALL	LDI
	INX	D
	INX	D
	INX	D	;POINT TO PSGRx+11
	MVI	A,11	;SEND TO REG #11
	CALL	VOICE3
	CALL	LDI
	MVI	A,12	;SEND TO REG #12
	CALL	VOICE3
	CALL	LDI
	MVI	A,13	;SEND TO REG #13
	CALL	VOICE3
	CALL	LDI
	JMP	RDFIL1
VOICE3	CALL	OUTP	;SET UP PORT
	MOV	A,M	;GET VAL FROM VOICEx+REG #
	CALL	OUTP2	;SEND ITŠ	RET
;-----------------------------
	DS	1FH
JUMP	;MOVES FILE POINTER TO MEASURE MARKED AS THE 
	;JUMP NUMBER TO REPLAY PORTIONS ACCORDING TO
	;THE FOLLOWING  J=JUMP EACH TIME  
	;K=JUMP ONE TIME (CHANGE K TO O)
	;L=JUMP ALTERNATE TIMES (CHANGE L TO N)
	;N=DONT JUMP  (CHANGE N TO L)
JUMP0	INX	H	;POINT TO ONE PAST LETTER
	MOV	A,M
	CPI	'\'	;IS IT SEPERATOR
	JZ	RDFIL1	;ABORT
	CPI	'0'	;IS IT LESS THAN 0?
	JM	JUMP0	;TRY NEXT ONE
	CPI	'9'+1	;IS IT > 9?
	JP	RDFIL2	;NOT NUMBER OR SEPERATOR
	CALL	SVFLPT	;POINT TO FIRST NUMBER
	LXI	H,MUSICB
JUMP1	MVI	A,'\'	;FIND FIRST SEPERATOR
	CMP	M
	JZ	JUMP2
	INX	H
	JMP	JUMP1+2
JUMP2	INX	H	;FIRST AFTER \
	MVI	A,'M'	;FIND MEASURE MARKER
JUMP2A	CMP	M
	JZ	JUMP3
	INX	H
	JMP	JUMP2A
JUMP3	INX	H	;POINT TO FIRST NUMBER
	MOV	A,M	;IS IT LESS THAN 0?
	CPI	'0'
	JM	JUMP2+1
	CPI	'9'+1	;IS IT > A NUMBER
	JP	JUMP2+1	;1; 
	;NOW POINTING TO FIRST NUMBER AFTER 'M'
	XCHG	;POINT TO MUSIC BUFFER WITH D
JUMP4	CALL	GTFLPT	;BEGIN TO MATCH NUMBER
		
JUMP5	;POINT TO 1ST NUMBER
	LDAX	D	;GET BUFFER NUMBER
	CMP	M	;COMPARE WITH MEASURE #
	JZ	JUMP6
	XCHG
	JMP	JUMP2
JUMP5A	INX	H
	INX	D
	JMP	JUMP5	;CHECK NEXT NUMBER FOR MATCH
JUMP6	MVI	A,'\'	;IS IT PAST NUMBER?
	CMP	M
	JZ	JUMP7
	JMP	JUMP5A
	;LOOK FOR NEXT 'M'
JUMP7	XCHG	;POINT TO NEW FILPT WITH HŠ	CALL	SVFLPT	;PUT NEW-FOUND MEASURE 
			;INTO FILE POINTER
	JMP	RDFIL2	;RETURN
JUMP8	MVI	M,'O'
	JMP	JUMP0
JUMP9	MVI	M,'N'
	JMP	JUMP0
JUMP10	MVI	M,'L'
	JMP	RDFIL2
;-------------------------
QUIT	;END OF MUSIC FILE.
	CALL	CLS
	LXI	D,QTMSG
	MVI	C,PSTRING
	CALL	BDOS
	JMP	0000H
QTMSG	DB	LF,LF,HT,HT,'END OF MUSIC FILE',LF,LF,ES
;------------------------
SHFTDR	;SHIFT DE RIGHT 1 BIT
	PUSH	PSW
	XRA	A	;CLEAR CARRY
	MOV	A,D
	RAR
	MOV	D,A
	MOV	A,E
	RAR
	MOV	E,A
	POP	PSW
	RET

;----------------
GETCHR	PUSH	H	;READ NEXT CONSOLE CHARACTER TO A
	PUSH	D
	PUSH	B
	MVI	C,CONIN
	CALL	BDOS
	POP	B
	POP	D
	POP	H
	RET
;-------------------
PUTCHR	PUSH	H	;WRITE NEXT CONSOLE CHARACTER TO A
	PUSH	D
	PUSH	B
	MVI	C,CONOUT
	MOV	E,A	;CHARACTER TO SEND
	CALL	BDOS
	POP	B
	POP	D
	POP	H
	RET
CRLF		;SEND CARRIAGE RETURN LINE FEED
	MVI	A,CR	;CARRIAGE RETURN
	CALL	PUTCHR
	MVI	A,LF	;LINE FEEDŠ	CALL	PUTCHR
	RET
PRINT		;PRINT THE BUFFER ADDRESSED BY DE UNTIL $
	PUSH	D
	CALL	CRLF
	POP	D	;NEW LINE
	MVI	C,PSTRING
	CALL	BDOS	;PRINT THE STRING
	RET
READ		;READ THE CONSOLE STRING
	MVI	C,RSTRING
	CALL	BDOS	;GET THE STRING
	RET
;------------------------
UCCV		;UPPER CASE CONVERSION
	CPI	97	;COMPARE WITH "a"
	RM		;NOT LOWER CASE
	CPI	123	;COMPARE WITH ("z"+1)
	RP		;NOT LOWER CASE
	SUI	32	;CONVERT TO UPPER CASE
	RET
;-----------------------
CLS			;H19 CLEAR SCREEN
	PUSH	H	;SAVE ENVIRONMENT
	PUSH	B
	PUSH	D
	LXI	D,CSI	;SET ADDRESS OF CLEAR STRING
	MVI	C,09H	;PRINT STRING
	CALL	BDOS
	POP	D
	POP	B
	POP	H
	RET
CSI	DB	27,'E$'	;MSG TO CLEAR SCREEN
;-------------------------
CURS	LXI	D,STCUR	;SET CURSOR TO CENTER SCREEN 
	MVI	C,09H	;PRINT STRING FCN
	CALL	BDOS
	RET
STCUR	DB	27,'Y',40,32,'$'
	DS	40	;20 LEVEL STACK
STACK
;----------------------------------------------------------

LDI	;	MEMORY TO MEMORY TRANSFER WITH AUTO INCREMENT
	;	OF MEMORY POINTER REGISTERS AND AUTO DECREMENT
	;	OF A BYTE COUNT REGISTER PAIR (AS Z80 OP CODE)
	DI
	MOV	A,M
        STAX	D	;MEMORY TRANSFER COMPLETE 
	INX	H
	INX	D
	DCX	B
	EI
	RETŠ
;--------------------------------------------------
SBCHB		;16 BIT SUBTRACT W CARRY (SBC HL,BC)
	XRA	A
	MOV	A,L
	SUB	C	;SUB C FROM L
	MOV	L,A
	MOV	A,H
	SBB	B	;SUBTRACT WITH BORROW
			;B FROM H
	MOV	H,A
	RET
;------------------------------------------------
	;SAME AS Z80 OP CODE OF SAME NAME (SEE LDI)
LDIR	CALL	LDI
	MOV	A,C
	ORA	B
	RZ
	JMP	LDIR

;--------------------------------------------------

LDD	DI		;(Z80 OP CODE EQUIVALENT)
	MOV	A,M
	STAX	D
	DCX	H
	DCX	D
	DCX	B
	EI
	RET
;
LDDR	CALL	LDD	;(Z80 OP CODE EQUIVALENT)
	MOV	A,C
	ORA	B
	RZ
	JMP	LDDR
;---------------------------------------------
OUTC			;Z80 OP ( OUT  A,(C)  )  SENDS 
			;CONTENTS OF A TO PORT SPECIFIED
			;IN REGISTER C
	PUSH	H
	LXI	H,OUTC+6	;SELF-MODIFYING CODE
	MOV	M,C
	OUT	00H	;PORT MODIFIED
	POP	H
	RET
;------------------------
PAUSE	LXI	D,PAUS1	;STOP FOR A WHILE
	CALL	PRINT
	LXI	D,PAUS2
	CALL	READ
	RET
PAUS1	DB	CR,LF,'Enter RETURN to continue.',CR,LF,'$'
PAUS2	DS	0FH
;-------------------------ŠINSTR1	DB	HT,HT,'Press the following keys for the function desired.'
	DB	CR,LF,LF,HT,HT,'P',HT,HT,'Play again',CR,LF
	DB	HT,HT,'T',HT,HT,'Change tempo and play again',CR,LF
	DB	HT,HT,'S',HT,HT,'Play another song',CR,LF
	DB	HT,HT,'Q',HT,HT,'Quit',CR,LF,LF,HT,HT,'**************'
	DB	'*******************',CR,LF,LF,'$'
;_________________________
PRNT	INX	H	;PRINT WORDS TO TERMINAL
	MOV	A,M
	CPI	'\'	;FINISHED?
	JNZ	PRNT1
	JMP	RDFIL2
PRNT1	MVI	C,2
	MOV	E,A
	PUSH	H
	CALL	BDOS
	POP	H
	JMP	PRNT
;-------------------------
MUSICB	DS	0FH
	END