TITLE BBC BASIC (C) R.T.RUSSELL 1981-2024 NAME ('EXEC') ; ;BBC BASIC INTERPRETER - Z80 VERSION ;STATEMENT EXECUTION MODULE - "EXEC" ;(C) COPYRIGHT R.T.RUSSELL 1981-2024 ; ;THE NAME BBC BASIC IS USED WITH THE PERMISSION ;OF THE BRITISH BROADCASTING CORPORATION AND IS ;NOT TRANSFERRABLE TO A FORKED OR DERIVED WORK. ; ;VERSION 2.1, 22-01-1984 ;VERSION 3.1, 11-06-1987 ;VERSION 5.0, 19-05-2024 ; GLOBAL XEQ GLOBAL RUN0 GLOBAL CHAIN0 GLOBAL CHECK GLOBAL MUL16 GLOBAL X14OR5 GLOBAL TERMQ GLOBAL STOREN GLOBAL STORE4 GLOBAL STORE5 GLOBAL STACCS GLOBAL SPACES GLOBAL FN GLOBAL USR GLOBAL ESCAPE GLOBAL SYNTAX GLOBAL CHANEL GLOBAL CHNL GLOBAL VAR GLOBAL TABIT GLOBAL MODIFY GLOBAL MODIFS ; EXTRN ASSEM EXTRN ERROR EXTRN REPORT EXTRN WARM EXTRN CLOOP EXTRN SAYLN EXTRN LOAD0 EXTRN CRLF EXTRN PBCDL EXTRN TELL EXTRN FINDL EXTRN SETLIN EXTRN CLEAR EXTRN GETVAR EXTRN PUTVAR EXTRN GETDEF EXTRN LOCATE EXTRN CREATE EXTRN OUTCHR EXTRN EXTERR EXTRN BYE EXTRN NXT EXTRN NLIST ; EXTRN OSWRCH EXTRN OSLINE EXTRN OSSHUT EXTRN OSBPUT EXTRN OSBGET EXTRN CLRSCN EXTRN PUTCSR EXTRN PUTIME EXTRN PUTIMS EXTRN PUTPTR EXTRN OSCALL EXTRN OSCLI EXTRN TRAP ; EXTRN SOUND EXTRN CLG EXTRN DRAW EXTRN ENVEL EXTRN GCOL EXTRN MODE EXTRN MOVE EXTRN PLOT EXTRN COLOUR EXTRN CIRCLE EXTRN ELLIPSE EXTRN FILL EXTRN MOUSE EXTRN ORIGIN EXTRN RECTAN EXTRN LINE EXTRN WAIT EXTRN TINT EXTRN SYS ; EXTRN STR EXTRN HEXSTR EXTRN EXPR EXTRN EXPRN EXTRN EXPRI EXTRN EXPRS EXTRN ITEMI EXTRN CONS EXTRN LOADS EXTRN VAL0 EXTRN SFIX EXTRN TEST EXTRN LOAD4 EXTRN LOADN EXTRN DLOAD5 EXTRN FPP EXTRN COMMA EXTRN BRAKET EXTRN PUSHS EXTRN POPS EXTRN ZERO EXTRN SCP EXTRN LETARR ; EXTRN ACCS EXTRN PAGE EXTRN LOMEM EXTRN HIMEM EXTRN FREE EXTRN BUFFER EXTRN ERRTRP EXTRN ONERSP EXTRN CURLIN EXTRN COUNT EXTRN WIDTH EXTRN STAVAR EXTRN DATPTR EXTRN RANDOM EXTRN TRACEN EXTRN LISTON EXTRN PC EXTRN OC ; LF EQU 0AH CR EQU 0DH TAND EQU 80H TOR EQU 84H TERROR EQU 85H TLINE EQU 86H TOFF EQU 87H TSTEP EQU 88H TSPC EQU 89H TTAB EQU 8AH TELSE EQU 8BH TTHEN EQU 8CH TLINO EQU 8DH TTO EQU 0B8H TCMD EQU 0C0H TWHILE EQU 0C7H TWHEN EQU 0C9H TOF EQU 0CAH TENDCASE EQU 0CBH TOTHERWISE EQU 0CCH TENDIF EQU 0CDH TENDWHILE EQU 0CEH TCALL EQU 0D6H TDATA EQU 0DCH TDEF EQU 0DDH TFOR EQU 0E3H TGOSUB EQU 0E4H TGOTO EQU 0E5H TLOCAL EQU 0EAH TNEXT EQU 0EDH TON EQU 0EEH TPROC EQU 0F2H TREM EQU 0F4H TREPEAT EQU 0F5H TRETURN EQU 0F8H TSTOP EQU 0FAH TUNTIL EQU 0FDH TEXIT EQU 10H ; CMDTAB: DEFW LEFTSL DEFW MIDSL DEFW RITESL DEFW SYNTAX ;STR$ DEFW SYNTAX ;STRING$ DEFW SYNTAX ;EOF DEFW SYNTAX ;SUM DEFW WHILE DEFW CASE DEFW SYNTAX ;WHEN DEFW SYNTAX ;OF DEFW XEQ ;ENDCASE DEFW SYNTAX ;OTHERWISE DEFW XEQ ;ENDIF DEFW ENDWHI ;ENDWHILE DEFW PTR DEFW PAGEV DEFW TIMEV DEFW LOMEMV DEFW HIMEMV DEFW SOUND DEFW BPUT DEFW CALL DEFW CHAIN DEFW CLR DEFW CLOSE DEFW CLG DEFW CLS DEFW REM ;DATA DEFW REM ;DEF DEFW DIM DEFW DRAW DEFW END DEFW ENDPRO DEFW ENVEL DEFW FOR DEFW GOSUB DEFW GOTO DEFW GCOL DEFW IF DEFW INPUT DEFW LET DEFW LOCAL DEFW MODE DEFW MOVE DEFW NEXT DEFW ON DEFW VDU DEFW PLOT DEFW PRINT DEFW PROC DEFW READ DEFW REM DEFW REPEAT DEFW REPOR DEFW RESTOR DEFW RETURN DEFW RUN DEFW STOP DEFW COLOUR DEFW TRACE DEFW UNTIL DEFW WIDTHV DEFW CLI ;OSCLI DEFW REM ;NUL DEFW CIRCLE DEFW ELLIPSE DEFW FILL DEFW MOUSE DEFW ORIGIN DEFW BYE ;QUIT DEFW RECTAN DEFW SWAP DEFW SYS DEFW TINT DEFW WAIT DEFW SYNTAX ;INSTALL DEFW REM ;CR DEFW PUT ;Token changed DEFW SYNTAX ;BY DEFW EXIT ; TLAST EQU TCMD-128+($-CMDTAB)/2 ; RUN: CALL TERMQ JR Z,RUN0 CHAIN: CALL EXPRS LD A,CR LD (DE),A CHAIN0: LD SP,(HIMEM) CALL LOAD0 RUN0: LD SP,(HIMEM) ;PREPARE FOR RUN LD IX,RANDOM RAND: LD A,R ;RANDOMISE (CARE!) JR Z,RAND RLCA RLCA LD (IX+3),A SBC A,A LD (IX+4),A CALL CLEAR LD HL,0 LD (ERRTRP),HL LD HL,(PAGE) CALL DSRCH ;LOOK FOR "DATA" LD (DATPTR),HL ;SET DATA POINTER LD IY,(PAGE) XEQ0: CALL NEWLIN LD A,(IY) CP TELSE JP Z,MELSE ;ELSE CP TWHEN JP Z,WHEN ;WHEN CP TOTHERWISE JP Z,WHEN XEQ: LD (CURLIN),IY ;ERROR POINTER CALL TRAP ;CHECK KEYBOARD XEQ1: CALL NXT INC IY CP ':' ;SEPARATOR JR Z,XEQ1 CP CR JR Z,XEQ0 ;NEW PROGRAM LINE CP TLAST JP PE,LET0 ;IMPLIED LET SUB TCMD JP M,EXTRAS ADD A,A LD C,A LD B,0 LD HL,CMDTAB ADD HL,BC LD A,(HL) ;TABLE ENTRY INC HL LD H,(HL) LD L,A CALL NXT JP (HL) ;EXECUTE STATEMENT ; ;END ; ENDIM: PUSH IY POP HL LD BC,(PAGE) SBC HL,BC ;IMMEDIATE MODE ? JP C,CLOOP END: LD E,0 CALL OSSHUT ;CLOSE ALL FILES JP WARM ;"Ready" ; NEWLIN: LD A,(IY+0) ;A=LINE LENGTH LD BC,3 ADD IY,BC OR A JR Z,ENDIM ;LENGTH=0, EXIT LD HL,(TRACEN) LD A,H OR L RET Z LD D,(IY-1) ;DE = LINE NUMBER LD E,(IY-2) SBC HL,DE RET C EX DE,HL LD A,'[' ;TRACE CALL OUTCHR CALL PBCDL LD A,']' CALL OUTCHR LD A,' ' JP OUTCHR ; ;ROUTINES FOR EACH STATEMENT: ; ;OSCLI ; CLI: CALL EXPRS LD A,CR LD (DE),A LD HL,ACCS CALL OSCLI JR XEQ ; ;REM, * ; EXT: PUSH IY POP HL CALL OSCLI REM: PUSH IY POP HL LD A,CR LD B,A CPIR ;FIND LINE END PUSH HL POP IY JP XEQ0 ; EXTRAS: CP TELSE-TCMD JR Z,REM ;ELSE CP TERROR-TCMD JR Z,THROW ;ERROR CP TLINE-TCMD JP Z,LINE ;LINE JP SYNTAX ; ;ERROR num,string$ ; THROW: CALL EXPRI EXX PUSH HL EXX CALL COMMA CALL EXPRS POP HL XOR A LD (DE),A LD A,L LD HL,ACCS LD DE,BUFFER PUSH DE LD BC,256 LDIR JP EXTERR ; ; SWAP ; SWAP: CALL GETVAR JR NZ,SWAPNZ PUSH AF PUSH HL CALL COMMA CALL NXT CALL GETVAR SWAPNZ: JR NZ,NOSUCH POP DE POP BC CP B JR NZ,MISMAT AND 00001111B JR Z,MISMAT LD A,B AND 11000000B JR Z,SWAP1 LD B,2 JP P,SWAP1 JP PE,SWAP1 LD B,4 SWAP1: LD C,(HL) LD A,(DE) LD (HL),A LD A,C LD (DE),A INC DE INC HL DJNZ SWAP1 JR XEQR ; ;[LET] var = expr ; LET0: CP '*' JR Z,EXT CP '=' JR Z,FNEND CP '[' JR Z,ASM DEC IY LET: CALL ASSIGN JP Z,XEQ JR C,SYNTAX ;"Syntax error" JP P,LETARR ;Numeric array JP PE,LETARR ;String array LD A,D ;Type PUSH DE PUSH HL CALL EXPRS POP IX POP HL CALL MODIFS XEQR: JP XEQ ; ; GETSTR - Get string variable ; Inputs: IY = text pointer ; Outputs: B = type ; Z-flag set if comma ; GETSTR: CALL GETVAR JR NZ,NOSUCH LD B,A AND 11000000B JP P,MISMAT JP PE,BADUSE BIT 0,B JR Z,MISMAT CALL NXT CP ',' RET ; VAR: CALL GETVAR RET Z JP NC,PUTVAR NOSUCH: LD A,26 ;'No such variable' DEFB 21H SYNTAX: LD A,16 ;"Syntax error" DEFB 21H ESCAPE: LD A,17 ;"Escape" DEFB 21H BADUSE: LD A,14 ;'Bad use of array' DEFB 21H MISMAT: LD A,6 ;'Type mismatch' ERROR0: JP ERROR ; ASM0: CALL NEWLIN ASM: LD (CURLIN),IY CALL TRAP CALL ASSEM JR C,SYNTAX CP CR JR Z,ASM0 LD HL,LISTON LD A,(HL) AND 0FH OR 30H LD (HL),A JR XEQR ; ;= ; FNEND: CALL EXPR ;FUNCTION RESULT EX AF,AF' ADD A,A LD A,E JR C,FNEND1 LD A,C FNEND1: EX AF,AF' PUSH HL EXX POP BC EX DE,HL ;SAVE RESULT IN A'B'C'D'E' EXX FNEND2: POP BC LD HL,FNCHK XOR A SBC HL,BC JR Z,FNEND3 PUSH BC CALL RESLOC JR NZ,FNEND2 LD A,7 JR ERROR0 ;"No FN" ; FNEND3: POP IY LD (CURLIN),IY ;IN CASE OF ERROR EXX EX DE,HL PUSH BC EXX POP HL EX AF,AF' LD E,A LD C,A RRA RET ; ;DIM var(dim1[,dim2[,...]])[,var(...] ;DIM var expr[,var expr...] ; DIM: PUSH IY CP '!' JP Z,DIM4 CALL LOCATE ;VARIABLE JP C,BADDIM CALL NZ,CREATE LD A,(IY) CP '(' JP NZ,DIM4 PUSH HL POP IX LD A,(HL) AND 0FEH INC HL OR (HL) JP NZ,DIM4 POP BC ;LEVEL STACK LD A,D LD HL,(FREE) PUSH HL EX (SP),IX PUSH HL PUSH AF ;SAVE TYPE LD DE,1 LD B,D ;DIMENSION COUNTER DIM1: INC IY PUSH BC PUSH DE PUSH IX CALL EXPRI ;DIMENSION SIZE BIT 7,H JR NZ,BADDIM EXX INC HL POP IX INC IX LD (IX),L ;SAVE SIZE INC IX LD (IX),H POP BC CALL MUL16 ;HL=HL*BC JR C,NOROOM ;TOO LARGE EX DE,HL ;DE=PRODUCT POP BC INC B ;DIMENSION COUNTER LD A,(IY) CP ',' ;ANOTHER JR Z,DIM1 INC IX CALL BRAKET ;CLOSING BRACKET POP AF ;RESTORE TYPE CALL X14OR5 ;DE=DE*n JR C,NOROOM POP HL LD (HL),B ;NO. OF DIMENSIONS EX (SP),IX POP HL AND 80H OR (IX) ;FLAGS ; ; A = flags: bit 7 = string, bit 0 = LOCAL ; DE = amount to allocate ; HL = where to allocate (if not LOCAL) ; (HL - FREE is size of 'descriptor') ; IX = where to store pointer ; DIM3: PUSH HL INC H ;Safety margin ADD HL,DE JR C,NOROOM SBC HL,SP JR NC,NOROOM POP HL PUSH HL LD BC,(FREE) OR A SBC HL,BC LD B,H LD C,L POP HL SBC HL,BC BIT 0,A JR Z,ARRCHK ;NOT LOCAL LD HL,0 ADD HL,SP SBC HL,DE SBC HL,BC LD SP,HL PUSH DE PUSH BC PUSH AF CALL ARRCHK ARRCHK: LD (IX+0),L ;SAVE POINTER LD (IX+1),H LD A,B OR C JR Z,DIM2 PUSH DE EX DE,HL LD HL,(FREE) LDIR ;COPY DESCRIPTOR EX DE,HL POP DE DIM2: LD A,D OR E JR Z,DIM5 LD (HL),0 ;INITIALISE ARRAY INC HL DEC DE JR DIM2 ; BADDIM: LD A,10 ;"Bad DIM" DEFB 21H NOROOM: LD A,11 ;"DIM space" ERROR1: JP ERROR ; DIM5: SBC HL,SP JR NC,DIM7 ;LOCAL ADD HL,SP LD (FREE),HL DIM7: CALL NLIST ;ANOTHER VARIABLE? JP DIM ; DIM4: POP IY CALL VAR OR A JR Z,BADDIM JP M,BADDIM BIT 6,A JR NZ,BADDIM LD B,A ;TYPE CALL NXT CP TLOCAL LD A,0 ;PRESET TO NOT LOCAL JR NZ,DIM8 INC IY INC A ;FLAG LOCAL DIM8: PUSH AF LD A,B ;TYPE EXX LD HL,0 LD C,H CALL STOREN ;RESERVED AREA PUSH IX CALL EXPRI POP IX EXX INC HL EX DE,HL LD HL,(FREE) POP AF ;LOCAL FLAG JP DIM3 ; ;PRINT list... ;PRINT #channel,list... ; PRINT: CP '#' JR NZ,PRINT0 CALL CHNL ;CHANNEL NO. = E PRNTN1: CALL NLIST PUSH DE CALL EXPR ;ITEM TO PRINT EX AF,AF' JP M,PRNTN2 ;STRING POP DE PUSH BC EXX LD A,L EXX CALL OSBPUT EXX LD A,H EXX CALL OSBPUT LD A,L CALL OSBPUT LD A,H CALL OSBPUT POP BC LD A,C CALL OSBPUT JR PRNTN1 PRNTN2: LD C,E POP DE LD HL,ACCS INC C PRNTN3: DEC C JR Z,PRNTN4 LD A,(HL) INC HL PUSH BC CALL OSBPUT POP BC JR PRNTN3 PRNTN4: LD A,CR CALL OSBPUT JR PRNTN1 ; PRINT6: LD B,2 JR PRINTC PRINT8: LD BC,100H JR PRINTC PRINT9: LD HL,STAVAR XOR A CP (HL) JR Z,PRINT0 LD A,(COUNT) OR A JR Z,PRINT0 PRINTA: SUB (HL) JR Z,PRINT0 JR NC,PRINTA NEG CALL SPACES PRINT0: LD A,(STAVAR) LD C,A ;PRINTS LD B,0 ;PRINTF PRINTC: CALL TERMQ JR Z,PRINT4 RES 0,B INC IY CP '~' JR Z,PRINT6 CP ';' JR Z,PRINT8 CP ',' JR Z,PRINT9 CALL FORMAT ;SPC, TAB, ' JR Z,PRINTC DEC IY PUSH BC CALL EXPR ;VARIABLE TYPE EX AF,AF' JP M,PRINT3 ;STRING POP DE PUSH DE BIT 1,D PUSH AF CALL Z,STR ;DECIMAL POP AF CALL NZ,HEXSTR ;HEX POP BC PUSH BC LD A,C SUB E CALL NC,SPACES ;RIGHT JUSTIFY PRINT3: POP BC CALL PTEXT ;PRINT JR PRINTC PRINT4: BIT 0,B CALL Z,CRLF JR XEQGO3 ; ONERR: INC IY ;SKIP "ERROR" CALL NXT LD HL,0 ;FLAG NOT LOCAL CP TLOCAL JR NZ,ONERR1 INC IY ;SKIP "LOCAL" LD HL,(ERRTRP) PUSH HL LD HL,(ONERSP) PUSH HL LD HL,400H ;TYPE = 4, 'EXPONENT' = 0 PUSH HL LD HL,ERRTRP PUSH HL LD HL,LOCCHK PUSH HL LD HL,0 ADD HL,SP CALL NXT ONERR1: LD (ONERSP),HL LD (ERRTRP),IY CP TOFF JP NZ,REM INC IY ;SKIP "OFF" SBC HL,HL LD (ONERSP),HL LD (ERRTRP),HL XEQGO3: JP XEQ ; ;ON expr GOTO line[,line...] [ELSE statement] ;ON expr GOTO line[,line...] [ELSE line] ;ON expr GOSUB line[,line...] [ELSE statement] ;ON expr GOSUB line[,line...] [ELSE line] ;ON expr PROCone [,PROCtwo..] [ELSE PROCotherwise] ;ON ERROR [LOCAL] statement [:statement...] ;ON ERROR [LOCAL] OFF ; ON: CP TERROR JR Z,ONERR ;"ON ERROR" CALL EXPRI LD A,(IY) INC IY LD E,',' ;SEPARATOR CP TGOTO JR Z,ON1 CP TGOSUB JR Z,ON1 LD E,TPROC CP E LD A,39 JR NZ,ERROR2 ;"ON syntax" ON1: LD D,A EXX PUSH HL EXX POP BC ;ON INDEX LD A,B OR H OR L JR NZ,ON4 ;OUT OF RANGE OR C JR Z,ON4 DEC C JR Z,ON3 ;INDEX=1 ON2: CALL TERMQ JR Z,ON4 ;OUT OF RANGE INC IY ;SKIP DELIMITER CP '"' JR Z,ON5 CP E JR NZ,ON2 DEC C JR NZ,ON2 ON3: LD A,E CP TPROC JR Z,ONPROC PUSH DE CALL ITEMI ;LINE NUMBER POP DE LD A,D CP TGOTO JR Z,GOTO2 CALL SPAN ;SKIP REST OF LIST JR GOSUB1 ; ON5: CALL QUOTE INC IY JR ON2 ; ON4: LD A,(IY) INC IY CP TELSE JP Z,IF1 ;ELSE CLAUSE CP CR JR NZ,ON4 LD A,40 ERROR2: JP ERROR ;"ON range" ; ONPROC: LD A,TON JP PROC ; ;GOTO line ; GOTO: CALL ITEMI ;LINE NUMBER GOTO1: CALL TERMQ JP NZ,SYNTAX GOTO2: EXX CALL FINDL PUSH HL POP IY JP Z,XEQ0 LD A,41 JR ERROR2 ;"No such line" ; ;GOSUB line ; GOSUB: CALL ITEMI ;LINE NUMBER GOSUB1: PUSH IY ;TEXT POINTER CALL CHECK ;CHECK ROOM CALL GOTO1 ;SAVE MARKER GOSCHK EQU $ ; ;RETURN ; RETURN: POP DE ;MARKER LD HL,GOSCHK OR A SBC HL,DE POP IY JR Z,XEQGO2 LD A,38 JR ERROR2 ;"No GOSUB" ; ;REPEAT ; REPEAT: PUSH IY CALL CHECK CALL XEQ REPCHK EQU $ ; ;UNTIL expr ; UNTIL: POP BC PUSH BC LD HL,REPCHK OR A SBC HL,BC JR Z,UNTIL1 LD A,3 CALL RESLOC JR NZ,UNTIL LD A,43 JR ERROR2 ;"Not in a REPEAT loop" ; UNTIL1: CALL EXPRI CALL TEST POP BC POP DE JR NZ,XEQGO2 ;TRUE PUSH DE PUSH BC PUSH DE POP IY XEQGO2: JP XEQ ; ;FOR var = expr TO expr [STEP expr] ; FORVAR: LD A,34 JR ERROR2 ;"FOR variable" ; FOR: CALL ASSIGN JR NZ,FORVAR ;"FOR variable" PUSH AF ;SAVE TYPE LD A,(IY) CP TTO LD A,36 JR NZ,ERROR2 ;"No TO" INC IY PUSH IX CALL EXPRN ;LIMIT POP IX POP AF LD B,A ;TYPE PUSH BC ;SAVE ON STACK PUSH HL LD HL,0 LD C,H EXX PUSH HL LD HL,1 ;PRESET STEP EXX LD A,(IY) CP TSTEP JR NZ,FOR1 INC IY PUSH IX CALL EXPRN ;STEP POP IX FOR1: LD B,8 ;FPP '>' BIT 7,H JR NZ,FOR2 ;STEP SIGN LD B,12 ;FPP '<' FOR2: PUSH BC PUSH HL EXX PUSH HL EXX PUSH IY ;SAVE TEXT POINTER PUSH IX ;LOOP VARIABLE CALL CHECK CALL XEQ FORCHK EQU $ ; ;NEXT [var[,var...]] ; NEXT: POP BC ;MARKER LD HL,FORCHK OR A SBC HL,BC JR Z,NEXT2 PUSH BC LD A,3 CALL RESLOC JR NZ,NEXT LD A,32 JR ERROR3 ;"Not in a FOR loop" ; NEXT2: CALL TERMQ POP HL PUSH HL PUSH BC PUSH HL CALL NZ,GETVAR ;VARIABLE POP DE EX DE,HL OR A NEXT0: SBC HL,DE JR NZ,NEXT1 PUSH DE LD IX,6+2 ADD IX,SP CALL DLOAD5 ;STEP LD A,(IX+11) ;TYPE POP IX CALL LOADN ;LOOP VARIABLE PUSH AF LD A,'+' AND 0FH CALL FPP ;ADD STEP JR C,ERROR3 POP AF ;RESTORE TYPE CALL STOREN ;UPDATE VARIABLE LD IX,12 ADD IX,SP CALL DLOAD5 ;LIMIT LD A,(IX-1) CALL FPP ;TEST AGAINST LIMIT JR C,ERROR3 INC H JR NZ,LOOP ;KEEP LOOPING LD HL,18 ADD HL,SP LD SP,HL CALL NLIST JR NEXT ; LOOP: POP BC POP DE POP IY PUSH IY PUSH DE PUSH BC JP XEQ ; NEXT1: LD HL,18 ADD HL,SP LD SP,HL ;"POP" THE STACK POP BC LD HL,FORCHK SBC HL,BC POP HL ;VARIABLE POINTER PUSH HL PUSH BC JR Z,NEXT0 LD A,33 ERROR3: JP ERROR ;"Can't match FOR" ; ;FNname ;N.B. ENTERED WITH A <> TON ; FN: PUSH AF ;MAKE SPACE ON STACK CALL PROC1 FNCHK EQU $ ; ;PROCname ;N.B. ENTERED WITH A = ON PROC FLAG ; PROC: PUSH AF ;MAKE SPACE ON STACK CALL PROC1 PROCHK EQU $ PROC1: CALL CHECK DEC IY PUSH IY CALL GETDEF POP BC JR Z,PROC4 LD A,30 JR C,ERROR3 ;"Bad call" PUSH BC LD HL,(PAGE) PROC2: LD A,TDEF CALL SEARCH ;LOOK FOR "DEF" JR C,PROC3 PUSH HL POP IY INC IY ;SKIP DEF CALL NXT CALL GETDEF PUSH IY POP DE JR C,PROC6 CALL NZ,CREATE PUSH IY POP DE LD (HL),E INC HL LD (HL),D ;SAVE ADDRESS PROC6: EX DE,HL LD A,CR LD B,A CPIR ;SKIP TO END OF LINE JR PROC2 PROC3: POP IY ;RESTORE TEXT POINTER CALL GETDEF LD A,29 JR NZ,ERROR3 ;"No such FN/PROC" PROC4: LD E,(HL) INC HL LD D,(HL) ;GET ADDRESS LD HL,2 ADD HL,SP CALL NXT ;ALLOW SPACE BEFORE ( PUSH DE ;EXCHANGE DE,IY EX (SP),IY POP DE CP '(' ;ARGUMENTS? JP NZ,PROC5 CALL NXT ;ALLOW SPACE BEFORE ( CP '(' JP NZ,SYNTAX ;"Syntax error" PUSH IY POP BC ;SAVE IY IN BC EXX EX AF,AF' XOR A ;INITIALISE RETURN COUNT EX AF,AF' CALL SAVLOC ;SAVE DUMMY VARIABLES EX AF,AF' OR A JR Z,RETCHK ;NO RETURNS PUSH HL NEG LD L,A NEG LD H,-1 ;HL = -RETURNS ADD HL,HL ADD HL,HL ADD HL,HL ;-RETURNS * 8 EX (SP),HL POP IX ADD IX,SP LD SP,IX PUSH AF ;PUSH RETURN COUNT CALL RETCHK ;PUSH MARKER RETCHK: EX AF,AF' CALL BRAKET ;CLOSING BRACKET EXX PUSH BC POP IY ;RESTORE IY PUSH HL CALL ARGUE ;TRANSFER ARGUMENTS POP HL ; ; If any of the dummy arguments is the same as a passed-by-reference ; variable, then it must not be restored on exit (it would overwrite ; the wanted returned values), therefore search the saved values on ; the stack and if a match is found set bit 4 of the type. On exit ; from the FN/PROC this will prevent the dummies from being restored. ; EX (SP),HL OR A LD BC,RETCHK SBC HL,BC ADD HL,BC EX (SP),HL JR NZ,PROC5 ;No RETURNs ; PUSH DE PUSH HL LD HL,7 ;Skip two PUSHes and RETCHK ADD HL,SP LD A,(HL) ;RETURN count INC HL PUSH HL POP IX ;Address RETURNs table PROC0: LD E,A LD D,0 EX DE,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,DE ;HL addresses SAVLOC stack INC HL INC HL ;Bump past LOCCHK PROC7: LD E,(HL) INC HL LD D,(HL) ;DE = SAVLOC VARPTR INC HL LD C,(HL) ;Length (if string) INC HL LD B,(HL) ;Variable type ; ; Scan RETURNs table for VARPTR match ; PUSH BC ;Save type PUSH HL PUSH IX LD B,A ;B = RETURN count PROC8: LD L,(IX+4) LD H,(IX+5) ;HL = RETURNed VARPTR OR A SBC HL,DE JR Z,PROC9 EX DE,HL LD DE,8 ADD IX,DE EX DE,HL DJNZ PROC8 PROC9: POP IX POP HL POP BC ;Restore type ; ; If match, set bit 4 of type: ; JR NZ,PROCA SET 4,(HL) ;Flag don't restore ; ; Increment past stacked data: ; PROCA: LD DE,3 BIT 6,B JR NZ,PROCB ;Whole array LD E,5 BIT 7,B JR Z,PROCB ;Numeric LD E,C INC DE PROCB: ADD HL,DE LD C,(HL) INC HL LD B,(HL) INC HL ; BC = marker ? EX DE,HL LD HL,LOCCHK OR A SBC HL,BC EX DE,HL JR Z,PROC7 ;Another POP HL POP DE ; PROC5: LD (HL),E ;SAVE "RETURN ADDRESS" INC HL LD A,(HL) LD (HL),D CP TON ;WAS IT "ON PROC" ? JR NZ,XEQGO PUSH DE EX (SP),IY CALL SPAN ;SKIP REST OF ON LIST EX (SP),IY POP DE LD (HL),D DEC HL LD (HL),E XEQGO: JP XEQ ; LOCERR: INC IY JR XEQGO ; ;LOCAL DATA ; LOCDAT: INC IY LD HL,(DATPTR) PUSH HL LD A,40H PUSH AF LD HL,DATPTR PUSH HL LD HL,LOCCHK PUSH HL JR XEQGO ; ;LOCAL var[,var...] ; LOCAL: CP TERROR JR Z,LOCERR CP TDATA JR Z,LOCDAT POP BC PUSH BC LD HL,FNCHK OR A SBC HL,BC JR Z,LOCAL1 LD HL,PROCHK OR A SBC HL,BC JR Z,LOCAL1 LD HL,LOCCHK OR A SBC HL,BC JR Z,LOCAL1 LD HL,ARRCHK OR A SBC HL,BC JR Z,LOCAL1 LD HL,RETCHK OR A SBC HL,BC LD A,12 JP NZ,ERROR ;"Not LOCAL" LOCAL1: PUSH IY POP BC EXX DEC IY CALL SAVLOC EXX PUSH BC POP IY LOCAL2: CALL GETVAR JP NZ,SYNTAX BIT 6,A ;ARRAY? JR NZ,LOCAL4 OR A ;TYPE EX AF,AF' CALL ZERO EX AF,AF' PUSH AF CALL P,STOREN ;ZERO POP AF LD E,C CALL M,STORES LOCAL3: CALL NLIST JR LOCAL2 ; LOCAL4: LD (IX+0),1 ;FLAG LOCAL ARRAY LD (IX+1),0 JR LOCAL3 ; ;ENDPROC ; ENDPRO: POP BC LD HL,PROCHK ;PROC MARKER XOR A SBC HL,BC JR Z,ENDPR1 PUSH BC ;PUT BACK CALL RESLOC JR NZ,ENDPRO LD A,13 JP ERROR ;"No PROC" ; ENDPR1: POP IY JP XEQ ; ;INPUT #channel,var,var... ; INPUTN: CALL CHNL ;E = CHANNEL NUMBER INPN1: CALL NLIST PUSH DE CALL VAR POP DE PUSH AF ;SAVE TYPE PUSH HL ;VARPTR OR A JP M,INPN2 ;STRING CALL OSBGET EXX LD L,A EXX CALL OSBGET EXX LD H,A EXX CALL OSBGET LD L,A CALL OSBGET LD H,A CALL OSBGET LD C,A POP IX POP AF ;RESTORE TYPE PUSH DE ;SAVE CHANNEL CALL STOREN POP DE JR INPN1 INPN2: LD HL,ACCS INPN3: CALL OSBGET CP CR JR Z,INPN4 LD (HL),A INC L JR NZ,INPN3 INPN4: POP IX POP AF PUSH DE EX DE,HL CALL STACCS POP DE JR INPN1 ; ;INPUT ['][SPC(x)][TAB(x[,y])]["prompt",]var[,var...] ;INPUT LINE [SPC(x)][TAB(x[,y])]["prompt",]var[,var...] ; INPUT: CP '#' JR Z,INPUTN LD C,0 ;FLAG PROMPT CP TLINE JR NZ,INPUT0 INC IY ;SKIP "LINE" LD C,80H INPUT0: LD HL,BUFFER LD (HL),CR ;INITIALISE EMPTY INPUT1: CALL TERMQ JP Z,XEQ ;DONE INC IY CP ',' JR Z,INPUT3 ;SKIP COMMA CP ';' JR Z,INPUT3 PUSH HL ;SAVE BUFFER POINTER CP '"' JR NZ,INPUT6 PUSH BC CALL CONS POP BC CALL PTEXT ;PRINT PROMPT JR INPUT9 INPUT6: CALL FORMAT ;SPC, TAB, ' JR NZ,INPUT2 INPUT9: POP HL SET 0,C ;FLAG NO PROMPT JR INPUT0 INPUT2: DEC IY PUSH BC CALL VAR POP BC POP HL PUSH AF ;SAVE TYPE LD A,(HL) INC HL CP CR ;BUFFER EMPTY? CALL Z,REFILL BIT 7,C PUSH AF CALL NZ,LINES POP AF CALL Z,FETCHS POP AF ;RESTORE TYPE PUSH BC PUSH HL OR A JP M,INPUT4 ;STRING PUSH AF PUSH IX CALL VAL0 POP IX POP AF CALL STOREN JR INPUT5 INPUT4: CALL STACCS INPUT5: POP HL POP BC INPUT3: RES 0,C JR INPUT1 ; REFILL: BIT 0,C JR NZ,REFIL0 ;NO PROMPT LD A,'?' CALL OUTCHR ;PROMPT LD A,' ' CALL OUTCHR REFIL0: LD HL,BUFFER PUSH BC PUSH HL PUSH IX CALL OSLINE POP IX POP HL POP BC LD B,A ;POS AT ENTRY XOR A LD (COUNT),A CP B RET Z REFIL1: LD A,(HL) CP CR RET Z INC HL DJNZ REFIL1 RET ; ;READ var[,var...] ; READ: CP '#' JP Z,INPUTN LD HL,(DATPTR) READ0: LD A,(HL) CP ':' CALL Z,REFIL1 INC HL ;SKIP COMMA OR "DATA" CP CR ;END OF DATA STMT? CALL Z,GETDAT PUSH HL CALL VAR POP HL OR A JP M,READ1 ;STRING PUSH HL EX (SP),IY PUSH AF ;SAVE TYPE PUSH IX CALL EXPRN POP IX POP AF CALL STOREN EX (SP),IY JR READ2 READ1: CALL FETCHS PUSH HL CALL STACCS READ2: POP HL LD (DATPTR),HL CALL NLIST JR READ0 ; GETDAT: CALL DSRCH INC HL RET NC LD A,42 JR ERROR4 ;"Out of DATA" ; ;IF expr statement ;IF expr THEN statement [ELSE statement] ;IF expr THEN line [ELSE line] ;IF expr THEN ; IF: CALL EXPRI CALL TEST JR Z,IFNOT ;FALSE LD A,(IY) CP TTHEN JP NZ,XEQ IF0: INC IY ;SKIP "THEN" LD A,(IY) CP ';' JR Z,IF0 IF1: CALL NXT CP TLINO JP NZ,XEQ ;STATEMENT FOLLOWS JP GOTO ;LINE NO. FOLLOWS ; IFELSE: LD A,(IY) INC IY CP ';' JR NZ,IFNEXT JR IFTHEN ; IF2: CALL QUOTE ;SKIP STRING IFNOT: LD A,(IY) INC IY IFNEXT: CP '"' JR Z,IF2 ;QUOTED STRING CP TREM JP Z,REM ;REM CP CR JP Z,XEQ0 ;END OF LINE CP TELSE JR Z,IF1 ;ELSE CLAUSE CP TTHEN JR NZ,IFNOT ;TRY FOR END AGAIN IFTHEN: LD A,(IY) CP CR JR NZ,IFELSE LD BC,TELSE LD DE,TENDIF*256+TTHEN INC IY CALL NSCAN JP Z,XEQ1 NENDIF: LD A,49 ERROR4: JP ERROR ;"Missing ENDIF" ; ; ELSE (multi-line) ; MELSE: LD BC,-3 ADD IY,BC LD BC,TENDIF LD DE,TENDIF*256+TTHEN CALL NSCAN JP Z,XEQ JR NENDIF ; ; WHEN and OTHERWISE: ; WHEN: LD BC,-3 ADD IY,BC LD BC,TENDCASE LD DE,TENDCASE*256+TOF CALL NSCAN JP Z,XEQ LD A,47 JR ERROR4 ;"Missing ENDCASE" ; ; CASE ; CASE: CALL EXPR ;String or numeric EX AF,AF' LD B,0 ;Flag numeric JP P,CASE6 ;numeric CALL PUSHS ;put string on stack POP BC ;C = length LD B,1 ;Flag string CASE6: LD A,(IY) INC IY CP TOF LD A,37 JR NZ,ERROR4 ;"Missing OF" LD A,(IY) INC IY ;Address line-length byte CP CR LD A,48 JR NZ,ERROR4 ;"OF not last" CASE1: XOR A ;Level CASE0: EXX PUSH HL ;Push to stack EXX PUSH HL PUSH BC LD L,A ;Level LD BC,TOTHERWISE*256+TWHEN LD DE,TENDCASE*256+TOF CALL NSCAN1 POP BC ;Restore from stack POP HL EXX POP HL EXX LD A,47 JP NZ,ERROR ;Missing ENDCASE LD A,(IY-1) CP TENDCASE JR Z,CASE9 CP TOTHERWISE JR Z,CASE9 CASE4: BIT 0,B ;Numeric or string? JR NZ,CASE3 PUSH BC ;Type/exponent/length PUSH HL ;MS 32 bits EXX PUSH HL ;LS 32 bits EXX CALL EXPRN LD IX,0 ADD IX,SP ;Address stack EXX LD E,(IX+0) ;Get LS 32-bits LD D,(IX+1) EXX LD E,(IX+2) LD D,(IX+3) ;Get MS 32-bits LD B,(IX+4) ;Get exponent LD A,9 CALL FPP ;In case integer vs float LD A,L OR A ;NZ if equal EXX POP HL EXX POP HL POP BC JR NZ,CASE5 ;Match found CASE2: LD A,(IY) INC IY CP ',' JR Z,CASE4 ;Not found, try another EXX PUSH IY EX (SP),HL LD A,CR LD B,A CPIR ;Find CR EX (SP),HL POP IY EXX LD A,(IY-2) ;Last token in previous line CP TOF ;CASE statement in WHEN line JR NZ,CASE1 LD A,1 JR CASE0 ; ;Finished, level stack if string: ; CASE9: BIT 0,B JR Z,CASE8 LD H,0 LD L,C ADD HL,SP LD SP,HL CASE8: JP XEQ ; ;Matched, so skip any more expressions: ; CASE5: CALL NXT CP ',' JR NZ,CASE9 ;End of list INC IY PUSH BC ;Save type and string length CALL EXPR ;Evaluate but discard POP BC JR CASE5 ; ;String compare: ; CASE3: PUSH BC CALL EXPRS POP BC LD HL,0 ADD HL,SP LD B,E LD DE,ACCS PUSH BC CALL SCP ;String compare POP BC LD B,1 JR NZ,CASE2 JR CASE5 ; ; WHILE ; WHILE: PUSH IY ;Save current position CALL CHECK CALL WHICHK ;Push marker WHICHK: CALL EXPRI CALL TEST JR NZ,XEQGO5 POP BC ;Pop marker POP BC ;Level stack LD BC,TWHILE+TENDWHILE*256 LD D,1 CALL WSRCH XEQGO5: JP XEQ ; ; ENDWHILE ; ENDWHI: POP BC ;Marker POP DE ;Saved text pointer PUSH DE PUSH BC OR A LD HL,WHICHK SBC HL,BC JR Z,ENDWH1 LD A,3 CALL RESLOC JR NZ,ENDWHI LD A,46 JR ERROR5 ;"Not in a WHILE loop" ; ENDWH1: PUSH IY LD IY,0 ADD IY,DE CALL EXPRI CALL TEST POP DE ;Text pointer JR NZ,XEQGO5 POP BC ;Junk marker POP BC ;Junk pointer LD IY,0 ADD IY,DE JR XEQGO5 ; ;CLS ; CLS: CALL CLRSCN XOR A LD (COUNT),A JP XEQ ; ;STOP ; STOP: CALL TELL DEFB CR DEFB LF DEFB TSTOP DEFB 0 CALL SETLIN ;FIND CURRENT LINE CALL SAYLN CALL CRLF JP CLOOP ; ;REPORT ; REPOR: CALL REPORT JP XEQ ; ;CLEAR ; CLR: CALL CLEAR LD HL,(PAGE) JR RESTR1 ; ;RESTORE ERROR ; RESERR: INC IY LD A,2 CALL RESLOC JP NZ,XEQ LD A,53 ;ON ERROR not LOCAL ERROR5: JP ERROR ; ;RESTORE DATA ; RESDAT: INC IY LD A,1 CALL RESLOC JP NZ,XEQ LD A,54 ;'DATA not LOCAL' DEFB 21H NOLINE: LD A,41 ;'No such line' JR ERROR5 ; ;RESTORE [line] ; RESTOR: CP TERROR JR Z,RESERR CP TDATA JR Z,RESDAT CP '+' JR Z,RESREL LD HL,(PAGE) CALL TERMQ JR Z,RESTR1 CALL ITEMI EXX CALL FINDL ;SEARCH FOR LINE JR NZ,NOLINE RESTR1: CALL DSRCH LD (DATPTR),HL JP XEQ ; RESREL: CALL EXPRI EXX EX DE,HL PUSH IY POP HL LD A,CR LD B,A CPIR ;FIND LINE END DEC E JR Z,RESTR1 JP M,RESTR1 XOR A LD B,A RESTR2: LD C,(HL) CP C JR Z,NOLINE ADD HL,BC DEC E JR NZ,RESTR2 JR RESTR1 ; ;PTR#channel=expr ;PAGE=expr ;TIME=expr ;LOMEM=expr ;HIMEM=expr ; PTR: CALL CHANEL CALL EQUALS LD A,E PUSH AF CALL EXPRI PUSH HL EXX POP DE POP AF CALL PUTPTR JR XEQGO1 ; PAGEV: CALL EQUALS CALL EXPRI EXX LD L,0 LD (PAGE),HL JR XEQGO1 ; TIMEV: CP '$' JR Z,TIMEVS CALL EQUALS CALL EXPRI PUSH HL EXX POP DE CALL PUTIME JR XEQGO1 ; TIMEVS: INC IY ;SKIP '$' CALL EQUALS CALL EXPRS CALL PUTIMS JR XEQGO1 ; LOMEMV: CALL EQUALS CALL EXPRI CALL CLEAR EXX LD (LOMEM),HL LD (FREE),HL JR XEQGO1 ; HIMEMV: CALL EQUALS CALL EXPRI EXX LD DE,(FREE) INC D XOR A SBC HL,DE ADD HL,DE JP C,ERROR ;"No room" LD DE,(HIMEM) LD (HIMEM),HL EX DE,HL SBC HL,SP JP NZ,XEQ EX DE,HL LD SP,HL ;LOAD STACK POINTER XEQGO1: JP XEQ ; ;WIDTH expr ; WIDTHV: CALL EXPRI EXX LD A,L LD (WIDTH),A JR XEQGO1 ; ;TRACE ON ;TRACE OFF ;TRACE line ; TRACE: INC IY LD HL,0 CP TON JR Z,TRACE0 CP TOFF JR Z,TRACE1 DEC IY CALL EXPRI EXX TRACE0: DEC HL TRACE1: LD (TRACEN),HL JR XEQGO1 ; ;VDU expr,expr;.... ; VDU: CALL EXPRI EXX LD A,L CALL OSWRCH LD A,(IY) CP ',' JR Z,VDU2 CP ';' JR NZ,VDU3 LD A,H CALL OSWRCH VDU2: INC IY VDU3: CALL TERMQ JR NZ,VDU JR XEQGO1 ; ;CLOSE channel number ; CLOSE: CALL CHANEL CALL OSSHUT JR XEQGO1 ; ;BPUT #channel,byte ;BPUT #channel,string[;] ; BPUT: CALL CHANEL ;CHANNEL NUMBER PUSH DE CALL COMMA CALL EXPR EX AF,AF' JP M,BPUTS CALL SFIX EXX LD A,L POP DE CALL OSBPUT BPUTX: JR XEQGO1 ; BPUTS: LD A,E POP DE LD D,A LD HL,ACCS BPUTS1: LD A,(HL) INC HL CALL OSBPUT DEC D JR NZ,BPUTS1 CALL NXT CP ';' INC IY JR Z,BPUTX LD A,LF CALL OSBPUT DEC IY JR BPUTX ; ;CALL address[,var[,var...]] ; CALL: CALL EXPRI ;ADDRESS EXX PUSH HL ;SAVE IT LD B,0 ;PARAMETER COUNTER LD DE,BUFFER ;VECTOR CALL1: CALL NXT CP ',' JR NZ,CALL2 INC IY INC B CALL NXT PUSH BC PUSH DE CALL VAR POP DE POP BC INC DE LD (DE),A ;PARAMETER TYPE INC DE EX DE,HL LD (HL),E ;PARAMETER ADDRESS INC HL LD (HL),D EX DE,HL JR CALL1 CALL2: LD A,B LD (BUFFER),A ;PARAMETER COUNT POP HL ;RESTORE ADDRESS CALL USR1 JP XEQ ; ;USR(address) ; USR: CALL ITEMI EXX USR1: PUSH HL ;ADDRESS ON STACK EX (SP),IY INC H ;PAGE &FF? LD HL,USR2 ;RETURN ADDRESS PUSH HL LD IX,STAVAR CALL Z,OSCALL ;INTERCEPT PAGE &FF LD C,(IX+24) PUSH BC POP AF ;LOAD FLAGS LD A,(IX+4) ;LOAD Z80 REGISTERS LD B,(IX+8) LD C,(IX+12) LD D,(IX+16) LD E,(IX+20) LD H,(IX+32) LD L,(IX+48) LD IX,BUFFER JP (IY) ;OFF TO USER ROUTINE USR2: POP IY XOR A LD C,A RET ; ; LEFT$(A$[,N]) = string ; MID$(A$,N[,M]) = string ; RIGHT$(A$[,N]) = string ; LEFTSL: CALL GETSTR LD HL,0FF00H ;Default all but last JR NZ,MIDSL1 JR MIDSL0 ; RITESL: CALL GETSTR LD HL,0FFFFH ;Default last char only JR NZ,MIDSL1 JR MIDSL0 ; MIDSL: CALL GETSTR LD A,5 JP NZ,ERROR ;'Missing comma' INC IY PUSH IX CALL EXPRI POP IX EXX CALL NXT DEC L LD H,254 ;Default rest of string CP ',' JR NZ,MIDSL1 MIDSL0: INC IY PUSH HL PUSH IX CALL EXPRI POP IX EXX LD A,L POP HL OR A JR Z,MIDSL2 ;Zero length DEC A ADD A,L LD H,A JR NC,MIDSL1 LD A,L INC A JR Z,MIDSL1 LD H,254 JR MIDSL1 ; MIDSL2: LD HL,1 MIDSL1: CALL BRAKET CALL EQUALS PUSH HL PUSH IX CALL EXPRS POP IX POP HL LD C,E LD B,(IX+0) LD E,(IX+2) LD D,(IX+3) ; ; Source string at ACCS, length C ; Destination string at DE, length B ; L = first character to modify 0-254 ; H = last character to modify 0-254 ; IF L=255 THEN modify rightmost H + 2 chars ; ELSE IF H=255 modify all but last character ; ELSE IF L > H do nothing ; IX = destination VARPTR ; LD A,L INC A JR NZ,SUBSL1 INC H INC H LD A,C CP H JR NC,SUBSL0 LD H,A SUBSL0: LD A,B SUB H JR NC,SUBSL6 XOR A SUBSL6: LD L,A JR SUBSL5 ; SUBSL1: LD A,H INC A JR NZ,SUBSL2 LD A,B SUB 2 JR C,SUBSL9 LD H,A SUBSL2: LD A,L CP B JR NC,SUBSL9 LD A,H CP B JR C,SUBSL3 SUBSL5: LD A,B DEC A LD H,A SUBSL3: LD A,H SUB L JR C,SUBSL9 INC A CP C JR C,SUBSL4 LD A,C SUBSL4: LD B,0 LD H,B LD C,A OR A JR Z,SUBSL9 EX DE,HL ADD HL,DE EX DE,HL LD HL,ACCS LDIR SUBSL9: JP XEQ ; ; EXIT FOR [var] ; EXIT REPEAT ; EXIT WHILE ; EXIT: INC IY ;Skip FOR/REPEAT/WHILE CP TFOR JR NZ,EXIT0 LD IX,0 ;For EXIT FOR CALL TERMQ CALL NZ,GETVAR LD A,TFOR EXIT0: LD D,1 ;Level for WSRCH LD E,A EXIT1: LD A,E POP BC ;Marker LD HL,FORCHK OR A SBC HL,BC JR Z,EXIT4 LD HL,REPCHK OR A SBC HL,BC JR Z,EXIT6 LD HL,WHICHK OR A SBC HL,BC JR Z,EXIT7 PUSH BC ;Put back marker PUSH IX POP BC EXX LD A,3 CALL RESLOC EXX PUSH BC POP IX JR NZ,EXIT1 LD A,44 JP ERROR ;'Bad EXIT' ; EXIT4: POP BC ;VARPTR LD HL,14 ;Skip text pointer, limit & step ADD HL,SP LD SP,HL ;Pop FOR record CP TFOR JR NZ,EXIT1 PUSH IX POP HL LD A,H OR L JR Z,EXIT5 SBC HL,BC EXIT5: LD BC,TFOR+TNEXT*256 JR Z,EXIT8 INC D ;Count nested FOR loops JR EXIT1 ; EXIT6: POP BC ;Text pointer CP TREPEAT JR NZ,EXIT1 LD BC,TREPEAT+TUNTIL*256 JR EXIT8 ; EXIT7: POP BC ;Text pointer CP TWHILE JR NZ,EXIT1 LD BC,TWHILE+TENDWHILE*256 EXIT8: CALL WSRCH CALL SPAN ;Skip UNTIL expression JP XEQ ; ;PUT port,data ; PUT: CALL EXPRI ;PORT ADDRESS EXX PUSH HL CALL COMMA CALL EXPRI ;DATA EXX POP BC OUT (C),L ;OUTPUT TO PORT BC JP XEQ ; ;SUBROUTINES: ; ;ASSIGN - Assign a numeric value to a variable. ;Outputs: NC, Z - OK, numeric scalar ; NC, NZ, PE - OK, string array (D = type, E = operator) ; else if NC, NZ, P - OK, numeric array (D = type, E = operator) ; else if NC, NZ - OK, string scalar ; C, NZ - illegal / invalid ; ASSIGN: CALL GETVAR ;VARIABLE RET C ;ILLEGAL VARIABLE CALL NZ,PUTVAR LD D,A ;Type CALL NXT INC IY LD E,A ;Operator (or =) CP '=' CALL NZ,EQUALS LD A,D AND 11000000B RET NZ ;String or array PUSH DE PUSH HL CALL EXPRN POP IX POP DE ; ; Falls through to... ; ; MODIFY - Update numeric variable according to operator: ; Inputs: D = type ; E = operator ; HLH'L'C = value ; IX = destination VARPTR ; Destroys: Everything except IX,IY,SP ; MODIFY: LD A,E CP '=' JR Z,STORE0 ;Simple assignment PUSH DE EXX EX DE,HL EXX EX DE,HL LD B,C EX (SP),HL LD A,H EX (SP),HL CALL LOADN EX (SP),HL LD A,L EX (SP),HL AND 15 PUSH IX CALL FPP POP IX POP DE JP C,ERROR STORE0: LD A,D ;Type STOREN: CP 5 JR Z,STORE5 PUSH AF INC C ;SPEED - & PRESERVE F' DEC C ; WHEN CALLED BY FNEND0 CALL NZ,SFIX ;CONVERT TO INTEGER POP AF CP 4 JR Z,STORE4 CP A ;SET ZERO STORE1: EXX LD (IX+0),L EXX RET ; STORE5: LD (IX+4),C STORE4: EXX LD (IX+0),L LD (IX+1),H EXX LD (IX+2),L LD (IX+3),H RET ; ; MODIFS - Update string variable according to operator: ; Inputs: H = type ; L = operator (= or +) ; E = string length (string in accumulator) ; IX = destination VARPTR ; Destroys: Everything except SP, IY ; MODIFS: LD A,L ;Operator CP '+' LD A,H ;Type JR NZ,STACCS PUSH IY PUSH IX POP IY CALL PUSHS PUSH IY POP IX CALL LOADS POP BC LD A,B ;Type INC C DEC C JR Z,MODFS1 ;Zero length LD HL,0 LD B,H ADD HL,SP LDIR LD SP,HL MODFS1: POP IY ; ; Falls through to: ; STACCS: LD HL,ACCS STORES: RRA JR NC,STORS3 ;FIXED STRING PUSH HL CALL LOAD4 LD A,E ;LENGTH OF STRING EXX LD L,A LD A,H ;LENGTH ALLOCATED EXX CP E JR NC,STORS1 ;ENOUGH ROOM EXX LD H,L EXX PUSH HL LD B,0 LD C,A ADD HL,BC LD BC,(FREE) SBC HL,BC ;IS STRING LAST? POP HL JR Z,STORS0 LD H,B LD L,C ;DESTINATION ; OR A ;V5 optimisation JR Z,STORS0 LD A,E STORS2: LD E,A DEC E AND E JR NZ,STORS2 SCF RL E LD A,E EXX LD H,A EXX ; STORS0: SCF STORS1: CALL STORE4 ;PRESERVES CARRY! LD B,0 LD C,E EX DE,HL POP HL DEC C INC C RET Z ;NULL STRING LDIR RET NC ;STRING REPLACED LD (FREE),DE CHECK: PUSH HL LD HL,(FREE) INC H SBC HL,SP POP HL RET C XOR A JP ERROR ;"No room" ; STORS3: LD C,E PUSH IX POP DE XOR A LD B,A CP C JR Z,STORS5 LDIR STORS5: LD A,CR LD (DE),A RET ; ; SAVRET - SAVE 'RETURNed' PARAMETER INFO ; SAVRET: LD (IX+0),L ;Formal VARPTR LD (IX+1),H LD (IX+2),A EX (SP),IY PUSH AF PUSH IY PUSH IX CALL NXT CALL VAR POP IX LD (IX+4),L ;Actual VARPTR LD (IX+5),H LD (IX+6),A POP IY POP AF LD BC,8 ADD IX,BC JR ARGUE0 ; ;ARGUE: TRANSFER FN OR PROC ARGUMENTS FROM THE ; CALLING STATEMENT TO THE DUMMY VARIABLES VIA ; THE STACK. IT MUST BE DONE THIS WAY TO MAKE ; PROCFRED(A,B) DEF PROCFRED(B,A) WORK. ; Inputs: DE addresses parameter list ; IY addresses dummy variable list ; IX addresses RETURNed parameter data block ; Outputs: DE,IY updated ; Destroys: Everything ; ARGUE: LD A,-1 PUSH AF ;PUT MARKER ON STACK ARGUE1: INC IY ;BUMP PAST ( OR , INC DE PUSH DE LD B,0 CALL NXT CP TRETURN JR NZ,ARGUE9 INC IY ;SKIP 'RETURN' CALL NXT INC B ;FLAG 'RETURN' ARGUE9: PUSH BC PUSH IX CALL GETVAR ;FORMAL PARAMETER JR C,ARGERR CALL NZ,PUTVAR POP IX POP BC POP DE PUSH HL ;VARPTR PUSH AF PUSH DE DEC B JR Z,SAVRET EX (SP),IY ARGUE0: BIT 6,A ;ARRAY? JR NZ,ARGUE3 OR A ;TYPE JP M,ARGUE2 ;STRING PUSH IX CALL EXPRN ;ACTUAL PARAMETER POP IX EX (SP),IY POP DE POP AF EXX PUSH HL EXX PUSH HL LD B,A PUSH BC JR ARGUE4 ; ARGUE2: PUSH IX CALL EXPRS EXX POP BC EX (SP),IY POP DE EXX POP AF CALL PUSHS EXX PUSH BC POP IX ARGUE4: CALL NXT CP ',' JR NZ,ARGUE5 LD A,(DE) CP ',' JR Z,ARGUE1 ;ANOTHER ARGERR: LD A,31 JP ERROR ;"Bad arguments" ; ARGUE3: PUSH IX CALL NXT CALL GETVAR JR C,ARGERR LD C,(IX+0) LD B,(IX+1) POP IX CALL NXT EX (SP),IY POP DE POP AF PUSH BC ;STACK ARRAY POINTER PUSH AF ;STACK TYPE JR ARGUE4 ; ARGUE5: CALL BRAKET LD A,(DE) CP ')' JR NZ,ARGERR INC DE UNSTAK: EXX ARGUE6: POP BC LD A,B INC A EXX RET Z ;MARKER POPPED EXX DEC A BIT 6,A ;ARRAY JR NZ,ARGUE8 OR A JP M,ARGUE7 ;STRING POP HL EXX POP HL EXX POP IX CALL STOREN ;WRITE TO DUMMY JR ARGUE6 ; ARGUE7: CALL POPS POP IX CALL STACCS JR ARGUE6 ; ARGUE8: POP BC ;ARRAY POINTER POP IX LD (IX+0),C LD (IX+1),B JR ARGUE6 ; ;Restore RETURNed parameters, via the stack to ensure that ; PROCFRED(A,B) DEF PROCFRED(RETURN B,RETURN A) works. ; RETXFR: LD A,-1 PUSH AF ;PUT MARKER ON STACK RETXF1: EXX LD L,(IX+4) ;Actual parameter (destination) LD H,(IX+5) PUSH HL ;STACK VARPTR LD L,(IX+0) ;Formal parameter (source) LD H,(IX+1) LD A,(IX+2) BIT 6,A ;ARRAY? JR NZ,RETXF3 OR A ;TYPE JP M,RETXF2 ;STRING PUSH HL EX (SP),IX CALL LOADN POP IX EXX ;STACK VALUE PUSH HL EXX PUSH HL RETXF6: LD B,(IX+6) PUSH BC ;TYPE & EXPONENT RETXF5: CALL CHECK ;CHECK ROOM JR RETXF4 ; RETXF3: LD E,(HL) INC HL LD D,(HL) PUSH DE ;STACK ARRAY POINTER JR RETXF6 ; RETXF2: PUSH HL EX (SP),IX CALL LOADS POP IX LD A,(IX+6) EXX PUSH IX POP HL EXX CALL PUSHS EXX PUSH HL POP IX EXX RETXF4: LD DE,8 ADD IX,DE EXX DJNZ RETXF1 JP UNSTAK ; ;Restore 'RETURNed' parameters, ; RESRET: POP BC ;B = 'RETURN' COUNT LD H,0 LD L,B ADD HL,HL ADD HL,HL ADD HL,HL ;RETURN COUNT * 8 ADD HL,SP LD IX,0 ADD IX,SP ;ADDRESS PARAMETER LIST PUSH AF PUSH DE PUSH HL EXX PUSH BC PUSH DE EXX LD A,B LD HL,ACCS LD DE,BUFFER LD BC,255 LDIR LD B,A CALL RETXFR ;TRANSFER VIA STACK LD HL,BUFFER LD DE,ACCS LD BC,255 LDIR EXX POP DE POP BC EXX POP HL POP DE POP AF JR RESAR1 ; ; Restore LOCAL array or memory block: ; RESARR: POP BC BIT 7,B ;String array? POP HL POP BC ADD HL,BC ADD HL,SP CALL NZ,FREESA ;Free string array RESAR1: LD SP,HL JR RESLO1 ; ; RESLOC - Restore local variables/arrays or DATA/ERROR status from stack ; Inputs: A = 0 if everything OK, bit0 set if DATPTR, bit1 set if ERRTRP ; Outputs: Z if nothing was restored, NZ if something was restored ; Destroys: A,B,C,D,E,H,L,H',L',IX,SP,flags ; RESLOC: POP DE ;Return address LD IX,0 ;To flag nothing was restored RESLO1: POP BC ;Marker ? LD HL,LOCCHK OR A SBC HL,BC JR Z,RESLO2 ;Something to restore OR A JR NZ,RESLO8 LD HL,RETCHK SBC HL,BC JR Z,RESRET LD HL,ARRCHK OR A SBC HL,BC JR Z,RESARR RESLO8: PUSH IX POP HL LD A,H OR L RESLO0: PUSH BC ;Put back marker EX DE,HL JP (HL) ;Return ; RESLO2: POP IX ;Variable pointer OR A JR Z,RESLO3 ;Everything allowed PUSH IX POP BC BIT 0,A JR Z,RESLO6 ;Bit 0 set, so LD HL,DATPTR ;test for DATPTR SBC HL,BC JR Z,RESLO3 RESLO6: OR A BIT 1,A JR Z,RESLO7 ;Bit 1 set, so LD HL,ERRTRP ;test for ERRPTR SBC HL,BC JR Z,RESLO3 RESLO7: PUSH BC ;Put back pointer LD BC,LOCCHK JR RESLO0 ; RESLO3: POP BC ;Type / exponent BIT 6,B JR NZ,RESLO4 ;Array? BIT 7,B JR NZ,RESLO5 ;String? POP HL EXX POP HL EXX BIT 4,B JR NZ,RESLO1 PUSH AF LD A,B CALL STOREN ;Numeric POP AF JR RESLO1 ; RESLO4: POP HL BIT 4,B JR NZ,RESLO1 LD (IX+0),L ;Array LD (IX+1),H JR RESLO1 ; RESLO9: LD B,0 ADD HL,BC LD SP,HL RESLGO: JR RESLO1 ; RESLO5: LD HL,0 ADD HL,SP BIT 4,B JR NZ,RESLO9 PUSH AF PUSH DE LD E,C LD A,B CALL STORES ;String POP DE POP AF LD SP,HL JR RESLGO ; ;SAVLOC: SUBROUTINE TO STACK LOCAL PARAMETERS ; OF A FUNCTION OR PROCEDURE. ;THERE IS A LOT OF STACK MANIPULATION - CARE!! ; Inputs: IY is parameters pointer ; Outputs: IY updated ; A' incremented for each RETURN ; Destroys: A',A,B,C,D,E,H,L,IX,IY,F,SP ; SAVLOC: POP DE ;RETURN ADDRESS SAVLO1: INC IY ;BUMP PAST ( OR , CALL NXT CP TRETURN JR NZ,SAVLO6 EX AF,AF' INC A ;RETURN counter EX AF,AF' INC IY ;Bump past RETURN CALL NXT SAVLO6: PUSH DE EXX PUSH BC PUSH DE PUSH HL EXX CALL VAR ;DUMMY VARIABLE EXX POP HL POP DE POP BC EXX POP DE BIT 6,A ;ARRAY? JR NZ,SAVLO3 OR A ;TYPE JP M,SAVLO2 ;STRING EXX PUSH HL ;SAVE H'L' EXX LD B,A ;TYPE CALL LOADN EXX EX (SP),HL EXX PUSH HL PUSH BC JR SAVLO4 ; SAVLO3: LD C,(IX+0) ;ARRAY POINTER LD B,(IX+1) PUSH BC ;SAVE TO STACK PUSH AF ;SAVE TYPE JR SAVLO4 ; SAVLO2: PUSH AF ;STRING TYPE PUSH DE EXX PUSH HL EXX CALL LOADS EXX POP HL EXX LD C,E POP DE CALL CHECK POP AF ;LEVEL STACK LD HL,0 LD B,L SBC HL,BC ADD HL,SP LD SP,HL LD B,A ;TYPE PUSH BC JR Z,SAVLO4 PUSH DE LD DE,ACCS EX DE,HL LD B,L LDIR ;SAVE STRING ON STACK POP DE SAVLO4: PUSH IX ;VARPTR CALL SAVLO5 LOCCHK EQU $ SAVLO5: CALL CHECK CALL NXT CP ',' ;MORE? JR Z,SAVLO1 EX DE,HL JP (HL) ;"RETURN" ; TERMQ: CALL NXT CP TELSE RET NC CP ':' ;ASSEMBLER SEPARATOR RET NC CP CR RET ; SPAN: CALL TERMQ RET Z INC IY CP '"' CALL Z,QUOTE JR SPAN ; EQUALS: CALL NXT INC IY CP '=' RET Z LD A,4 JP ERROR ;"Mistake" ; FORMAT: CP TTAB JR Z,DOTAB CP TSPC JR Z,DOSPC CP '''' RET NZ CALL CRLF XOR A RET ; DOTAB: PUSH BC CALL EXPRI EXX POP BC LD A,(IY) CP ',' JR Z,DOTAB1 CALL BRAKET LD A,L TABIT: LD HL,COUNT CP (HL) RET Z PUSH AF CALL C,CRLF POP AF SUB (HL) JR SPACES DOTAB1: INC IY PUSH BC PUSH HL CALL EXPRI EXX POP DE POP BC CALL BRAKET CALL PUTCSR XOR A RET ; DOSPC: PUSH BC CALL ITEMI EXX LD A,L POP BC SPACES: OR A RET Z PUSH BC LD B,A FILL1: LD A,' ' CALL OUTCHR DJNZ FILL1 POP BC XOR A RET ; PTEXT: LD HL,ACCS INC E PTEXT1: DEC E RET Z LD A,(HL) INC HL CALL OUTCHR JR PTEXT1 ; FETCHS: PUSH AF PUSH BC PUSH HL EX (SP),IY CALL XTRACT CALL NXT EX (SP),IY POP HL POP BC POP AF RET ; LINES: LD DE,ACCS LINE1S: LD A,(HL) LD (DE),A CP CR RET Z INC HL INC E JR LINE1S ; XTRACT: CALL NXT CP '"' INC IY JP Z,CONS DEC IY LD DE,ACCS XTRAC1: LD A,(IY) LD (DE),A CP ',' RET Z CP CR RET Z INC IY INC E JR XTRAC1 ; DSRCH: LD A,TDATA SEARCH: LD B,0 SRCH1: LD C,(HL) INC C DEC C JR Z,SRCH2 ;FAIL INC HL INC HL INC HL CP (HL) RET Z DEC C DEC C DEC C ADD HL,BC JP SRCH1 SRCH2: DEC HL ;POINT TO CR SCF RET ; ; NSCAN - scan for token at start of line, with nesting of inner structures ; Alternative entry at NSCAN1 with L = level (used by CASE) ; ; Inputs: B = token to find (1, start of line) ; C = token to find (2, start of line) ; E = token to nest (end of line) ; D = token to unnest (start of line) ; IY = start search area (line length byte) ; Outputs: NZ if not found ; Z if found, IY points to byte after token ; Destroys: A,B,C,L,IY,F ; NSCAN: LD L,0 ;nest level NSCAN1: LD A,(IY) ;get line length OR A ;test zero = end of prog JR Z,NSCAN6 LD A,(IY+3) ;initial token CP B ;test value reqd JR Z,NSCAN3 ;found (1) CP C JR Z,NSCAN3 ;found (2) NSCAN7: CP D ;unnest? JR Z,NSCAN5 NSCAN2: PUSH BC LD B,0 LD C,(IY) ADD IY,BC ;go to next line LD A,(IY-2) CP E ;nest? LD A,C POP BC JR NZ,NSCAN1 ;continue CP 5 ;empty line ? JR C,NSCAN1 ;continue INC L ;increment nest level JR NSCAN1 ;continue ; NSCAN3: INC L DEC L JR NZ,NSCAN7 NSCAN4: LD BC,4 ADD IY,BC XOR A ;Z RET ; NSCAN5: DEC L ;decrement nest level JP P,NSCAN2 JR NSCAN4 ; NSCAN6: OR 1 ;NZ RET ; ; WSRCH - search for token, with nesting of inner structures ; ; Inputs: B = token to find or unnest (anywhere) ; C = token to nest (anywhere), ignore after EXIT ; D = ordinal (1 = find first token, 2 = second) ; IY = address to start looking ; Outputs: IY points to byte after that found ; if not found abort to END ; Destroys: A,D,IY,F ; WSRCH: LD A,(IY) INC IY CP '"' CALL Z,QUOTE CP TREM JR Z,WSRCHM CP TEXIT JR Z,WSRCHE CP B JR Z,WSRCHX CP C JR Z,WSRCHP CP CR JR NZ,WSRCH WSRCH1: LD A,(IY) ;Line length INC IY OR A JP Z,END INC IY INC IY ;Skip line number LD A,(IY) CP TDATA JR NZ,WSRCH WSRCHM: LD A,(IY) INC IY CP CR JR NZ,WSRCHM ;Skip to end of line JR WSRCH1 ; WSRCHP: INC D JR WSRCH ; WSRCHX: DEC D JR NZ,WSRCH RET ; WSRCHE: CALL NXT INC IY JR WSRCH ; ; QUOTE - skip quoted string ; QUOTE: LD A,(IY) INC IY CP CR JP Z,MISQUO CP '"' JR NZ,QUOTE RET ; MISQUO: LD A,9 JP ERROR ;"Missing quote" ; ; X14OR5 - multiply by 1, 4 or 5 ; Inputs: DE = number to be multiplied ; A = 1, 4 or 5 (else multiply by 4) ; Outputs: DE = DE * A ; Carry set if overflow ; Destroys: D,E,H,L,F ; X14OR5: LD H,D LD L,E CP 1 RET Z CP 5 ADD HL,HL RET C ADD HL,HL RET C EX DE,HL RET NZ ADD HL,DE EX DE,HL RET ; ; MUL16 - 16-bit multiply ; Inputs: HL = number to be multiplied ; BC = multiplier ; Outputs: HL = HL * BC ; Carry set if overflow ; Destroys: A,D,E,H,L,F ; MUL16: EX DE,HL LD HL,0 LD A,16 MUL161: ADD HL,HL RET C ;OVERFLOW SLA E RL D JR NC,MUL162 ADD HL,BC RET C MUL162: DEC A JR NZ,MUL161 RET ; CHANEL: CALL NXT CP '#' LD A,45 JP NZ,ERROR ;"Missing #" CHNL: INC IY ;SKIP '#' CALL ITEMI EXX EX DE,HL RET ; ; FREESA - Free members of a string array if adjacent to the top of heap ; Inputs: BC = length of array (= 4 * number of elements) ; HL addresses array first byte *above* array ; Outputs: NZ if any array element freed, Z if none ; Destroys: nothing ; FREESA: PUSH AF FREES0: PUSH BC PUSH DE PUSH HL XOR A LD D,B LD E,C LD B,A FREES1: PUSH DE DEC HL LD D,(HL) DEC HL LD E,(HL) DEC HL LD C,(HL) DEC HL PUSH HL LD HL,(FREE) EX DE,HL ADD HL,BC SBC HL,DE JR NZ,FREES2 ADD HL,DE SBC HL,BC LD (FREE),HL OR H FREES2: POP DE POP HL LD C,4 OR A SBC HL,BC EX DE,HL JR NZ,FREES1 OR A POP HL POP DE POP BC OR A JR NZ,FREES0 POP AF RET ; END