; TB.asm
; Palo Alto Tiny BASIC Version Three by Li-Chen Wang
; https://archive.org/details/Palo_Alto_Tiny_BASIC_Version_3_Li-Chen_Wang_1977
;
; using Z80 opcodes, adapted to monz80 by Andre Adrian, DL1ADR
; assemble:
; tasm -80 -fff -b -c TB.asm TB.bin
;
; version 2026-04-24

XLF	.EQU 0AH	; @LF
XCR	.EQU 0DH	; @CR

#define	ITEM(s,a)	.text s\	.byte (a>>8)|80h,a&0FFh
#define	TSTC(c,a)	call TSTCH\	.text c\	.byte a-*-1

; PATB assembler	TASM assembler
; MVI A,BOTROM,<	LD A,BOTROM>>8
; CPI BUFFER,>		CP BUFFER&0FFH

;**************************************************************
;*
;*	    T B I
;* TINY BASIC INTERPRETER
;*	VERSION 3.0
;*    FOR 8080 SYSTEM
;*	LI-CHEN WANG
;*     26 APRIL. 1977
;*
;**************************************************************
;*
;* *** MEMORY USAGE ***
;*
;* 0080-01FF ARE FOR VARIABLES, INPUT LINE, AND STACK
;* 2000-3FFF ARE FOR TINY BASIC TEXT & ARRAY
;* F000-F7FF ARE FOR TBI CODE
;*
BOTSCR	.EQU 02080h	; 00080H
TOPSCR	.EQU 02200h	; 00200H
BOTRAM	.EQU 04000h	; 02000H
DFTLMT	.EQU 06000h	; 04000H
BOTROM	.EQU 0F000h	; 0F000H

	.ORG	BOTSCR
KEYWRD	.DS	1	; WAS INIT DONE?
TXTLMT	.DS	2	; ->LIMIT OF TEXT AREA
VARBGN:	.DS	2*26	; TB VARIABLES A-Z
CURRNT: .DS	2	; POINTS TO CURRENT LINE
STKGOS: .DS	2	; SAVES SP IN 'GOSUB'
VARNXT: .DS	0	; TEMP STORAGE
STKINP: .DS	2	; SAVES SP IN 'INPUT'
LOPVAR: .DS	2	; 'FOR' LOOP SAVE AREA
LOPINC: .DS	2	; INCREMENT
LOPLMT: .DS	2	; LIMIT
LOPLN:	.DS	2	; LINE NUMBER
LOPPT:	.DS	2	; TEXT POINTER
RANPNT: .DS	2	; RANDOM NUMBER POINTER
	.DS	1	; EXTRA BYTE FOR BUFFER
BUFFER:	.DS	132	; INPUT BUFFER
BUFEND: .DS	0	; BUFFER ENDS
	.DS	4	; EXTRA BYTES FOR STACK
STKLMT: .DS	0	; SOFT LIMIT FOR STACK
	.ORG	TOPSCR
STACK:	.DS	0	; STACK STARTS HERE
	.ORG	BOTRAM
TXTUNF: .DS	2
TEXT:	.DS	2
;*
;***************************************************************
;*
;* *** INITIALIZE ***
;*
	.ORG	BOTROM
INIT:	LD	SP,STACK
	CALL	CRLF
	LD	HL,KEYWRD	; AT POWER ON KEYWRD IS
	LD	A,0C3H		; PROBABLY NOT C3
	CP	(HL)
	JP	Z,TELL		; IT IS C3, CONTINUE
	LD	(HL),A		; NO, SET IT TO C3
	LD	HL,DFTLMT	; AND SET DEFAULT VALUE
	LD	(TXTLMT),HL	; IN 'TXTLMT'
	LD	A,BOTROM>>8	; INITIALIZE RANPNT
	LD	(RANPNT+1),A
PURGE:	LD	HL,TEXT+4	; PURGE TEXT AREA
	LD	(TXTUNF),HL
	LD	H,0FFH
	LD	(TEXT),HL
TELL:	LD	DE,MSG		; TELL USER
	CALL	PRTSTG
	JP	RSTART

MSG:	.DB	"TINY "
	.DB	"BASIC"
	.DB	" V3.0",XCR
OK:	.DB	"OK",XCR
WHAT:	.DB	"WHAT?",XCR
HOW:	.DB	"HOW?",XCR
SORRY:	.DB	"SORRY",XCR
;*
;**************************************************************
;*
;* *** DIRECT COMMAND / TEXT COLLECTER ***
;*
;*
;* TBI PRINTS OUT "OK(CR)", AND THEN IT PROMPTS ">" AND READS A LINE.
;* IF THE LINE STARTS WITH A NON-ZERO NUMBER. THIS NUMBER IS THE LINE
;* NUMBER. THE LINE NUMBER (IN 16 BIT BINARY) AND THE REST OF THE LINE
;* (INCLUDING CR) IS STORED IN THE MEMORY. IF A LINE WITH THE SAME
;* LINE NUMBER IS ALHEDY THERE, IT IS REPLACED BY THE NEW ONE. IF THE
;* REST OF THE LINE CONSISTS OF A CR ONLY. IT IS NOT STORED AND ANY
;* EXISTING LINE WITH THE SAME LINE NUMBER IS DELETED.
;*
;* AFTER A LINE INSERTED, REPLACED, OR DELETED, THE PROGRAM LOOPS
;* BACK AND ASK FOR ANCTHER LINE. THIS LOOP WILL BE TERMINATED WHEN IT
;* READS A LINE WITH ZERO OR NO LINE NUM8ER; AND CONTROL IS TRANSFERED
;* TO "DIRECT".
;*
;* TINY BASIC PROGRAM SAVE AREA STARTS AT THE MEMORY LOCATION LABELED
;* TEXT". THE END OF TEXT IS MARKED BY 2 BYTES XX FF. FOLLOWING
;* THESE ARE 2 BYTES RESERVED FOR THE ARRAY ELEMENT @(0). THE CONTENT
;* OF LOCATION LABELED "TXTUNF" POINTS TO ONE AFTER @(0).
;*
;* THE MEMORY LOCATION "CURRNT" POINTS TO THE LINE NUMBER THAT IS
;* CURRENTLY BEING INTERPRETED. WHILE WE ARE IN THIS LOOP OR WHILE WE
;* ARE INTERPRETING A DIRECT COMMAND (SEE NEXT SECTION). "CURRNT"
;* SHOULD POINT TO A 0.
;*
RSTART: LD	SP,STACK	; RE-INITIALLIZE STACK
	LD	HL,ST1+1	; LITERAL 0
	LD	(CURRNT),HL	; CURRENT->LINE # = 0
ST1:	LD	HL,0
	LD	(LOPVAR),HL
	LD	(STKGOS),HL
	LD	DE,OK		; DE->STRING
	CALL	PRTSTG		; PRINT STRING UNTIL CR
ST2:	LD	A,'>'		; PROMPT '>' AND
	CALL	GETLN		; READ A LINE
	PUSH	DE		; DE->END OF LINE
	LD	DE,BUFFER	; DE->BEGINNING OF LINE
	CALL	TSTNUM		; TEST IF IT IS A NUMBER
	CALL	IGNBLK
	LD	A,H		; HL=VALUE OF THE # OR
	OR	L		; 0 IF NO # FOUND
	POP	BC		; BC->END OF LINE
	JP	Z,DIRECT
	DEC	DE		; BACKUP DE AND SAVE
	LD	A,H		; VALUE OF LINE # THERE
	LD	(DE),A
	DEC	DE
	LD	A,L
	LD	(DE),A
	PUSH	BC		; BC,DE->BEGIN, END
	PUSH	DE
	LD	A,C
	SUB	E
	PUSH	AF		; A=# OF BYTES IN LINE
	CALL	FNDLN		; FIND THIS LINE IN SAVE
	PUSH	DE		; AREA. DE-> SAVE AREA
	JP	NZ,ST3		; NZ:NOT FOUND, INSERT
	PUSH	DE		; Z:FOUND, DELETE IT
	CALL	FNDNXT		; SET DE->NEXT LINE
	POP	BC		; BC->LINE TO BE DELETED
	LD	HL,(TXTUNF)	; HL->UNFILLED SAVE AREA
	CALL	MVUP		; MOVE UP TO DELETE
	LD	H,B		; TXTUNF->UNFILLED SAVE AREA
	LD	L,C
	LD	(TXTUNF),HL	; UPDATE
ST3:	POP	BC		; GET READY TO INSERT
	LD	HL,(TXTUNF)	; BUT FIRST CHECK IF
	POP	AF		; LENGTH OF THE NEW LINE
	PUSH	HL		; IS 3 (LINE # AND CR)
	CP	3		; THEN DO NOT INSERT
	JP	Z,RSTART	; MUST CLEAR THE STACK
	ADD	A,L		; COMPUTE NEW TXTUNF
	LD	E,A
	LD	A,0
	ADC	A,H
	LD	D,A		; DE->NEW UNFILLED AREA
	LD	HL,(TXTLMT)	; CHECK TO SEE IF THERE
	EX	DE,HL
	CALL	COMP		; IS ENOUGH SPACE
	JP	NC,QSORRY	; SORRY, NO ROOM FOR IT
	LD	(TXTUNF),HL	; OK, UPDATE TXTUNF
	POP	DE		; DE->OLD UNFILLED AREA
	CALL	MVDOWN
	POP	DE		; DE->BEGIN, HL->END
	POP	HL
	CALL	MVUP		; MOVE NEW LINE TO SAVE
	JP	ST2		; AREA
;*
;**************************************************************
;*
;* *** DIRECT *** & EXEC ***
;*
;* THIS SECTION OF THE CODE TESTS A STRING AGAINST A TABLE. WHEN A
;* MATCH IS FOUND. CONTROL IS TRANSFERED TO THE SECTION OF CODE
;* ACCORDING TO THE TABLE.
;*
;* AT 'EXEC*. DE SHOULD POINT TO THE STRING AND HL SHOULD POINT TO THE
;* TABLE—1. AT 'DIRECT', DE SHOULD POINT TO THE STRING, HL WILL BE SET
;* UP TO POINT TO TAB1-1, WHICH IS THE TABLE OF ALL DIRECT AND
;* STATEMENT COMMANDS.
;*
;* A '.' IN THE STRING WILL TERMINATE THE TEST AND THE PARTIAL MATCH
;* WILL BE CONSIDERED AS A MATCH. E.G., 'P.', 'PR.', 'PRI.', 'PRIN.',
;* OR 'PRINT' WILL ALL MATCH 'PRINT'.
;*
;* THE TABLE CONSISTS OF ANY NUMBER OF ITEMS. EACH ITEM IS A STRING OF
;* CHARACTERS WITH BIT 7 SET TO 0 AND A JUMP ADDRESS STORED HI-LOW WITH
;* BIT 7 OF THE HIGH BYTE SET TO 1.
;*
;* END UF TABLE IS AN ITEM WITH A JUMP ADDRESS ONLY. IF THE STRING
;* DOES NOT MATCH ANY OF THE OTHER ITFCMS. IT WILL MATCH THIS NULL ITEM
;* AS DEFAULT.
;*
DIRECT: LD	HL,TAB1-1	; *** DIRECT ***

EXEC:	CALL	IGNBLK		; *** EXEC ***
	PUSH	DE		; SAVE POINTER
EX1:	LD	A,(DE)		; IF FOUND '.' IN STRING
	INC	DE		; BEFORE ANY MISMATCH
	CP	'.'		; WE DECLARE A MATCH
	JP	Z,EX3
	INC	HL		; HL->TABLE
	CP	(HL)		; IF MATCH, TEST NEXT
	JP	Z,EX1
	LD	A,7FH		; ELSE, SEE IF BIT 7
	DEC	DE		; OF TABLE IS SET, WHICH
	CP	(HL)		; IS THE JUMP ADDR. (HI)
	JP	C,EX5		; C:YES, MATCHED
EX2:	INC	HL		; NC:NO, FIND JUMP ADDR.
	CP	(HL)
	JP	NC,EX2
	INC	HL		; BUMP TO NEXT TAB. ITEM
	POP	DE		; RESTORE STRING POINTER
	JP	EXEC		; TEST AGAINST NEXT ITEM
EX3:	LD	A,7FH		; PARTIAL MATCH, FIND
EX4:	INC	HL		; JUMP ADDR., WHICH IS
	CP	(HL)		; FLAGGED BY BIT 7
	JP	NC,EX4
EX5:	LD	A,(HL)		; LD HL WITH JUMP
	INC	HL		; ADDRESS FROM THE TABLE
	LD	L,(HL)
	AND	0FFH		; ***** AND 07FH *****
	LD	H,A
	POP	AF		; CLEAN UP THE GARBAGE
	JP	(HL)		; AND WE GO DO IT
;*
;**************************************************************
;*
;* WHAT FOLLOWS IS THE CODE TO EXECUTE DIRECT AND STATEMENT
;* COMMANDS.  CONTROL IS TRANSFERED TO THESE POINTS VIA THE
;* COMMAND TABLE LOOKUP CODE OF 'DIRECT' AND 'EXEC' IN LAST
;* SECTION.  AFTER THE COMMAND IS EXECUTED, CONTROL IS
;* TRANSFERED TO OTHER SECTIONS AS FOLLOWS:
;*
;* FOR 'LIST', 'NEW', AND 'STOP': GO BACK TO 'RSTART'
;* FOR 'RUN': GO EXECUTE THE FIRST STORED LINE IF ANY; ELSE
;* GO BACK TO 'RSTART'.
;* FOR 'GOTO' AND 'GOSUB': GO EXECUTE THE TARGET LINE.
;* FOR 'RETURN' AND 'NEXT': GO BACK TO SAVED RETURN LINE.
;* FOR ALL OTHERS: IF 'CURRNT' -> 0, GO TO 'RSTART', ELSE
;* GO EXECUTE NEXT COMMAND.  (THIS IS DONE IN 'FINISH'.)
;*
;**************************************************************
;*
;* *** NEW *** STOP *** RUN (& FRIENDS) *** & GOTO ***
;*
;* 'NEW(CR)' SETS 'TXTUNF' TO POINT TO 'TXTBGN'
;*
;* 'STOP(CR)' GOES BACK TO 'RSTART'
;*
;* 'RUN(CR)' FINDS THE FIRST STORED LINE, STORE ITS ADDRESS (IN
;* 'CURRNT'), AND START EXECUTE IT.  NOTE THAT ONLY THOSE
;* COMMANDS IN TAB2 ARE LEGAL FOR STORED PROGRAM.
;*
;* THERE ARE 3 MORE ENTRIES IN 'RUN':
;* 'RUNNXL' FINDS NEXT LINE, STORES ITS ADDR. AND EXECUTES IT.
;* 'RUNTSL' STORES THE ADDRESS OF THIS LINE AND EXECUTES IT.
;* 'RUNSML' CONTINUES THE EXECUTION ON SAME LINE.
;*
;* 'GOTO EXPR(CR)' EVALUATES THE EXPRESSION, FIND THE TARGET
;* LINE, AND JUMP TO 'RUNTSL' TO DO IT.
;*
NEW:	CALL	ENDCHK		; *** NEW(CR) ***
	JP	PURGE

STOP:	CALL	ENDCHK		; *** STOP(CR) ***
	JP	RSTART

RUN:	CALL	ENDCHK		; *** RUN (CR) ***
	LD	DE,TEXT		; FIRST SAVED LINE

RUNNXL: LD	HL,0		; *** RLNNXL ***
	CALL	FNDLP		; FIND WHATEVER LINE #
	JP	C,RSTART	; C:PASSED TXTUNF, QUIT

RUNTSL: EX	DE,HL		; *** RUNTSL ***
	LD	(CURRNT),HL	; SET 'CURRENT'->LINE #
	EX	DE,HL
	INC	DE		; BUMP PASS LINE #
	INC	DE

RUNSML: CALL	CHKIO		; *** RUNSML ***
	LD	HL,TAB2-1	; FIND COMMAND IN TAB2
	JP	EXEC		; AND EXECUTE IT

GOTO:	CALL	EXPR		; *** GOTO EXPR ***
	PUSH	DE		; SAVE FOR ERROR ROUTINE
	CALL	ENDCHK		; MUST FIND A CR
	CALL	FNDLN		; FIND THE TARGET LINE
	JP	NZ,AHOW		; NO SUCH LINE #
	POP	AF		; CLEAR THE "PUSH DE"
	JP	RUNTSL		; GO DO IT
;*
;*************************************************************
;*
;* *** LIST *** & PRINT ***
;*
;* LIST HAS THREE FORMS:
;* 'LIST(CR)' LISTS ALL SAVED LINES
;* 'LIST N(CR)' START LIST AT LINE N
;* 'LIST N1, N2(CR)' START LIST AT LINE N1 FOR N2 LINES YOU CAN STOP
;* THE LISTING BY CONTROL C KEY
;*
;* PRINT COMMAND IS 'PRINT ....;' OR 'PRINT ....(CR)'
;* WHERE '....' IS A LIST OF EXPRESSIONS, FORMATS, AND/OR STRINGS.
;* THESE ITEMS ARE SEPERATED BY COMMAS.
;*
;* A FORMAT IS A POUND SIGN FOLLOWED BY A NUMBER. IT CONTROLS THE
;* NUMBER OF SPACES THE VALUE OF A EXPRESSION IS GOING TO BE PRINTED.
;* IT STAYS EFFECTIVE FOR THE REST OF THE PRINT COMMAND UNLESS CHANGED
;* BY ANOTHER FORMAT. IF NO FORMAT IS SPECIFIED, 8 POSITIONS WILL BE
;* USED.
;*
;* A STRING IS QUOTED IN A PAIR OF SINGLE QUOTES OR A PAIR OF DOUBLE
;* QUOTES.
;*
;* CONTROL CHARACTERS AND LOWER CASE LETTERS CAN BE INCLUDED INSIDE THE
;* QUOTES. ANOTHER (BETTER) WAY OF GENERATING CONTROL CHARACTERS ON
;* THE OUTPUT IS USE THE UP-ARROW CHARACTER FOLLOWED BY A LETTER. |L
;* MEANS FF, |I MEANS HT. |G MEANS BELL ETC.
;*
;* A (CRLF) IS GENERATED AFTER THE ENTIRE LIST HAS BEEN PRINTED OR IF
;* THE LIST IS A NULL LIST. HOWEVER IF THE LIST ENDED WITH A COMMA. NO
;* (CRLF) IS GENERATED.
;*
LIST:	CALL	TSTNUM		; TEST IF THERE IS A #
	PUSH	HL
	LD	HL,0FFFFH
	TSTC("\054",LS1)	; ,
	CALL	TSTNUM
LS1:	EX	(SP),HL
	CALL	ENDCHK		; IF NO # WE GET A 0
	CALL	FNDLN		; FIND THIS OR NEXT LINE
LS2:	JP	C,RSTART	; C:PASSED TXTUNF
	EX	(SP),HL
	LD	A,H
	OR	L
	JP	Z,RSTART
	DEC	HL
	EX	(SP),HL
	CALL	PRTLN		; PRINT THE LINE
	CALL	PRTSTG
	CALL	CHKIO
	CALL	FNDLP		; FIND NEXT LINE
	JP	LS2		; AND LOOP BACK

PRINT:	LD	C,8		; C= # OF SPACES
	TSTC(";",PR1)		; IF NULL LIST & ";"
	call	CRLF		; GIVE CR-LF AND
	JP	RUNSML		; CONTINUE SAME LINE
PR1:	TSTC("\015",PR6)	; IF NULL LIST (XCR)
	CALL	CRLF		; ALSO GIVE CR-LF AND
	JP	RUNNXL		; GO TO NEXT LINE
PR2:	TSTC("#",PR4)		; ELSE IS IT FORMAT?
PR3:	call	EXPR		; YES, EVALUATE EXPR.
	LD	A,0C0H
	AND	L
	OR	H
	JP	NZ,QHOW
	LD	C,L		; AND SAVE IT IN C
	JP	PR5		; LOOK FOR MORE TO PRINT
PR4:	CALL	QTSTG		; OR IS IT A STRING?
	JP	PR9		; IF NOT, MUST BE EXPR.
PR5:	TSTC("\054",PR8)	; IF ",", GO FIND NEXT
PR6:	TSTC("\054",PR7)	; AGAIN
	LD	A,' '
	CALL	OUTCH
	JP	PR6
PR7:	CALL	FIN		; IN THE LIST.
	JP	PR2		; LIST CONTINUES
PR8:	CALL	CRLF		; LIST ENDS
	JP	FINISH
PR9:	CALL	EXPR		; EVALUATE THE EXPR
	PUSH	BC
	CALL	PRTNUM		; PRINT THE VALUE
	POP	BC
	JP	PR5		; MORE TO PRINT?
;*
;**************************************************************
;*
;* *** GOSUB *** & RETURN ***
;*
;* 'GOSUB EXPR;' OR 'GOSUB EXPR (CR)' IS LIKE THE 'GOTO' COMMAND.
;* EXCEPT THAT THE CURRENT TEXT POINTER, STACK POINTER ETC. ARE SAVE SO
;* THAT EXECUTION CAN BE CONTINUED AFTER THE SUBROUTINE 'RETURN'. IN
;* ORDER THAT 'GOSUB' CAN BE NESTED (AND EVEN RECURSIVE). THE SAVE AREA
;* MUST BE STACKED. THE STACK POINTER IS SAVED IN 'STKGOS'. THE OLD
;* 'STKGOS' IS SAVFCD IN THE STACK. IF WE ARE IN THE MAIN ROUTINE.
;* 'STKGOS' IS ZERO (THIS WAS DONE BY T HE "MAIN" SECTION OF THE CODE).
;* BUT WE STILL SAVE IT AS A FLAG FOR NO FURTHER 'RETURN'S.
;*
;* 'RETURN(CR>' UNDOS EVERYTHING THAT 'GOSUB' DID. AND THUS RETURN THE
;* EXCUTION TO THE COMMAND AFTER THE MOST RECENT 'GOSUB'. IF 'STKGOS'
;* IS ZERO, IT INDICATES THAT WE NEVER HAD A 'GOSUB' AND IS THUS AN
;* ERROR.
;*
GOSUB:	CALL	PUSHA		; SAVE THE CURRENT "FOR"
	CALL	EXPR		; PARAMETERS
	PUSH	DE		; AND TEXT POINTER
	CALL	FNDLN		; FIND THE TARGET LINE
	JP	NZ,AHOW		; NOT THERE. SAY "HOW?"
	LD	HL,(CURRNT)	; SAVE OLD
	PUSH	HL		; 'CURRNT' OLD 'STKGOS'
	LD	HL,(STKGOS)
	PUSH	HL
	LD	HL,0		; AND LOAD NEW ONES
	LD	(LOPVAR),HL
	ADD	HL,SP
	LD	(STKGOS),HL
	JP	RUNTSL		; THEN RUN THAT LINE
RETURN: CALL	ENDCHK		; THERE MUST BE A CR
	LD	HL,(STKGOS)	; OLD STACK POINTER
	LD	A,H		; 0 MEANS NOT EXIST
	OR	L
	JP	Z,QWHAT		; SO, WE SAY "WHAT?"
	LD	SP,HL		; ELSE, RESTORE IT
RESTOR:	POP	HL
	LD	(STKGOS),HL	; AND THE OLD 'STKGOS'
	POP	HL
	LD	(CURRNT),HL	; AND THE OLD 'CURRNT'
	POP	DE		; OLD TEXT POINTER
	CALL	POPA		; OLD "FOR" PARMETERS
	JP	FINISH
;*
;**************************************************************
;*
;* *** FOR *** & NEXT ***
;*
;* 'FOR' HAS TWO FORMS:
;* 'FOR VAR=EXP1 TO EXP2 STEP EXP1' AND 'FOR VAR=EXP1 TO EXP2'
;* THE SECOND FORM MEANS THE SAME THING AS THE FIRST FORM WITH
;* EXP1=1.  (I.E., WITH A STEP OF +1.)
;* TBI WILL FIND THE VARIABLE VAR. AND SET ITS VALUE TO THE
;* CURRENT VALUE OF EXP1.  IT ALSO EVALUATES EXPR2 AND EXP1
;* AND SAVE ALL THESE TOGETHER WITH THE TEXT POINTER ETC. IN
;* THE 'FOR' SAVE AREA, WHICH CONSISTS OF 'LOPVAR', 'LOPINC',
;* 'LOPLMT', 'LOPLN', AND 'LOPPT'.  IF THERE IS ALREADY SOME-
;* THING IN THE SAVE AREA (THIS IS INDICATED BY A NON-ZERO
;* 'LOPVAR'), THEN THE OLD SAVE AREA IS SAVED IN THE STACK
;* BEFORE THE NEW ONE OVERWRITES IT.
;* TBI WILL THEN DIG IN THE STACK AND FIND OUT IF THIS SAME
;* VARIABLE WAS USED IN ANOTHER CURRENTLY ACTIVE 'FOR' LOOP.
;* IF THAT IS THE CASE THEN THE OLD 'FOR' LOOP IS DEACTIVATED.
;* (PURGED FROM THE STACK..)
;*
;* 'NEXT VAR' SERVES AS THE LOGICAL (NOT NECESSARILLY PHYSICAL)
;* END OF THE 'FOR' LOOP.  THE CONTROL VARIABLE VAR. IS CHECKED
;* WITH THE 'LOPVAR'.  IF THEY ARE NOT THE SAME, TBI DIGS IN
;* THE STACK TO FIND THE RIGHT ONE AND PURGES ALL THOSE THAT
;* DID NOT MATCH.  EITHER WAY, TBI THEN ADDS THE 'STEP' TO
;* THAT VARIABLE AND CHECK THE RESULT WITH THE LIMIT.  IF IT
;* IS WITHIN THE LIMIT, CONTROL LOOPS BACK TO THE COMMAND
;* FOLLOWING THE 'FOR'.	 IF OUTSIDE THE LIMIT, THE SAVE AREA
;* IS PURGED AND EXECUTION CONTINUES.
;*
FOR:	CALL	PUSHA		; SAVE THE OLD SAVE AREA
	CALL	SETVAL		; SET THE CONTROL VAR
	DEC	HL		; HL IS ITS ADDRESS
	LD	(LOPVAR),HL	; SAVE THAT
	LD	HL,TAB4-1	; USE 'EXEC' TO LOOK
	JP	EXEC		; EXEC,FOR THE WORD 'TO'
FR1:	CALL	EXPR		; EVALUATE THE LIMIT
	LD	(LOPLMT),HL	; SAVE THAT
	LD	HL,TAB5-1	; USE 'EXEC' TO LOOK
	JP	EXEC		; EXEC,FOR THE WORD 'STEP'
FR2:	CALL	EXPR		; FOUND IT, GET STEP
	JP	FR4
FR3:	LD	HL,1		; NOT FOUND, SET TO 1
FR4:	LD	(LOPINC),HL	; SAVE THAT TOO
	LD	HL,(CURRNT)	; SAVE CURRENT LINE #
	LD	(LOPLN),HL
	EX	DE,HL		; AND TEXT POINTER
	LD	(LOPPT),HL
	LD	BC,10		; DIG INTO STACK TO
	LD	HL,(LOPVAR)	; FIND 'LOPVAR'
	EX	DE,HL
	LD	H,B
	LD	L,B		; HL=0 NOW
	ADD	HL,SP		; HERE IS THE STACK
	JP	FR6
FR5:	ADD	HL,BC		; EACH LEVEL IS 10 DEEP
FR6:	LD	A,(HL)		; GET THAT OLD 'LOPVAR'
	INC	HL
	OR	(HL)
	JP	Z,FR7		; 0 SAYS NO MORE IN IT
	LD	A,(HL)
	DEC	HL
	CP	D		; SAME AS THIS ONE?
	JP	NZ,FR5
	LD	A,(HL)		; THE OTHER HALF?
	CP	E
	JP	NZ,FR5
	EX	DE,HL		; YES, FOUND ONE
	LD	HL,0
	ADD	HL,SP		; TRY TO MOVE SP
	LD	B,H
	LD	C,L
	LD	HL,10
	ADD	HL,DE
	CALL	MVDOWN		; AND PURGE 10 WORDS
	LD	SP,HL		; IN THE STACK
FR7:	LD	HL,(LOPPT)	; JOB DONE, RESTORE DE
	EX	DE,HL
	JP	FINISH		; AND CONTINUE

NEXT:	CALL	TSTV		; GET ADDRESS OF VAR.
	JP	C,QWHAT		; NO VARIABLE, "WHAT?"
	LD	(VARNXT),HL	; YES, SAVE IT
NX1:	PUSH	DE		; SAVE TEXT POINTER
	EX	DE,HL
	LD	HL,(LOPVAR)	; GET VAR. IN 'FOR'
	LD	A,H
	OR	L		; 0 SAYS NEVER HAD ONE
	JP	Z,AWHAT		; SO WE ASK "WHAT?"
	CALL	COMP		; ELSE WE CHECK THEM
	JP	Z,NX2		; OK, THEY AGREE
	POP	DE		; NO, LET'S SEE
	CALL	POPA		; PURGE CURRENT LOOP
	LD	HL,(VARNXT)	; AND POP ONE LEVEL
	JP	NX1		; GO CHECK AGAIN
NX2:	LD	E,(HL)		; COME HERE WHEN AGREED
	INC	HL
	LD	D,(HL)		; DE=VALUE OF VAR.
	LD	HL,(LOPINC)	; LOPINC
	PUSH	HL
	LD	A,H
	XOR	D		; S=SIGN DIFFER
	LD	A,D		; A=SIGN OF DE
	ADD	HL,DE		; ADD ONE STEP
	JP	M,NX3		; CANNOT OVERFLOW
	XOR	H		; MAY CVERFLOW
	JP	M,NX5		; AND IT DID
NX3:	EX	DE,HL
	LD	HL,(LOPVAR)	; PUT IT BACK
	LD	(HL),E
	INC	HL
	LD	(HL),D
	LD	HL,(LOPLMT)	; HL=LIMIT
	POP	AF		; OLD HL
	OR	A
	JP	P,NX4		; STEP > 0
	EX	DE,HL		; STEP < 0
NX4:	CALL	CKHLDE		; COMPARE WITH LIMIT
	POP	DE		; RESTORE TEXT POINTER
	JP	C,NX6		; OUTSIDE LIMIT
	LD	HL,(LOPLN)	; WITHIN LIMIT, GO
	LD	(CURRNT),HL	; BACK TO THE SAVED
	LD	HL,(LOPPT)	; 'CURRNT' AND TEXT
	EX	DE,HL		; POINTER
	JP	FINISH
NX5:	POP	HL		; OVERFLOW. PURGE
	POP	DE		; GARBAGE IN STACK
NX6:	CALL	POPA		; PURGE THIS LOOP
	JP	FINISH
;*
;**************************************************************
;*
;* *** REM *** IF *** INPUT *** & LET (& DEFLT) ***
;*
;* 'REM' CAN BE FOLLOWED BY ANYTHING AND IS IGNORED 8Y TBI. TBI TREATS
;* IT LIKE AN 'IF' WITH A FALSE CONDITION.
;*
;* 'IF' IS FOLLOWED BY AN EXPR. AS A CONDITION AND ONE OR MORE COMMANDS
;* (INCLUDING OTHER 'IF'S) SEPERATED BY SEMI-COLONS. NOTE THAT THE
;* WORD 'THEN' IS NOT USED. TBI EVALUATES THE EXPR. IF IT IS NON-ZERO,
;* EXECUTION CONTINUES. IF THE EXPR. IS ZERO. THE COMMANDS THAT
;* FOLLOWS ARE IGNORED AND EXECUTION CONTINUES AT THE NEXT LINE.
;*
;* 'INPUT' COMMAND IS LIKE THE 'PRINT' COMMAND. AND IS FOLLOWED BY A
;* LIST OF ITEMS. IF THE ITEM IS A STRING IN SINGLE OR DOUBLE QUOTES,
;* OR IS AN UP-ARROW. IT HAS THE SAME EFFECT AS IN 'PRINT'. IF AN ITEM
;* IS A VARIABLE. THIS VARIABLE NAME IS PRINTED OUT FOLLOWED BY A
;* COLON. THEN TBI WAITS FOR AN EXPR. TO BE TYPED IN. THE VARIABLE IS
;* THEN SET TO THE VALUE OF THIS EXPR. IF THE VARIABLE IS PROCEDED BY
;* A STRING (AGAIN IN SINGLE OR DOUBLE QUOTES), THE STRING WILL BE
;* PRINTED FOLLOWED BY A COLON. TBI THEN WAITS FOR INPUT EXPR. AND
;* SET THE VARIABLE TO THE VALUE OF THE EXPR.
;*
;* IF THE INPUT EXPR. IS INVALID, TBI WILL PRINT "WHAT?", "HOW?" OR
;* "SORRY" AND REPRINT THE PROMPT AND REDO THE INPUT. THE EXECUTION
;* WILL NOT TERMINATE UNLESS YOU TYPE CONTROL-C. THIS IS HANDLED IN
;* 'INPERR'.
;*
;* 'LET' IS FOLLOWED BY A LIST OF ITEMS SEPERATED BY COMMAS. EACH ITEM
;* CONSISTS OF A VARIABLE, AN EQUAL SIGN, AND AN EXPR. TBI EVALUATES
;* THE EXPR. AND SET THE VARIBLE TO THAT VALUE. TBI WILL ALSO HANDLE
;* 'LET' COMMAND WITHOUT THE WORD 'LET'. THIS IS DONE BY 'DEFLT'.
;*
REM:	LD	HL,0		; *** REM ***
	JP	IF1		; THIS IS LIKE 'IF 0'

IFF:	CALL	EXPR		; *** IF ***
IF1:	LD	A,H		; IS THE EXPR.=0?
	OR	L
	JP	NZ,RUNSML	; NO CONTINUE
	CALL	FNDSKP		; YES SKIP REST OF LINE
	JP	NC,RUNTSL	; RUN THE NEXT LINE
	JP	RSTART		; IF NO NEXT, RE-START

INPERR: LD	HL,(VARNXT)	; *** INPERR ***
	LD	SP,HL		; RESTORE OLD SP
	POP	HL		; AND OLD 'CURRNT'
	LD	(CURRNT),HL
	POP	DE		; AND OLD TEXT POINTER
	POP	DE		; REDO INPUT

INPUT:	.DS	0
IP1:	PUSH	DE		; SAVE IN CASE OF ERROR
	CALL	QTSTG		; IS NEXT ITEM A STRING?
	JP	IP8		; NO
IP2:	CALL	TSTV		; YES, BUT FOLLOWED BY A
	JP	C,IP5		; VARIABLE? NO.
IP3:	CALL	IP12
	LD	DE,BUFFER	; POINTS TO BUFFER
	CALL	EXPR		; EVALUATE INPUT
	CALL	ENDCHK
	POP	DE		; OK,GET OLD HL
	EX	DE,HL
	LD	(HL),E		; SAVE VALUE IN VAR.
	INC	HL
	LD	(HL),D
IP4:	POP	HL		; GET OLD 'CURRNT'
	LD	(CURRNT),HL
	POP	DE		; AND OLD TEXT POINTER
IP5:	POP	AF		; PURGE JUNK IN STACK
IP6:	TSTC("\054",IP7)	; IS NEXT CH. ','?
	JP	INPUT		; YES. MORE ITEMS.
IP7:	JP	FINISH
IP8:	PUSH	DE		; SAVE FOR 'PRTSTG'
	CALL	TSTV		; MUST BE VARIABLE NOW
	JP	NC,IP11
IP10:	JP	QWHAT		; "WHAT?" IT IS NOT?
IP11:	LD	B,E
	POP	DE
	CALL	PRTCHS		; PRINT THOSE AS PROMPT
	JP	IP3		; YES. INPUT VARIABLE
IP12:	POP	BC		; RETURN ADDRESS
	PUSH	DE		; SAVE TEXT POINTER
	EX	DE,HL
	LD	HL,(CURRNT)	; ALSO SAVE 'CURRNT'
	PUSH	HL
	LD	HL,IP1		; A NEGATIVE NUMBER
	LD	(CURRNT),HL	; AS A FLAG
	LD	HL,0		; SAVE SP TOO
	ADD	HL,SP
	LD	(STKINP),HL
	PUSH	DE		; OLD HL
	LD	A,' '		; PRINT A SPACE
	PUSH	BC
	JP	GETLN		; AND GET A LINE

DEFLT:	LD	A,(DE)		; *** DEFLT ***
	CP	XCR		; EMPTY LINE IS OK
	JP	Z,LT4		; ELSE IT IS 'LET'

LET:	.DS	0		; *** LET ***
LT2:	CALL	SETVAL
LT3:	TSTC("\054",LT4)	; SET VALUE TO VAR.
	JP	LET		; ITEM BY ITEM
LT4:	JP	FINISH		; UNTIL FINISH

;*
;**************************************************************
;*
;* *** EXPR ***
;*
;* 'EXPR' EVALUATES ARITHMETICAL OR LOGICAL EXPRESSIONS.
;* <EXPR>::=<EXPR1>
;*	    <EXPR1><REL.OP.><EXPR1>
;* WHERE <REL.OP.> IS ONE OF THE OPERATORS IN TAB6 AND THE RESULT OF
;* THESE OPERATIONS IS 1 IF TRUE AND 0 IF FALSE.
;* <EXPR1>::=(+ OR — )<EXPR2>(+ OR -<EXPR2>)(....)
;* WHERE () ARE OPTIONAL AND (....) ARE OPTIONAL REPEATS.
;* <EXPR2>::=<EXPR3>(<* OR /><EXPR3>)(....)
;* <EXPR3>::=<VARIA8LE>
;*	     <FUNCTION>
;*	     (<EXPR>)
;* <EXPR> IS RECURSIVE SO THAT VARIABLE '@' CAN HAVE AN <EXPR> AS
;* INDEX, FUNCTIONS CAN HAVE AN <EXPR> AS ARGUMENTS, AND
;* <EXPR3> CAN BE AN <EXPR> IN PARANTHESE.
;*
EXPR:	CALL	EXPR1		; *** EXPR ***
	PUSH	HL		; SAVE <EXPR1> VALUE
	LD	HL,TAB6-1	; LOOKUP REL. OP.
	JP	EXEC		; GO DO IT
XPR1:	CALL	XPR8		; REL.OP.">="
	RET	C		; NO, RETURN HL=0
	LD	L,A		; YES,RETURN HL=1
	RET
XPR2:	CALL	XPR8		; REL.OP."#"
	RET	Z		; FALSE,RETURN HL=0
	LD	L,A		; TRUE, RETURN HL=1
	RET
XPR3:	CALL	XPR8		; REL.OP.">"
	RET	Z		; FALSE
	RET	C		; ALSO FALSE, HL=0
	LD	L,A		; TRUE,HL=1
	RET
XPR4:	CALL	XPR8		; REL. OP."<="
	LD	L,A		; SET HL=1
	RET	Z		; REL. TRUE, RETURN
	RET	C
	LD	L,H		; ELSE SET HL=0
	RET
XPR5:	CALL	XPR8		; REL.OP."="
	RET	NZ		; FALSE,RETRUN HL=0
	LD	L,A		; ELSE SET HL=1
	RET
XPR6:	CALL	XPR8		; REL.OP. "<"
	RET	NC		; FALSE, RETURN HL=0
	LD	L,A		; ELSE SET HL=1
	RET
XPR7:	POP	HL		; NOT REL.OP.
	RET			; RETURN HL=<EXPR1>
XPR8:	LD	A,C		; SUBROUTINE FOR ALL
	POP	HL		; REL. OPS.'S
	POP	BC
	PUSH	HL		; REVERSE TOP OF STACK
	PUSH	BC
	LD	C,A
	CALL	EXPR1		; GET 2ND <EXPR1>
	EX	DE,HL		; VALUE IN DE NOW
	EX	(SP),HL		; 1ST <EXPR1> IN HL
	CALL	CKHLDE		; COMPARE 1ST WITH 2ND
	POP	DE		; RESTORE TEXT POINTER
	LD	HL,0		; SET HL=0, A=1
	LD	A,1
	RET

EXPR1:	TSTC("-",XP11)		; NEGATIVE SIGN?
	LD	HL,0		; YES FAKE "0-"
	JP	XP16		; TREAT LIKE SUBTRACT
XP11:	TSTC("+",XP12)		; POSITIVE SIGN? IGNORE
XP12:	CALL	EXPR2		; 1ST <EXPR2>
XP13:	TSTC("+",XP15)		; ADD?
	PUSH	HL		; YES, SAVE VALUE
	CALL	EXPR2		; GET 2ND <EXPR2>
XP14:	EX	DE,HL		; 2ND IN DE
	EX	(SP),HL		; 1ST IN HL
	LD	A,H		; COMPARE SIGN
	XOR	D
	LD	A,D
	ADD	HL,DE
	POP	DE		; RESTORE TEXT POINTER
	JP	M,XP13		; 1ST 2ND SIGN DIFFER
	XOR	H		; 1ST 2ND SIGN EQUAL
	JP	P,XP13		; SO IS RESULT
	JP	QHOW		; ELSE WE HAVE OVERFLOW
XP15:	TSTC("-",XPR9)		; SUBTRACT?
XP16:	PUSH	HL		; YES, SAVE 1ST <EXPR2>
	CALL	EXPR2		; GET 2ND <EXPR2>
	CALL	CHGSGN		; NEGATE
	JP	XP14		; AND ADD THEM

EXPR2:	CALL	EXPR3		; GET 1ST <EXPR3>
XP21:	TSTC("*",XP24)		; MULTIPLY?
	PUSH	HL		; YES SAVE 1ST
	CALL	EXPR3		; AND GET 2ND <EXPR3>
	LD	B,0		; CLEAR B FOR SIGN
	CALL	CHKSGN		; CHECK SIGN
	EX	(SP),HL
	CALL	CHKSGN		; CHECK SIGN OF 1ST
	EX	DE,HL
	EX	(SP),HL
	LD	A,H		; IS HL > 255?
	OR	A
	JP	Z,XP22		; NO
	LD	A,D		; YES, HOW ABOUT DE
	OR	D
	EX	DE,HL		; PUT SMALLER IN HL
	JP	NZ,AHOW		; ALSO >, WILL OVERFLOW
XP22:	LD	A,L		; THIS IS DUMB
	LD	HL,0		; CLEAR RESULT
	OR	A		; ADD AND COUNT
	JP	Z,XP25
XP23:	ADD	HL,DE
	JP	C,AHOW		; OVERFLOW
	DEC	A
	JP	NZ,XP23
	JP	XP25		; FINISHED
XP24:	TSTC("/",XPR9)		; DIVIDE?
	PUSH	HL		; YES,SAVE 1ST <EXPR3>
	CALL	EXPR3		; AND GET 2ND ONE
	LD	B,0		; CLEAR B FOR SIGN
	CALL	CHKSGN		; CHECK SIGN OF 2ND
	EX	(SP),HL		; GET 1ST IN HL
	CALL	CHKSGN		; CHECK SIGN OF 1ST
	EX	DE,HL
	EX	(SP),HL
	EX	DE,HL
	LD	A,D		; DIVIDE BY 0?
	OR	E
	JP	Z,AHOW		; SAY "HOW?"
	PUSH	BC		; ELSE SAVE SIGN
	CALL	DIVIDE		; USE SUBROUTINE
	LD	H,B		; RESULT IN HL NOW
	LD	L,C
	POP	BC		; GET SIGN BACK
XP25:	POP	DE		; AND TEXT POINTER
	LD	A,H		; HL MUST BE +
	OR	A
	JP	M,QHOW		; ELSE IT IS OVERFLOW
	LD	A,B
	OR	A
	CALL	M,CHGSGN	; CHANGE SIGN IF NEEDED
	JP	XP21		; LOOK FOR MORE TERMS

EXPR3:	LD	HL,TAB3-1	; FIND FUNCTION IN TAB3
	JP	EXEC		; AND GO DO IT
NOTF:	CALL	TSTV		; NO, NOT A FUNCTION
	JP	C,XP32		; NOR A VARIABLE
	LD	A,(HL)		; VARIABLE
	INC	HL
	LD	H,(HL)		; VALUE IN HL
	LD	L,A
	RET
XP32:	CALL	TSTNUM		; OR IS IT A NUMBER
	LD	A,B		; # OF DIGIT
	OR	A
	RET	NZ		; OK
PARN:	TSTC("(",XPR0)		; NO DIGIT, MUST BE
PARNP:	CALL	EXPR		; "(EXPR)"
	TSTC("\051",XPR0)	; )
XPR9:	RET
XPR0:	JP	QWHAT		; ELSE SAY "WHAT?"

RND:	CALL	PARN		; *** RND(EXPR) ***
	LD	A,H		; EXPR MUST BE +
	OR	A
	JP	M,QHOW
	OR	L		; AND NON-ZERO
	JP	Z,QHOW
	PUSH	DE		; SAVE BOTH
	PUSH	HL
	LD	HL,(RANPNT)	; GET MEMORY AS RANDOM
	LD	DE,RANEND
	CALL	COMP
	JP	C,RA1		; WRAP AROUND IF LAST
	LD	HL,BOTROM
RA1:	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	LD	(RANPNT),HL
	POP	HL
	EX	DE,HL
	PUSH	BC
	CALL	DIVIDE		; RND(N)=MOD(M,N)+1
	POP	BC
	POP	DE
	INC	HL
	RET

ABS:	CALL	PARN		; *** ABS(EXPR) ***
	DEC	DE
	CALL	CHKSGN		; CHECK SIGN
	INC	DE
	RET

SIZE:	LD	HL,(TXTUNF)	; *** SIZE ***
	PUSH	DE		; GET THE NUMBER OF FREE
	EX	DE,HL		; BYTES BETWEEN 'TXTUNF'
	LD	HL,(TXTLMT)	; AND 'TXTLMT'
	CALL	SUBDE
	POP	DE
	RET
;*
;**************************************************************
;*
;* *** DIVIDE *** SUBDE *** CHKSGN *** CHGSGN *** & CKHLDE ***
;*
;* 'DIVIDE' DIVIDES HL BY DE, RESULT IN BC, REMAINDER IN HL
;*
;* 'SUBDE' SUBTRACTS DE FROM HL
;*
;* 'CHKSGN' CHECKS SIGN OF HL. IF +, NO CHANGE. IF -, CHANGE SIGN AND
;* FLIP SIGN OF B.
;*
;* 'CHGSGN' CHANGES SIGN OF HL AND B UNCONDITIONALLY.
;*
;* 'CKHLDE' CHECKS SIGN OF HL AND DE. IF DIFFERENT, HL AND DE ARE
;* INTERCHANGED. IF SAME SIGN, NOT INTERCHANGED.  EITHER, CASE, HL DE
;* ARE THEN COMPARED TO SET THE FLAGS.
;*
DIVIDE: PUSH	HL		; *** DIVIDE ***
	LD	L,H		; DIVIDE H BY DE
	LD	H,0
	CALL	DV1
	LD	B,C		; SAVE RESULT IN B
	LD	A,L		; (REMAINDER+L)/DE
	POP	HL
	LD	H,A
DV1:	LD	C,-1		; RESULT IN C
DV2:	INC	C		; DUMB ROUTINE
	CALL	SUBDE		; DIVIDE BY SUBTRACT
	JP	NC,DV2		; AND COUNT
	ADD	HL,DE
	RET

SUBDE:	LD	A,L		; *** SUBDE ***
	SUB	E		; SUBTRACT DE FROM
	LD	L,A		; HL
	LD	A,H
	SBC	A,D
	LD	H,A
	RET

CHKSGN: LD	A,H		; *** CHKSGN ***
	OR	A		; CHECK SIGN OF HL
	RET	P		; IF -, CHANGE SIGN

CHGSGN: LD	A,H		; *** CHGSGN ***
	OR	L
	RET	Z
	LD	A,H
	PUSH	AF
	CPL			; CHANGE SIGN OF HL
	LD	H,A
	LD	A,L
	CPL
	LD	L,A
	INC	HL
	POP	AF
	XOR	H
	JP	P,QHOW
	LD	A,B		; AND ALSO FLIP B
	XOR	80H
	LD	B,A
	RET

CKHLDE: LD	A,H
	XOR	D		; SAME SIGN?
	JP	P,CK1		; YES, COMPARE
	EX	DE,HL		; NO, XCH AND COMP
CK1:	CALL	COMP
	RET

COMP:	LD	A,H		; *** COMP ***
	CP	D		; COMPARE HL WITH DE
	RET	NZ		; RETURN CORRECT C AND
	LD	A,L		; Z FLAGS
	CP	E		; BUT OLD A IS LOST
	RET
;*
;**************************************************************
;*
;* *** SETVAL *** FIN *** ENDCHK *** & ERROR (& FRIENDS) ***
;*
;* "SETVAL" EXPECTS A VARIABLE. FOLLOWEO BV AN EQUAL SIGN AND THEN AN
;* EXPR. IT EVALUATES THE EXPR. AND SET THE VARIABLE TO THAT VALUE.
;*
;* "FIN" CHECKS THE END OF A COMMAND. IF IT ENDED WITH ";", EXECUTION
;* CONTINUES. IF IT ENDED WITH A CR. IT FINDS THE NEXT LINE AND
;* CONTINUE FROM THERE.
;*
;* "ENDCHK" CHECKS IF A COMMAND IS ENDED WITH CR. THIS IS REQUIRED IN
;* CERTAIN COMMANDS. (GOTO, RETURN, AND STOP ETC.)
;*
;* "ERROR" PRINTS THE STRING POINTED BY DE (ANO ENDS WITH CR). IT THEN
;* PRINTS THE LINE POINTED BY 'CURRNT' WITH A "?" INSERTED AT WHERE THE
;* OLD TEXT POINTER (SHOULD BE ON TOP OF THE STACK) POINTS TO.
;* EXECUTION OF TB IS STOPPED AND TBI IS RESTARTED. HOWEVER, IF
;* 'CURRNT' -> ZERO (INDICATING A DIRECT COMMAND), THE DIRECT COMMAND
;* IS NOT
;* PRINTED. AND IF 'CURRNT' -> NEGATIVE # (INDICATING 'INPUT'
;* COMMAND. THE INPUT LINE IS NOT PRINTED AND EXECUTION IS NOT
;* TERMINATED BUT CONTINUED AT 'INPERR'.
;*
;* RELATED TO 'ERROR' ARE THE FOLLOWING: 'QWHAT' SAVES TEXT POINTER IN
;* STACK AND GET MESSAGE "WHAT?" 'AWHAT' JUST GET MESSAGE "WHAT?" AND
;* JUMP TO 'ERROR'. 'QSORRY' AND 'ASORRY' DO SAME KIND OF THING.
;* 'QHOW' AND 'AHOW' IN THE ZERO PAGE SECTION ALSO DO THIS
;*
SETVAL: CALL	TSTV		; *** SETVAL ***
	JP	C,QWHAT		; "WHAT?" NO VARIABLE
	PUSH	HL		; SAVE ADDRESS OF VAR.
	TSTC("=",SV1)		; PASS "=" SIGN
	CALL	EXPR		; EVALUATE EXPR.
	LD	B,H		; VALUE IN BC NOW
	LD	C,L
	POP	HL		; GET ADDRESS
	LD	(HL),C		; SAVE VALUE
	INC	HL
	LD	(HL),B
	RET

FINISH: CALL	FIN		; CHECK END OF COMMAND
SV1:	JP	QWHAT		; PRINT "WHAT?" IF WRONG

FIN:	TSTC(";",FI1)		; *** FIN ***
	POP	AF		; ";", PURGE RET ADDR.
	JP	RUNSML		; CONTINUE SAME LINE
FI1:	TSTC("\015",FI2)	; NOT ";", IS IT XCR?
	POP	AF		; YES, PURGE RET ADDR.
	JP	RUNNXL		; RUN NEXT LINE
FI2:	RET			; ELSE RETURN TO CALLER

IGNBLK: LD	A,(DE)		; *** IGNBLK ***
	CP	' '		; IGNORE BLANKS
	RET	NZ		; IN TEXT (WHERE DE->)
	INC	DE		; AND RETURN THE FIRST
	JP	IGNBLK		; NON-BLANK CHAR. IN A

ENDCHK: CALL	IGNBLK		; *** ENDCHK ***
	CP	XCR		; END WITH CR?
	RET	Z		; OR, ELSE SAY: "WHAT?"


QWHAT:	PUSH	DE		; *** QWHAT ***
AWHAT:	LD	DE,WHAT		; *** AWHAT ***
ERROR:	CALL	CRLF
	CALL	PRTSTG		; PRINT ERROR MESSAGE
	LD	HL,(CURRNT)	; GET CURRENT LINE #
	PUSH	HL
	LD	A,(HL)		; CHECK THE VALUE
	INC	HL
	OR	(HL)
	POP	DE
	JP	Z,TELL		; IF ZERO, JUST RESTART
	LD	A,(HL)		; IF NEGATIVE,
	OR	A
	JP	M,INPERR	; REDO INPUT
	CALL	PRTLN		; ELSE PRINT THE LINE
	POP	BC
	LD	B,C
	CALL	PRTCHS
	LD	A,'?'		; PRINT A "?"
	CALL	OUTCH
	CALL	PRTSTG		; LINE
	JP	TELL		; THEN RESTART
QSORRY: PUSH	DE		; *** QSORRY ***
ASORRY: LD	DE,SORRY	; *** ASORRY ***
	JP	ERROR
;*
;**************************************************************
;*
;* *** FNDLN (& FRIENDS) ***
;*
;* 'FNDLN' FINDS A LINE WITH A GIVEN LINE # (IN HL) IN THE TEXT SAVE
;* AREA. DE IS USED AS THE TEXT POINTER. IF THE LINE IS FOUND, DE
;* WILL POINT TO THE BEGINNING OF THAT LINE (I.E., THE LCW BYTE OF THE
;* LINE #), AND FLAGS ARE NC & Z. IF THAT LINE IS NOT THERE AND A LINE
;* WITH A HIGHER LINE # IS FOUND, DE POINTS TO THERE ANO FLAGS ARE NC &
;* NZ. IF WE REACHED THE END OF TEXT SAVE ARE AND CANNOT FIND THE
;* LINE, FLAGS ARE C & NZ. 'FNDLN' WILL INITIALIZE DE TO THE BEGINNING
;* OF THE TEXT SAVE AREA TO START THE SEARCH. SOME OTHER ENTRIES OF
;* THIS ROUTINE WILL NOT INITIALIZE DE AND DO THE SEARCH. 'FNDLP'
;* WILL START WITH DE AND SEARCH FOR THE LINE #. 'FNDNXT' WILL BUMP DE
;* BY 2, FIND A CR AND THEN START SEARCH. 'FNDSKP' USE DE TO FIND A
;* CR. AND THEN START SEARCH.
;*
FNDLN:	LD	A,H		; *** FNDLN ***
	OR	A		; CHECK SIGN OF HL
	JP	M,QHOW		; IT CANNOT BE -
	LD	DE,TEXT		; INIT. TEXT POINTER

FNDLP:	INC	DE
	LD	A,(DE)
	DEC	DE
	ADD	A,A
	RET	C		; C,NZ PASSED END
	LD	A,(DE)		; WE DID NOT, GET BYTE 1
	SUB	L		; IS THIS THE LINE?
	LD	B,A		; COMPARE LOW ORDER
	INC	DE
	LD	A,(DE)		; GET BYTE 2
	SBC	A,H		; COMPARE HIGH ORDER
	JP	C,FL1		; NO, NOT THERE YET
	DEC	DE		; ELSE WE EITHER FOUND
	OR	B		; IT, OR IT IS NOT THERE
	RET			; NC,Z:FOUND; NC,NZ:NO

FNDNXT: INC	DE		; FIND NEXT LINE
FL1:	INC	DE		; JUST PAST BYTE 1 & 2

FNDSKP: LD	A,(DE)		; *** FNDSKP ***
	CP	XCR		; TRY TO FIND OR
	JP	NZ,FL1		; KEEP LOOKING
	INC	DE		; FOUND CR, SKIP OVER
	JP	FNDLP		; CHECK IF END OF TEXT

TSTV:	CALL	IGNBLK		; *** TSTV ***
	SUB	'@'		; TEST VARIABLES
	RET	C		; C:NOT A VARIABLE
	JP	NZ,TV1		; NOT "@" ARRAY
	INC	DE		; IT IS THE "@" ARRAY
	CALL	PARN		; @ SHOULD BE FOLLOWED
	ADD	HL,HL		; BY (EXPR) AS ITS INDEX
	JP	C,QHOW		; IS INDEX TOO BIG?
TSTB:	PUSH	DE		; WILL IT OVERWRITE?
	EX	DE,HL		; TEST?
	CALL	SIZE		; FIND SIZE OF FREE, CALL SIZE
	CALL	COMP		; AND CHECK THAT
	JP	C,ASORRY	; IF SO, SAY "SORRY"
	CALL	LOCR		; IF FITS, GET ADDRESS
	ADD	HL,DE		; OF @(EXPR) AND PUT IT
	POP	DE		; IN HL
	RET			; C FLAG IS CLEARED
TV1:	CP	27		; NOT @, IS IT A TO Z?
	CCF			; IF NOT RETURN C FLAG
	RET	C
	INC	DE		; IF A THROUGH Z
	LD	HL,VARBGN-2
	RLCA			; HL->VARIABLE
	ADD	A,L		; RETURN
	LD	L,A		; WITH C FLAG CLEARED
	LD	A,0
	ADC	A,H
	LD	H,A
	RET
;*
;**************************************************************
;*
;* *** TSTCH *** TSTNUM ***
;*
;* TSTCH IS USED TO TEST THE NEXT NON-BLANK CHARACTER IN THE TEXT
;* (P0INTED BY DE) AGAINST THE CHARACTER THAT FOLLOWS THE CALL. IF
;* THEY DO NOT MATCH. N BYTES OF CODE WILL 0E SKIPPED OVER. WHERE N IS
;* BETWEEN 0 AND 255 AND IS STORED IN THE SECOND BYTE FOLLOWING THE
;* CALL.
;*
;* TSTNUM IS USED TO CHECK WHETHER THE TEXT (POINTED BY OE) IS A
;* NUMBER. IF A NUMBER IS FOUNO. B WILL BE NON-ZERO AND HL WILL
;* CONTAIN THE VALUE (IN BINARY) OF THE NUMBER, ELSE B AND HL ARE 0.
;*
TSTCH:	EX	(SP),HL		; *** TSTCH ***
	CALL	IGNBLK		; IGNORE LEADING BLANKS
	CP	(HL)		; AND TEST THE CHARACTER
	INC	HL		; COMPARE THE BYTE THAT
	JP	Z,TC1		; FOLLOWS THE CALL INSTR.
	PUSH	BC		; WITH THE TEXT (DE ->)
	LD	C,(HL)		; IF NOT=. ADD THE 2ND
	LD	B,00H		; BYTE THAT FOLLOWS THE
	ADD	HL,BC		; RST TO THE OLD PC
	POP	BC		; I.E., DO A RELATIVE
	DEC	DE		; JUMP IF NOT =
TC1:	INC	DE		; IF =, SKIP THOSE BYTES
	INC	HL		; AND CONTINUE
	EX	(SP),HL
	RET

TSTNUM: LD	HL,0		; *** TSTNUM ***
	LD	B,H		; TEST IF THE TEXT IS
	CALL	IGNBLK		; A NUMBER
TN1:	CP	'0'		; IF NOT,RETURN 0 IN
	RET	C		; B AND HL
	CP	3AH		; IF NUMBERS, CONVERT
	RET	NC		; TO BINARY IN HL AND
	LD	A,0F0H		; SET A TO # OF DIGITS
	AND	H		; IF H>255, THERE IS NO
	JP	NZ,QHOW		; ROOM FOR NEXT DIGIT
	INC	B		; B COUNTS # OF DIGITS
	PUSH	BC		;
	LD	B,H		; HL=10*HL+(NEW DIGIT)
	LD	C,L
	ADD	HL,HL		; WHERE 10* IS DONE BY
	ADD	HL,HL		; SHIFT AND ADD
	ADD	HL,BC
	ADD	HL,HL
	LD	A,(DE)		; AND (DIGIT) IS FROM
	INC	DE		; STRIPPING THE ASCII
	AND	0FH		; CODE
	ADD	A,L
	LD	L,A
	LD	A,0
	ADC	A,H
	LD	H,A
	POP	BC
	LD	A,(DE)		; DO THIS DIGIT AFTER
	JP	P,TN1		; DIGIT. S SAYS OVERFLOW
QHOW:	PUSH	DE		; *** ERROR: "HOW?" ***
AHOW:	LD	DE,HOW
	JP	ERROR
;*
;**************************************************************
;*
;* *** MVUP *** MVDUWN *** POPA *** & PUSHA ***
;*
;* 'MVUP' MOVES A BLOCK UP FROM WHERE DE-> TO WHERE BC-> UNTIL DE = HL
;*
;* 'MVDOWN' MOVES A BLOCK DOWN FROM WHERE DE-> TO WHERE HL-> UNTIL DE =
;* BC
;*
;* 'POPA' RESTORES THE 'FOR* LOOP VARIABLE SAVE AREA FROM THE STACK
;*
;* 'PUSHA' STACKS THE 'FOR' LOOP VARIABLE SAVE AREA INTO THE STACK
;*
MVUP:	CALL	COMP		; *** MVUP ***
	RET	Z		; DE = HL, RETURN
	LD	A,(DE)		; GET ONE BYTE
	LD	(BC),A		; MOVE IT
	INC	DE		; INCREASE BOTH POINTERS
	INC	BC
	JP	MVUP		; UNTIL DONE

MVDOWN: LD	A,B		; *** MVDOWN ***
	SUB	D		; TEST IF DE = BC
	JP	NZ,MD1		; NO, GO MOVE
	LD	A,C		; MAYBE, OTHER BYTE?
	SUB	E
	RET	Z		; YES, RETURN
MD1:	DEC	DE		; ELSE MOVE A BYTE
	DEC	HL		; BUT FIRST DECREASE
	LD	A,(DE)		; BOTH POINTERS AND
	LD	(HL),A		; THEN DO IT
	JP	MVDOWN		; LOOP BACK

POPA:	POP	BC		; BC = RETURN ADDR.
	POP	HL		; RESTORE LOPVAR, BUT
	LD	(LOPVAR),HL	; =0 MEANS NO MORE
	LD	A,H
	OR	L
	JP	Z,PP1		; YES, GO RETURN
	POP	HL		; NOP, RESTORE OTHERS
	LD	(LOPINC),HL
	POP	HL
	LD	(LOPLMT),HL
	POP	HL
	LD	(LOPLN),HL
	POP	HL
	LD	(LOPPT),HL
PP1:	PUSH	BC		; BC = RETURN ADDR.
	RET

PUSHA:	LD	HL,STKLMT	; *** PUSHA ***
	CALL	CHGSGN
	POP	BC		; BC=RETURN ADDRESS
	ADD	HL,SP		; IS STACK NEAR THE TOP?
	JP	NC,QSORRY	; YES, SORRY FOR THAT.
	LD	HL,(LOPVAR)	; ELSE SAVE LOOP VAR.S
	LD	A,H		; BUT IF LOPVAR IS 0
	OR	L		; THAT WILL BE ALL
	JP	Z,PU1
	LD	HL,(LOPPT)	; ELSE, MORE TO SAVE
	PUSH	HL
	LD	HL,(LOPLN)
	PUSH	HL
	LD	HL,(LOPLMT)
	PUSH	HL
	LD	HL,(LOPINC)
	PUSH	HL
	LD	HL,(LOPVAR)
PU1:	PUSH	HL
	PUSH	BC		; BC = RETURN ADDR.
	RET

LOCR:	LD	HL,(TXTUNF)
	DEC	HL
	DEC	HL
	RET
;*
;*************************************************************
;*
;* *** PRTSTG *** QTSTG *** PRTNUH *** & PRTLN ***
;*
;* 'PRTSTG' PRINTS A STRING POINTED 8Y DE. IT STOPS PRINTING AND
;* RETURNS TO CALLER WHEN EITHER A CR IS PRINTED OR WHEN THE NEXT BYTE
;* IS ZERO. REG. A AND B ARE CHANGED. REG. DE POINTS TO WHAT FOLLOWS
;* THE CR OR TO THE ZERO.
;*
;* 'QTSTG' LOOKS FOR UP-ARROW. SINGLE QUOTE, OR DOUBLE QUGTE. IF NONE
;* OF THESE, RETURN TO CALLER. IF UP-ARROW. OUTPUT A CONTROL
;* CHARACTER. IF SINGLE OR DOUBLE QUOTE. PRINT THE STRING IN THE QUOTE
;* AND DEMANDS A MATCHING UNQUOTE. AFTER THE PRINTING THE NEXT 3 BYTES
;* OF THE CALLER IS SKIPPED OVER (USUALLY A JUMP INSTRUCTION).
;*
;* 'PRTNUM' PRINTS THE NUMBER IN HL. LEADING BLANKS ARE ADDED IF
;* NEEDED TO PAD THE NUMBcR OF SPACES TO THE NUMBER IN C. HOWEVER, IF
;* THE NUMBER OF DIGITS IS LARGER THAN THE # IN C, ALL DIGITS ARE
;* PRINTED ANYWAY. NEGATIVE SIGN IS ALSO PRINTED ANC COUNTED IN.
;* POSITIVE SIGN IS NOT.
;*
;* 'PRTLN' FINDS A SAVED LINE. PRINTS THE LINE # AND A SPACE.
;*
PRTSTG: SUB	A		; *** PRTSTG ***
PS1:	LD	B,A
PS2:	LD	A,(DE)		; GET A CHARACTER
	INC	DE		; BUMP POINTER
	CP	B		; SAME AS OLD A?
	RET	Z		; YES, RETURN
	CALL	OUTCH		; ELSE PRINT IT
	CP	XCR		; WAS IT A CR?
	JP	NZ,PS2		; NO, NEXT
	RET			; YES, RETURN

QTSTG:	TSTC("\"",QT3)		; *** QTSTG ***
	LD	A,22H		; "
QT1:	CALL	PS1		; PRINT UNTIL ANOTHER
QT2:	CP	XCR		; WAS LAST ONE A CR?
	POP	HL		; RETURN ADDRESS
	JP	Z,RUNNXL	; WAS CR, RUN NEXT LINE
	INC	HL		; SKIP 3 BYTES ON RETURN
	INC	HL
	INC	HL
	JP	(HL)		; RETURN
QT3:	TSTC("\047",QT4)	; IS IT A '
	LD	A,27H		; YES, DO SAME
	JP	QT1		; AS IN "
QT4:	TSTC("^",QT5)		; IS IT UP-ARROW?
	LD	A,(DE)
	XOR	40H
	CALL	OUTCH
	LD	A,(DE)		; JUST IN CASE IT IS A CR
	INC	DE
	JP	QT2
QT5:	RET			; NONE OF ABOVE

PRTCHS: LD	A,E
	CP	B
	RET	Z
	LD	A,(DE)
	CALL	OUTCH
	INC	DE
	JP	PRTCHS

PRTNUM:	.DS	0		; *** PRTNUM ***
PN3:	LD	B,0		; B=SIGN
	CALL	CHKSGN		; CHECK SIGN
	JP	P,PN4		; NO SIGN
	LD	B,'-'		; B=SIGN
	DEC	C		; '-' TAKES SPACE
PN4:	PUSH	DE
	LD	DE,10		; DECIMAL
	PUSH	DE		; SAVE AS A FLAG
	DEC	C		; C=SPACES
	PUSH	BC		; SAVE SIGN & SPACE
PN5:	CALL	DIVIDE		; DIVIDE HL BY 10
	LD	A,B		; RESULT 0?
	OR	C
	JP	Z,PN6		; YES, WE GOT ALL
	EX	(SP),HL		; NO, SAVE REMAINDER
	DEC	L		; AND COUNT SPACE
	PUSH	HL		; HL IS OLD BC
	LD	H,B		; MOVE RESULT TO BC
	LD	L,C
	JP	PN5		; AND DIVIDE BY 10
PN6:	POP	BC		; WE GOT ALL DIGITS IN
PN7:	DEC	C		; THE STACK
	LD	A,C		; LOOK AT SPACE COUNT
	OR	A
	JP	M,PN8		; NO LEADING BLANKS
	LD	A,' '		; LEADING BLANKS
	CALL	OUTCH
	JP	PN7		; MORE?
PN8:	LD	A,B		; PRINT SIGN
	OR	A
	CALL	NZ,OUTCH	; MAYBE - OR NULL
	LD	E,L		; LAST REMAINDER IN E
PN9:	LD	A,E		; CHECK DIGIT IN E
	CP	10		; 10 IS FLAG FOR NO MORE
	POP	DE
	RET	Z		; IF SO, RETURN
	ADD	A,'0'		; ELSE CONVERT TO ASCII
	CALL	OUTCH		; AND PRINT THE DIGIT
	JP	PN9		; GO BACK FOR MORE

PRTLN:	LD	A,(DE)		; *** PRTLN ***
	LD	L,A		; LOW ORDER LINE #
	INC	DE
	LD	A,(DE)		; HIGH ORDER
	LD	H,A
	INC	DE
	LD	C,4		; PRINT 4 DIGIT LINE NO.
	CALL	PRTNUM		; CALL PRTNUM
	LD	A,' '		; FOLLOWED BY A SPACE
	JP	OUTCH
	RET

TAB1:	ITEM("LIST",LIST)	; DIRECT COMMANDS
	ITEM("NEW",NEW)
	ITEM("RUN",RUN)
TAB2:	ITEM("NEXT",NEXT)	; DIRECT/STATEMENT
	ITEM("LET",LET)
	ITEM("IF",IFF)
	ITEM("GOTO",GOTO)
	ITEM("GOSUB",GOSUB)
	ITEM("RETURN",RETURN)
	ITEM("REM",REM)
	ITEM("FOR",FOR)
	ITEM("INPUT",INPUT)
	ITEM("PRINT",PRINT)
	ITEM("STOP",STOP)
	ITEM(,MOREC)

MOREC:	JP	DEFLT

TAB3:	ITEM("RND",RND)		; FUNCTIONS
	ITEM("ABS",ABS)
	ITEM("SIZE",SIZE)
	ITEM(,MOREF)

MOREF:	JP	NOTF

TAB4:	ITEM("TO",FR1)		; "FOR" COMMAND
	ITEM(,QWHAT)

TAB5:	ITEM("STEP",FR2)	; "FOR" COMMAND
	ITEM(,FR3)

TAB6:	ITEM(">=",XPR1)		; RELATION OPERATORS
	ITEM("#",XPR2)
	ITEM(">",XPR3)
	ITEM("=",XPR5)
	ITEM("<=",XPR4)
	ITEM("<",XPR6)
	ITEM(,XPR7)
RANEND:	.EQU *
;*
;*************************************************************
;*
;* *** INPUT OUTPUT ROUTINES ***
;*
;* USER MUST VARIFY AND/OR MODIFY THESE ROUTINES
;*
;*************************************************************
;*
;* *** CRLF *** OUTCH ***
;*
;* CRLF WILL OUTPUT A CR, ONLY A & FLAGS MAY CHANGE AT RETURN
;*
;* OUTCH WILL OUTPUT THE CHARACTER IN A. IF THE CHARACTER IS CR. IT
;* WILL ALSO OUTPUT A LF AND THREE NULLS. FLAGS MAY CHANGE AT RETURN,
;* OTHER REGISTERS DO NCT.
;*
;* *** CHKIO *** GETLN ***
;*
;* CHKIO CHECKS TO SEE IF THERE IS ANY INPUT. IF NO INPUT, IT RETURNS
;* WITH Z FLAG. IF THERE IS INPUT, IT FURTHER CHECKS WHETHER INPUT IS
;* CONTROL—C. IF NOT CONTROL-C, IT RETURNS THE CHARACTER IN A WITH Z
;* FLAG CLEARED. IF INPUT IS CONTROL-C. CHKIO JUMPS TO 'INIT' AND WILL
;* NOT RETURN. ONYL A & FLAGS MAY CHANGE AT RETURN.
;*
;* 'GETLN' READS A INPUT LINE INTO 'BUFFER'. IT FIRST PROMPT THE
;* CHARACTER IN A (GIVEN EY THE CALLER), THEN IT FILLS THE BUFFER
;* AND ECHOS. BACK—SPACE IS USED TO DELETE THE LAST CHARACTER (IF THERE
;* IS ONE). CR SIGNALS THE END OF THE LINE. AND CAUSE 'GETLN' TO
;* RETURN. WHEN BUFFER IS FULL, 'GETLN* WILL ACCEPT BACK-SPACE OR CR
;* ONLY AND WILL IGNORE (AND WILL NOT ECHO) OTHER CHARACTERS. AFTER
;* THE INPUT LINE IS STORED IN THE BUFFER, TWO MORE EYTES OF FF ARE
;* ALSO STORED AND DE POINTS TO THE LAST FF. A & FLAGS ARE ALSO
;* CHANGED AT RETURN.
;*
CRLF:	LD	A,XCR		; XCR IN A

OUTCH:	JP	XOUTX		; *** JMP USER-OUTPUT ***

CHKIO:	JP	XINX		; *** JMP USER-INPUT ****

GETLN:	LD	DE,BUFFER	; ***** MODIFY THIS *****
GL1:	CALL	OUTCH		; PROMPT OR ECHO
GL2:	CALL	CHKIO		; GET A CHARACTER
	JP	Z,GL2		; WAIT FOR INPUT
	CP	XLF
	JP	Z,GL2
GL3:	LD	(DE),A		; SAVE CH.
	CP	008H		; IS IT BACK-SPACE?
	JP	NZ,GL4		; NO, MORE TESTS
	LD	A,E		; YES, DELETE?
	CP	BUFFER&0FFH
	JP	Z,GL2		; NOTHING TO DELETE
	LD	A,(DE)		; DELETE
	DEC	DE
	JP	GL1
GL4:	CP	XCR		; WAS IT CR?
	JP	Z,GL5		; YES, END OF LINE
	LD	A,E		; ELSE, MORE FREE ROOM?
	CP	BUFEND&0FFH
	JP	Z,GL2		; NO. WAIT FOR CR/RUB-OUT
	LD	A,(DE)		; YES, BUMF POINTER
	INC	DE
	JP	GL1
GL5:	INC	DE		; END OF LINE
	INC	DE		; BUMP POINTER
	LD	A,0FFH		; PUT MARKER AFTER IT
	LD	(DE),A
	DEC	DE
	JP	CRLF

XOUTX:
;;	PUSH	AF		; OUTPUT ROUTINE
;;OT1:	IN	A,(0)		; PRINT WHAT IS IN A
;;	AND	001H		; TBE BIT
;;	JP	Z,OT1		; WAIT UNTIL READY
;;	POP	AF
;;	OUT	(1),A
	RST	08H		; monz80 putc
	CP	XCR		; WAS IT CR?
	RET	NZ		; NO, RETURN
	LD	A,XLF		; YES, GIVE LF
	CALL	XOUTX
	LD	A,XCR
	RET

XINX:
;;	IN	A,(0)
;;	AND	002H		; DAV BIT
	RST	18H		; monz80 keybd
	RET	Z		; NO INPUT, RETURN ZERO
;;	IN	A,(1)		; CHECK INPUT
;;	AND	07FH
	RST	10H		; monz80 getc
	CP	7FH		;; DLE?
	JR	Z,XINX		;; IGNORE
	CP	003H		; IS IT CONTROL-C?
	RET	NZ		; NO, RETURN CH.
	JP	INIT		; YES, RESTART
	.END
