Good tool for debugging a hang?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
sdwave
Posts: 27
Joined: Mon Feb 13, 2012 1:20 am

Good tool for debugging a hang?

Post by sdwave »

I've got a hang which I found out through Nintendulator is caused by
executing some data through the disassembly debugging option but I'm not sure exactly how the code actually ended up getting there.

Do you guys know of any tool / emulator that can show the contents of the stack and the last few hundred instructions executed?
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Post by thefox »

You can use the CPU logging feature in Nintendulator. The logs it will generate will be big (each instruction appears there together with CPU state in ASCII format), so be sure to enable it only a second or two before the crash occurs.

The logs will be saved, IIRC, in the directory %appdata%\Nintendulator
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

Nintendulator can do this. Debug, disassembly.

Put a breakpoint at some address slightly before you know the crash happens. If you don't know a repeatable way to cause the crash, go to the game state where it usually happens. Title screen? By this one wall? Go there.

Press step. Then start log. Run. If you know how to cause the crash, do it now. If you don't, have plenty of harddrive space and do stuff until it does happen. :o

when it happens, click stop logging. Look in Nintendulator's save directory, and you'll see a file with a name like (romname).20120312_175801.debug.

Open that in a text editor, go to the end and work backwards. SP is the current stack pointer.

Edit: I suppose that won't show the actual contents of the stack. You can step through the code and look at the CPU memory in the window, or you can try something like FCEUX which allows the CPU memory to be viewed while the game is running at full speed.

edit 2: FCEUX has the exact feature you want. It's just not in the debugger, so I didn't see it. :lol: Its trace logger can log the last X instructions to a window for you, and it has a "break on bad opcode" option in the debugger so you can get it to stop the moment is starts executing data as code.
User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Post by cpow »

In NESICIDE, depending on where the execution is ending up, you have a couple options:

1. If the code is always trying to execute an illegal instruction, set a breakpoint CPU Event::Any Undocumented Instruction Execution or CPU Event::Specific Undocumented Instruction Execution.
2. If the instruction pointer is wandering into RAM or SRAM or somewhere you would otherwise *not* expect your program to be executing from, set one or more breakpoints CPU Execution (starting/ending address).

Open the Execution Inspector. When the breakpoint is hit that will show you the last 256K CPU and PPU cycles. I'm adding a log-to-file for the next release.
sdwave
Posts: 27
Joined: Mon Feb 13, 2012 1:20 am

FCEUX to the rescue.

Post by sdwave »

I figured out what was happening. I had an IRQ firing in the middle of my MMG prg bank swithing code. The IRQ changed some character banks so it would interfere with the bank swithing code and prevent the prg bank switch from occuring. I managed to fix it with sei/cli around the bank switch code.

However.. I don't think this will protect me from NMI interrupts (which need to occur and also include char bank switches).

Any thoughts on how I can prevent the NMI interfereing with my prg bank switch code without disabling NMI? One possible idea is to call the bank switch twice inside the sei/cli block to ensure it always occurs.
User avatar
Dwedit
Posts: 4470
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

Read a variable called "was IRQ executed", run the bankswitch again if it reads as true (and clear the variable).

And of course, your IRQ/NMI will set that variable if it bankswitches anywhere, and IRQ/NMI will also bankswitch back to whatever user code expects to see.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
MottZilla
Posts: 2835
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla »

Maybe you should have PRG register Shadow Variables. So you'd set the variable to what you want and then call a function to set those registers. Then in NMI if you need to temporarily change the PRG banks you can do so and then restore them to the Shadow Variables afterward. I'm sure other people have ideas. This sort of thing is a potential problem with MMC1 with its serial write registers too.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Personally, I wouldn't use shadow registers just because of this... I mean, why waste several bytes when you can solve the problem with 1 bit?

The only problem that I see with Dwedit's solution is that the bankswitching code will run again even if the IRQ/NMI happened way before the actual switching (meaning that the switch wasn't really interrupted, and didn't actually need to be repeated). Nothing bad will happen, it will just waste a little CPU time.

To fix that, I would actually have the flag mean "bankswitch in progress", and set it right before bankswitching in the main thread. The IRQ/NMI will clear this flag (no need to check its value, just clear it every time) to indicate that it has interrupted a bankswitching operation. If the bankswitching routine sees that this flag is clear after finishing, it will know it has been interrupted and will switch the bank again.

This is all a bit more complicated with the MMC1 because of its painful serial input... In order to switch banks correctly inside the IRQ/NMI you'll have to reset the MMC1, to make sure that all the bits are written to it correctly.
Post Reply