changeset 73:2d52cd154ed1

Split some code into separate files for easier management 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 one of the split.
author William Astle <lost@l-w.ca>
date Sun, 06 Aug 2023 00:12:29 -0600
parents f492fa6f6dc8
children e74d00ac6b79
files src/consscr.s src/defs.s src/init.s src/irq.s src/keyb.s src/lwbasic.s src/vars.s
diffstat 7 files changed, 964 insertions(+), 935 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/consscr.s	Sun Aug 06 00:12:29 2023 -0600
@@ -0,0 +1,85 @@
+                *pragmapush list
+                *pragma list
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Console screen output driver
+;
+; Clear screen
+console_clear   ldb #0x60                       ; VDG space character
+                ldx #textscreen                 ; point to text screen
+                stx console_curptr              ; set cursor pointer to start of screen
+console_clear0  stb ,x+                         ; blank a character
+                cmpx #textscreen+0x200          ; end of screen?
+                blo console_clear0              ; brif not
+                rts
+; Output NUL terminated string
+console_outstr0 bsr console_outchr              ; output the character
+console_outstr  lda ,x+                         ; get byte from string
+                bne console_outstr0             ; brif not end of string
+                rts
+; Output NUL terminated string followed by a newline
+console_outstrn bsr console_outstr              ; output the string
+                ; fallthrough intentional
+; Output a newline (CR LF)
+console_outnl   lda #0x0d                       ; do the CR
+                bsr console_outchr
+                lda #0x0a                       ; do the LF
+                ; fallthrough intentional
+; Output a single character to the screen; enter with character in A
+console_outchr  pshs d,x                        ; save registers
+                ldx console_curptr              ; get current cursor pointer
+                cmpa #0x20                      ; printable character?
+                blo console_outchr5             ; brif not
+                tsta                            ; is it a graphics block?
+                bmi console_outchr1             ; brif so - don't do anything to it
+                cmpa #0x40                      ; number or most non-alpha characters?
+                blo console_outchr0             ; brif so - will need to flip bit 6
+                cmpa #0x60                      ; upper case?
+                blo console_outchr1             ; brif so - don't need to do anything to it
+                anda #0xdf                      ; clear bit 5 of lower case; moves it to bottom of character set
+console_outchr0 eora #0x40                      ; flip bit 6 - the "inversion" bit
+console_outchr1 sta ,x+                         ; stick it on screen
+console_outchr2 stx console_curptr              ; save new cursor pointer
+                cmpx #textscreen+0x200          ; end of screen?
+                blo console_outchr4             ; brif not
+                leax -32,x                      ; move pointer back one line
+                stx console_curptr
+                ldx #textscreen                 ; point to start of screen
+console_outchr3 ldd 32,x                        ; get bytes from next line
+                std ,x++                        ; stick them here
+                cmpx #textscreen+0x1e0          ; at last row?
+                blo console_outchr3             ; brif not
+                ldb #0x60                       ; space character for VDG screen
+                bsr console_clear0              ; blank out last row (borrowing screen clear loop)
+console_outchr4 puls d,x,pc                     ; restore registers and return
+console_outchr5 cmpa #0x0c                      ; form feed?
+                bne console_outchr6             ; brif not
+                bsr console_clear               ; clear screen
+                puls d,x,pc                     ; restore registers and return
+console_outchr6 cmpa #0x0d                      ; carriage return?
+                bne console_outchr7             ; brif not
+                ldb console_curptr+1            ; get current screen pointer LSB
+                andb #0xe0                      ; reset offset to start of line
+                stb console_curptr+1            ; save new pointer LSB
+                puls d,x,pc                     ; restore registers and return
+console_outchr7 cmpa #0x0a                      ; line feed?
+                bne console_outchr8             ; brif not
+                ldx console_curptr              ; get cursor pointer
+                leax 32,x                       ; move it forward exactly one line
+                bra console_outchr2             ; go update stuff check for scroll
+console_outchr8 cmpa #0x08                      ; backspace?
+                bne console_outchr9             ; brif not
+                cmpx #textscreen                ; at start of screen?
+                beq console_outchr4             ; brif so - backspace does nothing
+                leax -1,x                       ; back up pointer (backspace is non-destructive)
+                bra console_outchr2             ; go update pointers, etc.
+console_outchr9 cmpa #0x09                      ; TAB character?
+                bne console_outchr4             ; brif not
+                ldb console_curptr              ; get LSB of pointer
+                andb #7                         ; 8 space tabs - only keep low 3 bits
+                lda #0x60                       ; space character (tab is destructive)
+console_outchra sta ,x+                         ; put a space out
+                incb                            ; bump counter
+                cmpb #8                         ; at next tab stop?
+                blo console_outchra             ; brif not
+                bra console_outchr2             ; go update details and check for scroll
+                *pragmapop list
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/defs.s	Sun Aug 06 00:12:29 2023 -0600
@@ -0,0 +1,208 @@
+                *pragmapush list
+                *pragma list
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Various constants
+console_curdel  equ 10                          ; delay between cursor blink cycles
+keyb_bufflen    equ 64                          ; keyboard ring buffer length
+keyb_repdeli    equ 40                          ; ticks before initial repeat (2/3 s)
+keyb_repdelr    equ 6                           ; 10 repeats per second
+keyb_caps       equ 0x80                        ; capslock enabled
+keyb_alt        equ 0x04                        ; alt pressed
+keyb_ctrl       equ 0x02                        ; ctrl pressed
+keyb_shift      equ 0x01                        ; shift pressed
+linebuffsize    equ 0x100                       ; the line input buffer (256 bytes)
+stringstacknum  equ 20                          ; number of entries on the anonymous string descriptor stack
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Data structure used for calculations. Calculations are handled via structurs called value accumulators. A value
+; accumulator consists of a data type flag (at the end of the structure) and a data area whose layout varies based
+; on the actual data type. The layouts for each value type are described below.
+;
+; A value type that is NULL (not set to anything) has type 0 (valtype_none) and the rest should be zero.
+;
+; A value accumulator has the following structure for floating point:
+; Offset        Length          Contents
+; 0             1               fp exponent
+; 1             4               fp mantissa
+; 5             1               fp sign
+; 6             1               value type
+;
+; A value accumulator has the following structure for integers:
+; Offset        Length          Contents
+; 0             1               *unsued*
+; 1             4               integer value (two's complement)
+; 5             1               *unused*
+; 6             1               value type
+;
+; A value accumulator has the following structure for a string:
+; Offset        Length          Contents
+; 0             2               string length
+; 2             2               *reserved for string data pointer expansion, must be zero*
+; 4             2               string data pointer
+; 6             1               value type
+;
+; Value type constants
+valtype_none    equ 0                           ; unknown value type
+valtype_int     equ 1                           ; integer (32 bit) value (signed)
+valtype_float   equ 2                           ; float type (40 bit) value
+valtype_string  equ 3                           ; string type (16 bit length, 16(32) bit data pointer
+; Value accumulator structure definitions
+val.type        equ 6                           ; value type offset
+val.fpexp       equ 0                           ; fp exponent offset
+val.fpmant      equ 1                           ; fp mantissa offset
+val.fpsign      equ 5                           ; fp sign offset
+val.int         equ 1                           ; integer offset
+val.strlen      equ 0                           ; string length offset
+val.strptr      equ 4                           ; string data pointer (low word)
+val.size        equ 7                           ; size of a value accumulator
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+                ifdef COCO3
+; GIME INIT0
+GIME_COCO       equ 0x80                        ; Set for coco2 compatible mode (video display)
+GIME_MMUEN      equ 0x40                        ; Set to enable MMU
+GIME_IEN        equ 0x20                        ; GIME IRQ enable
+GIME_FEN        equ 0x10                        ; GIME FIRQ enable
+GIME_FExx       equ 0x08                        ; Enable constant RAM at 0xFExx (comes from block 0x3f)
+GIME_SCS        equ 0x04                        ; Set to enable standard SCS (switches 0xFF5x)
+GIME_ROME16     equ 0x00                        ; 16K internal, 16K external ROM mode
+GIME_ROME32     equ 0x03                        ; 32K external ROM
+GIME_ROMI32     equ 0x02                        ; 32K internal ROM
+; GIME INIT1
+GIME_TMRFAT     equ 0x20                        ; TIMER ticks approx every 279.365 ns
+GIME_TMRSLOW    equ 0x00                        ; TIMER ticks approx every 63.695 µs
+GIME_TASK0      equ 0x00                        ; MMU task 0
+GIME_TASK1      equ 0x01                        ; MMU task 1
+; GIME interrupt enable/status bits
+GIME_ITIMER     equ 0x20                        ; TIMER interrupt (timer reaches 0)
+GIME_IHBORD     equ 0x10                        ; HSYNC interrupt (falling edge)
+GIME_IVBORD     equ 0x08                        ; VSYNC interrupt (falling edge)
+GIME_ISERIAL    equ 0x04                        ; Falling edge of signal on pin 4 of serial port
+GIME_IKEYBOARD  equ 0x02                        ; Interrupt if a 0 bit appears on bits 6-0 of PIA0.DA
+GIME_ICART      equ 0x01                        ; Interrupt on falling edge of pin 8 of cartridge port
+; GIME VMODE
+GIME_BP         equ 0x80                        ; enable bit plane mode
+GIME_BPI        equ 0x20                        ; colour burst phase inversion (composite output only)
+GIME_MONO       equ 0x10                        ; disable colour burst (composite output only)
+GIME_H50        equ 0x08                        ; set to 50Hz operation
+GIME_LPR1       equ 0x00                        ; one line per row
+GIME_LPR2       equ 0x02                        ; two lines per row (also works on graphics)
+GIME_LPR8       equ 0x03                        ; 8 lines per row
+GIME_LPR9       equ 0x04                        ; 9 lines per row
+GIME_LPR10      equ 0x05                        ; 10 lines per row
+GIME_LPR11      equ 0x06                        ; 11 lines per row
+GIME_LPRINF     equ 0x07                        ; "infinite" lines per row
+; GIME VRES
+GIME_LPF192     equ 0x00                        ; 192 lines on screen
+GIME_LPF200     equ 0x40                        ; 200 lines on screen (actually 199 due to hardware bug)
+GIME_LPF225     equ 0x60                        ; 225 lines on screen
+GIME_BPR16      equ 0x00                        ; 16 bytes per row
+GIME_BPR20      equ 0x04                        ; 20 bytes per row
+GIME_BPR32      equ 0x08                        ; 32 bytes per row
+GIME_BPR40      equ 0x0c                        ; 40 bytes per row
+GIME_BPR64      equ 0x10                        ; 64 bytes per row
+GIME_BPR80      equ 0x14                        ; 80 bytes per row
+GIME_BPR128     equ 0x18                        ; 128 bytes per row
+GIME_BPR160     equ 0x1c                        ; 160 bytes per row
+GIME_TXT32      equ 0x00                        ; 32 characters per row
+GIME_TXT40      equ 0x04                        ; 40 characters per row
+GIME_TXT64      equ 0x10                        ; 64 characters per row
+GIME_TXT80      equ 0x14                        ; 80 characters per row
+GIME_BPP1       equ 0x00                        ; 1 bit per pixel
+GIME_BPP2       equ 0x01                        ; 2 bits per pixel
+GIME_BPP4       equ 0x02                        ; 4 bits per pixel
+GIME_TXTATTR    equ 0x01                        ; text attributes enabled
+                endc
+                ifdef COCO3
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Stuff on the fixed memory page
+                org 0xfe00
+                rmb 0xed                        ; unused
+INT.FLAG        rmb 1                           ; validity flag
+INT.SWI3        rmb 3                           ; SWI3 bounce vector
+INT.SWI2        rmb 3                           ; SWI2 bounce vector
+INT.FIRQ        rmb 3                           ; FIRQ bounce vector
+INT.IRQ         rmb 3                           ; IRQ bounce vector
+INT.SWI         rmb 3                           ; SWI bounce vector
+INT.NMI         rmb 3                           ; NMI bounce vector
+                endc
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Hardware definitions for the I/O page
+                org 0xff00
+PIA0            equ *                           ; Keyboard PIA
+PIA0.DA         rmb 1                           ; PIA0 data/direction A
+PIA0.CA         rmb 1                           ; PIA0 control A
+PIA0.DB         rmb 1                           ; PIA0 data/direction B
+PIA0.CB         rmb 1                           ; PIA0 control B
+                rmb 28                          ; mirror images of PIA0
+PIA1            equ *                           ; DA/misc stuff
+PIA1.DA         rmb 1                           ; PIA1 data/direction A
+PIA1.CA         rmb 1                           ; PIA1 control A
+PIA1.DB         rmb 1                           ; PIA1 data/direction B
+PIA1.CB         rmb 1                           ; PIA1 control B
+                rmb 28                          ; mirror images of PIA1
+                rmb 16                          ; SCS/Disk controller
+                rmb 16                          ; second half of SCS area
+                rmb 32                          ; miscelaneous hardware
+                ifdef COCO3
+                rmb 16                          ; *reserved* (unused but the GIME drives them)
+GIME.INIT0      rmb 1                           ; basic GIME system config
+GIME.INIT1      rmb 1                           ; MMU task and timer rate
+GIME.IRQ        rmb 1                           ; GIME IRQ enable/status register
+GIME.FIRQ       rmb 1                           ; GIME FIRQ enable/status register
+GIME.TIMER      rmb 2                           ; GIME programmable timer
+                rmb 2                           ; *reserved*
+GIME.VMODE      rmb 1                           ; GIME video mode setting
+GIME.VRES       rmb 1                           ; GIME video resolution setting
+                rmb 1                           ; *reserved* (used for MMU expansion on some memory boards)
+GIME.BORDER     rmb 1                           ; GIME border colour
+GIME.VSCROLL    rmb 1                           ; vertical scroll offset register/VDG screen mode variation
+GIME.VOFFSET    rmb 2                           ; address of video memory (8 byte increments)
+GIME.HOFFSET    rmb 1                           ; horizontal scroll offset
+GIME.MMU        equ *                           ; MMU registers (two tasks)
+GIME.MMU0       rmb 8                           ; MMU task 0
+GIME.MMU1       rmb 8                           ; MMU task 1
+GIME.PALETTE    rmb 16                          ; Palette registers
+                else
+                rmb 64                          ; unused on Coco 1/2 (GIME on Coco 3)
+                endc
+SAMREG          equ *                           ; the SAM configuration register
+SAM.V0CLR       rmb 1                           ; SAM video mode bits
+SAM.V0SET       rmb 1
+SAM.V1CLR       rmb 1
+SAM.V1SET       rmb 1
+SAM.V2CLR       rmb 1
+SAM.V2SET       rmb 1
+SAM.F0CLR       rmb 1                           ; SAM screen address bits
+SAM.F0SET       rmb 1
+SAM.F1CLR       rmb 1
+SAM.F1SET       rmb 1
+SAM.F2CLR       rmb 1
+SAM.F2SET       rmb 1
+SAM.F3CLR       rmb 1
+SAM.F3SET       rmb 1
+SAM.F4CLR       rmb 1
+SAM.F4SET       rmb 1
+SAM.F5CLR       rmb 1
+SAM.F5SET       rmb 1
+SAM.F6CLR       rmb 1
+SAM.F6SET       rmb 1
+SAM.P1CLR       rmb 1                           ; SAM "page 1" selection (or extra memory type flag)
+SAM.P1SET       rmb 1
+SAM.R0CLR       rmb 1                           ; SAM R0 bit (address dependent speedup, not used on Coco3)
+SAM.R0SET       rmb 1
+SAM.R1CLR       rmb 1                           ; SAM R1 bit (full speedup/coco 3 speedup)
+SAM.R1SET       rmb 1
+SAM.M0CLR       rmb 1                           ; SAM M0/M1 bits (memory type, not used on Coco3)
+SAM.M0SET       rmb 1
+SAM.M1CLR       rmb 1
+SAM.M1SET       rmb 1
+SAM.TYCLR       rmb 1                           ; force ROM mode (map type 0)
+SAM.TYSET       rmb 1                           ; set RAM mode (map type 1)
+                rmb 18                          ; *MPU reserved*
+CPU.SWI3        rmb 2                           ; CPU SWI3 vector
+CPU.SWI2        rmb 2                           ; CPU SWI2 vector
+CPU.FIRQ        rmb 2                           ; CPU FIRQ vector
+CPU.IRQ         rmb 2                           ; CPU IRQ vector
+CPU.SWI         rmb 2                           ; CPU SWI vector
+CPU.NMI         rmb 2                           ; CPU NMI vector
+CPU.RESET       rmb 2                           ; CPU RESET/startup vector
+                *pragmapop list
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/init.s	Sun Aug 06 00:12:29 2023 -0600
@@ -0,0 +1,268 @@
+                *pragmapush list
+                *pragma list
+START           orcc #0x50                      ; make sure interrupts are disabled if we come here in an unusual way
+                ifdef COCO3
+                ldu #gime_inite                 ; point to end of GIME initializer
+                ldx #GIME.INIT0+(gime_inite-gime_init) ; point to end of GIME registers
+                ldb #gime_inite-gime_init       ; number of bytes to transfer
+initc0          lda ,-u                         ; copy byte to GIME (count down so we init MMU before turning it on)
+                sta ,-x
+                decb                            ; done?
+                bne initc0                      ; brif not
+                endc
+                ldd #0xff34                     ; initizer for below
+                tfr a,dp                        ; set DP to I/O page
+                setdp 0xff                      ; tell assembler about DP value
+                clr PIA0.CA                     ; set PIA0 A to direction mode
+                clr PIA0.CB                     ; set PIA0 B to direction mode
+                clr PIA0.DA                     ; set PIA0 A to all inputs (comparator, keyboard rows)
+                sta PIA0.DB                     ; set PIA0 B to all outputs (keyboard columns)
+                stb PIA0.CA                     ; set PIA0 A to data mode, interrupt disabled, MUX to source 0
+                stb PIA0.CB                     ; set PIA0 B to data mode, interrupt disabled, MUX to source 0
+                clr PIA1.CA                     ; set PIA1 A to direction mode
+                clr PIA1.CB                     ; set PIA1 B to direction mode
+                deca                            ; set PIA1 A bits 7-1 output (DAC, RS232), 0 input (cassette)
+                sta PIA1.DA
+                lda #0xf8                       ;* set PIA1 B bits 7-3 output (VDG stuff), 2-0 input (single bit sound,
+                sta PIA1.DB                     ;* RS232 input, ram size input)
+                stb PIA1.CA                     ; set PIA1 A to data mode, interrupt disabled, cassette motor off
+                stb PIA1.CB                     ; set PIA1 B to data mode, interrupt disabled, sound off
+                lda #2                          ; set RS232 output to "marking" (stop bit)
+                sta PIA1.DA
+                lda #16                         ; clear 16 SAM register bits
+                ldu #SAMREG                     ; point to SAM register bits
+init0           sta ,u++                        ; clear SAM bit
+                deca                            ; done all?
+                bne init0                       ; brif not
+; set the SAM to point to the text screen, which the code will handle at any
+; arbitrary 512 byte aligned address in memory
+                ifne (textscreen)&0x200
+                sta SAM.F0SET
+                endc
+                ifne (textscreen)&0x400
+                sta SAM.F1SET
+                endc
+                ifne (textscreen)&0x800
+                sta SAM.F2SET
+                endc
+                ifne (textscreen)&0x1000
+                sta SAM.F3SET
+                endc
+                ifne (textscreen)&0x2000
+                sta SAM.F4SET
+                endc
+                ifne (textscreen)&0x4000
+                sta SAM.F5SET
+                endc
+                ifne (textscreen)&0x8000
+                sta SAM.F6SET
+                endc
+                ifdef COCO2B
+; The following SAM configuration sequence is different from the one in the usual
+; one used by the earlier models of the Coco because the Coco2B has the '785 variant
+; of the SAM instead of the '783 variant. The '785 variant supports 16Kx4 RAMs which
+; are used in Coco2B systems. Hence why there is a different version of this ROM
+; just for the Coco2B.
+                clr PIA0.DB                     ; strobe RAM size low
+                ldb #4                          ; is input low?
+                bitb PIA1.DB
+                beq init1                       ; brif not
+                sta SAM.M0SET                   ; program SAM for 16Kx4 RAMs
+                sta SAM.P1SET
+                skip2
+init1           sta SAM.M1SET                   ; program SAM for 64Kx1 RAMs
+                else
+                ifndef COCO3
+; Detect the installed memory size so the SAM ('783 variant) can be correctly
+; programmed for the installed memory. Note that this sequence is replaced with
+; a different one for the Coco2B which has the '785 variant of the SAM.
+                ldb #0xff                       ; strobe RAM size high
+                stb PIA0.DB
+                ldb #4                          ; mask for ram size check
+                bitb PIA1.DB                    ; is the bit set on ram size input?
+                beq init2                       ; brif not - 4Kx1 RAMs
+                sta PIA0.DB                     ; clear RAM size output to see what happens (A is 0 from above)
+                bitb PIA1.DB                    ; is it set now?
+                beq init1                       ; brif not - 64Kx1 RAMs
+                leau -2,u                       ; adjust pointer so we set the other RAM size bit for the SAM (16Kx1)
+init1           sta -3,u                        ; set M0 (16Kx1) or M1 (64Kx1)
+                endc
+                endc
+init2           tfr a,dp                        ; set DP to bottom of memory (A is 0 from above)
+                setdp 0                         ; tell assembler about it
+                lds #textscreen                 ; put the stack just below the text screen
+                ifdef COCO3
+; Check if we need to do a ROM/RAM copy, which will happen if the interrupt vectors are
+; not flagged valid OR the reset vector isn't valid
+                ldb INT.FLAG                    ; are the bounce vectors valid?
+                cmpb #0x55
+                bne initc4                      ; brif not - do ROM/RAM copy
+                ldb RSTFLG                      ; is reset vector valid?
+                bne initc2                      ; brif not - check secondary location
+                ldx RSTVEC                      ; get reset vector
+                ldb ,x                          ; is it valid?\
+                cmpb #0x12
+                bne initc2                      ; brif not
+initc1          jmp ,x                          ; transfer control to warm start routine
+initc2          clr GIME.MMU0                   ; check again with block 0 in the direct page
+                ldb RSTFLG                      ; get new RSTFLG
+                cmpb #0x55                      ; valid?
+                bne initc3                      ; brif not
+                ldx RSTVEC                      ; get new RSTVEC
+                ldb ,x                          ; is it valid?
+                cmpb #0x12
+                beq initc1                      ; brif so - transfer control
+initc3          ldb #0x38                       ; restore MMU
+                stb GIME.MMU0
+initc4          ldx #initc6                     ; point to helper
+                ldu #textscreen                 ; point to text screen
+                ldb #initc7-initc6              ; bytes to copy
+initc5          lda ,x+                         ; copy byte
+                sta ,u+
+                decb                            ; done?
+                bne initc5                      ; brif not
+                ldu #0x8000                     ; point to start of ROM
+                jmp textscreen                  ; transfer control to helper in RAM
+initc6          sta SAM.TYCLR                   ; drop to ROM mode
+                pulu d,x,y,s                    ; grab 8 bytes
+                sta SAM.TYSET                   ; go to RAM mode
+                pshu d,x,y,s                    ; stick the bytes in RAM
+                leau 8,u                        ; move to next 8 bytes
+                cmpu #0xfe00                    ; end of stuff to copy?
+                blo initc6                      ; brif not
+                jmp initc7                      ; go back to mainline
+initc7          lds #textscreen                 ; reset stack to somewhere safe
+                lda #0x12                       ; activate ROM warm start handler
+                sta warmstart
+                ldx #INT.FLAG                   ; point to bounce vector destination
+                ldu #int_init                   ; point to initializer for bounce vectors
+                ldb #int_inite-int_init         ; number of bytes to copy
+initc8          lda ,u+                         ; copy byte
+                sta ,x+
+                decb                            ; done?
+                bne initc8                      ; brif not
+; now recheck for warm start in case ROM/RAM copy made things valid
+                endc
+                ldb RSTFLG                      ; is the reset vector valid?
+                cmpb #0x55
+                bne coldstart                   ; brif not - do cold start
+                ldx RSTVEC                      ; get warm start routine pointer
+                ldb ,x                          ; does it start with NOP?
+                cmpb #0x12
+                bne coldstart                   ; brif not - do cold start
+                jmp ,x                          ; transfer control to warm start routine
+                ifdef COCO3
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; GIME register initializer
+gime_init       fcb GIME_COCO|GIME_MMUEN|GIME_FExx|GIME_SCS|GIME_ROMI32 ; enable MMU, SCS, constant page, internal ROM
+                fcb GIME_TASK0                  ; use MMU task 0
+                fcb 0                           ; do not enable IRQ sources
+                fcb 0                           ; do not enable FIRQ sources
+                fdb 0xfff                       ; set timer to max value
+                fdb 0                           ; *reserved placeholder*
+                fcb 0,0,0,0                     ; SG4 screen settings with black border
+                fcb 0x0f,0xe0,0x00,0x00         ; (puts screen in bottom 64K of memory)
+                fcb 0x38,0x39,0x3a,0x3b         ; MMU task 0 (bottom of top 64K of RAM)
+                fcb 0x3c,0x3d,0x3e,0x3f         ; (ROM shadow must be in 3c...3f)
+                fcb 0x38,0x39,0x3a,0x3b         ; MMU task 1 (copy of task 0)
+                fcb 0x3c,0x3d,0x3e,0x3f
+                fcb 18,54,9,36,63,27,45,38      ; palette values (RGB)
+                fcb 0,18,0,63,0,18,0,38
+gime_inite      equ *
+int_init        fcb 0x55                        ; vectors valid flag
+                jmp SW3VEC                      ; bounce to stock ROM compatibility vector
+                jmp SW2VEC                      ; bounce to stock ROM compatibility vector
+                jmp FRQVEC                      ; bounce to stock ROM compatibility vector
+                jmp IRQVEC                      ; bounce to stock ROM compatibility vector
+                jmp SWIVEC                      ; bounce to stock ROM compatibility vector
+                jmp NMIVEC                      ; bounce to stock ROM compatibility vector
+int_inite       equ *
+                endc
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Cold start handling
+coldstart       ldx #dpstart                    ; point to start of direct page
+                ldd #0                          ; set up for blanking
+coldstart0      std ,x++                        ; blank a couple of bytes
+                cmpx #textscreen                ; end of low memory?
+                blo coldstart0                  ; brif not
+                ifndef COCO3
+; This is the memory size detection sequence. This runs through memory starting at the bottom of memory
+; and stops when it reaches something that can't be modified successfully. This is basically the same
+; algorithm used by the stock ROM. It takes less space than doing a more pointed set of probes. The end
+; result will be X pointing to the byte one below the top of RAM. This is intentional to ensure there
+; is one writeable byte at the top of string space. Note that X will point to the byte after the end
+; of the text screen when we get here.
+                ldx #heapstart                  ; point to start of heap
+coldstart1      lda 2,x                         ; get original value at test location
+                coma                            ; invert all bits
+                sta 2,x                         ; write it to the memory location
+                cmpa 2,x                        ; did it take?
+                bne coldstart2                  ; brif not
+                com 2,x                         ; restore memory byte
+                leax 1,x                        ; move pointer forward
+                bra coldstart1                  ; go check next byte
+                else
+; For the Coco3, we do not need to concern ourselves about where the top actual memory is so we don't
+; bother doing a memory scan in the default 64K memory map. Because we always run from RAM, we can actually
+; set the top of memory to the actual top of the 32K space without having to ensure there is an extra byte
+; available above the string space.
+                ldx #ROMSTART-1                 ; point to top of memory
+                endc
+coldstart2      stx memtop                      ; save absolute top of memory
+                stx memsize                     ; save top of unreserved memory
+                stx stringtab                   ; mark string space as empty
+                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
+                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
+                clr ,x+                         ; put a NULL pointer to mark end of program
+                clr ,x+
+                stx vartab                      ; put start of integer variables at end of program
+                stx objecttab                   ; also put the start of large objects there
+                stx freestart                   ; mark the start of free memory
+                lda #keyb_caps                  ; enable caps lock but disable all other shift states
+                sta keyb_flags
+                ldx #warmstart                  ; set up warm start handler
+                stx RSTVEC
+                lda #0x55                       ; activate warm start handler
+                sta RSTFLG
+                ldd #0x7e3b                     ; opcodes for JMP extended and RTI
+                ldx #irqhandler                 ; enable IRQ handler with a JMP at the vector
+                sta IRQVEC
+                stx IRQVEC+1
+                sta FRQVEC                      ; initialize FIRQ handler with JMP
+                ldx #firqhandler
+                stx FRQVEC+1
+                stb NMIVEC                      ; initialize NMI to RTI
+                stb SW3VEC                      ; initialize SWI3 to RTI
+                stb SW2VEC                      ; initialize SWI2 to RTI
+                stb SWIVEC                      ; initialize SWI to RTI
+                ldx #greeting                   ; display greeting
+                jsr console_outstr
+                bra warmstartb                  ; finish up initialization
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Warm start handling
+                ifdef COCO3
+warmstart       fcb 0xff                        ; set to 0xff to force ROM/RAM copy on reset
+                else
+warmstart       nop                             ; flag warm start routine as valid
+                endc
+                jsr console_clear               ; clear screen
+                clr filenum                     ; reset I/O channel to the screen
+warmstartb      jsr keyb_reset                  ; reset the keyboard
+                lda #0x35                       ; enable VSYNC interrupt in PIA
+                sta PIA0.CB
+                andcc #0xaf                     ; enable interrupts at the cpu
+                jmp immediate                   ; go start immediate mode
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; System startup message
+; (start with form feed to clear screen; saves 2 bytes over 'jsr console_clear' in cold start)
+greeting        fcc '\fLWBASIC VERSION 2022.0\r\n'
+                fcc 'COPYRIGHT (C) 2022 BY LOST\r\n'
+                fcc 'WIZARD ENTERPRISES INC.\r\n'
+                fcn '\n'
+                *pragmapop list
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irq.s	Sun Aug 06 00:12:29 2023 -0600
@@ -0,0 +1,51 @@
+                *pragmapush list
+                *pragma nolist
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; IRQ handler
+;
+; Note that the interrupt flag in the PIA is cleared at the start of the interrupt handler. That means that if it takes
+; a long time to process this interrupt, or processing this interrupt was delayed somewhat, it is far less likely that
+; an interrupt gets missed. In that case, we may end up re-interrupting immediately on RTI, but it should reduce the
+; number of missed interrupts.
+irqhandler      lda PIA0.CB                     ; was it VSYNC?
+                bmi irqhandler0                 ; brif so
+                lda PIA0.DA                     ; clear HSYNC flag so we don't get stuck if it gets enabled
+                ifdef COCO3
+                lda GIME.IRQ                    ; clear GIME IRQ state flags
+                endc
+                rti
+irqhandler0     lda PIA0.DB                     ; clear VSYNC flag
+                clra                            ; make sure DP is pointing to the right place
+                tfr a,dp
+                lda console_blnkdel             ; is the cursor blinking?
+                beq irqhandler1                 ; brif not
+                dec console_blnkdel             ; time to cycle cursor?
+                bne irqhandler1                 ; brif not
+                lda #console_curdel             ; reset blink counter
+                sta console_blnkdel
+                lda [console_curptr]            ; get character at cursor
+                adda #0x10                      ; move to next colour
+                ora #0x8f                       ; force it to be a full 4x4 colour block
+                sta [console_curptr]            ; update cursor on screen
+irqhandler1     jsr keyb_read                   ; go handle the keyboard
+                rti
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; FIRQ handler
+;
+; This handler is present to prevent accidentally enabling the interrupt and thus hanging to system. It may seem to be
+; a waste of code space, but consider it a self defense situation.
+firqhandler     pshs a                          ; need a scratch register
+                ifdef COCO3
+                lda GIME.FIRQ                   ; clear GIME FIRQ state flags
+                endc
+                lda PIA1.DA                     ; clear interrupt flags
+                lda PIA1.DB
+                lda PIA1.CA                     ; disable interrupts to prevent system hang
+                anda #0xfe
+                sta PIA1.CA
+                lda PIA1.CB
+                anda #0xfe
+                sta PIA1.CB
+                puls a                          ; restore register
+                rti
+                *pragmapop list
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/keyb.s	Sun Aug 06 00:12:29 2023 -0600
@@ -0,0 +1,255 @@
+                *pragmapush list
+                *pragma list
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Check for BREAK; this needs to check the keyboard directly instead of just using the usual key fetching routine so
+; we don't interfere with keyboard buffering if BREAK isn't pressed. We also need to scan the keyboard directly for this
+; so we react even if the keyboard buffer is full. If BREAK is pressed, the keyboard buffer is emptied.
+breakcheck      lda #0xfb                       ; strobe column for BREAK
+                sta PIA0.DB
+                clra                            ; clear carry for no BREAK
+                lda PIA0.DA                     ; read rows
+                bita #0x40                      ; is BREAK down?
+                bne breakcheck0                 ; brif not - check for SHIFT-@
+                sync                            ; wait for interrupt to scan keyboard
+                bsr keyb_clearbuff              ; reset keyboard buffer
+                coma                            ; flag BREAK
+breakcheck1     rts
+breakcheck0     lda #0x7f                       ; check for SHIFT
+                sta PIA0.DB
+                lda PIA0.DA
+                bita #0x40                      ; shift?
+                bne breakcheck1                 ; brif not
+                lda #0xfe                       ; check for @
+                sta PIA0.DB
+                lda PIA0.DA
+                bita #1                         ; @?
+                bne breakcheck1                 ; brif not
+                bsr keyb_clearbuff              ; clear buffer
+breakcheck2     sync                            ; wait for keyboard to actually be scanned
+                bsr keyb_getkey
+                bcs breakcheck2                 ; brif no key down
+                bra breakcheck                  ; go do the break/pause check dance again
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Console keyboard input driver
+;
+; Reset the keyboard state, which means clearing the buffer and state flags
+keyb_reset      bsr keyb_clearbuff              ; clear keyboard buffer
+                lda keyb_flags                  ; reset keyboard state flags but keep capslock
+                anda #keyb_caps
+                sta keyb_flags
+                clr keyb_joystate               ; clear joystick button state
+                clr keyb_curscan                ; stop any keyboard repeating
+                ldx #0xffff                     ; mark all key state as "unpressed"
+                stx keyb_state
+                stx keyb_state+2
+                stx keyb_state+4
+                stx keyb_state+6
+                rts
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Empty the keyboard buffer
+keyb_clearbuff  ldx #keyb_buff                  ; point to start of buffer
+                stx keyb_buffr                  ; set both pointers to the start
+                stx keyb_buffw
+                rts                
+; Read character from keyboard ring buffer; return with C set if buffer empty; this doesn't actually need to have
+; interrupts disabled because the interrupt only ever updates the write pointer and then only to increase it. As a
+; result, worst case is that we don't detect the contents added to the buffer on this call and have to wait for the
+; next.
+keyb_getkey     pshs x                          ; save register
+                ldx keyb_buffr                  ; get read pointer
+                cmpx keyb_buffw                 ; same as write pointer?
+                bne keyb_getkey0                ; brif not - we have a result
+                coma                            ; set carry for empty buffer
+                puls x,pc                       ; restore register and return
+keyb_getkey0    lda ,x+                         ; get character from buffer
+                cmpx #keyb_buff+keyb_bufflen    ; did we run off end of buffer?
+                blo keyb_getkey1                ; brif not
+                ldx #keyb_buff                  ; reset to start
+keyb_getkey1    stx keyb_buffr                  ; save new read pointer
+                andcc #0xfe                     ; flag key retrieved
+                puls x,pc                       ; restore register and return
+; The PIA reading loop is specifically set up to NOT read PIA0.DB to avoid prematurely clearing the VSYNC interrupt flag
+; since that could lead to missing interrupts. Reading PIA0.DA will clear the HSYNC interrupt flag but that's less of a
+; problem because that interrupt is basically useless.
+;
+; As a note, doing the PIA read in a loop ends up using an extra 27 CPU cycles for the BCS instruction. However, it
+; saves 70 code bytes. The trade off seems worth it in this case.
+;
+; Once keyboard state is read, we do the following:
+;
+; * update the state of SHIFT, CTRL, ALT
+; * decode all other keys in a loop
+keyb_read0a     pshs b                          ; save flag bit
+                ldb a,y                         ; get state flag 
+                bitb #0x40                      ; did it change state?
+                bne keyb_read0d                 ; brif so
+                puls b,pc                       ; clean up and return
+keyb_read0d     andb #0xbf                      ; flag it as not changed
+                stb a,y
+                ldb a,u                         ; get current modifier state
+                eorb #0x40                      ; flip the state bit
+                stb a,u                         ; save new state flags
+                bitb #0x40                      ; Z set if not down
+                puls b                          ; get back flag bit
+                beq keyb_read0b                 ; brif key is pressed
+                comb                            ; invert bit flag
+                andb keyb_flags                 ; clear bit in flags
+                bra keyb_read0c                 ; finish up
+keyb_read0b     orb keyb_flags                  ; set the flag
+keyb_read0c     stb keyb_flags                  ; update flags
+                rts
+keyb_read       leas -9,s                       ; make temporary buffer
+                leay 1,s                        ; point to temporary state buffer
+                clra                            ;* set to 0xff with C clear; start by strobing no columns for joystick
+                deca                            ;* then rotate the 0 bit through to do the actual keyboard columns
+                ldu #keyb_state                 ; point to end of keyboard state buffer
+                sta PIA0.DB                     ; strobe no columns
+                ldb PIA0.DA                     ; get joystick button state
+                stb keyb_joystate               ; save it for later when needed
+                andb #0x7f                      ; mask off comparator (pretend "button" down)
+                stb ,s                          ; save button/comparator state mask
+                rola                            ; set up for first column
+keyb_read0      sta PIA0.DB                     ; set column strobe
+                ldb PIA0.DA                     ; read row data
+                eorb ,u+                        ; set bits if state changed
+                andb ,s                         ; mask off comparator and active buttons
+                stb ,y+                         ; save state change information
+                rola                            ; shift to next column
+                bcs keyb_read0                  ; brif we haven't done the last column
+                sta PIA0.DB                     ; reset column strobe to none
+                ldd #0xff00|keyb_shift
+                bsr keyb_read0a
+                ldd #0xfc00|keyb_ctrl
+                bsr keyb_read0a
+                ldd #0xfb00|keyb_alt
+                bsr keyb_read0a
+keyb_read3      ldd #0x0701                     ; initialize bit probe and counter
+keyb_read4      leay -1,y                       ; move pointers to next byte
+                leau -1,u
+keyb_read5      bitb ,y                         ; did this key change state?
+                bne keyb_read7                  ; brif so
+keyb_read6      adda #8                         ; adjust scan code
+                lslb                            ; shift bit probe
+                bpl keyb_read5                  ; brif we haven't done all bits
+                ldb ,y                          ; update state flags for this byte
+                eorb ,u
+                stb ,u
+                ldb #1                          ; reset bit probe
+                anda #0x07                      ; reset scan code
+                deca                            ; adjust for next column
+                bpl keyb_read4                  ; brif not - do another
+                leas 9,s                        ; clean up stack
+                ldb keyb_curscan                ; is key repeating?
+                bne keyb_read9                  ; brif so
+keyb_reada      rts
+keyb_read7      bitb ,u                         ; get current state
+                bne keyb_read8                  ; brif key pressed (make)
+                cmpa keyb_curscan               ; is it the currently repeating key?
+                bne keyb_read6                  ; brif not - don't need to do anything
+                clr keyb_curscan                ; clear the current repeat
+                bra keyb_read6
+keyb_read8      sta keyb_curscan                ; set the current scan code that is repeating
+                pshs d                          ; save current bit probe and scan code
+                ldb #keyb_repdeli               ; intialize repeat delay
+                stb keyb_repdel
+                bsr keyb_tobuff                 ; decode key to buffer
+                puls d                          ; restore scan code and bit probe
+                bra keyb_read6                  ; go handle the next bit
+keyb_read9      dec keyb_repdel                 ; is it time to repeat it?
+                bne keyb_reada                  ; brif not
+                ldb #keyb_repdelr               ; reset repeat delay
+                stb keyb_repdel
+                lda keyb_curscan                ; get current scan code
+keyb_tobuff     tsta                            ; @?
+                beq keyb_tobuff7                ; brif so
+                cmpa #26                        ; is it alpha or @?
+                bhi keyb_tobuff6                ; brif not
+                ldb keyb_flags                  ; get shift flags
+                bitb #keyb_ctrl|keyb_alt        ; ALT or CTRL?
+                bne keyb_tobuff4                ; brif one or both
+                ora #0x60                       ; make lower case
+                bitb #keyb_caps                 ; capslock enabled?
+                beq keyb_tobuff0                ; brif not
+                eora #0x20                      ; flip to upper case
+keyb_tobuff0    bitb #keyb_shift                ; shifted?
+                beq keyb_tobuff1                ; brif not
+                eora #0x20                      ; flip case if shifted
+keyb_tobuff1    ldx keyb_buffw                  ; get write pointer for keyboard buffer
+                sta ,x+                         ; put it in the buffer
+                cmpx #keyb_buff+keyb_bufflen    ; end of buffer?
+                blo keyb_tobuff2                ; brif not
+                ldx #keyb_buff                  ; reset pointer to start
+keyb_tobuff2    cmpx keyb_buffr                 ; did we run into the read pointer?
+                beq keyb_tobuff3                ; brif so - there wasn't room so don't save pointer
+                stx keyb_buffw                  ; update the write pointer
+keyb_tobuff3    rts
+keyb_tobuff4    bitb #keyb_alt                  ; is ALT?
+                beq keyb_tobuff1                ; brif not - scan code is CTRL-<letter> code
+                ora #0x80                       ; set bit 7 for "ALT" codes
+                bitb #keyb_shift                ; shifted?
+                beq keyb_tobuff5                ; brif not
+                ora #0x20                       ; set bit 5
+keyb_tobuff5    bitb #keyb_ctrl                 ; ctrl?
+                beq keyb_tobuff1                ; brif not - stash it in the buffer
+                ora #0x40                       ; set bit 6 for "ctrl
+                bra keyb_tobuff1                ; stash it the buffer
+keyb_tobuff6    suba #26                        ; codes above 26 down to 1; @ will be 0
+keyb_tobuff7    cmpa #6                         ; is it "0"?
+                bne keyb_tobuff8                ; brif not
+                ldb keyb_flags                  ; get shift flags
+                bitb #keyb_shift|keyb_ctrl      ; CTRL-0 or SHIFT-0?
+                beq keyb_tobuff8                ; brif not - not "capslock"
+                eorb #keyb_caps                 ; flip the capslock state
+                stb keyb_flags
+keyb_tobuffa    rts                             ; and don't put it in the buffer
+keyb_tobuff8    cmpa #25                        ; is it at or above ALT?
+                blo keyb_tobuff9                ; brif not
+                suba #2                         ; close gap for ALT/CTRL
+keyb_tobuff9    ldb #8                          ;* 8 codes; multiply by 8 and move to B
+                mul                             ;*
+                ldx #keyb_codetab               ; point to special code table
+                abx                             ; now X points to the base entry in the table
+                ldb keyb_flags                  ; get shift flags
+                andb #keyb_shift|keyb_ctrl|keyb_alt ; keep only shift/ctrl/alt
+                lda b,x                         ; fetch key code
+                beq keyb_tobuffa                ; brif no code to return
+                bra keyb_tobuff1                ; go stash it in the buffer
+; This is the keyboard code table; there are 8 bytes per entry in the following order:
+; 0: unmodified
+; 1: shift
+; 2: ctrl
+; 3: ctrl-shift
+; 4: alt
+; 5: alt-shift
+; 6: alt-ctrl
+; 7: alt-ctrl-shift
+;
+; No entries for ALT, CTRL, SHIFT, or letters
+keyb_codetab    fcb 0x40,0x13,0x40,0x40,0x80,0xa0,0xc0,0xe0     ; @
+                fcb 0x5e,0x5f,0x00,0x00,0x00,0x00,0x00,0x00     ; <UP>
+                fcb 0x0a,0x5b,0x00,0x00,0x00,0x00,0x00,0x00     ; <DOWN>
+                fcb 0x08,0x15,0x00,0x00,0x00,0x00,0x00,0x00     ; <LEFT>
+                fcb 0x09,0x5d,0x00,0x00,0x00,0x00,0x00,0x00     ; <RIGHT>
+                fcb 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20     ; <SPACE>
+                fcb 0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00     ; 0 (shift/ctrl variants shadowed above)
+                fcb 0x31,0x21,0x00,0x00,0x00,0x00,0x00,0x00     ; 1 !
+                fcb 0x32,0x22,0x00,0x00,0x00,0x00,0x00,0x00     ; 2 "
+                fcb 0x33,0x23,0x00,0x00,0x00,0x00,0x00,0x00     ; 3 #
+                fcb 0x34,0x24,0x00,0x00,0x00,0x00,0x00,0x00     ; 4 $
+                fcb 0x35,0x25,0x00,0x00,0x00,0x00,0x00,0x00     ; 5 %
+                fcb 0x36,0x26,0x00,0x00,0x00,0x00,0x00,0x00     ; 6 &
+                fcb 0x37,0x27,0x00,0x00,0x00,0x00,0x00,0x00     ; 7 '
+                fcb 0x38,0x28,0x00,0x00,0x00,0x00,0x00,0x00     ; 8 (
+                fcb 0x39,0x29,0x00,0x00,0x00,0x00,0x00,0x00     ; 9 )
+                fcb 0x3a,0x2a,0x00,0x00,0x00,0x00,0x00,0x00     ; : *
+                fcb 0x3b,0x2b,0x00,0x00,0x00,0x00,0x00,0x00     ; ; +
+                fcb 0x2c,0x3c,0x00,0x00,0x00,0x00,0x00,0x00     ; , <
+                fcb 0x2d,0x3d,0x00,0x00,0x00,0x00,0x00,0x00     ; - =
+                fcb 0x2e,0x3e,0x00,0x00,0x00,0x00,0x00,0x00     ; . >
+                fcb 0x2f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00     ; / ?
+                fcb 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d     ; <ENTER>
+                fcb 0x0c,0x5c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c     ; <CLEAR>
+                fcb 0x03,0x03,0x1b,0x1b,0x9b,0xbb,0xdb,0xfb     ; <BREAK>
+                fcb 0x1c,0x1d,0x1c,0x1d,0x00,0x00,0x00,0x00     ; <F1>
+                fcb 0x1e,0x1f,0x1e,0x1f,0x00,0x00,0x00,0x00     ; <F2>
+                *pragmapop list
--- a/src/lwbasic.s	Sun Jul 02 17:21:11 2023 -0600
+++ b/src/lwbasic.s	Sun Aug 06 00:12:29 2023 -0600
@@ -28,944 +28,20 @@
 skip2           macro noexpand
                 fcb 0x8c                        ; opcode for CMPX immediate
                 endm
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Include the various sub source files
+                include defs.s
+                include vars.s
                 *pragmapop list
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Various constants
-console_curdel  equ 10                          ; delay between cursor blink cycles
-keyb_bufflen    equ 64                          ; keyboard ring buffer length
-keyb_repdeli    equ 40                          ; ticks before initial repeat (2/3 s)
-keyb_repdelr    equ 6                           ; 10 repeats per second
-keyb_caps       equ 0x80                        ; capslock enabled
-keyb_alt        equ 0x04                        ; alt pressed
-keyb_ctrl       equ 0x02                        ; ctrl pressed
-keyb_shift      equ 0x01                        ; shift pressed
-linebuffsize    equ 0x100                       ; the line input buffer (256 bytes)
-stringstacknum  equ 20                          ; number of entries on the anonymous string descriptor stack
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Data structure used for calculations. Calculations are handled via structurs called value accumulators. A value
-; accumulator consists of a data type flag (at the end of the structure) and a data area whose layout varies based
-; on the actual data type. The layouts for each value type are described below.
-;
-; A value type that is NULL (not set to anything) has type 0 (valtype_none) and the rest should be zero.
-;
-; A value accumulator has the following structure for floating point:
-; Offset        Length          Contents
-; 0             1               fp exponent
-; 1             4               fp mantissa
-; 5             1               fp sign
-; 6             1               value type
-;
-; A value accumulator has the following structure for integers:
-; Offset        Length          Contents
-; 0             1               *unsued*
-; 1             4               integer value (two's complement)
-; 5             1               *unused*
-; 6             1               value type
-;
-; A value accumulator has the following structure for a string:
-; Offset        Length          Contents
-; 0             2               string length
-; 2             2               *reserved for string data pointer expansion, must be zero*
-; 4             2               string data pointer
-; 6             1               value type
-;
-; Value type constants
-valtype_none    equ 0                           ; unknown value type
-valtype_int     equ 1                           ; integer (32 bit) value (signed)
-valtype_float   equ 2                           ; float type (40 bit) value
-valtype_string  equ 3                           ; string type (16 bit length, 16(32) bit data pointer
-; Value accumulator structure definitions
-val.type        equ 6                           ; value type offset
-val.fpexp       equ 0                           ; fp exponent offset
-val.fpmant      equ 1                           ; fp mantissa offset
-val.fpsign      equ 5                           ; fp sign offset
-val.int         equ 1                           ; integer offset
-val.strlen      equ 0                           ; string length offset
-val.strptr      equ 4                           ; string data pointer (low word)
-val.size        equ 7                           ; size of a value accumulator
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-                ifdef COCO3
-; GIME INIT0
-GIME_COCO       equ 0x80                        ; Set for coco2 compatible mode (video display)
-GIME_MMUEN      equ 0x40                        ; Set to enable MMU
-GIME_IEN        equ 0x20                        ; GIME IRQ enable
-GIME_FEN        equ 0x10                        ; GIME FIRQ enable
-GIME_FExx       equ 0x08                        ; Enable constant RAM at 0xFExx (comes from block 0x3f)
-GIME_SCS        equ 0x04                        ; Set to enable standard SCS (switches 0xFF5x)
-GIME_ROME16     equ 0x00                        ; 16K internal, 16K external ROM mode
-GIME_ROME32     equ 0x03                        ; 32K external ROM
-GIME_ROMI32     equ 0x02                        ; 32K internal ROM
-; GIME INIT1
-GIME_TMRFAT     equ 0x20                        ; TIMER ticks approx every 279.365 ns
-GIME_TMRSLOW    equ 0x00                        ; TIMER ticks approx every 63.695 µs
-GIME_TASK0      equ 0x00                        ; MMU task 0
-GIME_TASK1      equ 0x01                        ; MMU task 1
-; GIME interrupt enable/status bits
-GIME_ITIMER     equ 0x20                        ; TIMER interrupt (timer reaches 0)
-GIME_IHBORD     equ 0x10                        ; HSYNC interrupt (falling edge)
-GIME_IVBORD     equ 0x08                        ; VSYNC interrupt (falling edge)
-GIME_ISERIAL    equ 0x04                        ; Falling edge of signal on pin 4 of serial port
-GIME_IKEYBOARD  equ 0x02                        ; Interrupt if a 0 bit appears on bits 6-0 of PIA0.DA
-GIME_ICART      equ 0x01                        ; Interrupt on falling edge of pin 8 of cartridge port
-; GIME VMODE
-GIME_BP         equ 0x80                        ; enable bit plane mode
-GIME_BPI        equ 0x20                        ; colour burst phase inversion (composite output only)
-GIME_MONO       equ 0x10                        ; disable colour burst (composite output only)
-GIME_H50        equ 0x08                        ; set to 50Hz operation
-GIME_LPR1       equ 0x00                        ; one line per row
-GIME_LPR2       equ 0x02                        ; two lines per row (also works on graphics)
-GIME_LPR8       equ 0x03                        ; 8 lines per row
-GIME_LPR9       equ 0x04                        ; 9 lines per row
-GIME_LPR10      equ 0x05                        ; 10 lines per row
-GIME_LPR11      equ 0x06                        ; 11 lines per row
-GIME_LPRINF     equ 0x07                        ; "infinite" lines per row
-; GIME VRES
-GIME_LPF192     equ 0x00                        ; 192 lines on screen
-GIME_LPF200     equ 0x40                        ; 200 lines on screen (actually 199 due to hardware bug)
-GIME_LPF225     equ 0x60                        ; 225 lines on screen
-GIME_BPR16      equ 0x00                        ; 16 bytes per row
-GIME_BPR20      equ 0x04                        ; 20 bytes per row
-GIME_BPR32      equ 0x08                        ; 32 bytes per row
-GIME_BPR40      equ 0x0c                        ; 40 bytes per row
-GIME_BPR64      equ 0x10                        ; 64 bytes per row
-GIME_BPR80      equ 0x14                        ; 80 bytes per row
-GIME_BPR128     equ 0x18                        ; 128 bytes per row
-GIME_BPR160     equ 0x1c                        ; 160 bytes per row
-GIME_TXT32      equ 0x00                        ; 32 characters per row
-GIME_TXT40      equ 0x04                        ; 40 characters per row
-GIME_TXT64      equ 0x10                        ; 64 characters per row
-GIME_TXT80      equ 0x14                        ; 80 characters per row
-GIME_BPP1       equ 0x00                        ; 1 bit per pixel
-GIME_BPP2       equ 0x01                        ; 2 bits per pixel
-GIME_BPP4       equ 0x02                        ; 4 bits per pixel
-GIME_TXTATTR    equ 0x01                        ; text attributes enabled
-                endc
-                ifdef COCO3
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Stuff on the fixed memory page
-                org 0xfe00
-                rmb 0xed                        ; unused
-INT.FLAG        rmb 1                           ; validity flag
-INT.SWI3        rmb 3                           ; SWI3 bounce vector
-INT.SWI2        rmb 3                           ; SWI2 bounce vector
-INT.FIRQ        rmb 3                           ; FIRQ bounce vector
-INT.IRQ         rmb 3                           ; IRQ bounce vector
-INT.SWI         rmb 3                           ; SWI bounce vector
-INT.NMI         rmb 3                           ; NMI bounce vector
-                endc
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Hardware definitions for the I/O page
-                org 0xff00
-PIA0            equ *                           ; Keyboard PIA
-PIA0.DA         rmb 1                           ; PIA0 data/direction A
-PIA0.CA         rmb 1                           ; PIA0 control A
-PIA0.DB         rmb 1                           ; PIA0 data/direction B
-PIA0.CB         rmb 1                           ; PIA0 control B
-                rmb 28                          ; mirror images of PIA0
-PIA1            equ *                           ; DA/misc stuff
-PIA1.DA         rmb 1                           ; PIA1 data/direction A
-PIA1.CA         rmb 1                           ; PIA1 control A
-PIA1.DB         rmb 1                           ; PIA1 data/direction B
-PIA1.CB         rmb 1                           ; PIA1 control B
-                rmb 28                          ; mirror images of PIA1
-                rmb 16                          ; SCS/Disk controller
-                rmb 16                          ; second half of SCS area
-                rmb 32                          ; miscelaneous hardware
-                ifdef COCO3
-                rmb 16                          ; *reserved* (unused but the GIME drives them)
-GIME.INIT0      rmb 1                           ; basic GIME system config
-GIME.INIT1      rmb 1                           ; MMU task and timer rate
-GIME.IRQ        rmb 1                           ; GIME IRQ enable/status register
-GIME.FIRQ       rmb 1                           ; GIME FIRQ enable/status register
-GIME.TIMER      rmb 2                           ; GIME programmable timer
-                rmb 2                           ; *reserved*
-GIME.VMODE      rmb 1                           ; GIME video mode setting
-GIME.VRES       rmb 1                           ; GIME video resolution setting
-                rmb 1                           ; *reserved* (used for MMU expansion on some memory boards)
-GIME.BORDER     rmb 1                           ; GIME border colour
-GIME.VSCROLL    rmb 1                           ; vertical scroll offset register/VDG screen mode variation
-GIME.VOFFSET    rmb 2                           ; address of video memory (8 byte increments)
-GIME.HOFFSET    rmb 1                           ; horizontal scroll offset
-GIME.MMU        equ *                           ; MMU registers (two tasks)
-GIME.MMU0       rmb 8                           ; MMU task 0
-GIME.MMU1       rmb 8                           ; MMU task 1
-GIME.PALETTE    rmb 16                          ; Palette registers
-                else
-                rmb 64                          ; unused on Coco 1/2 (GIME on Coco 3)
-                endc
-SAMREG          equ *                           ; the SAM configuration register
-SAM.V0CLR       rmb 1                           ; SAM video mode bits
-SAM.V0SET       rmb 1
-SAM.V1CLR       rmb 1
-SAM.V1SET       rmb 1
-SAM.V2CLR       rmb 1
-SAM.V2SET       rmb 1
-SAM.F0CLR       rmb 1                           ; SAM screen address bits
-SAM.F0SET       rmb 1
-SAM.F1CLR       rmb 1
-SAM.F1SET       rmb 1
-SAM.F2CLR       rmb 1
-SAM.F2SET       rmb 1
-SAM.F3CLR       rmb 1
-SAM.F3SET       rmb 1
-SAM.F4CLR       rmb 1
-SAM.F4SET       rmb 1
-SAM.F5CLR       rmb 1
-SAM.F5SET       rmb 1
-SAM.F6CLR       rmb 1
-SAM.F6SET       rmb 1
-SAM.P1CLR       rmb 1                           ; SAM "page 1" selection (or extra memory type flag)
-SAM.P1SET       rmb 1
-SAM.R0CLR       rmb 1                           ; SAM R0 bit (address dependent speedup, not used on Coco3)
-SAM.R0SET       rmb 1
-SAM.R1CLR       rmb 1                           ; SAM R1 bit (full speedup/coco 3 speedup)
-SAM.R1SET       rmb 1
-SAM.M0CLR       rmb 1                           ; SAM M0/M1 bits (memory type, not used on Coco3)
-SAM.M0SET       rmb 1
-SAM.M1CLR       rmb 1
-SAM.M1SET       rmb 1
-SAM.TYCLR       rmb 1                           ; force ROM mode (map type 0)
-SAM.TYSET       rmb 1                           ; set RAM mode (map type 1)
-                rmb 18                          ; *MPU reserved*
-CPU.SWI3        rmb 2                           ; CPU SWI3 vector
-CPU.SWI2        rmb 2                           ; CPU SWI2 vector
-CPU.FIRQ        rmb 2                           ; CPU FIRQ vector
-CPU.IRQ         rmb 2                           ; CPU IRQ vector
-CPU.SWI         rmb 2                           ; CPU SWI vector
-CPU.NMI         rmb 2                           ; CPU NMI vector
-CPU.RESET       rmb 2                           ; CPU RESET/startup vector
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Start of memory which has the direct page and other data.
-                org 0
-dpstart         equ *                           ; start of direct page
-zero            rmb 2                           ; constant zero word used for faster zeroing of 16 bit registers
-binval          rmb 2                           ; arbitary binary value, usually a line number or integer
-memtop          rmb 2                           ; absolute top of memory in 64K memory map
-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
-freestart       rmb 2                           ; pointer to start of unallocated memory
-readlinenoecho  rmb 1                           ; if nonzero, the readline routine won't echo its input
-console_curptr  rmb 2                           ; current cursor pointer for console driver
-console_blnkdel rmb 1                           ; cursor blink delay
-console_truelc  rmb 1                           ; set to nonzero if the console supports true lower case (gfx, etc.)
-filenum         rmb 1                           ; current input/output channel
-fileeof         rmb 1                           ; flag for whether last read detected EOF
-keyb_flags      rmb 1                           ; shift flags for the keyboard
-keyb_joystate   rmb 1                           ; joystick button state
-keyb_repdel     rmb 1                           ; repeat delay
-keyb_curscan    rmb 1                           ; current repeating scan code
-keyb_buffw      rmb 2                           ; keyboard ring buffer write pointer
-keyb_buffr      rmb 2                           ; keyboard ring buffer read pointer
-curline         rmb 2                           ; pointer to current line
-contline        rmb 2                           ; pointer to line for CONT
-contstmt        rmb 2                           ; interpretation pointer for CONT
-curstmt         rmb 2                           ; start of statement currently being interpreted
-endflag         rmb 1                           ; 00 = END, FF = STOP
-stringstackptr  rmb 2                           ; anonymous string descriptor stack pointer
-tok_skipkw      rmb 1                           ; flag for when skipping an unrecognized keyword
-tok_skipdt      rmb 1                           ; flag for when processing DATA
-tok_kwtype      rmb 1                           ; primary/secondary type flag for tokens
-tok_kwnum       rmb 1                           ; the actual token number
-tok_kwmatchl    rmb 1                           ; the length of the best match during lookup
-tok_kwmatch     rmb 2                           ; the current best matched token number
-val0            rmb val.size                    ; value accumulator 0
-val1            rmb val.size                    ; value accumulator 1
-                rmb 0x71-*                      ; align RSTFLG/RSTVEC for stock ROM compatibility
-RSTFLG          rmb 1                           ; 0x55 if RSTVEC is valid
-RSTVEC          rmb 2                           ; points to warm start routine (must start with NOP)
-inputptr        rmb 2                           ; pointer to current program execution location
-                rmb 0x100-*                     ; make sure the stuff that isn't direct page is outside of it
-SW3VEC          rmb 3                           ; SWI3 vector (for compatibility)
-SW2VEC          rmb 3                           ; SWI2 vector (for compatibility)
-SWIVEC          rmb 3                           ; SWI vector (for compatibility)
-NMIVEC          rmb 3                           ; NMI vector (for compatibility)
-IRQVEC          rmb 3                           ; IRQ vector (for compatibility)
-FRQVEC          rmb 3                           ; FIRQ vector (for compatibility)
-keyb_state      rmb 8                           ; rollover table state
-keyb_buff       rmb keyb_bufflen                ; the keyboard ring buffer
-linebuff        rmb linebuffsize                ; the line input buffer
-tokebuff        rmb linebuffsize+50             ; make it as long as line buffer plus a margin
-stringstack     rmb 5*stringstacknum            ; reserve space for the anonymous string descriptor stack           
-stringstackend  equ *                           ; end of string stack buffer
-                ifne *&0x1ff
-                rmb 0x200-(*&0x1ff)
-                endc
-textscreen      rmb 0x200                       ; the actual text screen (must be on 512 byte alignment)
-heapstart       equ *                           ; start of dynamically allocated stuff
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; The heap has the following items in order:
-;
-; Program text: preceded by a NUL and pointed to by progtext
-; Variable table: pointed to by vartab; contains records for all scalar and array variables
-; Free space: unused memory between the object table and the stack; pointed to by freestart
-; The stack: grows downward from the bottom of string space, pointed to by the stack pointer, obviously
-; String space: garbage collected non-constant string data pointed to by freetop
-; Reserved memory: immediately above string space; pointed to by memsize
-; Actual top of RAM: top of reserved memory; pointed to by memtop
-;
-; The variable table consists of several symbol tables defined as follows:
-;
-; Pointer       Size of entry   Variable types
-; vartabint     4               Integer scalars
-; vartablong    6               Long integer scalars
-; vartabfloat   7               Floating point scalars
-; vartabstring  6               String scalars
-;
-; Each entry starts with 2 bytes for the variable name followed by the data payload.
                 org 0x8000                      ; the hardware puts the ROMs here; it's not negotiable
 ROMSTART        equ *
-START           orcc #0x50                      ; make sure interrupts are disabled if we come here in an unusual way
-                ifdef COCO3
-                ldu #gime_inite                 ; point to end of GIME initializer
-                ldx #GIME.INIT0+(gime_inite-gime_init) ; point to end of GIME registers
-                ldb #gime_inite-gime_init       ; number of bytes to transfer
-initc0          lda ,-u                         ; copy byte to GIME (count down so we init MMU before turning it on)
-                sta ,-x
-                decb                            ; done?
-                bne initc0                      ; brif not
-                endc
-                ldd #0xff34                     ; initizer for below
-                tfr a,dp                        ; set DP to I/O page
-                setdp 0xff                      ; tell assembler about DP value
-                clr PIA0.CA                     ; set PIA0 A to direction mode
-                clr PIA0.CB                     ; set PIA0 B to direction mode
-                clr PIA0.DA                     ; set PIA0 A to all inputs (comparator, keyboard rows)
-                sta PIA0.DB                     ; set PIA0 B to all outputs (keyboard columns)
-                stb PIA0.CA                     ; set PIA0 A to data mode, interrupt disabled, MUX to source 0
-                stb PIA0.CB                     ; set PIA0 B to data mode, interrupt disabled, MUX to source 0
-                clr PIA1.CA                     ; set PIA1 A to direction mode
-                clr PIA1.CB                     ; set PIA1 B to direction mode
-                deca                            ; set PIA1 A bits 7-1 output (DAC, RS232), 0 input (cassette)
-                sta PIA1.DA
-                lda #0xf8                       ;* set PIA1 B bits 7-3 output (VDG stuff), 2-0 input (single bit sound,
-                sta PIA1.DB                     ;* RS232 input, ram size input)
-                stb PIA1.CA                     ; set PIA1 A to data mode, interrupt disabled, cassette motor off
-                stb PIA1.CB                     ; set PIA1 B to data mode, interrupt disabled, sound off
-                lda #2                          ; set RS232 output to "marking" (stop bit)
-                sta PIA1.DA
-                lda #16                         ; clear 16 SAM register bits
-                ldu #SAMREG                     ; point to SAM register bits
-init0           sta ,u++                        ; clear SAM bit
-                deca                            ; done all?
-                bne init0                       ; brif not
-; set the SAM to point to the text screen, which the code will handle at any
-; arbitrary 512 byte aligned address in memory
-                ifne (textscreen)&0x200
-                sta SAM.F0SET
-                endc
-                ifne (textscreen)&0x400
-                sta SAM.F1SET
-                endc
-                ifne (textscreen)&0x800
-                sta SAM.F2SET
-                endc
-                ifne (textscreen)&0x1000
-                sta SAM.F3SET
-                endc
-                ifne (textscreen)&0x2000
-                sta SAM.F4SET
-                endc
-                ifne (textscreen)&0x4000
-                sta SAM.F5SET
-                endc
-                ifne (textscreen)&0x8000
-                sta SAM.F6SET
-                endc
-                ifdef COCO2B
-; The following SAM configuration sequence is different from the one in the usual
-; one used by the earlier models of the Coco because the Coco2B has the '785 variant
-; of the SAM instead of the '783 variant. The '785 variant supports 16Kx4 RAMs which
-; are used in Coco2B systems. Hence why there is a different version of this ROM
-; just for the Coco2B.
-                clr PIA0.DB                     ; strobe RAM size low
-                ldb #4                          ; is input low?
-                bitb PIA1.DB
-                beq init1                       ; brif not
-                sta SAM.M0SET                   ; program SAM for 16Kx4 RAMs
-                sta SAM.P1SET
-                skip2
-init1           sta SAM.M1SET                   ; program SAM for 64Kx1 RAMs
-                else
-                ifndef COCO3
-; Detect the installed memory size so the SAM ('783 variant) can be correctly
-; programmed for the installed memory. Note that this sequence is replaced with
-; a different one for the Coco2B which has the '785 variant of the SAM.
-                ldb #0xff                       ; strobe RAM size high
-                stb PIA0.DB
-                ldb #4                          ; mask for ram size check
-                bitb PIA1.DB                    ; is the bit set on ram size input?
-                beq init2                       ; brif not - 4Kx1 RAMs
-                sta PIA0.DB                     ; clear RAM size output to see what happens (A is 0 from above)
-                bitb PIA1.DB                    ; is it set now?
-                beq init1                       ; brif not - 64Kx1 RAMs
-                leau -2,u                       ; adjust pointer so we set the other RAM size bit for the SAM (16Kx1)
-init1           sta -3,u                        ; set M0 (16Kx1) or M1 (64Kx1)
-                endc
-                endc
-init2           tfr a,dp                        ; set DP to bottom of memory (A is 0 from above)
-                setdp 0                         ; tell assembler about it
-                lds #textscreen                 ; put the stack just below the text screen
-                ifdef COCO3
-; Check if we need to do a ROM/RAM copy, which will happen if the interrupt vectors are
-; not flagged valid OR the reset vector isn't valid
-                ldb INT.FLAG                    ; are the bounce vectors valid?
-                cmpb #0x55
-                bne initc4                      ; brif not - do ROM/RAM copy
-                ldb RSTFLG                      ; is reset vector valid?
-                bne initc2                      ; brif not - check secondary location
-                ldx RSTVEC                      ; get reset vector
-                ldb ,x                          ; is it valid?\
-                cmpb #0x12
-                bne initc2                      ; brif not
-initc1          jmp ,x                          ; transfer control to warm start routine
-initc2          clr GIME.MMU0                   ; check again with block 0 in the direct page
-                ldb RSTFLG                      ; get new RSTFLG
-                cmpb #0x55                      ; valid?
-                bne initc3                      ; brif not
-                ldx RSTVEC                      ; get new RSTVEC
-                ldb ,x                          ; is it valid?
-                cmpb #0x12
-                beq initc1                      ; brif so - transfer control
-initc3          ldb #0x38                       ; restore MMU
-                stb GIME.MMU0
-initc4          ldx #initc6                     ; point to helper
-                ldu #textscreen                 ; point to text screen
-                ldb #initc7-initc6              ; bytes to copy
-initc5          lda ,x+                         ; copy byte
-                sta ,u+
-                decb                            ; done?
-                bne initc5                      ; brif not
-                ldu #0x8000                     ; point to start of ROM
-                jmp textscreen                  ; transfer control to helper in RAM
-initc6          sta SAM.TYCLR                   ; drop to ROM mode
-                pulu d,x,y,s                    ; grab 8 bytes
-                sta SAM.TYSET                   ; go to RAM mode
-                pshu d,x,y,s                    ; stick the bytes in RAM
-                leau 8,u                        ; move to next 8 bytes
-                cmpu #0xfe00                    ; end of stuff to copy?
-                blo initc6                      ; brif not
-                jmp initc7                      ; go back to mainline
-initc7          lds #textscreen                 ; reset stack to somewhere safe
-                lda #0x12                       ; activate ROM warm start handler
-                sta warmstart
-                ldx #INT.FLAG                   ; point to bounce vector destination
-                ldu #int_init                   ; point to initializer for bounce vectors
-                ldb #int_inite-int_init         ; number of bytes to copy
-initc8          lda ,u+                         ; copy byte
-                sta ,x+
-                decb                            ; done?
-                bne initc8                      ; brif not
-; now recheck for warm start in case ROM/RAM copy made things valid
-                endc
-                ldb RSTFLG                      ; is the reset vector valid?
-                cmpb #0x55
-                bne coldstart                   ; brif not - do cold start
-                ldx RSTVEC                      ; get warm start routine pointer
-                ldb ,x                          ; does it start with NOP?
-                cmpb #0x12
-                bne coldstart                   ; brif not - do cold start
-                jmp ,x                          ; transfer control to warm start routine
-                ifdef COCO3
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; GIME register initializer
-gime_init       fcb GIME_COCO|GIME_MMUEN|GIME_FExx|GIME_SCS|GIME_ROMI32 ; enable MMU, SCS, constant page, internal ROM
-                fcb GIME_TASK0                  ; use MMU task 0
-                fcb 0                           ; do not enable IRQ sources
-                fcb 0                           ; do not enable FIRQ sources
-                fdb 0xfff                       ; set timer to max value
-                fdb 0                           ; *reserved placeholder*
-                fcb 0,0,0,0                     ; SG4 screen settings with black border
-                fcb 0x0f,0xe0,0x00,0x00         ; (puts screen in bottom 64K of memory)
-                fcb 0x38,0x39,0x3a,0x3b         ; MMU task 0 (bottom of top 64K of RAM)
-                fcb 0x3c,0x3d,0x3e,0x3f         ; (ROM shadow must be in 3c...3f)
-                fcb 0x38,0x39,0x3a,0x3b         ; MMU task 1 (copy of task 0)
-                fcb 0x3c,0x3d,0x3e,0x3f
-                fcb 18,54,9,36,63,27,45,38      ; palette values (RGB)
-                fcb 0,18,0,63,0,18,0,38
-gime_inite      equ *
-int_init        fcb 0x55                        ; vectors valid flag
-                jmp SW3VEC                      ; bounce to stock ROM compatibility vector
-                jmp SW2VEC                      ; bounce to stock ROM compatibility vector
-                jmp FRQVEC                      ; bounce to stock ROM compatibility vector
-                jmp IRQVEC                      ; bounce to stock ROM compatibility vector
-                jmp SWIVEC                      ; bounce to stock ROM compatibility vector
-                jmp NMIVEC                      ; bounce to stock ROM compatibility vector
-int_inite       equ *
-                endc
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Cold start handling
-coldstart       ldx #dpstart                    ; point to start of direct page
-                ldd #0                          ; set up for blanking
-coldstart0      std ,x++                        ; blank a couple of bytes
-                cmpx #textscreen                ; end of low memory?
-                blo coldstart0                  ; brif not
-                ifndef COCO3
-; This is the memory size detection sequence. This runs through memory starting at the bottom of memory
-; and stops when it reaches something that can't be modified successfully. This is basically the same
-; algorithm used by the stock ROM. It takes less space than doing a more pointed set of probes. The end
-; result will be X pointing to the byte one below the top of RAM. This is intentional to ensure there
-; is one writeable byte at the top of string space. Note that X will point to the byte after the end
-; of the text screen when we get here.
-                ldx #heapstart                  ; point to start of heap
-coldstart1      lda 2,x                         ; get original value at test location
-                coma                            ; invert all bits
-                sta 2,x                         ; write it to the memory location
-                cmpa 2,x                        ; did it take?
-                bne coldstart2                  ; brif not
-                com 2,x                         ; restore memory byte
-                leax 1,x                        ; move pointer forward
-                bra coldstart1                  ; go check next byte
-                else
-; For the Coco3, we do not need to concern ourselves about where the top actual memory is so we don't
-; bother doing a memory scan in the default 64K memory map. Because we always run from RAM, we can actually
-; set the top of memory to the actual top of the 32K space without having to ensure there is an extra byte
-; available above the string space.
-                ldx #ROMSTART-1                 ; point to top of memory
-                endc
-coldstart2      stx memtop                      ; save absolute top of memory
-                stx memsize                     ; save top of unreserved memory
-                stx stringtab                   ; mark string space as empty
-                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
-                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
-                clr ,x+                         ; put a NULL pointer to mark end of program
-                clr ,x+
-                stx vartab                      ; put start of integer variables at end of program
-                stx objecttab                   ; also put the start of large objects there
-                stx freestart                   ; mark the start of free memory
-                lda #keyb_caps                  ; enable caps lock but disable all other shift states
-                sta keyb_flags
-                ldx #warmstart                  ; set up warm start handler
-                stx RSTVEC
-                lda #0x55                       ; activate warm start handler
-                sta RSTFLG
-                ldd #0x7e3b                     ; opcodes for JMP extended and RTI
-                ldx #irqhandler                 ; enable IRQ handler with a JMP at the vector
-                sta IRQVEC
-                stx IRQVEC+1
-                sta FRQVEC                      ; initialize FIRQ handler with JMP
-                ldx #firqhandler
-                stx FRQVEC+1
-                stb NMIVEC                      ; initialize NMI to RTI
-                stb SW3VEC                      ; initialize SWI3 to RTI
-                stb SW2VEC                      ; initialize SWI2 to RTI
-                stb SWIVEC                      ; initialize SWI to RTI
-                ldx #greeting                   ; display greeting
-                jsr console_outstr
-                bra warmstartb                  ; finish up initialization
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Warm start handling
-                ifdef COCO3
-warmstart       fcb 0xff                        ; set to 0xff to force ROM/RAM copy on reset
-                else
-warmstart       nop                             ; flag warm start routine as valid
-                endc
-                jsr console_clear               ; clear screen
-                clr filenum                     ; reset I/O channel to the screen
-warmstartb      jsr keyb_reset                  ; reset the keyboard
-                lda #0x35                       ; enable VSYNC interrupt in PIA
-                sta PIA0.CB
-                andcc #0xaf                     ; enable interrupts at the cpu
-                jmp immediate                   ; go start immediate mode
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; System startup message
-; (start with form feed to clear screen; saves 2 bytes over 'jsr console_clear' in cold start)
-greeting        fcc '\fLWBASIC VERSION 2022.0\r\n'
-                fcc 'COPYRIGHT (C) 2022 BY LOST\r\n'
-                fcc 'WIZARD ENTERPRISES INC.\r\n'
-                fcn '\n'
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Check for BREAK; this needs to check the keyboard directly instead of just using the usual key fetching routine so
-; we don't interfere with keyboard buffering if BREAK isn't pressed. We also need to scan the keyboard directly for this
-; so we react even if the keyboard buffer is full. If BREAK is pressed, the keyboard buffer is emptied.
-breakcheck      lda #0xfb                       ; strobe column for BREAK
-                sta PIA0.DB
-                clra                            ; clear carry for no BREAK
-                lda PIA0.DA                     ; read rows
-                bita #0x40                      ; is BREAK down?
-                bne breakcheck0                 ; brif not - check for SHIFT-@
-                sync                            ; wait for interrupt to scan keyboard
-                bsr keyb_clearbuff              ; reset keyboard buffer
-                coma                            ; flag BREAK
-breakcheck1     rts
-breakcheck0     lda #0x7f                       ; check for SHIFT
-                sta PIA0.DB
-                lda PIA0.DA
-                bita #0x40                      ; shift?
-                bne breakcheck1                 ; brif not
-                lda #0xfe                       ; check for @
-                sta PIA0.DB
-                lda PIA0.DA
-                bita #1                         ; @?
-                bne breakcheck1                 ; brif not
-                bsr keyb_clearbuff              ; clear buffer
-breakcheck2     sync                            ; wait for keyboard to actually be scanned
-                bsr keyb_getkey
-                bcs breakcheck2                 ; brif no key down
-                bra breakcheck                  ; go do the break/pause check dance again
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Empty the keyboard buffer
-keyb_clearbuff  ldx #keyb_buff                  ; point to start of buffer
-                stx keyb_buffr                  ; set both pointers to the start
-                stx keyb_buffw
-                rts                
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; IRQ handler
-;
-; Note that the interrupt flag in the PIA is cleared at the start of the interrupt handler. That means that if it takes
-; a long time to process this interrupt, or processing this interrupt was delayed somewhat, it is far less likely that
-; an interrupt gets missed. In that case, we may end up re-interrupting immediately on RTI, but it should reduce the
-; number of missed interrupts.
-irqhandler      lda PIA0.CB                     ; was it VSYNC?
-                bmi irqhandler0                 ; brif so
-                lda PIA0.DA                     ; clear HSYNC flag so we don't get stuck if it gets enabled
-                ifdef COCO3
-                lda GIME.IRQ                    ; clear GIME IRQ state flags
-                endc
-                rti
-irqhandler0     lda PIA0.DB                     ; clear VSYNC flag
-                clra                            ; make sure DP is pointing to the right place
-                tfr a,dp
-                lda console_blnkdel             ; is the cursor blinking?
-                beq irqhandler1                 ; brif not
-                dec console_blnkdel             ; time to cycle cursor?
-                bne irqhandler1                 ; brif not
-                lda #console_curdel             ; reset blink counter
-                sta console_blnkdel
-                lda [console_curptr]            ; get character at cursor
-                adda #0x10                      ; move to next colour
-                ora #0x8f                       ; force it to be a full 4x4 colour block
-                sta [console_curptr]            ; update cursor on screen
-irqhandler1     bsr keyb_read                   ; go handle the keyboard
-                rti
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; FIRQ handler
-;
-; This handler is present to prevent accidentally enabling the interrupt and thus hanging to system. It may seem to be
-; a waste of code space, but consider it a self defense situation.
-firqhandler     pshs a                          ; need a scratch register
-                ifdef COCO3
-                lda GIME.FIRQ                   ; clear GIME FIRQ state flags
-                endc
-                lda PIA1.DA                     ; clear interrupt flags
-                lda PIA1.DB
-                lda PIA1.CA                     ; disable interrupts to prevent system hang
-                anda #0xfe
-                sta PIA1.CA
-                lda PIA1.CB
-                anda #0xfe
-                sta PIA1.CB
-                puls a                          ; restore register
-                rti
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Console keyboard input driver
-;
-; Reset the keyboard state, which means clearing the buffer and state flags
-keyb_reset      bsr keyb_clearbuff              ; clear keyboard buffer
-                lda keyb_flags                  ; reset keyboard state flags but keep capslock
-                anda #keyb_caps
-                sta keyb_flags
-                clr keyb_joystate               ; clear joystick button state
-                clr keyb_curscan                ; stop any keyboard repeating
-                ldx #0xffff                     ; mark all key state as "unpressed"
-                stx keyb_state
-                stx keyb_state+2
-                stx keyb_state+4
-                stx keyb_state+6
-                rts
-; Read character from keyboard ring buffer; return with C set if buffer empty; this doesn't actually need to have
-; interrupts disabled because the interrupt only ever updates the write pointer and then only to increase it. As a
-; result, worst case is that we don't detect the contents added to the buffer on this call and have to wait for the
-; next.
-keyb_getkey     pshs x                          ; save register
-                ldx keyb_buffr                  ; get read pointer
-                cmpx keyb_buffw                 ; same as write pointer?
-                bne keyb_getkey0                ; brif not - we have a result
-                coma                            ; set carry for empty buffer
-                puls x,pc                       ; restore register and return
-keyb_getkey0    lda ,x+                         ; get character from buffer
-                cmpx #keyb_buff+keyb_bufflen    ; did we run off end of buffer?
-                blo keyb_getkey1                ; brif not
-                ldx #keyb_buff                  ; reset to start
-keyb_getkey1    stx keyb_buffr                  ; save new read pointer
-                andcc #0xfe                     ; flag key retrieved
-                puls x,pc                       ; restore register and return
-; The PIA reading loop is specifically set up to NOT read PIA0.DB to avoid prematurely clearing the VSYNC interrupt flag
-; since that could lead to missing interrupts. Reading PIA0.DA will clear the HSYNC interrupt flag but that's less of a
-; problem because that interrupt is basically useless.
-;
-; As a note, doing the PIA read in a loop ends up using an extra 27 CPU cycles for the BCS instruction. However, it
-; saves 70 code bytes. The trade off seems worth it in this case.
-;
-; Once keyboard state is read, we do the following:
-;
-; * update the state of SHIFT, CTRL, ALT
-; * decode all other keys in a loop
-keyb_read0a     pshs b                          ; save flag bit
-                ldb a,y                         ; get state flag 
-                bitb #0x40                      ; did it change state?
-                bne keyb_read0d                 ; brif so
-                puls b,pc                       ; clean up and return
-keyb_read0d     andb #0xbf                      ; flag it as not changed
-                stb a,y
-                ldb a,u                         ; get current modifier state
-                eorb #0x40                      ; flip the state bit
-                stb a,u                         ; save new state flags
-                bitb #0x40                      ; Z set if not down
-                puls b                          ; get back flag bit
-                beq keyb_read0b                 ; brif key is pressed
-                comb                            ; invert bit flag
-                andb keyb_flags                 ; clear bit in flags
-                bra keyb_read0c                 ; finish up
-keyb_read0b     orb keyb_flags                  ; set the flag
-keyb_read0c     stb keyb_flags                  ; update flags
-                rts
-keyb_read       leas -9,s                       ; make temporary buffer
-                leay 1,s                        ; point to temporary state buffer
-                clra                            ;* set to 0xff with C clear; start by strobing no columns for joystick
-                deca                            ;* then rotate the 0 bit through to do the actual keyboard columns
-                ldu #keyb_state                 ; point to end of keyboard state buffer
-                sta PIA0.DB                     ; strobe no columns
-                ldb PIA0.DA                     ; get joystick button state
-                stb keyb_joystate               ; save it for later when needed
-                andb #0x7f                      ; mask off comparator (pretend "button" down)
-                stb ,s                          ; save button/comparator state mask
-                rola                            ; set up for first column
-keyb_read0      sta PIA0.DB                     ; set column strobe
-                ldb PIA0.DA                     ; read row data
-                eorb ,u+                        ; set bits if state changed
-                andb ,s                         ; mask off comparator and active buttons
-                stb ,y+                         ; save state change information
-                rola                            ; shift to next column
-                bcs keyb_read0                  ; brif we haven't done the last column
-                sta PIA0.DB                     ; reset column strobe to none
-                ldd #0xff00|keyb_shift
-                bsr keyb_read0a
-                ldd #0xfc00|keyb_ctrl
-                bsr keyb_read0a
-                ldd #0xfb00|keyb_alt
-                bsr keyb_read0a
-keyb_read3      ldd #0x0701                     ; initialize bit probe and counter
-keyb_read4      leay -1,y                       ; move pointers to next byte
-                leau -1,u
-keyb_read5      bitb ,y                         ; did this key change state?
-                bne keyb_read7                  ; brif so
-keyb_read6      adda #8                         ; adjust scan code
-                lslb                            ; shift bit probe
-                bpl keyb_read5                  ; brif we haven't done all bits
-                ldb ,y                          ; update state flags for this byte
-                eorb ,u
-                stb ,u
-                ldb #1                          ; reset bit probe
-                anda #0x07                      ; reset scan code
-                deca                            ; adjust for next column
-                bpl keyb_read4                  ; brif not - do another
-                leas 9,s                        ; clean up stack
-                ldb keyb_curscan                ; is key repeating?
-                bne keyb_read9                  ; brif so
-keyb_reada      rts
-keyb_read7      bitb ,u                         ; get current state
-                bne keyb_read8                  ; brif key pressed (make)
-                cmpa keyb_curscan               ; is it the currently repeating key?
-                bne keyb_read6                  ; brif not - don't need to do anything
-                clr keyb_curscan                ; clear the current repeat
-                bra keyb_read6
-keyb_read8      sta keyb_curscan                ; set the current scan code that is repeating
-                pshs d                          ; save current bit probe and scan code
-                ldb #keyb_repdeli               ; intialize repeat delay
-                stb keyb_repdel
-                bsr keyb_tobuff                 ; decode key to buffer
-                puls d                          ; restore scan code and bit probe
-                bra keyb_read6                  ; go handle the next bit
-keyb_read9      dec keyb_repdel                 ; is it time to repeat it?
-                bne keyb_reada                  ; brif not
-                ldb #keyb_repdelr               ; reset repeat delay
-                stb keyb_repdel
-                lda keyb_curscan                ; get current scan code
-keyb_tobuff     tsta                            ; @?
-                beq keyb_tobuff7                ; brif so
-                cmpa #26                        ; is it alpha or @?
-                bhi keyb_tobuff6                ; brif not
-                ldb keyb_flags                  ; get shift flags
-                bitb #keyb_ctrl|keyb_alt        ; ALT or CTRL?
-                bne keyb_tobuff4                ; brif one or both
-                ora #0x60                       ; make lower case
-                bitb #keyb_caps                 ; capslock enabled?
-                beq keyb_tobuff0                ; brif not
-                eora #0x20                      ; flip to upper case
-keyb_tobuff0    bitb #keyb_shift                ; shifted?
-                beq keyb_tobuff1                ; brif not
-                eora #0x20                      ; flip case if shifted
-keyb_tobuff1    ldx keyb_buffw                  ; get write pointer for keyboard buffer
-                sta ,x+                         ; put it in the buffer
-                cmpx #keyb_buff+keyb_bufflen    ; end of buffer?
-                blo keyb_tobuff2                ; brif not
-                ldx #keyb_buff                  ; reset pointer to start
-keyb_tobuff2    cmpx keyb_buffr                 ; did we run into the read pointer?
-                beq keyb_tobuff3                ; brif so - there wasn't room so don't save pointer
-                stx keyb_buffw                  ; update the write pointer
-keyb_tobuff3    rts
-keyb_tobuff4    bitb #keyb_alt                  ; is ALT?
-                beq keyb_tobuff1                ; brif not - scan code is CTRL-<letter> code
-                ora #0x80                       ; set bit 7 for "ALT" codes
-                bitb #keyb_shift                ; shifted?
-                beq keyb_tobuff5                ; brif not
-                ora #0x20                       ; set bit 5
-keyb_tobuff5    bitb #keyb_ctrl                 ; ctrl?
-                beq keyb_tobuff1                ; brif not - stash it in the buffer
-                ora #0x40                       ; set bit 6 for "ctrl
-                bra keyb_tobuff1                ; stash it the buffer
-keyb_tobuff6    suba #26                        ; codes above 26 down to 1; @ will be 0
-keyb_tobuff7    cmpa #6                         ; is it "0"?
-                bne keyb_tobuff8                ; brif not
-                ldb keyb_flags                  ; get shift flags
-                bitb #keyb_shift|keyb_ctrl      ; CTRL-0 or SHIFT-0?
-                beq keyb_tobuff8                ; brif not - not "capslock"
-                eorb #keyb_caps                 ; flip the capslock state
-                stb keyb_flags
-keyb_tobuffa    rts                             ; and don't put it in the buffer
-keyb_tobuff8    cmpa #25                        ; is it at or above ALT?
-                blo keyb_tobuff9                ; brif not
-                suba #2                         ; close gap for ALT/CTRL
-keyb_tobuff9    ldb #8                          ;* 8 codes; multiply by 8 and move to B
-                mul                             ;*
-                ldx #keyb_codetab               ; point to special code table
-                abx                             ; now X points to the base entry in the table
-                ldb keyb_flags                  ; get shift flags
-                andb #keyb_shift|keyb_ctrl|keyb_alt ; keep only shift/ctrl/alt
-                lda b,x                         ; fetch key code
-                beq keyb_tobuffa                ; brif no code to return
-                bra keyb_tobuff1                ; go stash it in the buffer
-; This is the keyboard code table; there are 8 bytes per entry in the following order:
-; 0: unmodified
-; 1: shift
-; 2: ctrl
-; 3: ctrl-shift
-; 4: alt
-; 5: alt-shift
-; 6: alt-ctrl
-; 7: alt-ctrl-shift
-;
-; No entries for ALT, CTRL, SHIFT, or letters
-keyb_codetab    fcb 0x40,0x13,0x40,0x40,0x80,0xa0,0xc0,0xe0     ; @
-                fcb 0x5e,0x5f,0x00,0x00,0x00,0x00,0x00,0x00     ; <UP>
-                fcb 0x0a,0x5b,0x00,0x00,0x00,0x00,0x00,0x00     ; <DOWN>
-                fcb 0x08,0x15,0x00,0x00,0x00,0x00,0x00,0x00     ; <LEFT>
-                fcb 0x09,0x5d,0x00,0x00,0x00,0x00,0x00,0x00     ; <RIGHT>
-                fcb 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20     ; <SPACE>
-                fcb 0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00     ; 0 (shift/ctrl variants shadowed above)
-                fcb 0x31,0x21,0x00,0x00,0x00,0x00,0x00,0x00     ; 1 !
-                fcb 0x32,0x22,0x00,0x00,0x00,0x00,0x00,0x00     ; 2 "
-                fcb 0x33,0x23,0x00,0x00,0x00,0x00,0x00,0x00     ; 3 #
-                fcb 0x34,0x24,0x00,0x00,0x00,0x00,0x00,0x00     ; 4 $
-                fcb 0x35,0x25,0x00,0x00,0x00,0x00,0x00,0x00     ; 5 %
-                fcb 0x36,0x26,0x00,0x00,0x00,0x00,0x00,0x00     ; 6 &
-                fcb 0x37,0x27,0x00,0x00,0x00,0x00,0x00,0x00     ; 7 '
-                fcb 0x38,0x28,0x00,0x00,0x00,0x00,0x00,0x00     ; 8 (
-                fcb 0x39,0x29,0x00,0x00,0x00,0x00,0x00,0x00     ; 9 )
-                fcb 0x3a,0x2a,0x00,0x00,0x00,0x00,0x00,0x00     ; : *
-                fcb 0x3b,0x2b,0x00,0x00,0x00,0x00,0x00,0x00     ; ; +
-                fcb 0x2c,0x3c,0x00,0x00,0x00,0x00,0x00,0x00     ; , <
-                fcb 0x2d,0x3d,0x00,0x00,0x00,0x00,0x00,0x00     ; - =
-                fcb 0x2e,0x3e,0x00,0x00,0x00,0x00,0x00,0x00     ; . >
-                fcb 0x2f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00     ; / ?
-                fcb 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d     ; <ENTER>
-                fcb 0x0c,0x5c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c     ; <CLEAR>
-                fcb 0x03,0x03,0x1b,0x1b,0x9b,0xbb,0xdb,0xfb     ; <BREAK>
-                fcb 0x1c,0x1d,0x1c,0x1d,0x00,0x00,0x00,0x00     ; <F1>
-                fcb 0x1e,0x1f,0x1e,0x1f,0x00,0x00,0x00,0x00     ; <F2>
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Console screen output driver
-;
-; Clear screen
-console_clear   ldb #0x60                       ; VDG space character
-                ldx #textscreen                 ; point to text screen
-                stx console_curptr              ; set cursor pointer to start of screen
-console_clear0  stb ,x+                         ; blank a character
-                cmpx #textscreen+0x200          ; end of screen?
-                blo console_clear0              ; brif not
-                rts
-; Output NUL terminated string
-console_outstr0 bsr console_outchr              ; output the character
-console_outstr  lda ,x+                         ; get byte from string
-                bne console_outstr0             ; brif not end of string
-                rts
-; Output NUL terminated string followed by a newline
-console_outstrn bsr console_outstr              ; output the string
-                ; fallthrough intentional
-; Output a newline (CR LF)
-console_outnl   lda #0x0d                       ; do the CR
-                bsr console_outchr
-                lda #0x0a                       ; do the LF
-                ; fallthrough intentional
-; Output a single character to the screen; enter with character in A
-console_outchr  pshs d,x                        ; save registers
-                ldx console_curptr              ; get current cursor pointer
-                cmpa #0x20                      ; printable character?
-                blo console_outchr5             ; brif not
-                tsta                            ; is it a graphics block?
-                bmi console_outchr1             ; brif so - don't do anything to it
-                cmpa #0x40                      ; number or most non-alpha characters?
-                blo console_outchr0             ; brif so - will need to flip bit 6
-                cmpa #0x60                      ; upper case?
-                blo console_outchr1             ; brif so - don't need to do anything to it
-                anda #0xdf                      ; clear bit 5 of lower case; moves it to bottom of character set
-console_outchr0 eora #0x40                      ; flip bit 6 - the "inversion" bit
-console_outchr1 sta ,x+                         ; stick it on screen
-console_outchr2 stx console_curptr              ; save new cursor pointer
-                cmpx #textscreen+0x200          ; end of screen?
-                blo console_outchr4             ; brif not
-                leax -32,x                      ; move pointer back one line
-                stx console_curptr
-                ldx #textscreen                 ; point to start of screen
-console_outchr3 ldd 32,x                        ; get bytes from next line
-                std ,x++                        ; stick them here
-                cmpx #textscreen+0x1e0          ; at last row?
-                blo console_outchr3             ; brif not
-                ldb #0x60                       ; space character for VDG screen
-                bsr console_clear0              ; blank out last row (borrowing screen clear loop)
-console_outchr4 puls d,x,pc                     ; restore registers and return
-console_outchr5 cmpa #0x0c                      ; form feed?
-                bne console_outchr6             ; brif not
-                bsr console_clear               ; clear screen
-                puls d,x,pc                     ; restore registers and return
-console_outchr6 cmpa #0x0d                      ; carriage return?
-                bne console_outchr7             ; brif not
-                ldb console_curptr+1            ; get current screen pointer LSB
-                andb #0xe0                      ; reset offset to start of line
-                stb console_curptr+1            ; save new pointer LSB
-                puls d,x,pc                     ; restore registers and return
-console_outchr7 cmpa #0x0a                      ; line feed?
-                bne console_outchr8             ; brif not
-                ldx console_curptr              ; get cursor pointer
-                leax 32,x                       ; move it forward exactly one line
-                bra console_outchr2             ; go update stuff check for scroll
-console_outchr8 cmpa #0x08                      ; backspace?
-                bne console_outchr9             ; brif not
-                cmpx #textscreen                ; at start of screen?
-                beq console_outchr4             ; brif so - backspace does nothing
-                leax -1,x                       ; back up pointer (backspace is non-destructive)
-                bra console_outchr2             ; go update pointers, etc.
-console_outchr9 cmpa #0x09                      ; TAB character?
-                bne console_outchr4             ; brif not
-                ldb console_curptr              ; get LSB of pointer
-                andb #7                         ; 8 space tabs - only keep low 3 bits
-                lda #0x60                       ; space character (tab is destructive)
-console_outchra sta ,x+                         ; put a space out
-                incb                            ; bump counter
-                cmpb #8                         ; at next tab stop?
-                blo console_outchra             ; brif not
-                bra console_outchr2             ; go update details and check for scroll
+                *pragmapush list
+                *pragma nolist
+                include init.s
+                include keyb.s
+                include irq.s
+                include consscr.s
+                *pragmapop list
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; General I/O handling package
 ;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vars.s	Sun Aug 06 00:12:29 2023 -0600
@@ -0,0 +1,86 @@
+                *pragmapush list
+                *pragma list
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Start of memory which has the direct page and other data.
+                org 0
+dpstart         equ *                           ; start of direct page
+zero            rmb 2                           ; constant zero word used for faster zeroing of 16 bit registers
+binval          rmb 2                           ; arbitary binary value, usually a line number or integer
+memtop          rmb 2                           ; absolute top of memory in 64K memory map
+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
+freestart       rmb 2                           ; pointer to start of unallocated memory
+readlinenoecho  rmb 1                           ; if nonzero, the readline routine won't echo its input
+console_curptr  rmb 2                           ; current cursor pointer for console driver
+console_blnkdel rmb 1                           ; cursor blink delay
+console_truelc  rmb 1                           ; set to nonzero if the console supports true lower case (gfx, etc.)
+filenum         rmb 1                           ; current input/output channel
+fileeof         rmb 1                           ; flag for whether last read detected EOF
+keyb_flags      rmb 1                           ; shift flags for the keyboard
+keyb_joystate   rmb 1                           ; joystick button state
+keyb_repdel     rmb 1                           ; repeat delay
+keyb_curscan    rmb 1                           ; current repeating scan code
+keyb_buffw      rmb 2                           ; keyboard ring buffer write pointer
+keyb_buffr      rmb 2                           ; keyboard ring buffer read pointer
+curline         rmb 2                           ; pointer to current line
+contline        rmb 2                           ; pointer to line for CONT
+contstmt        rmb 2                           ; interpretation pointer for CONT
+curstmt         rmb 2                           ; start of statement currently being interpreted
+endflag         rmb 1                           ; 00 = END, FF = STOP
+stringstackptr  rmb 2                           ; anonymous string descriptor stack pointer
+tok_skipkw      rmb 1                           ; flag for when skipping an unrecognized keyword
+tok_skipdt      rmb 1                           ; flag for when processing DATA
+tok_kwtype      rmb 1                           ; primary/secondary type flag for tokens
+tok_kwnum       rmb 1                           ; the actual token number
+tok_kwmatchl    rmb 1                           ; the length of the best match during lookup
+tok_kwmatch     rmb 2                           ; the current best matched token number
+val0            rmb val.size                    ; value accumulator 0
+val1            rmb val.size                    ; value accumulator 1
+                rmb 0x71-*                      ; align RSTFLG/RSTVEC for stock ROM compatibility
+RSTFLG          rmb 1                           ; 0x55 if RSTVEC is valid
+RSTVEC          rmb 2                           ; points to warm start routine (must start with NOP)
+inputptr        rmb 2                           ; pointer to current program execution location
+                rmb 0x100-*                     ; make sure the stuff that isn't direct page is outside of it
+SW3VEC          rmb 3                           ; SWI3 vector (for compatibility)
+SW2VEC          rmb 3                           ; SWI2 vector (for compatibility)
+SWIVEC          rmb 3                           ; SWI vector (for compatibility)
+NMIVEC          rmb 3                           ; NMI vector (for compatibility)
+IRQVEC          rmb 3                           ; IRQ vector (for compatibility)
+FRQVEC          rmb 3                           ; FIRQ vector (for compatibility)
+keyb_state      rmb 8                           ; rollover table state
+keyb_buff       rmb keyb_bufflen                ; the keyboard ring buffer
+linebuff        rmb linebuffsize                ; the line input buffer
+tokebuff        rmb linebuffsize+50             ; make it as long as line buffer plus a margin
+stringstack     rmb 5*stringstacknum            ; reserve space for the anonymous string descriptor stack           
+stringstackend  equ *                           ; end of string stack buffer
+                ifne *&0x1ff
+                rmb 0x200-(*&0x1ff)
+                endc
+textscreen      rmb 0x200                       ; the actual text screen (must be on 512 byte alignment)
+heapstart       equ *                           ; start of dynamically allocated stuff
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; The heap has the following items in order:
+;
+; Program text: preceded by a NUL and pointed to by progtext
+; Variable table: pointed to by vartab; contains records for all scalar and array variables
+; Free space: unused memory between the object table and the stack; pointed to by freestart
+; The stack: grows downward from the bottom of string space, pointed to by the stack pointer, obviously
+; String space: garbage collected non-constant string data pointed to by freetop
+; Reserved memory: immediately above string space; pointed to by memsize
+; Actual top of RAM: top of reserved memory; pointed to by memtop
+;
+; The variable table consists of several symbol tables defined as follows:
+;
+; Pointer       Size of entry   Variable types
+; vartabint     4               Integer scalars
+; vartablong    6               Long integer scalars
+; vartabfloat   7               Floating point scalars
+; vartabstring  6               String scalars
+;
+; Each entry starts with 2 bytes for the variable name followed by the data payload.
+                *pragmapop list