From 3000c399bd72abf726eb6c8f05ab2e229e16074f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 22 Dec 2025 21:13:31 +0100 Subject: [PATCH] cx16: add support for VIA timer IRQ in the irq routines --- compiler/res/prog8lib/cx16/syslib.p8 | 67 +++++++++++++- docs/source/targetsystem.rst | 13 ++- docs/source/todo.rst | 2 - examples/test.p8 | 133 ++++----------------------- 4 files changed, 90 insertions(+), 125 deletions(-) diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index 95dffde92..6103ffe72 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -424,10 +424,13 @@ cx16 { &ubyte via1pra = VIA1_BASE + 1 &ubyte via1ddrb = VIA1_BASE + 2 &ubyte via1ddra = VIA1_BASE + 3 + &uword via1t1 = VIA1_BASE + 4 &ubyte via1t1l = VIA1_BASE + 4 &ubyte via1t1h = VIA1_BASE + 5 + &uword via1t1lw = VIA1_BASE + 6 &ubyte via1t1ll = VIA1_BASE + 6 &ubyte via1t1lh = VIA1_BASE + 7 + &uword via1t2 = VIA1_BASE + 8 &ubyte via1t2l = VIA1_BASE + 8 &ubyte via1t2h = VIA1_BASE + 9 &ubyte via1sr = VIA1_BASE + 10 @@ -437,15 +440,18 @@ cx16 { &ubyte via1ier = VIA1_BASE + 14 &ubyte via1ora = VIA1_BASE + 15 - const uword VIA2_BASE = $9f10 ;VIA 6522 #2 + const uword VIA2_BASE = $9f10 ;VIA 6522 #2, not always present &ubyte via2prb = VIA2_BASE + 0 &ubyte via2pra = VIA2_BASE + 1 &ubyte via2ddrb = VIA2_BASE + 2 &ubyte via2ddra = VIA2_BASE + 3 + &uword via2t1 = VIA2_BASE + 4 &ubyte via2t1l = VIA2_BASE + 4 &ubyte via2t1h = VIA2_BASE + 5 + &uword via2t1lw = VIA2_BASE + 6 &ubyte via2t1ll = VIA2_BASE + 6 &ubyte via2t1lh = VIA2_BASE + 7 + &uword via2t2 = VIA2_BASE + 8 &ubyte via2t2l = VIA2_BASE + 8 &ubyte via2t2h = VIA2_BASE + 9 &ubyte via2sr = VIA2_BASE + 10 @@ -1322,7 +1328,7 @@ inline asmsub disable_irqs() clobbers(A) { asmsub enable_irq_handlers(bool disable_all_irq_sources @Pc) clobbers(A,Y) { ; Install the "master IRQ handler" that will dispatch IRQs - ; to the registered handler for each type. (Only Vera IRQs supported for now). + ; to the registered handler for each type. ; The handlers don't need to clear its ISR bit, but have to return 0 or 1 in A, ; where 1 means: continue with the system IRQ handler, 0 means: don't call that. %asm {{ @@ -1330,7 +1336,11 @@ asmsub enable_irq_handlers(bool disable_all_irq_sources @Pc) clobbers(A,Y) { sei bcc + lda #%00001111 - trb cx16.VERA_IEN ; disable all IRQ sources + ; disable all IRQ sources on VERA and VIA1 + trb cx16.VERA_IEN + lda #%01111111 + sta cx16.via1ier + + lda #<_irq_dispatcher ldy #>_irq_dispatcher sta cbm.CINV @@ -1348,6 +1358,8 @@ asmsub enable_irq_handlers(bool disable_all_irq_sources @Pc) clobbers(A,Y) { sty _aflow_vec+1 sta _sprcol_vec sty _sprcol_vec+1 + sta _timer1_vec + sty _timer1_vec+1 plp rts @@ -1357,11 +1369,12 @@ _vsync_vec .word ? _line_vec .word ? _aflow_vec .word ? _sprcol_vec .word ? +_timer1_vec .word ? _continue_with_system_handler .byte ? .send BSS _irq_dispatcher - ; order of handling: LINE, SPRCOL, AFLOW, VSYNC. + ; order of handling: LINE, TIMER1(VIA1), VSYNC, SPRCOL, AFLOW jsr sys.save_prog8_internals cld lda cx16.VERA_ISR @@ -1377,7 +1390,17 @@ _irq_dispatcher tsb _continue_with_system_handler pla -+ lsr a + ; timer1 (VIA1) irq? ++ pha + lda #%01000000 + and cx16.via1ifr + beq + ; not timer1 irq + sta cx16.via1ifr + jsr _timer1_handler + tsb _continue_with_system_handler ++ pla + + lsr a bcc + pha jsr _vsync_handler @@ -1422,6 +1445,8 @@ _sprcol_handler jmp (_sprcol_vec) _aflow_handler jmp (_aflow_vec) +_timer1_handler + jmp (_timer1_vec) }} } @@ -1486,6 +1511,28 @@ asmsub set_aflow_irq_handler(uword address @AY) clobbers(A) { }} } +asmsub set_timer1_irq_handler(uword address @AY) { + ; Sets the VIA1 TIMER1 irq handler to use with enable_irq_handlers(). Does not enable or disable VIA1 timer irqs setting. + %asm {{ + php + sei + sta enable_irq_handlers._timer1_vec + sty enable_irq_handlers._timer1_vec+1 + plp + rts + }} +} + + +sub set_timer1(uword delay, bool keeprunning) { + ; -- set VIA1 timer1 to trigger after the given delay (cycles). Enables VIA timer1 irqs if delay>0, otherwise disables it. + cx16.via1acr &= %00111111 + if keeprunning + cx16.via1acr |= %01000000 ; continuous (free-run) mode + cx16.via1ier = if delay==0 then %01000000 else %11000000 + cx16.via1t1lw = delay +} + inline asmsub disable_irq_handlers() { ; back to the system default IRQ handler. @@ -1681,6 +1728,16 @@ asmsub restore_irq() clobbers(A) { and #%11110000 ; disable all Vera IRQs but the vsync ora #%00000001 sta cx16.VERA_IEN + lda #%01111111 + sta cx16.via1ier + ; set timer1 back to free running mode + lda cx16.via1acr + and %00111111 + ora %01000000 + sta cx16.via1acr + lda #$ff + sta cx16.via1t1ll + sta cx16.via1t1lh plp rts .section BSS_NOCLEAR diff --git a/docs/source/targetsystem.rst b/docs/source/targetsystem.rst index 29289742e..217f858b0 100644 --- a/docs/source/targetsystem.rst +++ b/docs/source/targetsystem.rst @@ -276,7 +276,7 @@ Here they are, all available in ``cx16``: by setting the various handlers), or pass false to not touch this. The handlers don't need to clear its ISR bit, but have to return 0 or 1 in A, where 1 means: continue with the system IRQ handler, 0 means: don't call that. - The order in which the handlers are invoked if multiple interrupts occur simultaneously is: LINE, SPRCOL, AFLOW, VSYNC. + The order in which the handlers are invoked if multiple interrupts occur simultaneously is: LINE, TIMER1(VIA1), VSYNC, SPRCOL, AFLOW. ``set_vsync_irq_handler (uword address)`` Sets the verical sync interrupt handler routine. Also enables VSYNC interrupts. @@ -294,5 +294,16 @@ Here they are, all available in ``cx16``: Note: the handler must fill the Vera's audio fifo buffer by itself with at least 25% worth of data (1 kb) otherwise the aflow irq keeps triggering. +``set_timer1_irq_handler (uword address)`` + Sets the VIA1 TIMER1 irq handler to use with enable_irq_handlers(). Does not enable or disable VIA1 timer irqs setting. + ``disable_irq_handlers ()`` Hand control back to the system default IRQ handler. + +And a utility method to set the VIA1 timer1 to trigger IRQs at regular intervals: + +``sub set_timer1 (uword delay, bool keeprunning)`` + Set VIA1 timer1 to trigger after the given delay (cycles). + Enables VIA timer1 irqs if delay>0, otherwise disables it. + If keeprunning then the timer keeps triggering, otherwise it stops after a single trigger. + Note that the speed of the timer depends on the clock speed of the X16 (controlled by a jumper on the motherboard, usually 8 MHz). diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 7a1d8da80..93c9249e4 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,8 +1,6 @@ TODO ==== -- cx16: add support for VIA timer IRQ in the irq routines, example code for timer irqs see https://cx16forum.com/forum/viewtopic.php?p=37808 - Weird Heisenbug ^^^^^^^^^^^^^^^ - BUG: examples/cube3d-float crashes with div by zero error on C64 (works on cx16. ALready broken in v11, v10 still worked) diff --git a/examples/test.p8 b/examples/test.p8 index f59cee47a..405b0f631 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,129 +1,28 @@ %import textio +%zeropage basicsafe %option no_sysinit -; C64: 160, 220, 164 -; C128: 167, 229, 171 - main { + uword timercounter + sub start() { - ubyte char=0 - ubyte x,y - str name = sc:"irmen" - long setcc_time, setchr_time, setclr_time + cx16.enable_irq_handlers(true) + cx16.set_timer1_irq_handler(&timerhandler) + cx16.set_timer1(1000*8, true) ; timer irq every 1000 milliseconds (assuming 8mhz clock rate) - for x in 0 to 4 - txt.setcc(x+20, 19, name[x], x) - - txt.print("9 0 18 1 13 2 5 3 14 4\n") - for x in 0 to 4 { - txt.print_ub(txt.getchr(x+20, 19)) - txt.spc() - txt.print_ub(txt.getclr(x+20, 19) & 15) - txt.spc() - } - - sys.wait(200) - - cbm.SETTIML(0) - setchr() - setchr_time = cbm.RDTIML() - sys.wait(60) txt.cls() - - char = 0 - cbm.SETTIML(0) - setcc() - setcc_time = cbm.RDTIML() - sys.wait(60) - - cbm.SETTIM(0,0,0) - setclr() - setclr_time = cbm.RDTIML() - sys.wait(60) - txt.cls() - - txt.print("setchr: ") - txt.print_l(setchr_time) - txt.print("\n setcc: ") - txt.print_l(setcc_time) - txt.print("\nsetclr: ") - txt.print_l(setclr_time) - txt.nl() - repeat {} - - sub setchr() { - repeat 30 { - ubyte left = 0 - ubyte top = 0 - ubyte right = txt.width()-1 - ubyte bottom = txt.height()-1 - - while top bool { + timercounter++ + return false } }