Try the two programs in the attached zip file on a real XL/XE machine. press the joystick fire button!
1) s15.xex program. This works as expected. Nice colours down the screen and a raygun sound when firing
2) s64.xex program. Same, except the colours glitch and shift when firing.
program 1 syncs an IRQ exactly to a scan line using the ~15KHz clock (using 1.79MHz also works).
program 2 is using the ~64KHz Pokey clock. This doesn't sync exactly to a scan line (2 cycles out every line)
But why is program 2 causing such a big problem? The DLI's are NMI's (Non Maskable) so should always run at the correct time regardless of what the IRQ is doing? They are obviously not though. Note that this behaviour doesn't show up on any emulators I've tried, even those that supposedly have cycle exact POKEY emulation. But it happens on my PAL 800XL and 130XE. I thought I was beginning to understand the POKEY timers, but now I'm not sure
Maybe if an IRQ and NMI happen at exactly the same time the NMI doesn't get done? (I'd always heard to the contrary though)
Here's the code. Yes, I know it can be made faster, but it is only an example. Unfortunately I have passed the 64KHz code off as a good way to do sample playback before, but it's not much good if it doesn't work on real hardware.
; IRQ sample playback ; NB XL/XE only ATARI=0 .include "equates.asm" ; variables *=$f0 sample .ds 2 sample_nybble .ds 1 sample_value .ds 1 sample_size .ds 1 table_index .ds 1 trigger .ds 1 *=$02e0 .byte <run,>run ; initial run address ; main code *=$2000 run lda #0 sta NMIEN ; turn off NMI's sei sta IRQEN ; disable IRQ's lda #128+32+16+2 ; bit 7 on - ram at $5000, bits 4&5 on - 130XE compatible sta PORTB ; bit 1 on - disable os & rom, bit 0 off - disable basic lda #<nmi ; setup custom NMI handler sta $fffa lda #>nmi sta $fffa+1 lda #<irq ; setup custom IRQ handler sta $fffe lda #>irq sta $fffe+1 lda #<dlist ; set up a display list sta DLISTL lda #>dlist sta DLISTH lda #2+32 ; enable normal width screen+screen dma sta DMACTL lda #128+64 sta NMIEN ; turn on NMI's - DLI's and VBI cli ; enable IRQ's ldx #0 run1 txa asl a sta colour_table,x ; create a table of colours dex bne run1 ; initialize sample play irq play jsr init ; initialize the sample to play wait1 lda TRIG0 ; any other stuff can go here, but... beq wait1 ; just wait until fire button is pressed wait2 lda TRIG0 bne wait2 beq play ; end of main code init sei lda #<sample_start sta sample lda #>sample_start sta sample+1 lda #[>sample_end->sample_start] sta sample_size ; sample size in 256 byte pages lda #0 ;1 ; 0=POKEY 64KHz, 1=15KHz sta AUDCTL lda #15 ;3 ; ~64KHz clock 16 = ~4Khz timer, ~15KHz clock 4 = ~4KHz sta AUDF1 ; in timer 1 lda #$f0 ; test - no polycounters + volume only sta AUDC1 lda #1 sta IRQEN ; enable timer 1 lda #0 sta sample_nybble ; initialize nybble ldx #$f8 stx sample_value ; an initial sample value (with no polycounters + volume only bit) lda #$70 wait3 cmp VCOUNT bne wait3 ; sync to a scanline lda #0 sta SKCTL lda #3 sta SKCTL ; test - reset pokey and polycounters sta STIMER ; start timers cli rts ; IRQ irq pha ; save a lda sample_value sta AUDC1 ; play sample ASAP to minimise DMA lag tya pha ; save y ldy #0 sty IRQEN ; reset interrupt lda #1 sta IRQEN ; re-enable only timer 1 eor sample_nybble ; switch between 0 and 1 (right and left nybble) sta sample_nybble beq irq1 lda (sample),y lsr a lsr a lsr a lsr a ; left nybble of sample bpl irq2 ; always branch irq1 lda (sample),y and #$0f ; right nybble of sample inc sample ; next lo byte of sample bne irq2 inc sample+1 ; next hi byte of sample dec sample_size ; check if at end of sample bne irq2 ; branch if not tya sta IRQEN ; end of sample - reset interrupt beq irq3 ; always branch irq2 ora #$f0 ; no polycounters+volume only bit irq3 sta sample_value ; save sample to play next irq pla tay pla ; restore y and a rti ; NMI nmi pha bit NMIST ; check for cause of interrupt bvs vbi ; branch if a VBI ; DLI dli txa pha ldx table_index lda colour_table,x sta WSYNC sta COLBK inc table_index pla tax pla rti ; VBI ; critical vbi vbi sta NMIRES ; reset interrupt txa pha ; save x tya pha lda #0 sta table_index ; reset table index sta COLBK ; tsx ; lda $104,x ; and #$04 ; bne vbiexit ; exit VBI if VBI interrupted an IRQ ; cli ; allow IRQ to interrupt from now on ; deferred VBI here vbiexit pla tay pla tax pla ; restore x and a rti ; display list with some random DLIS *=$2400 dlist .byte $70,$70,$70 .byte $8D+$40,<screen_start,>screen_start .byte $8D,$D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$D,$8D,$8D,$D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$8D,$8D,$D,$8D,$8D,$8D .byte $8D,$D,$8D,$D,$8D,$8D,$D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$8D,$D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$D,$D,$D .byte $41,<dlist,>dlist ; sample data *=$2800 ; NB start on a 256 byte page boundary ; sample will stop playing at nearest page boundary if not a multiple of 256 bytes sample_start .incbin "raygun44.raw" ; 4KHz, 4-bit raw sample data sample_end ; screen data *=$8000 screen_start ; colour table *=$9000 colour_table

















