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