The Star Rider FIRQ handler is pretty nifty because it is designed to be called when the hardware blitter "special" chips have finished an operation and immediately give the blitter "special" chips more work to do. The neat thing about this design is that the main CPU can still be doing other work while the blitter chips are running. In previous Williams' games, such as Joust, the blitter chip halts the main CPU and no work can be done while the blitter chip is doing its thing. With Star Rider, the hardware guys (apparently) realized that halting the main CPU was a potential waste of time and instead let the main CPU keep running while simultaneously disabling the main CPU's access to the video/DMA bus. It's a really clever design for 1983/1984 or whenever.
Also cool is that the FIRQ handler can be called without an interrupt occurring (see $EAD9). If called in this way, the program will mimic what the CPU does for a real FIRQ so that the RTI (return from interrupt) instruction works correctly regardless of whether there was a real FIRQ or if the program faked it.
Finally, the FIRQ handler is even more nifty because it has an optional "wait for vsync" type behavior built-in. It can optionally check to see whether the next blit will cause visible vertical tearing and if so delay the blit until this is no longer a concern. I am not sure if this code actually ends up getting used in production because, just from eyeballing it, I am not 100% convinced that the vertical counter hardware value matches up with the actual vertical line value (maybe it does!) and the ROM program definitely seems to be making this assumption. Decide for yourself :)
ROM:EAD9 ; =============== S U B R O U T I N E =======================================
ROM:EAD9 ; Execute the FIRQ interrupt handler without the FIRQ interrupt firing.
ROM:EAD9 ; This appears to create an environment where RTI will act like RTS.
ROM:EAD9 ; NOTE: it appears that IRQ's are not disabled
ROM:EAD9 FakeFIRQ: ; CODE XREF: DoBlit:loc_0_EA90 P
ROM:EAD9 ; sub_0_EAB4+18 P
ROM:EAD9 tfr cc, a
ROM:EADB anda #$7F ; '' ; disable E flag (for subsequent RTI call)
ROM:EADD pshs a ; save CC flags (For subsequent RTI call)
ROM:EADF orcc #%1000000 ; disable F flag (for subsequent RTI call)
ROM:EAE1 FIRQ: ; DATA XREF: ROM:FFF6 o
ROM:EAE1 tst PIA_VGG_U7_PERFDIR_A ; clear PIA's IRQA'
ROM:EAE4 dec BlitterPendingOperationsCount ; When FIRQ gets called, it means a blitter operation just finished.
ROM:EAE4 ; So decrement the pending count.
ROM:EAE7 beq FIRQ_End ; branch if we have no more blitter operations left
ROM:EAE9 pshs a,b,x ; save regs (since this is FIRQ, they won't be automatically saved)
ROM:EAEB ldx BlitterContextPtr ; points to the current blitter context to be sent to the blitter hardware
ROM:EAEE lda BlitDontAvoidTearing ; 0: try to avoid vsync tearing artifacts
ROM:EAEE ; 1: blit as fast as possible ignoring vertical tearing
ROM:EAEE ; (see EAEE)
ROM:EAF1 bne DoBlitAndAdvanceBlitterContextPtr ; X points to the blitter context
ROM:EAF3 lda 8,x
ROM:EAF5 bmi DoBlitAndAdvanceBlitterContextPtr ; branch if IMPG value has high-bit set
ROM:EAF7 lda 5,x ; A = blitter destination Y value
ROM:EAF9 adda 7,x ; add blit size height to A
ROM:EAF9 ; This makes A now the bottom coordinate of the blit rectangle
ROM:EAFB suba HW_VerticalCounter ; Subtract current vertical counter value from blit bottom Y value.
ROM:EAFB ; If positive, beam is above bottom of blit can tearing can occur.
ROM:EAFB ; If negative, beam is below bottom of blit and tearing is not a risk.
ROM:EAFB ; AT LEAST that's what it is looking like to me! :)
ROM:EAFE bls DoBlitAndAdvanceBlitterContextPtr ; branch if tearing is not a concern
ROM:EB00 ldb #65 ; this appears to create a new blit size
ROM:EB00 ; of up to 64x65 pixels, but can be less.
ROM:EB00 ; It appears to be a way to stall to avoid tearing artifacts on the screen.
ROM:EB02 cmpa #64
ROM:EB04 bcs DoNullBlit ; if width is already less than 64 pixels, branch
ROM:EB06 lda #64 ; force width down to 64 pixels
ROM:EB08 DoNullBlit: ; CODE XREF: FakeFIRQ+2B j
ROM:EB08 std HW_BlitterSize
ROM:EB0B inc BlitterPendingOperationsCount ; make sure next time FIRQ fires, the blit we didn't perform gets redone
ROM:EB0E lda #%11000000 ; this appears to execute a NOP to the blitter chips by suppressing all writes. It may be to queue up a future FIRQ.
ROM:EB10 sta HW_BlitterControl ; writing here executes the blitter chips.
ROM:EB10 ; Control Bits
ROM:EB10 ; 7: 1=write suppress odd pixels
ROM:EB10 ; 6: 1=write suppress even pixels
ROM:EB10 ; 5: 1=shift image 1 pixel to the right (odd flavor)
ROM:EB10 ; 4: 1=solid color mode (constant substitution)
ROM:EB10 ; 3: 1=zero write suppress (only blit non-zero color values)
ROM:EB10 ; 2: 1=sync to E clock (half speed writes, RAM to RAM)
ROM:EB10 ; 1: write format (0=serial, 1=block)
ROM:EB10 ; 0: read format (0=serial, 1=block)
ROM:EB13 bra FIRQ_RestoreRegsThenEnd
ROM:EB15 ; ---------------------------------------------------------------------------
ROM:EB15 DoBlitAndAdvanceBlitterContextPtr: ; CODE XREF: FakeFIRQ+18 j
ROM:EB15 ; FakeFIRQ+1C j ...
ROM:EB15 ldd 8,x ; X points to the blitter context
ROM:EB17 std HW_IMPG_PRIME
ROM:EB1A ldd 6,x
ROM:EB1C std HW_BlitterSize
ROM:EB1F ldd 4,x
ROM:EB21 std HW_BlitterDest
ROM:EB24 ldd 2,x
ROM:EB26 std HW_BlitterSource
ROM:EB29 ldd ,x
ROM:EB2B stb HW_BlitterSolidClr
ROM:EB2E sta HW_BlitterControl ; writing here executes the blitter chips.
ROM:EB2E ; Control Bits
ROM:EB2E ; 7: 1=write suppress odd pixels
ROM:EB2E ; 6: 1=write suppress even pixels
ROM:EB2E ; 5: 1=shift image 1 pixel to the right (odd flavor)
ROM:EB2E ; 4: 1=solid color mode (constant substitution)
ROM:EB2E ; 3: 1=zero write suppress (only blit non-zero color values)
ROM:EB2E ; 2: 1=sync to E clock (half speed writes, RAM to RAM)
ROM:EB2E ; 1: write format (0=serial, 1=block)
ROM:EB2E ; 0: read format (0=serial, 1=block)
ROM:EB31 leax 10,x ; advance to the next blitter context in the array
ROM:EB31 ; (each context is 10 bytes long)
ROM:EB33 cmpx #word_0_AC1C ; have we got to the end of the blitter contexts?
ROM:EB36 bcs StoreNewBlitterContextPointer
ROM:EB38 ldx #BlitterContextArray ; a 1000 byte array which has room for 100 10-byte blitter context entries.
ROM:EB3B StoreNewBlitterContextPointer: ; CODE XREF: FakeFIRQ+5D j
ROM:EB3B stx BlitterContextPtr ; points to the current blitter context to be sent to the blitter hardware
ROM:EB3E FIRQ_RestoreRegsThenEnd: ; CODE XREF: FakeFIRQ+3A j
ROM:EB3E puls x,b,a
ROM:EB40 FIRQ_End: ; CODE XREF: FakeFIRQ+E j
ROM:EB40 ; End of function FakeFIRQ