Mercurial > hg > index.cgi
view src/lwbasic.s @ 20:0b3b4daa0d92
Fix off by one when detecting if line input buffer full
author | William Astle <lost@l-w.ca> |
---|---|
date | Tue, 08 Nov 2022 22:10:35 -0700 |
parents | 20fa3242c6a5 |
children | 2264569d7aca |
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? bhs 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