nmarshall commited on
Commit
48c7db8
·
1 Parent(s): 3bae649

qvm: view motion

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -254,3 +254,5 @@ Research progress: [QVM movement-frame setup](research/QVM_MOVEMENT_FRAME.md) co
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.
255
 
256
  Research progress: [QVM child flags/clock/dispatch](research/QVM_STEP_STATE.md) completes bounded RE-421–423. RE-420/424 retain omitted child/callee/physics/native effects and complete gameplay/runtime/platform work; broad game/rendering milestones remain open.
 
 
 
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.
255
 
256
  Research progress: [QVM child flags/clock/dispatch](research/QVM_STEP_STATE.md) completes bounded RE-421–423. RE-420/424 retain omitted child/callee/physics/native effects and complete gameplay/runtime/platform work; broad game/rendering milestones remain open.
257
+
258
+ Research progress: [QVM view/basis/position tails](research/QVM_VIEW_MOTION.md) completes bounded RE-425–427. RE-424/428 retain native trig/callees/full mode physics/composition/runtime/platform work; broad game/rendering milestones remain open.
docs/FINDINGS.md CHANGED
@@ -492,3 +492,9 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
492
  **F-176 — Child clock prefix:** requested timestamp copies directly to player-state time while elapsed is separately clamped1–200, snapshotted vectors are copied and fractional binary32 time is computed. Selected schedulers now compose these actual prefix instructions, omitting all other child work and runtime effects. [STEP-002](research/QVM_STEP_STATE.md).
493
 
494
  **F-177 — Early mode dispatch:** signed direction updates a player-state bit before input suppression; frame/type/bit predicates choose three early call sequences, a call-free return or the main path. Callee effects/physics and semantic type names remain unverified. [STEP-003](research/QVM_STEP_STATE.md).
 
 
 
 
 
 
 
492
  **F-176 — Child clock prefix:** requested timestamp copies directly to player-state time while elapsed is separately clamped1–200, snapshotted vectors are copied and fractional binary32 time is computed. Selected schedulers now compose these actual prefix instructions, omitting all other child work and runtime effects. [STEP-002](research/QVM_STEP_STATE.md).
493
 
494
  **F-177 — Early mode dispatch:** signed direction updates a player-state bit before input suppression; frame/type/bit predicates choose three early call sequences, a call-free return or the main path. Callee effects/physics and semantic type names remain unverified. [STEP-003](research/QVM_STEP_STATE.md).
495
+
496
+ **F-178 — View-angle consumer:** helper53186 gates by type/flag/health-like fields, truncates command+delta to signed16, clamps pitch to[-18000,14000] before bias2000 and scale360/65536, and rewrites delta only on clamp. Extra keyed angle bit16 is discarded here; other consumers/legal domains remain open. [VIEW-001](research/QVM_VIEW_MOTION.md).
497
+
498
+ **F-179 — Conditional basis algebra:** child angle/trig requests and rounded nine-word basis construction match independent rotation/orthogonality oracles under supplied source angles/trig returns. Native trig/return ABI, view/helper50525 effects and real physics composition remain unverified. [VIEW-002](research/QVM_VIEW_MOTION.md).
499
+
500
+ **F-180 — Mode position tails:** helper42486 integrates supplied velocity only if any component magnitude exceeds1; helper45461's tail integrates unconditionally. Exact rounded position words and velocity retention match independent fixtures; complete modes/friction/acceleration/collision/runtime remain open. [VIEW-003](research/QVM_VIEW_MOTION.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -566,3 +566,5 @@ Research progress: [QVM movement-frame setup](research/QVM_MOVEMENT_FRAME.md) co
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.
567
 
568
  Research progress: [QVM child flags/clock/dispatch](research/QVM_STEP_STATE.md) completes bounded RE-421–423. RE-420/424 retain omitted child/callee/physics/native effects and complete gameplay/runtime/platform work; broad game/rendering milestones remain open.
 
 
 
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.
567
 
568
  Research progress: [QVM child flags/clock/dispatch](research/QVM_STEP_STATE.md) completes bounded RE-421–423. RE-420/424 retain omitted child/callee/physics/native effects and complete gameplay/runtime/platform work; broad game/rendering milestones remain open.
569
+
570
+ Research progress: [QVM view/basis/position tails](research/QVM_VIEW_MOTION.md) completes bounded RE-425–427. RE-424/428 retain native trig/callees/full mode physics/composition/runtime/platform work; broad game/rendering milestones remain open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -301,3 +301,9 @@ Validation: all preceding composition reports regenerated byte-identically, incl
301
  Completed bounded RE-421–423 with `analyze_qvm_step_state.py`: verified shipped qagame child53418 slices, 240 packed-byte/flag-order probes, eight clock-prefix probes, twenty evaluated-clock scheduler compositions and 105 early-mode dispatch probes. STEP-001–003/F-175–177 recover initial-bit256/packed-byte/button/type gating of bit512 and later independent bit256 release; requested-time copy distinct from clamped1–200 elapsed, vector snapshots and binary32 fractional time; and signed direction-bit/input suppression plus exact early-mode call ordering/main-path continuation. Selected schedulers now evaluate the child clock prefix instead of directly assigning time; all other child work/physics/native reset/callee effects remain omitted, with vector source words seeded explicitly. Shared evaluator adds signed-int32 CVIF; scheduler's optional child-effect interface preserves its old default/report. Eight independent tests verify bit-order and packed-byte conditions, actual clock/clamp/fraction/vector outputs, each composed scheduler prefix, directional flags and suppression, exact call sequences, verified source/scratch request, CVIF boundaries and explicit omitted-effect scope. No original executable/native callee/full QVM/physics executes. All 455 tests pass; storage/links/whitespace checks pass. RE-420 remains open; RE-424 tracks scratch/native trap effects, omitted angle/trigonometric/early-mode callees, real clock/vector mutations and remaining weapon/action/physics/collision, semantic selector/flag/type bounds, complete movement composition/gameplay/macOS/Linux and Rust/Bevy/Solari compatibility; packet/entity/player/snapshot/rendering milestones remain open.
302
 
303
  Validation: all preceding composition reports regenerated byte-identically, including shared-QVM-evaluator and scheduler-default users. The new step-state report reproduced twice byte-identically. Final suite: 455 passing tests. Storage policy/filter setup, documentation links and whitespace checks pass.
 
 
 
 
 
 
 
301
  Completed bounded RE-421–423 with `analyze_qvm_step_state.py`: verified shipped qagame child53418 slices, 240 packed-byte/flag-order probes, eight clock-prefix probes, twenty evaluated-clock scheduler compositions and 105 early-mode dispatch probes. STEP-001–003/F-175–177 recover initial-bit256/packed-byte/button/type gating of bit512 and later independent bit256 release; requested-time copy distinct from clamped1–200 elapsed, vector snapshots and binary32 fractional time; and signed direction-bit/input suppression plus exact early-mode call ordering/main-path continuation. Selected schedulers now evaluate the child clock prefix instead of directly assigning time; all other child work/physics/native reset/callee effects remain omitted, with vector source words seeded explicitly. Shared evaluator adds signed-int32 CVIF; scheduler's optional child-effect interface preserves its old default/report. Eight independent tests verify bit-order and packed-byte conditions, actual clock/clamp/fraction/vector outputs, each composed scheduler prefix, directional flags and suppression, exact call sequences, verified source/scratch request, CVIF boundaries and explicit omitted-effect scope. No original executable/native callee/full QVM/physics executes. All 455 tests pass; storage/links/whitespace checks pass. RE-420 remains open; RE-424 tracks scratch/native trap effects, omitted angle/trigonometric/early-mode callees, real clock/vector mutations and remaining weapon/action/physics/collision, semantic selector/flag/type bounds, complete movement composition/gameplay/macOS/Linux and Rust/Bevy/Solari compatibility; packet/entity/player/snapshot/rendering milestones remain open.
302
 
303
  Validation: all preceding composition reports regenerated byte-identically, including shared-QVM-evaluator and scheduler-default users. The new step-state report reproduced twice byte-identically. Final suite: 455 passing tests. Storage policy/filter setup, documentation links and whitespace checks pass.
304
+
305
+ ## 2026-10-15 — Game-QVM view conversion, basis algebra and mode position tails
306
+
307
+ Completed bounded RE-425–427 with `analyze_qvm_view_motion.py`: verified shipped qagame helper53186/basis/tail slices, 42 view probes, twelve supplied-trig basis probes and 48 mode-tail probes. VIEW-001–003/F-178–180 recover view state gates, signed16 command+delta conversion, pitch clamp/bias/delta rewrites and bit16 truncation in this consumer; conditional view-helper/trig requests and rounded nine-word rotation basis; and per-component velocity threshold versus unconditional rounded position integration. Independent formulas verify exact view/delta/basis/position words, orthogonality, requests and velocity retention. Trig returns/source angles and post-acceleration velocity/timestep are supplied; view conversion is evaluated separately and helper53186/50525 effects in the basis slice remain omitted. No full QVM/original executable/native callee/collision/physics executes. Shared evaluator adds explicit byte-backed halfwords/sign-extension and finite add/subtract/negate/comparison operations, retaining prior MULF behavior and rejecting unknown halfwords/nonfinite cases. General alias/coherence/control-state/runtime semantics remain open. Eight independent tests cover gates/clamps/bias/deltas/high-word truncation, trig/skip/argument behavior, exact rotation and orthogonality, threshold/tail integration/retention, source boundaries, halfword failure domains and finite operations. All 463 tests pass; storage/links/whitespace checks pass. RE-424 remains open; RE-428 tracks native trig ABI/services/helper50525, real view-to-child vector composition, complete mode friction/acceleration/collision, semantic type/coordinate/producer/key domains, exceptional/alias/control-state cases, main movement and full gameplay/macOS/Linux/Rust/Bevy/Solari compatibility; packet/entity/player/snapshot/rendering milestones remain open.
308
+
309
+ Validation: all preceding composition reports regenerated byte-identically, including shared-QVM-evaluator users. The new view-motion report reproduced twice byte-identically. Final suite: 463 passing tests. Storage policy/filter setup, documentation links and whitespace checks pass.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -512,3 +512,8 @@ Purpose: turn the supplied installation into an evidence-backed specification of
512
  - [x] RE-422 Recover actual child clock/elapsed/vector/fraction prefix and compose it into selected scheduler fixtures, replacing assumed clock updates while omitting other child work. [STEP-002](research/QVM_STEP_STATE.md).
513
  - [x] RE-423 Recover bounded direction-flag/input suppression and exact early-mode dispatch call sequences/main-path boundary. [STEP-003](research/QVM_STEP_STATE.md).
514
  - [ ] RE-424 Recover scratch/native trap effects, angle/trigonometric and early-mode callees, real clock/vector mutations and remaining weapon/action/physics/collision branches; confirm selector/flag/type semantics/bounds, compose complete steps/outer loop and validate gameplay/macOS/Linux before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
 
 
 
 
 
 
512
  - [x] RE-422 Recover actual child clock/elapsed/vector/fraction prefix and compose it into selected scheduler fixtures, replacing assumed clock updates while omitting other child work. [STEP-002](research/QVM_STEP_STATE.md).
513
  - [x] RE-423 Recover bounded direction-flag/input suppression and exact early-mode dispatch call sequences/main-path boundary. [STEP-003](research/QVM_STEP_STATE.md).
514
  - [ ] RE-424 Recover scratch/native trap effects, angle/trigonometric and early-mode callees, real clock/vector mutations and remaining weapon/action/physics/collision branches; confirm selector/flag/type semantics/bounds, compose complete steps/outer loop and validate gameplay/macOS/Linux before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
515
+
516
+ - [x] RE-425 Recover bounded complete helper53186 view gates, signed16 conversion, pitch clamp/bias and persistent delta rewrites; verify extra angle-word bit16 truncation in this consumer. [VIEW-001](research/QVM_VIEW_MOTION.md).
517
+ - [x] RE-426 Recover bounded child basis-vector algebra and helper/trig request gating with explicitly supplied trig returns and independent rotation/orthogonality checks. [VIEW-002](research/QVM_VIEW_MOTION.md).
518
+ - [x] RE-427 Recover bounded per-component-threshold and unconditional position-update tails of helpers42486/45461 using supplied post-acceleration vectors/timestep. [VIEW-003](research/QVM_VIEW_MOTION.md).
519
+ - [ ] RE-428 Recover native trig ABI/services/helper50525, real view-to-child vector composition, complete mode acceleration/friction/collision effects, field/type/coordinate meanings and angle producer/key domains; characterize alias/exceptional/control-state cases, main movement branches and full gameplay/macOS/Linux before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
docs/research/QVM_STEP_STATE.md CHANGED
@@ -69,3 +69,5 @@ All recorded calls have empty explicit argument slots here and rely on surroundi
69
  Eight independent tests cover gate ordering/packed bytes, clock writes versus clamped elapsed, evaluated-prefix scheduler composition, directional flags/suppression, exact early-dispatch requests, verified source/scratch-request evidence, CVIF boundaries and explicit model scope. Earlier reports are regenerated to protect shared-evaluator and scheduler defaults.
70
 
71
  RE-420 remains open for full child behavior and runtime work. RE-424 must recover scratch reset and native trap effects, omitted angle/trigonometric helpers, early-dispatch callee semantics and real clock/vector mutations, remaining action/weapon/physics/collision branches, packed selectors/flags/types through producers and consumers, complete step/outer-loop composition and actual gameplay/macOS/Linux behavior before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot/rendering work also remains open.
 
 
 
69
  Eight independent tests cover gate ordering/packed bytes, clock writes versus clamped elapsed, evaluated-prefix scheduler composition, directional flags/suppression, exact early-dispatch requests, verified source/scratch-request evidence, CVIF boundaries and explicit model scope. Earlier reports are regenerated to protect shared-evaluator and scheduler defaults.
70
 
71
  RE-420 remains open for full child behavior and runtime work. RE-424 must recover scratch reset and native trap effects, omitted angle/trigonometric helpers, early-dispatch callee semantics and real clock/vector mutations, remaining action/weapon/physics/collision branches, packed selectors/flags/types through producers and consumers, complete step/outer-loop composition and actual gameplay/macOS/Linux behavior before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot/rendering work also remains open.
72
+
73
+ Follow-up: [view and position tails](QVM_VIEW_MOTION.md) recovers helper53186 angle conversion, conditional basis algebra and two position tails. Native trig, omitted callee/physics effects and real composition/runtime validation remain open.
docs/research/QVM_VIEW_MOTION.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Game-QVM view angles, basis vectors and position tails
2
+
3
+ RE-425–427 / F-178–180. [qvm-view-motion.json](qvm-view-motion.json) retains shipped qagame instruction evidence, 42 view-helper probes, twelve supplied-trig basis probes and 48 position-tail probes. Reproduce with `python3 scripts/analyze_qvm_view_motion.py`; verify with `python3 scripts/test_qvm_view_motion.py`.
4
+
5
+ This follows [child clock and dispatch](QVM_STEP_STATE.md). All results are bounded synthetic instruction evaluations. No full QVM, original executable, native callee or collision/physics system executes. View conversion is evaluated separately. The basis slice records the view-helper call without effects and uses supplied source angles and trig return words. Position tails use supplied post-acceleration velocity and timestep; preceding friction/acceleration/collision/callback effects remain omitted.
6
+
7
+ ## VIEW-001 — Complete view helper53186 (RE-425)
8
+
9
+ The helper rejects type5 immediately. Other types proceed when type2, player-state+220 bit2048, or player-state+208 >0; otherwise they return without angle/delta mutations.
10
+
11
+ For each of three axes it adds the command angle at command+4/+8/+12 to player-state delta word+56/+60/+64, stores the low16 bits in a local halfword, then sign-extends that halfword. Thus extra high word bits, including the extra keyed-decoder bit16 described in [CMD-002](MESSAGE_USERCMD.md), are discarded **in this consumer**. This does not close other angle-word consumers, buttons or legal key-domain work.
12
+
13
+ Axis0 has a separate pitch-like policy:
14
+
15
+ 1. Clamp the signed halfword to[-18000,14000].
16
+ 2. On a clamp, rewrite its delta word to signed32(clamp_boundary-original_command_angle). The other delta words are retained.
17
+ 3. Add2000, storing again through the local halfword.
18
+ 4. Convert the resulting signed integer to binary32, multiply by exact binary32 word1001652224 (360/65536), and store at player-state+152.
19
+
20
+ Axes1/2 skip clamp/bias and store their signed16 scaled results at+156/+160. Pitch output after clamp/bias ranges from-87.890625 to87.890625 in the observed degree-like scale. The bias does not independently add2000 to the persistent delta word.
21
+
22
+ 42 probes cover type/flag/health-like gates; values around both clamp boundaries; signed16 wrap; and command words65536/65537 with supplied deltas0/100. Tests independently predict exact float words, unchanged outputs on rejection and full-word delta rewrites. No input-angle producer, exceptional arithmetic, overlapping pointers or actual runtime fixture is established here.
23
+
24
+ ## VIEW-002 — Basis construction with supplied trig returns (RE-426)
25
+
26
+ The child slice53789–54022 records helper53186(player-state,frame+4) only when frame+240 ==0. It then reads player-state view words+156/+152/+160 in yaw/pitch/roll order, multiplies each by binary32 word1016003125, and records targets-104/-105 for paired trig returns. Six return words per probe are supplied by an ordinary sin/cos model after binary32 radian scaling; native trap implementations, argument reduction, error cases and floating-control behavior do not execute.
27
+
28
+ The evaluated multiply/add/negate sequence constructs nine words at35023892 through35023924: three candidate forward/right/up basis vectors. Supplied-angle fixtures0/90-degree axes, a mixed30/45/60 case and negative/180-degree values exercise signs and rounding. The final helper50525 request is recorded without effects.
29
+
30
+ Twelve probes (six angle triples, skip flag0/1) verify call gating, trig argument words, exact basis words from independent rotation formulas with binary32 operation rounding, vector lengths and orthogonality. These checks establish conditional algebra and data flow; they do not validate the native trig ABI/return convention, helper50525 effects, coordinate conventions for the complete game or a composed view-to-physics pipeline.
31
+
32
+ ## VIEW-003 — Two mode-specific position-update tails (RE-427)
33
+
34
+ Helper42486's tail42671–42836 examines absolute values of three supplied velocity words at player-state+32/+36/+40. If all are <=1, it returns without integrating position. If any is >1, it updates all three position words+20/+24/+28 as binary32(position + binary32(velocity * global35023928)). The threshold is per component, not vector magnitude. Equal1/-1 does not trigger integration.
35
+
36
+ Helper45461's tail45813–45885 performs the same three update expressions without that threshold. Neither retained tail changes velocity or calls a helper. This observation does not characterize earlier acceleration/friction or prove that either complete mode lacks collisions or other effects.
37
+
38
+ 48 probes cover zero, sub-unit, exact +/-1, just-over-threshold components on each axis and mixed velocities, with supplied fractional times0/0.016/1. Independent rounded arithmetic predicts every position word and verifies velocity retention and empty call lists.
39
+
40
+ ## Evaluator boundaries and validation
41
+
42
+ The QVM slice model adds explicit byte-backed STORE2/LOAD2/SEX16 for local halfwords, finite ADDF/SUBF/NEGF and finite float comparisons. Unknown halfword reads or stores without explicit byte memory fail; general word/halfword alias coherence remains unmodeled. Nonfinite arithmetic/comparison inputs fail closed rather than inventing native NaN/infinity behavior. Earlier finite MULF behavior/output is retained.
43
+
44
+ Eight tests independently verify view gates/clamps/bias/deltas/word-bit truncation, trig request/skip behavior, rounded basis algebra and orthogonality, per-component threshold/unconditional position tails, source/boundary evidence, halfword operations/failure domains and finite arithmetic rejection. Previous report regeneration checks protect shared evaluator users.
45
+
46
+ RE-424 remains open for full mode/callee/runtime work. RE-428 must recover native trig services/return ABI and helper50525, compose view updates with real child vectors, trace complete mode friction/acceleration and collision effects, verify field/type/coordinate meanings and angle producer/key domains, characterize alias/exceptional/control-state cases, continue main movement branches and validate full gameplay/macOS/Linux before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot/rendering milestones remain open.
docs/research/README.md CHANGED
@@ -167,3 +167,5 @@ The extension collector adds five focused tests for native tail calls, command-r
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.
168
 
169
  [QVM child flags, clock and early dispatch](QVM_STEP_STATE.md) adds RE-421–423. Reproduce with `python3 scripts/analyze_qvm_step_state.py`; check with `python3 scripts/test_qvm_step_state.py`. Only the decoded clock prefix is composed into selected schedulers; physics and other child effects remain open.
 
 
 
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.
168
 
169
  [QVM child flags, clock and early dispatch](QVM_STEP_STATE.md) adds RE-421–423. Reproduce with `python3 scripts/analyze_qvm_step_state.py`; check with `python3 scripts/test_qvm_step_state.py`. Only the decoded clock prefix is composed into selected schedulers; physics and other child effects remain open.
170
+
171
+ [QVM view angles, supplied-trig basis and position tails](QVM_VIEW_MOTION.md) adds RE-425–427. Reproduce with `python3 scripts/analyze_qvm_view_motion.py`; check with `python3 scripts/test_qvm_view_motion.py`. Trig returns and post-acceleration vectors are supplied; full mode/native/runtime effects remain open.
docs/research/qvm-view-motion.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_deferred_auth.py CHANGED
@@ -80,18 +80,46 @@ def trace_slice(instructions, start, stop, memory, locals_map, limit=256, byte_m
80
  elif op in ('BAND', 'BOR'):
81
  right, left = stack.pop(), stack.pop()
82
  stack.append(signed32(left & right if op == 'BAND' else left | right))
83
- elif op == 'MULF':
84
  right, left = stack.pop(), stack.pop()
85
  a, b = [struct.unpack('<f', struct.pack('<I', v & 0xffffffff))[0] for v in (left, right)]
 
86
  if not math.isfinite(a) or not math.isfinite(b):
87
- raise ValueError('nonfinite bounded float multiply')
88
  try:
89
- result = struct.pack('<f', a*b)
90
  except OverflowError as exc:
91
- raise ValueError('overflow bounded float multiply') from exc
92
  if not math.isfinite(struct.unpack('<f', result)[0]):
93
  raise ValueError('nonfinite bounded float result')
94
  stack.append(signed32(struct.unpack('<I', result)[0]))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  elif op == 'CVIF':
96
  value = signed32(stack.pop())
97
  stack.append(signed32(struct.unpack('<I', struct.pack('<f', value))[0]))
 
80
  elif op in ('BAND', 'BOR'):
81
  right, left = stack.pop(), stack.pop()
82
  stack.append(signed32(left & right if op == 'BAND' else left | right))
83
+ elif op in ('MULF', 'ADDF', 'SUBF'):
84
  right, left = stack.pop(), stack.pop()
85
  a, b = [struct.unpack('<f', struct.pack('<I', v & 0xffffffff))[0] for v in (left, right)]
86
+ label = {'MULF':'multiply', 'ADDF':'add', 'SUBF':'subtract'}[op]
87
  if not math.isfinite(a) or not math.isfinite(b):
88
+ raise ValueError('nonfinite bounded float '+label)
89
  try:
90
+ result = struct.pack('<f', a*b if op == 'MULF' else a+b if op == 'ADDF' else a-b)
91
  except OverflowError as exc:
92
+ raise ValueError('overflow bounded float '+label) from exc
93
  if not math.isfinite(struct.unpack('<f', result)[0]):
94
  raise ValueError('nonfinite bounded float result')
95
  stack.append(signed32(struct.unpack('<I', result)[0]))
96
+ elif op == 'NEGF':
97
+ word = stack.pop() & 0xffffffff
98
+ if not math.isfinite(struct.unpack('<f', struct.pack('<I', word))[0]):
99
+ raise ValueError('nonfinite bounded float negate')
100
+ stack.append(signed32(word ^ 0x80000000))
101
+ elif op == 'STORE2':
102
+ value, address = stack.pop(), stack.pop()
103
+ if bytes_map is None:
104
+ raise ValueError('halfword store requires byte memory')
105
+ for offset in range(2):
106
+ bytes_map[address+offset] = (value >> (8*offset)) & 255
107
+ byte_writes.append({'address': address+offset, 'value': bytes_map[address+offset]})
108
+ elif op == 'LOAD2':
109
+ address = stack.pop()
110
+ if bytes_map is None or any(address+offset not in bytes_map for offset in range(2)):
111
+ raise ValueError('unknown synthetic halfword read')
112
+ stack.append(bytes_map[address] | (bytes_map[address+1] << 8))
113
+ elif op == 'SEX16':
114
+ value = stack.pop() & 65535
115
+ stack.append(value-65536 if value & 32768 else value)
116
+ elif op in ('EQF', 'NEF', 'LTF', 'LEF', 'GTF', 'GEF'):
117
+ right, left = [struct.unpack('<f', struct.pack('<I', stack.pop() & 0xffffffff))[0] for _ in range(2)]
118
+ if not math.isfinite(left) or not math.isfinite(right):
119
+ raise ValueError('nonfinite bounded float comparison')
120
+ condition = {'EQF':left==right, 'NEF':left!=right, 'LTF':left<right, 'LEF':left<=right, 'GTF':left>right, 'GEF':left>=right}[op]
121
+ if condition:
122
+ pc = value
123
  elif op == 'CVIF':
124
  value = signed32(stack.pop())
125
  stack.append(signed32(struct.unpack('<I', struct.pack('<f', value))[0]))
scripts/analyze_qvm_view_motion.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Bounded view angles, supplied-trig basis and mode position tails."""
3
+ import itertools
4
+ import json
5
+ import math
6
+ import struct
7
+ from pathlib import Path
8
+ from analyze_bsp_references import trace_fields
9
+ from analyze_deferred_auth import trace_slice
10
+ from analyze_qvm import decode,write_json
11
+ from analyze_vm_extensions import verified
12
+
13
+ PS=0x10000
14
+ CMD=0x20000
15
+ FRAME=0x30000
16
+
17
+
18
+ def word(v):return struct.unpack('<I',struct.pack('<f',v))[0]
19
+ def number(v):return struct.unpack('<f',struct.pack('<I',v&0xffffffff))[0]
20
+ def rounded(v):return number(word(v))
21
+
22
+
23
+ def view_probe(ins,kind,flags,health,commands,deltas):
24
+ m={PS+4:kind,PS+220:flags,PS+208:health}
25
+ for i in range(3):m[CMD+4+4*i]=commands[i];m[PS+56+4*i]=deltas[i];m[PS+152+4*i]=0x3f000000+i
26
+ t=trace_slice(ins,53187,53350,m,{36:PS,40:CMD},byte_memory={},limit=512)
27
+ return {'type_word':kind,'flags_word':flags,'health_word':health,'command_words':commands,'delta_words':deltas,'result_delta_words':[t['memory'][PS+56+i*4]&0xffffffff for i in range(3)],'view_float_words':[t['memory'][PS+152+i*4]&0xffffffff for i in range(3)],**trace_fields(t,False)}
28
+
29
+
30
+ def basis_probe(ins,angles,skip):
31
+ # Trig return words are supplied from a separate ordinary-math model. Native
32
+ # trap implementations, argument reduction and exceptional cases do not run.
33
+ m={35024032:FRAME,FRAME:PS,FRAME+240:skip};returns={};factor=number(1016003125)
34
+ for i,a in enumerate(angles):m[PS+152+i*4]=word(a)
35
+ for axis,sin_site,cos_site in [(1,53827,53838),(0,53859,53870),(2,53891,53902)]:
36
+ radians=rounded(number(m[PS+152+axis*4])*factor);returns[sin_site]=word(math.sin(radians));returns[cos_site]=word(math.cos(radians))
37
+ t=trace_slice(ins,53789,54022,m,{104:FRAME},call_results=returns,limit=512)
38
+ return {'angle_float_words':[word(a) for a in angles],'skip_word':skip,'supplied_trig_return_words':returns,'calls':t['calls'],'basis_float_words':[t['memory'][35023892+i*4]&0xffffffff for i in range(9)],**trace_fields(t,False)}
39
+
40
+
41
+ def tail_probe(ins,mode,velocity,dt):
42
+ m={35024032:FRAME,FRAME:PS,35023928:word(dt)}
43
+ for i,v in enumerate([10.0,20.0,30.0]):m[PS+20+i*4]=word(v);m[PS+32+i*4]=word(velocity[i])
44
+ start,end=(42671,42836) if mode=='threshold' else (45813,45885);t=trace_slice(ins,start,end,m,{},limit=512)
45
+ return {'mode':mode,'velocity_float_words':[word(v) for v in velocity],'dt_float_word':word(dt),'origin_float_words':[t['memory'][PS+20+i*4]&0xffffffff for i in range(3)],'result_velocity_float_words':[t['memory'][PS+32+i*4]&0xffffffff for i in range(3)],'calls':t['calls'],**trace_fields(t,False)}
46
+
47
+
48
+ def main():
49
+ r=json.loads(Path('docs/research/qvm/qagame.json').read_text());data=verified(Path(r['source']['member']['output']).read_bytes(),r['sha256']);_,ins=decode(data)
50
+ views=[view_probe(ins,k,f,h,[0,65535,32768],[0,1,-1]) for k,f,h in itertools.product([0,2,5],[0,2048],[-1,0,1])]+[view_probe(ins,0,0,1,[p,65535,32768],[d,1,-1]) for p,d in itertools.product([0,13999,14000,14001,-17999,-18000,-18001,32767,32768,65535,65536,0x10001],[0,100])]
51
+ bases=[basis_probe(ins,a,s) for a,s in itertools.product([(0,0,0),(90,0,0),(0,90,0),(0,0,90),(30,45,60),(-45,-90,180)],[0,1])];velocities=[(0,0,0),(.5,0,0),(1,1,-1),(1.001,0,0),(-1.001,0,0),(0,1.001,0),(0,0,-1.001),(3,-4,5)];tails=[tail_probe(ins,*v) for v in itertools.product(['threshold','unconditional'],velocities,[0.0,.016,1.0])]
52
+ out={'source':r['source'],'sha256':r['sha256'],'view_instructions':ins[53186:53352],'basis_instructions':ins[53789:54022],'threshold_tail_instructions':ins[42671:42838],'unconditional_tail_instructions':ins[45813:45887],'view_probes':views,'basis_probes':bases,'tail_probes':tails,'limitations':'Bounded synthetic shipped-QVM instruction slices only; no full QVM/original runtime/native callee/physics/collision executes. View helper evaluated separately; basis helper53186 call only recorded with omitted effects and source angles supplied. Trig return words supplied by ordinary-math model, not native traps. Position tails use supplied post-acceleration velocity/dt and omit all earlier mode helper effects. Finite float model only; halfword locals require explicit byte memory, no general word/byte coherence guarantee. Flag/type/vector semantic names, exceptional/control-state/alias/callback/runtime/platform contracts remain open.'};write_json(Path('docs/research/qvm-view-motion.json'),out);print('Recovered view-angle conversion supplied-trig basis and position tails')
53
+
54
+
55
+ if __name__=='__main__':main()
scripts/test_qvm_view_motion.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Independent view, rotation and finite position-tail oracles."""
3
+ import hashlib
4
+ import json
5
+ import math
6
+ from pathlib import Path
7
+ import struct
8
+ import unittest
9
+ from analyze_deferred_auth import trace_slice
10
+
11
+
12
+ def word(v):return struct.unpack('<I',struct.pack('<f',v))[0]
13
+ def number(v):return struct.unpack('<f',struct.pack('<I',v&0xffffffff))[0]
14
+ def f(v):return number(word(v))
15
+ def m(a,b):return f(a*b)
16
+ def a(x,y):return f(x+y)
17
+ def s16(v):
18
+ v&=65535;return v-65536 if v&32768 else v
19
+
20
+
21
+ class QvmViewMotionTests(unittest.TestCase):
22
+ @classmethod
23
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-view-motion.json').read_text())
24
+
25
+ def test_view_gates_signed_halfwords_pitch_bias_and_delta_rewrite(self):
26
+ self.assertEqual(len(self.r['view_probes']),42)
27
+ for p in self.r['view_probes']:
28
+ active=p['type_word']!=5 and (p['type_word']==2 or p['flags_word']&2048 or p['health_word']>0);deltas=[d&0xffffffff for d in p['delta_words']];views=[0x3f000000+i for i in range(3)]
29
+ if active:
30
+ for i,(cmd,delta) in enumerate(zip(p['command_words'],p['delta_words'])):
31
+ v=s16(cmd+delta)
32
+ if i==0:
33
+ clamped=max(-18000,min(14000,v))
34
+ if clamped!=v:deltas[i]=(clamped-cmd)&0xffffffff
35
+ v=s16(clamped+2000)
36
+ views[i]=word(f(v*number(1001652224)))
37
+ self.assertEqual(p['result_delta_words'],deltas);self.assertEqual(p['view_float_words'],views)
38
+
39
+ def test_extra_angle_word_bit16_is_discarded_here(self):
40
+ rows=[p for p in self.r['view_probes'] if p['type_word']==0 and p['health_word']==1 and p['flags_word']==0 and p['delta_words'][0]==0];x=next(p for p in rows if p['command_words'][0]==0x10001);self.assertEqual(number(x['view_float_words'][0]),f(2001*360/65536))
41
+ hi=next(p for p in rows if p['command_words'][0]==14001);self.assertEqual(hi['result_delta_words'][0],0xffffffff);self.assertEqual(number(hi['view_float_words'][0]),16000*360/65536)
42
+
43
+ def test_supplied_trig_requests_skip_gate_and_parameter_words(self):
44
+ self.assertEqual(len(self.r['basis_probes']),12)
45
+ for p in self.r['basis_probes']:
46
+ calls=p['calls'];self.assertEqual([c['target'] for c in calls],([] if p['skip_word'] else [53186])+[-104,-105,-104,-105,-104,-105,50525]);trigs=[c for c in calls if c['target'] in [-104,-105]]
47
+ for c,axis in zip(trigs,[1,1,0,0,2,2]):self.assertEqual(c['args']['8']&0xffffffff,word(m(number(p['angle_float_words'][axis]),number(1016003125))))
48
+ if not p['skip_word']:self.assertEqual(calls[0]['args'],{'8':0x10000,'12':0x30004})
49
+
50
+ def test_basis_matches_rotation_formula_and_orthogonality(self):
51
+ for p in self.r['basis_probes']:
52
+ t=p['supplied_trig_return_words'];sy,cy,sp,cp,sr,cr=[number(t[str(k)]) for k in [53827,53838,53859,53870,53891,53902]];expected=[m(cp,cy),m(cp,sy),-sp,a(m(m(m(sr,-1),sp),cy),m(m(cr,-1),-sy)),a(m(m(m(sr,-1),sp),sy),m(m(cr,-1),cy)),m(m(sr,-1),cp),a(m(m(cr,sp),cy),m(-sr,-sy)),a(m(m(cr,sp),sy),m(-sr,cy)),m(cr,cp)];self.assertEqual(p['basis_float_words'],[word(v) for v in expected]);vec=[list(map(number,p['basis_float_words'][i:i+3])) for i in [0,3,6]]
53
+ for v in vec:self.assertAlmostEqual(sum(x*x for x in v),1,places=5)
54
+ for i,j in [(0,1),(0,2),(1,2)]:self.assertAlmostEqual(sum(x*y for x,y in zip(vec[i],vec[j])),0,places=5)
55
+
56
+ def test_mode_tail_component_threshold_and_unconditional_integration(self):
57
+ self.assertEqual(len(self.r['tail_probes']),48)
58
+ for p in self.r['tail_probes']:
59
+ vel=list(map(number,p['velocity_float_words']));dt=number(p['dt_float_word']);integrate=p['mode']=='unconditional' or any(abs(v)>1 for v in vel);expected=[word(a(pos,m(v,dt))) if integrate else word(pos) for pos,v in zip([10,20,30],vel)];self.assertEqual(p['origin_float_words'],expected);self.assertEqual(p['result_velocity_float_words'],p['velocity_float_words']);self.assertEqual(p['calls'],[])
60
+
61
+ def test_source_and_retained_boundaries(self):
62
+ 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['view_instructions'][0]['index'],53186);self.assertEqual(self.r['basis_instructions'][-2]['opcode'],'CALL')
63
+
64
+ def test_halfword_storage_sign_extension_and_fail_closed_reads(self):
65
+ rows=[{'index':0,'opcode':'CONST','operand':0x1000},{'index':1,'opcode':'CONST','operand':0x12348000},{'index':2,'opcode':'STORE2','operand':None},{'index':3,'opcode':'CONST','operand':0x1000},{'index':4,'opcode':'LOAD2','operand':None},{'index':5,'opcode':'SEX16','operand':None}];t=trace_slice(rows,0,6,{}, {},byte_memory={},capture_stack=True);self.assertEqual(t['stack'],[-32768]);self.assertEqual(t['byte_memory'],{0x1000:0,0x1001:128})
66
+ with self.assertRaisesRegex(ValueError,'requires byte memory'):trace_slice(rows,0,3,{}, {})
67
+ with self.assertRaisesRegex(ValueError,'unknown synthetic halfword read'):trace_slice(rows,3,5,{}, {},byte_memory={})
68
+
69
+ def test_finite_add_negate_compare_and_nonfinite_rejection(self):
70
+ rows=[{'index':0,'opcode':'CONST','operand':word(1.5)},{'index':1,'opcode':'CONST','operand':word(-.5)},{'index':2,'opcode':'ADDF','operand':None},{'index':3,'opcode':'NEGF','operand':None}];self.assertEqual(trace_slice(rows,0,4,{}, {},capture_stack=True)['stack'][0]&0xffffffff,word(-1))
71
+ rows[0]['operand']=0x7f800000
72
+ with self.assertRaisesRegex(ValueError,'nonfinite bounded float add'):trace_slice(rows,0,4,{}, {})
73
+ cmp=[{'index':0,'opcode':'CONST','operand':0x7fc00000},{'index':1,'opcode':'CONST','operand':0},{'index':2,'opcode':'GEF','operand':3}]
74
+ with self.assertRaisesRegex(ValueError,'nonfinite bounded float comparison'):trace_slice(cmp,0,3,{}, {})
75
+
76
+
77
+ if __name__=='__main__':unittest.main()