Jump to content
IGNORED

NMI + IRQ collision detection


Recommended Posts

So I'm playing with this bass ..

Thing is it generates tons of random IRQs, which is quite limiting for DLI. But I'm thinking, if I can make the DLI fire, with some delay, I can work with it.

But of that's the problem .. when IRQ comes in right time, some DLI will not fire.

 

I made little test which detects the problem, simply by expecting the DLI to fire on every second line, and comparing expected line with VCOUNT. It's the first XEX. You'll need Altirra if you want to emulate it and enable NMI/IRQ collision in CPU options.

Every time there is mismatch, VCOUNT is loaded into COLBK, so you will see colors flash on the screen.

 

Now the idea was I could use NMIST to detect if NMI overlap happened during IRQ handling. And it's not so simple.

 

First, NMIST is not reset between DLIs. VBI sets it to VBI. First DLI sets it to DLI and it stays there. So step number 1 is to write to NMIRES in every DLI. I do it as first instruction in DLI handler.

 

Second problem is that NMIST is set on cycle 6. But NMI itself is invoked few cycles laters. So situation can (and does) arise, that you read NMIST in IRQ handler, it is set to DLI .. but DLI will be handled properly just few cycles later.

 

I solved this by reading NMIST twice in a row, with exactly one NOP in between. One seems to be enough, without it it still can all happen before DLI is actually fired. So there is code like this in the IRQ handler:

 

        lda nmist
        nop
        and nmist
        bpl no_collision
collision
            ;act like it's DLI
            sta nmires
            ;do what you gotta do
no_collision
        ;continue IRQ handler

And it seems to work ! At least in this test of mine, it's the second XEX. I'm not trying to do anything specific inside the 'fake DLI' section, I just INC the zero page address I use for detection of missed DLI, to prove the concept.

 

Have you tried to solve this problem ? Have you succeed ? Do you think there is some more problems ?

 

 

IRQ1.xex IRQ2.xex

  • Like 2
Link to comment
Share on other sites

9 hours ago, Stephen said:

Is this tone supposed to be constant?  This, and the previous ones you posted, have a cycling "beat frequency".  It's not just my ears, the spectrum analyzer on my audio system shows it too.

That's intentional, the width of the square wafe is modulated. That's one of the features of this approach. If you turn on audio monitor in Altirra, you'll understand.

  • Like 1
Link to comment
Share on other sites

The lost NMI problem has been known for a while now.  It's due to how the 6502 works and a design fault with ANTIC in that it doesn't pull /NMI low for long enough.

 

Workarounds, yes can seemingly be problematic especially considering that when we're using Pokey Timers, we want tight coding to waste as few cycles as possible.

If using Timers in 16 KHz mode it's no problem at all since that mode is exactly in sync with the 114 cycle line rate so all that needs doing is to put Pokey into the INIT state then bring it out at a time where conflict won't occur (and in doing that we can predetermine where in the scanline we have the IRQs being fired).

 

But in the most common usage case, 64 KHz mode is 28 cycles per tick so the IRQ will be at different times in the scanline.

The missed NMI problem occurs when an IRQ is triggered on cycle 4 of the scanline.

 

Maybe phaeron can help here but possibly if the AUDF period ends on an odd cycle we can avoid missed NMIs?

 

Link to comment
Share on other sites

1 hour ago, Rybags said:

Maybe phaeron can help here but possibly if the AUDF period ends on an odd cycle we can avoid missed NMIs?

Interesting idea. I would just have to start the IRQ right, and keep DLI on even lines. Will try.

Link to comment
Share on other sites

Aaand it's nonsense actually. Antic will trigger IRQ on odd cycle, no problem with that. But CPU will trigger IRQ only on instruction boundary.

Of course I had to test it for 10 minutes before realizing why it doesn't work :-D

 

  • Like 2
Link to comment
Share on other sites

Isn't following code enough to solve the problem?

irqroutine:
  asl NMIST  ;reset NMIRES too
  scc
  jmp (VDSLST)
  bpl ?irq
  pha
  txa
  pha
  tya
  pha
  cld        ;not necessary if IRQVEC is used
  jmp (VVBLKI)
?irq ...     ;irq stuff

Correct me if I'm wrong, but RMW ASL should reset NMI flag and prevents to call NMI twice.

Edited by mono
fixed support of VBLK
Link to comment
Share on other sites

From Altirra Hardware Reference Manual - "NMIST bits 6 and 7 are set starting on cycle 7 of a scan line where a VBI or DLI is active. Clearing those bits by writing NMIRES does not prevent the interrupt from firing, but can confuse an NMI dispatch routine."

Link to comment
Share on other sites

Thanks MaPa. So should it be working fine with similar code on NMI service routine too?

nmiroutine:
  asl NMIST
  scc
  jmp (VDSLST)
  smi
  rti
  pha
  txa
  pha
  tya
  pha
  cld
  jmp (VVBLKI)

Or if execution of VBLKI routine is prevented in dependency on NMIST status (when we don't use NMIVEC but VBLKI).

Edited by mono
Link to comment
Share on other sites

I don't use ROM, some nothing of this really applies. Also the problem isn't that NMI is invoked twice. The problem is that if IRQ and NMI are invoked at the same time (simply speaking), CPU only calls IRQ handler, not NMI handler.

 

I read NMIST twice, because there is like 6 cycles (don't know exactly) window, between NMIST being set and NMI being invoked.

 

It's something like this:

1) is NMIST set to DLI ?

2) .. NMI should happen any moment now ..

3) is it STILL set to DLI ?

3) if so, NMI didn't happen

 

Note that it requires NMIST being reset in DLI handler.

Link to comment
Share on other sites

By "odd cycle" I mean odd cycle on a scanline.  Odd or even scanlines makes no difference.

The deterministic way to control where AUDF counters start/end is to put Pokey into INIT state (SKCTL bits 1,0=00) then bring it out at a known time.

Link to comment
Share on other sites

14 minutes ago, Rybags said:

By "odd cycle" I mean odd cycle on a scanline.  Odd or even scanlines makes no difference.

The deterministic way to control where AUDF counters start/end is to put Pokey into INIT state (SKCTL bits 1,0=00) then bring it out at a known time.

Yes, I know, Mapa already pointed that to me via PM. Doesn't matter what line you are. Just what cycle on the line.

But post #6 still holds. It doesn't matter at what cycle Antic fires the IRQ. It depends on at what cycle CPU starts to handle the IRQ. And that depends on current instruction. In other words, you can't control it.

Link to comment
Share on other sites

Yeah, it's hard to avoid this with 64KHz timing. The 64KHz clock ticks every 28 cycles and scanlines take 114 cycles, giving: 114 mod 28 = 2, 262*114 mod 28 = 20, 312*114 mod 28 = 8. This means that no matter the offset, the 64KHz clock will sweep through half of all possible offsets each scanline and a quarter of all possible offsets every frame. With average instruction lengths this makes it impossible to avoid the condition by adjusting the clock phase.

 

The 15KHz clock is different because it is exactly aligned to scanline rate, so you can easily adjust its offset to a point where there's no way that the 6502 will acknowledge the IRQ at the same time that the NMI fires, because no instruction will be long enough to delay the IRQ ack that far. The longest 6502 instruction is 8 unhalted cycles, and the IRQ can be scheduled early enough that it isn't possible to push the IRQ all the way to the DLI even with an 8 cycle instruction + DMA + WSYNC. However, 15KHz mode also affects all pitches in a way inconvenient for music.

 

Writing to NMIRES indeed won't stop the NMI, as ANTIC handles NMIs differently than the way POKEY handles IRQs. In POKEY, the IRQ is derived from the same signals reported in IRQST, so by clearing those bits through IRQEN you also remove the source of the IRQ. In ANTIC, the NMI signal and NMIST are independently updated from a common set of request lines, so clearing the NMIST bits with NMIRES does nothing to stop the NMI.

 

There are two ways for IRQ-to-NMI routing to screw up:

  1. IRQ handler executes first, senses the DLI, then is interrupted by the NMI handler which also senses and services the DLI. On return, the IRQ handler also thinks it needs to service the DLI and the DLI handler is executed twice. Atomically reading NMIST and writing NMIRES with an RMW instruction like ASL avoids this, since if the IRQ handler senses the DLI it will clear it before the NMI handler checks it, but....
  2. IRQ handler doesn't sense an DLI, but then writes NMIRES anyway with the ASL instruction, which then clears a DLI which just went active. The IRQ handler didn't sense a DLI, so it won't handle it, but since it cleared the DLI bit, the NMI handler won't do it either. Result is a missed DLI. This means that the IRQ handler must not write NMIRES unless it has sensed a DLI and is going to run the DLI handler.

The double-check with a NOP in between seems theoretically sound. The I bit is set so no other IRQs can be acknowledged, and if the first check senses the leading edge of a new DLI it should be serviced by the 6502 before the second check can execute.

 

  • Like 2
Link to comment
Share on other sites

Thanks for insight phareon. I have yet to test it properly on real HW. I have reports that this version with one NOP sometimes flashes the colors too. I think it might need 2 NOPs. As long as the time between checks isn't long enough so that DLI is fired twice, it should be OK.

 

This IRQ bass can't be used in character mode, and you must avoid writes to WSYNC, as both mess timing so it can be heard. Typically it leads to IRQ being missed, which creates nasty click in the audio.

 

Still it allows at least for some graphics on the screen.

Edited by R0ger
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...