; 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