OpenCores
URL https://opencores.org/ocsvn/rf68000/rf68000/trunk

Subversion Repositories rf68000

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /rf68000
    from Rev 8 to Rev 9
    Reverse comparison

Rev 8 → Rev 9

/trunk/rtl/cpu/rf68000.sv
3276,6 → 3276,7
end
 
//-----------------------------------------------------------------------------
// The destination store for the MOVE instruction.
// Flags are not updated if the target is an address register.
//-----------------------------------------------------------------------------
 
3466,6 → 3467,7
begin
flag_update <= FU_ADD;
if (sz==2'b11) begin
flag_update <= FU_NONE;
Rt <= {1'b1,AAA};
if (ir[8]) begin
rfwrL <= 1'b1;
3527,6 → 3529,7
begin
flag_update <= FU_SUB;
if (sz==2'b11) begin
flag_update <= FU_NONE;
Rt <= {1'b1,AAA};
if (ir[8]) begin
rfwrL <= 1'b1;
/trunk/software/examples/TinyBasicFlt.asm
0,0 → 1,2732
******************************************************************
* *
* Tiny Float 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 *
* *
******************************************************************
* Copyright (C) 1984 by Gordon Brandly. This program may be *
* freely distributed for personal use only. All commercial *
* rights are reserved. *
******************************************************************
* Modified (c) 2022 for the rf68000. Robert Finch
* Numerics changed to floating-point
* added string handling
******************************************************************
 
* 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
 
* OPT FRS,BRS forward ref.'s & branches default to short
 
;CR EQU $0D ASCII equates
;LF EQU $0A
;TAB EQU $09
;CTRLC EQU $03
;CTRLH EQU $08
;CTRLS EQU $13
;CTRLX EQU $18
 
DT_NONE equ 0
DT_NUMERIC equ 1
DT_STRING equ 2 ; string descriptor
DT_TEXTPTR equ 3 ; pointer into program text
 
BUFLEN EQU 80 length of keyboard input buffer
STRAREASIZE EQU 2048 ; size of string area
CODE
* ORG $10000 first free address using Tutor
*
* Standard jump table. You can change these addresses if you are
* customizing this interpreter for a different environment.
*
START BRA CSTART Cold Start entry point
GOWARM BRA WSTART Warm Start entry point
GOOUT BRA OUTC Jump to character-out routine
GOIN BRA INC Jump to character-in routine
GOAUXO BRA AUXOUT Jump to auxiliary-out routine
GOAUXI BRA AUXIN Jump to auxiliary-in routine
GOBYE BRA BYEBYE Jump to monitor, DOS, etc.
*
* Modifiable system constants:
*
TXTBGN DC.L $41000 beginning of program memory
ENDMEM DC.L $47FF0 end of available memory
*
* The main interpreter starts here:
*
CSTART MOVE.L ENDMEM,SP initialize stack pointer
move.l #OUTC1,OUTPTR
move.l #INC1,INPPTR
move.l #1,_fpTextIncr
LEA INITMSG,A6 tell who we are
BSR PRMESG
MOVE.L TXTBGN,TXTUNF init. end-of-program pointer
MOVE.L ENDMEM,D0 get address of end of memory
SUB.L #4096,D0 reserve 4K for the stack
MOVE.L D0,STRSTK
ADD.L #32,D0
MOVE.L D0,STKLMT
SUB.L #512,D0 reserve variable area (32 16 byte floats)
MOVE.L D0,VARBGN
bsr ClearStringArea
WSTART
CLR.L D0 initialize internal variables
move.l #1,_fpTextIncr
clr.l IRQROUT
MOVE.L D0,LOPVAR
MOVE.L D0,STKGOS
MOVE.L D0,CURRNT ; current line number pointer = 0
MOVE.L ENDMEM,SP ; init S.P. again, just in case
bsr ClearStringStack
LEA OKMSG,A6 ; display "OK"
bsr PRMESG
ST3
MOVE.B #'>',D0 Prompt with a '>' and
bsr GETLN read a line.
bsr 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 TSTNUM is there a number there?
bsr IGNBLK skip trailing blanks
FMOVE.L FP1,D1
TST.L D2 ; does line no. exist? (or nonzero?)
BEQ DIRECT ; if not, it's a direct statement
CMP.L #$FFFF,D1 see if line no. is <= 16 bits
BCC QHOW if not, we've overflowed
MOVE.B D1,-(A0) store the binary line no.
ROR #8,D1 (Kludge to store a word on a
MOVE.B D1,-(A0) possible byte boundary)
ROL #8,D1
bsr FNDLN find this line in save area
MOVE.L A1,A5 save possible line pointer
BNE ST4 if not found, insert
bsr FNDNXT find the next line (into A1)
MOVE.L A5,A2 pointer to line to be deleted
MOVE.L TXTUNF,A3 points to top of save area
bsr 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?
BLE ST3 if so, it was just a delete
MOVE.L TXTUNF,A3 compute new end
MOVE.L A3,A6
ADD.L D0,A3
MOVE.L StrArea,D0 see if there's enough room
CMP.L A3,D0
BLS 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 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 MVUP do it
BRA ST3 go back and get another line
 
ClearStringArea:
move.l VARBGN,d0
SUB.L #STRAREASIZE,D0
MOVE.L D0,StrArea
MOVE.L D0,LastStr
move.l StrArea,a0
clr.l (a0)+
clr.l (a0)+
rts
 
ClearStringStack:
moveq #7,d0
move.l STRSTK,a1
.0001
clr.l (a1)+ ; clear the string stack
dbra d0,.0001
move.l a1,StrSp ; set string stack stack pointer
rts
 
even
 
*******************************************************************
*
* *** 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 addresses that correspond 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 '<CO',('M'+$80)
DC.B '<CO',('N'+$80)
DC.B '>CO',('M'+$80)
DC.B '>CO',('N'+$80)
DC.B '<>CO',('M'+$80)
DC.B '<>CO',('N'+$80)
DC.B 'LIS',('T'+$80) Direct commands
DC.B 'LOA',('D'+$80)
DC.B 'NE',('W'+$80)
DC.B 'RU',('N'+$80)
DC.B 'SAV',('E'+$80)
DC.B 'CL',('S'+$80)
TAB2
DC.B 'NEX',('T'+$80) Direct / statement
DC.B 'LE',('T'+$80)
DC.B 'I',('F'+$80)
DC.B 'GOT',('O'+$80)
DC.B 'GOSU',('B'+$80)
DC.B 'RETUR',('N'+$80)
DC.B 'RE',('M'+$80)
DC.B 'FO',('R'+$80)
DC.B 'INPU',('T'+$80)
DC.B 'PRIN',('T'+$80)
DC.B 'POK',('E'+$80)
DC.B 'STO',('P'+$80)
DC.B 'BY',('E'+$80)
DC.B 'CAL',('L'+$80)
DC.B 'ONIR',('Q'+$80)
DC.B 0
TAB4
DC.B 'PEE',('K'+$80) Functions
DC.B 'RN',('D'+$80)
DC.B 'AB',('S'+$80)
DC.B 'SIZ',('E'+$80)
DC.B 'TIC',('K'+$80)
DC.B 'COREN',('O'+$80)
DC.B 'LEFT',('$'+$80)
DC.B 'RIGHT',('$'+$80)
DC.B 'MID',('$'+$80)
DC.B 0
TAB5
DC.B 'T',('O'+$80) "TO" in "FOR"
DC.B 0
TAB6
DC.B 'STE',('P'+$80) "STEP" in "FOR"
DC.B 0
TAB8
DC.B '>',('='+$80) Relational operators
DC.B '<',('>'+$80)
DC.B ('>'+$80)
DC.B ('='+$80)
DC.B '<',('='+$80)
DC.B ('<'+$80)
DC.B 0
DC.B 0 <- for aligning on a word boundary
TAB9
DC.B 'AN',('D'+$80)
DC.B 0
TAB10
DC.B 'O',('R'+$80)
DC.B 0
DC.B 0
 
; Execution address tables:
align 2
TAB1_1
DC.L INCOM
DC.L INCON
DC.L OUTCOM
DC.L OUTCON
DC.L IOCOM
DC.L IOCON
DC.L LIST Direct commands
DC.L LOAD
DC.L NEW
DC.L RUN
DC.L SAVE
DC.L CLS
TAB2_1
DC.L NEXT Direct / statement
DC.L LET
DC.L IF
DC.L GOTO
DC.L GOSUB
DC.L RETURN
DC.L REM
DC.L FOR
DC.L INPUT
DC.L PRINT
DC.L POKE
DC.L STOP
DC.L GOBYE
DC.L CALL
DC.L ONIRQ
DC.L DEFLT
TAB4_1
DC.L PEEK ; Functions
DC.L RND
DC.L ABS
DC.L SIZE
DC.L TICK
DC.L CORENO
DC.L LEFT
DC.L RIGHT
DC.L MID
DC.L XP40
TAB5_1
DC.L FR1 ; "TO" in "FOR"
DC.L QWHAT
TAB6_1
DC.L FR2 ; "STEP" in "FOR"
DC.L FR3
TAB8_1
DC.L XP11 >= Relational operators
DC.L XP12 <>
DC.L XP13 >
DC.L XP15 =
DC.L XP14 <=
DC.L XP16 <
DC.L XP17
TAB9_1
DC.L XP_AND
DC.L XP_ANDX
TAB10_1
DC.L XP_OR
DC.L XP_ORX
 
even
DIRECT
move.w #1,DIRFLG
LEA TAB1,A1
LEA TAB1_1,A2
EXEC
bsr 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
MOVE.B (A1),D1 ; get the table character
BNE EXNGO ; If end of table,
MOVE.L A3,A0 ; restore the text 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 #4,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 end 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
MOVE.L (A2),A3 ; execute the appropriate routine
JMP (A3)
 
*******************************************************************
* Console redirection
* <COM will redirect input to the COM port
* >COM will redirect output to the COM port
* <CON will redirect input to the console
* >CON will redirect output to the console
* <>COM will redirect input and output to the COM port
* <>CON will redirect input and output to the console
*******************************************************************
INCON
move.l #INC1,INPPTR
bra FINISH
INCOM
move.l #AUXIN,INPPTR
bra FINISH
IOCOM
move.l #AUXIN,INPPTR
OUTCOM
move.l #AUXOUT,OUTPTR
bra FINISH
IOCON
move.l #INC1,INPPTR
OUTCON
move.l #OUTC1,OUTPTR
bra FINISH
 
*******************************************************************
*
* 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 program.
*
* 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 ENDCHK
MOVE.L TXTBGN,TXTUNF set the end pointer
bsr ClearStringArea
bsr ClearStringStack
 
STOP
bsr ENDCHK
BRA WSTART
 
RUN
clr.w DIRFLG
bsr ENDCHK
MOVE.L TXTBGN,A0 set pointer to beginning
MOVE.L A0,CURRNT
 
RUNNXL
TST.L CURRNT ; executing a program?
beq WSTART ; if not, we've finished a direct stat.
tst.l IRQROUT ; are we handling IRQ's ?
beq RUN1
tst.b IRQFlag ; was there an IRQ ?
beq RUN1
clr.b IRQFlag
 
; same code as GOSUB
sub.l #128,sp ; allocate storage for local variables
move.l sp,STKFP
bsr PUSHA ; save the current 'FOR' parameters
MOVE.L A0,-(SP) ; save text pointer
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
 
move.l IRQROUT,a1
bra RUNTSL
RUN1
CLR.L D1 ; else find the next line number
MOVE.L A0,A1
bsr FNDLNP
BCS WSTART ; if we've fallen off 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 CHKIO see if a control-C was pressed
LEA TAB2,A1 find command in TAB2
LEA TAB2_1,A2
BRA EXEC and execute it
 
GOTO
bsr INT_EXPR ; evaluate the following expression
bsr ENDCHK ; must find end of line
move.l d0,d1
bsr FNDLN ; find the target line
bne QHOW ; no such line no.
bra RUNTSL ; go do it
 
;******************************************************************
; ONIRQ <line number>
; ONIRQ sets up an interrupt handler which acts like a specialized
; subroutine call. ONIRQ is coded like a GOTO that never executes.
;******************************************************************
 
ONIRQ:
bsr INT_EXPR ; evaluate the following expression
bsr ENDCHK ; must find end of line
move.l d0,d1
bsr FNDLN ; find the target line
bne ONIRQ1
clr.l IRQROUT
bra FINISH
ONIRQ1:
move.l a1,IRQROUT
jmp FINISH
 
 
WAITIRQ:
jsr CHKIO ; see if a control-C was pressed
tst.b IRQFlag
beq WAITIRQ
jmp FINISH
 
*******************************************************************
*
* *** LIST *** PRINT ***
*
* LIST has two forms:
* 'LIST<CR>' lists all saved lines
* 'LIST #<CR>' starts listing at the 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 a separated by commas.
*
* A format is a pound 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 a <LF>
*
* A <CR LF> is generated after the entire list has been printed
* or if the list is empty. If the list ends with a semicolon,
* however, no <CR LF> is generated.
*
 
LIST
bsr TSTNUM see if there's a line no.
bsr ENDCHK if not, we get a zero
bsr FNDLN find this or next line
LS1
BCS FINISH warm start if we passed the end
bsr PRTLN print the line
bsr CHKIO check for listing halt request
BEQ LS3
CMP.B #CTRLS,D0 pause the listing?
BNE LS3
LS2
bsr CHKIO if so, wait for another keypress
BEQ LS2
LS3
bsr FNDLNP find the next line
BRA LS1
 
PRINT
MOVE.L #11,D4 D4 = number of print spaces
bsr TSTC if null list and ":"
DC.B ':',PR2-*
bsr CRLF give CR-LF and continue
BRA RUNSML execution on the same line
PR2
bsr TSTC if null list and <CR>
DC.B CR,PR0-*
bsr CRLF also give CR-LF and
BRA RUNNXL execute the next line
PR0
bsr TSTC ; else is it a format?
dc.b '#',PR1-*
bsr INT_EXPR ; yes, evaluate expression
move.l d0,d4 ; and save it as print width
bra PR3 ; look for more to print
PR1
bsr TSTC ; is character expression? (MRL)
dc.b '$',PR8-*
bsr INT_EXPR ; yep. Evaluate expression (MRL)
bsr GOOUT ; print low byte (MRL)
bra PR3 ; look for more. (MRL)
PR3
bsr TSTC ; if ",", go find next
dc.b ',',PR6-*
bsr FIN ; in the list.
BRA PR0
PR6
bsr CRLF ; list ends here
BRA FINISH
PR8
move.l d4,-(SP) ; save the width value
bsr EXPR ; evaluate the expression
move.l (sp)+,d4 ; restore the width
cmpi.l #DT_STRING,d0 ; is it a string?
beq PR9
fmove fp0,fp1
move.l #35,d4
bsr PRTNUM ; print its value
bra PR3 ; more to print?
; Print a string
PR9
fmove.x fp0,_fpWork
move.w _fpWork,d1
move.l _fpWork+4,a1
bsr PRTSTR2
bra PR3
 
FINISH
bsr FIN ; Check end of command
BRA 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 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 zero (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:
sub.l #128,sp ; allocate storage for local variables
move.l sp,STKFP
bsr PUSHA ; save the current 'FOR' parameters
bsr INT_EXPR ; get line number
MOVE.L A0,-(SP) save text pointer
move.l d0,d1
bsr FNDLN find the target line
BNE 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 ENDCHK there should be just a <CR>
MOVE.L STKGOS,D1 get old stack pointer
BEQ 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 POPA and the old 'FOR' parameters
move.l STKFP,sp
add.l #128,sp
BRA FINISH and we are back home
 
*******************************************************************
*
* *** 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 a
* STEP of positive 1. The interpreter will find the variable 'var'
* and set its value to the current value of 'exp1'. It also
* evaluates 'exp2' and 'exp1' and saves all these together with
* the text pointer, etc. in the 'FOR' save area, which consisits 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 PUSHA save the old 'FOR' save area
bsr 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 NUM_EXPR evaluate the limit
FMOVE.X FP0,LOPLMT save that
LEA TAB6,A1 use 'EXEC' to look for the
LEA TAB6_1,A2 word 'STEP'
BRA EXEC
FR2
bsr NUM_EXPR found it, get the step value
BRA FR4
FR3
FMOVE.B #1,FP0 ; not found, step defaults to 1
FR4
FMOVE.X FP0,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 #40,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 40,A3
ADD.L A1,A3
bsr MVDOWN
MOVE.L A3,SP set the SP 5 long words up
FR8
BRA FINISH and continue execution
 
NEXT
bsr TSTV get address of variable
BCS QWHAT if no variable, say "What?"
MOVE.L D0,A1 save variable's address
NX0
MOVE.L LOPVAR,D0 If 'LOPVAR' is zero, we never...
BEQ QWHAT had a FOR loop, so say "What?"
CMP.L D0,A1 else we check them
BEQ NX3 OK, they agree
bsr POPA nope, let's see the next frame
BRA NX0
NX3
FMOVE.X (A1),FP0 get control variable's value
FADD LOPINC,FP0 add in loop increment
; BVS QHOW say "How?" for 32-bit overflow
FMOVE.X FP0,(A1) save control variable's new value
FMOVE.X LOPLMT,FP1 get loop's limit value
FTST LOPINC
FBGE NX1 ; branch if loop increment is positive
FMOVE.X FP0,-(a7) ; exchange FP0,FP1
FMOVE.X FP1,FP0
FMOVE.X (a7)+,FP1
NX1
FCMP FP0,FP1 ; test against limit
FBLT NX2 ; branch if outside limit
MOVE.L LOPLN,CURRNT Within limit, go back to the...
MOVE.L LOPPT,A0 saved 'CURRNT' and text pointer.
BRA FINISH
NX2
bsr POPA purge this loop
BRA FINISH
 
*******************************************************************
*
* *** REM *** IF *** INPUT *** LET (& DEFLT) ***
*
* 'REM' can be followed by anything and is ignored by the
* interpreter.
*
* 'IF' is followed by an expression, as a condition and one or
* more commands (including other 'IF's) separated by colons.
* Note that the word 'THEN' is not used. The interpreter evaluates
* the expression. If it is non-zero, execution continues. If it
* is zero, the commands that follow are ignored and execution
* continues on the next line.
*
* 'INPUT' 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 underline (back 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 the interpreter waits for
* an expression to be typed in. The variable is then set to the
* value of this expression. If the variable is preceeded by a
* string (again in single or double quotes), the string will be
* displayed followed by a colon. The interpreter the waits for an
* expression to be entered and sets the variable equal to the
* expression's value. If the input expression is invalid, the
* interpreter will print "What?", "How?", or "Sorry" and reprint
* the prompt and redo the input. The execution will not terminate
* unless you press control-C. This is handled in 'INPERR'.
*
* 'LET' is followed by a list of items separated by commas.
* Each item consists of a variable, an equals sign, and an
* expression. The interpreter evaluates the expression and sets
* the variable to that value. The interpreter will also handle
* 'LET' commands without the word 'LET'. This is done by 'DEFLT'.
 
REM
BRA IF2 skip the rest of the line
 
IF
bsr INT_EXPR evaluate the expression
IF1
TST.L d0 is it zero?
BNE RUNSML if not, continue
IF2
MOVE.L A0,A1
CLR.L D1
bsr FNDSKP if so, skip the rest of the line
BCC RUNTSL and run the next line
BRA WSTART if no next line, do a warm start
 
INPERR MOVE.L STKINP,SP restore the old stack pointer
MOVE.L (SP)+,CURRNT and old 'CURRNT'
ADDQ.L #4,SP
MOVE.L (SP)+,A0 and old text pointer
 
INPUT
MOVE.L A0,-(SP) save in case of error
bsr EXPR
cmpi.b #DT_STRING,d0
bne IP6
fmove.x fp0,_fpWork
move.w _fpWork,d1
move.l _fpWork+4,a1
bsr PRTSTR2
; bsr QTSTG is next item a string?
; BRA.S IP2 nope
IP7
bsr TSTV yes, but is it followed by a variable?
BCS IP4 if not, branch
MOVE.L D0,A2 put away the variable's address
BRA IP3 if so, input to variable
IP6
move.l (sp),a0 ; restore text pointer
bra IP7
IP2
MOVE.L A0,-(SP) save for 'PRTSTG'
bsr TSTV must be a variable now
BCS QWHAT "What?" it isn't?
MOVE.L D0,A2 put away the variable's address
MOVE.B (A0),D2 get ready for 'PRTSTG'
CLR.B D0
MOVE.B D0,(A0)
MOVE.L (SP)+,A1
bsr PRTSTG print string as prompt
MOVE.B D2,(A0) restore text
IP3
MOVE.L A0,-(SP) save in case of error
MOVE.L CURRNT,-(SP) also save 'CURRNT'
MOVE.L #-1,CURRNT flag that we are in INPUT
MOVE.L SP,STKINP save the stack pointer too
MOVE.L A2,-(SP) save the variable address
MOVE.B #':',D0 print a colon first
bsr GETLN then get an input line
LEA BUFFER,A0 point to the buffer
bsr EXPR evaluate the input
MOVE.L (SP)+,A2 restore the variable address
move.l d0,(a2) ; save data type
FMOVE.X FP0,4(A2) ; save value in variable
MOVE.L (SP)+,CURRNT restore old 'CURRNT'
MOVE.L (SP)+,A0 and the old text pointer
IP4
ADDQ.L #4,SP clean up the stack
bsr TSTC is the next thing a comma?
DC.B ',',IP5-*
BRA INPUT yes, more items
IP5
BRA FINISH
 
DEFLT
CMP.B #CR,(A0) ; empty line is OK
BEQ FINISH ; else it is 'LET'
 
LET
bsr SETVAL ; do the assignment
bsr TSTC ; check for more 'LET' items
DC.B ',',LT1-*
BRA LET
LT1
BRA FINISH ; until we are finished.
 
 
*******************************************************************
*
* *** LOAD *** & SAVE ***
*
* These two commands transfer a program to/from an auxiliary
* device such as a cassette, another computer, etc. The program
* is converted to an easily-stored format: each line starts with
* a colon, the line no. as 4 hex digits, and the rest of the line.
* At the end, a line starting with an '@' sign is sent. This
* format can be read back with a minimum of processing time by
* the 68000.
*
LOAD
MOVE.L TXTBGN,A0 set pointer to start of prog. area
MOVE.B #CR,D0 For a CP/M host, tell it we're ready...
BSR GOAUXO by sending a CR to finish PIP command.
LOD1
BSR GOAUXI look for start of line
BEQ LOD1
CMP.B #'@',D0 end of program?
BEQ LODEND
CMP.B #':',D0 if not, is it start of line?
BNE LOD1 if not, wait for it
BSR GBYTE get first byte of line no.
MOVE.B D1,(A0)+ store it
BSR GBYTE get 2nd bye of line no.
MOVE.B D1,(A0)+ store that, too
LOD2
BSR GOAUXI get another text char.
BEQ LOD2
MOVE.B D0,(A0)+ store it
CMP.B #CR,D0 is it the end of the line?
BNE LOD2 if not, go back for more
BRA LOD1 if so, start a new line
LODEND
MOVE.L A0,TXTUNF set end-of program pointer
BRA WSTART back to direct mode
 
GBYTE
MOVEQ #1,D2 get two hex characters from auxiliary
CLR.L D1 and store them as a byte in D1
GBYTE1
BSR GOAUXI get a char.
BEQ GBYTE1
CMP.B #'A',D0
BCS GBYTE2
SUBQ.B #7,D0 if greater than 9, adjust
GBYTE2
AND.B #$F,D0 strip ASCII
LSL.B #4,D1 put nybble into the result
OR.B D0,D1
DBRA D2,GBYTE1 get another char.
RTS
 
SAVE
MOVE.L TXTBGN,A0 set pointer to start of prog. area
MOVE.L TXTUNF,A1 set pointer to end of prog. area
SAVE1
MOVE.B #CR,D0 send out a CR & LF (CP/M likes this)
BSR GOAUXO
MOVE.B #LF,D0
BSR GOAUXO
CMP.L A0,A1 are we finished?
BLS SAVEND
MOVE.B #':',D0 if not, start a line
BSR GOAUXO
MOVE.B (A0)+,D1 send first half of line no.
BSR PBYTE
MOVE.B (A0)+,D1 and send 2nd half
BSR PBYTE
SAVE2
MOVE.B (A0)+,D0 get a text char.
CMP.B #CR,D0 is it the end of the line?
BEQ SAVE1 if so, send CR & LF and start new line
BSR GOAUXO send it out
BRA SAVE2 go back for more text
SAVEND
MOVE.B #'@',D0 send end-of-program indicator
BSR GOAUXO
MOVE.B #CR,D0 followed by a CR & LF
BSR GOAUXO
MOVE.B #LF,D0
BSR GOAUXO
MOVE.B #$1A,D0 and a control-Z to end the CP/M file
BSR GOAUXO
BRA WSTART then go do a warm start
 
PBYTE MOVEQ #1,D2 send two hex characters from D1's low byte
PBYTE1 ROL.B #4,D1 get the next nybble
MOVE.B D1,D0
AND.B #$F,D0 strip off garbage
ADD.B #'0',D0 make it into ASCII
CMP.B #'9',D0
BLS PBYTE2
ADDQ.B #7,D0 adjust if greater than 9
PBYTE2 BSR GOAUXO send it out
DBRA D2,PBYTE1 then send the next nybble
RTS
 
*******************************************************************
*
* *** POKE *** & CALL ***
*
* 'POKE expr1,expr2' stores the byte from 'expr2' into the memory
* address specified by 'expr1'.
*
* 'CALL expr' jumps to the machine language subroutine whose
* starting address is specified by 'expr'. The subroutine can use
* all registers but must leave the stack the way it found it.
* The subroutine returns to the interpreter by executing an RTS.
*
POKE
move.b #'B',d7
move.b (a0),d1
cmpi.b #'.',d1
bne .0001
addq #1,a0
move.b (a0),d1
cmpi.b #'B',d1
beq .0002
cmpi.b #'W',d1
beq .0002
cmpi.b #'L',d1
beq .0002
cmpi.b #'F',d1
bne PKER
.0002
addq #1,a0
move.b d1,d7
.0001
BSR INT_EXPR get the memory address
bsr TSTC it must be followed by a comma
DC.B ',',PKER-*
move.l d0,-(sp) ; save the address
BSR NUM_EXPR ; get the value to be POKE'd
move.l (sp)+,a1 ; get the address back
CMPI.B #'B',D7
BNE .0003
FMOVE.B FP0,(A1) store the byte in memory
BRA FINISH
.0003
CMPI.B #'W',d7
BNE .0004
FMOVE.W FP0,(A1)
BRA FINISH
.0004
CMPI.B #'L',D7
BNE .0005
FMOVE.L FP0,(A1)
BRA FINISH
.0005
CMPI.B #'F',D7
BNE .0006
FMOVE.X FP0,(A1)
BRA FINISH
.0006
PKER
BRA QWHAT if no comma, say "What?"
 
CALL
BSR INT_EXPR ; get the subroutine's address
TST.l d0 ; make sure we got a valid address
BEQ QHOW ; if not, say "How?"
MOVE.L A0,-(SP) ; save the text pointer
MOVE.L D0,A1
JSR (A1) ; jump to the subroutine
MOVE.L (SP)+,A0 ; restore the text pointer
BRA 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 result
* of these operations is 1 if true and 0 if 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 the variable '@' can have an <EXPR>
* as an index, functions can have an <EXPR> as arguments, and
* <EXPR4> can be an <EXPR> in parenthesis.
 
;-------------------------------------------------------------------------------
; Push string whose string descriptor is in fp0 on string stack.
;-------------------------------------------------------------------------------
 
PushString:
move.l a1,-(sp)
move.l StrSp,a1 ; get string stack pointer
cmpa.l STRSTK,a1 ; ensure not too deeep
bls QHOW
subq.l #4,a1 ; decrement sp
move.l a1,StrSp
fmove.x fp0,_fpWork ; save descriptor in temp area
move.l _fpWork+4,(a1) ; copy string pointer to stack
move.l (sp)+,a1
rts
 
;-------------------------------------------------------------------------------
; Pop string from string stack.
;-------------------------------------------------------------------------------
 
PopString:
move.l a1,-(sp)
move.l StrSp,a1 ; remove string from string stack
clr.l (a1) ; clear the string pointer
add.l #4,StrSp
move.l (sp)+,a1
rts
;-------------------------------------------------------------------------------
; Push a value on the stack.
;-------------------------------------------------------------------------------
 
XP_PUSH:
move.l (sp)+,a1 ; a1 = return address
move.l _canary,-(sp) ; push the canary
sub.l #16,sp ; allocate for value
move.l d0,(sp) ; push data type
fmove.x fp0,4(sp) ; and value
cmpi.l #DT_STRING,d0 ; if it is a string
bne .0001
bsr PushString ; push string on string stack
.0001
jmp (a1)
 
;-------------------------------------------------------------------------------
; Pop value from stack into first operand.
;-------------------------------------------------------------------------------
XP_POP:
move.l (sp)+,a1 ; get return address
move.l (sp),d0 ; pop data type
fmove.x 4(sp),fp0 ; and data element
add.l #16,SP
cchk (SP) ; check the canary
add.l #4,SP ; pop canary
cmpi.l #DT_STRING,d0
bne .0001 ; if a string
bsr PopString ; pop string from string stack
.0001
jmp (a1)
 
;-------------------------------------------------------------------------------
; Pop value from stack into second operand.
;-------------------------------------------------------------------------------
 
XP_POP1:
move.l (sp)+,a1 ; get return address
move.l (sp),d1 ; pop data type
fmove.x 4(sp),fp1 ; and data element
add.l #16,sp
cchk (sp) ; check the canary
add.l #4,sp ; pop canary
cmpi.l #DT_STRING,d1
bne .0001 ; if a string
bsr PopString ; pop string from string stack
.0001
jmp (a1)
 
;-------------------------------------------------------------------------------
; Get and expression and make sure it is numeric.
;-------------------------------------------------------------------------------
 
NUM_EXPR:
bsr EXPR
cmpi.l #DT_NUMERIC,d0
bne ETYPE
rts
 
;-------------------------------------------------------------------------------
; Get and expression and make sure it is numeric. Convert to integer.
;-------------------------------------------------------------------------------
 
INT_EXPR:
bsr EXPR
cmpi.l #DT_NUMERIC,d0
bne ETYPE
fmove.l fp0,d0
rts
 
;-------------------------------------------------------------------------------
; The top level of the expression parser.
; Get an expression, string or numeric.
;
; EXEC will smash a lot of regs, so push the current expression value before
; doing EXEC
;-------------------------------------------------------------------------------
 
EXPR
EXPR_OR
BSR EXPR_AND
BSR XP_PUSH
LEA TAB10,A1
LEA TAB10_1,A2
BRA EXEC
;-------------------------------------------------------------------------------
; Boolean 'Or' level
;-------------------------------------------------------------------------------
 
XP_OR
BSR EXPR_AND
bsr XP_POP1
bsr CheckNumeric
FMOVE.L FP1,D1
FMOVE.L FP0,D0
OR.L D1,D0
FMOVE.L D0,FP0
rts
;-------------------------------------------------------------------------------
; Boolean 'And' level
;-------------------------------------------------------------------------------
 
EXPR_AND
bsr EXPR_REL
bsr XP_PUSH
LEA TAB9,A1
LEA TAB9_1,A2
BRA EXEC
 
XP_AND
BSR EXPR_REL
bsr XP_POP1
bsr CheckNumeric
FMOVE.L FP1,D1
FMOVE.L FP0,D0
AND.L D1,D0
FMOVE.L D0,FP0
RTS
XP_ANDX
XP_ORX
bsr XP_POP
rts
 
;-------------------------------------------------------------------------------
; Check that two numeric values are being used.
;-------------------------------------------------------------------------------
 
CheckNumeric:
CMPI.B #DT_NUMERIC,D1
BNE ETYPE
CMPI.B #DT_NUMERIC,D0
BNE ETYPE
RTS
 
;-------------------------------------------------------------------------------
; Relational operator level, <,<=,>=,>,=,<>
;-------------------------------------------------------------------------------
 
EXPR_REL
bsr EXPR2
bsr XP_PUSH
LEA TAB8,A1 ; look up a relational operator
LEA TAB8_1,A2
bra EXEC go do it
 
XP11
bsr XP_POP
BSR XP18 is it ">="?
FBLT XPRT0 no, return D0=0
BRA XPRT1 else return D0=1
 
XP12
bsr XP_POP
BSR XP18 is it "<>"?
FBEQ XPRT0 no, return D0=0
BRA XPRT1 else return D0=1
 
XP13
bsr XP_POP
BSR XP18 is it ">"?
FBLE XPRT0 no, return D0=0
BRA XPRT1 else return D0=1
 
XP14
bsr XP_POP
BSR XP18 is it "<="?
FBGT XPRT0 no, return D0=0
BRA XPRT1 else return D0=1
 
XP15
bsr XP_POP
BSR XP18 is it "="?
FBNE XPRT0 if not, return D0=0
BRA XPRT1 else return D0=1
XP15RT
RTS
 
XP16
bsr XP_POP
BSR XP18 is it "<"?
FBGE XPRT0 if not, return D0=0
BRA XPRT1 else return D0=1
RTS
 
XPRT0
FMOVE.B #0,FP0 ; return fp0 = 0 (false)
RTS
 
XPRT1
FMOVE.B #1,FP0 ; return fp0 = 1 (true)
RTS
 
XP17 ; it's not a rel. operator
bsr XP_POP ; return FP0=<EXPR2>
rts
 
XP18
bsr XP_PUSH
bsr EXPR2 ; do second <EXPR2>
bsr XP_POP1
bsr CheckNumeric
fcmp fp0,fp1 ; compare with the first result
RTS ; return the result
 
;-------------------------------------------------------------------------------
; Add/Subtract operator level, +,-
;-------------------------------------------------------------------------------
 
EXPR2
bsr TSTC ; negative sign?
DC.B '-',XP21-*
FMOVE.B #0,FP0
BRA XP26
XP21
bsr TSTC ; positive sign? ignore it
DC.B '+',XP22-*
XP22
BSR EXPR3 ; first <EXPR3>
XP23
bsr TSTC ; add?
DC.B '+',XP25-*
bsr XP_PUSH
BSR EXPR3 ; get the second <EXPR3>
XP24
bsr XP_POP1
CMP.B #DT_NUMERIC,d0
BNE .notNum
CMP.B #DT_NUMERIC,d1
BNE .notNum
FADD FP1,FP0 ; add it to the first <EXPR3>
; FBVS QHOW branch if there's an overflow
BRA XP23 else go back for more operations
.notNum
CMP.L #DT_STRING,d0
bne ETYPE
CMP.L #DT_STRING,d1
bne ETYPE
bsr ConcatString
rts
 
XP25
bsr TSTC ; subtract?
dc.b '-',XP27-*
XP26
bsr XP_PUSH
BSR EXPR3 ; get second <EXPR3>
cmpi.b #DT_NUMERIC,d0
bne ETYPE
FNEG FP0 ; change its sign
JMP XP24 ; and do an addition
 
XP27
rts
 
;-------------------------------------------------------------------------------
; Concatonate strings, for the '+' operator.
;
; Parameters:
; fp0 = holds string descriptor for second string
; fp1 = holds string descriptor for first string
; Returns:
; fp0 = string descriptor for combined strings
;-------------------------------------------------------------------------------
 
ConcatString:
fmove.x fp1,_fpWork ; save first string descriptor to memory
fmove.x fp0,_fpWork+16; save second string descriptor to memory
move.w _fpWork,d2 ; d2 = length of first string
add.w _fpWork+16,d2 ; add length of second string
ext.l d2 ; make d2 a long word
bsr AllocateString ; allocate
move.l a1,a4 ; a4 = allocated string, saved for later
move.l a1,a2 ; a2 = allocated string
move.w d2,(a2) ; save length of new string (a2)
addq.l #2,a2 ; a2 = pointer to new string text area
move.l _fpWork+4,a1 ; a1 = pointer to string text of first string
move.l a1,a3 ; compute pointer to end of first string
move.w _fpWork,d3 ; d3 = length of first string
ext.l d3
add.l d3,a3 ; add length of first string
bsr MVUP ; move from A1 to A2 until A1=A3
move.l _fpWork+20,a1 ; a1 = pointer to second string text
move.l a1,a3
move.w _fpWork+16,d3 ; d3 = length of second string
ext.l d3
add.l d3,a3 ; a3 points to end of second string
bsr MVUP ; concatonate on second string
move.w _fpWork+16,d2 ; d2 = length of string 2
add.w _fpWork,d2 ; d2 = total string length
move.w d2,_fpWork ; save total string length in fp work
addq.l #2,a4 ; a4 points to text area of allocated string
move.l a4,_fpWork+4 ; save pointer in fp work area
moveq #DT_STRING,d0 ; set return data type = string
fmove.x _fpWork,fp0 ; fp0 = string descriptor
rts
 
;-------------------------------------------------------------------------------
; Multiply / Divide operator level, *,/,%
;-------------------------------------------------------------------------------
 
EXPR3
bsr EXPR4 ; get first <EXPR4>
XP31
bsr XP_PUSH
bsr TSTC ; multiply?
dc.b '*',XP34-*
bsr EXPR4 ; get second <EXPR4>
bsr XP_POP1
bsr CheckNumeric
fmul fp1,fp0 ; multiply the two
bra XP31 ; then look for more terms
XP34
bsr TSTC ; divide?
dc.b '/',XP35-*
bsr EXPR4 ; get second <EXPR4>
bsr XP_POP1
bsr CheckNumeric
fdiv fp1,fp0 ; do the division
bra XP31 ; go back for any more terms
XP35
bsr TSTC
dc.b '%',XP36-*
bsr EXPR4 ; get second <EXPR4>
bsr XP_POP1
bsr CheckNumeric
FDIV FP1,FP0 ; do the division
BRA XP31 ; go back for any more terms
XP36
bsr XP_POP
rts
 
;-------------------------------------------------------------------------------
; Lowest Level of expression evaluation.
; Check for
; a function or
; a variable or
; a number or
; a string or
; ( expr )
;-------------------------------------------------------------------------------
 
EXPR4
LEA TAB4,A1 ; find possible function
LEA TAB4_1,A2
BRA EXEC
XP40
bsr TSTV ; nope, not a function
bcs XP41 ; nor a variable
move.l d0,a1 ; a1 = variable address
move.l (a1),d0 ; return type in d0
fmove.x 4(a1),fp0 ; if a variable, return its value in fp0
EXP4RT
rts
XP41
bsr TSTNUM ; or is it a number?
fmove fp1,fp0
cmpi.l #DT_NUMERIC,d0
beq EXP4RT ; if so, return it in FP0
XPSTNG
bsr TSTC ; is it a string constant?
dc.b '"',XP44-*
move.b #'"',d3
XP45
move.l a0,a1 ; record start of string in a1
move.l #511,d2 ; max 512 characters
.0003
move.b (a0)+,d0 ; get a character
beq .0001 ; should not be a NULL
cmpi.b #CR,d0 ; CR means the end of line was hit without a close quote
beq .0001
cmp.b d3,d0 ; close quote?
beq .0002
dbra d2,.0003 ; no close quote, go back for next char
.0001
bra QHOW
.0002
move.l a0,d0 ; d0 = end of string pointer
sub.l a1,d0 ; compute string length + 1
subq #1,d0 ; subtract out closing quote
move.l d0,d2 ; d2 = string length
move.l a1,a3 ; a3 = pointer to string text
bsr AllocateString
addq.l #2,a1 ; point to text area
move.l a1,a2 ; a2 points to new text area
move.l a1,a4 ; save a1 for later
move.l a3,a1 ; a1 = pointer to string in program
move.w d2,-2(a2) ; copy length into place
add.l d2,a3 ; a3 points to end of string
bsr MVUP ; move from A1 to A2 until A1=A3
move.w d2,_fpWork ; copy length into place
move.l a4,_fpWork+4 ; copy pointer to text into place
fmove.x _fpWork,fp0 ; put string descriptor into fp0
moveq #DT_STRING,d0 ; return string data type
rts
XP44
bsr TSTC ; alternate string constant?
dc.b '''',PARN-*
move.b #'''',d3
bra XP45
PARN
bsr TSTC ; else look for ( EXPR )
DC.B '(',XP43-*
BSR EXPR
bsr TSTC
DC.B ')',XP43-*
XP42
rts
XP43
bra QWHAT ; else say "What?"
 
;-------------------------------------------------------------------------------
; Allocate storage for a string variable.
;
; Parameters:
; d2 = number of bytes needed
; Returns:
; a1 = pointer to string text area
;-------------------------------------------------------------------------------
 
AllocateString:
movem.l d2-d4/a2-a5,-(sp)
move.l VARBGN,d4
move.l LastStr,a1 ; a1 = last string
move.w (a1),d3 ; d3 = length of last string (0)
ext.l d3
sub.l d3,d4 ; subtract off length
subq.l #2,d4 ; size of length field
sub.l a1,d4 ; and start position
cmp.l d4,d2 ; is there enough room?
bhi .needMoreRoom
.0001
move.l LastStr,a1
move.l a1,a3
addq.l #2,a1 ; point a1 to text part of string
move.w d2,(a3)
add.l d2,a3
addq.l #5,a3
move.l a3,d3
andi.l #$FFFFFFFE,d3
move.l d3,a3
move.l a3,LastStr ; set new last str position
clr.w (a3) ; set zero length
movem.l (sp)+,d2-d4/a2-a5
rts
.needMoreRoom
bsr GarbageCollectStrings
move.l VARBGN,d4 ; d4 = start of variables
move.l LastStr,a1 ; a1 = pointer to last string
move.w (a1),d3 ; d3 = length of last string (likely 0)
ext.l d3
add.l a1,d3 ; d3 = pointer past end of last string
addq.l #3,d3 ; 1+2 for length and rounding
andi.l #$FFFFFFFE,d3 ; make even address
sub.l d3,d4 ; free = VARBGN - LastStr+length of (LastStr)
cmp.l d4,d2 ; request < free?
blo .0001
lea NOSTRING,a6
bra ERROR
;-------------------------------------------------------------------------------
; Garbage collect strings. This copies all strings in use to the lower end of
; the string area and adjusts the string pointers in variables and on the
; string stack to point to the new location.
;-------------------------------------------------------------------------------
 
GarbageCollectStrings:
move.l StrArea,a1 ; source area pointer
move.l StrArea,a2 ; target area pointer
; move.l VARBGN,a6 ; a6 = top of string area
move.l LastStr,a5
.0001
bsr StringInVar ; check if the string is used by a variable
bcs .moveString
bsr StringOnStack ; check if string is on string expression stack
bcc .nextString ; if not on stack or in a var then move to next string
; The string is in use, copy to active string area
.moveString:
bsr UpdateStringPointers ; update pointer to string on stack or in variable
move.l a1,d3 ; d3 = pointer to string
add.w (a1),d3 ; add string length to pointer
addq.l #3,d3 ; size +1+2 for length word
andi.l #$FFFFFFFE,d3 ; round address to even word
move.l d3,a3
bsr MVUP ; move from A1 to A2 until A1=A3
.0003
move.l a2,d3
andi.l #$FFFFFFFE,d3 ; make sure at even long word address
move.l d3,a2
.0005
move.l a3,a1 ; point to next string in area
cmp.l a5,a3 ; is it the last string?
bls .0001
move.l a2,LastStr ; update last string pointer
rts
.nextString:
move.l a1,d3 ; d3 = string address
add.w (a1),d3 ; add length of string
addq.l #3,d3 ; plus 1+2 for rounding
andi.l #$FFFFFFFE,d3 ; round address to even word
move.l d3,a3
bra .0005
 
;-------------------------------------------------------------------------------
; Check if a variable is using a string
;
; Modifies:
; d2,d3,a4
; Parameters:
; a1 = pointer to string descriptor
; Returns:
; cf = 1 if string in use, 0 otherwise
;-------------------------------------------------------------------------------
 
StringInVar:
move.l VARBGN,a4
moveq #31,d3 ; 32 vars
.0002
cmp.l #DT_STRING,(a4) ; check data type = string
bne .0001
move.l 8(a4),d2 ; look a pointer match
subq.l #2,d2
cmp.l d2,a1 ;
bne .0001
ori #1,ccr ; set carry if in use
rts
.0001
addq.l #8,a4
addq.l #8,a4
dbra d3,.0002
; andi #$FE,ccr ; clear carry if not in use
; now check local vars
move.l STKFP,a4
moveq #7,d3
.0003
cmp.l #DT_STRING,(a4)
bne .0004
move.l 8(a4),d2
subq.l #2,d2
cmp.l d2,a1
bne .0004
ori #1,ccr
rts
.0004
addq.l #8,a4
addq.l #8,a4
dbra d3,.0003
andi #$FE,ccr
rts
 
;-------------------------------------------------------------------------------
; Check if the string is a temporary on stack
;
; Parameters:
; a1 = pointer to string
; Returns:
; a4 = stack entry
; cf = 1 if string in use, 0 otherwise
;-------------------------------------------------------------------------------
 
StringOnStack:
moveq #7,d3
move.l STRSTK,a4
.0002
cmp.l (a4)+,a1
beq .0001
dbra d3,.0002
andi #$FE,ccr
rts
.0001
ori #1,ccr
rts
;-------------------------------------------------------------------------------
; Update pointers to string to point to new area. All string areas must be
; completely checked because there may be more than one pointer to the string.
;
; Modifies:
; d2,d3,d4,a4
; Parameters:
; a1 = old pointer to string
; a2 = new pointer to string
;-------------------------------------------------------------------------------
 
UpdateStringPointers:
; check variable space
move.l VARBGN,a4
moveq #31,d3 ; 32 vars to check
.0002
cmp.l #DT_STRING,(a4) ; check the data type
bne .0001 ; not a string, go to next
move.l 8(a4),d2
subq.l #2,d2
cmp.l d2,a1 ; does pointer match old pointer?
bne .0001
move.l a2,8(a4) ; copy in new pointer
addi.l #2,8(a4) ; point to string text
.0001
addq.l #8,a4
addq.l #8,a4
dbra d3,.0002
 
; check local variable space
USP1:
move.l STKFP,a4
moveq #7,d3 ; 8 locals to check
.0002
cmp.l #DT_STRING,(a4) ; check data type
bne .0001
move.l 8(a4),d2
subq.l #2,d2
cmp.l d2,a1 ; does pointer match old pointer?
bne .0001
move.l a2,8(a4) ; copy in new pointer
addi.l #2,8(a4) ; point to string text
.0001
addq.l #8,a4
addq.l #8,a4
dbra d3,.0002
 
; check string stack
USP2:
move.l STRSTK,a4
moveq #7,d3 ; 8 entries on stack
.0002
cmp.l (a4),a1 ; does pointer match old pointer?
bne .0001
move.l a2,(a4) ; copy in new pointer
.0001
addq.l #4,a4
dbra d3,.0002
rts
;-------------------------------------------------------------------------------
; ===== Test for a valid variable name. Returns Carry=1 if not
; found, else returns Carry=0 and the address of the
; variable in D0.
 
TSTV:
bsr IGNBLK
CLR.L D0
MOVE.B (A0),D0 ; look at the program text
SUB.B #'@',D0
BCS TSTVRT ; C=1: not a variable
BNE TV1 ; branch if not "@" array
ADDQ #1,A0 ; If it is, it should be
BSR PARN ; followed by (EXPR) as its index.
ADD.L D0,D0
BCS QHOW ; say "How?" if index is too big
ADD.L D0,D0
BCS QHOW
ADD.L D0,D0
BCS QHOW
ADD.L D0,D0
BCS QHOW
move.l d0,-(sp) ; save the index
bsr SIZE ; get amount of free memory
move.l (sp)+,d1 ; get back the index
fmove.l fp0,d0 ; convert to integer
cmp.l d1,d0 ; see if there's enough memory
bls QSORRY ; if not, say "Sorry"
move.l VARBGN,d0 ; put address of array element...
sub.l d1,d0 ; into D0
rts
TV1
CMP.B #27,D0 ; if not @, is it A through Z?
EOR #1,CCR
BCS TSTVRT ; if not, set Carry and return
ADDQ #1,A0 ; else bump the text pointer
cmpi.b #'L',d0 ; is it a local? L0 to L7
bne TV2
move.b (a0),d0
cmpi.b #'0',d0
blo TV2
cmpi.b #'7',d0
bhi TV2
sub.b #'0',d0
addq #1,a0 ; bump text pointer
lsl.l #4,d0 ; *16 bytes per var
add.l STKFP,d0
rts
TV2
LSL.L #4,D0 ; compute the variable's address
MOVE.L VARBGN,D1
ADD.L D1,D0 ; and return it in D0 with Carry=0
TSTVRT
RTS
 
 
* ===== Divide the 32 bit value in D0 by the 32 bit value in D1.
* Returns the 32 bit quotient in D0, remainder in D1.
*
DIV32
TST.L D1 check for divide-by-zero
BEQ QHOW if so, say "How?"
MOVE.L D1,D2
MOVE.L D1,D4
EOR.L D0,D4 see if the signs are the same
TST.L D0 take absolute value of D0
BPL DIV1
NEG.L D0
DIV1 TST.L D1 take absolute value of D1
BPL DIV2
NEG.L D1
DIV2 MOVEQ #31,D3 iteration count for 32 bits
MOVE.L D0,D1
CLR.L D0
DIV3 ADD.L D1,D1 (This algorithm was translated from
ADDX.L D0,D0 the divide routine in Ron Cain's
BEQ DIV4 Small-C run time library.)
CMP.L D2,D0
BMI DIV4
ADDQ.L #1,D1
SUB.L D2,D0
DIV4 DBRA D3,DIV3
EXG D0,D1 put rem. & quot. in proper registers
TST.L D4 were the signs the same?
BPL DIVRT
NEG.L D0 if not, results are negative
NEG.L D1
DIVRT RTS
 
 
; ===== The PEEK function returns the byte stored at the address
; contained in the following expression.
 
PEEK
MOVE.B #'B',d7
MOVE.B (a0),d1
CMPI.B #'.',d1
BNE .0001
ADDQ #1,a0
move.b (a0)+,d7
.0001
BSR PARN get the memory address
cmpi.l #DT_NUMERIC,d0
bne ETYPE
FMOVE.L FP0,D0
MOVE.L D0,A1
cmpi.b #'B',d7
bne .0002
.0005
CLR.L D0 ; upper 3 bytes will be zero
MOVE.B (A1),D0
FMOVE.B D0,FP0 ; get the addressed byte
moveq #DT_NUMERIC,d0 ; data type is a number
RTS ; and return it
.0002
cmpi.b #'W',d7
bne .0003
CLR.L d0
MOVE.W (A1),D0
FMOVE.W D0,FP0 ; get the addressed word
moveq #DT_NUMERIC,d0 ; data type is a number
RTS ; and return it
.0003
cmpi.b #'L',d7
bne .0004
CLR.L d0
MOVE.L (A1),D0
FMOVE.L D0,FP0 ; get the lword
moveq #DT_NUMERIC,d0 ; data type is a number
RTS ; and return it
.0004
cmpi.b #'F',d7
bne .0005
FMOVE.X (A1),FP0 ; get the addressed float
moveq #DT_NUMERIC,d0 ; data type is a number
RTS and return it
 
; ===== The RND function returns a random number from 0 to
; the value of the following expression in fp0.
 
RND:
bsr PARN ; get the upper limit
cmpi.l #DT_NUMERIC,d0
bne ETYPE
ftst.x fp0 ; it must be positive and non-zero
fbeq QHOW
fblt QHOW
fmove fp0,fp2
moveq #40,d0 ; function #40 get random float
trap #15
fmul fp2,fp0
moveq #DT_NUMERIC,d0 ; data type is a number
rts
 
; ===== The ABS function returns an absolute value in D0.
 
ABS:
bsr PARN ; get the following expr.'s value
fabs.x fp0
moveq #DT_NUMERIC,d0 ; data type is a number
rts
 
; ===== The SIZE function returns the size of free memory in D0.
 
SIZE:
move.l StrArea,d0 ; get the number of free bytes...
sub.l TXTUNF,d0 ; between 'TXTUNF' and 'StrArea'
fmove.l d0,fp0
moveq #DT_NUMERIC,d0 ; data type is a number
rts ; return the number in fp0
; ===== The TICK function returns the processor tick register in D0.
 
TICK:
movec tick,d0
fmove.l d0,fp0
moveq #DT_NUMERIC,d0 ; data type is a number
rts
 
; ===== The CORENO function returns the core number in D0.
 
CORENO:
movec coreno,d0
fmove.l d0,fp0
moveq #DT_NUMERIC,d0 ; data type is a number
rts
 
;-------------------------------------------------------------------------------
; Get a pair of argments for the LEFT$ and RIGHT$ functions.
; (STRING, NUM)
; Returns:
; fp0 = number
; fp1 = string
;-------------------------------------------------------------------------------
 
LorRArgs:
bsr TSTC ; else look for ( STRING EXPR, NUM EXPR )
dc.b '(',LorR1-*
bsr EXPR
cmpi.l #DT_STRING,d0
bne ETYPE
bsr XP_PUSH
bsr TSTC
dc.b ',',LorR1-*
bsr EXPR
cmpi.l #DT_NUMERIC,d0
bne ETYPE
bsr TSTC
dc.b ')',LorR1-*
bsr XP_POP1
rts
LorR1
bra QHOW
;-------------------------------------------------------------------------------
; MID$ function gets a substring of characters from start position for
; requested length.
;-------------------------------------------------------------------------------
 
MID:
bsr TSTC ; look for ( STRING EXPR, NUM EXPR [, NUM_EXPR] )
dc.b '(',MID1-*
bsr EXPR
cmpi.l #DT_STRING,d0
bne ETYPE
bsr XP_PUSH
bsr TSTC
dc.b ',',MID1-*
bsr EXPR
cmpi.l #DT_NUMERIC,d0
bne ETYPE
bsr XP_PUSH
bsr TSTC
moveq #2,d5
dc.b ',',MID2-*
bsr EXPR
cmpi.l #DT_NUMERIC,d0
bne ETYPE
moveq #3,d5 ; d5 indicates 3 params
MID2
bsr TSTC
dc.b ')',MID1-*
bsr XP_POP1
cmpi.b #3,d5 ; did we have 3 arguments?
beq MID5 ; branch if did
fmove.l #$FFFF,fp0 ; set length = max
MID5
fmove.x fp1,fp2 ; fp2 = start pos
bsr XP_POP1 ; fp1 = string descriptor
;-------------------------------------------------------------------------------
; Perform MID$ function
; fp1 = string descriptor
; fp2 = starting position
; fp0 = length
;-------------------------------------------------------------------------------
DOMID
fmove.x fp1,_fpWork ; _fpWork = string descriptor
fmove.l fp2,d3 ; d3 = start pos
cmp.w _fpWork,d3 ; is start pos < length
bhs QHOW
fmove.l fp0,d2 ; d2=length
add.l d2,d3 ; start pos + length < string length?
cmp.w _fpWork,d2
bls MID4
move.w _fpWork,d2 ; move string length to d2
ext.l d2
MID4
bsr AllocateString ; a1 = pointer to new string
move.l a1,a2 ; a2 = pointer to new string
move.l _fpWork+4,a1 ; a1 = pointer to string
fmove.l fp2,d3 ; d3 = start pos
add.l d3,a1 ; a1 = pointer to start pos
move.w d2,_fpWork ; length
move.l a2,_fpWork+4 ; prep to return target string
move.l a1,a3 ; a3 = pointer to start pos
add.l d2,a3 ; a3 = pointer to end pos
bsr MVUP ; move A1 to A2 until A1 = A3
moveq #DT_STRING,d0 ; data type is a string
fmove.x _fpWork,fp0 ; string descriptor in fp0
rts
MID1
bra QHOW
;-------------------------------------------------------------------------------
; LEFT$ function truncates the string after fp0 characters.
; Just like MID$ but with a zero starting postion.
;-------------------------------------------------------------------------------
LEFT:
bsr LorRArgs ; get arguments
fmove.b #0,fp2 ; start pos = 0
bra DOMID
 
;-------------------------------------------------------------------------------
; RIGHT$ function gets the rightmost characters.
; The start position must be calculated based on the number of characters
; requested and the string length.
;-------------------------------------------------------------------------------
 
RIGHT:
bsr LorRArgs ; get arguments
fmove.l fp0,d2 ; d2 = required length
fmove.x fp1,_fpWork ; _fpWork = string descriptor
move.w _fpWork,d3 ; d3 = string length
ext.l d3 ; make d3 a long
cmp.l d2,d3 ; is length > right
bhi .0001
moveq #0,d2 ; we want all the characters if length <= right
.0001
sub.l d2,d3 ; d3 = startpos = length - right
fmove.l d3,fp2 ; fp2 = start position
bra DOMID
 
*******************************************************************
*
* *** SETVAL *** FIN *** ENDCHK *** ERROR (& friends) ***
*
* 'SETVAL' expects a variable, followed by an equal sign and then
* an expression. It evaluates the expression and sets 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
* the next line and continues from there.
*
* 'ENDCHK' checks if a command is ended with a CR. This is
* required in certain commands, such as GOTO, RETURN, STOP, etc.
*
* 'ERROR' prints the string pointed to by A0. It then prints the
* line pointed to by CURRNT with a "?" inserted at where the
* old text pointer (should be on top of the stack) points to.
* Execution of Tiny BASIC is stopped and a warm start is done.
* If CURRNT is zero (indicating a direct command), the direct
* command is not printed. If CURRNT is -1 (indicating
* 'INPUT' command in progress), the input line is not printed
* and execution is not terminated but continues at 'INPERR'.
*
* Related to 'ERROR' are the following:
* 'QWHAT' saves text pointer on stack and gets "What?" message.
* 'AWHAT' just gets the "What?" message and jumps to 'ERROR'.
* 'QSORRY' and 'ASORRY' do the same kind of thing.
* 'QHOW' and 'AHOW' also do this for "How?".
*
SETVAL
bsr TSTV ; variable name?
bcs QWHAT ; if not, say "What?"
move.l d0,-(sp) ; save the variable's address
bsr TSTC ; get past the "=" sign
dc.b '=',SV1-*
bsr EXPR ; evaluate the expression
move.l (sp)+,a6
move.l d0,(a6) ; save type
fmove.x fp0,4(a6) ; and save its value in the variable
rts
SV1
bra QWHAT ; if no "=" sign
 
FIN
bsr TSTC ; *** FIN ***
DC.B ':',FI1-*
ADDQ.L #4,SP ; if ":", discard return address
BRA RUNSML ; continue on the same line
FI1
bsr TSTC ; not ":", is it a CR?
DC.B CR,FI2-*
ADDQ.L #4,SP ; yes, purge return address
BRA RUNNXL ; execute the next line
FI2
RTS ; else return to the caller
 
ENDCHK
bsr IGNBLK
CMP.B #':',(a0)
BEQ ENDCHK1
CMP.B #CR,(A0) ; does it end with a CR?
BNE QWHAT ; if not, say "WHAT?"
ENDCHK1:
RTS
 
QWHAT
MOVE.L A0,-(SP)
AWHAT
LEA WHTMSG,A6
ERROR
bsr PRMESG display the error message
MOVE.L (SP)+,A0 restore the text pointer
MOVE.L CURRNT,D0 get the current line number
BEQ WSTART if zero, do a warm start
CMP.L #-1,D0 is the line no. pointer = -1?
BEQ INPERR if so, redo input
MOVE.B (A0),-(SP) save the char. pointed to
CLR.B (A0) put a zero where the error is
MOVE.L CURRNT,A1 point to start of current line
bsr PRTLN display the line in error up to the 0
MOVE.B (SP)+,(A0) restore the character
MOVE.B #'?',D0 display a "?"
BSR GOOUT
CLR D0
SUBQ.L #1,A1 point back to the error char.
bsr PRTSTG display the rest of the line
BRA WSTART and do a warm start
QSORRY
MOVE.L A0,-(SP)
ASORRY
LEA SRYMSG,A6
BRA ERROR
QHOW
MOVE.L A0,-(SP) Error: "How?"
AHOW
LEA HOWMSG,A6
BRA ERROR
ETYPE
lea TYPMSG,a6
bra ERROR
 
*******************************************************************
*
* *** GETLN *** FNDLN (& friends) ***
*
* 'GETLN' reads in input line into 'BUFFER'. It first prompts with
* the character in D0 (given by the caller), then it fills the
* buffer and echos. It ignores LF's but still echos
* them back. Control-H is used to delete the last character
* entered (if there is one), and control-X is used to delete the
* whole line and start over again. CR signals the end of a line,
* and causes 'GETLN' to return.
*
* 'FNDLN' finds a line with a given line no. (in D1) in the
* text save area. A1 is used as the text pointer. If the line
* is found, A1 will point to the beginning of that line
* (i.e. the high byte of the line no.), and flags are NC & Z.
* If that line is not there and a line with a higher line no.
* is found, A1 points there and flags are NC & NZ. If we reached
* the end of the text save area and cannot find the line, flags
* are C & NZ.
* 'FNDLN' will initialize A1 to the beginning of the text save
* area to start the search. Some other entries of this routine
* will not initialize A1 and do the search.
* 'FNDLNP' will start with A1 and search for the line no.
* 'FNDNXT' will bump A1 by 2, find a CR and then start search.
* 'FNDSKP' uses A1 to find a CR, and then starts the search.
 
GETLN
BSR GOOUT display the prompt
MOVE.B #' ',D0 and a space
BSR GOOUT
LEA BUFFER,A0 A0 is the buffer pointer
GL1
bsr CHKIO check keyboard
BEQ GL1 wait for a char. to come in
CMP.B #CTRLH,D0 delete last character?
BEQ GL3 if so
CMP.B #CTRLX,D0 delete the whole line?
BEQ GL4 if so
CMP.B #CR,D0 accept a CR
BEQ GL2
CMP.B #' ',D0 if other control char., discard it
BCS GL1
GL2
MOVE.B D0,(A0)+ save the char.
BSR GOOUT echo the char back out
CMP.B #CR,D0 if it's a CR, end the line
BEQ GL7
CMP.L #(BUFFER+BUFLEN-1),A0 any more room?
BCS GL1 yes: get some more, else delete last char.
GL3
MOVE.B #CTRLH,D0 delete a char. if possible
BSR GOOUT
MOVE.B #' ',D0
BSR GOOUT
CMP.L #BUFFER,A0 any char.'s left?
BLS GL1 if not
MOVE.B #CTRLH,D0 if so, finish the BS-space-BS sequence
BSR GOOUT
SUBQ.L #1,A0 decrement the text pointer
BRA GL1 back for more
GL4
MOVE.L A0,D1 delete the whole line
SUB.L #BUFFER,D1 figure out how many backspaces we need
BEQ GL6 if none needed, branch
SUBQ #1,D1 adjust for DBRA
GL5
MOVE.B #CTRLH,D0 and display BS-space-BS sequences
BSR GOOUT
MOVE.B #' ',D0
BSR GOOUT
MOVE.B #CTRLH,D0
BSR GOOUT
DBRA D1,GL5
GL6
LEA BUFFER,A0 reinitialize the text pointer
BRA GL1 and go back for more
GL7
MOVE.B #LF,D0 echo a LF for the CR
BRA GOOUT
 
FNDLN
CMP.L #$FFFF,D1 line no. must be < 65535
BCC QHOW
MOVE.L TXTBGN,A1 init. the text save pointer
 
FNDLNP
MOVE.L TXTUNF,A2 check if we passed the end
SUBQ.L #1,A2
CMP.L A1,A2
BCS FNDRET if so, return with Z=0 & C=1
MOVE.B (A1),D2 if not, get a line no.
LSL #8,D2
MOVE.B 1(A1),D2
CMP.W D1,D2 is this the line we want?
BCS FNDNXT no, not there yet
FNDRET
RTS return the cond. codes
 
FNDNXT
ADDQ.L #2,A1 find the next line
 
FNDSKP
CMP.B #CR,(A1)+ try to find a CR
BEQ FNDLNP
CMP.L TXTUNF,A1
BLO FNDSKP
BRA FNDLNP check if end of text
 
*******************************************************************
*
* *** MVUP *** MVDOWN *** POPA *** PUSHA ***
*
* 'MVUP' moves a block up from where A1 points to where A2 points
* until A1=A3
*
* 'MVDOWN' moves a block down from where A1 points to where A3
* points until A1=A2
*
* 'POPA' restores the 'FOR' loop variable save area from the stack
*
* 'PUSHA' stacks for 'FOR' loop variable save area onto the stack
*
MVUP
CMP.L A1,A3 see the above description
BEQ MVRET
MOVE.B (A1)+,(A2)+
BRA MVUP
MVRET
RTS
 
MVDOWN
CMP.L A1,A2 see the above description
BEQ MVRET
MOVE.B -(A1),-(A3)
BRA MVDOWN
 
POPA
MOVE.L (SP)+,A6 A6 = return address
MOVE.L (SP)+,LOPVAR restore LOPVAR, but zero means no more
BEQ PP1
MOVE.L (SP)+,LOPINC+8 if not zero, restore the rest
MOVE.L (SP)+,LOPINC+4
MOVE.L (SP)+,LOPINC
MOVE.L (SP)+,LOPLMT+8
MOVE.L (SP)+,LOPLMT+4
MOVE.L (SP)+,LOPLMT
MOVE.L (SP)+,LOPLN
MOVE.L (SP)+,LOPPT
PP1
JMP (A6) return
 
PUSHA
MOVE.L STKLMT,D1 Are we running out of stack room?
SUB.L SP,D1
BCC QSORRY if so, say we're sorry
MOVE.L (SP)+,A6 else get the return address
MOVE.L LOPVAR,D1 save loop variables
BEQ PU1 if LOPVAR is zero, that's all
MOVE.L LOPPT,-(SP) else save all the others
MOVE.L LOPLN,-(SP)
MOVE.L LOPLMT,-(SP)
MOVE.L LOPLMT+4,-(SP)
MOVE.L LOPLMT+8,-(SP)
MOVE.L LOPINC,-(SP)
MOVE.L LOPINC+4,-(SP)
MOVE.L LOPINC+8,-(SP)
PU1
MOVE.L D1,-(SP)
JMP (A6) return
 
*******************************************************************
*
* *** PRTSTG *** QTSTG *** PRTNUM *** PRTLN ***
*
* 'PRTSTG' prints a string pointed to by A1. It stops printing
* and returns to the caller when either a CR is printed or when
* the next byte is the same as what was passed in D0 by the
* caller.
*
* 'QTSTG' looks for an underline (back-arrow on some systems),
* single-quote, or double-quote. If none of these are found, returns
* to the caller. If underline, outputs a CR without a LF. If single
* or double quote, prints the quoted string and demands a matching
* end quote. After the printing, the next 2 bytes of the caller are
* skipped over (usually a short branch instruction).
*
* 'PRTNUM' prints the 32 bit number in D1, leading blanks are added if
* needed to pad the number of spaces to the number in D4.
* However, if the number of digits is larger than the no. in
* D4, all digits are printed anyway. Negative sign is also
* printed and counted in, positive sign is not.
*
* 'PRTLN' prints the saved text line pointed to by A1
* with line no. and all.
*
PRTSTG:
MOVE.B D0,D1 save the stop character
PS1
MOVE.B (A1)+,D0 get a text character
CMP.B D0,D1 same as stop character?
BEQ PRTRET if so, return
BSR GOOUT display the char.
CMP.B #CR,D0 is it a C.R.?
BNE PS1 no, go back for more
MOVE.B #LF,D0 yes, add a L.F.
BSR GOOUT
PRTRET
RTS then return
 
PRTSTR2a
move.b (a1)+,d0
bsr GOOUT
PRTSTR2:
dbra d1,PRTSTR2a
rts
if 0
QTSTG
bsr TSTC *** QTSTG ***
DC.B '"',QT3-*
MOVE.B #'"',D0 it is a "
QT1
MOVE.L A0,A1
BSR PRTSTG print until another
MOVE.L A1,A0
MOVE.L (SP)+,A1 pop return address
CMP.B #LF,D0 was last one a CR?
BEQ RUNNXL if so, run next line
QT2
ADDQ.L #2,A1 skip 2 bytes on return
JMP (A1) return
QT3
bsr TSTC is it a single quote?
DC.B '''',QT4-*
MOVE.B #'''',D0 if so, do same as above
BRA QT1
QT4
bsr TSTC is it an underline?
DC.B '_',QT5-*
MOVE.B #CR,D0 if so, output a CR without LF
bsr GOOUT
MOVE.L (SP)+,A1 pop return address
BRA QT2
QT5
RTS none of the above
endif
 
PRTNUM:
link a2,#-48
move.l _canary,44(a0)
movem.l d0/d1/d2/d3/a1,(sp)
fmove.x fp0,20(sp)
fmove.x fp1,32(sp)
fmove.x fp1,fp0 ; fp0 = number to print
lea _fpBuf,a1 ; a1 = pointer to buffer to use
moveq #39,d0 ; d0 = function #39 print float
move.l d4,d1 ; d1 = width
move.l d4,d2 ; d2 = precision max
moveq #'e',d3
trap #15
movem.l (sp),d0/d1/d2/d3/a1
fmove.x 20(sp),fp0
fmove.x 32(sp),fp1
cchk 44(a0)
unlk a2
rts
 
; Debugging
if 0
PRTFP0:
link a2,#-48
move.l _canary,44(a0)
movem.l d0/d1/d2/d3/a1,(sp)
fmove.x fp0,20(sp)
lea _fpBuf,a1 ; a1 = pointer to buffer to use
moveq #39,d0 ; d0 = function #39 print float
moveq #30,d1 ; d1 = width
moveq #25,d2 ; d2 = precision max
moveq #'e',d3
trap #15
movem.l (sp),d0/d1/d2/d3/a1
fmove.x 20(sp),fp0
cchk 44(a0)
unlk a2
rts
endif
 
PRTLN:
CLR.L D1
MOVE.B (A1)+,D1 get the binary line number
LSL #8,D1
MOVE.B (A1)+,D1
FMOVE.W D1,FP1
MOVEQ #5,D4 ; display a 5 digit line no.
BSR PRTNUM
MOVE.B #' ',D0 followed by a blank
BSR GOOUT
CLR D0 stop char. is a zero
BRA PRTSTG display the rest of the line
 
 
; ===== Test text byte following the call to this subroutine. If it
; equals the byte pointed to by A0, return to the code following
; the call. If they are not equal, branch to the point
; indicated by the offset byte following the text byte.
 
TSTC:
BSR IGNBLK ; ignore leading blanks
MOVE.L (SP)+,A1 ; get the return address
MOVE.B (A1)+,D1 ; get the byte to compare
CMP.B (A0),D1 ; is it = to what A0 points to?
BEQ TC1 ; if so
CLR.L D1 ; If not, add the second
MOVE.B (A1),D1 ; byte following the call to
ADD.L D1,A1 ; the return address.
JMP (A1) ; jump to the routine
TC1
ADDQ.L #1,A0 ; if equal, bump text pointer
ADDQ.L #1,A1 ; Skip the 2 bytes following
JMP (A1) ; the call and continue.
 
 
; ===== See if the text pointed to by A0 is a number. If so,
; return the number in FP1 and the number of digits in D2,
; else return zero in FP1 and D2.
; If text is not a number, then A0 is not updated, otherwise
; A0 is advanced past the number. Note A0 is always updated
; past leading spaces.
 
TSTNUM
link a2,#-32
move.l _canary,28(sp)
movem.l d1/a1,(sp)
fmove.x fp0,16(sp)
moveq #41,d0 ; function #41, get float
moveq #1,d1 ; d1 = input stride
move.l a0,a1 ; a1 = pointer to input buffer
trap #15 ; call BIOS get float function
move.l a1,a0 ; set text pointer
moveq #DT_NUMERIC,d0 ; default data type = number
fmove.x fp0,fp1 ; return expected in fp1
tst.w d1 ; check if a number (digits > 0?)
beq .0002
clr.l d2 ; d2.l = 0
move.w d1,d2 ; d2 = number of digits
bra .0001
.0002 ; not a number, return with orignal text pointer
moveq #0,d0 ; data type = not a number
moveq #0,d2 ; d2 = 0
fmove.l d2,fp1 ; return a zero
.0001
movem.l (sp),d1/a1
fmove.x 16(sp),fp0
cchk 28(sp)
unlk a2
rts
; ===== Skip over blanks in the text pointed to by A0.
 
IGNBLK
CMP.B #' ',(A0)+ ; see if it's a space
BEQ IGNBLK ; if so, swallow it
SUBQ.L #1,A0 ; decrement the text pointer
RTS
 
*
* ===== Convert the line of text in the input buffer to upper
* case (except for stuff between quotes).
*
TOUPBUF LEA BUFFER,A0 set up text pointer
CLR.B D1 clear quote flag
TOUPB1
MOVE.B (A0)+,D0 get the next text char.
CMP.B #CR,D0 is it end of line?
BEQ TOUPBRT if so, return
CMP.B #'"',D0 a double quote?
BEQ DOQUO
CMP.B #'''',D0 or a single quote?
BEQ DOQUO
TST.B D1 inside quotes?
BNE TOUPB1 if so, do the next one
BSR TOUPPER convert to upper case
MOVE.B D0,-(A0) store it
ADDQ.L #1,A0
BRA TOUPB1 and go back for more
TOUPBRT
RTS
 
DOQUO TST.B D1 are we inside quotes?
BNE DOQUO1
MOVE.B D0,D1 if not, toggle inside-quotes flag
BRA TOUPB1
DOQUO1 CMP.B D0,D1 make sure we're ending proper quote
BNE TOUPB1 if not, ignore it
CLR.B D1 else clear quote flag
BRA TOUPB1
 
*
* ===== Convert the character in D0 to upper case
*
TOUPPER CMP.B #'a',D0 is it < 'a'?
BCS TOUPRET
CMP.B #'z',D0 or > 'z'?
BHI TOUPRET
SUB.B #32,D0 if not, make it upper case
TOUPRET RTS
 
*
* 'CHKIO' checks the input. If there's no input, it will return
* to the caller with the Z flag set. If there is input, the Z
* flag is cleared and the input byte is in D0. However, if a
* control-C is read, 'CHKIO' will warm-start BASIC and will not
* return to the caller.
*
CHKIO
bsr GOIN get input if possible
BEQ CHKRET if Zero, no input
CMP.B #CTRLC,D0 is it control-C?
BNE CHKRET if not
BRA WSTART if so, do a warm start
CHKRET
RTS
 
*
* ===== Display a CR-LF sequence
*
;CRLF LEA CLMSG,A6
 
 
; ===== Display a zero-ended string pointed to by register A6
 
PRMESG
MOVE.B (A6)+,D0 ; get the char.
BEQ PRMRET ; if it's zero, we're done
BSR GOOUT ; else display it
BRA PRMESG
PRMRET
RTS
 
******************************************************
* The following routines are the only ones that need *
* to be changed for a different I/O environment. *
******************************************************
 
; ===== Clear screen and home cursor
 
CLS:
moveq #11,d0 ; set cursor position
move.w #$FF00,d1 ; home cursor and clear screen
trap #15
bra FINISH
 
; ===== Output character to the console (Port 1) from register D0
;(Preserves all registers.)
 
OUTC:
move.l a6,-(a7)
move.l OUTPTR,a6
jsr (a6)
move.l (a7)+,a6
rts
 
OUTC1:
movem.l d0/d1,-(a7)
move.l d0,d1
moveq.l #6,d0
trap #15
movem.l (a7)+,d0/d1
rts
 
*OUTC BTST #1,$10040 is port 1 ready for a character?
* BEQ OUTC if not, wait for it
* MOVE.B D0,$10042 out it goes.
* RTS
 
*
* ===== Input a character from the console into register D0 (or
* return Zero status if there's no character available).
*
INC
move.l a6,-(a7)
move.l INPPTR,a6
jsr (a6)
move.l (a7)+,a6
rts
 
INC1
move.l d1,-(a7)
moveq.l #5,d0 * function 5 GetKey
trap #15
move.l d1,d0
move.l (a7)+,d1
cmpi.b #-1,d0
bne .0001
clr.b d0
.0001:
rts
 
*INC BTST #0,$10040 is character ready?
* BEQ INCRET if not, return Zero status
* MOVE.B $10042,D0 else get the character
* AND.B #$7F,D0 zero out the high bit
*INCRET RTS
 
*
* ===== Output character to the host (Port 2) from register D0
* (Preserves all registers.)
*
AUXOUT:
movem.l d0/d1,-(a7)
move.l d0,d1
moveq #34,d0
trap #15
movem.l (a7)+,d0/d1
rts
 
*AUXOUT BTST #1,$10041 is port 2 ready for a character?
* BEQ AUXOUT if not, wait for it
* MOVE.B D0,$10043 out it goes.
* RTS
 
*
* ===== Input a character from the host into register D0 (or
* return Zero status if there's no character available).
*
AUXIN:
move.l d1,-(a7)
moveq #36,d0 ; serial get char from buffer
trap #15
move.l d1,d0
move.l (a7)+,d1
cmpi.w #-1,d0
beq .0001
andi.b #$7F,d0 ; clear high bit
ext.w d0 ; return character in d0
ext.l d0
rts
.0001:
moveq #0,d0 ; return zf=1 if no character available
rts
 
;AUXIN
*AUXIN BTST #0,$10041 is character ready?
* BEQ AXIRET if not, return Zero status
* MOVE.B $10043,D0 else get the character
* AND.B #$7F,D0 zero out the high bit
AXIRET RTS
 
; ===== Return to the resident monitor, operating system, etc.
;
BYEBYE
move.l #8,_fpTextIncr
bra Monitor
; MOVE.B #228,D7 return to Tutor
; TRAP #14
 
INITMSG DC.B CR,LF,'MC68000 Tiny Float BASIC, v1.0',CR,LF,LF,0
OKMSG DC.B CR,LF,'OK',CR,LF,0
HOWMSG DC.B 'How?',CR,LF,0
WHTMSG DC.B 'What?',CR,LF,0
TYPMSG DC.B 'Type?',CR,LF,0
NOSTRING DC.B 'No string space',CR,LF,0
SRYMSG DC.B 'Sorry.'
CLMSG DC.B CR,LF,0
DC.B 0 <- for aligning on a word boundary
LSTROM EQU * end of possible ROM area
*
* Internal variables follow:
*
align 2
RANPNT DC.L START random number pointer
INPPTR DS.L 1 input pointer
OUTPTR DS.L 1 output pointer
CURRNT DS.L 1 ; Current line pointer
STKFP DS.L 1 ; saves frame pointer
STKGOS DS.L 1 Saves stack pointer in 'GOSUB'
STKINP DS.L 1 Saves stack pointer during 'INPUT'
LOPVAR DS.L 1 'FOR' loop save area
LOPINC DS.L 3 increment
LOPLMT DS.L 3 limit
LOPLN DS.L 1 line number
LOPPT DS.L 1 text pointer
IRQROUT DS.L 1
STRSTK DS.L 1 ; string pointer stack area, 8 entries
StrSp DS.L 1 ; string stack stack pointer
StrArea DS.L 1 ; pointer to string area
LastStr DS.L 1 ; pointer to last used string in area
TXTUNF DS.L 1 points to unfilled text area
VARBGN DS.L 1 points to variable area
STKLMT DS.L 1 holds lower limit for stack growth
DIRFLG DS.L 1 ; indicates 1=DIRECT mode
BUFFER DS.B BUFLEN Keyboard input buffer
TXT EQU * Beginning of program area
; END

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.