changeset 119:a6a53e5c04bd

Make a call stack implementation that is more complete and maybe cleaner.
author William Astle <lost@l-w.ca>
date Fri, 29 Dec 2023 01:40:39 -0700
parents cddbe8bc07e5
children 1c1a0150fdda
files Makefile src/error.s src/init.s src/lwbasic.s src/progctrl.s src/stack.s src/vars.s
diffstat 7 files changed, 90 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Thu Dec 28 22:00:32 2023 -0700
+++ b/Makefile	Fri Dec 29 01:40:39 2023 -0700
@@ -1,7 +1,7 @@
 .PHONY: all
 all: bin/lwbasic.rom bin/lwbasic-coco2b.rom bin/lwbasic-coco3.rom bin/coco.zip bin/coco2.zip bin/coco2b.zip bin/coco3.zip
 
-lwb_srcs := consscr.s defs.s error.s expr.s fps.s genio.s init.s int.s interp.s irq.s keyb.s keywords.s miscdata.s number.s print.s progctrl.s token.s vars.s
+lwb_srcs := consscr.s defs.s error.s expr.s fps.s genio.s init.s int.s interp.s irq.s keyb.s keywords.s miscdata.s number.s print.s progctrl.s stack.s token.s vars.s
 lwb_srcs := $(addprefix src/,$(lwb_srcs))
 
 bin/lwbasic.rom: src/lwbasic.s $(lwb_srcs)
--- a/src/error.s	Thu Dec 28 22:00:32 2023 -0700
+++ b/src/error.s	Fri Dec 29 01:40:39 2023 -0700
@@ -27,7 +27,7 @@
                 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
+                sts cstackptr
                 jmp immediate                   ; go back to immediate mode
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Error messages
--- a/src/init.s	Thu Dec 28 22:00:32 2023 -0700
+++ b/src/init.s	Fri Dec 29 01:40:39 2023 -0700
@@ -214,7 +214,7 @@
                 leax -200,x                     ; allocate 200 bytes of string space
                 stx freetop                     ; save top of free memory
                 clr ,-x                         ; make a hole for the "end of call stack" flag
-                stx stackptr                    ; save the new call stack pointer
+                stx cstackptr                   ; 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
--- a/src/lwbasic.s	Thu Dec 28 22:00:32 2023 -0700
+++ b/src/lwbasic.s	Fri Dec 29 01:40:39 2023 -0700
@@ -44,6 +44,7 @@
                 include genio.s
                 include interp.s
                 include progctrl.s
+                include stack.s
                 include print.s
                 include error.s
                 include expr.s
--- a/src/progctrl.s	Thu Dec 28 22:00:32 2023 -0700
+++ b/src/progctrl.s	Fri Dec 29 01:40:39 2023 -0700
@@ -48,7 +48,7 @@
                 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
+                sts cstackptr                   ; reset pointer for call stack
                 clr contstmt                    ; clear "CONT" destination
                 clr contstmt+1
                 jmp ,x                          ; return to caller
@@ -106,7 +106,7 @@
 ; 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
+                jsr cstack_alloc                ; make a stack frame
                 ldx curline                     ; save current line pointer
                 stx ,u
                 ldx inputptr                    ; save current input pointer
@@ -121,11 +121,11 @@
 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
+                jsr cstack_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
+cmd_return0     jsr cstack_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
@@ -135,54 +135,6 @@
                 stx curline
                 ldx 2,u                         ; get back saved input pointer
                 stx inputptr
-cmd_return2     bsr callstack_pop               ; clean up call stack
+cmd_return2     jsr cstack_popto                ; 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/stack.s	Fri Dec 29 01:40:39 2023 -0700
@@ -0,0 +1,80 @@
+                *pragmapush list
+                *pragma list
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Call stack management.
+;
+; The call stack exists above the interpreter stack. The call stack consists of a sequence of stack records of varying
+; sizes which start with an 8 bit length field, then the payload, which will usually begin with an 8 bit type code. The
+; bottom of the call stack is stored in cstackptr. The frame sizes must all be <= 125 bytes since a 2 byte frame header
+; will be included in the sizing
+;
+; Allocate a call stack frame of size B and type A. Return a pointer to the payload area of the allocated entry in U
+cstack_alloc    pshs a                          ; save type flag
+                addb #2                         ; account for the frame header
+                negb                            ; so we can subtract from pointers
+                leax ,s                         ; point to current stack pointer
+                leas b,s                        ; move the stack pointer down
+                leau ,s                         ; point to new stack pointer for data copy
+cstack_alloc0   lda ,x+                         ; copy a byte in the stack down
+                sta ,y+
+                cmpx cstackptr                  ; have we reached the original call stack?
+                bne cstack_alloc0               ; brif not
+                negb                            ; get back original size
+                stu cstackptr                   ; save the new bottom of the call stack
+                stb ,u++                        ; save size in entry
+                puls a                          ; get back type flag
+                sta -11,u                       ; put it in the stack frame
+cstack_pop0     rts
+; Clear call stack without resetting the interpreter stack
+cstack_clear    ldd stringtab                   ; point to the bottom of the string space
+                subd #1                         ; make room for a NULL stack frame marking the top of the stack
+cstack_pop1     tfr d,u                         ; save new call stack pointer - destination of copy
+                subd cstackptr                  ; subtract out the current call stack pointer
+                beq cstack_clear1               ; brif the stack is already empty
+                tfr d,y                         ; save byte count to move in counter
+                sts ,--s                        ; save the current stack pointer - end of copy address
+                ldx cstackptr                   ; get current call stack pointer - source of copy
+                stu cstackptr                   ; save new call stack pointer
+                clr ,u                          ; put the NULL frame at the top of the call stack
+cstack_clear0   lda ,-x                         ; copy a byte upward
+                sta ,-u
+                cmpx ,s                         ; have we reached the end of the copy?
+                bne cstack_clear0               ; brif not
+                leas ,u                         ; reset the main stack to the moved location
+cstack_clear1   rts
+; Pop the entry pointed to by U off the call stack and all entries below it
+cstack_popto    ldb ,u                          ; get size of the current entry
+                bra cstack_popto0               ; go do the rest of the processing                
+; Pop the first entry off the call stack
+cstack_pop      ldu cstackptr                   ; point to the call stack
+                ldb ,u                          ; get size of this frame
+                beq cstack_pop0                 ; brif there's nothing to pop - do nothing
+cstack_popto0   leau b,u                        ; point to the next entry
+                tfr u,d                         ; put pointer where we need it
+                bra cstack_pop1                 ; go adjust the call stack
+; Search for the first matching stack frame where the second byte matches the value in A. Enter
+; at cstack_searchnext to search from the entry following the entry pointed to by X. Return with
+; C set if not found.
+cstack_search   ldx cstackptr                   ; point to the call stack
+cstack_search0  ldb ,x                          ; get size of this entry
+                bne cstack_search2              ; brif not the end of the call stack
+cstack_search1  comb                            ; clear C for not found
+                rts
+cstack_search2  cmpa 1,x                        ; does the type match?
+                bne cstack_search3              ; brif not
+                rts                             ; return result (C is clear from CMPA match)
+cstack_searchnext
+                ldb ,x                          ; get length of this frame
+                beq cstack_search1              ; brif we're already at the end of the stack
+cstack_search3  abx                             ; move to next entry
+                bra cstack_search0              ; check if this one matches
+; Point X to the first entry on the call stack
+cstack_first    ldx cstackptr                   ; point to stack entry
+                lda ,x++                        ; set Z set if end of stack, move to payload
+                rts
+; Point X to the next entry on the call stack
+cstack_next     ldb -2,x                        ; get length of this entry
+                abx                             ; adjust to next entry payload
+                ldb -2,x                        ; set Z if end of stack
+                rts
+                *pragmapop list
--- a/src/vars.s	Thu Dec 28 22:00:32 2023 -0700
+++ b/src/vars.s	Fri Dec 29 01:40:39 2023 -0700
@@ -10,7 +10,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)
+cstackptr       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