;--------------------------------------------------------------
; snd_irq -- Timer-1 handler: ONE 4-bit sample for every live voice, then the
; timer is switched off if that emptied the mixer. The OS enters it with CLD +
; JMP (VIMIRQ) and does NOT save anything, hence the pha/pla.
; 2026-09-29 (drac030): X AND Y are pushed at full width -- the body's
; sep #$30 clears both high bytes of the code it interrupted.
;--------------------------------------------------------------
.proc snd_irq
rep #$10 ; X/Y 16-bit first, pushed at that width
phx
phy
sep #$30 ; 8-bit A/X/Y for the body
pha
.ifdef ANTONIA2
phb ; DBR = $01 inside the card's divide (math.asm
phk ; udiv24): bank 0 for the absolute operands,
plb ; or POKEY is never acked (K = 0 here)
.endif
lda IRQST_R
and #$01
beq ?mine ; bit0 = 0 -> Timer-1, ours
lda PORTB ; foreign IRQ. snd_old_irq points INTO THE
lsr ; OS ROM, and the frame loop runs with it
bcs ?chain ; banked OUT (rom_out + urom_init), so the
lda POKMSK_R ; old chain jumped into whatever RAM lies
sta IRQEN_R ; under $E000-$FFFF and, never acking, came
jmp ?out ; straight back -- the freeze the 2026-08-07
; trace caught parked here.
?chain
.ifdef ANTONIA2
plb
.endif
pla ; A (one byte), then Y and X at the width
rep #$10 ; they were pushed; the OS chain gets
ply ; 8-bit X/Y
plx
sep #$10
jmp (snd_old_irq)
?mine
stz IRQEN_R ; ack Timer-1: drop bit 0, then restore it.
lda POKMSK_R ; POKMSK is $01 here -- this IS its timer,
sta IRQEN_R ; and snd_init/snd_disarm are the only
; writers (it holds $00 or $01, never
; more) -- so `and #$FE` of it is 0
ldx #SND_VTOP
?voice lda sv_act,x ; 0 = idle, 1 = playing
beq ?next
; --- WHICH NIBBLE: bit 7 of the voice's fraction. sv_frc counts the ...
lda sv_cur,x
bit sv_frc,x ; N = bit 7 (A untouched): the LOW nibble
bmi ?nib ; is current
lsr ; (NOT `?out` -- that is the handler's own
lsr ; exit label further down, and reusing it
lsr ; silently pointed the empty-mixer sweep's
lsr ; `beq ?out` back INTO this loop)
?nib and #$0F
ora #$10
bit sv_side,x ; snd_out INLINE on the sample path
bmi ?so_r ; (2026-09-15: the jsr/rts went, ~3,960
; times a second while anything plays).
sta.l AUDC1_R,x ; 2026-09-22 (rapidus-bus-timing): long,x has no
bvs ?so_d ; dummy read of POKEY before the store -- abs,x
?so_r sta.l AUDC1_R+$10,x ; paid a second chip cycle for it
?so_d ; output ASAP -- less jitter. STEREO
; (2026-08-31): the voice's side routes ...
lda sv_frc,x
clc
adc sv_stp,x
sta sv_frc,x
bcc ?next ; still inside this byte
inc sv_rl,x ; count UP toward $0000 = sample finished
bne ?adv
inc sv_rh,x
bne ?adv
inc sv_fin ; a voice just went quiet -- the ONLY way
; the mixer can empty, so the sweep below
; is owed exactly here (see ?any)
lda #0 ; done: this voice only. The timer belongs
sta sv_act,x ; to ALL of them, so only the sweep after
jsr snd_out ; the loop may switch it off. snd_out's
beq ?next ; (always: BIT with A=0 leaves Z=1, and
; both stores keep it -- the contract
; this beq was already leaning on)
?adv inc sv_al,x ; advance this voice's read address
bne ?fetch
inc sv_ah,x ; (a sound never crosses a bank end, so
; sv_ab is never touched -- wadsound.py)
; --- fetch the next byte.
?fetch jsr snd_fetch
?next dex
dex
bpl ?voice
; --- did that empty the mixer?
lda sv_fin
beq ?out
stz sv_fin
ldx #SND_VTOP
lda #0
?any ora sv_act,x
dex
dex
bpl ?any
cmp #0
bne ?out
; 2026-09-22 idiom: snd_disarm inlined (-12; its
stz POKMSK_R ; live body is these two stz -- see the proc)
stz IRQEN_R
?out
.ifdef ANTONIA2
plb
.endif
pla ; A ...
rep #$10 ; ... then Y and X at the FULL width the
ply ; entry pushed them
plx
rti ; (RTI's pulled P puts the real widths back)
.endp
https://turiecfoto.sk/atari/doom-test/d ... -2026c.zip