Implement ADV skip lifecycle
This commit is contained in:
@@ -33,12 +33,14 @@ Struct `EngineCtx`, size `0xa1000`. Applied to the Ghidra `/v2` image (dispatch-
|
||||
| `0x51b80` | `gfx_animation_service_flags` | `uint` | retained_gfx_manager+0xb56c service flags; bit 1 suppresses op 0x243 force-complete/clock-reset request |
|
||||
| `0x52bd4` | `surfaces` | `void*` | surface array base [~1000 slots]; create/set-texture (0x1f8/0x1f9) allocate |
|
||||
| `0x53d14` | `cur_ctx_index` | `uint` | current script-context index (curCtx); indexes 0x78-byte coroutine/frame records |
|
||||
| `0x53d24` | `save_load_stack_restore_active` | `int` | set while a deserialized numbered-save script stack is being reconstructed; op 0xae restores saved frame PCs/contexts and clears it at the terminal saved context |
|
||||
| `0x53d28` | `frame_codebase` | `void*` | current frame codebase (PC = codebase + off*4) |
|
||||
| `0x53d2c` | `frame_pc` | `int` | current frame PC column (op = *(0x53d2c + curCtx*0x78)) |
|
||||
| `0x53d60` | `ctx_record_base` | `void*` | 0x78-byte context-record array base (coroutine/script contexts) |
|
||||
| `0x53d64` | `frame_script_resource_id` | `uint` | raw packed SYS4/AAI resource id for this 0x78-byte script frame; persisted ReadTextDB script key |
|
||||
| `0x53d88` | `frame_instruction_word_count` | `int` | current decoded instruction length in dwords for each 0x78-byte script frame; interpreter advances PC by this value * 4 |
|
||||
| `0x550fc` | `message_skip_display_enabled` | `int` | persistent all-message Skip state returned by op 0x19a for the ADV control-strip active overlay |
|
||||
| `0x55100` | `adv_skip_service_enabled` | `int` | ADV presentation lifecycle gate: op 0x19b clears it while preserving the persistent Skip toggle; op 0x19c sets it before recomputing active message/read skip |
|
||||
| `0x55104` | `auto_message_enabled` | `int` | ADV Auto mode flag; op 0x1b6 reads, op 0x1b7 writes, adv_input_service_poll consumes |
|
||||
| `0x55110` | `text_history_recording_suppressed` | `uint` | high bit suppresses ADV retained-history index/text/metadata/voice recording; op 0x1bb writes 0x80000000 or zero, and HISTORY.BIN brackets itself with disable/enable |
|
||||
| `0x55120` | `anti_tamper_a` | `int` | anti-tamper checksum operand |
|
||||
|
||||
@@ -1240,6 +1240,23 @@ the missing native governor and remains fast without teleporting between blockin
|
||||
op `0x88` state reaches the host before the following cadence yields. Validation is engine 168/168,
|
||||
zero-warning Godot build, and threaded `SELFTEST OK`.
|
||||
|
||||
**ADV entry/exit lifecycle.** The script comments `savemesskip` and `loadmesskip` describe a temporary
|
||||
suspension boundary, not a second saved preference. `op_0x19b_suspend_adv_skip_service@0x416560` clears
|
||||
active `ctx+0x13dc`, run-state bit `0x08000000`, and `ctx+0x55100` (`adv_skip_service_enabled`) while
|
||||
deliberately preserving `ctx+0x550fc`, the all-message Skip toggle returned by op `0x19a`.
|
||||
`op_0x19c_resume_adv_skip_service@0x4165a0` sets the lifecycle gate again and reconstructs active
|
||||
fast-forward from persistent all-message Skip or `adv_read_skip_state`; the separate startup guard at
|
||||
`ctx+0x6f86c` can suppress that reactivation. SC0000 brackets ADV teardown/setup with this pair, as do
|
||||
branch transitions and `CALLBACK_LOAD.BIN`.
|
||||
|
||||
The port therefore keeps persistent Skip separate from currently active fast-forward. Opcode `0x19b`
|
||||
deactivates the host service without changing the control-strip toggle, and `0x19c` recomputes the host
|
||||
service from the persistent toggle plus the host's live read-skip channel. This also makes op `0x1c7`
|
||||
correctly report inactive during the suspended interval. Focused regressions cover preservation across the
|
||||
pair and read-skip-only reactivation. Implementing the `message:ReadTextSkip` preference itself remains
|
||||
deferred with ops `0x1ca`/`0x1cb` and the profile-owned `ReadTextDB`; the lifecycle implementation does not
|
||||
invent a storage backend.
|
||||
|
||||
### Opcode `0x1ad` marks the numbered-save resume-frame boundary (2026-07-20)
|
||||
|
||||
Opcode `0x1ad` is a zero-operand persistence marker, not an input reset or modal-UI synchronization call.
|
||||
@@ -1266,6 +1283,26 @@ real implementation belongs in the future unified save architecture, where the V
|
||||
`ExecFrame` chain and remember which frame is the resume boundary. This is the same architectural deferral as
|
||||
the already-deferred profile/read-state work, not a reason to invent a seed or offset-specific shortcut.
|
||||
|
||||
### Opcode `0xae` continues numbered-save stack restoration (2026-07-20)
|
||||
|
||||
Opcode `0xae` is the load-side rendezvous paired with serialized script-frame state. Its handler,
|
||||
`op_0xae_continue_save_load_stack_restore@0x416790`, normally returns after recording its one-dword length.
|
||||
It only becomes effectful while `ctx+0x53d24` (`save_load_stack_restore_active`) is set by
|
||||
`save_data_deserialize_and_begin_restore@0x40fd10`. That deserializer restores the selected save layout,
|
||||
loads `CALLBACK_LOAD.BIN` or the saved entry script, and resets the current context so ordinary opcode
|
||||
`0xae` sites can rebuild the saved stack.
|
||||
|
||||
On an active restore, the handler reads `set:SaveVersion1`/`set:SaveVersion2`, selects the matching saved
|
||||
frame layout, replaces the current frame PC with its saved resume or call target, and advances through the
|
||||
serialized contexts. At the saved terminal context it clears the restore flag and reinstates the saved
|
||||
context/return state. The corpus placement supports that control-flow role: 305 calls overwhelmingly follow
|
||||
coroutine-resume or call boundaries, including SC0000's main-loop resume sequence.
|
||||
|
||||
The opcode remains an effectful port gap. It is a no-op during every currently reachable port execution,
|
||||
but its actual branch cannot be implemented until numbered saves serialize and restore the active
|
||||
`ExecFrame` chain. Counting an unconditional no-op as coverage would conceal that dependency, so it stays
|
||||
grouped with `0x1ad` rather than receiving a placeholder VM case.
|
||||
|
||||
### ADV read-message Skip and shared `RT.DAT` history (2026-07-18)
|
||||
|
||||
Read-message Skip is backed by an engine-owned `ReadTextDB`, not a VM-global flag and not ordinary numbered
|
||||
|
||||
@@ -251,6 +251,11 @@ This also names the whole call graph statically (build/callscript-names.json).
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0xa3_handler@0x420060 formats operand 1, queries value_dispatch_lookup@0x419290, writes the matched or operand-2 default PC into the current frame, and clears the command type. Corpus pairs it with 0xa1/0xa2 in 12 generic switch sequences.
|
||||
|
||||
### 0xae `continue-save-load-stack-restore` (u00415130, argc 0)
|
||||
- **summary:** () - during serialized save restoration, replace the current frame PC with its saved resume/call target and advance through the saved script-context stack; otherwise a no-op.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0xae_continue_save_load_stack_restore@0x416790 first tests ctx+0x53d24 (set by save_data_deserialize_and_begin_restore@0x40fd10). When clear it returns. When set, it selects the serialized frame layout through set:SaveVersion1/2, restores the current PC from that layout's saved return/call target, advances through contexts with FUN_0040f2d0, and clears the restore flag on reaching the saved terminal context. Its 305 corpus sites overwhelmingly follow coroutine-resume/call boundaries, which provide the rendezvous points used while reconstructing the stack.
|
||||
|
||||
### 0xc8 `sleep` (sleep, argc 1)
|
||||
- **summary:** Pause the current script for <duration> milliseconds while retained presentation continues.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
@@ -681,6 +686,16 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x19a_get_message_skip@0x4271b0 writes ctx+0x550fc to operand 1. The shared ADV redraw routine uses it to select SO001's active Message-skip icon at x=728.
|
||||
|
||||
### 0x19b `suspend-adv-skip-service` (u00414E80, argc 0)
|
||||
- **summary:** () - suspend active ADV fast-forward while preserving the user's persistent all-message Skip toggle.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x19b_suspend_adv_skip_service@0x416560 clears run-state bit 0x08000000, ctx+0x13dc active message skip, ctx+0x55100 ADV skip-service enable, and the click-cancel phase, but deliberately leaves ctx+0x550fc persistent all-message Skip unchanged. Corpus comments call it `savemesskip`; ADV-end/branch and CALLBACK_LOAD paths use it before leaving ADV presentation.
|
||||
|
||||
### 0x19c `resume-adv-skip-service` (u00414EC0, argc 0)
|
||||
- **summary:** () - enable the ADV skip service and recompute active fast-forward from persistent all-message Skip or the live read-skip channel.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
- **evidence:** Ghidra /v2: op_0x19c_resume_adv_skip_service@0x4165a0 sets ctx+0x55100, clears the click-cancel phase, and normally activates run-state bit 0x08000000 when ctx+0x550fc persistent all-message Skip or ctx+0x6dbd4 adv_read_skip_state is nonzero. Otherwise it clears ctx+0x13dc and the run-state bit. A separate startup fast-forward guard at ctx+0x6f86c suppresses reactivation while active. Corpus comments call it `loadmesskip`; ADV setup, branch, and CALLBACK_LOAD paths pair it with op 0x19b.
|
||||
|
||||
### 0x1b6 `get-auto-message` (u00414F60, argc 1)
|
||||
- **summary:** (out) - return whether automatic message advance is enabled.
|
||||
- **grounding:** source=investigation, confidence=high
|
||||
@@ -895,10 +910,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=med
|
||||
|
||||
### 0xae `u00415130` (u00415130, argc 0)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
|
||||
### 0xb7 `u0041D0E0` (u0041D0E0, argc 1)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
@@ -1031,14 +1042,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=med
|
||||
|
||||
### 0x19b `u00414E80` (u00414E80, argc 0)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
|
||||
### 0x19c `u00414EC0` (u00414EC0, argc 0)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
|
||||
### 0x19d `u0041C680` (u0041C680, argc 2)
|
||||
- **summary:** —
|
||||
- **grounding:** source=kelebek, confidence=low
|
||||
|
||||
@@ -2104,3 +2104,31 @@ important than any one opcode's count; persistence-bound `0x1a2`, `0x1ad`, and `
|
||||
|
||||
Validation: all 211 engine tests pass; opcode and EngineCtx tests/lints, vm0 RECOVER, the zero-warning
|
||||
Godot build, threaded self-test, and `git diff --check` are clean.
|
||||
|
||||
### Slice A2b ADV skip lifecycle implemented; adjacent persistence gaps classified (2026-07-20)
|
||||
|
||||
Native RE resolves script `savemesskip`/`loadmesskip` opcodes `0x19b`/`0x19c` as an ADV presentation
|
||||
lifecycle pair. Suspend clears active fast-forward and the service gate while preserving the persistent
|
||||
all-message Skip toggle. Resume re-enables the gate and recomputes active fast-forward from that toggle or
|
||||
the live read-skip channel. The VM now models persistent and active skip state separately, and focused tests
|
||||
prove both persistent-toggle preservation and read-skip-only reactivation.
|
||||
|
||||
The same investigation classified the two adjacent one-call gaps without papering them over. Opcode `0x1ca`
|
||||
is the already-known `message:ReadTextSkip` settings write and remains deferred with `0x1cb`, `RT.DAT`, and
|
||||
the profile-owned read-state decision. Opcode `0xae` is normally inert but becomes the serialized save-stack
|
||||
restoration rendezvous: it restores frame PCs and walks saved contexts while a flag set by the save-data
|
||||
deserializer is active. Because the port has no numbered-save `ExecFrame` backend, an unconditional no-op
|
||||
would be a false implementation; it remains grouped with `0x1ad`.
|
||||
|
||||
The canonical opcode source and expanded 78-field EngineCtx source are regenerated. `/v2` names/comments
|
||||
the three handlers and save deserializer, applies `adv_skip_service_enabled` and
|
||||
`save_load_stack_restore_active`, and is saved. SC0000 is now **124/129 distinct opcodes handled (96.1%)**
|
||||
and **16,041/16,257 instructions handled or safe-noop (98.7%)**. Its five remaining gaps total 216
|
||||
instructions: `0x1a2`×206, `0x1ad`×6, `0x1cb`×2, `0x1ca`×1, and `0xae`×1.
|
||||
|
||||
**Next:** keep all five remaining gaps behind the unified profile/read-state/numbered-save ownership work.
|
||||
Choose the next implementation slice from a different scene or runtime subsystem rather than claiming
|
||||
normal-path no-ops for `0xae` or a transient-only `ReadTextSkip` setting.
|
||||
|
||||
Validation: 213 engine tests pass; opcode and EngineCtx generators/tests/lints, SC0000 coverage, vm0
|
||||
RECOVER, the zero-warning Godot build, threaded self-test, and `git diff --check` are clean.
|
||||
|
||||
Reference in New Issue
Block a user