Jump to content
IGNORED

How can POKEY IRQ Timers mess up NMI timing?


Sheddy

Recommended Posts

I've just confirmed the behaviour using similar technique (enable SEROC IRQ at known times).

 

Burn 5 cycles after WSYNC then Store 8 to IRQEN. It blocks any DLI, except those types that are on single scanline modes.

 

To block single-scanline DLIs, strangely enough, you just need to burn 4 cycles after the DLI.

Might be just the way Antic does DLIs... if it's a single scanline mode line then it must start the NMI one cycle earlier.

 

I also tried the same technique but executing a 7-cycle 6502 Instruction... no problematic effects caused there at all.

 

Also tried varying the delay after WSYNC - no effect. It would seem that you need to store to IRQEN exactly on cycle 112 (or cycle 111 if dealing with single-line modes). But it could be that POKEY has some delay before triggering the IRQ once we've enabled it.

 

What mode/width are you using? WSYNC can be delayed by up to two cycles if playfield or refresh DMA extends to the end of a scanline. I've also suspected that you may encounter yet another cycle delay if the cycle immediately after STA WSYNC is contended, but unfortunately, I can't find my test app to confirm this.

Link to comment
Share on other sites

I had an idea I tried earlier... use Serial Output, as you can control the Interrupt more easily.

 

Sadly... the only useful Serial Output modes generate a high pitched noise you can't mask, and you also get a bit of noise from the data transmission.

 

There's another major bug in the 400/800 OS. To try it, just hit the RESET key on the exact cycle that VBI occurs and only one NMI gets serviced although both are in the status register 54287.

 

In that code I posted, if you press the RESET key, you will see the PINK bar change to BLUE meaning you can still read the RESET key like a console key although NMIs are disabled. So you can use RESET like a console key if you write cycle-exact code. Press RESET to Start the game like going to START menu to do SHUT-DOWN under Windows.

Link to comment
Share on other sites

If I have time I'm going to whip up a program to show where the interrupts fall on a scan line for a given divisor. :)

 

I'd be interested in knowing your results, as I'm currently implementing POKEY initialization in my emulator. The tricky part is that there is an offset from when SKCTL bits 0+1 go up and when the audio clocks turn over, because they're implemented as polynomial counters where the clear value is mid-way through the cycle. In the case of the 64KHz clock, it appears to start 7 cycles prior to the comparator firing, but there is an additional two cycles of delay from the clock to the counters. I haven't quite figured out the 15KHz clock yet since the comparator seems to work slightly differently in that portion of the schematic.

Link to comment
Share on other sites

If we ever get good enough schematics for the chips, we could build them in a logic simulator and watch the cycle by cycle operation. As it is, we figure out a little more each day. :)

Edited by Bryan
Link to comment
Share on other sites

This quick BASIC program shows the distribution of IRQ's and how many lines the entire sequence takes. You can see why a divisor of 14 is much safer than 15.

 

10 GRAPHICS 7:COLOR 3:TRAP 10
20 PLOT 23,30:DRAWTO 136,30
30 PLOT 23,40:DRAWTO 136,40
40 ? "64KHZ IRQ PLOTTER - AUDF VALUE";:INPUT D
50 IF D<0 OR D>255 THEN 10
55 COLOR 0:PLOT 23,35:DRAWTO 136,35
60 COLOR 1:PLOT 23,35:X=0:L=1:F=D:? "LINE 1"
70 X=X+28:IF X>113 THEN X=X-114:L=L+1:? "LINE ";L
80 IF F=0 THEN 100
90 F=F-1:GOTO 70
100 IF X=0 THEN 120
110 PLOT 23+X,35:F=D:GOTO 70
120 GOTO 40

64PLOT.zip

Edited by Bryan
Link to comment
Share on other sites

Here is a solution that works without any extra mucking about in the interrupt code.

 

AUDF is changed from 15 to 14, which creates a repeating interrupt pattern that can avoid the NMI (but the pitch is a little higher). The timer is aligned with WSYNC and the sample plays without issues.

 

There are only changes to the init routine (marked with asterisks).

 

EDIT: I revised the attachment. The first one had the wrong source file.

 

great work Bryan :cool: Still trying to make sure I understand why this works though :P

 

actually I have to say everyone here has done great in pinning this sucker down and creating workarounds so quickly :thumbsup:

Edited by Sheddy
Link to comment
Share on other sites

You're welcome.

 

A brief explanation of why it works:

 

The goal is to find what cycles on a scan line will have an IRQ at some point.

 

The number of cycles between IRQ's is (AUDF+1)*28. A scan line is 114 cycles, so you can string a bunch of scan lines end-to-end and start plotting where the IRQ's will fall. Eventually you land on the same cycle number you started with and the pattern will repeat. Fortunately, using the SKCTL trick, we can pick where the starting cycle is so we can align the pattern to always miss the DLI and VBI.

 

BTW, try a divisor of 18 in the program above!

Edited by Bryan
Link to comment
Share on other sites

The test I was doing used Blank lines where all the DLIs were occurring. Trust me - the required cycle for an IRQ to mask out a DLI is one earlier when the next line contains a single-line mode.

 

The problem with using SKCTL is that it shuts down the Poly-Counters... so I guess we have to do this at a "quiet time" and then ensure that we retain that voice in 64 KHz mode.

 

Changing AUDF on a voice only comes into effect after the current value has counted down. Changing the base clock of a voice is immediate.

 

ed - has anyone tested all this with HScrol-enabled hires modes? I suspect the timings might vary even more. Or, on the other hand, there might be some cases where the DMA does us a favour and prevents IRQs occurring in critical cycles.

Edited by Rybags
Link to comment
Share on other sites

The test I was doing used Blank lines where all the DLIs were occurring. Trust me - the required cycle for an IRQ to mask out a DLI is one earlier when the next line contains a single-line mode.

The IRQ positions are spaced 6 cycles apart with a divisor of 14 (there are only 19 places on the line where an IRQ can occur). It should be possible to set the 64KHz timer to make sure both single and double line DLI's are covered.

 

The problem with using SKCTL is that it shuts down the Poly-Counters... so I guess we have to do this at a "quiet time" and then ensure that we retain that voice in 64 KHz mode.

I think we should only have to set the 64KHz timer once at the beginning of the program. After that it should maintain its alignment with the screen. Unless we discover that the DLI can move around even more, that should be fine.

 

Changing AUDF on a voice only comes into effect after the current value has counted down. Changing the base clock of a voice is immediate.

 

ed - has anyone tested all this with HScrol-enabled hires modes? I suspect the timings might vary even more. Or, on the other hand, there might be some cases where the DMA does us a favour and prevents IRQs occurring in critical cycles.

I only tested it for Sheddy's demo program, but we could modify it to try a more complex display list.

Edited by Bryan
Link to comment
Share on other sites

The test I was doing used Blank lines where all the DLIs were occurring. Trust me - the required cycle for an IRQ to mask out a DLI is one earlier when the next line contains a single-line mode.

 

The problem with using SKCTL is that it shuts down the Poly-Counters... so I guess we have to do this at a "quiet time" and then ensure that we retain that voice in 64 KHz mode.

 

Changing AUDF on a voice only comes into effect after the current value has counted down. Changing the base clock of a voice is immediate.

 

ed - has anyone tested all this with HScrol-enabled hires modes? I suspect the timings might vary even more. Or, on the other hand, there might be some cases where the DMA does us a favour and prevents IRQs occurring in critical cycles.

 

Just a thought-- it may be that timing of POKEY is a little ahead of ANTIC (subcycle) so when NMI occurs before ANTIC/6502 can suppress IRQs, POKEY already beat them to it. Perhaps, people with schematics can figure it out for sure.

 

And SKCTL stops/resets the 64Khz/15Khz clocks but not the 1.79Mhz clock.

Link to comment
Share on other sites

I'm getting ready for bed and it occurred to me that my second point above is rubbish. :)

 

I already know from plotting different divisors that the 64KHz cycles can fall all over a scan line, so what we're really aligning is the beginning of our division cycle. So, the hard part is knowing how to re-align the Pokey channel without SKCTL if it has been used for something else. I'm sure there's a way...

Link to comment
Share on other sites

Just a thought-- it may be that timing of POKEY is a little ahead of ANTIC (subcycle) so when NMI occurs before ANTIC/6502 can suppress IRQs, POKEY already beat them to it. Perhaps, people with schematics can figure it out for sure.

 

And SKCTL stops/resets the 64Khz/15Khz clocks but not the 1.79Mhz clock.

I can tell you what my fix would be-

 

Gate out the IRQ line as long as /NMI is asserted. Once the NMI's are cleared, and IRQ can happen. This would require consistent use of NMIRES, though. A more complex fix would be to use a falling /NMI to set an /IRQ supressing flip-flop that would be cleared on the next clock.

Link to comment
Share on other sites

I don't think those fixes would work either. I suspect this entire thing might be due to the way 6502 works.

 

My theory - during certain cycle/s of early IRQ processing (ie, 6502 pushing stuff to the stack, loading PC from $FFFE/F), the 6502 misses the falling edge on NMI.

 

Since NMIs are always edge-triggered, not seeing the transition means the NMI is missed. Masking IRQs by an XOR type method with NMI won't work because our problem is most likely occurring when an IRQ has already started but the NMI trails it slightly.

 

Surely though, if this is the case, it must have been encountered by other 6502 users out there and be documented.

Link to comment
Share on other sites

Surely though, if this is the case, it must have been encountered by other 6502 users out there and be documented.

 

Depends on how deep coders have gone into coding techniques. How many people do you know who have used fullscreen 3D animations plus DLIs plus digi speech at the same time ;)

 

 

But it is interesting.

Did I understand it right?

NMI does not happen, if a POKEY timer is getting activated some cycles before?

Link to comment
Share on other sites

That's the conclusion so far... I've encountered 2 cycles that can mess them up, there could be more due to all the different cirumstances due to DMA (PMG, scrolling etc).

 

You'd reckon the C64 guys might have encountered it if it's a 6502 thing... I'd say it's reasonably common to play Digi sound with their NMIs while also having Raster IRQs.

Link to comment
Share on other sites

That's the conclusion so far... I've encountered 2 cycles that can mess them up, there could be more due to all the different cirumstances due to DMA (PMG, scrolling etc).

 

You'd reckon the C64 guys might have encountered it if it's a 6502 thing... I'd say it's reasonably common to play Digi sound with their NMIs while also having Raster IRQs.

With the 64 the only condition I'm aware of that can cause dropped interrupts is if you read the interrupt control register (I think 2 cycles) before an IRQ is triggered which can cause it not to raise a CIA interrupt, be that NMI or IRQ depending upon which CIA you're polling, but this is CIA bug not '6502' bug.. So basically if you ACK the interrupt 2 cycles before it happens, it won't happen from the point of view of triggering an interrupt..

 

Wish I had something useful to contribute to this thread, but it's been fascinating to watch you guys doing your Sherlock Holmes impressions this weekend :) I'm dying to know what is is that causes this now, and good luck :)

Link to comment
Share on other sites

The other difference is that it's "RDY vs HALT"... the '64 uses RDY when VIC wants to halt the CPU, in theory the CPU just idles for those cycles.

 

DMA/HALT is different, supposedly if the 6502 is held on a particular cycle phase, then maybe it's effectively stalled rather than idling.

 

Another thing is that the CIA will hold NMI low indefinately until you clear it - it's a common trick on the C64 to use a CIA NMI to disable the Restore key. It seems Antic only holds NMI low for a short period.

Link to comment
Share on other sites

My theory - during certain cycle/s of early IRQ processing (ie, 6502 pushing stuff to the stack, loading PC from $FFFE/F), the 6502 misses the falling edge on NMI.

 

Yeah, it's got to have something to do with the timing used by either /IRQ (Pokey), /NMI (Antic) or the /HALT line (Antic). It's possible that a small sub-cycle adjustment would have prevented it.

 

I think Atari coders have run into this many times before (probably during disk loads) and didn't know what it was.

 

I thought of a possible way to align a playback routine with the 64KHz clock prior to play-

 

On a non-DMA line (Vblank), write 0 to AUDF1, change the IRQ vector to point to the alignment routine, and wait until you are past the memory refresh cycles. Then enable the TIMER1 interrupt and start loading A with immediates: lda #, lda #, lda #, lda #... fourteen times so you've covered an entire 28-cycle (64KHz) interval. When the alignment IRQ runs, it will know which cycle the IRQ is currently on (A) and can load AUDF1 with a value to resync the playback. Then change the vector back and reload AUDF1 with the playback divisor.

 

It's complicated, but it might work.

Edited by Bryan
Link to comment
Share on other sites

Something like that sounds feasible.

 

I just wonder, though, if we could align the divisor on an odd cycle (or whatever may be needed) in the first place, maybe we could reduce the conflicts to only be those that coincide with single-line DLIs.

 

I've not done a lot of incorporating DLIs into disk loaders... the main problem I found was that the DLI activity would cause the occasional glitch in SIO, so I stayed away from doing it.

Link to comment
Share on other sites

Something like that sounds feasible.

 

I just wonder, though, if we could align the divisor on an odd cycle (or whatever may be needed) in the first place, maybe we could reduce the conflicts to only be those that coincide with single-line DLIs.

 

I've not done a lot of incorporating DLIs into disk loaders... the main problem I found was that the DLI activity would cause the occasional glitch in SIO, so I stayed away from doing it.

 

Hmm... once the 64KHz timer is started, it can potentially hit every even or odd cycle depending on alignment. It should be possible to pick one or the other with SKCTL. If I monitor a 64KHz oscillation on Pokey I can see if I can shift it one cycle relative to sync. This can help us determine if one state is better than the other.

 

Another experiment would be to tie /NMI to both /IRQ and /NMI on the 6502 and set up two handling routines. The NMI should run, followed by the IRQ (assuming /NMI is held for some time) and we can see if that's the case or if only IRQ runs.

Edited by Bryan
Link to comment
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.
Note: Your post will require moderator approval before it will be visible.

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