;***************************************************************
;*
;*
;*      	TINY BASIC FOR INTEL 8086
;*
;*
;*      		VERSION: 1.1
;*
;*      			BY
;*
;*      		MICHAEL SULLIVAN
;*                              BASED
;*                               ON
;*                       LI-CHEN WANG'S
;*
;*                    8080 TINY BASIC
;*
;*
;*                    27 JUNE 1982
;*
;*		@COPYLEFT
;*		ALL WRONGS RESERVED
;*
;*	NOTE:
;*		8080 REGISTERS HAVE BEEN MAPPED AS FOLLOWS:
;*
;*		8080		8086
;*	-------------------------------------
;*
;*		BC	<->	CX
;*		DE	<->	DX
;*		HL	<->	BX
;*
;*
;*	VERS 1.1 - SUPPORT MS-DOS INTERUPT I/O
;*		   IMPROVE RND ACTION
;*		   SUPPORT TIME AND DATE FROM MS-DOS
;*
;**************************************************************
	;
	;
	ORG	100H	;STANDARD MS-DOS START ADDR.
START:
	MOV	SP,STACK	;SET UP STACK
	MOV	DX,MSG1	;GET SIGN-ON MSG
	CALL	PRTSTG	;SEND IT
	MOV	B,[BUFFER-2],80H ;INIT CMD LINE BUFFER
;
;	MAIN
;
; THIS IS THE MAIN LOOP THAT COLLECTS THE TINY BASIC PROGRAM
; AND STORES IT IN MEMORY.
;
; AT START, IT PRINTS OUT "(CR)OK(LF)", AND INITIALIZES THE
; STACK AND SOME OTHER INTERNAL VARIABLES. THEN IT PROMPTS
; ">" AND READS A LINE. IF THE LINE STARTS WITH A NONZERO
; NUMBER, THIS NUMBER IS THE LINE NUMBER. THE LINE NUMBER
; (IN 16 BIT BINARY) AND THE REST OF THE LINE (INCLUDING
; ITS (CR))IS STORED IN MEMORY. IF A LINE WITH THE SAME
; LINE NUMBER IS ALREADY THERE, IT IS REPLACED BY THE NEW
; ONE. IF THE REST OF THE LINE CONSISTS OF A (CR) ONLY, IT
; IS STORED AND ANY EXISTING LINE WITH THE SAME LINE
; NUMBER IS DELETED.
;
; AFTER A LINE IS INSERTED, REPLACED, OR DELETED, THE
; PROGRAM LOOPS BACK AND ASKS FOR ANOTHER LINE. THIS LOOP
; WILL BE TERMINATED WHEN IT READS A LINE WITH ZERO OR NO
; LINE NUMBER: CONTROL IS THEN TRANSFERED TO "DIRECT".
;
; THE TINY BASIC PROGRAM SAVE AREA STARTS AT THE MEMORY
; LOCATION LABELED "TXTBGN" AND ENDS AT "TXTEND". WE ALWAYS
; FILL THIS AREA STARTING AT "TXTBGN", THE UNFILLED PORTION
; POINTED TO BY THE CONTENTS OF THE MEMORY LOCATION LABELED
; "TXTUNF".
;
; THE MEMORY LOCATION "CURRNT" POINTS TO THE LINE NUMBER
; THAT IS CURRENTLY BEING INTERPRETED. WHILE WE AR IN THIS
; LOOP OR WHILE WE ARE INTERPRETING A DIRECT COMMAND
; (SEE NEXT SECTION), "CURRNT" SHOULD POINT TO A 0.
;
RSTART:
	MOV	SP,STACK	;SET STACK POINTER
ST1:
	CALL	CRLF
	MOV	DX,OK	;DE->STRING
	SUB	AL,AL
	CALL	PRTSTG	;PRINT PROMPT
	MOV	W,[CURRNT],0 ;CURRENT LINE # = 0
ST2:
	MOV	W,[LOPVAR],0
	MOV	W,[STKGOS],0
ST3:
	MOV	AL,'>'	;PROMPT ">" NOW
	CALL	GETLN	;READ A LINE
	PUSH	DI	;DI -> END OF LINE
ST3A:
	MOV	DX,BUFFER ;DX -> BEGINNING OF LINE
	CALL	TSTNUM	;TEST IF IT'S A NUMBER
	MOV	AH,0
	CALL	IGNBLNK
	OR	BX,BX	;BX:= VALUE OF # OR 0 IF NO # FOUND
	POP	CX	;CX -> END OF LINE
	JNZ	ST3B
	JMP	DIRECT
ST3B:
	DEC	DX
	DEC	DX
	MOV	AX,BX	;GET LINE #
	MOV	DI,DX
	STOW		;VALUE OF LINE # THERE
	PUSH	CX
	PUSH	DX	;BX,DX -> BEGIN,END
	MOV	AX,CX
	SUB	AX,DX
	PUSH	AX	;AX:= # BYTES IN LINE
	CALL	FNDLN	;FIND THIS LINE IN SAVE
	PUSH	DX	;AREA, DX -> SAVE AREA
	JNZ	ST4	;NZ:NOT FOUND, INSERT
	PUSH	DX	;Z:FOUND, DELERE IT
	CALL	FNDNXT	;FIND NEXT LINE
			;DE -> NEXT LIE
	POP	CX	;CX -> LINE TO BE DELETED
	MOV	BX,[TXTUNF] ;BX -> UNFILLED SAVE AREA
	CALL	MVUP	;MOVE UP TO DELETE
	MOV	BX,CX	;TXTUNF -> UNFILLED AREA
	MOV	[TXTUNF],BX ;UPDATE
ST4:
	POP	CX	;GET READY TO INSERT
	MOV	BX,[TXTUNF] ;BUT FIRST CHECK IF
	POP	AX	;AX = # CHARS IN LINE
	PUSH	BX	;IS 3 (LINE # AND CR)
	CMP	AL,3	;THEN DO NOT INSERT
	JZ	RSTART	;MUST CLEAR THE STACK
	ADD	AX,BX	;COMPUTE NEW TSTUNF
	MOV	BX,AX	;BX -> NEW UNFILLED AREA
ST4A:
	MOV	DX,TXTEND ;CHECK TO SEE IF THERE
	CMP	BX,DX	;IS ENOUGH SPACE
	JC	ST4B	;SORRY, NO ROOM FOR IT
	JMP	QSORRY
ST4B:
	MOV	[TXTUNF],BX ;OK, UPDATE TXTUNF
	POP	DX	;DX -> OLD UNFILLED AREA
	CALL	MVDOWN
	POP	DX	;DX -> BEGIN, BX -> END
	POP	BX
	CALL	MVUP	;MOVE NEW LINE TO SAVE AREA
	JP	ST3
	
TSTV:	MOV	AH,64	;TEST VARIABLES
	CALL	IGNBLNK
	JC	RET
TSTV1:
	JNZ	TV1	;NOT @ ARRAY
	CALL	PARN	;@ SHOULD BE FOLLOWED
	ADD	BX,BX
	JNC	SS1B	;IS INDEX TOO BIG?
        JMP     QHOW
SS1B:	PUSH	DX	;WILL IT OVERWRITE
	XCHG	DX,BX	;TEXT?
	CALL	SIZE	;FIND SIZE OF FREE
	CMP	BX,DX	;AND CHECK THAT
	JNC	SS1A	;IFF SO, SAY "SORRY"
        JMP     ASORRY
SS1A:
	MOV	BX,VARBGN	;IFF NOT, GET ADDRESS
	SUB	BX,DX	;OF @(EXPR) AND PUT IT
	POP	DX	;IN HL
	RET	;C FLAG IS CLEARED
TV1:
	CMP	AL,27	;NOT @, IS IT A TO Z?
CMC:
	;IFF NOT, RETURN C FLAG
	JC	RET	;IFF NOT, RETURN C FLAG
	INC	DX
TV1A:
	MOV	BX,VARBGN	;COMPUTE ADDRESS OF
	MOV	AH,0	;CLEAR UPPER BYTE
	ADD	AX,AX	;AX:=AX*2 (WORD STORAGE)
	ADD	BX,AX	;BX:=VARBGN+2*AL
	RET		;USE CARRY AS ERROR INDICATOR
;
; TSTNUM - AT ENTRY DX -> BUFFER OF ASCII CHARACTERS
;
TSTNUM:
	MOV	BX,0	;****TSTNUM****
	MOV	CH,BH	;TEST IFF THE TEXT IS
	MOV	AH,0	;FOR CMP IN IGNBLNK
	CALL	IGNBLNK	;A NUMBER.
TN1:
	CMP	AL,'0'	;IFF NOT, RETURN 0 IN
	JC	RET	;B AND HL
	CMP	AL,':'	;IFF NUMBERS, CONVERT
	JNC	RET	;TO BINARY IN BX AND
	MOV	AL,0F0H		;SET AL TO # OF DIGITS
	AND	AL,BH	;IFF BH>255, THERE IS NO
	JNZ	QHOW	;ROOM FOR NEXT DIGIT
	INC	CH	;CH COUNTS NUMBER OF DIGITS
	PUSH	CX
	MOV	AX,BX	;BX:=10*BX+(NEW DIGIT)
        MOV     CX,10
	PUSH	DX	;SAVE DX
        MUL     AX,CX
        MOV     BX,AX   ;PARTIAL RESULT NOW IN BX
	POP	DX	;RESTORE
	MOV	SI,DX
	LODB	        ;ASCII DIGIT IN AL NOW
        SUB     AL,48   ;CONVERT TO BINARY
        MOV     AH,0
        ADD     BX,AX   ;FULL RESULT NOW IN BX
	POP	CX
	LODB            ;REPEAT FOR MORE DIGITS
	LAHF		;SAVE FLAGS
	INC	DX
	SAHF		;RESTORE FLAGS
	JNS	TN1	;QUIT IF NO NUM OR OVERFLOW
QHOW:
	PUSH	DX	;****ERROR: "HOW?"****
AHOW:
	MOV	DX,HOW
	JMP	ERROR
HOW:
	DB	'HOW?',0DH
OK:
	DB	'OK',0DH
WHAT:
	DB	'WHAT?',0DH
SORRY:
	DB	'SORRY',0DH
;
;*
;**********************************************************
;*
;* *** TABLES *** DIRECT *** & EXEC ***
;*
;* THIS SECTION OF THE CODE TESTS A STRING AGAINST A TABLE.
;* WHEN A MATCH IS FOUND, CONTROL IS TRANSFERRED TO THE SECTION
;* OF CODE ACCORDING TO THE TABLE.
;*
;* AT 'EXEC' DX SHOULD POINT TO THE STRING AND BX SHOULD POINT
;* TO THE TABLE-1. AT 'DIRECT', DX SHOULD POINT TO THE STRING,
;* BX 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., '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 1 IN LAST CHAR
;* A JUMP ADDRESS IS STORED FOLLOWING EACH CHARACTER ENTRY.
;*
;* END OF TABLE IS AN ITEM WITH A JUMP ADDRESS ONLY. IF THE 
;* STRING DOES NOT MATCH ANY OF THE OTHER ITEMS, IT WILL
;* MATCH THIS NULL ITEM AS DEFAULT. THE DEFAULT IS INDICATED
;* BY FOLLOWING THE 80H DEFAULT INDICATOR.
;*

TAB1:   EQU	$	;DIRECT COMMANDS
	DM	'LIST'
	DW	LIST	;EXECUTION ADDRESSES
	DM	'EDIT'
	DW	EDIT
	DM	'E'
	DW	EDIT	;HAVE SHORT FORM DEFINED ALSO
	DM	'RUN'
	DW	RUN
	DM	'NEW'
	DW	NEW
	DM	'LOAD'
	DW	DLOAD
	DM	'SAVE'
	DW	DSAVE
	DM	'BYE'	;GO BACK TO DOS (EXIT TBASIC)
	DW	BYE
TAB2:	EQU	$	;DIRECT/STATEMENT
	DM	'NEXT'
	DW	NEXT	;EXECUTION ADDRESSES
	DM	'LET'
	DW	LET
	DM	'OUT'
	DW	OUTCMD
	DM	'POKE'
	DW	POKE
	DM	'WAIT'
	DW	WAITCM
	DM	'IF'
	DW	IFF
	DM	'GOTO'
	DW	GOTO
	DM	'GOSUB'
	DW	GOSUB
	DM	'RETURN'
	DW	RETURN
	DM	'REM'
	DW	REM
	DM	'FOR'
	DW	FOR
	DM	'INPUT'
	DW	INPUT
	DM	'PRINT'
	DW	PRINT
	DM	'STOP'
	DW	STOP
	DB	128	;SIGNALS END
			;REMEMBER TO MOVE DEFAULT DOWN.
	DW	DEFLT	;LAST POSIBILITY
TAB4:	EQU	$	;FUNCTIONS
	DM	'RND'
	DW	RND
	DM	'INP'
	DW	INP
	DM	'PEEK'
	DW	PEEK
	DM	'USR'
	DW	USR
	DM	'ABS'
	DW	ABS
	DM	'SIZE'
	DW	SIZE
	DB	128	;SIGNALS END
			;YOU CAN ADD MORE FUNCTIONS BUT REMEMBER
			;TO MOVE XP40 DOWN
	DW	XP40
TAB5:	EQU	$	;"TO" IN "FOR"
	DM	'TO'
TAB5A:	DW	FR1
	DB	128
	DW	QWHAT
TAB6:	EQU	$	;"STEP" IN "FOR"
	DM	'STEP'
TAB6A:	DW	FR2
	DB	128
	DW	FR3
TAB8:	EQU	$	;RELATION OPERATORS
	DM	'>='
	DW	XP11	;EXECUTION ADDRESS
	DM	'#'
	DW	XP12
	DM	'>'
	DW	XP13
	DM	'='
	DW	XP15
	DM	'<='
	DW	XP14
	DM	'<'
	DW	XP16
	DB	128
	DW	XP17
;
; END OF PARSER ACTION TABLE
;
;
; AT ENTRY BX -> COMMAND TABLE (ABOVE)
;	   DX -> COMMAND LINE (I.E. "BUFFER")
;
DIRECT:
	MOV	BX,TAB1-1	;***DIRECT***
	;*
EXEC:	EQU	$	;***EXEC***
EX0:
	MOV	AH,0
	CALL	IGNBLNK	;IGNORE LEADING BLANKS
	PUSH	DX	;SAVE POINTER
	MOV	SI,DX
EX1:	LODB		;GET CHAR WHERE DX ->
	INC	DX	;PRESERVE POINTER
	CMP	AL,'.'	;WE DECLARE A MATCH
	JZ	EX4
	INC	BX
	MOV	AH,[BX]
	AND	AH,127	;STRIP BIT 7
	CMP	AL,AH	;COMPARISON NOW EASY
	JZ	EX2
	; NO MATCH - CHECK NEXT ENTRY
EX0A:	CMP	B,[BX],128	;BYTE COMPARE
	JNC	EX0B
	INC	BX
	JP	EX0A
	; AT THIS POINT HAVE LAST LETTER
EX0B:	ADD	BX,3	;GET PAST EXECUTION ADDRESS
	CMP	B,[BX],128	;FOUND DEFAULT?
	JZ	EX3A	;IF SO, EXECUTE DEFAULT
	DEC	BX	;CORRECT FOR PRE-INCREMENT
	POP	DX	;RESTORE POINTER
	JP	EX0	;LOOK SOME MORE FOR A MATCH
EX4:	INC	BX
	CMP	B,[BX],128
	JC	EX4
	JP	EX3
;
EX3A:	DEC	SI
	JP	EX3	;CORRECT SI FOR DEFAULT EXECUTION
EX2:	CMP	B,[BX],128	;END OF RESERVED WORD?
	JC	EX1	;NO - CHECK SOME MORE
	; AT THIS POINT NEED TO GET EXECUTION ADDRESS

EX3:	INC	BX	;BX -> EXECUTION ADDRESS
	POP	AX	;CLEAR STACK
	MOV	DX,SI	;RESET POINTER
	JMP	[BX]	;DO IT
;*
;
;
; WHAT FOLLOWS IS THE CODE TO ECECUTE DIRECT AND STATEMENT COM-
; MANDS. CONTROL IS TRANSFERED TO THESE POINTS VIA THE  COMMAND
; TABLE LOOKUP CODE OF 'DIRECT' AND 'EXEC' IN THE LAST SECTION.
; AFTER THE COMMAND IS EXECUTED,  CONTROL  IS  TRANSFERRED   TO
; OTHER SECTIONS AS FOLLOWS:
;
; FOR 'LIST','NEW', ANS 'STOP': GO BACK TO 'RSTART'
;
; FOR 'RUN',: GO EXECUTE THE FIRST STORED LINE IFF 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: IFF '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 STROED LINE, STORES ITS ADDRESS
;	(IN 'CURRNT'), AND START TO EXECUTE IT. NOTE THAT ONLY
;	THOSE COMMANDS IN TAB2 ARE LEGAL FOR STORED PROGRAMS.
;
; THERE ARE THREE MORE ENTRIES IN 'RUN':
;
;	'RUNNXL' FINDS NEXT LINE, STORES ITS ADDR AND EXEC IT.
;	'RUNTSL' STORES THE ADDRESS OF THIS LINE AND EXECUTES IT
;	'RUNSML' CONTINUES THE EXECUTION ON SAME LINE.
;
; 'GOTO(EXPR)' EVALUATES THE EXPRESSION, FINDS THE TARGET LINE,
;	AND JUMPS TO 'RUNTSL' TO DO IT.
;
; 'DLOAD' LOADS A NAMES PROGRAM FROM DISK (ANYNAME.TBI)
;
; 'DSAVE' SAVES A NAMES PROGRAM ON DISK
;
; 'FCBSET' SETS UP THE MSDOS FILE CONTROL BLOCK FOR SUBSEQUENT
;	DISK I/O.
;
;
NEW:
	MOV	W,[TXTUNF],TXTBGN
	;
STOP:
	CALL	ENDCHK	;****STOP(CR)****
	JMP	RSTART
	;
RUN:
	CALL	ENDCHK	;****RUN(CR)****
	MOV	DX,TXTBGN	;FIRST SAVED LINE
	;
RUNNXL:
	MOV	BX,0	;****RUNNXL****
	CALL	FNDLNP	;FIND WHATEVER LINE
	JNC	RUNTSL	;C: PASSED TXTUNF, QUIT
        JMP     RSTART
	;
RUNTSL:
	XCHG	DX,BX	;****RUNTSL****
	MOV	[CURRNT],BX	;SET 'CURRNT"->LINE #
	XCHG	DX,BX
	INC	DX
	INC	DX
	;
RUNSML:
	CALL	CHKIO	;****RUNSML****
	MOV	BX,TAB2-1	;FIND COMMAND IN TABLE 2
	JMP	EXEC	;AND EXECUTE IT
	;
GOTO:
	CALL	EXP	;****GOTO(EXPR)****
	PUSH	DX	;SAVE FOR ERROR ROUTINE
	CALL	ENDCHK	;MUST FIND A 0DH (CR)
	CALL	FNDLN	;FIND THE TARGET LINE
	JZ	GT1	;NO SUCH LINE #
        JMP     AHOW
GT1:	POP	AX
	JP	RUNTSL	;GO DO IT
	;
	; BDOS EQUATES (FOR MS-DOS)
	;
BYE:	EQU	0	;BDOS EXIT ADDRESS
FCB:	EQU	5CH
SETDMA:	EQU	26
OPEN:	EQU	15
READD:	EQU	20
WRITED:	EQU	21
CLOSE:	EQU	16
MAKE:	EQU	22
BCONIN:	EQU	10	;BUFFERED CONSOLE INPUT
DELETE:	EQU	19
CONOUT:	EQU	2	;CONSOLE OUTPUT
CONST:	EQU	11	;CONSOLE STATUS
	;
	;
DLOAD:
	MOV	AH,0
	CALL	IGNBLNK	;IGNORE BLANKS
	PUSH	BX	;SAVE H
	CALL	FCBSET	;SET UP FILE CONTROL BLOCK
	PUSH	DX	;SAVE THE REST
	PUSH	CX	;SAVE THE REST
	MOV	DX,FCB	;GET FCB ADDR
	MOV	AH,OPEN	;PREPARE TO OPEN FILE
	INT	33	;CALL MS-DOS TO OPEN FILE
	CMP	AL,0FFH	;IS IT THERE?
	JNZ	DL1	;NO, SEND ERROR
        JMP     QHOW
DL1:	XOR	AL,AL	;CLEAR A
	MOV	[FCB+32],AL	;START AT RECORD 0
	MOV	DX,TXTBGN	;GET BEGINNING
LOAD:
	PUSH	DX	;SAVE DMA ADDRESS
	MOV	AH,SETDMA
	INT	33	;CALL MS-DOS TO SET DAM ADDR
	MOV	AH,READD
	MOV	DX,FCB
	INT	33	;CALL MS-DOS TO READ SECTOR
	CMP	AL,1	;DONE?
	JC	RDMORE	;NO, READ MORE
	JZ	LL1
LOAD1:	JMP	QHOW	;BAD READ OR NO DELIMITER
LL1:	MOV	AH,CLOSE
	MOV	DX,FCB
	INT	33	;CALL MS-DOS TO CLOSE FILE
	POP	BP	;DMA ADDR IN BP
	SUB	BP,100H ;BACKUP
	MOV	CX,100H ;MAX LOOPS
RDM1:	INC	BP	;PRE INC
	CMP	W,[BP],0 ;FOUND DELIMITER?
	LOOPNZ	RDM1	;KEEP LOOKING
	CMP	CL,0	;MAC LOOPS EXECUTED?
	JZ	LOAD1	;GIVE ERROR IF SO
	MOV	[TXTUNF],BP ;UPDATE POINTER
	POP	CX	;GET OLD REG BACK
	POP	DX	;GET OLD REG BACK
	POP	BX	;GET OLD REG BACK
	CALL	FINISH	;FINISH
RDMORE:
	POP	DX	;GET DMA ADDR
	MOV	BX,80H	;GET 128
	ADD	BX,DX	;ADD IT TO DMA ADDR
	XCHG	DX,BX	;BACK IN D
	JMP	LOAD	;AND READ SOME MORE
	;
DSAVE:
	CMP	W,[TXTUNF],TXTBGN ;SEE IF ANYTHING TO SAVE
	JNZ	DS1A
	JMP	QWHAT
DS1A:
	MOV	BP,[TXTUNF]
	MOV	W,[BP],0 ;SET DELIMITER
	MOV	AH,0
	CALL	IGNBLNK	;IGNORE BLANKS
	PUSH	BX	;SAVE BX
	CALL	FCBSET	;SETUP FCB
	PUSH	DX
	PUSH	CX	;SAVE OTHERS
	MOV	DX,FCB
	MOV	AH,DELETE
	INT	33	;CALL MS-DOS TO ERASE FILE
	MOV	DX,FCB
	MOV	AH,MAKE
	INT	33	;CALL MS-DOS TO MAKE A NEW ONE
	CMP	AL,0FFH	;IS THERE SPACE?
	JNZ	DS1
        JMP     QHOW	;NO, ERROR
DS1:	XOR	AL,AL	;CLEAR A
	MOV	[FCB+32],AL	;START AT RECORD 0
	MOV	DX,TXTBGN	;GET BEGINNING
SAVE:
	PUSH	DX	;SAVE DMA ADDR
	MOV	AH,SETDMA
	INT	33	;CALL MS-DOS TO SET DMA ADDR
	MOV	AH,WRITED
	MOV	DX,FCB
	INT	33	;CALL MS-DOS TO WRITE SECTOR
	OR	AL,AL	;SET FLAGS
	JZ	SS1	;IF NOT ZERO, ERROR
        JMP     QHOW
SS1:	POP	DX	;GET DMA ADDR BACK
	MOV	AX,DX
	CMP	AX,[TXTUNF]	;SEE IF DONE
	JZ	SAVDON
	JNC	SAVDON	;JUMP IF DONE
WRITMOR:
	MOV	BX,80H
	ADD	BX,DX
	XCHG	DX,BX	;GET IT TO D
	JP	SAVE
SAVDON:
	MOV	AH,CLOSE
	MOV	DX,FCB
	INT	33	;CALL MS-DOS TO CLOSE FILE
	POP	CX	;GET REGS BACK
	POP	DX	;GET REGS BACK
	POP	BX	;GET REGS BACK
	CALL	FINISH
	;
FCBSET:
	MOV	BX,FCB	;GET FCB ADDR
	MOV	B,[BX],0	;CLEAR ENTRY TYPE
FNCLR:
	INC	BX
	MOV	B,[BX],' '	;CLEAR TO SPACE
	MOV	AX,FCB+8
	CMP	AX,BX	;DONE?
	JNZ	FNCLR	;NO, DO IT AGAIN
	INC	BX
	MOV	B,[BX],'T'	;SET FILE TYPE TO 'TBI'
	INC	BX
	MOV	B,[BX],'B'
	INC	BX
	MOV	B,[BX],'I'
EXRC:
	INC	BX
	MOV	B,[BX],0
	MOV	AX,FCB+15
	CMP	AX,BX
	JNZ	EXRC	;NO, CONTINUE
	MOV	BX,FCB+1	;GET FILENAME START
FN:
	MOV	SI,DX
	LODB	;GET CHAR
	CMP	AL,0DH	;IS IT A 'CR'
	JZ	RET	;YES, DONE
	CMP	AL,'!'	;LEGAL CHAR?
	JNC     FN1	;NO, SEND ERROR
        JMP     QWHAT
FN1:	CMP	AL,'['	;AGAIN
	JC	FN2	;DITTO
        JMP     QWHAT
FN2:	MOV	[BX],AL	;SAVE IT IN FCB
	INC	BX
	INC	DX
	MOV	AX,FCB+9
	CMP	AX,BX	;LAST?
	JNZ	FN	;NO, CONTINUE
	RET	;TRUNCATE AT EIGHT CHARS
;
;
; ****LIST**** AND ****PRINT**** AND ****EDIT****
;
; LIST HAS TWO FORMS:
;	'LIST(CR)' LISTS ALL SAVED LINES
;	'LIST #(CR)' START LIST AT THIS LINE #
; YOU CAN STOP LISTING BY CONTROL C KEY
;
; PRINT COMMAND IS 'PRINT ....;' OR 'PRINT ....(CR)'
; WHERE '....' IS A LIST OF EXPRESIONS, FORMATS, BACKARROWS, AND
; 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 AN EXPRESSION IS TO BE PRINTED.
; TED. IT STAYS EFFECTIVE FOR THE REST OF THE PRINT, UNLESS
; CHANGED BY ANOTHER FORMAT. IF NO FORMAT SPEC, 6 POSITIONS
; WILL BE USED.
;
; A STRING IS QUOTED IN A PAIR OF SINGLE QUOTES OR DOUBLE
; QUOTES.
;
; A BACK-ARROW MEANS GENERATE A (CR) WITHOUT (LF).
;
; A (CRLF) IS GENERATED AFTER THE ENTIRE LIST HAS BEEN PRINT OR
; IF THE LIST IS A NULL LIST. HOWEVER IF THE LIST ENDED WITH A 
; COMMA, NO (CR) IS GENERATED.
;
;
LIST:
	CALL	TSTNUM	;TEST IFF THERE IS A #
	CALL	ENDCHK	;IFF NO # WE GET A 0
	CALL	FNDLN	;FIND THIS OR NEXT LINE
LS1:
	JNC	LS2	;C: PASSED TXTUNF
        JMP     RSTART
LS2:	CALL	PRTLN	;PRINT THE LINE
	CALL	CHKIO	;SEE IF ^X OR ^C
	CALL	FNDLNP	;FIND NEXT LINE
	JP	LS1	;LOOP BACK
	;
;
EDIT:
	CALL	TSTNUM	;TEST IF THERE IS A #
	CALL	ENDCHK	;AT END?
	CALL	FNDLN	;FIND SPEC LINE OR NEXT LINE
	PUSH	DX	;SAVE LINE #
	JNC	ED2	;C: PASSED TXTUNF
	POP	DX	;THROW AWAY LINE #
ED1:	JMP	RSTART
ED2:
	CALL	PRTLN	;PRINT THE LINE
	POP	DX	;GET LINE # BACK
	MOV	B,[OCSW],0 ;DIRECT OUTPUT TO BUFFER
	MOV	B,[BUFFER-1],0 ;CLEAR CHAR COUNT
	MOV	B,[PRTLN1+1],4 ;PRINT ONE LESS SPACE
	MOV	DI,BUFFER ;PREPARE TO MOVE
	CALL	PRTLN
	MOV	B,[OCSW],0FFH ;REDIRECT OUTPUT TO CONSOLE
	DEC	[BUFFER-1] ;AVOID CR?
	MOV	B,[PRTLN1+1],5 ;RESTORE PRTLN
	JMP	ST3	;PROMPT AND GETLINE ONLY
PRINT:
	MOV	CL,6	;C:= # OF SPACES
	MOV	AH,';'	;CHECK FOR ';' IN IGNBLNK
	CALL	IGNBLNK	;IGNORE BLANKS
	JNZ	PR2	;JUMP IF ';' NOT FOUND
	CALL	CRLF	;GIVE CR,LF AND
	JMP	RUNSML	;CONTINUE SAME LINE
PR2:
	MOV	AH,0DH
	CALL	IGNBLNK
	JNZ	PR0
	CALL	CRLF	;ALSO GIVE CRLF AND
	JMP	RUNNXL	;GOTO NEXT LINE
PR0:
	MOV	AH,'#'
	CALL	IGNBLNK
	JNZ	PR1
	CALL	EXP	;YES, EVALUATE EXPR
	MOV	CL,BL	;AND SAVE IT IN C
	JP 	PR3	;LOOK FOR MORE TO PRINT
PR1:
	CALL	QTSTG	;OR IS IT A STRING?
	JP	PR8	;IFF NOT, MUST BE EXPRESSION
PR3:
	MOV	AH,','
	CALL	IGNBLNK
	JNZ	PR6
	CALL	FIN	;IN THE LIST
	JP	PR0	;LIST CONTINUES
PR6:
	CALL	CRLF	;LIST ENDS
	CALL	FINISH
PR8:
	CALL	EXP	;EVAL THE EXPR
	PUSH	CX
	CALL	PRTNUM	;PRINT THE VALUE
	POP	CX
	JP	PR3	;MORE TO PRINT?
;
;
; ****GOSUB**** AND ****RETURN****
;
; 'GOSUB (EXPR);' OR 'GOSUB EXPR(CR)' IS LIKE THE 'GOTO' COMMAND
; EXCEPT THAT THE CURRENT TEXT POINTER, STACK POINTER ETC.   ARE
; SAVED SO THAT EXECUTION CAN BE CONTINUED AFTER THE  SUBROUTINE
; 'RETURN'. IN ORDER THAT 'GOSUB' CAN BE NESTED (AND EVEN RECUR-
; SIVE), THE SAVE AREA MUST BE  STACKED.  THE  STACK  POINTER IS
; SAVED IN 'STKGOS'. THE OLD 'STKGOS' IS SAVED IN THE STACK. IF
; WE ARE IN THE MAIN ROUTINE, 'STKGOS' IS ZERO (THIS WAS DONE BY
; THE "MAIN" SECTION OF THE CODE),  BUT  WE  STILL  SAVE  IT  AS
; A FLAG FOR NO FURTHER RETURNS.
;
; 'RETURN(CR)' UNDOES EVERYTHING THAT 'GOSUB' DID, AND THUS  RE-
; TURNS THE EXECUTION TO THE COMMAND AFTER THE MOST RECENT  'GO-
; SUB'. IFF 'STKGOS' IS ZERO, IT INDICATES THAT WE NEVER  HAD  A
; 'GOSUB' AND IS THUS AN ERROR.
;
;
GOSUB:
	CALL	PUSHA	;SAVE THE CURRENT 'FOR'
	CALL	EXP	;PARAMETERS
	PUSH	DX
	CALL	FNDLN	;FIND THE TARGET LINE
	JZ	GS1	;NOT THERE, SAY "HOW?"
        JMP     AHOW
GS1:	MOV	BX,[CURRNT]	;FOUND IT, SAVE OLD
	PUSH	BX	;'CURRNT' OLD 'STKGOS'
	MOV	BX,[STKGOS]
	PUSH	BX
	MOV	BX,0	;AND LOAD NEW ONES
	MOV	[LOPVAR],BX
	ADD	BX,SP
	MOV	[STKGOS],BX
	JMP	RUNTSL	;THEN RUN THAT LINE
RETURN:
	CALL	ENDCHK	;THERE MUST BE A 0DH
	MOV	BX,[STKGOS]	;OLD STACK POINTER
	OR	BX,BX
	JNZ	RET1	;SO, WE SAY: "WHAT?"
        JMP     QWHAT
RET1:	XCHG	BX,SP		;ELSE RESTORE IT
	POP	BX	;ELSE RESTORE IT
	MOV	[STKGOS],BX	;AND THE OLD 'STKGOS'
	POP	BX
	MOV	[CURRNT],BX	;AND THE OLD 'CURRNT'
	POP	DX	;OLD TEXT POINTER
	CALL	POPA	;OLD "FOR" PARAMETERS
	CALL	FINISH	;AND WE ARE BACK HOME
;
;
; ****FOR**** AND ****NEXT****
;
;
; 'FOR' HAS TWO FORMS:
;	'FOR VAR=EXP1 TO EXP2 STEP EXP3'
;	'FOR VAR=EXP1 TO EXP2'
; THE SECOND FORM MEANS THE SAME AS THE FIRST FORM WITH EXP3=1.
;
; TBI WILL FIND THE VARIABLE VAR AND SET ITS VALUE TO THE CUR-
; RENT VALUE OF EXP1. IT ALSO  EVALUATES  EXP2  AND  EXP3  AND
; SAVES ALL OF THESE TOGETHER  WITH  THE  TEXT  POINTER ETC IN
; THE 'FOR' SAVE AREA, WHICH CONSISTS OF 'LOPVAR',   'LOPINC',
; 'LOPLMT', 'LOPLN', AND 'LOPPT'. IFF 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  BE-
; FORE THE NEW ONE OVERWRITES IT.
;
; TBI WILL THEN DIG IN THE  STACK  AND  FIND  OUT IFF     THIS
; SAME VARIABLE WAS USED IN  ANOTHER  CURRENTLY  ACTIVE    FOR
; LOOP. IT THAT IS THE CASE THEN THE OLD 'FOR'   LOOP IS   DE-
; IVATED (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'. IFF THEY ARE NOT THE SAME, TBI DIGGS   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 CHECKS THE RESULT WITH THE LIMIT.  IFF  IT   IS
; WITHIN THE LIMIT, CONTROL LOOPS BACK TO THE COMMAND  FOLLOW-
; ING THE 'FOR'. IFF OUTSIDE THE LIMIT, THE SAVE AREA IS PURG-
; ED AND EXECUTION CONTINUES.
;
;
FOR:
	CALL	PUSHA	;SAVE THE OLD SAVE AREA
	CALL	SETVAL	;SET THE CONTROL VAR.
	DEC	BX
	MOV	[LOPVAR],BX	;SAVE TGAT
	MOV	BX,TAB5-1	;USE 'EXEC' TO LOOK
	JMP	EXEC	;FOR THE WORD 'TO'
FR1:
	CALL	EXP	;EVALUATE THE LIMIT
	MOV	[LOPLMT],BX	;SAVE THAT
	MOV	BX,TAB6-1	;USED 'EXEC' TO LOOK
	JMP	EXEC	;FOR THE WORD 'STEP'
FR2:
	CALL	EXP	;FOUND IT, GET STEP
	JP	FR4	;FOUND IT, GET STEP
FR3:
	MOV	BX,1	;NOT FOUND, SET TO ONE
FR4:
	MOV	[LOPINC],BX	;SAVE THAT TOO
FR5:
	MOV	BX,[CURRNT]	;SAVE CURRENT LINE #
	MOV	[LOPLN],BX
	XCHG	DX,BX	;AND TEXT POINTER
	MOV	[LOPPT],BX
	MOV	CX,10	;DIG INTO STACK TO
	MOV	BX,[LOPVAR]	;FIND 'LOPVAR'
	XCHG	DX,BX
	MOV	BX,CX	;BX:=10 NOW
	ADD	BX,SP
	JP	FR7A
FR7:
	ADD	BX,CX
FR7A:	MOV	AX,[BX]	;GET THAT OLD 'LOPVAR'
	OR	AX,AX
	JZ	FR8	;0 SAYS NO MORE IN IT
	CMP	AX,DX	;SAME AS THIS ONE?
	JNZ	FR7
	XCHG	DX,BX
	MOV	BX,0	;THE OTHER HALF?
	ADD	BX,SP
	MOV	CX,BX
	MOV	BX,10
	ADD	BX,DX
	CALL	MVDOWN	;AND PURGE 10 WORDS
	XCHG	BX,SP		;IN THE STACK
FR8:
	MOV	BX,[LOPPT]	;JOB DONE, RESTORE DE
	XCHG	DX,BX
	CALL	FINISH	;AND CONTINUE
	;
NEXT:
	CALL	TSTV	;GET ADDR OF VAR
	JNC	NX4	;NO VARIABLE, "WHAT?"
        JMP     QWHAT
NX4:	MOV	[VARNXT],BX	;YES, SAVE IT
NX0:
	PUSH	DX	;SAVE TEXT POINTER
	XCHG	DX,BX
	MOV	BX,[LOPVAR]	;GET VAR IN 'FOR'
	MOV	AL,BH
	OR	AL,BL	;0 SAY NEVER HAD ONE
	JNZ	NX5	;SO WE ASK: "WHAT?"
        JMP     AWHAT
NX5:	CMP	DX,BX	;ELSE WE CHECK THEM
	JZ	NX3	;OK, THEY AGREE
	POP	DX	;NO, LET'S SEE
	CALL	POPA	;PURGE CURRENT LOOP
	MOV	BX,[VARNXT]	;AND POP ONE LEVEL
	JMP	NX0	;GO CHECK AGAIN
NX3:
	MOV	DL,[BX]	;COME HERE WHEN AGREED
	INC	BX
	MOV	DH,[BX]	;DE = VAL OF VAR
	MOV	BX,[LOPINC]
	PUSH	BX
	ADD	BX,DX
	XCHG	DX,BX	;ADD ONE STEP
	MOV	BX,[LOPVAR]	;PUT IT BACK
	MOV	[BX],DL
	INC	BX
	MOV	[BX],DH
	MOV	BX,[LOPLMT]	;HL-> LIMIT
	POP	AX
	XCHG	AH,AL
	OR	AX,AX
	JNS	NX1	;STEP > 0
	XCHG	DX,BX
NX1:
	CALL	CKHLDE	;COMPARE WITH LIMIT
	POP	DX	;RESTORE TEXT POINTER
	JC	NX2	;OUTSIDE LIMIT
	MOV	BX,[LOPLN]	;WITHIN LIMIT, GO
	MOV	[CURRNT],BX	;BACK TO THE SAVED
	MOV	BX,[LOPPT]	;'CURRNT' AND TEXT
	XCHG	DX,BX	;POINTER
	CALL	FINISH	;POINTER
NX2:
	CALL	POPA	;PURGE THIS LOOP
	CALL	FINISH
;
;
; ****REM**** AND ****IF**** AND ****LET*****
;
;
; 'REM' CAN BE FOLLOWED BY ANYTHING AND IS IGNORED BY 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. IFF IT IS NON-ZERO, EXECUTION CONTINUES. IFF THE EXPR.
; IS ZERO, THE COMMANDS THAT FOLLOW ARE IGNORED AND  EXECUTION
; CONTINUES AT THE NEXT LINE.
;
; 'IPUT' COMMANS IS LIKE THE 'PRINT' COMMAND, AND IS  FOLLOWED
; BY A LIST OF ITEMS. IFF THE ITEM IS A  STRING  IN  SINGLE OR
; DOUBLE QUOTES, OR IS A BACK-ARROW, IT HAS THE SAME EFFEDT AS
; PRINTED OUT FOLLOWED BY A COLON. THEN TBI WAITS FOR AN EXPR.
; TO BE TYPEN IN. THE VARIABLE IS THEN  SET  TO  THE  VALUE OF
; THIS EXPR. IFF THE VARIABLE IS PROCEDED BY A STRING  PRINTED
; FOLLOWED BY A COLON. TBI THEN WAITS FOR INPUT EXPR. AND SETS
; THE VARIABLE TO THE VALUE OF THE EXPR.
;
; IFF 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 SETS THE VARIABLE TO  THAT
; VALUE. TBI WILL ALSO HANDLE 'LET' COMMAND WITHOUT THE   WORD
; 'LET'. THIS IS DONE BY 'DEFLT'.
;
;
;
REM:
	MOV	BX,0	;****REM****
	JP	IFF1A	;JUMP AROUND EXPR
	;
IFF:
	CALL	EXP	;****IF****
IFF1A:	CMP	BX,0	;IS THE EXPR = 0?
	JZ	IFF1	;NO, CONTINUE
        JMP     RUNSML
IFF1:	CALL	FNDSKP	;YES, SIKP REST OF LINE
	JC	IFF2	;YES, SIKP REST OF LINE
        JMP     RUNTSL
IFF2:	JMP	RSTART	;YES, SIKP REST OF LINE
	;
INPERR:
	MOV	BX,[STKINP]	;****INPERR****
	XCHG	BX,SP		;RESTORE OLD STACK POINTER
	POP	BX	;AND OLD 'CURRNT'
	MOV	[CURRNT],BX
	POP	DX
	POP	DX	;REDO INPUT
	;
INPUT:	EQU	$	;****INPUT****
IP1:
	PUSH	DX	;SAVE IN CASE OF ERROR
	CALL	QTSTG	;IS NEXT ITEM A STRING?
	JP	IP2	;NO
	CALL	TSTV	;YES, BUT FOLLOWED BY A
	JC	IP4	;VARIABLE? NO.
	JP	IP3	;YES. INPUT VAR.
IP2:
	PUSH	DX	;SAVE FOR 'PRTSTG'
	CALL	TSTV	;MUST BE A VAR NOW
	JNC	IP2A	;"WHAT" IT IS NOT!
        JMP     QWHAT
IP2A:	MOV	SI,DX
	LODB		;GET READY FOR 'RTSTG'
	MOV	CL,AL
	SUB	AL,AL
	MOV	DI,DX
	STOB
	POP	DX
	CALL	PRTSTG	;PRINT STRING AS PROMPT
	MOV	AL,CL
	DEC	DX
	MOV	DI,DX
	STOB
IP3:
	PUSH	DX
	XCHG	DX,BX
	MOV	BX,[CURRNT]	;ALSO SAVE 'CURRNT'
	PUSH	BX
	MOV	BX,IP1
	MOV	[CURRNT],BX	;NEG NUMBER AS FLAG
	MOV	[STKINP],SP
	PUSH	DX	;OLD HL
	MOV	AL,':'	;PRINT THIS TOO
	CALL	GETLN	;AND GET A LINE
IP3A:
	MOV	DX,BUFFER	; POINTS TO BUFFER
	CALL	EXP	;EVALUATE INPUT
	NOP	        ;CAN BE 'CALL ENDCHK'
	NOP	 	;CAN BE 'CALL ENDCHK'
	NOP	 	;CAN BE 'CALL ENDCHK'
	POP	DX	;OK,GET OLD HL
	XCHG	DX,BX	;OK,GET OLD HL
	MOV	[BX],DX
	POP	BX	;GET OLD 'CURRNT'
	MOV	[CURRNT],BX
	POP	DX	;AND GET OLD TEXT POINTER
IP4:
	POP	AX
	MOV	AH,','
	CALL	IGNBLNK
	JNZ	IP5
	JP	IP1	;YES, MORE ITEMS
IP5:
	CALL	FINISH
	;
DEFLT:
	MOV	SI,DX
	LODB	;****DEFLT****
	CMP	AL,0DH	;EMPTY LINE IS OK
	JZ	LT1	;ELSE IT IS 'LET'
	;
LET:
	CALL	SETVAL	;****LET****
	MOV	AH,','
	CALL	IGNBLNK
	JNZ	LT1
	JP	LET	;ITEM BY ITEM
LT1:
	CALL	FINISH	;UNTIL FINISH
;
;
; ****EXPR****
;
; 'EXPR' EVALUATES ARITHMETICAL OR LOGICAL EXPRESSIONS.
;	<EXPR>::=<EXPR2>
;				<EXPR2><REL.OP><EXPR2>
;
; WHERE <REL.OP> IS ONE OF THE OPERATORS IN TAB8 AND THE RE-
; SULT OF THESE OPERATIONS IS 1 IFF TRUE AND  0  IFF  FALSE.
;
;	<EXPR2>::=(+ OR -)<EXPR3>(+ OR -<EXPR3>(....)
;
; WHERE () ARE OPTIONAL AND (....) ARE OPTIONAL REPEATS.
;
;	<EXPR3>::=<EXPR4>(<* OR /><EXPR4>)(....)
;	<EXPR4>::=<VARIABLE>
;			<FUNCTION>
;			(<EXPR>)
;
; <EXPR> IS RECURSIVE SO THAT VARIABLE '@' CAN HAVE AN EXPR
; AS INDEX, FUCTIONS CAN HAVE AN <EXPR> AS ARGUMENTS,   AND
; <EXPR4> CAN BE AN <EXPR> IN PARANTHESES.
;
;
EXP:	CALL	EXPR2
	PUSH	BX
EXPR1:
	MOV	BX,TAB8-1	;LOOKUP REL.OP
	JMP	EXEC	;GO DO IT
XP11:
	CALL	XP18
	JC	RET	;NO RETURN HL=0
	MOV	BL,AL	;YES, RETURN HL=1
	RET
XP12:
	CALL	XP18
	JZ	RET	;FALSE, RETURN HL=0
	MOV	BL,AL	;TRUE, RETURN HL=1
	RET
XP13:
	CALL	XP18	;REL.OP '>'
	JZ	RET	;FALSE
	JC	RET	;ALSO FALSE, HL=0
	MOV	BL,AL	;TRUE, HL=1
	RET
XP14:
	CALL	XP18	;REL OP '<='
	MOV	BL,AL	;SET HL=1
	JZ	RET	;REL. TRUE, RETURN
	JC	RET	;REL. TRUE, RETURN
	MOV	BL,BH	;ELSE SET HL=0
	RET
XP15:
	CALL	XP18	;REL OP '='
	JNZ	RET	;FALSE, RETURN HL=0
	MOV	BL,AL	;ELSE SET HL=1
	RET
XP16:
	CALL	XP18	;REL.OP '<'
	JNC	RET	;FALSE, RETURN HL=0
	MOV	BL,AL	;ELSE SET HL=1
	RET
XP17:
	POP	BX	;NOT REL OP
	RET	;RETURN HL=<EPTR2>
XP18:
	MOV	AL,CL	;SUBROUTINE FOR ALL
	POP	BX	;REL.OP'S
	POP	CX	;REL.OP'S
	PUSH	BX	;REVERSE TOP OF STACK
	PUSH	CX	;REVERSE TOP OF STACK
	MOV	CL,AL
	CALL	EXPR2	;GET 2ND EXPRESSION
	XCHG	DX,BX	;VALUE IN DE NOW
	POP	AX
	PUSH	BX
	MOV	BX,AX	;LAST 3 INSTR FOR XTHL INST!
	CALL	CKHLDE	;COMPARE 1ST WITH SECOND
	POP	DX
	MOV	BX,0	;SET HL=0, A=1
	MOV	AL,1	;SET HL=0, A=1
	RET
	;
EXPR2:
	MOV	AH,'-'
	CALL	IGNBLNK	;NEGATIVE SIGN?
	JNZ	XP21
	MOV	BX,0	;YES, FAKE '0-'
	JP	XP26	;TREAT LIKE SUBTRACT
XP21:
	MOV	AH,'+'	;POSITIVE SIGN?
	CALL	IGNBLNK
XP22:
	CALL	EXPR3	;1ST <EXPR3>
XP23:
	MOV	AH,'+'
	CALL	IGNBLNK	;ADD?
	JNZ	XP25	;NOTE OFFSET WHAS 21 BYTES IN 8080 VERSION
	PUSH	BX	;YES, SAVE VALUE
	CALL	EXPR3	;GET 2ND <EXPR3>
XP24:
	XCHG	DX,BX	;2ND IN DE
	POP	AX	;THIS + NEXT 2 LINES FOR 8080 XTHL INST!!
	PUSH	BX
	MOV	BX,AX	;BX <-> [SP] NOW, [SP]->BUFFER,BX=OLD EXPR3
	ADD	BX,DX
	POP	DX
	JNO	XP23	;CHECK FOR OVERFLOW
XP24A:	JMP	QHOW	;ELSE WE HAVE OVERFLOW
XP25:
	MOV	AH,'-'
	CALL	IGNBLNK	;SUBTRACT?
	JNZ	RET
XP26:	PUSH	BX	;YES, SAVE 1ST <EXPR3>
	CALL	EXPR3	;GET 2ND <EXPR3>
	CALL	CHGSGN
	JP	XP24
	;
EXPR3:
	CALL	EXPR4	;GET 1ST <EXPR4>
XP31:
	MOV	AH,'*'
	CALL	IGNBLNK	;MULTIPLY?
	JNZ	XP34
	PUSH	BX	;YES, SAVE 1ST
	CALL	EXPR4	;AND GET 2ND <EXPR4>
	XCHG	DX,BX	;2ND IN DE NOW
	POP	AX	;SUBSITUTE FOR 8080 XTHL
	PUSH	BX
	IMUL	AX,DX	;AX:=AX*DX
	JO	XP32	;SEE INTEL BOOK ON OVERFLOW FLAG
	MOV	BX,AX	;RESULT NOW IN BX
	JP	XP35	;LOOK FOR MORE
XP34:
	MOV	AH,'/'
	CALL	IGNBLNK	;DIVIDE?
	JNZ	RET
	PUSH	BX	;YES, SAVE 1ST <EXPR4>
	CALL	EXPR4	;AND GET SECOND ONE
	XCHG	DX,BX	;PUT 2ND IN DE
	POP	AX	;REPLACEMENT FOR XTHL
	PUSH	BX
	MOV	BX,AX
	OR	DX,DX
	JNZ	XP34A	;SAY "HOW?"
XP32:	JMP	AHOW
XP34A:	CALL	DIVIDE	;USE SUBROUTINE
	MOV	BX,CX	;GET RESULT
	MOV	CX,6	;SIX SPACES
XP35:
	POP	DX	;AND TEXT POINTER
	JP	XP31	;LOOK FOR MORE TERMS
	;
EXPR4:
	MOV	BX,TAB4-1	;FIND FUCNTION IN TAB4
	JMP	EXEC	;AND GOT DO IT
XP40:
	CALL	TSTV	;NO, NOT A FUNCTION
	JC	XP41	;NOR A VARIABLE
	MOV	AL,[BX]	;VARIABLE
	LAHF
	INC	BX
	SAHF
	MOV	BH,[BX]	;VALUE IN HL
	MOV	BL,AL	;VALUE IN HL
	RET
XP41:
	CALL	TSTNUM	;OR IS IT A NUMBER?
	MOV	AL,CH	;# OF DIGITS
	OR	AL,AL
	JNZ	RET	;OK
PARN:
	MOV	AH,'('
	CALL	IGNBLNK	;NO DIGIT, MUST BE
	JNZ	PARN1
	CALL	EXP	;"(EXPR)"
PARN1:	MOV	AH,')'
	CALL	IGNBLNK	;"(EXPR)"
	JNZ	XP43	;******WHY CHECK THIS?******
XP42:
	RET
XP43:
	JMP	QWHAT	;ELSE SAY: "WHAT?"
	;
RND:
	CALL	PARN	;****RND(EXPR)****
	OR	BX,BX
	JNS	RND1	;MUST BE POSITIVE
	JNZ	RND1	;AND NON-ZERO
        JMP     QHOW
RND1:
	PUSH	CX
	PUSH	DX
	MOV	AH,2CH	;GET TIME
	INT	33	;ASK MS-DOS
	MOV	AX,327
	MOV	DH,0
	MUL	AX,DX	; 0<=AX<=32700
	XCHG	DX,BX
	MOV	BX,AX
	CALL	DIVIDE	;RND(N)=MOD(M,N)+1
	POP	DX
	POP	CX
	INC	BX
	RET
	;
ABS:
	CALL	PARN	;****ABS(EXPR)****
	CALL	CHKSGN	;CHECK SIGN
	OR	AX,BX
	JNS	RET	;OK
	JMP	QHOW	;SO SAY: "HOW?"
SIZE:
	MOV	BX,[TXTUNF]	;****SIZE****
	PUSH	DX	;GET THE NUMBER OF FREE
	XCHG	DX,BX	;BYTES BETWEEN 'TXTUNF'
SIZEA:
	MOV	BX,VARBGN	;AND 'VARBGN'
	SUB	BX,DX
	POP	DX
	RET
;
;
; ****OUT**** AND ****INP**** AND ****WAIT**** AND
; ****POKE**** AND ****PEEK**** AND ****USR****
;
;
; 'OUT I,J(,K,L)'
;
; OUTPUTS EXPRESSION 'J' TO PORT 'I', AND MAY BE REPEATED AS
; IN DATA 'L' TO PORT 'K' AS MANY TIMES AS NEEDED. THIS COM-
; MAND MODIFIES *, A SMALL SECTION OF CODE ABOVE ADDRESS 2K.
;
; 'INP (I)'
;
; THIS FUNCTION RETURNDS DATA READ FROM  INPUT  PORT 'I'  AS
; ITS VALUE. IT ALSO MODIFIES CODE JUST ABOVE 2K.
;
; 'WAIT I,J,K'
;
; THIS COMMAND READS THE STATUS OF PORT 'I', EXCLUSIVE  OR'S
; THE RESULT WITH 'K', IF THE RESULT IS ONE, OR IF NOT  WITH
; ZERO, AND'S WITH 'J' AND RETURNS WHEN THE RESULT IS   NON-
; ZERO. ITS MODIFIED CODE IS ALSO ABOVE 2K.
;
; 'POKE I,J(,K,L)
;
; THIS COMMAND WORKS LIKE OUT EXCEPT THAT IT PUTS  DATA  'J'
; INTO MEMORY LOCATION 'I'.
;
; 'PEEK (I)'
;
; THIS FUNCTION WORKS LIKE INP EXCEPT THAT IT PUTS DATA  'J'
; FROM MEMORY LOCATION 'I'.
;
; 'USR(I(,J))'
;
; USR CALL A MACHINE LANGUAGE SUBROUTINE AT LOCATION 'I'  IF
; THE OPTIONAL PARAMETER 'J' IS USED ITS VALUE IS PASSED  IN
; HL. THE VALUE OF THE FUNCTION SHOULD BE RETURNED IN HL.
;
;
OUTCMD:
	CALL	EXP
	MOV	AL,BL
	MOV	[OUTIO+1],AL
	MOV	AH,','
	CALL	IGNBLNK
	JZ	OUT1	;FOUND MORE TO WORK ON
	JMP	QWHAT
OUT1:	CALL	EXP
	MOV	AL,BL
	CALL	OUTIO
	MOV	AH,','
	CALL	IGNBLNK
	JNZ	OUTCMD1
	JP	OUTCMD
OUTCMD1:CALL	FINISH
WAITCM:
	CALL	EXP
	MOV	AL,BL
	MOV	[WAITIO+1],AL
	MOV	AH,','
	CALL	IGNBLNK
	JZ	WT1
	JMP	QWHAT
WT1:	CALL	EXP
	PUSH	BX
	MOV	AH,','
	CALL	IGNBLNK
	JNZ	WAIT1
	CALL	EXP
	MOV	AL,BL
	POP	BX
	MOV	BL,AL
	JP	WAIT2
WAIT1:	MOV	BH,0
WAIT2:	JMP	WAITIO
INP:
	CALL	PARN
	MOV	AL,BL
	MOV	[INPIO+1],AL
	MOV	BX,0
	JMP	INPIO
	JP	QWT
POKE:
	CALL	EXP
	PUSH	BX
	MOV	AH,','
	CALL	IGNBLNK
	JZ	POK1
	JMP	QWHAT
POK1:	CALL	EXP
	MOV	AL,BL
	POP	BX
	MOV	[BX],AL
	MOV	AH,','
	CALL	IGNBLNK
	JNZ	POK2
	JP	POKE
POK2:	CALL	FINISH
PEEK:
	CALL	PARN
	MOV	BL,[BX]
	MOV	BH,0
	RET
	JMP	QWHAT
USR:
	PUSH	CX
	MOV	AH,'('
	CALL	IGNBLNK
	JNZ	QWT
	CALL	EXP	;EXPR
	MOV	AH,')'
	CALL	IGNBLNK	;EXPR
	JNZ	PASPRM
	PUSH	DX
	MOV	DX,USRET
	PUSH	DX
	PUSH	BX
	RET	;CALL USR ROUTINE
PASPRM:
	MOV	AH,','
	CALL	IGNBLNK
	JNZ	USRET1
	PUSH	BX
	CALL	EXP
	MOV	AH,')'
	CALL	IGNBLNK
	JNZ	USRET1
	POP	CX
	PUSH	DX
	MOV	DX,USRET
	PUSH	DX
	PUSH	CX
	RET	;CALL USR ROUTINE
USRET:
	POP	DX
USRET1:	POP	CX
	RET
QWT:	JMP	QWHAT
;
;
; ****DIVIDE**** AND ****CHKSGN****
; ****CHKSGN**** AND ****CKHLDE****
;
;
; 'DIVIDE DIVIDES BX BY DX, RESULT IN CX, REMAINDER IN BX
;
; 'CHKSGN' CHECKS SIGN OF BX. IFF +, NO CHANGE. IFF -, CHANGE
;	SIGN AND FLIP SIGN OF C
;
; 'CHGSGN' CHANGES SIGN OF BX AND CL UNCONDITIONALLY.
;
; 'CKHLDE' CHECK SIGN OF BX AND DX. IFF DIFFERENT, BX AND DX
; ARE INTERCHANGED. IFF SAME SIGN, NOT INTERCHANGED.   EITHER
; CASE, BX AND DX ARE THEN COMPARED TO SET THE FLAGS.
;
;
DIVIDE:
	PUSH	DX	;PRESERVE DX ACCROSS CALL
	PUSH	DX
	XOR	DX,DX
	POP	CX
	MOV	AX,BX
	IDIV	AX,CX
	MOV	CX,AX	;QUOTIENT
	MOV	BX,DX	;REMAINDER
	POP	DX	;DX RESTORED
	RET
	;
CHKSGN:
	OR	BX,BX	;SET FLAGS TO CHECK SIGN
	JNS	RET	;IFF -, CHANGE SIGN
	;
CHGSGN:
	NOT	BX	;****CHGSGN****
	INC	BX
	XOR	CH,128
	RET
	;
CKHLDE:
	MOV	AL,BH
	XOR	AL,DH	;SAME SIGN?
	JNS	CK1	;YES, COMPARE
	XCHG	DX,BX
CK1:
	CMP	BX,DX
	RET
;
;
; ****SETVAL**** AND ****FIN**** AND ****ENDCHK****
; ****ERROR**** AND FRIENDS
;
;
; 'SETVAL' EXPECTS A VARIABLE, FOLLOWED BY AN EQUAL SIGN AND
; THEN AN EXPR. IT EVALUATES THE EXPR AND SETS THE  VARIABLE
; TO THAT VALUE.
;
; 'FIN' CHECKS THE END OF A COMMAND. IFF IT ENDED WITH ";" ,
; EXECUTION CONTINUES. IFF IT ENDED WITH A CR, IT FINDS  THE
; NEXT LINE AND CONTINUES FROM THERE.
;
; 'ENDCHK' CHECKS IFF A COMMAND IS ENDED WITH A CR, THIS  IS
; REQUIRED IN CERTAIN COMMANDS. (GOTO, RETURN, AND STOP,ETC)
;
; 'ERROR' PRINTS THE STRING POINTED BY DX (AND ENDS  WITH  A
; 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, IFF 'CURRNT' -> ZERO (INDICAT -
; ING A DIRECT COMMAND), THE DIRECT COMMAND IS NOT PRINTED ,
; AND IFF 'CURRNT' -> NEGATIVE # (INDICATING 'INPUT' COMMAND
; THE INPUT LINE IS NOT PRINTED AND EXECUTION IS NOT TERMIN-
; ATED BUR CONTINUED AT 'INPERR').
;
; RELATED TO 'ERROR' ARE THE FOLLOWING:
;
;	'QWHAT' SAVES TEXT POINTER IN STACK AND GETS MESSAGE
;		"WHAT?"
;	'AWHAT'	JUST GETS MESSAGE "WHAT?" AND JUMPS TO ERROR
;
;	'QSORRY' AND 'ASORRY' DO THE SAME KIND OF THING.
;
;	'QHOW' AND 'AHOW' IN THE ZERO PAGE SECTION ALSO   DO
;		THIS.
;
;
SETVAL:
	CALL	TSTV	;SEE IT IT'S A VARIABLE
	JC	QWHAT	;"WHAT" NO VARIABLE
	PUSH	BX	;SAVE ADDR OF VARIABLE
	MOV	AH,'='
	CALL	IGNBLNK
	JNZ	SV1
	CALL	EXP
	MOV	CX,BX	;VALUE IN CX NOW
	POP	BX	;GET ADDR
	MOV	[BX],CL	;SAVE VALUE
	INC	BX
	MOV	[BX],CH	;SAVE VALUE
	RET
SV1:
	JMP	QWHAT	;NO '=' SIGN
	;
FIN:
	MOV	AH,';'
	CALL	IGNBLNK
	JNZ	FI1
	POP	AX
	JMP	RUNSML
FI1:
	MOV	AH,0DH
	CALL	IGNBLNK
	JNZ	RET
	POP	AX
	JMP	RUNNXL	;RUN NEXT LINE
FI2:
	RET	;ELSE RETURN TO CALLER
	;
ENDCHK:
	MOV	AH,0DH	;END WITH CR?
	CALL	IGNBLNK
	JZ	RET	;OK, ELSE SAY "WHAT?"
	;
QWHAT:
	PUSH	DX	;****QWHAT****
AWHAT:
	MOV	DX,WHAT	;****AWHAT****
ERROR:
	SUB	AL,AL	;****ERROR****
	CALL	PRTSTG	;PRINT 'WHAT?','HOW?'
	POP	DX
	MOV	SI,DX
	LODB
	PUSH	AX	;SAVE THE CHARACTER
	SUB	AL,AL	;AND PUT A ZERO THERE
	MOV	DI,DX
	STOB
	MOV	BX,[CURRNT]	;GET CURRENT LINE #
	CMP	W,[CURRNT],0	;DIRECT COMMAND?
	JNZ	ERR1	;IFF ZERO, JUST RESTART
	JP	ERR2	;SAVE A BYTE
ERR1:	MOV	AL,[BX]	;IFF NEGATIVE,
	OR	AL,AL
	JNS	ERR1A
	JMP	INPERR	;REDO INPUT
ERR1A:	CALL	PRTLN	;ELSE PRINT THE LINE
	DEC	DX
	POP	AX
	MOV	DI,DX
	STOB	;RESTORE THE CHAR
	MOV	AL,63	;PRINT A '?'
	CALL	CHROUT
	SUB	AL,AL	;AND THE REST OF THE
	CALL	PRTSTG	;LINE
ERR2:	JMP	RSTART	
QSORRY:
	PUSH	DX	;****QSORRY****
ASORRY:
	MOV	DX,SORRY	;****ASORRY****
	JP	ERROR
;
;
; ****GETLN**** AND ****FNDLN****
;
;
; 'GETLN' READS AN INPUT LINE INTO 'BUFFER'. IT FIRST PROMPTS
; THE CHARACTER IN A (GIVEN BY THE CALLER), THEN IT FILLS THE
; BUFFER AND ECHOS IT. IT USES BDOS PRIMITIVES TO  ACCOMPLISH
; THIS. ONCE A FULL LINE IS READ IN, 'GETLN' RETURNS.
;
; 'FNDLN' FINDS A LINE WITH A GIVEN LINE #(IN BX) IN THE TEXT
; SAVE AREA. DX IS USED AS THE TEXT POINTER. IFF THE LINE  IS
; FOUND, DX WILL POINT TO THE BEGINNING OF THAT LINE IFF THAT
; LINE (I.E. THE LOW BYTE OF THE LINE #), AND FLAGS ARE NC&Z.
; IFF THAT LINE IS NOT THERE AND A LINE WITH A HIGHER LINE  #
; IS FOUND, DX POINTS TO THERE AND FLAGS ARE NC&NZ.  IFF   WE
; REACHED THE END OF TEXT SAVE AREA AND CANNOT FIND THE LINE,
; FLAGS ARE C&NZ.
; 'FNDLN' WILL INITIALIZE DX TO THE  BEGINNING  OF  THE  TEXT
; SAVE AREA TO START THE SEARCH. SOME OTHER ENTRIES  OF  THIS
; ROUTINE WILL NOT INITIALIZE DX AND DO THE SEARCH.
;
; 'FNDLNP' WILL START WITH DX AND SEARCH FOR THE LINE #.
;
; 'FNDNXT' WILL BUMP DX BY  2, FIND A 0DH AND THEN START  THE
;	SEARCH.
; 'FNDSKP' USES DX TO FIND A CR, AND THEN STARTS THE SEARCH.
;
;
;
GETLN:
	CALL	CHROUT	;****GETLN****
GL1:
	MOV	DX,BUFFER-2
	PUSH	DX
	MOV	AH,BCONIN ;BUFFERED CONSOLE INPUT
	INT	33	;CALL MS-DOS
	POP	DX
	ADD	DL,[BUFFER-1]
	INC	DX
	INC	DX
	INC	DX
	MOV	DI,DX	;FOR CONSISTANCY
	PUSH	DX
	CALL	CRLF	;NEED CRLF
	POP	DX
	RET	;WE'VE GOT A LINE
;
; AT ENTRY BX -> LINE # TO BE FOUND
;
FNDLN:
	OR	BX,BX	;CHECK SIGN OF BX
	JNS	FND1	;IT CAN'T BE -
	JMP	QHOW	;ERROR
FND1:	MOV	DX,TXTBGN
	;
FNDLNP:
FL1:
	PUSH	BX	;SAVE LINE #
	MOV	BX,[TXTUNF]	;CHECK IFF WE PASSED END
	DEC	BX
	CMP	BX,DX	;SUBSTITUTE FOR CALL 4
	POP	BX	;GET LINE # BACK
	JC	RET	;C, NZ PASSED END
	MOV	SI,DX
	LODW
	CMP	AX,BX
	JC	FL2
	RET	;NC,Z:FOUND;NC,NZ:NOT FOUND
	;
FNDNXT:                 ;****FNDNXT****
	INC	DX
FL2:
	INC	DX
	;
FNDSKP:
	MOV	SI,DX
	LODB	;****FNDSKP****
	CMP	AL,0DH	;TRY TO FIND CR
	JNZ	FL2	;KEEP LOOKING
	INC	DX
	JP	FL1	;CHECK IFF END OF TEXT
;
;
; **** PRTSTG **** QTSTG **** PRTNUM **** PRTLN ****
;
;
; 'PRTSTG PRINTS A STRING POINTED TO BY DX. IT STOPS PRINTING
; AND RETURNS TO CALLER WHEN EITHER A 0DH IS PRINTED OR  WHEN
; THE NEXT BYTE IS THE SAMES AS WHAT WAS IN A  ( GIVEN BY THE
; CALLER). OLD AL IS STORED IN CH, OLD CH IS LOST.
;
; 'QTSTG' LOOKS FOR A BACK-SLASH,  SINGLE QUOTE,   OR  DOUBLE
; QUOTE. IFF NONE OF THESE, RETURN TO CALLER. IF BACK SLASH \
; OUTPUT A ODH WITHOUT A LF. IFF SINGLE OR DOUBLE QUOTE,PRINT
; THE STRING IN THE QUOTE AND DEMANDS A MATCHING UNQUOTE. AF-
; TER THE PRINTING THE NEXT 3 BYTES OF THE CALLER  IS SKIPPED
; OVER (USUALLY A JMP INSTRUCTION).
;
; 'PRTNUM' PRINTS THE NUMBER IN HL. LEADING BLANKS  ARE ADDED
; IFF NEEDED TO PAD THE NUMBER OF SPACES TO THE NUMBER IN  C.
; NOWEVER, IFF THE NUMBER OF DIGITS IS LARGER THAN THE NUMBER
; IN C, ALL DIGITS ARE PRINTED ANYWAY. NEGATIVE SIGN IS  ALSO
; PRINTED AND COUNTED IN, POSITIVE SIGN IS NOT.
;
; 'PRTLN' PRINTS A SAVED TEXT LINE WITH LINE # AND ALL.
;
;
;
PRTSTG:
	MOV	CH,AL	;****PRTSTG****
PS1:
	MOV	SI,DX
	LODB	;GET A CHAR
	LAHF            ;PRESERVE FLAGS
	INC	DX
	SAHF	        ;RESTORE FLAGS
	CMP	AL,CH	;SAME AS OLD A?
	JNZ	PS2	;YES, RETURN
        RET
PS2:	CALL	CHROUT	;ELSE, PRINT IT
	CMP	AL,0DH	;WAS IT A CR?
	JNZ	PS1	;NO, NEXT
	RET
	;
QTSTG:
	MOV	AH,'"'
	CALL	IGNBLNK
	JNZ	QT3
	MOV	AL,34	;IT IS A '"'
QT1:
	CALL	PRTSTG	;PRINT UNTIL ANOTHER
	CMP	AL,0DH	;WAS LAST ONE A CR?
	POP	BX	;RETURN ADDRESS
	JNZ	QT2	;WAS CR, RUN NEXT LINE
        JMP     RUNNXL
QT2:
	INC	BX	;SKIPS TWO BYTES ON RETURN!!!!
	INC	BX
	JMP	BX	;JUMP TO ADDRESS IN BX
QT3:
	MOV	AH,39	;IS IT A SINGLE QUOTE (')?
	CALL	IGNBLNK
	JNZ	QT4
	MOV	AL,39	;YES, DO SAME
	JP	QT1	;AS IN ' " '
QT4:
	MOV	AH,'\'
	CALL	IGNBLNK	;IS IT BACK-SLASH?('\')
	JNZ	QT5
	MOV	AL,141	;YES, 0DH WITHOUT LF!
	CALL	CHROUT	;DO IT TWICE
	CALL	CHROUT	;TO GIVE TTY ENOUGH TIME
	POP	BX	;RETURN ADDRESS
	JP	QT2
QT5:
	RET		;NONE OF THE ABOVE
	;
; ON ENTRY BX = BINARY #,CL = # SPACES
;
PRTNUM:
	PUSH	DX	;****PRTNUM****
	MOV	DX,10	;DECIMAL
	PUSH	DX	;SAVE AS A FLAG
	MOV	CH,DH	;CH=SIGN
	DEC	CL	;CL=SPACES
	CALL	CHKSGN	;CHECK SIGN
	JNS	PN1	;NO SIGN
	MOV	CH,45	;CH=SIGN
	DEC	CL	;'-' TAKES SPACE
PN1:
	PUSH	CX	;SAVE SIGN % SPACE
PN2:
	CALL	DIVIDE	;DIVIDE BX BY 10 (IN DX)
	OR	CX,CX	;CX HAS QUOTIENT
	JZ	PN3	;YES, WE GOT ALL
	POP	AX	;GET SIGN AND SPACE COUNT
	PUSH	BX	;SAVE REMAINDER
	DEC	AL	;DEC SPACE COUNT
	PUSH	AX	;SAVE NEW SIGN AND SPACE COUNT
	MOV	BX,CX	;MOVE RESULT TO BX
	JP	PN2	;AND DIVIDE BY 10
PN3:
	POP	CX	;WE GOT ALL DIGITS IN
PN4:
	DEC	CL	;THE STACK
	MOV	AL,CL	;LOOK AT SPACE COUNT
	OR	AL,AL
	JS	PN5	;NO LEADING BLANKS
	MOV	AL,32	;LEADING BLANKS
	CALL	CHROUT
	JP	PN4
PN5:
	MOV	AL,CH	;PRINT SIGN
	CALL	CHROUT	;MAYBE, OR NULL
	MOV	DL,BL	;LAST REMAINDER IN E
PN6:
	MOV	AL,DL	;CHECK DIGIT IN E
	CMP	AL,10	;10 IS FLAG FOR NO MORE
	POP	DX
	JZ	RET	;IFF SO, RETURN
	ADD	AL,48	;ELSE CONVERT TO ASCII
	CALL	CHROUT	;AND PRINT THE DIGIT
	JP	PN6	;GO BACK FOR MORE
	;
PRTLN:
	MOV	SI,DX
	LODW
	MOV	BX,AX
	INC	DX
	INC	DX	;MOVE POINTER
PRTLN1:	MOV	CL,5	;PRINT 5 DIGIT LINE #
	CALL	PRTNUM
	MOV	AL,32	;FOLLOWED BY A BLANK
	CALL	CHROUT
	SUB	AL,AL	;AND THEN THE TEXT
	CALL	PRTSTG
	RET
;
;
;
; **** MVUP **** MVDOWN **** POPA **** PUSHA ****
;
; 'MVUP' MOVES A BLOCK UP FROM WHERE DX -> WHERE CX -> UNTIL
; DX = BX
;
; 'MVDOWN' MOVES A BLOCK DOWN FROM WHERE DX -> TO WHERE BX->
; UNTIL DX = CX.
;
; 'POPA' RESTORES THE 'FOR' LOOP VAR SAVE AREA FROM THE STACK.
;
; 'PUSHA' STACKS THE 'FOR' LOOP VARIABLE SAVE AREA IN THE STACK
;
;
MVUP:
	CMP	DX,BX	;***MVUP***
	JZ	RET	;DE = HL, RETURN
	MOV	SI,DX
	LODB	;GET ONE BYTE
	MOV	DI,CX
	STOB	;MOVE IT
	INC	DX
	INC	CX
	JP	MVUP	;UNTIL DONE
	;
MVDOWN:
	CMP	DX,CX
	JZ	RET	;YES, RETURN
MD1:
	LAHF
	DEC	DX
	DEC	BX
	MOV	SI,DX
	LODB	;BOTH POINTERS AND
	MOV	[BX],AL	;THEN DO IT
	JP	MVDOWN	;LOOP BACK
	;
POPA:
	POP	CX	;CX = RETURN ADDR
	POP	BX	;RESTORE LOPVAR, BUT
	MOV	[LOPVAR],BX	;=0 MEANS NO MORE
	OR	BX,BX
	JZ	PP1	;YES, GO RETURN
	POP	BX	;NO, RESTORE OTHERS
	MOV	[LOPINC],BX
	POP	BX
	MOV	[LOPLMT],BX
	POP	BX
	MOV	[LOPLN],BX
	POP	BX
	MOV	[LOPPT],BX
PP1:
	PUSH	CX	;CX = RETURN ADDR
	RET
	;
PUSHA:
	MOV	BX,STKLMT	;****PUSHA****
	CALL	CHGSGN
	POP	CX	;CX=RET ADDR
	ADD	BX,SP
	JC	PUSHB	;YES, SORRY FOR THAT.
        JMP     QSORRY
PUSHB:	MOV	BX,[LOPVAR]	;ELSE SAVE LOOP VARS
	OR	BX,BX	;THAT WILL BE ALL
	JZ	PU1
	MOV	BX,[LOPPT]	;ELSE, MORE TO SAVE
	PUSH	BX
	MOV	BX,[LOPLN]	;ELSE, MORE TO SAVE
	PUSH	BX
	MOV	BX,[LOPLMT]
	PUSH	BX
	MOV	BX,[LOPINC]
	PUSH	BX
	MOV	BX,[LOPVAR]
PU1:
	PUSH	BX
	PUSH	CX	;CX = RETURN ADDR
	RET
	;
	;
	; **** OUTC **** CHKIO ****
	;
	;
	; THESE ARE THE ONLY I/O ROUTINES IN TBI.
	;
	;
	; 'CHKIO' CHECKS THE INPUT, IFF NO INPUT, IT WILL RETURN TO  THE
	; CALLER WITH THE Z FLAG SET. IFF THERE IS INPUT, THE Z FLAG  IS
	; CLEARED AND THE INPUT BYRE IS IN A. HOWEVER, IFF THE INPUT  IS
	; A CONTROL-O, THE 'OCSW' IS COMPLIMENTED, AND THE Z FLAG IS RE-
	; TURNED. IFF A CONTROL-C IS READ, 'CHKIO' WILL RESTART TBI  AND
	; DOES NOT RETURN TO THE CALLER.
	;
CRLF:	MOV	AL,0DH	;****CRLF****
CHROUT:
	CMP	[OCSW],0
	JZ	COUT1	;SEE IF OUTPUT REDIRECTED
	PUSH	CX	;SAVE CX ON STACK
	PUSH	DX	;AND DX
	PUSH	BX	;AND BX TOO
	MOV	[OUTCAR],AL	;SAVE CHATACTER
	MOV	DL,AL	;PUT CHAR IN E FOR CP/M
	MOV	AH,CONOUT ;CONSOLE OUTPUT
	INT	33	;CALL MS-DOS AND OUTPUT CHAR
	MOV	AL,[OUTCAR]	;GET CHAR. BACK
	CMP	AL,0DH	;WAS IT A 'CR'?
	JNZ	DONE	;NO,DONE
	MOV	DL,0AH	;GET LINEFEED
	MOV	AH,CONOUT ;CONSOLE OUTPUT AGAIN
	INT	33	;CALL MS-DOS
DONE:
	MOV	AL,[OUTCAR]	;GET CHAR BACK
IDONE:
	POP	BX	;GET H BACK
	POP	DX	;AND D
	POP	CX	;THEN H
	RET	;DONE AT LAST
COUT1:
	CMP	B,AL,0	;IS IT NULL?
	JZ	RET	;SKIP IT
	STOB		;STORE AL (CHAR) IN BUFFER
	INC	[BUFFER-1] ;INCREMENT COUNTER
	RET		;DONE
CHKIO:
	PUSH	CX	;SAVE B ON STACK
	PUSH	DX	;AND D
	PUSH	BX	;THEN H
	MOV	AH,CONST ;GET CONSOLE STATUS WORD
	INT	33	;CALL MS-DOS
	OR	AL,AL	;SET FLAGS
	JNZ	CI1	;IF READY, GET CHAR
	JP	IDONE	;RESTORE AND RETURN
CI1:
	MOV	AH,1	;CALL THE BDOS
	INT	33	;CALL MS-DOS
CI2:
	CMP	AL,18H	;IS TI CONTROL-X?
	JNZ	IDONE	;RETURN AND RESTORE IF NOT
	JMP	RSTART	;YES, RESTART TBI
LSTROM:	EQU	$	;ALL ABOVE CAN BE ROM
OUTIO:
	OUTB	0FFH
	RET
WAITIO:
	INB	0FFH
	XOR	AL,BH
	AND	AL,BL
	JZ	WAITIO
	CALL	FINISH
INPIO:
	INB	0FFH
	MOV	BL,AL
	RET
	;
;
; IGNBLNK
;
;	DEBLANKS WHERE DX->
;	IF (DX)=AH THEN DX:=DX+1
;
IGNBLNK:MOV	SI,DX
IGN1:   LODB            ;GET CHAR IN AL
	CMP	AL,32	;IGNORE BLANKS
	JNZ	IGN2	;IN TEXT (WHERE DX ->)
        INC     DX
        JP      IGN1
IGN2:	CMP	AL,AH	;IS SEARCH CHARACTER FOUND AT (DX)?
	JNZ	RET	;NO, RETURN, POINTER (DX) STAYS
	LAHF		;SAVE RESULTS OF COMPARISON
	INC	DX	;INC POINTER IF CHARACTER MATCHES
	SAHF		;RETURN RESULT OF COMPARISON TO FLAGS
	RET
	;
FINISH:	POP	AX
	CALL	FIN	;CHECK END OF COMMAND
	JMP	QWHAT	;PRINT "WHAT?" IFF WRONG
	;
OUTCAR:
	DB	0	;OUTPUT CHAR STORAGE
OCSW:
	DB	0FFH	;OUTPUT SWITCH
CURRNT:
	DW	0	;POINTS TO CURRENT LINE
STKGOS:
	DW	0	;SAVES SP IN 'GOSUB'
VARNXT:
	DW	0	;TEMP STORAGE
STKINP:
	DW	0	;SAVES SP IN 'INPUT'
LOPVAR:
	DW	0	;'FOR' LOOP SAVE AREA
LOPINC:
	DW	0	;INCREMENT
LOPLMT:
	DW	0	;LIMIT
LOPLN:
	DW	0	;LINE NUMBER
LOPPT:
	DW	0	;TEST POINTER
RANPNT:
	DW	0	;RANDOM NUMBER POINTER
TXTUNF:
	DW	TXTBGN	;-> UNFILLED TEXT AREA
TXTBGN:	DS	1
MSG1:	DB	'8086 TINY BASIC V1.1 27 JUNE 82',0DH
	ORG	2000H	;MISC STORAGE, INCLUDING STACK
TXTEND:	EQU	$	;TEST AREA SAVE AREA ENDS
VARBGN:
	DS	54	;VARIABLE @(0)
	DB	80	;MAX CHARS IN BUFFER
	DB	0	;CHAR COUNT
BUFFER:
	DS	80	;BUFFER MUST BE AFTER TEXT AREA
BUFEND:	EQU	$
	DS	400	;EXTRA BYTES FOR STACK
STKLMT:	DS	100	;TOP LIMIT FOR STACK
STACK:	EQU	$	;STACK STARTS HERE
	END	
