Mercurial > hg > index.cgi
changeset 62:eba95ed43423
Add GOSUB/RETURN/POP
Add call stack handling along with GOSUB, RETURN, and POP.
author | William Astle <lost@l-w.ca> |
---|---|
date | Tue, 21 Feb 2023 20:57:25 -0700 |
parents | a0f7c8768867 |
children | a3122251b5fe |
files | src/lwbasic.s |
diffstat | 1 files changed, 95 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lwbasic.s Mon Feb 20 16:13:40 2023 -0700 +++ b/src/lwbasic.s Tue Feb 21 20:57:25 2023 -0700 @@ -201,6 +201,7 @@ memsize rmb 2 ; top of memory not reserved freetop rmb 2 ; top of free memory (bottom of string space) stringtab rmb 2 ; bottom of used string space +stackptr rmb 2 ; bottom of the "stack frame" stack (the actual stack is below here) progtext rmb 2 ; pointer to start of program text vartab rmb 2 ; pointer to start of integer scalars objecttab rmb 2 ; pointer to start of arrays and other variable sized objects @@ -486,8 +487,9 @@ stx stringtab ; mark string space as empty leax -200,x ; allocate 200 bytes of string space stx freetop ; save top of free memory - leas ,x ; put the stack there - clr ,-s ; put a flag to stop NEXT/RETURN searches + clr ,-x ; make a hole for the "end of call stack" flag + stx stackptr ; save the new call stack pointer + leas ,x ; put the actual stack below the above ldx #heapstart ; point to start of free memory clr ,x+ ; put a NUL before the start of the program stx progtext ; put the start of the program there @@ -1110,6 +1112,8 @@ 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 @@ -1263,8 +1267,9 @@ cmd_newstack ldx #stringstackend ; reset string stack (string stack counts down) stx stringstackptr ldx ,s ; get return address - lda freetop ; reset stack to top of memory - clr ,-s ; but a flag to stop stack searches (NEXT, RETURN) + 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 @@ -1380,7 +1385,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; GOTO command cmd_goto jsr parse_lineno ; parse the line number - jsr prog_findlinecl ; go look up 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 @@ -1390,7 +1395,87 @@ rts ; resume interpretation at the new location ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; GOSUB command -cmd_gosub rts +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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Miscelaneous strings prompt fcn 'OK' ; general prompt @@ -1467,6 +1552,8 @@ fcn 'Syntax error' deferr ul fcn 'Undefined line number' + deferr rg + fcn 'RETURN without GOSUB' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The LET command which is the default if no token begins a statement cmd_let jmp SNERROR ; not yet implemented @@ -1766,6 +1853,8 @@ defcmd 'RUN',run defcmd 'GOTO',goto defcmd 'GOSUB',gosub + defcmd 'RETURN',return + defcmd 'POP',pop defcmd '-',minus,SNERROR primarydict cmdtab secondarydict functab