view src/lwbasic.s @ 19:20fa3242c6a5

Fix modifier key state detection in keyboard driver
author William Astle <lost@l-w.ca>
date Tue, 08 Nov 2022 22:09:48 -0700
parents 6a046bd8107f
children 0b3b4daa0d92
line wrap: on
line source

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; LWBasic Version 0.1
; Copyright © 2022 Lost Wizard Enterprises Incorporated
;
; This is LWBasic, a replacement Basic ROM system for the TRS-80 Color Computer which
; is most definitely not binary compatible with the stock ROMs.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                *pragmapush list
                *pragma nolist
                *pragma noexpandcond
                *pragma cescapes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                
; Utility macros
;
; skip next byte; flags preserved
skip1           macro noexpand
                fcb 0x21                        ; opcode for BRN
                endm
; skip next byte and load nonzero to A
skip1lda        macro noexpand
                fcb 0x86                        ; opcode for LDA immediate
                endm
; skip next byte and load nonzero to B
skip1ldb        macro noexpand
                fcb 0xc6                        ; opcoe for LDB immediate
                endm
; skip next 2 bytes; clobbers flags
skip2           macro noexpand
                fcb 0x8c                        ; opcode for CMPX immediate
                endm
                *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 20                          ; 3 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)
                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
readlinenoecho  rmb 1                           ; if nonzero, the readline routine won't echo its input
                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)
console_curptr  rmb 2                           ; current cursor pointer for console driver
console_blnkdel rmb 1                           ; cursor blink delay
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
                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
                rmb 0x200-*                     ; unused
textscreen      rmb 0x200                       ; the actual text screen (must be on 512 byte alignment)
linebuff        rmb linebuffsize                ; the line input buffer
                org 0x8000                      ; the hardware puts the ROMs here; it's not negotiable
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
                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'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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      ldx #keyb_buff                  ; point to start of keyboard ring buffer
                stx keyb_buffw                  ; set write point there
                stx keyb_buffr                  ; set read point there (pointers equal means empty buffer)
                clr keyb_flags                  ; reset keyboard state 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      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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; General I/O handling package
;
; These routines operate on the I/O channel specified by filenum. The defined values of filenum are:
;
; 0: keyboard/screen console
;
; Read a line from the active file into linebuff. The resulting line will be NUL terminated leading to at most
; linbuffsize-1 character input. The trailing CR/LF will not be included. The input will be echoed if linebuffecho is
; enabled. Exit with the length of the input line in B.
readline        ldx #linebuff                   ; point to line input buffer
                clr ,x                          ; make sure buffer is NUL terminated
readline0       bsr readchr                     ; read an input character
                bcs readline1                   ; brif not EOF
                cmpa #0x0d                      ; CR (carriage return)
                beq readline1                   ; brif so - return
                cmpa #0x03                      ; BREAK?
                bne readline3                   ; brif not
                coma                            ; set carry for irregular exit
                skip1
readline1       clra                            ; clear carry for regular exit
                pshs cc                         ; save carry state
                lda readlinenoecho              ; are we echoing?
                bne readline2                   ; brif not
                lda #0x0d                       ; echo carriage return + line feed
                bsr writechr
readline2       tfr x,d                         ; get end address after input
                subd #linebuff                  ; subtract start of buffer; D is now length and C is clear
                clr ,x                          ; make sure line is NUL terminated
                puls cc,pc                      ; restore BREAK flag (C) and return
readline3       cmpa #0x08                      ; backspace?
                bne readline4                   ; brif not
                cmpx #linebuff                  ; at start of buffer?
                beq readline0                   ; brif so - do nothing
                leax -1,x                       ; move back buffer pointer
                bsr readlinee                   ; write a BS
                lda #0x20                       ; write a space
                bsr readlinee
                lda #0x08                       ; and finally a BS
                bsr readlinee
                bra readline0                   ; go process more characters
readline4       cmpa #0x0c                      ; form feed?
                bne readline5                   ; brif not
                bsr readlinee                   ; go echo character if needed
                bra readline                    ; go restart line entry
readline5       cmpa #0x20                      ; is it non-printing?
                blo readline0                   ; brif so - don't store it and continue
                bsr readlines                   ; stash character in buffer and echo if necessary
                bra readline0                   ; go get another character
readlines       cmpx #linebuff+linebuffsize-1   ; is the line buffer full?
                bhi readlinee0                  ; brif so - don't store character OR echo it
                sta ,x+                         ; stash character
readlinee       ldb readlinenoecho              ; are we echoing?
                bne readlinee0                  ; brif not
                bsr writechr                    ; echo the character
readlinee0      rts
; Write a character to the active file; all registers preserved but C will be set if the output file cannot handle
; an output character (doesn't exist, etc.)
writechr        tst filenum                     ; is it screen?
                beq writechr_scr                ; brif writing to screen
                orcc #1                         ; unknown device flag
                rts
; Handle output to the screen. This is where we convert CR to CRLF
writechr_scr    jsr console_outchr              ; output the character
                cmpa #0x0d                      ; was it CR?
                bne writechr_scr0               ; brif not
                lda #0x0a                       ; ouptut an LF
                jsr console_outchr
                lda #0x0d                       ; restore original value
writechr_scr0   andcc #0xfe                     ; clear error flag
                rts
; Read a character from the active file and return it in A; in the event that EOF is detected, readeof will be nonzero
; and the call will return with carry set.
readchr         clr fileeof                     ; flag not end of file (and clear carry)
                lda filenum                     ; get input file number
                beq readchr_kb                  ; brif keyboard input
                com fileeof                     ; flag end of file (C set and fileeof nonzero)
                rts
; Read character from keyboard; blink cursor while doing so
readchr_kb      pshs b                          ; preserve B as temp storage
                ldb [console_curptr]            ; get character at cursor
                inc console_blnkdel             ; activate cursor blinking (first interrupt will cycle it)
readchr_kb0     jsr keyb_getkey                 ; read keyboard
                bcc readchr_kb1                 ; brif we got a result
                cwai #0xaf                      ; wait for interrupt to scan keyboard
                bra readchr_kb0                 ; see if we have something yet
readchr_kb1     clr console_blnkdel             ; disable cursor blinking
                stb [console_curptr]            ; restore screen character
                clrb                            ; clear carry to indicate not eof
                puls b,pc                       ; restore temp and return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Immediate mode handler
prompt          fcn 'OK'
immediate       ldx #prompt                     ; point to prompt string
                jsr console_outstrn
immediate0      jsr readline                    ; read input line
                bcs immediate0                  ; brif ended with BREAK
                ; handle line
                bra immediate
                ifndef COCO3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Need to ensure the vectors are at 0xbff2
                zmb 0xbff2-*                    ; pad ROM up to the vector point
                fdb SW3VEC                      ; SWI3 vector
                fdb SW2VEC                      ; SWI2 vector
                fdb FRQVEC                      ; FIRQ vector
                fdb IRQVEC                      ; IRQ vector
                fdb SWIVEC                      ; SWI vector
                fdb NMIVEC                      ; NMI vector
                fdb START                       ; RESET vector (ROM entry point)
                endc
                ifdef COCO3
                zmb 0xfff2-*                    ; pad ROM to bottom of vectors
                fdb INT.SWI3                    ; SWI3 vector
                fdb INT.SWI2                    ; SWI2 vector
                fdb INT.FIRQ                    ; FIRQ vector
                fdb INT.IRQ                     ; IRQ vector
                fdb INT.SWI                     ; SWI vector
                fdb INT.NMI                     ; NMI vector
                fdb START                       ; RESET vector (ROM entry point)
                else
                zmb 0x10000-*                   ; pad ROM to full size
                endc