Analyze QVM movement scheduling step prefix and outputs
Browse files- docs/ASSET_PIPELINE_TODO.md +2 -0
- docs/FINDINGS.md +6 -0
- docs/PROVISIONAL_TODO.md +2 -0
- docs/RESEARCH_LEDGER.md +6 -0
- docs/REVERSE_ENGINEERING_TODO.md +5 -0
- docs/research/QVM_MOVEMENT_FRAME.md +2 -0
- docs/research/QVM_MOVEMENT_LOOP.md +58 -0
- docs/research/README.md +2 -0
- docs/research/qvm-movement-loop.json +0 -0
- scripts/analyze_deferred_auth.py +4 -2
- scripts/analyze_qvm_movement_loop.py +47 -0
- scripts/test_qvm_movement_loop.py +62 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -250,3 +250,5 @@ Research progress: [native/QVM usercmd bridge](research/QVM_USERCMD.md) complete
|
|
| 250 |
Research progress: [QVM think routes/inactivity/mode gates](research/QVM_THINK_GATES.md) completes bounded RE-409–411. RE-408/412 retain producers/action effects/movement/simulation/full-command/runtime/platform work; broad game/rendering milestones remain open.
|
| 251 |
|
| 252 |
Research progress: [QVM movement-frame setup](research/QVM_MOVEMENT_FRAME.md) completes bounded RE-413–415. RE-412/416 retain consumers/physics/callbacks/producers/full simulation/runtime/platform work; broad game/rendering milestones remain open.
|
|
|
|
|
|
|
|
|
| 250 |
Research progress: [QVM think routes/inactivity/mode gates](research/QVM_THINK_GATES.md) completes bounded RE-409–411. RE-408/412 retain producers/action effects/movement/simulation/full-command/runtime/platform work; broad game/rendering milestones remain open.
|
| 251 |
|
| 252 |
Research progress: [QVM movement-frame setup](research/QVM_MOVEMENT_FRAME.md) completes bounded RE-413–415. RE-412/416 retain consumers/physics/callbacks/producers/full simulation/runtime/platform work; broad game/rendering milestones remain open.
|
| 253 |
+
|
| 254 |
+
Research progress: [QVM movement scheduler/output gates](research/QVM_MOVEMENT_LOOP.md) completes bounded RE-417–419. RE-416/420 retain actual child clock/physics/callbacks/event effects/full movement/runtime/platform work; broad game/rendering milestones remain open.
|
docs/FINDINGS.md
CHANGED
|
@@ -480,3 +480,9 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 480 |
**F-170 — Command and mask gates:** a separate three-condition gate clears forward/right bytes and only signed-positive up bytes. Type/linked-helper/bit8/game-mode branches select four observed masks, with zero helper result clearing two link words. Physics and helper/bit meanings remain unverified. [FRAME-002](research/QVM_MOVEMENT_FRAME.md).
|
| 481 |
|
| 482 |
**F-171 — Movement frame assembly:** 160 bounded fixtures match every byte of a supplied-zero 268-byte frame containing pointer/command/mask/callback/flag inputs, plus vector copies and recorded helper54475 requests. No movement helper executes; consumer layout, callback/native collision effects and runtime parity remain open. [FRAME-003](research/QVM_MOVEMENT_FRAME.md).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 480 |
**F-170 — Command and mask gates:** a separate three-condition gate clears forward/right bytes and only signed-positive up bytes. Type/linked-helper/bit8/game-mode branches select four observed masks, with zero helper result clearing two link words. Physics and helper/bit meanings remain unverified. [FRAME-002](research/QVM_MOVEMENT_FRAME.md).
|
| 481 |
|
| 482 |
**F-171 — Movement frame assembly:** 160 bounded fixtures match every byte of a supplied-zero 268-byte frame containing pointer/command/mask/callback/flag inputs, plus vector copies and recorded helper54475 requests. No movement helper executes; consumer layout, callback/native collision effects and runtime parity remain open. [FRAME-003](research/QVM_MOVEMENT_FRAME.md).
|
| 483 |
+
|
| 484 |
+
**F-172 — Movement scheduler:** helper54475 rejects backward time, caps excessive gap at1000, increments/masks a counter and resets output words even for equal timestamps. Step caps8/66 and post-step bit8192 up-byte edits are verified under a supplied child clock-update model; actual physics/termination domains remain open. [LOOP-001](research/QVM_MOVEMENT_LOOP.md).
|
| 485 |
+
|
| 486 |
+
**F-173 — Single-step input prefix:** child53418 stores a global frame pointer, increments another counter, clears outputs, removes button bit16 when signed forward/right magnitude exceeds64, then applies bit2 button/movement and player-state flag precedence. Semantic bit names and later action/physics behavior remain unverified. [LOOP-002](research/QVM_MOVEMENT_LOOP.md).
|
| 487 |
+
|
| 488 |
+
**F-174 — Post-loop outputs:** a separate suffix computes signed-direction/button/flag bits in player-state+240 for positive +208, caches buttons and records ordered helper29913 event93/21/31 requests. The third path also extracts two packed bytes without a visible selector bounds check. Event effects/full composition/runtime remain open. [LOOP-003](research/QVM_MOVEMENT_LOOP.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -562,3 +562,5 @@ Research progress: [native/QVM usercmd bridge](research/QVM_USERCMD.md) complete
|
|
| 562 |
Research progress: [QVM think routes/inactivity/mode gates](research/QVM_THINK_GATES.md) completes bounded RE-409–411. RE-408/412 retain producers/action effects/movement/simulation/full-command/runtime/platform work; broad game/rendering milestones remain open.
|
| 563 |
|
| 564 |
Research progress: [QVM movement-frame setup](research/QVM_MOVEMENT_FRAME.md) completes bounded RE-413–415. RE-412/416 retain consumers/physics/callbacks/producers/full simulation/runtime/platform work; broad game/rendering milestones remain open.
|
|
|
|
|
|
|
|
|
| 562 |
Research progress: [QVM think routes/inactivity/mode gates](research/QVM_THINK_GATES.md) completes bounded RE-409–411. RE-408/412 retain producers/action effects/movement/simulation/full-command/runtime/platform work; broad game/rendering milestones remain open.
|
| 563 |
|
| 564 |
Research progress: [QVM movement-frame setup](research/QVM_MOVEMENT_FRAME.md) completes bounded RE-413–415. RE-412/416 retain consumers/physics/callbacks/producers/full simulation/runtime/platform work; broad game/rendering milestones remain open.
|
| 565 |
+
|
| 566 |
+
Research progress: [QVM movement scheduler/output gates](research/QVM_MOVEMENT_LOOP.md) completes bounded RE-417–419. RE-416/420 retain actual child clock/physics/callbacks/event effects/full movement/runtime/platform work; broad game/rendering milestones remain open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -289,3 +289,9 @@ Validation: all preceding composition reports regenerated byte-identically, incl
|
|
| 289 |
Completed bounded RE-413–415 with `analyze_qvm_movement_frame.py`: verified shipped qagame setup/assembly slices, 72 finite gravity/speed probes, four raw packed-word probes and 160 complete supplied-zero frame probes. FRAME-001–003/F-169–171 recover bit0-controlled gravity/speed writes, finite binary32 multiply/truncate and one-use pointer consumption, unmasked shift/OR packing, signed-positive-up versus forward/right suppression, four observed mask words/precedence and linked-helper failure cleanup, and every byte of the 268-byte frame passed to helper54475. Two bit-preserving vector copies and raw-versus-Boolean flags/callback values are independently verified. Trap100 frame-zeroing and helper151954 results are supplied with omitted effects; helper54475 requests stop and no movement physics/full QVM/native callee/original runtime executes. Semantic names/mask bits/configuration/layout consumers and callback contracts remain candidates. Shared evaluator extensions are restricted aligned word-backed BLOCK_COPY with byte overlay, BOR and finite in-range binary32 MULF/CVFI; unknown/unaligned copy and exceptional float cases fail closed. Eight independent tests verify arithmetic/pointer behavior, raw packing, command bytes/canaries, full frame/vector output, mask/link/request precedence, source identities, copy overlay/failure domains and finite conversion/OR boundaries. All 439 tests pass; storage/links/whitespace checks pass. RE-412 remains open; RE-416 tracks helper54475 consumers/physics, callback/native collision ABI, helper151954/link/configuration/packed-word producers/legal ranges, copy/float/alias/runtime edge cases, post-movement mutation and full command-to-simulation/gameplay/macOS/Linux conformance before Rust/Bevy/Solari claims.
|
| 290 |
|
| 291 |
Validation: all preceding composition reports regenerated byte-identically, including shared-QVM-evaluator users. The new movement-frame report reproduced twice byte-identically. Final suite: 439 passing tests. Storage policy/filter setup, documentation links and whitespace checks pass.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
Completed bounded RE-413–415 with `analyze_qvm_movement_frame.py`: verified shipped qagame setup/assembly slices, 72 finite gravity/speed probes, four raw packed-word probes and 160 complete supplied-zero frame probes. FRAME-001–003/F-169–171 recover bit0-controlled gravity/speed writes, finite binary32 multiply/truncate and one-use pointer consumption, unmasked shift/OR packing, signed-positive-up versus forward/right suppression, four observed mask words/precedence and linked-helper failure cleanup, and every byte of the 268-byte frame passed to helper54475. Two bit-preserving vector copies and raw-versus-Boolean flags/callback values are independently verified. Trap100 frame-zeroing and helper151954 results are supplied with omitted effects; helper54475 requests stop and no movement physics/full QVM/native callee/original runtime executes. Semantic names/mask bits/configuration/layout consumers and callback contracts remain candidates. Shared evaluator extensions are restricted aligned word-backed BLOCK_COPY with byte overlay, BOR and finite in-range binary32 MULF/CVFI; unknown/unaligned copy and exceptional float cases fail closed. Eight independent tests verify arithmetic/pointer behavior, raw packing, command bytes/canaries, full frame/vector output, mask/link/request precedence, source identities, copy overlay/failure domains and finite conversion/OR boundaries. All 439 tests pass; storage/links/whitespace checks pass. RE-412 remains open; RE-416 tracks helper54475 consumers/physics, callback/native collision ABI, helper151954/link/configuration/packed-word producers/legal ranges, copy/float/alias/runtime edge cases, post-movement mutation and full command-to-simulation/gameplay/macOS/Linux conformance before Rust/Bevy/Solari claims.
|
| 290 |
|
| 291 |
Validation: all preceding composition reports regenerated byte-identically, including shared-QVM-evaluator users. The new movement-frame report reproduced twice byte-identically. Final suite: 439 passing tests. Storage policy/filter setup, documentation links and whitespace checks pass.
|
| 292 |
+
|
| 293 |
+
## 2026-10-12 — Game-QVM movement scheduler, step prefix and post-loop outputs
|
| 294 |
+
|
| 295 |
+
Completed bounded RE-417–419 with `analyze_qvm_movement_loop.py`: verified shipped qagame helper54475 partition and child53418 prefix, 96 scheduler probes, 324 single-prefix probes and 334 separate suffix probes. LOOP-001–003/F-172–174 recover signed backward/equal/capped time behavior, masked outer counter/output resets, step caps8/66 and post-step bit8192 up-byte edits under a supplied child clock-update model; signed movement magnitude threshold64/button bit16 removal and bit2 flag/input precedence; and post-loop direction/button bits, button cache, ordered event93/21/31 helper requests and sampled packed-byte lookup. Equal timestamps reset/increment without a child request; backward time exits before those mutations. Each requested substep is followed by explicit supplied PS.time advancement only, not actual physics. Single-prefix/suffix evaluations are independent and external helper/event effects omitted; no full QVM/native callee/original runtime/collision/physics executes. Shared evaluator adds an optional in-bounds resume address to retain supplied locals across stopped continuations, preserving existing default behavior. Eight independent tests verify schedules/requests/caps/counters/resets/up edits, signed-byte thresholds/button precedence, output flags/cache/events/packed lookup, provenance, resume/failure domains and supplied-child scope. All 447 tests pass; storage/links/whitespace checks pass. RE-416 remains open; RE-420 tracks real child clock/state/weapon/physics/callback effects, nonprogressing/overflow scheduling domains, flag/event/packed-selector semantics/bounds and helper29913 mutations, real movement-step composition/gameplay/macOS/Linux and Rust/Bevy/Solari compatibility; packet/entity/player/snapshot/rendering milestones remain open.
|
| 296 |
+
|
| 297 |
+
Validation: all preceding composition reports regenerated byte-identically, including shared-QVM-evaluator users. The new movement-loop report reproduced twice byte-identically. Final suite: 447 passing tests. Storage policy/filter setup, documentation links and whitespace checks pass.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -502,3 +502,8 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 502 |
- [x] RE-414 Recover bounded signed movement-byte suppression and movement-frame mask/helper/linked-pointer cleanup gates with independent request/byte oracles. [FRAME-002](research/QVM_MOVEMENT_FRAME.md).
|
| 503 |
- [x] RE-415 Recover complete supplied-zero 268-byte frame assembly, command/vector copies, raw/Boolean flags and encoded callback values; stop at helper54475 and verify every frame byte. [FRAME-003](research/QVM_MOVEMENT_FRAME.md).
|
| 504 |
- [ ] RE-416 Recover helper54475 frame consumers/physics, callback/native collision ABI, helper151954/link producers, setup globals/configuration/packed-word legal ranges, copy/float/alias/runtime edge cases and post-movement mutations; compose full command-to-simulation and validate gameplay/macOS/Linux before Rust/Bevy/Solari claims. Continue entity/player/snapshot/rendering work.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
- [x] RE-414 Recover bounded signed movement-byte suppression and movement-frame mask/helper/linked-pointer cleanup gates with independent request/byte oracles. [FRAME-002](research/QVM_MOVEMENT_FRAME.md).
|
| 503 |
- [x] RE-415 Recover complete supplied-zero 268-byte frame assembly, command/vector copies, raw/Boolean flags and encoded callback values; stop at helper54475 and verify every frame byte. [FRAME-003](research/QVM_MOVEMENT_FRAME.md).
|
| 504 |
- [ ] RE-416 Recover helper54475 frame consumers/physics, callback/native collision ABI, helper151954/link producers, setup globals/configuration/packed-word legal ranges, copy/float/alias/runtime edge cases and post-movement mutations; compose full command-to-simulation and validate gameplay/macOS/Linux before Rust/Bevy/Solari claims. Continue entity/player/snapshot/rendering work.
|
| 505 |
+
|
| 506 |
+
- [x] RE-417 Recover bounded helper54475 time rejection/cap/counter/reset/substep scheduling and post-step byte edits under explicitly supplied child clock progress. [LOOP-001](research/QVM_MOVEMENT_LOOP.md).
|
| 507 |
+
- [x] RE-418 Recover bounded child53418 global/reset/signed-movement threshold and button/flag cleanup prefix with independent byte oracles. [LOOP-002](research/QVM_MOVEMENT_LOOP.md).
|
| 508 |
+
- [x] RE-419 Recover bounded post-loop flag/cache and event-helper requests, packed-byte lookup and ordering with supplied suffix state. [LOOP-003](research/QVM_MOVEMENT_LOOP.md).
|
| 509 |
+
- [ ] RE-420 Recover actual child53418 clock/state/weapon/physics and callback effects, nonprogress/overflow scheduling domains, input/flag/event meanings/packed selector bounds and helper29913 mutations; compose real movement steps/outputs and validate gameplay/macOS/Linux before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
|
docs/research/QVM_MOVEMENT_FRAME.md
CHANGED
|
@@ -64,3 +64,5 @@ The shared evaluator adds restricted aligned word-backed BLOCK_COPY with a byte
|
|
| 64 |
Eight independent tests verify finite arithmetic/pointer consumption, unmasked shift/OR packing, signed input suppression/canaries, complete frame/vector bytes, mask/cleanup/call precedence, provenance, byte-overlay copy/failure domains and finite conversion/OR boundaries. Existing report regeneration checks protect previous evaluator users.
|
| 65 |
|
| 66 |
RE-412 remains open for complete setup/producer/simulation/runtime work. RE-416 must recover helper54475 and its entire frame layout/consumers, callback ABI/native collision integration, helper151954 and link producers, gravity/speed and packed-word registrations/legal ranges, byte-copy/float/alias/runtime edge cases, post-movement mutations and full packet-to-simulation composition. Validate actual gameplay and macOS/Linux behavior before Rust/Bevy/Solari compatibility claims; entity/player/snapshot codecs and rendering also remain open.
|
|
|
|
|
|
|
|
|
| 64 |
Eight independent tests verify finite arithmetic/pointer consumption, unmasked shift/OR packing, signed input suppression/canaries, complete frame/vector bytes, mask/cleanup/call precedence, provenance, byte-overlay copy/failure domains and finite conversion/OR boundaries. Existing report regeneration checks protect previous evaluator users.
|
| 65 |
|
| 66 |
RE-412 remains open for complete setup/producer/simulation/runtime work. RE-416 must recover helper54475 and its entire frame layout/consumers, callback ABI/native collision integration, helper151954 and link producers, gravity/speed and packed-word registrations/legal ranges, byte-copy/float/alias/runtime edge cases, post-movement mutations and full packet-to-simulation composition. Validate actual gameplay and macOS/Linux behavior before Rust/Bevy/Solari compatibility claims; entity/player/snapshot codecs and rendering also remain open.
|
| 67 |
+
|
| 68 |
+
Follow-up: [scheduler/output gates](QVM_MOVEMENT_LOOP.md) recovers bounded outer-loop scheduling under supplied child clock progress, the child input prefix and a separate post-loop suffix. Actual child clock/physics/callbacks and event effects remain open.
|
docs/research/QVM_MOVEMENT_LOOP.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Game-QVM movement scheduler and output gates
|
| 2 |
+
|
| 3 |
+
RE-417–419 / F-172–174. [qvm-movement-loop.json](qvm-movement-loop.json) retains the complete linear partition of helper54475 and the entry prefix of child53418 from shipped qagame. Reproduce with `python3 scripts/analyze_qvm_movement_loop.py`; verify with `python3 scripts/test_qvm_movement_loop.py`.
|
| 4 |
+
|
| 5 |
+
This follows [movement-frame assembly](QVM_MOVEMENT_FRAME.md). All results use bounded synthetic QVM memory. No original executable, native callee, full QVM, collision callback or physics runs. Scheduler requests to child53418 use an explicitly supplied effect: advance player-state time to the requested command time, with no other child effects. Child-prefix and post-loop slices are evaluated separately; helper29913 requests are recorded with omitted effects. Actual child clock behavior and complete composition remain open.
|
| 6 |
+
|
| 7 |
+
## LOOP-001 — Time gate, cap and substeps (RE-417)
|
| 8 |
+
|
| 9 |
+
Helper54475 receives the frame pointer. It loads final command time at frame+4 and current time through frame[+0]. Using signed comparisons, a final time below current time exits before the counter/reset stores. If final time exceeds signed32(current+1000), it sets current time to signed32(final-1000).
|
| 10 |
+
|
| 11 |
+
Accepted paths increment player-state+456 and mask the result with63. They clear frame words +48/+252/+256/+260/+264, even when final time equals current time and no child step is requested.
|
| 12 |
+
|
| 13 |
+
Until current time equals the captured final time, each iteration computes signed32(final-current), then caps the step at8 when frame+220 is nonzero, or66 when it is zero. It sets frame+4 to signed32(current+step), records child53418(frame), and inspects player-state+12 afterward. Bit8192 forces the frame's signed movement byte at +27 to20 before the next iteration. Child clock progress is supplied by the model, not recovered by executing physics.
|
| 14 |
+
|
| 15 |
+
Ninety-six fixtures use current time1000 and finals999/1000/1001/1008/1066/1067/2000/2001, fixed flags0/1, counters0/63/0xffffffff and bit8192 off/on. Independent arithmetic checks every substep request/time, cap, counter, reset and later up-byte input. In the supplied-clock model, the 1000-unit capped gap requires at most125 eight-unit steps. Zero delta still increments/reset; backward time retains those fields.
|
| 16 |
+
|
| 17 |
+
This establishes a bounded scheduler contract under the supplied child effect. It does not establish wraparound legality, behavior when the child fails to advance time, real game-clock units, physics integration or callback side effects. Signed overflow domains and runtime loop termination remain open.
|
| 18 |
+
|
| 19 |
+
## LOOP-002 — Single-step entry input cleanup (RE-418)
|
| 20 |
+
|
| 21 |
+
The retained child53418 prefix (53419–53593) stores the incoming frame pointer in global35024032, increments global21232 and clears frame+52/+208/+212. This global counter does not use the outer player-state counter's observed mask63 in the prefix.
|
| 22 |
+
|
| 23 |
+
It sign-extends forward/right bytes at frame+25/+26 and obtains their absolute values. If either exceeds64, it clears button-word bit16 at frame+20. Absolute64 is retained; absolute65 is not. Byte128 becomes signed-128 and absolute128, so it reaches the large-input path.
|
| 24 |
+
|
| 25 |
+
Then button bit2 controls a separate branch. When set, the prefix ORs bit524288 into player-state+104, replaces the complete button word with2 and clears all three movement bytes at frame+25/+26/+27. Otherwise it clears player-state flag bit524288 and retains the movement bytes. WALK/TALK-like interpretations are candidates; producer/consumer and configuration evidence is required before assigning semantic names.
|
| 26 |
+
|
| 27 |
+
324 probes cover all forward/right combinations of bytes0/1/64/65/127/128/191/192/255 with button words0/16/17/18. Independent signed-byte and bit-mask oracles check threshold boundaries, bit2 precedence, exact final buttons/movement, counter/global pointer and reset words. Evaluation stops before subsequent weapon/action/physics branches.
|
| 28 |
+
|
| 29 |
+
## LOOP-003 — Post-loop flags and event requests (RE-419)
|
| 30 |
+
|
| 31 |
+
The separate suffix (54637–54979) copies frame+20 to player-state+212, clears player-state+240, then ORs the following observed bits only when player-state+208 >0. It reads the command through global35024032; the fixture explicitly sets that global to the supplied frame.
|
| 32 |
+
|
| 33 |
+
| Source predicate | Bit ORed into player-state+240 |
|
| 34 |
+
| --- | --- |
|
| 35 |
+
| Buttons bit16 | 64 |
|
| 36 |
+
| Buttons bit256 | 32 |
|
| 37 |
+
| Signed up byte negative | 128 |
|
| 38 |
+
| Player-state+12 bit8192 | 1 |
|
| 39 |
+
| Signed forward byte positive / negative | 2 / 4 |
|
| 40 |
+
| Signed right byte negative / positive | 8 / 16 |
|
| 41 |
+
|
| 42 |
+
Other sampled health-like words leave +240 zero. Flag meanings and their animation/gameplay consumers are not recovered here.
|
| 43 |
+
|
| 44 |
+
The suffix records helper29913 requests `(event, parameter, player-state pointer)` in this order:
|
| 45 |
+
|
| 46 |
+
- Nonzero frame+48: event93, parameter frame+48.
|
| 47 |
+
- Nonzero frame+252: event21, parameter frame+256.
|
| 48 |
+
- Nonzero frame+260: event31, parameter frame+264.
|
| 49 |
+
|
| 50 |
+
Before the third request it reads `player-state +312 +4*player-state[+144]`, extracts low byte and next byte into locals32/36, and does not pass these two locals as that request's visible arguments. No index bounds check appears in this retained path. This connects the [earlier packed-state lookup evidence](CHAT_ITEMS_TIME.md) to another consumer without declaring valid selector ranges or event semantics.
|
| 51 |
+
|
| 52 |
+
334 suffix fixtures cover health-like/button/signed-direction combinations and independent event combinations with selectors0/7. Tests predict every output flag, cached button word, event request/ordering and sampled packed-byte result. No event helper effects are applied; these suffix probes are separate from the scheduler's supplied child-clock model.
|
| 53 |
+
|
| 54 |
+
## Verification and next tasks
|
| 55 |
+
|
| 56 |
+
Eight independent tests verify backward/equal/capped scheduling, every step/request, resets/counters/up-byte edits, entry threshold/button precedence, post-loop flag arithmetic/cache, event requests/packed lookup, source/boundaries, explicit resume behavior and supplied-child scope. The evaluator adds an optional resume address within the declared bounded slice, retaining synthetic locals across stopped-call continuations. Invalid resume addresses fail; default behavior for existing scripts is unchanged.
|
| 57 |
+
|
| 58 |
+
RE-416 remains open for full consumers/physics/runtime work. RE-420 must trace the child53418 clock update and remaining state/weapon/physics branches, real callback/frame mutations and nonprogressing/overflow scheduling domains; confirm input/flag/event meanings and packed selector bounds; recover helper29913 effects; compose actual frame-to-step-to-output behavior and validate gameplay/macOS/Linux before Rust/Bevy/Solari compatibility claims. Native packet and entity/player/snapshot/rendering milestones also remain open.
|
docs/research/README.md
CHANGED
|
@@ -163,3 +163,5 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 163 |
[QVM think routes, inactivity and mode selection](QVM_THINK_GATES.md) adds RE-409–411. Reproduce with `python3 scripts/analyze_qvm_think_gates.py`; check with `python3 scripts/test_qvm_think_gates.py`. External actions are recorded with omitted effects; full simulation/runtime work remains open.
|
| 164 |
|
| 165 |
[QVM gravity and movement-frame assembly](QVM_MOVEMENT_FRAME.md) adds RE-413–415. Reproduce with `python3 scripts/analyze_qvm_movement_frame.py`; check with `python3 scripts/test_qvm_movement_frame.py`. Frame zeroing/helper results are supplied; movement physics and callback/runtime effects remain open.
|
|
|
|
|
|
|
|
|
| 163 |
[QVM think routes, inactivity and mode selection](QVM_THINK_GATES.md) adds RE-409–411. Reproduce with `python3 scripts/analyze_qvm_think_gates.py`; check with `python3 scripts/test_qvm_think_gates.py`. External actions are recorded with omitted effects; full simulation/runtime work remains open.
|
| 164 |
|
| 165 |
[QVM gravity and movement-frame assembly](QVM_MOVEMENT_FRAME.md) adds RE-413–415. Reproduce with `python3 scripts/analyze_qvm_movement_frame.py`; check with `python3 scripts/test_qvm_movement_frame.py`. Frame zeroing/helper results are supplied; movement physics and callback/runtime effects remain open.
|
| 166 |
+
|
| 167 |
+
[QVM movement scheduler and outputs](QVM_MOVEMENT_LOOP.md) adds RE-417–419. Reproduce with `python3 scripts/analyze_qvm_movement_loop.py`; check with `python3 scripts/test_qvm_movement_loop.py`. Child clock advancement is supplied; physics and event effects remain open.
|
docs/research/qvm-movement-loop.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_deferred_auth.py
CHANGED
|
@@ -17,7 +17,7 @@ def signed32(value):
|
|
| 17 |
|
| 18 |
|
| 19 |
def trace_slice(instructions, start, stop, memory, locals_map, limit=256, byte_memory=None, capture_stack=False,
|
| 20 |
-
halt_on_leave=False, stop_targets=(), call_results=None):
|
| 21 |
"""Evaluate only a selected straight/bounded slice against synthetic memory.
|
| 22 |
|
| 23 |
Record calls rather than entering callees. Reject unknown reads/opcodes and
|
|
@@ -31,7 +31,9 @@ def trace_slice(instructions, start, stop, memory, locals_map, limit=256, byte_m
|
|
| 31 |
local_base = 0x40000000
|
| 32 |
for offset, value in locals_map.items():
|
| 33 |
memory[local_base+offset] = value
|
| 34 |
-
pc = start
|
|
|
|
|
|
|
| 35 |
while pc != stop and pc not in stop_targets:
|
| 36 |
if not start <= pc < stop or len(trace) >= limit:
|
| 37 |
raise ValueError('slice control flow escaped bound')
|
|
|
|
| 17 |
|
| 18 |
|
| 19 |
def trace_slice(instructions, start, stop, memory, locals_map, limit=256, byte_memory=None, capture_stack=False,
|
| 20 |
+
halt_on_leave=False, stop_targets=(), call_results=None, resume_at=None):
|
| 21 |
"""Evaluate only a selected straight/bounded slice against synthetic memory.
|
| 22 |
|
| 23 |
Record calls rather than entering callees. Reject unknown reads/opcodes and
|
|
|
|
| 31 |
local_base = 0x40000000
|
| 32 |
for offset, value in locals_map.items():
|
| 33 |
memory[local_base+offset] = value
|
| 34 |
+
pc = start if resume_at is None else resume_at
|
| 35 |
+
if not start <= pc <= stop:
|
| 36 |
+
raise ValueError('resume outside bounded slice')
|
| 37 |
while pc != stop and pc not in stop_targets:
|
| 38 |
if not start <= pc < stop or len(trace) >= limit:
|
| 39 |
raise ValueError('slice control flow escaped bound')
|
scripts/analyze_qvm_movement_loop.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Bounded movement scheduler/input cleanup/post-loop slices, no physics."""
|
| 3 |
+
import itertools
|
| 4 |
+
import json
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
from analyze_bsp_references import trace_fields
|
| 7 |
+
from analyze_deferred_auth import signed32,trace_slice
|
| 8 |
+
from analyze_qvm import decode,write_json
|
| 9 |
+
from analyze_vm_extensions import verified
|
| 10 |
+
|
| 11 |
+
FRAME=0x10000
|
| 12 |
+
PS=0x20000
|
| 13 |
+
LOCAL=0x40000000
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def loop_probe(ins,old,final,fixed,counter,flag):
|
| 17 |
+
m={FRAME:PS,FRAME+4:final,FRAME+220:fixed,PS:old,PS+456:counter,PS+12:flag};b={FRAME+27:1}
|
| 18 |
+
for o in [48,252,256,260,264]:m[FRAME+o]=0x1234
|
| 19 |
+
pc=None;trace=[];requests=[];times=[];up=[];initial=None
|
| 20 |
+
while True:
|
| 21 |
+
t=trace_slice(ins,54476,54979,m,{72:FRAME} if pc is None else {},stop_targets=[54560,54637] if pc==54614 else [54614,54637],resume_at=pc,byte_memory=b,capture_stack=True,limit=512);m=t['memory'];b=t['byte_memory'];trace+=t['trace']
|
| 22 |
+
if initial is None:initial=m[PS]
|
| 23 |
+
requests+=t['calls']
|
| 24 |
+
if t['stop']==54560:pc=54560;continue
|
| 25 |
+
if t['stop']!=54614:break
|
| 26 |
+
if t['calls']!=[{'instruction':54612,'target':53418,'args':{8:FRAME}}]:raise ValueError('step request '+str(t['calls']))
|
| 27 |
+
times.append(m[FRAME+4]);up.append(b[FRAME+27]);m[PS]=m[FRAME+4] # supplied child clock advance only; no physics/callback effects
|
| 28 |
+
pc=54614
|
| 29 |
+
if len(times)>126:raise ValueError('step bound')
|
| 30 |
+
return {'old_word':old,'final_word':final,'fixed_word':fixed,'counter_word':counter,'ps_flags_word':flag,'accepted':t['stop']==54637,'clock_before_first_child':initial,'step_times':times,'step_up_bytes':up,'child_requests':requests,'result_ps_time':m[PS],'result_command_time':m[FRAME+4],'result_counter_word':m[PS+456]&0xffffffff,'result_up_byte':b[FRAME+27],'output_words':[m[FRAME+o]&0xffffffff for o in [48,252,256,260,264]],**trace_fields({'trace':trace},False)}
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def single_probe(ins,forward,right,buttons):
|
| 34 |
+
m={FRAME:PS,FRAME+20:buttons,FRAME+52:1,FRAME+208:2,FRAME+212:3,PS+104:0xa5a5a5a5,21232:63};b={FRAME+25:forward,FRAME+26:right,FRAME+27:128};t=trace_slice(ins,53419,53593,m,{104:FRAME},byte_memory=b)
|
| 35 |
+
return {'forward_byte':forward,'right_byte':right,'buttons_word':buttons,'result_buttons_word':t['memory'][FRAME+20]&0xffffffff,'result_ps_flags_word':t['memory'][PS+104]&0xffffffff,'result_movement_bytes':[t['byte_memory'][FRAME+o] for o in [25,26,27]],'global_frame_pointer':t['memory'][35024032],'result_global_counter':t['memory'][21232],'reset_words':[t['memory'][FRAME+o] for o in [52,208,212]],**trace_fields(t,False)}
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def suffix_probe(ins,health,buttons,forward,right,up,flags=0,events=(0,0,0),selector=0):
|
| 39 |
+
m={FRAME:PS,FRAME+20:buttons,35024032:FRAME,PS+208:health,PS+12:flags,PS+240:0x1234,PS+144:selector,PS+312+4*selector:0xa5f01234,FRAME+48:events[0]*7,FRAME+252:events[1],FRAME+256:11,FRAME+260:events[2],FRAME+264:13};b={FRAME+25:forward&255,FRAME+26:right&255,FRAME+27:up&255};t=trace_slice(ins,54637,54979,m,{72:FRAME},byte_memory=b,limit=1024)
|
| 40 |
+
return {'health_word':health,'buttons_word':buttons,'movement_bytes':[forward&255,right&255,up&255],'ps_flags_word':flags,'event_flags':events,'selector_word':selector,'result_movement_flags_word':t['memory'][PS+240]&0xffffffff,'result_buttons_cache_word':t['memory'][PS+212]&0xffffffff,'cached_packed_bytes':[t['memory'][LOCAL+32],t['memory'][LOCAL+36]] if events[2] else None,'calls':t['calls'],**trace_fields(t,False)}
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def main():
|
| 44 |
+
r=json.loads(Path('docs/research/qvm/qagame.json').read_text());data=verified(Path(r['source']['member']['output']).read_bytes(),r['sha256']);header,ins=decode(data);loops=[loop_probe(ins,*v) for v in itertools.product([1000],[999,1000,1001,1008,1066,1067,2000,2001],[0,1],[0,63,0xffffffff],[0,8192])];singles=[single_probe(ins,*v) for v in itertools.product([0,1,64,65,127,128,191,192,255],[0,1,64,65,127,128,191,192,255],[0,16,17,18])];suffixes=[suffix_probe(ins,*v) for v in itertools.product([-1,0,1],[0,16,256,272],[-1,0,1],[-1,0,1],[-1,0,1])]+[suffix_probe(ins,1,272,1,-1,-1,8192,e,s) for e,s in itertools.product([(0,0,0),(1,0,0),(0,1,0),(0,0,1),(1,1,1)],[0,7])];out={'source':r['source'],'sha256':r['sha256'],'outer_instructions':ins[54475:54981],'single_prefix_instructions':ins[53418:53593],'loop_probes':loops,'single_probes':singles,'suffix_probes':suffixes,'limitations':'Bounded synthetic shipped-QVM instruction slices only; original executable/native callees/full QVM/physics never execute. Scheduler child53418 requests recorded and supplied effect advances PS.time to requested command time only, preserving all other state except native outer-loop edits. Single prefix stops before later action/physics. Suffix helper29913 calls recorded with default return0 and omitted effects. Semantic flag/event names, valid clock domains, callback/physics/frame consumers and actual runtime/platform conformance remain open.'};write_json(Path('docs/research/qvm-movement-loop.json'),out);print('Recovered movement scheduler single-step input cleanup and post-loop outputs')
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
if __name__=='__main__':main()
|
scripts/test_qvm_movement_loop.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Independent bounded movement scheduling/input/output oracles."""
|
| 3 |
+
import hashlib
|
| 4 |
+
import json
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import unittest
|
| 7 |
+
from analyze_deferred_auth import trace_slice
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def s8(v):return v if v<128 else v-256
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class MovementLoopTests(unittest.TestCase):
|
| 14 |
+
@classmethod
|
| 15 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-movement-loop.json').read_text())
|
| 16 |
+
|
| 17 |
+
def test_backward_equal_cap_and_independent_substep_schedule(self):
|
| 18 |
+
self.assertEqual(len(self.r['loop_probes']),96)
|
| 19 |
+
for p in self.r['loop_probes']:
|
| 20 |
+
accepted=p['final_word']>=p['old_word'];self.assertEqual(p['accepted'],accepted);clock=max(p['old_word'],p['final_word']-1000) if accepted else p['old_word'];self.assertEqual(p['clock_before_first_child'],clock);times=[]
|
| 21 |
+
while accepted and clock<p['final_word']:clock+=min(p['final_word']-clock,8 if p['fixed_word'] else 66);times.append(clock)
|
| 22 |
+
self.assertEqual(p['step_times'],times);self.assertEqual(p['result_ps_time'],clock);self.assertEqual(p['result_command_time'],p['final_word']);self.assertEqual(p['child_requests'],[{'instruction':54612,'target':53418,'args':{'8':0x10000}}]*len(times))
|
| 23 |
+
|
| 24 |
+
def test_outer_counter_output_resets_and_post_step_up_edit(self):
|
| 25 |
+
for p in self.r['loop_probes']:
|
| 26 |
+
self.assertEqual(p['result_counter_word'],(p['counter_word']+1)&63 if p['accepted'] else p['counter_word']);self.assertEqual(p['output_words'],[0]*5 if p['accepted'] else [0x1234]*5);changed=bool(p['step_times']) and p['ps_flags_word']&8192!=0;self.assertEqual(p['result_up_byte'],20 if changed else 1);self.assertEqual(p['step_up_bytes'],[1]+[20 if changed else 1]*(len(p['step_times'])-1) if p['step_times'] else [])
|
| 27 |
+
|
| 28 |
+
def test_single_step_threshold_and_button_two_precedence(self):
|
| 29 |
+
self.assertEqual(len(self.r['single_probes']),324)
|
| 30 |
+
for p in self.r['single_probes']:
|
| 31 |
+
buttons=p['buttons_word'];large=abs(s8(p['forward_byte']))>64 or abs(s8(p['right_byte']))>64
|
| 32 |
+
if large:buttons&=~16
|
| 33 |
+
talking=bool(buttons&2);self.assertEqual(p['result_buttons_word'],2 if talking else buttons);self.assertEqual(p['result_ps_flags_word'],0xa5a5a5a5|524288 if talking else 0xa5a5a5a5&~524288);self.assertEqual(p['result_movement_bytes'],[0,0,0] if talking else [p['forward_byte'],p['right_byte'],128]);self.assertEqual(p['global_frame_pointer'],0x10000);self.assertEqual(p['result_global_counter'],64);self.assertEqual(p['reset_words'],[0]*3)
|
| 34 |
+
|
| 35 |
+
def test_post_loop_bit_oracle_health_gate_and_button_cache(self):
|
| 36 |
+
self.assertEqual(len(self.r['suffix_probes']),334)
|
| 37 |
+
for p in self.r['suffix_probes']:
|
| 38 |
+
f,r,u=map(s8,p['movement_bytes']);bits=0
|
| 39 |
+
if p['health_word']>0:bits=(64 if p['buttons_word']&16 else 0)|(32 if p['buttons_word']&256 else 0)|(128 if u<0 else 0)|(1 if p['ps_flags_word']&8192 else 0)|(2 if f>0 else 4 if f<0 else 0)|(8 if r<0 else 16 if r>0 else 0)
|
| 40 |
+
self.assertEqual(p['result_movement_flags_word'],bits);self.assertEqual(p['result_buttons_cache_word'],p['buttons_word'])
|
| 41 |
+
|
| 42 |
+
def test_post_loop_event_requests_and_sampled_packed_lookup(self):
|
| 43 |
+
for p in self.r['suffix_probes']:
|
| 44 |
+
expected=[]
|
| 45 |
+
for enabled,site,event,param in zip(p['event_flags'],[54879,54905,54977],[93,21,31],[7,11,13]):
|
| 46 |
+
if enabled:expected.append({'instruction':site,'target':29913,'args':{'8':event,'12':param,'16':0x20000}})
|
| 47 |
+
self.assertEqual(p['calls'],expected);self.assertEqual(p['cached_packed_bytes'],[0x34,0x12] if p['event_flags'][2] else None)
|
| 48 |
+
|
| 49 |
+
def test_source_hash_and_complete_retained_boundaries(self):
|
| 50 |
+
r=json.loads(Path('docs/research/qvm/qagame.json').read_text());raw=Path(r['source']['member']['output']).read_bytes();self.assertEqual(hashlib.sha256(raw).hexdigest(),self.r['sha256']);self.assertEqual(r['source'],self.r['source']);self.assertEqual(self.r['outer_instructions'][0]['index'],54475);self.assertEqual(self.r['outer_instructions'][-1]['index'],54980);self.assertEqual(self.r['single_prefix_instructions'][-1]['index'],53592)
|
| 51 |
+
|
| 52 |
+
def test_explicit_resume_preserves_supplied_local_state_and_bounds(self):
|
| 53 |
+
ins=[{'index':0,'opcode':'BREAK','operand':None},{'index':1,'opcode':'LOCAL','operand':8},{'index':2,'opcode':'LOAD4','operand':None}];t=trace_slice(ins,0,3,{}, {8:7},resume_at=1,capture_stack=True);self.assertEqual(t['trace'],[1,2]);self.assertEqual(t['stack'],[7])
|
| 54 |
+
with self.assertRaisesRegex(ValueError,'resume outside bounded slice'):trace_slice(ins,0,3,{}, {},resume_at=4)
|
| 55 |
+
with self.assertRaisesRegex(ValueError,'unsupported slice opcode'):trace_slice(ins,0,3,{}, {})
|
| 56 |
+
|
| 57 |
+
def test_supplied_child_scope_is_explicit_and_zero_delta_is_not_rejected(self):
|
| 58 |
+
self.assertIn('advances PS.time to requested command time only',self.r['limitations']);rows=[p for p in self.r['loop_probes'] if p['final_word']==p['old_word']];self.assertTrue(rows)
|
| 59 |
+
for p in rows:self.assertTrue(p['accepted']);self.assertEqual(p['step_times'],[]);self.assertEqual(p['output_words'],[0]*5)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
if __name__=='__main__':unittest.main()
|