weird stack bug

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
weird stack bug
by on (#54889)
First off, this forum has been a tremendous help for me when developing the first three iterations of my emulator.

However, I've run into a little difficulty, and I was hoping that somebody here might have an idea/suggestion that could point me in the right direction.

I recently rewrote my CPU emulation core and I've come across a bug which is giving me a headache (or migraine, rather).

Whenever the CPU is about to RTI from a NMI, it pops the wrong return adress from the stack and jumps into oblivion. I've tracked down the issue to that the stackpointer isn't incremented/decremented as it should be, or rather, there is one push more than there are pops before RTI.

The only game out of my test roms I've found that this bug doesn't affect is SMB, which runs properly.

If I add another POP to my RTI-implementation, many other roms loads and runs without issues, such as donkey kong, pac-man and mario brothers, but alas, it breaks super mario bros.

Does anybody have any idea what I'm doing wrong?

Please don't flame me too hard if I have overlooked something obvious, this is my first post after all :-)

by on (#54890)
If I'm reading your post right, you already found the problem.

Look at your NMI code -- count the number of stack pushes. There should be 3

Look at your RTI code -- count the number of stack pops. There should be 3.

Make sure the push/pops are in the right order:

push high PC
push low PC
push status

pop status
pop low PC
pop high PC

Or you could post your NMI/RTI code here and maybe we can spot it.

by on (#54891)
NMI:


Code:
NMI()
{
   
                PUSH(MSB(PC)); //push PC to stack
                PUSH(LSB(PC)); //

                PUSH(flags.SR); //push status register
                PC = MAKE_WORD(memory->ReadMemory(0xFFFA), memory->ReadMemory(0xFFFB));
               

           
                ppu.NMI_occured = true;
      
                flags.members.I = 1;
      branch = true;

}



Code:
RTI()
{   
flags.SR = POP();
      
      byte msb, lsb;
      lsb = POP();//lsb
      msb = POP();//msb

      PC = MAKE_WORD(lsb, msb);

      ppu.NMI_occured = false;
   //   PrintDebug("RTId @ $%04X", PC);
      branch = true;
}

by on (#54892)
Looks fine to me.

Quote:
I've tracked down the issue to that the stackpointer isn't incremented/decremented as it should be, or rather, there is one push more than there are pops before RTI.


I might've misunderstood this sentence before. Maybe SP is getting screwed up somewhere between NMI and RTI.

Do you have a CPU tracer? I recommdend writing one. Not only to solve this problem, but to solve all future problems (not just CPU related -- a CPU tracer is key in solving any emulation bug)

If you make a trace of all the instructions the CPU is executing and the state of the registers (specifically, SP), you will easiliy be able to spot where SP is getting screwed up.

by on (#54935)
edit:

eventually found the bug. When NMI occured I didn't didn't increment the PC accordingly, so the first instruction (PHA) executed twice, thus fuxx0ring the SP. a nights' sleep can really do wonders for code-weary eyes :-)

cheers disch!