******************************************************************
*                                                                *
*                 Tiny BASIC for the Motorola MC68000            *
*                                                                *
*                                                                *
* Derived from Palo Alto Tiny BASIC as published in the May 1976 *
* issue of Dr. Dobb's Journal. Adapted to the 68000 by:          *
*                 Gordon Brandly                                 *
*                 Apt. C 8710-97th Ave                           *
*                 Edmonton, Alberta                              *
*                 Canada  T6C 2C1                                *
*                                                                *
*                                                                *
* This version typed in from Dr. Dobbs Toolbook of 68000         *
* Programming June 1991 by W. de Waal                            *
*                                                                *
******************************************************************
*                                                                *
*    Copyright (C) 1984 by Gordon Brandly. This program may be   *
*    freely distributed for personal use only. All commercial    *
*                      rights are reserved.                      *
******************************************************************
*
* Vers 1.0 1984/7/17  - Original version by Gordon Brandly
*      1.1 1984/12/9  - Addition of '$' print term by Marvin Lipford
*      1.2 1985/4/9   - Bug fix in multiply routine by Rick Murray

CR	EQU	$0D	ASCII EQUATES
LF	EQU	$0A
TAB	EQU	$09
CTRLC	EQU	$03
CTRLH	EQU	$08
CTRLS	EQU	$13
CTRLX	EQU	$18

BUFLEN	EQU	80	length of keyboard input buffer

	ORG	$2000	MARVIN USER RAM START
*
* Standard jump table. You can change these addresses if you are
* customizing this interpreter for a different environment.
*
START	BRA.L	CSTART	Cold Start entry point
GOWARM	BRA.L	WSTART  Warm Start entry point
GOOUT	BRA.L	OUTC	Jump to character-out routine
GOIN	BRA.L	INC	Jump to character-in routine
GOAUXO	BRA.L	AUXOUT  Jump to Auxiliary-out routine
GOAUXI	BRA.L	AUXIN	Jump to Auxiliary-in routine
GOBYE	BRA.L	BYEBYE	Jump to monitor, DOS, etc.
*
* Modifiable system constants:
TXTBGN	DC.L	TXT	beginning of program memory
ENDMEN	DC.L	$8000	end of available memory
*
* The main interpreter starts here
*
CSTART	MOVE.L	ENDMEM,SP	INITIALISE STACK POINTER
	LEA	INITMSG,A6	TELL WHO WE ARE
	BSR.L	PRMESG
	MOVE.L	TXTBGN,TXTUNF	INIT. END-OF-PROGRAM POINTER
	MOVE.L	ENDMEM,D0	GET ADDRESS OF END OF MEMORY
	SUB.L	#2048,D0	RESERVE 2K FOR THE STACK
	MOVE.L	D0,STKLMT
	SUB.L	#108,D0		RESERVE VARIABLE AREA (27 LONG WORDS)
	MOVE.L	D0,VARBGN
WSTART	CLR.L	D0		INITIALISE INTERNAL VARIABLES
	MOVE.L	D0,LOPVAR
	MOVE.L	D0,STKGOS
	MOVE.L	D0,CURRNT	CURRENT LINE NUMBER POINTER = 0
	MOVE.L	ENDMEM,SP	INIT SP AGAIN, JUST IN CASE
	LEA	OKMSG,A6	DISPLAY "OK"
	BSR.L	PRMESG
ST3	MOVE.B	#'>',D0		PROMPT WITH A '>' AND
	BSR.L	GETLN		READ A LINE
	BSR.L	TOUPBUF		CONVERT TO UPPER CASE
	MOVE.L	A0,A4		SAVE POINTER TO END OF LINE
	LEA	BUFFER,A0	POINT TO THE BEGINNING OF LINE
	BSR.L	TSTNUM		IS THERE A NUMBER THERE?
	BSR.L	IGNBLK		SKIP TRAILING BLANKS
	TST	D1		DOES LINE NO. EXIST? (OR NON ZERO?)
	BEQ.L	DIRECT		IF NOT, IT'S A DIRECT STATEMENT
	CMP.L	#$FFFF,D1	SEE IF LINE NO. IS <= 16 BITS
	BCC.L	QHOW		IF NOT,WE'VE OVERFLOWED
	MOVE.B	D1,-(A0)	STORE THE BINARY LINE NO.
	ROR	#8,D1		(KLUDGE TO STORE WORD ON A
	MOVE.B	D1,-(A0)	POSSIBLE BYTE BOUNDARY)
	ROL	#8,D1
	BSR.L	FNDLN		FIND THIS LINE IN SAVE AREA
	MOVE.L	A1,A5		SAVE POSSIBLE LINE POINTER
	BNE	ST4		IF NOT FOUND, INSERT
	BSR.L	FNDNXT		FIND THE NEXT LINE (INTO A1)
	MOVE.L	A5,A2		POINTER TO THE LINE TO BE DELETED
	MOVE.L	TXTUNF,A3	POINTS TO TOP OF SAVE AREA
	BSR.L	MVUP		MOVE UP TO DELETE
	MOVE.L	A2,TXTUNF	UPDATE THE END POINTER
ST4	MOVE.L	A4,D0		CALCULATE THE LENGTH OF NEW LINE
	SUB.L	A0,D0
	CMP.L	#3,D0		IS IT JUST A LINE NO. & CR?
	BEQ	ST3		IF SO, IT WAS JUST A DELETE
	MOVE.L	A3,A6
	ADD.L	D0,A3
	MOVE.L	VARBGN,D0	SEE IF THERE'S ENOUGH ROOM
	CMP.L	A3,D0
	BLS.L	QSORRY		IF NOT, SAY SO
	MOVE.L	A3,TXTUNF	IF SO, STORE NEW END POSITION
	MOVE.L	A6,A1		POINTS TO OLD UNFILLED AREA
	MOVE.L	A5,A2		POINTS TO BEGINNING OF MOVE AREA
	BSR.L	MVDOWN		MOVE THINGS OUT OF THE WAY
	MOVE.L	A0,A1		SET UP TO DO THE INSERTION
	MOVE.L	A5,A2
	MOVE.L	A4,A3
	BSR.L	MVUP		DO IT
	BRA	ST3		GO BACK AND DO ANOTHER LINE
*
******************************************************************
*
* *** 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', A0 should point to the string. A1 should point to
* the character table, and A2 should point to the execution
* table. At 'DIRECT', A0 should point to the string, A1 and
* A2 will be set up to point to TAB1 and TAB1.1, which are
* the tables 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'.
*
* There are two tables; the character table and the execution
* table. The character table consists of any number of text items.
* Each item is a string of characters with the last character's
* high bit set to one. The execution table holds a 16-bit
* execution address that corresponds to each entry in the
* character table.
*
* The end of the character table is a 0 byte which corresponds
* to the default routine in the execution table, which is
* executed if none of the other table items are matched.
*
* Character-matching tables:
TAB1	DC.B	'LIST'		Direct commands
	DC.B	'LOAD'
	DC.B	'NEW'
	DC.B	'RUN'
	DC.B	'SAVE'
TAB2	DC.B	'NEXT'
	DC.B	'LET'
	DC.B	'IF'
	DC.B	'GOTO'
	DC.B	'GOSUB'
	DC.B	'RETURN'
	DC.B	'REM'
	DC.B	'FOR'
	DC.B	'INPUT'
	DC.B	'PRINT'
	DC.B	'POKE'
	DC.B	'STOP'
	DC.B	'BYE'
	DC.B	'CALL'
	DC.B	0
TAB4	DC.B	'PEEK'
	DC.B	'RND'
	DC.B	'ABS'
	DC.B	'SIZE'
	DC.B	0
TAB5	DC.B	'TO'
	DC.B	0
TAB6	DC.B	'STEP'
	DC.B	0
TAB8	DC.B	'>='
	DC.B	'<>'
	DC.B	'>'
	DC.B	'='
	DC.B	'<='
	DC.B	'<'
	DC.B	0
*
	EVEN			Pseudo-op to align to word boundary
* Exectution address tables :
TAB1.1	DC.W	LIST
	DC.W	LOAD
	DC.W	NEW
	DC.W	RUN
	DC.W	SAVE
TAB2.1	DC.W	NEXT
	DC.W	LET
	DC.W	IF
	DC.W	GOTO
	DC.W	GOSUB
	DC.W	RETURN
	DC.W	REM
	DC.W	FOR
	DC.W	INPUT
	DC.W	PRINT
	DC.W	POKE
	DC.W	STOP
	DC.W	GOBYE
	DC.W	CALL
	DC.W	DEFLT
TAB4.1	DC.W	PEEK
	DC.W	RND
	DC.W	ABS
	DC.W	SIZE
	DC.W	XP40
TAB5.1	DC.W	FR1
	DC.W	QWHAT
TAB6.1	DC.W	FR2
	DC.W	FR3
TAB7.1	DC.W	XP11		>=
	DC.W	XP12		<>
	DC.W	XP13		>
	DC.W	XP15		=
	DC.W	XP14		<=
	DC.W	XP16		<
	DC.W	XP17
*
DIRECT	LEA	TAB1,A1
	LEA	TAB1.1,A2
EXEC	BSR.L	IGNBLK		IGNORE LEADING BLANKS
	MOVE.L	A0,A3		SAVE THE POINTER
	CLR.B	D2		CLEAR MATCH FLAG
EXLP	MOVE.B	(A0)+,D0	GET THE PROGRAM CHARACTER
	MODE.B	(A1),D1		GET THE TABLE CHARACTER
	BNE	EXNGO		IF END OF TABLE,
	MOVE.L	A3,A0		RESTORE THE END POINTER AND ...
	BRA	EXGO		EXECUTE THE DEFAULT
EXNGO	MOVE.B	D0,D3		ELSE CHECK FOR PERIOD
	AND.B	D2,D3		AND A MATCH.
	CMP.B	#'.',D3
	BEQ	EXGO		IF SO, EXECUTE
	AND.B	#$7F,D1		IGNORE THE TABLE'S HIGH BIT
	CMP.B	D0,D1		IS THERE A MATCH?
	BEQ	EXMAT
	ADDQ.L	#2,A2		IF NOT, TRY THE NEXT ENTRY
	MOVE.L	A3,A0		RESET THE PROGRAM POINTER
	CLR.B	D2		SORRY, NO MATCH
EX1	TST.B	(A1)+		GET TO THE ENT OF THE ENTRY
	BPL	EX1
	BRA	EXLP		BACK FOR MORE MATCHING
EXMAT	MOVEQ	#-1,D2		WE'VE GOT A MATCH SO FAR
	TST.B	(A1)+		END OF TABLE ENTRY?
	BPL	EXLP		IF NOT, GO BACK FOR MORE
EXGO	LEA	0,A3		EXECUTE THE APPROPRIATE ROUTINE
	MOVE	(A2),A3
	JMP	(A3)
*
******************************************************************
*
* What follows is the code to execute direct and statement
* commands. Control is transferred 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', and 'STOP': go back to the warm start point.
* For 'RUN': go execute the first stored line if any; else go
* back to the warm start point.
* FOR 'GOTO' and 'GOSUB': go execute the target line.
* For 'RETURN' and 'NEXT': go back to saved return line.
* For all others: if 'CURRNT' is 0, go to warm start; 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 WSTART
*
* 'RUN <CR>' finds the first stored line, stores its address
* in CURRNT, and starts executing it. Note that only those
* commands in TAB2 are legal for a stored programs.
*
* There are 3 more entries in 'RUN':
* 'RUNNXL' finds next line, stores it's address 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, finds the target
* line, and jumps to 'RUNTSL' to do it.
*
NEW	BSR.L	ENDCHK
	MOVE.L	TXTBGN,TXTUNF	SET THE END POINTER

STOP	BSR.L	ENDCHK
	BRA	WSTART

RUN	BSR.L	ENDCHK
	MOVE.L	TXTBGN,A0	SET POINTER TO BEGINNING
	MOVE.L	A0,CURRNT
RUNNXL	TST.L	CURRNT		EXECUTING A PROGRAM?
	BEQ.L	WSTART		IF NOT, WE'VE FINISHED A DIRECT STAT.
	CLR.L	D1		ELSE FIND THE NECT LINE NUMBER
	MOVE.L	A0,A1
	BSR.L	FNDLNP
	BCS	WSTART		IF WE'VE FALLEN OF THE END, STOP

RUNTSL	MOVE.L	A1,CURRNT	SET CURRNT TO POINT TO THE LINE NO.
	MOVE.L	A1,A0		SET THE TEXT POINTER TO
	ADDQ.L	#2,A0		THE START OF THE LINE TEXT

RUNSML	BSR.L	CHKIO		SEE IF CTRL-C WAS PRESSED
	LEA	TAB2,A1		FIND COMMAND IN TAB2
	LEA	TAB2.1,A2
	BRA	EXEC		AND EXECUTE IT

GOTO	BSR.L	EXPR		EVALUATE THE FOLLOWING EXPRESSION
	BSR.L	ENDCHK		MUST FIND END OF LINE
	MOVE.L	D0,D1
	BSR.L	FNDLN		FIND THE TARGET LINE
	BNE.L	QHOW		NO SUCH LINE NO.
	BRA	RUNTSL		GO DO IT.
*
******************************************************************
*
* *** LIST *** PRINT ***
*
* LIST has two forms:
* 'LIST <CR>' lists all saved lines
* 'LIST # <CR>' starts listing at line #
* Control-S pauses the listing, control-C stops it.
*
* PRINT command is 'PRINT ...:' or 'PRINT ...<CR>'
* where '...' is a list of expressions, formats, back-arrows,
* and strings. These items are separated by commas.
*
* A format is a # sign followed by a number. It controls
* the number of spaces the value of an expression is going to
* be printed in. It stays effective for the rest of the print
* command unless changed by another format. If no format is
* specified, 11 positions will be used.
*
* A string is quoted in a pair of single- or double-quotes
*
* An underline (back-arrow) means generate a <CR> without an <LF>
*
* A <CR LF> is generated after the entire line has been printed
* or if the list is empty. If the list ends with a semicolon,
* however, no <CR LF> is generated.
*
LIST	BSR.L	TSTNUM		SEE IF THER'S A LINE NO.
	BSR.L	ENDCHK		IF NOT, WE GET A ZERO
	BSL.L	FNDLN		FIND THIS OR NECT LINE
LS1	BCS	WSTART		WARM START IF WE PASSED THE END
	BSR.L	PRTLN		PRINT THE LINE
	BSR.L	CHKIO		CHECK FOR LISTING HALT REQUEST
	BEQ	LS3
	CMP.B	#CTRLS,D0	PAUSE THE LISTING?
	BNE	LS3
LS2	BSR.L	CHKIO		IF SO, WAIT FOR ANOTHER KEYPRESS
	BEQ	LS2
LS3	BSR.L	FNDLNP		FIND THE NEXT LINE
	BRA	LS1

PRINT	MOVE	#$11,D4		D4 = NUMBER OF PRINT SPACES
	BSR.L	TSTC		IF NULL LIST AND ":"
	DC.B	':',PR2-*
	BSR.L	CRLF		GIVE CR-LF AND CONTINUE
	BRA	RUNSML		EXECUTION ON THE SAME LINE
PR2	BSR.L	TSTC		ELSE IS IT A FORMAT?
	BC.B	'#',PR1-*
	BSR.L	EXPR		YES, EVALUATE EXPRESSION
	MOVE	D0,D4		AND SAVE IT AS PRINT WIDTH
	BRA	PR3		LOOK FOR MORE TO PRINT
PR1	BSR.L	TSTC		IS CHARACTER EXPRESSION? (MRL)
	DC.B	'$',PR4-*
	BSR.L	EXPR		YES, EVALUATE EXPRESSION (MRL)
	BSR	GOOUT		PRINT LOW BYTE (MRL)
	BRA	PR3		LOOK FOR MORE (MRL)
PR4	BSR.L	QTSTG		IS IT A STRING?
	BRA.S	PR8		IF NOT, MUST BE AN EXPRESSION
PR3	BSR.L	TSTC		IF ",", GO FIND NEXT
	DC.B	',',PR6-*
	BSR.L	FIN		IN THE LIST.
	BRA	PR0
PR6	BSR.L	CRLF		LIST ENDS HERE
	BRA	FINISH
PR8	MOVE	D4,-(SP)	SAVE THE WIDTH VALUE
	BSR.L	EXPR		EVALUATE THE EXPRESSION
	MOVE	(SP)+,D4	RESTORE THE WIDTH
	MOVE.L	D0,D1
	BSR.L	PRTNUM		PRINT ITS VALUE
	BRA	PR3		MORE TO PRINT?

FINISH	BSR.L	FIN		CHECK END OF COMMAND
	BRA.L	QWHAT		PRINT "What?" IF WRONG
*
******************************************************************
*
* *** GOSUB *** & 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 THE EXECUTION CAN BE CONTINUED AFTER THE SUBROUTINE
* 'RETURN'S. 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 SAVED ON THE STACK.
* IF WE ARE IN THE MAIN ROUTINE, 'STKGOS' IS ZEROS (THIS WAS DONE
* IN THE INITIALIZATION SECTION OF THE INTERPRETER), BUT WE STILL
* SAVE IT AS A FLAG FOR NO FURTHER 'RETURN'S.
*
* 'RETURN<CR>' UNDOES EVERYTHING THAT 'GOSUB' DID, AND THUS
* RETURNS THE EXECUTION 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	BSR.L	PUSHA		SAVE THE CURRENT 'FOR' PARAMETERS
	BSR.L	EXPR		GET LINE NUMBER
	MOVE.L	A0,-(SP)	SAVE TEXT POINTER
	MOVE.L	D0,D1
	BSR.L	FNDLN		FIND TARGET LINE
	BNE.L	AHOW		IF NOT THERE, SAY "HOW?"
	MOVE.L	CURRNT,-(SP)	FOUND IT, SAVE OLD 'CURRNT'...
	MOVE.L	STKGOS,-(SP)	AND 'STKGOS'
	CLR.L	LOPVAR		LOAD NEW VALUES
	MOVE.L	SP,STKGOS
	BRA	RUNTSL

RETURN	BSR.L	ENDCHK		THERE SHOULD BE JUST A <CR>
	MOVE.L	STKGOS,D1	GET OLD STACK POINTER
	BEQ.L	QWHAT		IF ZERO, IT DOESN'T EXIST
	MOVE.L	D1,SP		ELSE RESTORE IT
	MOVE.L	(SP)+,STKGOS	AND THE OLD 'STKGOS'
	MOVE.L	(SP)+,CURRNT	AND THE OLD 'CURRNT'
	MOVE.L	(SP)+,A0	AND THE OLD TEXT POINTER
	BSR.L	POPA		AND THE OLD 'FOR' PARAMETERS
	BRA	FINISH		AND WE ARE BACK HOME
*
******************************************************************
*
* *** FOR *** & NEXT ***
*
* 'FOR' HAS TWO FORMS:
* 'FOR VAR = EXPR1 TO EXPR2 STEP EXPR3' AND 'FOR VAR=EXPR1 TO EXPR2'
* THE SECOND FORM MEANS THE SAME THING AS THE FIRST WITH A
* STEP OF POSITIVE 1. THE INTERPRETER WILL FIND THE VARIABLE 'var'
* and set its value to the current value of 'expr1'. It also
* evaluates 'expr2' and 'expr3' and saves 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 something in the save area (indicated by a non-zero
* 'LOPVAR'), then the old save area is saved on the stack before
* the new values are stored. The interpreter 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. (i.e. purged from the stack)
*
* 'NEXT var' serves as the logical (not necessarily physical) end
* of the 'FOR' loop. The control variable 'var' is checked with
* the 'LOPVAR'. If they are not the same, the interpreter digs in
* the stack to find the right one and purges all those that didn't
* match. Either way, it then adds the 'STEP' to that variable and
* checks the result with against the limit value. If it is within
* the limit, control loops back to the command following the
* 'FOR'. If it's outside the limit, the save area is purged and
* execution continues.
*
FOR	BSR.L	PUSHA		SAVE THE OLD 'FOR' SAVE AREA
	BSR.L	SETVAL		SET THE CONTROL VARIABLE
	MOVE.L	A6,LOPVAR	SAVE ITS ADDRESS
	LEA	TAB5,A1		USE 'EXEC' TO TEST FOR 'TO'
	LEA	TAB5.1,A2
	BRA	EXEC
FR1	BSR.L	EXPR		EVALUATE THE LIMIT
	MOVE.L	D0,LOPLMT	SAVE THAT
	LEA	TAB6,A1		USE 'EXEC' TO LOOK FOR THE
	LEA	TAB6.1,A2	WORD 'STEP'
	BRA	EXEC
FR2	BSR.L	EXPR		FOUND IT, GET THE STEP VALUE
	BRA	FR4
FR3	MOVEQ	#1,D0		NOT FOUND, STEP DEFAULTS TO 1
FR4	MOVE.L	D0,LOPINC	SAVE THAT TOO
FR5	MOVE.L	CURRNT,LOPLN	SAVE ADDRESS OF CURRENT LINE NUMBER
	MOVE.L	A0,LOPPT	AND TEXT POINTER
	MOVE.L	SP,A6		DIG INTO THE STACK TO FIND 'LOPVAR'
	BRA	FR7
FR6	ADD.L	#20,A6		LOOK AT NEXT STACK FRAME
FR7	MOVE.L	(A6),D0		IS IT ZERO?
	BEQ	FR8		IF SO, WE'RE DONE
	CMP.L	LOPVAR,D0	SAME AS CURRENT LOPVAR?
	BNE	FR6		NOPE, LOOK SOME MORE
	MOVE.L	SP,A2		ELSE REMOVE 5 LONG WORDS FROM ...
	MOVE.L	A6,A1		INSIDE THE STACK
	LEA	20,A3
	ADD.L	A1,A3
	BSR.L	MVDOWN
	MOVE.L	A3,SP		SET THE SP 5 LONG WORDS UP
FR8	BRA	FINISH		AND CONTINUE EXECUTION

NEXT	BSR.L	TSTV		GET ADDRESS OF VARIABLE
	BCS.L	QWHAT		IF NO VARIABLE, SAY "WHAT?"
	MOVE.L	D0,A1		SAVE VAIRAIBLE'S ADDRESS
NX0	MOVE.L	LOPVAR,D0	IF 'LOPVAR' IS ZERO, WE NEVER...
	BEQ.L	QWHAT		HAD A FOR LOOP, SO SAY "WHAT?"
	CMP.L	D0,A1		ELSE WE CHECK THEM
	BEQ	NX3		OK, THEY AGREE
	BSR.L	POPA		NOPE, LET'S SEE THE NEXT FRAME
	BRA	NX0
NX3	MOVE.L	(A1),D0		GET CONTROL VARIABLE'S VALUE
