Jump to content
Sheddy

How can POKEY IRQ Timers mess up NMI timing?

Recommended Posts

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.

 

But STA to STIMER for 15Khz does not change it's position in the scanline regardless of where you do it in the scanline.

Share this post


Link to post
Share on other sites

I think maybe the STIMER functionality mightn't be documented properly.

 

Could it be that it resets a voice only based upon the divisor it's working on.

 

e.g.

 

Say channel 1 is counting from 10 to 0 in 16 Khz mode. It's got 20 cycles remaining until it's divisor comes up. Maybe it's the case that hitting STIMER doesn't affect it until that next /114 event occurs.

 

Stuff like this could be tested easily enough... just do something like use the lower 2 bits of VCount as a delay loop counter, then STA STIMER at the finish of the loop inside the IRQ routine.

 

If my theory is wrong, then the timers should get skewed across the screen.

 

 

All this stuff could answer why my Pole Position mod just locked up sometimes... I think the game needs the DLI to execute always, as it sets vital flags and stuff for the VBI.

 

It opens a can of worms for 64 Khz timers too... we might have to employ some clever tricks to ensure these things don't trash our DLIs.

Share this post


Link to post
Share on other sites
I think maybe the STIMER functionality mightn't be documented properly.

 

STIMER also sets the hi/lo state of the audio channels. I would assume that an audible pop would be heard when the timers reset unless they happen asynchronously.

Share this post


Link to post
Share on other sites

It affects audio, which is easy to test. Just play a couple of sounds in Basic and POKE 53769,0 - you almost always hear them resetting.

 

Here's a demo program + source of randomizing the alignment of the Timers in 16 KHz mode. Just press Start and it will reinit Pokey on a random alignment. Real machine needed.

 

Program displays horizontal colour bars which indicate alignment.

 

Timer3.zip

Edited by Rybags

Share this post


Link to post
Share on other sites

I'm trying to decipher the Pokey schematic. My understanding is that STIMER always operates at system speed. That is, the counter is reloaded on the cycle of the strobe (asserts the LD input to the counter cell [cell 24]). It doesn't affect the main clock divisors so the next clock happens whenever it happens.

Share this post


Link to post
Share on other sites
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.

 

It works consistently across many Atari 400/800s that I have tried. But it is on an exact cycle on a scanline. If you look at the code I posted-- this part:

 

Sta WSYNC

Nop

;Nop

CLI

Sta 53769 ;start timer counter

 

has to be exact. If I add a NOP or remove one, the system reset starts working. And if I use 15Khz mode, I can't trap the system reset regardless of phase shift which led me to investigate that the 15khz timer interrupt is occurring on exact spot on scanline regardless of where you set it. So no NMIs will ever miss if you use 15Khz mode.

Share this post


Link to post
Share on other sites
I'm trying to decipher the Pokey schematic. My understanding is that STIMER always operates at system speed. That is, the counter is reloaded on the cycle of the strobe (asserts the LD input to the counter cell [cell 24]). It doesn't affect the main clock divisors so the next clock happens whenever it happens.

 

You need to look at when it decrements the counters in 15Khz mode.

Share this post


Link to post
Share on other sites

Well, adding this took care of the jitters... not sure what it means but it looks like NMI's are getting lost

 

irq
pha; save a
tya
pha
cli
lda NMIST
bpl continue;skip if not set

;insert an extra DLI here

ldy table_index
lda colour_table,y
sta WSYNC
sta COLBK
inc table_index

continue

lda sample_value
sta AUDC1; play sample ASAP to minimise DMA lag
tya

and so on....

 

Oh yes, I also added an NMIRES strobe to the DLI.

 

dli
sta NMIRES
pha
txa
pha
ldx table_index
lda colour_table,x
sta WSYNC
sta COLBK
inc table_index
pla
tax
pla
rti

 

EDIT: CLI is necessary-- I guess the CLI gives the DLI handler a chance to clear the NMI flags (but it's an NMI!). If they aren't clear, then it never happened and we must do it manually.

 

If there IS an NMI bug, then it's unique to the Atari because I can't find info on it anywhere (although the lost BRK bug is well known) and you'd think the 64 guys would have discovered it along time ago. Perhaps it's related to Atari's stretching of the clock on DMA cycles.

Edited by Bryan

Share this post


Link to post
Share on other sites

Seemingly knowledgeable resource about 6502 interrupts http://www.6502.org/tutorials/interrupts.html states NMIs have priority over IRQs in event of a clash. This doesn't seem to be so with Atari on what we've seen so far

 

I still hope that there is a flaw in the code somewhere, but it isn't looking good :sad:

Edited by Sheddy

Share this post


Link to post
Share on other sites

Here's what I'm guessing at this point:

 

The Atari is about the only machine that freezes the clock to stop the CPU. Most other machines have a different scheme. Perhaps if an NMI and IRQ both happen while the clock is stopped, the CPU is unable to sort them out like it normally would. I haven't looked at the datasheet in a while, but perhaps the timing diagrams might be of assistance.

Share this post


Link to post
Share on other sites
Here's what I'm guessing at this point:

 

The Atari is about the only machine that freezes the clock to stop the CPU. Most other machines have a different scheme. Perhaps if an NMI and IRQ both happen while the clock is stopped, the CPU is unable to sort them out like it normally would. I haven't looked at the datasheet in a while, but perhaps the timing diagrams might be of assistance.

 

Possibly, but it still behaves badly even when not using WSYNC. Does the clock get stopped for other things? (Can't be DMA, lots of machines do that)

Share this post


Link to post
Share on other sites
Seemingly knowledgeable resource about 6502 interrupts http://www.6502.org/tutorials/interrupts.html states NMIs have priority over IRQs in event of a clash. This doesn't seem to be so with Atari on what we've seen so far

 

I still hope that there is a flaw in the code somewhere, but it isn't looking good :sad:

 

NMIs do have priority over IRQs on Atari, but for the exception for one fixed cycle point on the scanline.

Share this post


Link to post
Share on other sites
Here's what I'm guessing at this point:

 

The Atari is about the only machine that freezes the clock to stop the CPU. Most other machines have a different scheme. Perhaps if an NMI and IRQ both happen while the clock is stopped, the CPU is unable to sort them out like it normally would. I haven't looked at the datasheet in a while, but perhaps the timing diagrams might be of assistance.

 

Possibly, but it still behaves badly even when not using WSYNC. Does the clock get stopped for other things? (Can't be DMA, lots of machines do that)

 

Well, DMA is done differently on a machine like the 64. It accesses the RAM at 2X the CPU speed and then uses an even/odd scheme for video and CPU accesses. I believe WSYNC pulls down the CPU's READY line instead of the clock-stretching HALT line.

 

Now I think my method didn't work.... I think I just locked up the CPU when I put the CLI in there (endless loop of IRQ responses). I wasn't playing the sample in my test code, so I wouldn't know for sure. Dang...

 

What assembler are you using so I can work directly with your code? I'm sick of TASM because I can't include binaries.

Edited by Bryan

Share this post


Link to post
Share on other sites

I agree, it definitely looks like the DLI is being lost. I've modified the test app to run interrupts at a much higher rate and added code to the VBI to resync the 64KHz clock and timer 1 at a known offset every frame. IRQs are periodically enabled and disabled and you can see the last DLI is being lost if you run it on a real Atari. It's definitely not a problem with the length of the routines or the DLI missing WSYNC because by my calculations there is about ~10 cycles of buffer in worst case in the original test app for NMI to STA WSYNC. I suspect that something funny happens if an IRQ strikes with exactly the right timing relative to when ANTIC pulls NMI, but I need to work out the exact cycle timing of timer 1 here. Whatever it is, though, this glitch is 100% reliable on my Atari with the timing locked.

 

(My Atari is an NTSC version, btw, so if you are testing on a PAL machine, you may need to delay the reset code in the VBI by 50 scan lines to make it reproduce. You should see the lower half of the screen periodically change color. I ported the assembly to MADS, too.)

 

I don't believe DMA timing and the resultant clock halting is the issue here. ANTIC pulls NMI at cycle 8, and the 6502 takes two cycles to acknowledge it, leaving cycle 10 as the earliest point at which a DLI or VBI will execute. DMA contention is never a problem in this area, however, because the earliest that playfield DMA occurs is cycle 10 (wide playfield, HSCROL=0, first scan line of character mode). The test app uses a normal playfield with no scrolling, which means the playfield DMA starts at cycle 18 instead, which is a long way off.

 

The NMIRES strobe in ANTIC is a bit unusual, and as far as I can tell, the most you need it for is the VBI at most, and maybe not even that. DLIs never have to strobe NMIRES because NMIs are edge triggered instead of level triggered like IRQs are, so an interrupt only happens when ANTIC requests it even if the DLI request bit stays set. What usually happens is that the DLI bit latches on at the first DLI on screen and then stays set until it is automatically cleared by ANTIC when the VBI is fired at scan line 248. I'm pretty sure on this since I had to do a bunch of testing on this on a real Atari when to figure out why Zaxxon was glitching in Altirra.

irq.zip

Edited by phaeron

Share this post


Link to post
Share on other sites
...

I don't believe DMA timing and the resultant clock halting is the issue here. ANTIC pulls NMI at cycle 8, and the 6502 takes two cycles to acknowledge it, leaving cycle 10 as the earliest point at which a DLI or VBI will execute. DMA contention is never a problem in this area, however, because the earliest that playfield DMA occurs is cycle 10 (wide playfield, HSCROL=0, first scan line of character mode). The test app uses a normal playfield with no scrolling, which means the playfield DMA starts at cycle 18 instead, which is a long way off.

...

 

In my code, I have screen off so only refresh cycles are occurring and NMI is missed so it can't be DMA-related. Which clock is stopping?

Share this post


Link to post
Share on other sites

Chris,

 

Try this for me:

 

1. Add a sta NMIRES to the end of DLI code:

 

dli

pha
txa
pha
ldx table_index
lda colour_table,x
sta WSYNC
sta COLBK
sta sample_value
inc table_index
pla
tax
pla
sta NMIRES	
rti

 

Then make the beginning of the irq look like this:

 

irq
pha		; save a
tya
pha
lda NMIST
nop
and NMIST	;2 checks!
bpl continue	;skip if not set

;insert a virtual DLI here
ldy table_index
lda colour_table,y
sta WSYNC
sta COLBK
inc table_index

continue

lda sample_value
sta AUDC1	; play sample ASAP to minimise DMA lag
tya.....
......and so on....

 

That worked for me provided I put the NOP spacer in between the first and second read of NMIST.

Edited by Bryan

Share this post


Link to post
Share on other sites
I've been messing around with it and it occasionally becomes stable, but I can't tell why.

 

Maybe Atariksi's one "bad" cycle could explain it? The 64KHz clock doesn't hit every possible cycle position on the scanline, only every other one, as it's 2 cycles different every scan line. It might be expected to be stable more often in that case though...

 

BTW am using ATasm assembler, although you're probably better off using MADS and Phaeron's code. I must be about the only one left still using ATasm.

Share this post


Link to post
Share on other sites
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.

...

I'm using MPDOS (PC-based 6502 assembler that executes on real Atari via parallel port).

 

>But... try something like this:

 

>

  lda $d20a

> eor seed; seed = just some number you choose

...

 

I think you need to narrow it down to exact cycle on the scanline-- make the miss occur consistently.

Share this post


Link to post
Share on other sites
Chris,

 

Try this for me:

 

1. Add a sta NMIRES to the end of DLI code:

 

dli

pha
txa
pha
ldx table_index
lda colour_table,x
sta WSYNC
sta COLBK
sta sample_value
inc table_index
pla
tax
pla
sta NMIRES	
rti

 

Then make the beginning of the irq look like this:

 

irq
pha	; save a
tya
pha
lda NMIST
nop
and NMIST;2 checks!
bpl continue;skip if not set

;insert a virtual DLI here
ldy table_index
lda colour_table,y
sta WSYNC
sta COLBK
inc table_index

continue

lda sample_value
sta AUDC1; play sample ASAP to minimise DMA lag
tya.....
......and so on....

 

That worked for me provided I put the NOP spacer in between the first and second read of NMIST.

 

Thanks! Yep, works on my machine. This pretty much proves that an NMI is ignored sometimes.

s64.zip

Share this post


Link to post
Share on other sites

I added another NMIRES to the "virtual DLI" code which got rid of an infrequent glitch. This is a fairly significant hardware bug.

Share this post


Link to post
Share on other sites
I added another NMIRES to the "virtual DLI" code which got rid of an infrequent glitch. This is a fairly significant hardware bug.

 

But you don't see it any games-- perhaps they are using 15Khz timers or not using IRQs or have them so that NMI/IRQ trigger points don't overlap where bug occurs.

Share this post


Link to post
Share on other sites
I added another NMIRES to the "virtual DLI" code which got rid of an infrequent glitch. This is a fairly significant hardware bug.

 

But you don't see it any games-- perhaps they are using 15Khz timers or not using IRQs or have them so that NMI/IRQ trigger points don't overlap where bug occurs.

Games usually do everything off of DLI's and VBI's. It's not a significant bug in that it renders the machine unusable, it's just that it's something an engineer should have caught.

Share this post


Link to post
Share on other sites

From the Synertek datasheet:

 

Inputs /IRQ and /NMI are hardware interrupt lines that are sampled during 02 (phase 2) and will begin the appropriate interrupt routine on the 01 (phase 1) following the completion of the current instruction.

 

So both lines are sampled once per clock, and a transition on /NMI should get precedence since the IRQ will still be there until it is cleared. Somehow either the NMI isn't going low (unlikely, since Antic knows nothing of pending IRQ's) or something about the overall system timing is causing the 6502 to get confused and ignore it sometimes. I'd like to see it all on a logic analyzer.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...