PSX Decompilation
This commit is contained in:
parent
56f6099820
commit
bbd29b1f10
25 changed files with 1921 additions and 701 deletions
221
docs/usecode-debugger-break-state-layout.md
Normal file
221
docs/usecode-debugger-break-state-layout.md
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
# Usecode Debugger Break-State Layout
|
||||
|
||||
## Purpose
|
||||
|
||||
This note captures the current class-lift-relevant evidence for the dormant seg1408 debugger-state object.
|
||||
|
||||
The retail binary still appears to leave this family orphaned at runtime, but the object model itself is strong enough to justify explicit class authoring in Ghidra.
|
||||
|
||||
Current working family name:
|
||||
|
||||
- `UsecodeDebuggerBreakState`
|
||||
|
||||
## Current Best Class-Level Read
|
||||
|
||||
`UsecodeDebuggerBreakState` is a retained debugger object that owns:
|
||||
|
||||
- a small breakpoint table
|
||||
- current interpreted-line state
|
||||
- single-step / break-armed flags
|
||||
- a callstack entry stack
|
||||
- a small vtable-backed break/notify surface used by the interpreter callback lane
|
||||
|
||||
The compiled interpreter still calls into this object when the global debugger-state pointer is non-null, even though the retail binary no longer seems to instantiate it during normal play.
|
||||
|
||||
## Strongest Evidence Anchors
|
||||
|
||||
### Constructor
|
||||
|
||||
#### `1408:0000` `Create`
|
||||
|
||||
Current verified behavior:
|
||||
|
||||
- allocates `0x2f2` bytes when `this == null`
|
||||
- writes retail vtable offset `0x65ab` at object `+0x00`
|
||||
- fills the breakpoint-entry region starting at `+0x04` with `0xffff`
|
||||
- clears `+0x02`, `+0x75`, and `+0x7a`
|
||||
- returns the object far pointer in `DX:AX`
|
||||
|
||||
This is a real constructor-style path, not just a helper wrapper.
|
||||
|
||||
### Break gate
|
||||
|
||||
#### `1408:0053` `MaybeBreakOnCurrentLine`
|
||||
|
||||
Current verified behavior:
|
||||
|
||||
- stores the incoming interpreted line minus one at `+0x72`
|
||||
- resolves the current unit-name pointer through `CurrentEntryGetUnitName`
|
||||
- checks file+line breakpoints through `HasBreakpoint`
|
||||
- dispatches through the object vtable when a break condition is met
|
||||
|
||||
This is the strongest proof that the hidden debugger lane is object-based and that the interpreter-side callback still expects a live debugger object.
|
||||
|
||||
### Breakpoint table helpers
|
||||
|
||||
#### `1408:00dd` `BreakpointInsertSorted`
|
||||
|
||||
Current verified behavior:
|
||||
|
||||
- enforces a maximum of ten breakpoint entries
|
||||
- scans the `0x0b`-byte breakpoint-entry table rooted at `+0x04`
|
||||
- compares unit-name strings via the common string helper
|
||||
- inserts a new `(unit_name, line_number)` pair into the sorted table
|
||||
|
||||
#### `1408:01a5` `BreakpointRemove`
|
||||
|
||||
Current verified behavior:
|
||||
|
||||
- scans the same `0x0b`-byte breakpoint-entry table for an exact `(unit_name, line_number)` match
|
||||
- compares the stored inline name bytes first, then the stored line word at entry `+0x09`
|
||||
- compacts the remaining entries downward when a match is found
|
||||
- decrements `breakpoint_count`
|
||||
|
||||
#### `1408:029e` `HasBreakpoint`
|
||||
|
||||
Current verified behavior:
|
||||
|
||||
- scans the same breakpoint-entry table
|
||||
- compares the requested line number against entry `+0x09`
|
||||
- compares the requested unit-name pair against the stored name bytes
|
||||
- returns a boolean-style `uint` in `AX`
|
||||
|
||||
### Callstack helpers
|
||||
|
||||
#### `1408:02f5` `CallstackPushFrame`
|
||||
|
||||
Current verified behavior:
|
||||
|
||||
- computes the current callstack-entry base as `this + 0x7c + callstack_depth * 0x15`
|
||||
- copies an inline unit-name buffer into entry `+0x00`
|
||||
- enforces a maximum visible unit-name length of eight characters plus terminator
|
||||
- stores three trailing far-pointer/state dwords at `+0x09`, `+0x0d`, and `+0x11`
|
||||
- increments `callstack_depth`
|
||||
|
||||
#### `1408:03b0` `CallstackPushEntry`
|
||||
|
||||
Current verified behavior:
|
||||
|
||||
- uses `+0x7a` as the current callstack depth
|
||||
- acts as a thinner wrapper over `CallstackPushFrame` when only the inline unit-name payload matters
|
||||
- increments the depth and asserts when it reaches `0x1e`
|
||||
|
||||
#### `1408:03f7` `CallstackPopEntry`
|
||||
|
||||
Current verified behavior:
|
||||
|
||||
- decrements `+0x7a`
|
||||
- asserts if the depth underflows below zero
|
||||
|
||||
### Step-state helpers
|
||||
|
||||
#### `1408:0419` `EnableSingleStep`
|
||||
|
||||
- clears `+0x76/+0x78`
|
||||
- sets `+0x75 = 1`
|
||||
|
||||
#### `1408:0432` `ClearStepState`
|
||||
|
||||
- clears `+0x74`
|
||||
- clears `+0x75`
|
||||
|
||||
### Current-entry name accessor
|
||||
|
||||
#### `1408:0444` `CurrentEntryGetUnitName`
|
||||
|
||||
Current verified behavior:
|
||||
|
||||
- returns null when `callstack_depth <= 0`
|
||||
- otherwise returns a far pointer to the current callstack entry's inline unit-name buffer
|
||||
|
||||
## Recovered Entry Schemas
|
||||
|
||||
The live debugger-state model is now strong enough to split the old table blobs into concrete fixed-size entry records.
|
||||
|
||||
### `UsecodeDebuggerBreakpointEntry` (`0x0b` bytes)
|
||||
|
||||
| Offset | Current name | Confidence | Current meaning |
|
||||
|---|---|---|---|
|
||||
| `+0x00..+0x08` | `unit_name_inline[9]` | High | Inline unit-name buffer, consistent with eight visible characters plus terminator. |
|
||||
| `+0x09` | `line_number` | High | Breakpoint line number compared by `BreakpointInsertSorted`, `BreakpointRemove`, and `HasBreakpoint`. |
|
||||
|
||||
### `UsecodeDebuggerCallstackEntry` (`0x15` bytes)
|
||||
|
||||
| Offset | Current name | Confidence | Current meaning |
|
||||
|---|---|---|---|
|
||||
| `+0x00..+0x08` | `unit_name_inline[9]` | High | Inline unit-name buffer for the active frame. |
|
||||
| `+0x09` | `source_stream_target_farptr` | Medium | Far pointer derived from the interpreter source-stream lane plus one fetched word in the only verified caller. |
|
||||
| `+0x0d` | `current_frame_payload_farptr` | Medium | Far pointer to the current frame payload at `frame_base + 0x04` in the only verified caller. |
|
||||
| `+0x11` | `aux_farptr` | Low | Trailing auxiliary far pointer slot; still zero in the only verified caller. |
|
||||
|
||||
## Current Working Layout
|
||||
|
||||
The live datatype `/Remorse/UsecodeDebuggerBreakState` now exists in-session with the currently safest anchors:
|
||||
|
||||
| Offset | Current name | Confidence | Current meaning |
|
||||
|---|---|---|---|
|
||||
| `+0x00` | `vtable_offset` | High | Retail debugger-state vtable offset `0x65ab`. |
|
||||
| `+0x02` | `breakpoint_count` | High | Count of `0x0b`-byte breakpoint entries. |
|
||||
| `+0x04..+0x71` | `breakpoint_entries[10]` | High | Ten inline `UsecodeDebuggerBreakpointEntry` records. |
|
||||
| `+0x72` | `current_line` | High | Current interpreted line minus one. |
|
||||
| `+0x74` | `break_armed` | Medium | Break/armed flag cleared by `ClearStepState`. |
|
||||
| `+0x75` | `single_step_enabled` | High | Single-step flag set by `EnableSingleStep`. |
|
||||
| `+0x76/+0x78` | `step_state_lo/hi` | Medium | Step-state pair cleared by `EnableSingleStep`. |
|
||||
| `+0x7a` | `callstack_depth` | High | Current callstack depth. |
|
||||
| `+0x7c..+0x2f1` | `callstack_entries[30]` | High | Thirty inline `UsecodeDebuggerCallstackEntry` records. |
|
||||
|
||||
## Live Ghidra Authoring Status
|
||||
|
||||
Verified first live class batch landed on 2026-04-06.
|
||||
|
||||
- Created class owner `Remorse::UsecodeDebuggerBreakState`.
|
||||
- Created `/Remorse/UsecodeDebuggerBreakpointEntry` (`0x0b`) and `/Remorse/UsecodeDebuggerCallstackEntry` (`0x15`) in the live data-type manager.
|
||||
- Rewrote `/Remorse/UsecodeDebuggerBreakState` in-session so the old blob regions are now explicit `UsecodeDebuggerBreakpointEntry[10]` and `UsecodeDebuggerCallstackEntry[30]` arrays at the recovered offsets.
|
||||
- Moved the main seg1408 helpers under the class owner:
|
||||
- `1408:0000` -> `Create`
|
||||
- `1408:0053` -> `MaybeBreakOnCurrentLine`
|
||||
- `1408:00dd` -> `BreakpointInsertSorted`
|
||||
- `1408:01a5` -> `BreakpointRemove`
|
||||
- `1408:029e` -> `HasBreakpoint`
|
||||
- `1408:02f5` -> `CallstackPushFrame`
|
||||
- `1408:03b0` -> `CallstackPushEntry`
|
||||
- `1408:03f7` -> `CallstackPopEntry`
|
||||
- `1408:0419` -> `EnableSingleStep`
|
||||
- `1408:0432` -> `ClearStepState`
|
||||
- `1408:0444` -> `CurrentEntryGetUnitName`
|
||||
- Tightened the live method signatures to explicit object-style forms, including:
|
||||
- `UsecodeDebuggerBreakState * __cdecl16far Create(UsecodeDebuggerBreakState * this, dword init_spec)`
|
||||
- `void __cdecl16far MaybeBreakOnCurrentLine(UsecodeDebuggerBreakState * this, word current_line)`
|
||||
- `byte __cdecl16far BreakpointInsertSorted(UsecodeDebuggerBreakState * this, dword unit_name_farptr, word line_number)`
|
||||
- `void __cdecl16far BreakpointRemove(UsecodeDebuggerBreakState * this, dword unit_name_farptr, word line_number)`
|
||||
- `uint __cdecl16far HasBreakpoint(UsecodeDebuggerBreakState * this, dword unit_name_farptr, word line_number)`
|
||||
- `void __cdecl16far CallstackPushFrame(UsecodeDebuggerBreakState * this, dword unit_name_farptr, dword source_stream_target_farptr, dword current_frame_payload_farptr, dword aux_farptr)`
|
||||
- `byte __cdecl16far CallstackPushEntry(UsecodeDebuggerBreakState * this, dword unit_name_farptr)`
|
||||
- `void __cdecl16far CallstackPopEntry(UsecodeDebuggerBreakState * this)`
|
||||
- `void __cdecl16far EnableSingleStep(UsecodeDebuggerBreakState * this)`
|
||||
- `void __cdecl16far ClearStepState(UsecodeDebuggerBreakState * this)`
|
||||
- `dword __cdecl16far CurrentEntryGetUnitName(UsecodeDebuggerBreakState * this)`
|
||||
- Added decompiler comments on the breakpoint and callstack helpers so the recovered inline-record layout is visible in-session even before every field is formally typed.
|
||||
- Added decompiler comments on the only verified `Interpreter_NextUsecodeOp` caller of `CallstackPushFrame`, which confirms the current live read of the three trailing callstack dwords:
|
||||
- `source_stream_target_farptr` is source-stream-derived from the interpreter `+0xd6/+0xd8` lane plus one fetched word
|
||||
- `current_frame_payload_farptr` is current-frame-derived from the `frame_base + 0x04` lane
|
||||
- `aux_farptr` is still zero in the only verified caller
|
||||
|
||||
## Current Cautions
|
||||
|
||||
- The retail instantiation path still appears absent; no normal caller currently reaches `Create` in the unpatched retail binary.
|
||||
- The record boundaries inside both tables are now landed in the live datatype, and two of the three trailing callstack dwords now have caller-backed structural names. The exact gameplay role behind those two far pointers is still only partly recovered.
|
||||
- `init_spec` on `Create` and `unit_name_farptr` on the breakpoint/callstack helpers are intentionally neutral names; the live signatures are object-correct, but the payload semantics should stay conservative.
|
||||
|
||||
## Best Next Moves
|
||||
|
||||
1. Identify the real gameplay semantics of `source_stream_target_farptr` and `current_frame_payload_farptr` from the interpreter-side caller lanes before promoting subsystem-specific names.
|
||||
2. Identify the vtable callback slots used by `MaybeBreakOnCurrentLine` and decide whether one or two additional methods belong on the class owner.
|
||||
3. Cross-check the seg1408 class note against the interpreter callback site at `1418:04b5` so the dormant-orphan lifecycle remains explicit in the live notes.
|
||||
4. Decide whether `aux_farptr` should remain neutral or can be promoted after one more caller or consumer pass.
|
||||
|
||||
## Bottom Line
|
||||
|
||||
`UsecodeDebuggerBreakState` is now past the “interesting orphan subsystem” stage and into real class territory.
|
||||
|
||||
The live database now has a bounded debugger-state class with a constructor, breakpoint gate, breakpoint table helpers, callstack helpers, explicit recovered `0x0b` / `0x15` entry schemas, and step-state helpers, even though the retail game still appears to leave that object dormant.
|
||||
Loading…
Add table
Add a link
Reference in a new issue