Mercurial > hg > index.cgi
diff src/progctrl.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 | a6a53e5c04bd |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/progctrl.s Sun Aug 06 00:36:48 2023 -0600 @@ -0,0 +1,188 @@ + *pragmapush list + *pragma list +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; The END command. +cmd_end bne SNERROR ; error out if there is an argument + ;jsr closeall ; close all files for END + clra ; flag END (clear carry) + bra cmd_stop0 ; go do the stop/end +cmd_stop bne SNERROR ; raise error if there was an argument + coma ; flag STOP - set carry +cmd_stop0 ror endflag ; set stop/end flag +cmd_stop1 clr filenum ; reset I/O to console + ldx curline ; in immediate mode? + beq cmd_stop2 ; brif so - don't save the continue pointers + stx contline ; save pointer to current line for CONT + ldx curstmt ; get current statement address + stx contstmt ; save it for CONT +cmd_stop2 rol endflag ; get STOP/END to C (1=STOP) + bcc cmd_stop3 ; brif END - don't do message + ldx #breakmsg ; do "BREAK IN" + jmp ERROR2 ; the bottom half of the error handler can deal with the details +cmd_stop3 puls x,pc ; lose return address and return to caller of interpretation loop +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; The NEW command. +; +; This also includes several useful entry points: +; +; cmd_newraw: does the whole NEW but without any syntax checks +; cmd_newinptr: skips clearing the program text +; cmd_newvars: clears variables and resets the stack and other misc state +; cmd_newstack: just reset the stack and other misc state +cmd_new bne cmd_new0 ; brif there was an argument - don't wipe things out on syntax error +cmd_newraw ldx progtext ; point to start of program + clr -1,x ; make sure there's a NUL before the start of the program + clr ,x+ ; put a NULL pointer at the start of the program + clr ,x+ + stx vartab ; set start of variables after that +cmd_newinptr ldx progtext ;* set input pointer to the NUL before the program; this will cause the + leax -1,x ;* the interpreter to drop to immediate mode no matter what it was + stx inputptr ;* executing before this call if called from the main loop +cmd_newvars ldx memsize ; get top of memory + stx stringtab ; clear out string space + ldx vartab ; get start of variables + stx objecttab ; set start of large objects (arrays) there too (clear vars) + stx freestart ; set start of free memory (end of large objects) (clear arrays) +cmd_newstack ldx #stringstackend ; reset string stack (string stack counts down) + stx stringstackptr + ldx ,s ; get return address + lds freetop ; reset stack to top of memory + clr ,-s ; put a flag to stop stack searches (NEXT, RETURN) + sts stackptr ; reset pointer for call stack + clr contstmt ; clear "CONT" destination + clr contstmt+1 + jmp ,x ; return to caller +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; REM and ' commands; also ELSE comes here since it needs to skip the rest of the line in that case. +cmd_else +cmd_apos +cmd_rem clra ; clear carry + ldx curline ; get start of current line + beq cmd_stop3 ; brif immediate mode - fall back to caller + ldx ,x ; get address of next line + leax -1,x ; move back one + stx inputptr ; put input pointer there +cmd_new0 rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; DATA command +; +; need to skip to the end of the current statement, which is either the end of the line OR a colon not included inside +; a quoted string +cmd_data ldx inputptr ; get input pointer +cmd_data0 lda ,x+ ; get character at pointer + beq cmd_data1 ; brif end of line + cmpa #': ; end of statement? + bne cmd_data2 ; brif not +cmd_data1 leax -1,x ; move back to the NUL or colon + stx inputptr ; reset input pointer for interpreter + rts +cmd_data2 cmpa #'" ; start of constant string? + bne cmd_data0 ; brif not - process more characters +cmd_data3 lda ,x+ ; get next string character + beq cmd_data1 ; brif end of line + cmpa #'" ; string delimiter? + bne cmd_data3 ; brif not - keep going + bra cmd_data0 ; process stuff outside string +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; RUN command +cmd_run ;jsr closeall ; close all files + jsr curchar ; what do we have as an argument? + bcs cmd_goto ; brif a digit - it's a line number (RUN ###); do GOTO + lbne SNERROR ; brif anything else on the line - not legit command + ldx progtext ; point to start of program + bra cmd_goto0 ; go transfer control to the start of the program +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; GOTO command +cmd_goto jsr parse_lineno ; parse the line number +cmd_gosub0 jsr prog_findlinecl ; go look up line number + bcc cmd_goto0 ; brif line found +ULERROR ldb #err_ul ; raise undefined line error + jmp ERROR +cmd_goto0 stx curline ; make sure we aren't flagging immediate mode + leax -1,x ; move input pointer to NUL before destination line + stx inputptr ; put input pointer there + rts ; resume interpretation at the new location +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; GOSUB command +cmd_gosub jsr parse_lineno ; parse the destination line so return location is after the line number + ldd #tok_gosub*256+4 ; stack frame details + bsr callstack_alloc ; make a stack frame + ldx curline ; save current line pointer + stx ,u + ldx inputptr ; save current input pointer + stx 2,u + bra cmd_gosub0 ; go finish up as a GOTO +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; RETURN command +; POP command +; +; RETURN will search the call stack for the first GOSUB frame and remove all other placeholders it finds. A frame type +; of 0 will cause it to stop. +cmd_pop skip1lda ; set nonzero for POP +cmd_return clra ; set zero for RETURN + pshs a ; save operation type + bsr callstack_first ; get first entry on call stack + bne cmd_return1 ; brif there's a frame - don't error +RG_ERROR ldb #err_rg ; raise RETURN without GOSUB + jmp ERROR +cmd_return0 bsr callstack_next ; move to next entry + beq RG_ERROR ; brif end of stack - raise error +cmd_return1 cmpb #tok_gosub ; do we have a GOSUB frame? + bne cmd_return0 ; brif not - try again + lda ,s+ ; is it "POP"? + bne cmd_return2 ; brif so - don't change flow control but clear stack frame + ldx ,u ; get back saved line pointer + stx curline + ldx 2,u ; get back saved input pointer + stx inputptr +cmd_return2 bsr callstack_pop ; clean up call stack + bra cmd_data ; move to end of statement (move past any "ON GOSUB" entries +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Point to the first entry on the call stack; yes this is trivial but it points to the payload, not the header. Also +; sets Z if there is nothing on the stack. +callstack_first ldu stackptr ; get stack pointer + ldb ,u++ ; set flags on frame type and adjust pointer + rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Move to the next frame on the call stack; enter with U pointing to a stack frame payload area +callstack_next ldb -1,u ; get length of this frame + leau b,u ; move to the next frame + ldb -2,u ; set flags on frame type code + rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Create a stack frame. Enter with the frame type flag in A and the size in B. +; +; The stack frame of size B bytes plus 2 bytes for the length and type flag will be allocated between the actual +; hardware stack and the current call stack pointer. Return with the pointer to the allocated frame in U. As long as +; there are no pointers to anything on the hardware stack, this will allow the stack to be entirely intact after +; the call. +callstack_alloc addb #2 ; account for the header bytes + pshs a,b ; save the type and length + negb ; need a negative offset + leax ,s ; point to current bottom of stack + leas b,s ; make a hole below the stack + leau ,s ; get a pointer to the destination for copying +callstack_alloc0 + lda ,x+ ; copy a byte down + sta ,u+ + cmpx stackptr ; have we reached the top of the stack? + blo callstack_alloc0 ; brif not + stu stackptr ; save the new call stack pointer + puls d ; get back the type and length values + std ,u++ ; save type and length + rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Pop the call stack to the end of the frame pointed to by U; this will relocate the hardware stack to close the +; newly made gap in memory. +callstack_pop leau -2,u ; move back to header + ldb 1,u ; get length of frame + leax b,u ; point to element after this frame + sts ,--s ; save the current bottom of the stack + stx stackptr ; save new call stack pointer +callstack_pop0 lda ,-u ; copy a byte up + sta ,-x + cmpu ,s ; at the bottom of the call stack? + bhi callstack_pop0 ; brif not + leas 2,x ; reset the stack pointer (and lose the saved stack pointer value) + rts + *pragmapop list