Replies: 2 comments 6 replies
-
Not sure if I fully understand the request - can't this be done using a Lua script + the Lua breakpoint API? Adding int3 instructions is only really useful for making the emulator itself breakpoint when running inside a debugger like lldb or gdb. I have already added support for this here pcsx-redux/src/core/DynaRec_x64/emitter.h Lines 225 to 226 in ccd8348 Implementing breakpoints for the emulated PS1 is a whole other problem. |
Beta Was this translation helpful? Give feedback.
-
If I understand what you're saying properly, you want something to have breakpoints on "new codepath", or something like this? There is already something in Redux for this, but it's badly documented. I'll write something later today in order to explain better how this works, but basically there's something right now where you can, say, run around in a game, tagging all of the existing code, and then turn on breakpoint mode and do something you haven't done yet, like getting damage. The breakpoint should trigger on code that hasn't been executed yet, showing the codepath that was just taken as a result of the behavior change. Does this sound like what you're trying to achieve? |
Beta Was this translation helpful? Give feedback.
-
My idea is to add a new window "Event Finder" in the Debug menu.
This new window contains three controls:
When the user clicks on this button PCSX-Redux adds a new one-time execute breakpoint after each conditional branch instruction (
beq
,bne
and etc) and at the target address of each conditional branch instruction (where the PC teleports when the condition is met and the branch is taken) within the range defined by both the start and stop addresses.When the event that the user wants to find occurs then the game pauses on a one-time execute breakpoint that PCSX-Redux added when the "Begin search for an event" button was clicked.
To find the conditional branch instruction for this event then just click on the "Jump to PC previous state" button because the PC is after this conditional branch instruction and if the condition was met and the branch was taken then the PC may be too far away from this conditional branch instruction. So click on the "Jump to PC previous state" button and you found the conditional branch instruction for the event that interests you.
Note that the "Remember PC previous state" checkbox must be ticked to make "Jump to PC previous state" button to work.
Then the memory editor can be used to change this conditional branch instruction to make the event to never happen or always happen by either changing the instruction to unconditional branch instruction or replacing it with a
nop
instruction.A one-time breakpoint is a breakpoint that pauses emulation when triggered but then it is automatically removed unlike a normal breakpoint that stays even when triggered.
NOTE
The Event Finder adds and uses one-time execute breakpoints so one-time breakpoints must be implemented first before implementing Event Finder.
The "Remember PC previous state" checkbox and "Jump to PC previous state" button must also be implemented to find and modify the condition of any event.
This new window can be useful to make Crash Bandicoot, for example or instance, invulnerable to all enemies where Memory Observer is useful to make infinite lives so the recent checkpoint can be used indefinitely without having to start the entire level from the beginning when the lives are over but this cheat is not needed because PCSX-Redux already supports save and load states which users will prefer more than the in-game checkpoints.
In Crash Bandicoot the PC is both stuck and trapped in a loop while Crash is alive until Crash touches an enemy then the PC exits the loop and reaches a code that kills Crash.
There is a conditional branch instruction that prevents the PC from reaching the code that kills Crash Bandicoot but this conditional branch instruction let's the PC reach that code that kills Crash Bandicoot when Crash bandicoot touches any enemy.
The hacker would like to change this conditional branch instruction to always prevent the PC from reaching this code because the hacker doesn't want to see Crash dies every time he touches any enemy but how the hacker will know what is the address of this conditional branch instruction?
There are too many conditional branch instructions in the RAM of the game. Using the Event Finder window can help a lot to find this conditional branch instruction.
Once the one-time breakpoints are set the user just has to click on the Resume button several times until all the unwanted one-time breakpoints are removed automatically and he can continue play the game.
Then just touch any enemy and the interesting one-time execute breakpoint will be triggered in the code that kills Crash Bandicoot.
Then simply click on the "Jump to PC previous state" button and ta da! The wanted conditional branch instruction has been found!
At last use the memory editor to change this conditional branch instruction to unconditional branch or
nop
so the code that kills Crash Bandicoot is unreachable and Crash won't die when touching any enemy!Another example or instance is Pang! Where the character that you control with dualshock dies when hitted by a colored ball or bubble.
Again memory observer is useful to make infinite lives but to make the character invulnerable you must find the conditional branch instruction that prevents the PC from reaching the code that kills the character on a condition and change it so the code that kills the character is unreachable.
The "Event Finder" window that I suggest can be useful for pang too.
Like normal breakpoints, the interpreter is needed to trigger one-time breakpoints until the recompiler will know to generate
int 3
codes for added breakpoints.At last this is wanted to add a "cancel event search" button in this new "Event Finder" window for use when the user wants to cancel the event search.
"cancel event search" simply undoes "Begin search for an event" by simply removing all the one-time execute breakpoints that "Begin search for an event" added.
Another better way to implement "Event Finder" without one-time execute breakpoints is to add the following to the "Event Finder" window:
While the "Break on leaving a conditional branch" checkbox is ticked the game pauses every time the PC leaves a conditional branch instruction if the "Do not break when a branch was" ListView is empty.
The "Do not break when a branch was" ListView has two columns:
The first column is "Address" and the second column is "Suffix".
The "Address" column always contains addresses of conditional branch instructions (beq, bne and etc) and the "Suffix" column is always either "taken" or "not taken".
When a conditional branch instruction is added to this ListView and the "Suffix" column is "taken" then when the PC encounters this conditional branch instruction and the branch was taken then the game won't pause even if the "Break on leaving a conditional branch" checkbox is ticked.
But if the branch was NOT taken then the game will pause if this checkbox is ticked.
If the "Suffix" column is "not taken" then when the PC encounters this conditional branch instruction and the branch was taken then the game will pause if the "Break on leaving a conditional branch" checkbox is ticked.
But if the branch was NOT taken then the game won't pause even if the "Break on leaving a conditional branch" checkbox is ticked.
At last add to the context menu of the "Assembly" window two commands:
These two commands appears in the context menu of the "Assembly" window if and only if the right clicked instruction is a conditional branch (beq and bne for examples).
The "Do not break if branch was taken" command adds a new row to the "Do not break when a branch was" ListView where the address of the right clicked conditional branch instruction is added to the "Address" column and "taken" to the "Suffix" column.
And the "Do not break if branch was NOT taken" command does the same as "Do not break if branch was taken" but the "not taken" is added to the "Suffix" column instead of "taken".
If the "Break at most twice for each conditional branch" checkbox is ticked then the "Do not break if branch was taken" command is automatically done when the PC encounters a conditional branch instruction, the condition was met and the branch was taken and the "Do not break if branch was NOT taken" command is automatically done when the PC encounters a conditional branch instruction, the condition was NOT met and the branch was NOT taken.
With this better implementation of the "Event Finder" window the one-time execute breakpoints are no longer needed but both the "Remember PC previous state" checkbox and the "Jump to PC previous state" button are still needed in case the condition was met, the branch was taken and the PC got too far away from the conditional branch instruction of interest.
A better alternative to the "Remember PC previous state" checkbox and the "Jump to PC previous state" button is a "Remember last visited conditional branch" checkbox and a "Jump to last visited conditional branch" button.
No need to explain what these two controls do because their names are too self explanatory.
Another idea if you don't want this additional checkbox and button is to always automatically set the cursor of the "Assembly" window to always point to the last visited conditional branch instruction that the PC left if the "Break on leaving a conditional branch" checkbox is ticked and the game was paused because of this.
Or just tick the "Break at most twice for each conditional branch" checkbox to make the "Do not break" commands done automatically for you so you don't need to set the cursor of the Assembly window to point at the conditional branch instruction to perform any of these "Do not break" commands manually.
If you like the "Break on leaving a conditional branch" checkbox but neither like one-time execute breakpoints nor the "Do not break when branch was" ListView then another solution is to allow the user to add and toggle NoBreakpoints with the Assembly window (You will have to add to the context menu of the Assembly window a "Toggle NoBreakpoint" command).
A NoBreakpoint is a thing that prevents the game from pause when triggered.
If the user enabled a debug feature that should pause the game at some time and if a NoBreakpoint was triggered in that time then the game won't pause even if the debug feature should pause the game.
For example or instance if a word write breakpoint (of size 4 bytes) was added and an execute NoBreakpoint was added on a
sw
instruction then if thesw
instruction writes to a memory where the write breakpoint was added but an execute NoBreakpoint was added on thissw
instruction then because of the execute NoBreakpoint the write breakpoint can't pause the game! The execute NoBreakpoint prevents the write breakpoint from pausing the game!In theory execute NoBreakpoints can also be useful to prevent the "Break on leaving a conditional branch" from pausing the game when the pause is not wanted.
Another example is to add a word write NoBreakpoint (of size 4 bytes) at some memory address and add an execute breakpoint on a
sw
instruction.If the PC encounters this
sw
instruction and thesw
instruction writes to the memory address that the NoBreakpoint was added at then the execute breakpoint can't pause the game because of the write NoBreakpoint. In this example the write NoBreakpoint prevents the execute breakpoint from pausing the game!In theory write NoBreakpoints can also be useful to prevent "Break on RAM change" as suggested in https://github.com/grumpycoders/pcsx-redux/discussions/1061 from pausing the game when the pause is not wanted.
In short:
Breakpoints are defined when to pause the game while NoBreakpoints are defined when NOT to pause the game.
If a NoBreakpoint was triggered then the game won't pause even if a breakpoint was triggered at the same time.
NoBreakpoints temporarily disable breakpoints when triggered.
A "Add NoBreakpoint on PC and Resume" button can also be handy in conjunction with "Break on leaving a conditional branch" checkbox.
EDIT:
After posting the Logpoints feature request https://github.com/grumpycoders/pcsx-redux/discussions/1075 came to my mind a new idea to add a "Log on leaving a conditional branch" checkbox in addition to "Break on leaving a conditional branch" checkbox.
This means that the value of PC is printed in the Logs window every time the PC leaves a conditional branch instruction instead of pausing the game.
Tick the "Log at most twice every conditional branch" checkbox, similar to "Break at most twice for each conditional branch", to prevent spams and repetitions in the Logs window.
If the Assembly Pause/Resume button is also available in the Logs window then finding the event that you seek becomes easy.
Just clear the logs window as long as the event that you are interested on did not fire.
When the Logs window is empty then fire the event and go back to the Logs window.
There you find the address of the first instruction that is executed every time the event is fired.
Beta Was this translation helpful? Give feedback.
All reactions