Add hidden title debug mode and pace menu polls

This commit is contained in:
gamer147
2026-07-20 23:38:19 -04:00
parent b8cf21245a
commit 0dfba316f1
10 changed files with 67 additions and 11 deletions

View File

@@ -302,7 +302,10 @@ TITLE happens to contain a developer menu immediately after its `0x1`, including
former unknown-op fallback did expose that menu when selecting TITLE's fifth action, providing a useful
visual confirmation of the static mapping but not a legitimate retail route. Opcode `0x1` now propagates a
process-exit request through hotspot callbacks and nested script frames and ends the VM session without
executing the following bytecode. End-to-end visual validation of the native
executing the following bytecode. An explicit Godot `--native-debug-menu` diagnostic can deliberately
restore the old fall-through for developer archaeology; it maps to `VmOptions.IgnoreExitRequests`, defaults
off, and is not part of the native compatibility path. Because it changes the opcode globally, it also
suppresses SYSTEM4's invalid-execution-mode exit site during that run. End-to-end visual validation of the native
`SYSTEM4 -> TITLE -> child -> 0x9 -> SYSTEM4 -> TITLE` history remains deferred until the frontend
exit/return-to-title boundary or a natural game-over/completion route exists.
@@ -778,6 +781,11 @@ native presentation trace proved that back-to-back retained mutations execute wi
first published at the following `0x20c`/`0x21c`, sleep, or stable input boundary. An earlier claim that the
opening was generally sleep-paced was inherited without execution evidence and is superseded.
The native timer arm clamps every duration to at least one millisecond. This is semantically important for
menu scripts: ROOM's steady input loop uses `sleep 0` as a one-engine-tick yield. The Godot host formerly
allowed a zero deadline, turning that loop into a free-running burst that exhausted the VM step guard and
showed `-end-`; it now applies the native minimum after the debug speed multiplier.
**Related — `present-frame` (op `0x20c`):** dispatch `param_1[0x26e9f] = gfx_op_0x20c_present_frame`
`gfx_render_frame`@`0x4820b0` (buffer flip). The port treats `0x20c` as an explicit retained-state publication
boundary in the interactive host while keeping it `noop_headless=true`; the Kelebek label `u00416200` was

View File

@@ -273,7 +273,7 @@ Implemented as a whole-stack root-reload boundary in the persistent VM. A reques
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra: dispatch ctx[0x26c93+0xc8]=0x420ec0; sleep_op_0xc8 + sleep_timer_arm decoded/annotated 2026-07-08. docs/engine-re.md sleep section.
Native handler sleep_op_0xc8 @0x420ec0 is NON-BLOCKING: it arms a timer (sleep_timer_arm @0x44cff0 at ctx+0x5f304 = active flag + start tick + duration) that the engine main loop polls, resuming the script when elapsed. Operand UNIT = MILLISECONDS (start = ms tick source DAT_0056f3d4, timeGetTime/GetTickCount class). duration<10 fast-paths via [0x56f0b8]; all real scene sleeps (100/750/1000) are >=10. The handler also records its generic 3-dword instruction length and runs anti-tamper checks, neither needed host-side. Port equivalent: the Godot host parks the VM thread for duration ms while the presentation compositor continues. Sleep is one proven presentation-capable service boundary; ordinary AE setup runs burst-fast to 0x21c and is not paced per opcode. Headless hosts no-op it (parity).
Native handler sleep_op_0xc8 @0x420ec0 is NON-BLOCKING: it arms a timer (sleep_timer_arm @0x44cff0 at ctx+0x5f304 = active flag + start tick + duration) that the engine main loop polls, resuming the script when elapsed. Operand UNIT = MILLISECONDS (start = ms tick source DAT_0056f3d4, timeGetTime/GetTickCount class), and the native arm helper clamps duration to a minimum of 1 ms. ROOM's input loop deliberately uses sleep(0), making it a one-engine-tick yield rather than a no-op. The handler also records its generic 3-dword instruction length and runs anti-tamper checks, neither needed host-side. Port equivalent: the Godot host parks the VM thread for max(1, scaled duration) ms while the presentation compositor continues. Sleep is one proven presentation-capable service boundary; ordinary AE setup runs burst-fast to 0x21c and is not paced per opcode. Headless hosts no-op it (parity).
### 0xd3 `begin-timed-callback-sequence` (u00425960, argc 0)
- **summary:** Clear and initialize the current script frame's timed local-callback sequence.

View File

@@ -150,7 +150,8 @@ route through the first `0x9` remains deferred: Himegari's readily accessible re
to the still-unimplemented frontend exit-request policy, while the other known natural paths require later
gameplay, game over, or completion. Do not use TITLE's post-`0x1` developer menu as evidence; native `0x1`
is non-returning, and the port now propagates that exit request instead of falling through into the hidden
bytecode. See `docs/engine-re.md`.
bytecode. The opt-in `--native-debug-menu` diagnostic deliberately restores fall-through for exploring that
retained developer UI, but does not qualify as native lifecycle validation. See `docs/engine-re.md`.
**Godot debug scene launcher (2026-07-20; implemented and manually validated).** The first version
is deliberately narrower than arbitrary hot swapping:
@@ -236,6 +237,13 @@ were presented, its terminal op `0x9` at `0x1fb` ran, and SYSTEM4 reconstructed
launcher/session-lifecycle discrepancy was observed. DEBUG-specific content oddities are not acceptance
failures for this developer route and remain out of scope unless they reproduce in a normal game script.
**TITLE Extra Room entry (`ROOM.BIN`, 2026-07-20).** The title dispatch itself is correct. The first manual
entry exposed a scheduler discrepancy: ROOM's stable input poll uses `sleep 0`, which native `0xc8` clamps
to a one-millisecond timer. Godot previously treated it as a zero-duration no-op, letting the script consume
the 20-million-step guard and display `-end-`. The interactive host now preserves the native one-tick yield;
Godot also prints the VM halt reason and step count before its generic end marker so future terminations are
distinguishable. Manual ROOM presentation and its genuine op-`0x9` return to TITLE remain to be rechecked.
## Stage B2 — Faithful full boot
Replace `--boot`'s diagnostic seeding and separately injected inherited surfaces with normal boot execution.

View File

@@ -139,13 +139,19 @@ at 2.5M lines). All observe-only → parity preserved; all on `run`/`play`/`swee
Plays the real bytecode with call-script execution on (subroutines run live). A no-argument launch starts
the persistent `SYSTEM4.BIN` root and reaches TITLE naturally. The local `run-godot.ps1`/`.cmd` launchers
make that route explicit with `--scene SYSTEM4` and pass neither `--boot` nor SC0000 seeds. `--headless` can't
render texture ops (no GPU context) — run windowed for real scenes. User args (after `--`):
render texture ops (no GPU context) — run windowed for real scenes. On VM termination the console prints
the exact halt reason and step count before the generic on-screen `-end-` marker. User args (after `--`):
- `--scene <NAME>` — override the default `SYSTEM4` root with a direct diagnostic scene, e.g. `--scene SC0240` (executes 29 nested subroutines).
- `--selftest` — headless; runs a **synthesized** scene through the thread/suspend/`CallDeferred` plumbing and asserts it matches a live headless run (full handling; no vm0/frozen golden). Exits.
- `--seed 0xADDR=VAL` (repeatable) — seed initial global state, e.g. `--seed 0xa57=1` unlocks Lily's form-A voiced dialogue.
- `--boot` — direct-scene diagnostic only: with `--scene <non-SYSTEM4>`, run the old
`INITCONFIG/INIT2/INIT` state prefix before that isolated scene. The normal SYSTEM4-rooted launch neither
needs nor applies it. e.g. `godot --path godot -- --scene SC0000 --boot`.
- `--native-debug-menu` — intentionally treat exit-request opcode `0x1` as a no-op, exposing TITLE's
otherwise unreachable shipped developer menu. This is a non-native debug divergence and also suppresses
SYSTEM4's invalid-execution-mode exit request for that run. Direct Godot and PowerShell launches leave it
disabled unless explicitly requested. The local `run-godot.cmd` convenience launcher enables it by
default; use `run-godot.ps1` without `-NativeDebugMenu` for a native-faithful launch.
- `--shot <png> [--shot-page N]` — capture page N to a PNG then quit (dev screenshot). At scene end it also prints the call-scripts executed as nested frames.
- `--shot-sequence <dir> [--frames N]` — dump one PNG per rendered frame (`frame_0000.png…`, default N=180 ≈ 3s @60fps) then quit, auto-advancing past input waits. Verifies time-based retained effects and publication boundaries as distinct frames, which a single `--shot` cannot. CPU/IO-heavy by design (a PNG every frame); a dev diagnostic, not a normal run. e.g. `godot --path godot -- --scene SC0000 --boot --shot-sequence out/seq --frames 300`.
- `--sleep-scale <f>` — multiply every explicit `sleep` (op 0xc8) duration by `f` (default 1.0). This stretches only script-authored sleep holds; it does not slow ordinary opcode bursts or replace `0x20c`/`0x21c` presentation pacing. Debug-only; leave at 1.0 for real playback.