Mercurial > hg > index.cgi
view src/interp.s @ 74:e74d00ac6b79
Split some code into separate files for easier management (2)
Because the source for lwbasic is so large, split it into several
different files to make it easier to navigate and modify. This is
part two of the split. Also includes fix for dependency tracking
related to the split in the make file.
author | William Astle <lost@l-w.ca> |
---|---|
date | Sun, 06 Aug 2023 00:36:48 -0600 |
parents | |
children | 5f8f0b0781e8 |
line wrap: on
line source
*pragmapush list *pragma list ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Fetch next input character, skip spaces. This is structured the way it is to avoid burning any register except A ; which is used for the returned value. Z will be set if the input character is NUL or a colon. C will be set if the ; input character is an ASCII digit. This allows testing Z to identify the end of a command due to either a colon or ; the end of a line. ; ; Compared to Color Basic, the instruction sequence only varies in the handling of the LDA. In Color Basic, the sequence ; is an LDA extended followed by a JMP extended. This totals to 9 cycles (5 for LDA, 4 for JMP). In LWBasic, an LDA ; with extended indirect addressing is used. This also totals 9 cycles. The only other difference is when a space is ; detected where the branch can be direct to the nextchar code instead of having to branch around a direct page JUMP ; which saves 3 cycles for the case where a space is detected. In other words, this is only slower by virtue of the ; fact that it is called with an extended JSR instead of a direct JSR which causes one extra cycle to be used there ; and one extra byte for each call to nextchar or curchar. ; ; On 6309, native move saves an extra cycle in the LDA sequence using the LDA extended followed by JMP extended ; sequence. ; ; This whole thing could be sped up by keeping the input pointer in a register. However, retaining the ability to ; use Y without having to save it first is likely more beneficial. nextchar inc inputptr+1 ; bump LSB of input pointer bne curchar ; brif no carry inc inputptr ; bump MSB curchar lda [inputptr] ; read the byte cmpa #'9+1 ; clear C if above ASCII digits, Z if colon bhs curchar0 ; brif above the ASCII digits cmpa #0x20 ; is it a space? beq nextchar ; brif so - skip over it suba #'0 ; clever way to set C if >= ASCII 0, Z if zero suba #-'0 curchar0 rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This is exactly the same as nextchar except it doesn't skip spaces. Unfortunately, for efficiency purposes, we need ; to actually duplicate code here. nextcharraw inc inputptr+1 ; bump LSB of input pointer bne curchar ; brif no carry inc inputptr ; bump MSB curcharraw lda [inputptr] ; fetch the byte cmpa #'9+1 ; clear C if above digits, set Z if colon bhs curcharraw0 ; brif above digits suba #'0 ; clever way to set C if >= ASCII 0, Z if zero suba #-'0 curcharraw0 rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The error handler ; ; Enter with the error number in B. This routine will do some cleanup and handle any ON ERROR GOTO handler that ; may be active. ; ; Note the error message lookup does not need to be efficient which is why the lookup just runs through the list ; of error messages in sequence looking for NUL terminators. The specific handling of B (error number) below avoids ; issues if there happen to be error codes above 128. ERROR clr filenum ; reset display device to console jsr writecondnl ; do a newline if needed (will preserve B) ldx #errormsg ; point to error message list incb ; account for decb below bra ERROR1 ; go search for correct message ERROR0 lda ,x+ ; end of message? bne ERROR0 ; brif not end of message ERROR1 decb ; at the correct one? bne ERROR0 ; brif not - skip to next one ERROR2 jsr writestrconduc ; output error message ldu curline ; are we in immediate mode? beq ERROR3 ; brif so ldx #inmsg ; point to " in " jsr writestrconduc ; output " in " ldd 2,u ; get line number jsr print_uint16d ; display the line number ERROR3 lds freetop ; reset the stack pointer (error routine could be called anywhere) clr ,-s ; reset the call stack sts stackptr ; fall through to immediate mode intentional ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Immediate mode handler immediate jsr writecondnl ; do newline if required ldx #prompt ; point to prompt string jsr console_outstrn immediate0 jsr readline ; read input line bcs immediate0 ; brif ended with BREAK ldx #linebuff ; point to start of line input buffer stx inputptr ; set input pointer jsr curchar ; skip spaces and set flags bcs immediate1 ; brif there's a line number tsta ; is there anything there at all (end of line)? beq immediate0 ; brif not - read another line ldx inputptr ; get the modified input pointer processing above jsr tokenize ; tokenize the line at inputptr, return with result at tokebuff and X jsr interpretline ; go interpret the tokenized line bra immediate ; go handle another line immediate1 bsr parse_lineno ; parse the line number bsr prog_findline ; go see if the line is in the program bne immediate3 ; brif not - no need to delete it ldu ,x ; get next line pointer which is where we start the copy from leay ,x ; use temp pointer for copying immediate2 lda ,u+ ; get source byte sta ,y+ ; stash it cmpu vartab ; did we reach the end of the program text? blo immediate2 ; brif not sty vartab ; save new end of program immediate3 jsr curchar ; skip any spaces after line number tsta ; is it the end of input (don't test for colon) beq immediate6 ; brif so - we don't need to insert a line pshs x ; save program insert location and line number ldx inputptr ; point to line text jsr tokenize ; tokenize line, get length to D leay ,x ; save tokenized line pointer addd #4 ; account for next line pointer and line number ldx vartab ; get start of copy location leau d,x ; set destination copy location D bytes further up stu vartab ; save new end of program immediate4 lda ,-x ; get byte from program sta ,-u ; stash it above the empty space cmpx ,s ; did we reach the insertion point? bne immediate4 ; brif not - keep going leas 2,s ; clear insertion location stu ,x++ ; set next line pointer to not null ldd binval ; set the line number for the program std ,x++ immediate5 lda ,y+ ; get byte from tokenized line sta ,x+ ; stash it in the program bne immediate5 ; brif not at end of tokenized line (see note for fixlineptrs) immediate6 bsr prog_fixlineptrs ; fix up line pointers (all of them) ldx vartab ; clear out variables stx objecttab stx freestart bra immediate0 ; go handle more input ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Fix up next line pointers. Enter at prog_fixlineptrs to do the entire program. Enter at prog_fixlineptrsx to start ; at the line pointered to by X, which MUST NOT point to the end of the program. ; ; Works by simply scanning for a NUL in the program text after a line header (pointer to next line and line number) ; and uses that as the new next line pointer. A NULL next line pointer flags the end of the program. ; ; Observation: if the program text format is changed such that it can include NULs embedded within a line, this routine ; will need to be updated to grok that. prog_fixlineptrs ldx progtext ; point to start of program prog_fixlineptrsx ldu ,x ; are we at the end of the program? beq prog_findline2 ; brif not (borrow RTS from findline) leau 4,x ; point to line text (past pointer and line number) prog_fixlineptrs1 lda ,u+ ; are we at the end of this line? bne prog_fixlineptrs1 ; brif not stu ,x ; set the next pointer for the previous line leax ,u ; move to the next line bra prog_fixlineptrsx ; go handle the next line ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Find a line in the program. Returns with C set and Z clear if no match and C clear and Z set if a match is found. X ; will point to either the exact matched line *or* the line that would be immediately after the desired line number if ; the line had been present, which could be the end of the program. D and U are clobbered. Enter at prog_findlinex to ; start searching at the line pointed to by X. Enter at prog_findline to start at the beginning of the program. Enter ; with the desired line number in binval. prog_findlinecl ldx curline ; get current line pointer beq prog_findline ; brif immediate mode ldd binval ; get desired line number cmpd 2,x ; is the desired line number >= current line? beq prog_findline2 ; brif this is the right line (optimizes goto self) bhi prog_findlinex ; brif desired line higher: start here instead of program start prog_findline ldx progtext ; point to start of program prog_findlinex ldu binval ; get line number to search for prog_findline0 ldd ,x ; end of program? beq prog_findline1 ; brif not cmpu 2,x ; does line number match? Z set if so, clear if not; C set not found bls prog_findline2 ldx ,x ; move to next line bra prog_findline0 ; see if we found the line yet prog_findline1 coma ; set carry for not found; also clears Z because D is zero from above prog_findline2 rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Parse a line number and return it in binval; raise syntax error if the line number overflows 16 bits unsigned. ; Preserves; registers except D. This will accept the entire 16 bit unsigned number range which is why there is ; a BCS after every shift or add. Enter with the input pointer pointing to the number to parse. parse_lineno ldd zero ; clear out accumlator but preserve carry flag std binval jsr curchar ; set flags on current character; skip spaces bcc parse_lineno1 ; brif first character wasn't a digit - default to zero parse_lineno0 suba #0x30 ; adjust to binary digit pshs a ; save digit so we can add it later ldd binval ; get accumulated number lslb ; multiply accumulator by 10 rola ; times 2 bcs SNERROR ; brif overflow lslb rola ; times 4 bcs SNERROR ; brif overflow addd binval ; times 5 (add orignal value to times 4) bcs SNERROR ; brif overflow lslb rola ; times 10 bcs SNERROR ; brif overflow addb ,s+ ; add in accumulated digit adca #0 bcs SNERROR ; brif overflow std binval ; save accumulated number jsr nextcharraw ; get next input character; DO NOT skip spaces bcs parse_lineno0 ; brif it's also a digit parse_lineno1 rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Main interpretation loop ; ; Enter at interpret with inputptr pointing to the code stream to interpret. ; Enter at interpretline with X pointing to the command stream to interpret which will return to the caller one the ; command stream has completed. STOP or BREAK will return with carry set while END or falling off the end of the ; code will return with carry clear. In the event of an error, the usual error processing will be done and control ; will return to immediate mode with the stack reset. interpret jsr breakcheck ; check for BREAK bcs cmd_stop0 ; brif BREAK detected - go stop the program ldx inputptr ; get interpration address stx curstmt ; save address of the current statement (needed for some stuff) lda ,x+ ; are we at the end of the line? beq interpret0 ; brif so cmpa #': ; end of statement? beq interpret3 ; brif so - do a statement SNERROR ldb #err_sn ; raise a syntax error jmp ERROR interpret0 sta endflag ; flag the program exit state as "END" (will be zero) ldd curline ; were we in immediate mode? bne interpret1 ; brif not clra ; clear carry to indicate normal exit rts ; return to caller interpret1 ldd ,x ; are we at the end of the program? beq interpret4 ; brif so - bail out stx curline ; save pointer to current line leax 3,x ; set input pointer one before the start of the line text interpret2 stx inputptr interpret3 jsr nextchar ; fetch first character of next statement beq interpret ; brif end of statement - do the next statement dance tsta ; set flags properly for token lbpl cmd_let ; brif no command - do assignment (LET command is optional) ldx #primaryjump ; point to jump table anda #0x7f ; lose bit 7 leax a,x ; get half way to the correct offset ldx a,x ; get the address the other half of the way from here jsr nextchar ; skip past token and set flags jsr ,x ; call the routine bra interpret ; go handle the next statement dance interpret4 bsr cmd_stop1 ; make sure stack is aligned correctly (will not return) interpretline clr curline ; blank out current line pointer (for immediate mode) clr curline+1 leax -1,x ; move back before start of code stream bra interpret2 ; go interpret this statement and then continue with stuff ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Check for character in B and raise a syntax error if not found at current input pointer. If it is found, fetch the ; next input character. syncheckb cmpb [inputptr] ; do we have a syntax match? bne SNERROR ; brif not jmp nextchar ; return next input character *pragmapop list