Fix handling of IRQs that occur when ROM is active

This commit is contained in:
Richard Halkyard
2019-10-29 22:45:22 -05:00
parent 89c8a988bf
commit bf4c9c3c8c

View File

@@ -64,6 +64,15 @@ L1: lda sp,x
sta ENABLE_RAM
stx $FFFE ; Install interrupt handler
sty $FFFF
ldx IRQVec
ldy IRQVec+1
stx IRQInd+1
sty IRQInd+2
ldx #<IRQStub
ldy #>IRQStub
stx IRQVec
sty IRQVec+1
cli ; Allow interrupts
; Clear the BSS data.
@@ -94,6 +103,13 @@ _exit: pha ; Save the return code
lda #0
sta irqcount ; Disable custom IRQ handlers
sei
ldx IRQInd+1
ldy IRQInd+2
stx IRQVec
sty IRQVec+1
cli
; Copy back the zero-page stuff.
ldx #zpspace-1
@@ -121,9 +137,13 @@ L2: lda zpsave,x
; IRQ handler. The handler in the ROM enables the Kernal, and jumps to
; $CE00, where the ROM code checks for a BRK or IRQ, and branches via the
; indirect vectors at $314/$316.
; To make our stub as fast as possible, we skip the whole part of the ROM
; handler, and jump to the indirect vectors directly. We do also call our
; own interrupt handlers if we have any; so, they need not use $314.
;
; When RAM is banked in, we skip the whole part of the ROM handler, and jump to
; the indirect vectors directly, after calling our own interrupt handlers.
;
; When ROM is banked in, a stub installed in the $314 indirect vector ensures
; that our interrupt handlers are still called (otherwise, interrupts that are
; not serviced by the ROM handler may cause a deadlock).
.segment "LOWCODE"
@@ -138,15 +158,6 @@ IRQ: cld ; Just to be sure
and #$10 ; Test for BRK bit
bne dobreak
; It's an IRQ; and, RAM is enabled. If we have handlers, call them. We will use
; a flag here instead of loading __INTERRUPTOR_COUNT__ directly, since the
; condes function is not reentrant. The irqcount flag will be set/reset from
; the main code, to avoid races.
ldy irqcount
beq @L1
jsr callirq_y ; Call the IRQ functions
; Since the ROM handler will end with an RTI, we have to fake an IRQ return
; on the stack, so that we get control of the CPU after the ROM handler,
; and can switch back to RAM.
@@ -160,7 +171,7 @@ IRQ: cld ; Just to be sure
pha ; Push faked X register
pha ; Push faked Y register
sta ENABLE_ROM ; Switch to ROM
jmp (IRQVec) ; Jump indirect to Kernal IRQ handler
jmp (IRQVec) ; Jump indirect to IRQ stub
irq_ret:
sta ENABLE_RAM ; Switch back to RAM
@@ -182,6 +193,20 @@ nohandler:
sta ENABLE_ROM
jmp (BRKVec) ; Jump indirect to the break vector
; IRQ stub called by the Kernal IRQ handler, via $314.
; If we have handlers, call them. We will use a flag here instead of loading
; __INTERRUPTOR_COUNT__ directly, since the condes function is not reentrant.
; The irqcount flag will be set/reset from the main code, to avoid races.
IRQStub:
cld ; Just to be sure
sta ENABLE_RAM
ldy irqcount
beq @L1
jsr callirq_y ; Call the IRQ functions
@L1: sta ENABLE_ROM
jmp (IRQInd+1) ; Jump to the saved IRQ vector
; ------------------------------------------------------------------------
; Data