bsnes-plus icon indicating copy to clipboard operation
bsnes-plus copied to clipboard

[Feat. Req.] Call Stack

Open bazz1tv opened this issue 8 years ago • 3 comments

[debugger] Seeing as how there is a Step Out function, does that mean there is an internal call stack based on JSR/JSL calls, or is it more complex? I think it would be really cool to have the ability to view the call stack from the debugger.

I imagine there are certain circumstances that are harder to detect in call stack, such as a manual push to stack of an address which is later RTS/RTL'd.

bazz1tv avatar Jul 14 '15 21:07 bazz1tv

The step out/over features work by counting the number of call/return instructions that are executed after the button is pressed, and then breaking after "enough" RTS/RTL instructions happen.

It has basically the same weaknesses as a call stack would (i.e. it can still be fooled by code that pushes or pops return addresses manually), and it also doesn't work with Super FX code (since the FX doesn't really have call/return instructions or even a processor stack...)

A proper call stack would be pretty useful, though, and not too hard to implement on top of what's already in place. I'll probably work on adding this feature at some point soon.

devinacker avatar Jul 14 '15 22:07 devinacker

Fair warning: since I started working on retrodasm I don't think I've come across a single NES, SNES or Gameboy game that doesn't do something tricky with the stack that's likely to screw up a "call stack viewer".

  • Many games run the entire main loop out of an NMI handler that pulls its return address off the stack and throws it away. This NMI handler never returns (obviously), it just eventually hits an infinite loop that the next NMI breaks it out of.
  • It's common particularly in bytecode interpreters (script engines in RPGs, etc.) to unwind the stack by pulling one or more return addresses and throwing them away.
  • Many games have one or more routines that pull their return address off the stack, read some data at that address, and push back an adjusted return address that skips over the data. In effect, the arguments to the routine are inlined into the instruction stream (this idiom is incredibly common and is the reason I started writing my own disassembler in the first place)
  • Some games use BRK or COP as a two-byte JSR, or occasionally a three-byte JSL. The "signature" byte or word after the BRK/COP is used as an index into a jumptable. Not only is this a special case of return-address manipulation, it's also a 50/50 guess (depending on the game) whether the called routine will use RTS or RTI to return.

awjackson avatar May 27 '16 22:05 awjackson

You might want to look into mednafen's debugger. It implements "something like a call stack" One could view its implementation as a way of circumventing those issues. I've written a PCE game that uses at least one of the tricks you mentioned but I had no problems reaping the benefits of mednafen's debugger's "call stack"

Another neat thing about its call stack is that also has color coded symbols for when certain interrupts occur. On PCE, this was vblank and the Timer. Very useful to see that for context.

-----Original Message----- From: "Alex W. Jackson" [email protected] Sent: ‎5/‎27/‎2016 6:17 PM To: "devinacker/bsnes-plus" [email protected] Cc: "Bazz" [email protected]; "Author" [email protected] Subject: Re: [devinacker/bsnes-plus] [Feat. Req.] Call Stack (#31)

Fair warning: since I started working on retrodasm I don't think I've come across a single NES, SNES or Gameboy game that doesn't do something tricky with the stack that's likely to screw up a "call stack viewer". Many games run the entire main loop out of an NMI handler that pulls its return address off the stack and throws it away. This NMI handler never returns (obviously), it just eventually hits an infinite loop that the next NMI breaks it out of. It's common particularly in bytecode interpreters (script engines in RPGs, etc.) to unwind the stack by pulling one or more return addresses and throwing them away. Many games have one or more routines that pull their return address off the stack, read some data at that address, and push back an adjusted return address that skips over the data. In effect, the arguments to the routine are inlined into the instruction stream (this idiom is incredibly common and is the reason I started writing my own disassembler in the first place) Some games use BRK or COP as a two-byte JSR, or occasionally a three-byte JSL. The "signature" byte or word after the BRK/COP is used as an index into a jumptable. Not only is this a special case of return-address manipulation, it's also a 50/50 guess (depending on the game) whether the called routine will use RTS or RTI to return. — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

bazz1tv avatar May 27 '16 22:05 bazz1tv