diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index df29cba93..ac471a7a8 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -850,8 +850,149 @@ asmsub restore_vera_context() clobbers(A) { rts }} } + + + ; Commander X16 IRQ dispatcher routines + +asmsub enable_irq_handlers() 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). + ; 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 {{ + php + sei + lda #<_irq_dispatcher + ldy #>_irq_dispatcher + sta cx16.CINV + sty cx16.CINV+1 + plp + rts + +_irq_dispatcher + jsr sys.save_prog8_internals + cld + lda cx16.VERA_ISR + lsr a + bcc + +_mod_vsync_jump + jsr _default_vsync_handler ; modified + cmp #0 + bne _dispatch_end + lda #1 + sta cx16.VERA_ISR + bra _return_irq ++ lsr a + bcc + +_mod_line_jump + jsr _default_line_handler ; modified + ldy #2 + sty cx16.VERA_ISR + bra _dispatch_end ++ lsr a + bcc + +_mod_sprcol_jump + jsr _default_sprcol_handler ; modified + ldy #4 + sty cx16.VERA_ISR + bra _dispatch_end ++ lsr a + bcc + +_mod_aflow_jump + jsr _default_aflow_handler ; modified + ; note: AFLOW can only be cleared by filling the audio FIFO for at least 1/4. Not via the ISR bit. + bra _dispatch_end ++ lda #0 +_dispatch_end + cmp #0 + beq _return_irq + jsr sys.restore_prog8_internals + jmp (sys.restore_irq._orig_irqvec) ; continue with normal kernal irq routine +_return_irq + jsr sys.restore_prog8_internals + ply + plx + pla + rti + +_default_vsync_handler + lda #1 + rts +_default_line_handler + lda #0 + rts +_default_sprcol_handler + lda #0 + rts +_default_aflow_handler + lda #0 + rts + }} } +asmsub set_vsync_irq_handler(uword address @AY) clobbers(A) { + ; Sets the VSYNC irq handler to use with enable_irq_handlers(). Also enables VSYNC irqs. + ; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes. + %asm {{ + sta enable_irq_handlers._mod_vsync_jump+1 + sty enable_irq_handlers._mod_vsync_jump+2 + lda #1 + tsb cx16.VERA_IEN + rts + }} +} + +asmsub set_line_irq_handler(uword rasterline @R0, uword address @AY) clobbers(A,Y) { + ; Sets the LINE irq handler to use with enable_irq_handlers(), for the given rasterline. Also enables LINE irqs. + ; You can use sys.set_rasterline() later to adjust the rasterline on which to trigger. + ; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes. + %asm {{ + sta enable_irq_handlers._mod_line_jump+1 + sty enable_irq_handlers._mod_line_jump+2 + lda cx16.r0 + ldy cx16.r0+1 + jsr sys.set_rasterline + lda #2 + tsb cx16.VERA_IEN + rts + }} +} + +asmsub set_sprcol_irq_handler(uword address @AY) clobbers(A) { + ; Sets the SPRCOL irq handler to use with enable_irq_handlers(). Also enables SPRCOL irqs. + ; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes. + %asm {{ + sta enable_irq_handlers._mod_sprcol_jump+1 + sty enable_irq_handlers._mod_sprcol_jump+2 + lda #4 + tsb cx16.VERA_IEN + rts + }} +} + +asmsub set_aflow_irq_handler(uword address @AY) clobbers(A) { + ; Sets the AFLOW irq handler to use with enable_irq_handlers(). Also enables AFLOW irqs. + ; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes. + %asm {{ + sta enable_irq_handlers._mod_aflow_jump+1 + sty enable_irq_handlers._mod_aflow_jump+2 + lda #8 + tsb cx16.VERA_IEN + rts + }} +} + + +asmsub disable_irq_handlers() { + ; back to the system default IRQ handler. + %asm {{ + jmp sys.restore_irq + }} +} + +} + + sys { ; ------- lowlevel system routines -------- @@ -962,9 +1103,7 @@ _modified plx pla rti - -_use_kernal .byte 0 - }} + }} } asmsub restore_irq() clobbers(A) { diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index 4477e8a5c..2955266f5 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -117,7 +117,8 @@ class TestCompilerOnExamplesCx16: FunSpec({ "kefrenbars", "keyboardhandler", "mandelbrot", - "multi-irq", + "multi-irq-old", + "multi-irq-new", "plasma", "rasterbars", "snow", diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 7b17f3298..ebe2868ad 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,6 +2,10 @@ TODO ==== +- move x16 irq examples over to new style handler +- optimize sys.set_rasterline on X16 +- document new x16 irq handling stuff in docs + - use TRB/TSB instructions more on the x16 such as when ack vera irq bits cx16.VERA_ISR |= 4 xx |= %0001000, xx &= %1110111 diff --git a/examples/cx16/multi-irq-new.p8 b/examples/cx16/multi-irq-new.p8 new file mode 100644 index 000000000..21edc8872 --- /dev/null +++ b/examples/cx16/multi-irq-new.p8 @@ -0,0 +1,51 @@ +%import palette +%import textio +%import syslib +%zeropage basicsafe + +; Example that shows a way to handle multiple IRQ sources on the X16. +; This uses the "NEW" way using the X16 specific interrupt handler routines in cx16. +; Currently only Vera interrupts are supported. VIA irqs are an exercise for the reader. + +main { + sub start() { + cx16.enable_irq_handlers() + cx16.set_line_irq_handler(150, &irq.line_irq) + cx16.set_vsync_irq_handler(&irq.vsync_irq) + + txt.print("\n\n\nx16 irq handlers installed (new style)\n") + txt.print("red = vsync irq\n") + txt.print("green = first line irq\n") + txt.print("blue = second line irq\n") + } +} + +irq { + sub vsync_irq() -> bool { + cx16.save_vera_context() + palette.set_color(0, $f00) + repeat 1000 { + cx16.r0++ + } + palette.set_color(0, $000) + cx16.restore_vera_context() + return true + } + + sub line_irq() -> bool { + cx16.save_vera_context() + if cx16.VERA_SCANLINE_L==150 { + palette.set_color(0, $0f0) + sys.set_rasterline(200) ; prepare next line irq + } else { + palette.set_color(0, $00f) + sys.set_rasterline(150) ; back to first line irq + } + repeat 500 { + cx16.r0++ + } + palette.set_color(0, $000) + cx16.restore_vera_context() + return false + } +} diff --git a/examples/cx16/multi-irq.p8 b/examples/cx16/multi-irq-old.p8 similarity index 86% rename from examples/cx16/multi-irq.p8 rename to examples/cx16/multi-irq-old.p8 index 68f50b3c5..6063bbd1c 100644 --- a/examples/cx16/multi-irq.p8 +++ b/examples/cx16/multi-irq-old.p8 @@ -4,7 +4,9 @@ %zeropage basicsafe ; Example that shows a way to handle multiple IRQ sources on the X16. -; Currently only Vera interrupts are supported. +; This uses the "OLD" way using the interrupt handler routines in sys. +; (see multi-irq-new example to do it the "new" way) +; Currently only Vera interrupts are supported. VIA irqs are an exercise for the reader. main { sub start() { @@ -12,8 +14,8 @@ main { sys.set_irq(&irq.master_handler) ; ..will just enable vsync.. cx16.VERA_IEN |= 2 ; .. so also enable line irq here. - txt.print("\n\n\nisr installed\n") - txt.print("red = vsync\n") + txt.print("\n\n\nx16 irq handlers installed (old style)\n") + txt.print("red = vsync irq\n") txt.print("green = first line irq\n") txt.print("blue = second line irq\n") }