NMI Suppression

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
NMI Suppression
by on (#90282)
Ok back to work. I have been lazy with my emu for a couple of months, but altought i have not much time i put hands on it when there are free time.

Im stuck in Blarrg's test NMI Suppression. It throws me error #3 "Reading flag when it's set should suppress NMI".

I ask your help to someone explain me what's is going on since i can't fix the error. Maybe a short explanation of what happens?

Thanks in advance.

by on (#90285)
Reading $2002 at the exact same cycle where NMI triggers cancels the NMI.

by on (#90287)
Ok thanks for the answer i got it.
The thing is i cannot implement it in my framework.

Im using C and i use pointer to functions when $2002 is read.

Then i here is my vblank code:

Code:
   
while ( Ppu.scanline >= 241 && Ppu.scanline <= 260 )
{
   while( Ppu.scanline_cycle < 341)
   {

   if (Ppu.nmi_onvblank && Ppu.on_vblank)
   {
                    SetNmiPending();
                  }

                   Ppu.scanline_cycle++;
      Ppu.ppu_timestamp += NTSC_INC;

      if( Ppu.ppu_timestamp >= runto)
         return;
   }

   Ppu.scanline_cycle = 0;
   Ppu.scanline++;
  }


The main function is RunPpu(int run_to) that runs the ppu cycles given certain cycles (cpu cycles).

Where NTSC_INC is equal to "1".

I don't figure out how to implment it.

by on (#90291)
Your code seems too strict. Perhaps you must change the way you handle PPU cycles and scanlines... a bit.

Well, if nobody else replies, I'll give you some advice. ^_^;;

by on (#90292)
Quote:
Your code seems too strict. Perhaps you must change the way you handle PPU cycles and scanlines... a bit.


yeah but i don't know how to change it in a better way. Any ideas how to make the code more flexible?

by on (#90295)
1. Your emulator uses a timestamp system; mine doesn't - it's all real time.
2. That makes a few things different to handle, since you could predict when an IRQ will happen, for example.
3. Shouldn't your RunPpu () always run for 3 PPU cycles? Unless, of course, you emulate the PAL hardware.

by on (#90299)
Quote:
1. Your emulator uses a timestamp system; mine doesn't - it's all real time.

mmm... i don't get it very well, define me real time
Quote:
2. That makes a few things different to handle, since you could predict when an IRQ will happen, for example.

That's a thing a never understood: predicting interruptions, but i suppouse that i have to understand "realtime" first
Quote:
3. Shouldn't your RunPpu () always run for 3 PPU cycles? Unless, of course, you emulate the PAL hardware.

No RunPpu() i emulate per instrucction cycle * 3, im not taking pal into account right now.

Forgive me to bother you: is it possible that you pass me a little info about realtime (as you call it) emulation?
That would be very helpfully for me. If you don't want to it's fine, no problem, maybe i will post a thread "a method about emulating".

Zepper it gave me and idea and i will try to figure out how to emulate in realtime. I really don't want the faster and/or the best emulator possible, i just want my emulator be accurate by now.
Anyway thank you for all.

Edit: maybe emulating opcodes cycles per cycles could help?

by on (#90305)
If you're only emulating three PPU cycles at once, that's "real time".

A "timestamp" system means you run the CPU for 100-some cycles, keeping track of what it writes to PPU ports and when. Then you run the PPU for one scanline, executing the writes as if they had happened on the time that the CPU marked for each write.

What you need to do is distinguish writes that happen on the first, second, or third PPU cycle of a given CPU cycle. Only one of those cycles causes a missed NMI.

Emulating one instruction at a time is OK as long as the write happens on the last CPU cycle of the instruction.

PAL is conceptually easy the way you're doing it: emulate 3, 3, 3, 3, 4 instead of 3, 3, 3, 3, 3.

by on (#90306)
Ok much clear now.
I have per instruction CPU emulation, is it valid to put a RunPpu() (wich will run 3 ppu cycles) inside of the instructions? Of course always respecting what each one does in each cycle.
I mean stopping cpu and inside the instruction run 3 ppu cycles.
Maybe could be a solution for not re-writing a per cycle emulation CPU core.
But there will be to much function call overhead.

What is convinient to make a new CPU core per cycle or executing PPU inside instructions?

Taking into account i want to change to "real time".

by on (#90312)
Back to the topic, well... your error translates into:

reading $2002 with bit #7 ($80) set, the NMI doesn't trigger.

So, you cannot allow the NMI to trigger on $2002 reads, as mentioned before.

by on (#90322)
Where do you set Ppu.nmi_onvblank as true? When you reach a specific cycle?

If you can somehow avoid setting it when $2002 is read on that same cycle, that should solve your problem.

by on (#90356)
Setting the NMI flag a few cycles before the effective address read could be the error. Remember that LDA $2000 takes 4 CPU cycles to complete, making 4x3 = 12 PPU cycles.