This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Good tool for debugging a hang?

Good tool for debugging a hang?
by on (#91294)
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?

by on (#91295)
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

by on (#91296)
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.

by on (#91304)
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.
FCEUX to the rescue.
by on (#91307)
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.

by on (#91308)
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.

by on (#91344)
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.

by on (#91349)
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.