Fix handling of IRQs that occur when ROM is active
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user