Jump to content

Photo

How can POKEY IRQ Timers mess up NMI timing?


210 replies to this topic

#1 Sheddy OFFLINE  

Sheddy

    Dragonstomper

  • 769 posts
  • Location:UK

Posted Fri Aug 7, 2009 7:43 PM

I've been doing some experimenting, but I'm at a loss to understand what's going on here:
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.

[codebox]
; 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
[/codebox]

Attached Files



#2 Rybags OFFLINE  

Rybags

    Gridrunner

  • 15,990 posts
  • Location:Australia

Posted Fri Aug 7, 2009 7:55 PM

In another thread, atariksi claimed you could mask System Reset (NMI) on 400/800 - I thought it was impossible without resorting to crashing the CPU but it isn't.

I think it comes down to a couple of things:
- Antic only holds the NMI line at (low) activate level for a certain period. If you can distract the CPU long enough, it can be missed.
- IIRC, the 6502 itself has a "bug" such that if an NMI and IRQ occur at the same time, the NMI can actually be ignored.

IRQs are level triggered, ie IRQ sources tend to hold the IRQ line low until the CPU services the interrupt and takes action to disable it.
NMIs are edge triggered, ie they rely on the CPU acting upon a high->low transition of the NMI input. For example, on the C64 there's the old trick of masking out the RESTORE key. Normally it's not maskable in any way, but it can be disabled by triggering a Timer NMI, and having a custom routine that does an RTI without resetting the NMI status on the CIA.

I haven't tried your program, will see if I can give it a go later. For your scanline exact IRQ, it might be desirable to set it up to occur at a different time so that it doesn't clash with DLIs.

#3 Bryan OFFLINE  

Bryan

    Quadrunner

  • 10,926 posts
  • Cruise Elroy = 4DB7
  • Location:Chesaning, MI

Posted Fri Aug 7, 2009 8:27 PM

Is there any chance that the IRQ service is introducing a longer delay than the usual instructions that are running? Here's the internals of an IRQ request:

Posted Image

A sequential IRQ/DLI can take up to 13 cycles to get to the first instruction of the DLI. Is that more latency than your code is designed to deal with?

#4 Rybags OFFLINE  

Rybags

    Gridrunner

  • 15,990 posts
  • Location:Australia

Posted Fri Aug 7, 2009 8:49 PM

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

That's not an exact align there for the timer, it could drift a couple of cycles either way.

I'd change it a bit. Put the reset SKCTL and poly stuff before the wait loop. Do a STA WSYNC immediately before the STA STIMER. That should make the IRQ occur earlier in the scanline, might be enough to stop if interfering with the NMIs.

If that don't work, I'd try different delay loops after the WSYNC until you hit the sweet spot.

#5 Heaven/TQA OFFLINE  

Heaven/TQA

    Quadrunner

  • 11,160 posts
  • Location:Baden-Württemberg, Germany

Posted Sat Aug 8, 2009 2:27 AM

Sheddy... your post came right in time... ;) Rybags/Miker know why... :)

#6 atariksi OFFLINE  

atariksi

    Quadrunner

  • 5,337 posts

Posted Sat Aug 8, 2009 5:49 AM

...
sta PORTB ; bit 1 on - disable os & rom, bit 0 off - disable basic
...

Your "rem" statement is wrong. That may explain the problem.

>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)

64Khz should be out of sync since clocks are as follows:

1.789773 is color burst/2.
15.7Khz is exactly 1.789773/114.
64Khz is exactly 1.789773/28 (they would have to divide by 28.5 to get it divide evenly into scanline cycles).

>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 :(

Actually, even at 1.79Mhz it should NOT work (cause glitches). You may just have tried a case where it was phase shifted enough to not cause conflict.

#7 atariksi OFFLINE  

atariksi

    Quadrunner

  • 5,337 posts

Posted Sat Aug 8, 2009 5:56 AM

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

That's not an exact align there for the timer, it could drift a couple of cycles either way.

I'd change it a bit. Put the reset SKCTL and poly stuff before the wait loop. Do a STA WSYNC immediately before the STA STIMER. That should make the IRQ occur earlier in the scanline, might be enough to stop if interfering with the NMIs.

If that don't work, I'd try different delay loops after the WSYNC until you hit the sweet spot.


Sta STIMER will reset timers to zero for IRQ; what is initializing SKCTL accomplishing? True, he should base things on WSYNC so he knows the EXACT cycle when things start but his problem is the 64Khz mode since that's misaligned with scanline rate anyway. He needs to use some divisor (prime # or something) so that DLI triggerring and IRQ triggerring cycles never overlap assuming that's the entire issue here.

#8 Rybags OFFLINE  

Rybags

    Gridrunner

  • 15,990 posts
  • Location:Australia

Posted Sat Aug 8, 2009 6:08 AM

I wouldn't worry about SKCTL... maybe he just wants a known sequence of Random numbers for whatever reason.

I'm going to have to play around with some of this stuff... although I suspect our answer lies in how the 6502 does Interrupt processing, I'm sure there's a known issue where it can ignore an NMI under certain circumstances.

#9 Sheddy OFFLINE  

Sheddy

    Dragonstomper

  • Topic Starter
  • 769 posts
  • Location:UK

Posted Sat Aug 8, 2009 7:01 AM

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

That's not an exact align there for the timer, it could drift a couple of cycles either way.

I'd change it a bit. Put the reset SKCTL and poly stuff before the wait loop. Do a STA WSYNC immediately before the STA STIMER. That should make the IRQ occur earlier in the scanline, might be enough to stop if interfering with the NMIs.

If that don't work, I'd try different delay loops after the WSYNC until you hit the sweet spot.


Sta STIMER will reset timers to zero for IRQ; what is initializing SKCTL accomplishing? True, he should base things on WSYNC so he knows the EXACT cycle when things start but his problem is the 64Khz mode since that's misaligned with scanline rate anyway. He needs to use some divisor (prime # or something) so that DLI triggerring and IRQ triggerring cycles never overlap assuming that's the entire issue here.


yes, sorry, starting timers with the 64KHz clock at a certain point in a scanline is a red herring to the main concern. It should drift 2 cycles every scan line. The code is only in there because it was enough to help avoid the NMI's with the 15KHz and I just left it in there when changing the code to use the 64KHz clock.

the reset SKCTL was just a test to see if polycounters made any difference to timers (if they do it is subtle)

The main concern is why IRQ's can affect NMI's so badly? Keeping IRQ's from triggering at/near NMI's avoids the problem, but doesn't explain the problem. I don't think the IRQ latency can be it if the 15KHz works. If the NMI's aren't actually being ignored, maybe they are being delayed by the IRQ if they trigger at the same time? I'm still tending towards thinking Rybags is correct and this is a 6502C "feature". It is a shame if that is the case, as even with it getting interrupted by NMI's, sample playing with 64KHz clock sounds pretty good on real hardware, and only needs 1 channel.

Why do you think the CPU based POKEY clock shouldn't work Atariksi? It can trigger at a specific point in the scan line as per the 15KHz clock. Or do you just mean the lax timing when starting the timer (no WSYNC etc.)?

Edited by Sheddy, Sat Aug 8, 2009 7:07 AM.


#10 Rybags OFFLINE  

Rybags

    Gridrunner

  • 15,990 posts
  • Location:Australia

Posted Sat Aug 8, 2009 7:09 AM

Just tried a couple of things, kinda distantly related.

NMIST ($D40F) will reflect a requested NMI whether that NMI is enabled or not, e.g. you can setup a DList with DLIs in it and it will still be triggered within Antic and reflected in NMIST - of course, the 6502 isn't signalled.
This is already known... technique has been described before "DLIs in polling mode". In polling mode, we typically store to NMIRES to clear the pending DLI.

Despite NMIST reflecting a requested DLI, by enabling DLIs in NMIEN, the pending DLI will not trigger an NMI to the 6502... (ed) tested this just then, even a tight loop waiting for NMIST to trigger a DLI, then re-enabling DLIs won't trigger it.

Despite NMIST holding a pending DLI, it seems a VBlank NMI resets that bit, ie - the OS won't get confused and JMP ($0200) instead of processing the VBI.

Edited by Rybags, Sat Aug 8, 2009 7:24 AM.


#11 Bryan OFFLINE  

Bryan

    Quadrunner

  • 10,926 posts
  • Cruise Elroy = 4DB7
  • Location:Chesaning, MI

Posted Sat Aug 8, 2009 7:47 AM

I wonder what it would do on an 800 without a 6502C. I see that you've taken over the FFFx vectors, though.

Edited by Bryan, Sat Aug 8, 2009 8:18 AM.


#12 Sheddy OFFLINE  

Sheddy

    Dragonstomper

  • Topic Starter
  • 769 posts
  • Location:UK

Posted Sat Aug 8, 2009 8:22 AM

I wonder what it would do on an 800 without a 6502C (if the PORTB stuff was removed).


yes, that would be interesting. I did try the code with the OS and it has the same problem. I'll post it so someone can give it a go.

#13 Rybags OFFLINE  

Rybags

    Gridrunner

  • 15,990 posts
  • Location:Australia

Posted Sat Aug 8, 2009 8:25 AM

I'd get my 'scope onto the NMI line and see what's going on internally, but I've virtually trashed one machine in the last 24 hours... don't want to risk another just yet.

#14 Bryan OFFLINE  

Bryan

    Quadrunner

  • 10,926 posts
  • Cruise Elroy = 4DB7
  • Location:Chesaning, MI

Posted Sat Aug 8, 2009 8:48 AM

I took out the sta WSYNC to see where the DLI was being serviced. It's about 1/3 of the way across (well, that's where the COLBK change is)- unless you're playing sound- then it scoots around.

Edited by Bryan, Sat Aug 8, 2009 8:51 AM.


#15 atariksi OFFLINE  

atariksi

    Quadrunner

  • 5,337 posts

Posted Sat Aug 8, 2009 9:25 AM

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

That's not an exact align there for the timer, it could drift a couple of cycles either way.

I'd change it a bit. Put the reset SKCTL and poly stuff before the wait loop. Do a STA WSYNC immediately before the STA STIMER. That should make the IRQ occur earlier in the scanline, might be enough to stop if interfering with the NMIs.

If that don't work, I'd try different delay loops after the WSYNC until you hit the sweet spot.


Sta STIMER will reset timers to zero for IRQ; what is initializing SKCTL accomplishing? True, he should base things on WSYNC so he knows the EXACT cycle when things start but his problem is the 64Khz mode since that's misaligned with scanline rate anyway. He needs to use some divisor (prime # or something) so that DLI triggerring and IRQ triggerring cycles never overlap assuming that's the entire issue here.


yes, sorry, starting timers with the 64KHz clock at a certain point in a scanline is a red herring to the main concern. It should drift 2 cycles every scan line. The code is only in there because it was enough to help avoid the NMI's with the 15KHz and I just left it in there when changing the code to use the 64KHz clock.

the reset SKCTL was just a test to see if polycounters made any difference to timers (if they do it is subtle)

The main concern is why IRQ's can affect NMI's so badly? Keeping IRQ's from triggering at/near NMI's avoids the problem, but doesn't explain the problem. I don't think the IRQ latency can be it if the 15KHz works. If the NMI's aren't actually being ignored, maybe they are being delayed by the IRQ if they trigger at the same time? I'm still tending towards thinking Rybags is correct and this is a 6502C "feature". It is a shame if that is the case, as even with it getting interrupted by NMI's, sample playing with 64KHz clock sounds pretty good on real hardware, and only needs 1 channel.

Why do you think the CPU based POKEY clock shouldn't work Atariksi? It can trigger at a specific point in the scan line as per the 15KHz clock. Or do you just mean the lax timing when starting the timer (no WSYNC etc.)?


The CPU based clock (1.79Mhz) works as long as it's phase shifted away from the DLI. The 15Khz cannot be phase shifted from the tests that I did. Even if you delay STIMER, the 15Khz interrupt seems to occur at exact same spot on the scanline. Enclosed below is the IRQ-based code that locks out the System Reset NMI on Atari 400/800-- but it doesn't work if I use 15Khz timer regardless of phase shift. By the way, I see you are optimizing code by writing directly to ROM vector area, but then you mine as well do STA ZP1 and STX ZP2 rather than to PHA, TXA/PHA and reverse for exiting from DLI.

;*** ATIRQ400.asm: Atari Timer IRQ measured to exact cycles so no WSYNC is required by Krishna Software Inc.
;*** This plays music on voice #1 and uses IRQ timer interrupt on timer #4. Voice #0 is for constant tone.
;*** There are three CLKs which can be used by either 8 or 16 bit divisors as controlled by 53768.
;*** The 15.6999Khz CLK and 63.921Khz CLK can be turned off via 53775 but 1.78979Mhz CLK is always on.
;*** The 15.6999Khz CLK is 1.78979Mhz/114 and gives the scanline interrupt at end of scanline.
;*** The 63.921Khz CLK is 1.78979Mhz/28 and does not divide evenly into 29868 cycles/frame so cannot be used
;*** for color clock accurate or scanline accurate interrupts. This program also attempts to write IRQ vector
;*** directly at 65534/65535 by disabling ROM for XL/XE series (should have no effect on Atari 400/800).
;*** This program locks out SYSTEM RESET on Atari 400/800 where it's a NMI.
TIMERFREQLSB = 53760
TIMERFREQMSB = 53762
WSYNC = 54282
VCOUNT = 54283

DOSVEC = 10
CASINI = 2
WARMSTART = 58484
VMIRQ = 534 ;hardware irq ptr

ORG = 600h
;DW 0FFFFh
;DW StartAdr
;DW LastOffset-1
DB 0,3 ;# of sectors to load 1..255
DW ORG
DW StartAdr
Rts
StartAdr: Lda #MyReset,L
Sta CASINI
Lda #MyReset,H
Sta CASINI+1
Lda #0
Sta 580
Lda #2
Sta 9
Jmp WARMSTART
MyReset: Lda #2
Sta 9
Lda #MyReset,L
Sta CASINI
Lda #MyReset,H
Sta CASINI+1
Sei
Lda #0 ;no VBIs nor DLIs for maximum performance
Sta 54286
Sta 53774 ;disable all IRQs
Sta 54272 ;turn off screen
Sta 54017 ;disable ROM and BASIC for XL/XE series via PORTB
Lda #TimerTwoIRQ,L ;general IRQ routine but we use only for timer #2
Sta VMIRQ
Ldy #TimerTwoIRQ,H
Sty VMIRQ+1
Sta 65534
Sty 65535
Cpy 65535
Beq ROMVectorSet
Lda #IdleLoop,L
Sta SelfModifyThis+1 ;Remove 5 cycles from background task (for Jmp [534])
Lda #IdleLoop,H
Sta SelfModifyThis+2
ROMVectorSet: Lda #40 ;+40 for join channels 3,4; +80 for channels 1+2 @1.79Mhz
Sta 53768 ;join channels at 1.79 Mhz
Lda #160
Sta 53760
Lda #143
Sta 53761
Lda #175
Sta 53763
Lda #114-7 ;lsb 114-7 or 57-7 for two irqs/scanline
Sta 53764 ;timer #2 freq = 1789790/[A+1]
Lda #0 ;msb for rate divisor A
Sta 53766
Lda #4 ;1,2,4=timer interrupts
Sta 53774 ;enable IRQ #2
Lda #128
;Sta 53248 ;unrem to show sprite using 0 CPU cycles and 0 DMA cycles
Lda #144
;Sta 53249
Lda #255
Sta 53261
Sta 53262
Sta 53256
Sta 53257
Lda #32
Sta 53275
Lda #10
Sta 53266
Lda #12
Sta 53267
Ldx #0
Ldy #4 ;load ack parameters
Lda #65
NotMidScr: Cmp VCOUNT
Bne NotMidScr ;CF=1 when A=65
Sta WSYNC
Nop
;Nop
CLI
Sta 53769 ;start timer counter
;CLI
;Nop
;Nop
;Nop
;Nop
;Lda #34
;Sta 54272
IdleLoop: ;(29868 cycles in NTSC frame - 9*262 for Refresh = 27510 cycles so to keep the IRQ
; stable, cycles must be aligned to 27510 including IRQ routine). For PAL frame, use
; 312*114 = 35568 - 312*9 = 32760 cycles.)
;(27510 factors to 2*5*7*3*131)

Lda #0
Sta 53274
Lda 1535
;Lda 53770 ;No longer random if bits 0,1 of 53775 = 0
ROL
Sta 53274
;Rol
Sta 53762 ;make construction site noise
Lda #48
Sta 53274
Lda #64 ;which of these two instruction to rem out depends on IRQ routine total cycles
;Sta 53274
Lda #96
Sta 53274
Lda #128
Sta 53274
Lda #144
Sta 53274
Lda #160
Sta 53274
Lda #176
Sta 53274
SelfModifyThis: Jmp Idle1 ;For Atari 400/800, this jmp and following NOP (5 cycles) will be removed since ROM vector not used
Idle1: Nop
Jmp IdleLoop

;*** H/W does jmp [65534] (7 cycles) and XL/XE OS does a CLD and JMP [534] for 2+5 = 7 cycles.
;*** Below routine 32 cycles+7+5 = 44 cycles on old OS (like on Atari 400/800).
;*** Optimized by using X/Y registers and not using it in main routine so PHA/PLA not needed.
TimerTwoIRQ: ;Pha ;3 cycles
;Lda #255
;Sta 53771 ;POTGO
;Nop
Inc 53274 ;change register (like color for example)
;Lda #0
;Sta 53774
;Lda #4
;Sta 53774 ;send ack to timer irq
Stx 53774
Sty 53774 ;do ack
Inc 1535
;Nop
;Lda #96
Ror 53274 ;change register (like color for example)
;Pla ;4 cycles
Rti ;6 cycles

;LastOffset: DW 2e0h,2e1h,ORG

#16 Sheddy OFFLINE  

Sheddy

    Dragonstomper

  • Topic Starter
  • 769 posts
  • Location:UK

Posted Sat Aug 8, 2009 9:29 AM

I'd get my 'scope onto the NMI line and see what's going on internally, but I've virtually trashed one machine in the last 24 hours... don't want to risk another just yet.


ha, ha! sounds like you've been having fun. what are you up to now? (the interlace discovery is very cool BTW)

I took out the sta WSYNC to see where the DLI was being serviced. It's about 1/3 of the way across (well, that's where the COLBK change is)- unless you're playing sound- then it scoots around.


interesting. no way should the sound affect when the DLI is serviced that much

here's the code using the OS, which should also work on an 800. Programs in the zip as before. If anyone can try and see if the colours glitch and wobble with s64.xex on a real 800 it'd be much appreciated.

ATARI=0	.include "equates.asm"; variables	*=$f0sample 		.ds 2sample_nybble	.ds 1sample_value	.ds 1sample_size	.ds 1table_index	.ds 1	*=$02e0	.byte <run,>run			; initial run address; main code	*=$2000run	sei	lda #0	sta NMIEN			; turn off NMI's	sta POKMSK	sta IRQEN			; disable IRQ's	lda #<irq			; use immediate IRQ vector (no other OS IRQ's will happen)	sta VIMIRQ	lda #>irq	sta VIMIRQ+1	lda #<dlist			; set up a display list	sta SDLSTL	lda #>dlist	sta SDLSTH	lda #<vbi			; set up an immediate VBI	sta VVBLKI	lda #>vbi	sta VVBLKI+1	lda #<dli			; set up a DLI	sta VDSLST	lda #>dli	sta VDSLST+1	lda #128+64			; turn on NMI's - DLI's and VBI	sta NMIEN	cli				; enable IRQ's		ldx #0run1	txa	asl a	sta colour_table,x		; create a table of colours	dex	bne run1; initialize sample play irqplay	jsr init			; initialize the sample to playwait1	lda TRIG0			; any other stuff can go here, but...	beq wait1			; just wait until fire button is pressedwait2	lda TRIG0			; 	bne wait2	beq play; end of main codeinit	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				; ~64KHz POKEY clock;	lda #1				; ~15KHz POKEY clock		sta AUDCTL	lda #15				; ~64KHz clock divided by 16 = ~4Khz;	lda #3				; ~15KHz clock divided by 4 = ~4KHz	sta AUDF1			; in timer 1	lda #0	sta sample_nybble		; initialize nybble	sta AUDC1	lda #1	sta IRQEN			; enable timer 1	sta POKMSK	ldx #$18	stx sample_value		; an initial sample value (with volume only bit)	lda #$70wait3	cmp VCOUNT	bne wait3			; sync to a scanline (only relevant when 15KHz)	stx STIMER			; start timers	rts; IRQirq	pha				; save a	lda sample_value	sta AUDC1			; play sample ASAP to minimise DMA lag	tya	pha				; save y	ldy #0	lda #1	sty IRQEN			; reset interrupt	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 branchirq1	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	sta POKMSK	beq irq3			; always branchirq2	ora #$10			; turn on volume only bitirq3	sta sample_value		; save sample to play next irq	pla	tay	pla				; restore y and a	rti; DLIdli	pha	txa	pha	ldx table_index	lda colour_table,x	sta WSYNC	sta COLBK	inc table_index	pla	tax	pla	rti; VBIvbi	lda #0	sta table_index			; reset table index	sta COLBK	jmp SYSVBV			; 58463 - do system vertical blank processes; display list	*=$2400dlist	.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 bytessample_start	.incbin "raygun44.raw"		; 4KHz, 4-bit raw sample datasample_end; screen data	*=$8000screen_start; colour table	*=$9000colour_table

Attached Files



#17 Sheddy OFFLINE  

Sheddy

    Dragonstomper

  • Topic Starter
  • 769 posts
  • Location:UK

Posted Sat Aug 8, 2009 9:57 AM

The CPU based clock (1.79Mhz) works as long as it's phase shifted away from the DLI. The 15Khz cannot be phase shifted from the tests that I did. Even if you delay STIMER, the 15Khz interrupt seems to occur at exact same spot on the scanline. Enclosed below is the IRQ-based code that locks out the System Reset NMI on Atari 400/800-- but it doesn't work if I use 15Khz timer regardless of phase shift. By the way, I see you are optimizing code by writing directly to ROM vector area, but then you mine as well do STA ZP1 and STX ZP2 rather than to PHA, TXA/PHA and reverse for exiting from DLI.


OK, I see what you mean now. (As an aside, according to Altirra emu author Avery Lee, when POKEY is initialized should relate to where that spot on the scanline is for 15KHz as there is nothing else that particularly syncs it)
I don't understand how your code disables reset NMI yet, but it could well be related to the glitches I'm seeing.
Yes, no hardcore optimizes have been done! purely example code.

Edited by Sheddy, Sat Aug 8, 2009 9:58 AM.


#18 Rybags OFFLINE  

Rybags

    Gridrunner

  • 15,990 posts
  • Location:Australia

Posted Sat Aug 8, 2009 10:01 AM

I've done something and I've made the DLIs miss... I think even VBI gets clobbered at times.

The 16 KHz Timing should be able to be aligned to any cycle... I don't see why not. Pokey has no idea whatsoever what HCOUNT is, only Antic and GTIA bother to keep track.

Remember too, emulators don't do Timers properly. Don't trust them for this, or any hardcore stuff for that matter.

Another possibility though... could it be the case that it's important during what part of the scanline you actually switch AUDCTL to the 16 KHz mode, irrespective of what you do with STIMER down the track?

#19 Bryan OFFLINE  

Bryan

    Quadrunner

  • 10,926 posts
  • Cruise Elroy = 4DB7
  • Location:Chesaning, MI

Posted Sat Aug 8, 2009 10:16 AM

If NMI's are vulnerable, then wouldn't disk loading cause missed DLI's and VBI's? Do we have an example of a stable DLI-filled screen during loads?

#20 Rybags OFFLINE  

Rybags

    Gridrunner

  • 15,990 posts
  • Location:Australia

Posted Sat Aug 8, 2009 10:31 AM

atariksi: what Assembler, debugger, monitor were you testing with?

I'm loading stuff up here in Atari Asm/Ed...

I modified my own stuff, and came to the conclusion your theory was right.
But... try something like this:

lda $d20a
  eor seed; seed = just some number you choose
  tax
  eor #$7f
  tay
  lda #0
  sta $d20f; put POKEY in INIT state
wait1  dey
  bne wait1
  lda #3
  sta $d20f; POKEY back to normal
  txa
  eor #$44
  tay
wait2
  dey
  bne wait2
wait3
  dex
  bne wait3
  lda #1
  sta $d208; voices in 16 KHz mode
  sta $d209; STIMER... don't really need it.

OK, there's probably some stuff in there not needed, but I'm able to randomize where the IRQs occur during a scanline with something like this.

#21 Heaven/TQA OFFLINE  

Heaven/TQA

    Quadrunner

  • 11,160 posts
  • Location:Baden-Württemberg, Germany

Posted Sat Aug 8, 2009 10:32 AM

Bryan,

I never had a DLI screen without flickering while loading... look at Alternate Reality f.e.

#22 Sheddy OFFLINE  

Sheddy

    Dragonstomper

  • Topic Starter
  • 769 posts
  • Location:UK

Posted Sat Aug 8, 2009 10:45 AM

The CPU based clock (1.79Mhz) works as long as it's phase shifted away from the DLI. The 15Khz cannot be phase shifted from the tests that I did. Even if you delay STIMER, the 15Khz interrupt seems to occur at exact same spot on the scanline. Enclosed below is the IRQ-based code that locks out the System Reset NMI on Atari 400/800-- but it doesn't work if I use 15Khz timer regardless of phase shift. By the way, I see you are optimizing code by writing directly to ROM vector area, but then you mine as well do STA ZP1 and STX ZP2 rather than to PHA, TXA/PHA and reverse for exiting from DLI.


OK, I see what you mean now. (As an aside, according to Altirra emu author Avery Lee, when POKEY is initialized should relate to where that spot on the scanline is for 15KHz as there is nothing else that particularly syncs it)
I don't understand how your code disables reset NMI yet, but it could well be related to the glitches I'm seeing.
Yes, no hardcore optimizes have been done! purely example code.


On the slower clocks when you hit STIMER doesn't matter much - it doesn't reset the base clock. The timer will count down only when the base clock changes. I didn't understand this until recently.

#23 Rybags OFFLINE  

Rybags

    Gridrunner

  • 15,990 posts
  • Location:Australia

Posted Sat Aug 8, 2009 10:48 AM

DLIs aren't used during disk loads all that much... they can tend to upset SIO. Only XL/XE OS allows them too.

Missed VBIs aren't critical, although of course if there's DLIs running that rely on counters etc being reset, then you might get problems.

I think with the method I've employed, we should be able to work out where our 16 KHz IRQs occur... that way we could avoid conflict.
Of course, we can scrap all this random/seed loop stuff - I think it's probably a case of putting POKEY into 64 Khz mode, wait a little bit, then go into INIT state, do a known wait to a blank scanline then wait a little more. Then put Pokey into normal operation, finally switch to 15 KHz mode.

#24 Bryan OFFLINE  

Bryan

    Quadrunner

  • 10,926 posts
  • Cruise Elroy = 4DB7
  • Location:Chesaning, MI

Posted Sat Aug 8, 2009 11:04 AM

here's the code using the OS, which should also work on an 800. Programs in the zip as before. If anyone can try and see if the colours glitch and wobble with s64.xex on a real 800 it'd be much appreciated.


EDIT:

Okay, ran it on my 800 (Standard Rockwell R6502) and the glitches still happen.

Edited by Bryan, Sat Aug 8, 2009 11:13 AM.


#25 Rybags OFFLINE  

Rybags

    Gridrunner

  • 15,990 posts
  • Location:Australia

Posted Sat Aug 8, 2009 11:05 AM

I think I've got the whole situation worked out...

Since the OS does it's clear chipset register routine at cold/warmstart, we'd likely have a situation where Pokey always gets initialized and put back into normal mode "in sync" with the same spot on the scanline.

If you go through the OS, you see a loop similar to:

LDA #0
TAX
loop: sta $d000,x
sta $d200,x
sta $d400,x
inx
bne loop

etc.

During the loop of course, we're hitting $D40A. Further into the warmstart, the OS puts Pokey back into normal mode. I would suspect that in possibly 100% of cases, that always occurs at the same place in a scanline. It might be the case though, that with different revisions of the OS that the alignment would be slightly different.




0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users