nmarshall commited on
Commit
537a3ef
·
1 Parent(s): ae4ba74

qvm: friction

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -258,3 +258,5 @@ Research progress: [QVM child flags/clock/dispatch](research/QVM_STEP_STATE.md)
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.
259
 
260
  Research progress: [QVM command scale/acceleration](research/QVM_ACCELERATION.md) completes bounded RE-429–431. RE-428/432 retain native sqrt/friction/producers/legal ranges/full mode/collision/runtime/platform work; broad game/rendering milestones remain open.
 
 
 
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.
259
 
260
  Research progress: [QVM command scale/acceleration](research/QVM_ACCELERATION.md) completes bounded RE-429–431. RE-428/432 retain native sqrt/friction/producers/legal ranges/full mode/collision/runtime/platform work; broad game/rendering milestones remain open.
261
+
262
+ Research progress: [QVM friction policies](research/QVM_FRICTION.md) completes bounded RE-433–435. RE-432/436 retain live coefficients/producers/native sqrt/full mode-state/collision/runtime/platform work; broad game/rendering milestones remain open.
docs/FINDINGS.md CHANGED
@@ -504,3 +504,9 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
504
  **F-182 — Acceleration policy:** helper40275 computes projection gap, returns unchanged for gap<=0 and otherwise adds a direction-scaled coefficient/timestep/wish-speed amount capped to the gap. It does not clamp total speed; full surrounding friction/collision/runtime policy remains open. [ACCEL-002](research/QVM_ACCELERATION.md).
505
 
506
  **F-183 — Partial mode composition:** 48 supplied-basis/post-friction fixtures compose decoded command-scale, caller normalization, acceleration and position-tail instructions with exact word/request oracles and disjoint synthetic local frames. Earlier mode/native sqrt/collision/full-runtime effects remain omitted. [ACCEL-003](research/QVM_ACCELERATION.md).
 
 
 
 
 
 
 
504
  **F-182 — Acceleration policy:** helper40275 computes projection gap, returns unchanged for gap<=0 and otherwise adds a direction-scaled coefficient/timestep/wish-speed amount capped to the gap. It does not clamp total speed; full surrounding friction/collision/runtime policy remains open. [ACCEL-002](research/QVM_ACCELERATION.md).
505
 
506
  **F-183 — Partial mode composition:** 48 supplied-basis/post-friction fixtures compose decoded command-scale, caller normalization, acceleration and position-tail instructions with exact word/request oracles and disjoint synthetic local frames. Earlier mode/native sqrt/collision/full-runtime effects remain omitted. [ACCEL-003](research/QVM_ACCELERATION.md).
507
+
508
+ **F-184 — Shared friction:** helper39988 optionally measures planar speed, retains Z on low-speed X/Y clear, adds gated drop terms and then conditionally replaces total drop before scaling the original XYZ velocity. File-backed initial coefficients are verified; live writers and semantic fields remain open. [FRIC-001](research/QVM_FRICTION.md).
509
+
510
+ **F-185 — Mode friction differs:** helper45461's prefix measures full XYZ, clears all velocity below1, uses1.5 times the source friction word and writes player-state+164=38. This is a separate policy from helper39988; configuration/type meanings and runtime remain unverified. [FRIC-002](research/QVM_FRICTION.md).
511
+
512
+ **F-186 — Staged velocity composition:** eighteen fixtures pass evaluated friction velocity into prior scale/acceleration/position slices with the source-image acceleration word8. Earlier example coefficient10/default output remains unchanged. Only velocity is bridged; full state/callee/collision/runtime effects remain omitted. [FRIC-003](research/QVM_FRICTION.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -570,3 +570,5 @@ Research progress: [QVM child flags/clock/dispatch](research/QVM_STEP_STATE.md)
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.
571
 
572
  Research progress: [QVM command scale/acceleration](research/QVM_ACCELERATION.md) completes bounded RE-429–431. RE-428/432 retain native sqrt/friction/producers/legal ranges/full mode/collision/runtime/platform work; broad game/rendering milestones remain open.
 
 
 
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.
571
 
572
  Research progress: [QVM command scale/acceleration](research/QVM_ACCELERATION.md) completes bounded RE-429–431. RE-428/432 retain native sqrt/friction/producers/legal ranges/full mode/collision/runtime/platform work; broad game/rendering milestones remain open.
573
+
574
+ Research progress: [QVM friction policies](research/QVM_FRICTION.md) completes bounded RE-433–435. RE-432/436 retain live coefficients/producers/native sqrt/full mode-state/collision/runtime/platform work; broad game/rendering milestones remain open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -313,3 +313,9 @@ Validation: all preceding composition reports regenerated byte-identically, incl
313
  Completed bounded RE-429–431 with `analyze_qvm_acceleration.py`: verified shipped qagame helpers40397/40275 and selected helper45461 caller, 256 command-scale probes, 324 acceleration probes and 48 partial caller compositions. ACCEL-001–003/F-181–183 recover literal220/340 speed gating, signed-byte maximum/norm scale and zero bypass; rounded projection/gap/capped-amount velocity updates without total-speed clamping; and evaluated scale/normalization/acceleration/position data flow with supplied identity basis/post-friction velocity. Square-root returns are supplied from ordinary finite math rather than native trap106; prior mode/friction/collision/callback effects remain omitted and no full QVM/original executable/native callee/physics system executes. Explicit disjoint synthetic caller/scale/acceleration frames prevent local-word overwrites. Shared evaluator adds finite DIVF with zero/nonfinite rejection and configurable local base with unchanged default. Eight independent tests verify exact scale/sqrt requests/strict thresholds, full projection/gap/amount/velocity, above-target retention, partial caller direction/speed/velocity/position/requests, source partitions, frame isolation and division failure domains. All 471 tests pass; storage/links/whitespace checks pass. RE-428 remains open; RE-432 tracks friction/surrounding velocity policy, native sqrt ABI, input/button/meter/coefficient/timestep producers/legal ranges, real basis/frame/callee/collision effects and complete mode/step gameplay/macOS/Linux/Rust/Bevy/Solari compatibility; packet/entity/player/snapshot/rendering milestones remain open.
314
 
315
  Validation: all preceding composition reports regenerated byte-identically, including shared-QVM-evaluator users. The new acceleration report reproduced twice byte-identically. Final suite: 471 passing tests. Storage policy/filter setup, documentation links and whitespace checks pass.
 
 
 
 
 
 
 
313
  Completed bounded RE-429–431 with `analyze_qvm_acceleration.py`: verified shipped qagame helpers40397/40275 and selected helper45461 caller, 256 command-scale probes, 324 acceleration probes and 48 partial caller compositions. ACCEL-001–003/F-181–183 recover literal220/340 speed gating, signed-byte maximum/norm scale and zero bypass; rounded projection/gap/capped-amount velocity updates without total-speed clamping; and evaluated scale/normalization/acceleration/position data flow with supplied identity basis/post-friction velocity. Square-root returns are supplied from ordinary finite math rather than native trap106; prior mode/friction/collision/callback effects remain omitted and no full QVM/original executable/native callee/physics system executes. Explicit disjoint synthetic caller/scale/acceleration frames prevent local-word overwrites. Shared evaluator adds finite DIVF with zero/nonfinite rejection and configurable local base with unchanged default. Eight independent tests verify exact scale/sqrt requests/strict thresholds, full projection/gap/amount/velocity, above-target retention, partial caller direction/speed/velocity/position/requests, source partitions, frame isolation and division failure domains. All 471 tests pass; storage/links/whitespace checks pass. RE-428 remains open; RE-432 tracks friction/surrounding velocity policy, native sqrt ABI, input/button/meter/coefficient/timestep producers/legal ranges, real basis/frame/callee/collision effects and complete mode/step gameplay/macOS/Linux/Rust/Bevy/Solari compatibility; packet/entity/player/snapshot/rendering milestones remain open.
314
 
315
  Validation: all preceding composition reports regenerated byte-identically, including shared-QVM-evaluator users. The new acceleration report reproduced twice byte-identically. Final suite: 471 passing tests. Storage policy/filter setup, documentation links and whitespace checks pass.
316
+
317
+ ## 2026-10-15 — Game-QVM shared/mode friction and velocity bridge
318
+
319
+ Completed bounded RE-433–435 with `analyze_qvm_friction.py`: verified shipped qagame helper39988/helper45461 prefix, nine file-backed initialized words, 342 shared-friction probes, 24 mode-prefix probes and eighteen velocity-bridge compositions. FRIC-001–003/F-184–186 recover optional planar norm, low-speed X/Y clear with Z retention, gated additive drop terms followed by conditional replacement, all-component ratio scaling, and the separate mode's full norm/whole-vector clear/effective1.5 coefficient/field38 write. Source-image coefficients100/8/4/1/5/3000 and zero-vector words retain exact offsets/bytes; live writers/configuration remain open. Evaluated mode-friction velocity is transferred into partial scale/acceleration/position slices with source-image coefficient8 and matching supplied timestep, while other state/basis/callee/collision effects remain omitted. Sqrt returns come from a supplied finite math model; no full QVM/original executable/native callee/collision/physics system executes. Eight independent tests verify source words/provenance, every shared drop/ratio/vector/gate, low-speed vertical retention, replacement precedence, separate full-norm mode outputs, sqrt requests, pipeline velocity/coefficient/timestep/acceleration/position propagation and scope/boundaries. All 479 tests pass; storage/links/whitespace checks pass. RE-432 remains open; RE-436 tracks live coefficients and flag/level/surface/type producers/legal ranges, native sqrt, other caller policies and full basis/frame/state/callee/collision/mode/step gameplay/macOS/Linux/Rust/Bevy/Solari compatibility; packet/entity/player/snapshot/rendering milestones remain open.
320
+
321
+ Validation: all preceding composition reports regenerated byte-identically, including the partial-caller default report. The new friction report reproduced twice byte-identically. Final suite: 479 passing tests. Storage policy/filter setup, documentation links and whitespace checks pass.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -522,3 +522,8 @@ Purpose: turn the supplied installation into an evidence-backed specification of
522
  - [x] RE-430 Recover bounded complete helper40275 dot/gap/capped-amount/vector acceleration and above-target retention. [ACCEL-002](research/QVM_ACCELERATION.md).
523
  - [x] RE-431 Compose evaluated scale/acceleration into selected helper45461 wish-vector/normalization/position slices using supplied basis/post-friction state and disjoint synthetic frames. [ACCEL-003](research/QVM_ACCELERATION.md).
524
  - [ ] RE-432 Recover friction/surrounding velocity policy, native sqrt106 ABI, input/button/meter/coefficient/time producers/legal ranges, real basis/frame/callee/collision effects and complete mode/step gameplay/macOS/Linux validation before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
 
 
 
 
 
 
522
  - [x] RE-430 Recover bounded complete helper40275 dot/gap/capped-amount/vector acceleration and above-target retention. [ACCEL-002](research/QVM_ACCELERATION.md).
523
  - [x] RE-431 Compose evaluated scale/acceleration into selected helper45461 wish-vector/normalization/position slices using supplied basis/post-friction state and disjoint synthetic frames. [ACCEL-003](research/QVM_ACCELERATION.md).
524
  - [ ] RE-432 Recover friction/surrounding velocity policy, native sqrt106 ABI, input/button/meter/coefficient/time producers/legal ranges, real basis/frame/callee/collision effects and complete mode/step gameplay/macOS/Linux validation before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
525
+
526
+ - [x] RE-433 Recover bounded shared helper39988 measured norm, additive/replacement drop terms, low-speed Z retention and file-backed initialized coefficient words. [FRIC-001](research/QVM_FRICTION.md).
527
+ - [x] RE-434 Recover bounded helper45461 full-norm friction prefix, whole-vector low-speed clear, effective coefficient and state-word write. [FRIC-002](research/QVM_FRICTION.md).
528
+ - [x] RE-435 Bridge evaluated mode-friction velocity into partial scale/acceleration/position fixtures with source-image coefficient8 and explicit omitted-state scope. [FRIC-003](research/QVM_FRICTION.md).
529
+ - [ ] RE-436 Recover coefficient/global writers/live configuration and ground/level/surface/flag/type producers/legal ranges, native sqrt ABI, other caller friction policy and complete basis/frame/state/callee/collision effects; validate full mode/step/gameplay/macOS/Linux before Rust/Bevy/Solari claims. Continue packet/entity/player/snapshot/rendering work.
docs/research/QVM_ACCELERATION.md CHANGED
@@ -50,3 +50,5 @@ The shared evaluator adds finite DIVF with explicit zero-divisor/nonfinite rejec
50
  Eight tests verify signed command scale and sqrt requests, strict boost boundaries/strafe exclusion, dot/gap/amount/full velocity, unchanged above-target projections, complete partial caller outputs/requests, source partitions, local-frame isolation and finite division failure domains. Prior report regeneration checks protect shared evaluator users.
51
 
52
  RE-428 remains open for complete mode/native/runtime work. RE-432 must recover friction and surrounding velocity/speed policy, native sqrt106 ABI/service, field+184/button256 producers and legal movement/coefficient/timestep ranges, real basis/stack/callee/collision effects, complete mode/step composition and gameplay/macOS/Linux validation before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot/rendering milestones remain open.
 
 
 
50
  Eight tests verify signed command scale and sqrt requests, strict boost boundaries/strafe exclusion, dot/gap/amount/full velocity, unchanged above-target projections, complete partial caller outputs/requests, source partitions, local-frame isolation and finite division failure domains. Prior report regeneration checks protect shared evaluator users.
51
 
52
  RE-428 remains open for complete mode/native/runtime work. RE-432 must recover friction and surrounding velocity/speed policy, native sqrt106 ABI/service, field+184/button256 producers and legal movement/coefficient/timestep ranges, real basis/stack/callee/collision effects, complete mode/step composition and gameplay/macOS/Linux validation before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot/rendering milestones remain open.
53
+
54
+ Follow-up: [friction policies](QVM_FRICTION.md) recovers shared/mode-specific friction and bridges evaluated velocity into new partial-caller fixtures with source-image coefficient8. This report retains its supplied coefficient10/defaults; live configuration and full mode effects remain open.
docs/research/QVM_FRICTION.md ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Game-QVM friction policies and velocity composition
2
+
3
+ RE-433–435 / F-184–186. [qvm-friction.json](qvm-friction.json) retains helper39988, the friction prefix of helper45461, nine file-backed initialized words, 342 shared-friction probes, 24 mode-prefix probes and eighteen staged caller compositions. Reproduce with `python3 scripts/analyze_qvm_friction.py`; verify with `python3 scripts/test_qvm_friction.py`.
4
+
5
+ This follows [command scale and acceleration](QVM_ACCELERATION.md). Results are bounded synthetic instruction evaluations; no complete QVM, original executable, native callee or collision/physics system executes. Square-root returns are supplied from ordinary finite math. The staged pipeline transfers only evaluated friction velocity into the previous partial caller. It supplies the identity basis, command/button/meter and timestep, and omits other state/callee/frame/collision effects.
6
+
7
+ ## FRIC-001 — Initialized words and shared helper39988 (RE-433)
8
+
9
+ The collector verifies source-image bytes, file offsets and VM addresses for these words. These are an initialized image snapshot, not a proof of immutable constants or live configuration:
10
+
11
+ | VM address | Binary32 value | Observed use in these slices |
12
+ | --- | --- | --- |
13
+ | 21160 | 100 | Minimum control speed in ground-like drop |
14
+ | 21200 | 8 | Acceleration coefficient supplied to the new pipeline |
15
+ | 21208 | 4 | Ground-like/shared friction coefficient |
16
+ | 21212 | 1 | Frame-level-dependent drop coefficient |
17
+ | 21220 | 5 | Type/flag-dependent drop coefficient |
18
+ | 21224 | 3000 | Player-state flag-mask12 drop coefficient |
19
+ | 21348/21352/21356 | 0/0/0 | Vector copied by the mode-specific low-speed branch |
20
+
21
+ Helper39988 copies velocity from player-state+32/+36/+40 into a local vector. Nonzero global35023936 removes Z **only from the measured vector**, before requesting its length from target-107. The original velocity vector is not projected to a plane at this point.
22
+
23
+ If measured speed <1, it clears original X/Y velocity and returns, retaining original Z. Otherwise it accumulates drop with operation-level binary32 rounding. Ground/level/surface terminology below is provisional pending producers and consumers.
24
+
25
+ | Predicate | Term added to drop |
26
+ | --- | --- |
27
+ | Frame+212 <=1, global35023936 nonzero, global35023988 bit2 clear, player-state+12 bit64 clear | max(speed,word21160) * word21208 * dt |
28
+ | Frame+212 nonzero | speed * word21212 * float(frame+212) * dt |
29
+ | Player-state+12 mask12 nonzero | speed * word21224 * dt |
30
+ | Type at player-state+4 ==2 or player-state+220 bit2048 | speed * word21220 * dt |
31
+
32
+ After those additions, player-state+104 bit1 **replaces** accumulated drop with `dt *1000 * binary32(0.15)`; it does not add another term. The slice then computes `ratio = max(speed-drop,0)/speed` and scales all three original velocity components by the ratio. Thus a planar measured speed can determine scaling of original vertical velocity as well; the low-speed return is the separate exception described above.
33
+
34
+ 342 probes vary supplied vectors, norm mode, frame levels, surface/PS bits, types, special flags, replacement flag and timesteps0/0.016/0.5. Independent rounded formulas verify measured sqrt requests, every drop/ratio/final vector, low-horizontal-speed vertical retention and replacement behavior. Numeric fields/flags, negative or unusual configuration ranges, callbacks and runtime behavior remain open.
35
+
36
+ ## FRIC-002 — Mode-specific helper45461 prefix (RE-434)
37
+
38
+ The prefix45462–45647 first writes38 to player-state+164. It measures full XYZ speed, without the shared helper's optional planar measurement. A speed below1 copies the file-backed zero vector into **all three** velocity components, even when supplied dt is zero.
39
+
40
+ Other paths use an effective coefficient of `word21208 *1.5` (six under this image snapshot). Drop is `max(speed,word21160) * effective_coefficient *dt`, with rounding after each operation. It scales velocity by `max(speed-drop,0)/speed`.
41
+
42
+ This differs from shared helper39988 in norm selection, low-speed Z treatment and coefficient, and does not include its other drop terms/replacement branch. The two policies must not be merged into a single inferred friction rule.
43
+
44
+ 24 probes cover tiny/exact-one/mixed/high/signed vectors with three timesteps. Independent full-norm and rounded drop/ratio formulas verify all velocity words, the+164 write and the square-root request. Earlier configuration/lifecycle and the field38 meaning remain unverified.
45
+
46
+ ## FRIC-003 — Velocity-only bridge into the partial caller (RE-435)
47
+
48
+ Eighteen compositions evaluate the mode-specific friction prefix, then transfer its three velocity words into the prior helper45461 partial caller. That caller evaluates command scaling, wish-vector construction/normalization, helper40275 acceleration and unconditional position-tail updates. Source-image coefficient8 replaces the earlier report's explicitly supplied example coefficient10 in these new fixtures; the previous report retains its original defaults and scope.
49
+
50
+ The new fixtures cover three initial velocity vectors, zero/axis/diagonal commands and dt0.016/0.5. They use button256 and supplied meter11, identity basis and initial position10/20/30. Larger timestep probes are synthetic arithmetic cases, not asserted reachable within the recovered scheduler caps. Tests independently verify friction output, exact velocity handoff, coefficient/timestep propagation, acceleration and final position words.
51
+
52
+ Only velocity is transferred between the separately evaluated stages. This is not a full helper45461 call-frame composition: its other state words, earlier/callee effects, native sqrt/basis generation, collision and runtime context remain omitted. The shared partial-caller helper now accepts coefficient and dt parameters with unchanged defaults, preserving earlier report output.
53
+
54
+ ## Validation and remaining work
55
+
56
+ Eight independent tests verify file bytes/values/provenance, all shared drop/ratio/vector outputs and sqrt requests, low-speed Z retention, replacement rather than addition, full-norm mode behavior/field write, staged velocity/acceleration/position propagation and explicit scope/boundaries. Prior report regeneration checks protect the partial caller's default behavior.
57
+
58
+ RE-432 remains open for complete policy/native/runtime recovery. RE-436 must trace coefficient/global writers and live configuration, ground/level/surface/PS-bit/type producers and legal ranges, native sqrt ABI, other callers' friction combinations, complete basis/frame/state/callee transfer and collision/simulation effects, then validate full mode/step/gameplay/macOS/Linux before Rust/Bevy/Solari compatibility claims. Packet/entity/player/snapshot/rendering milestones remain open.
docs/research/README.md CHANGED
@@ -171,3 +171,5 @@ The extension collector adds five focused tests for native tail calls, command-r
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.
172
 
173
  [QVM command scaling and acceleration](QVM_ACCELERATION.md) adds RE-429–431. Reproduce with `python3 scripts/analyze_qvm_acceleration.py`; check with `python3 scripts/test_qvm_acceleration.py`. Sqrt, basis and post-friction inputs are supplied; complete mode/native/runtime effects remain open.
 
 
 
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.
172
 
173
  [QVM command scaling and acceleration](QVM_ACCELERATION.md) adds RE-429–431. Reproduce with `python3 scripts/analyze_qvm_acceleration.py`; check with `python3 scripts/test_qvm_acceleration.py`. Sqrt, basis and post-friction inputs are supplied; complete mode/native/runtime effects remain open.
174
+
175
+ [QVM friction and velocity composition](QVM_FRICTION.md) adds RE-433–435. Reproduce with `python3 scripts/analyze_qvm_friction.py`; check with `python3 scripts/test_qvm_friction.py`. Constants are initialized-image snapshots; native sqrt, live policy and full state/collision/runtime effects remain open.
docs/research/qvm-friction.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_acceleration.py CHANGED
@@ -40,15 +40,15 @@ def accel_probe(ins,velocity,direction,speed,acceleration,dt):
40
  m={PS+32+i*4:word(v) for i,v in enumerate(velocity)};_,p=accel(ins,m,direction,speed,acceleration,dt);p['initial_velocity_float_words']=[word(v) for v in velocity];return p
41
 
42
 
43
- def composed_probe(ins,cmd,buttons,meter,velocity):
44
- sp=scale_probe(ins,*cmd,buttons,meter);m={35024032:FRAME,FRAME:PS,21200:word(10)};b={FRAME+25+i:v&255 for i,v in enumerate(cmd)}
45
  for i,v in enumerate([1,0,0]):m[35023892+i*4]=word(v)
46
  for i,v in enumerate([0,1,0]):m[35023904+i*4]=word(v)
47
  root=word(math.sqrt(sum(v*v for v in cmd)));t=trace_slice(ins,45647,45813,m,{},byte_memory=b,call_results={45654:sp['result_scale_float_word'],45758:root},local_base=CALLER,limit=512);m=t['memory'];request=t['calls'][-1]
48
  if request['target']!=40275:raise ValueError('acceleration request')
49
  direction=[number(m[request['args'][8]+4*i]) for i in range(3)];speed=number(request['args'][12]);coefficient=number(request['args'][16])
50
  for i,v in enumerate(velocity):m[PS+32+4*i]=word(v)
51
- m,ap=accel(ins,m,direction,speed,coefficient,.016)
52
  for i,v in enumerate([10,20,30]):m[PS+20+4*i]=word(v)
53
  tail=trace_slice(ins,45813,45885,m,{},local_base=CALLER,limit=512)
54
  return {'commands':cmd,'buttons_word':buttons,'meter_word':meter,'initial_velocity_float_words':[word(v) for v in velocity],'scale_probe':sp,'supplied_wish_sqrt_word':root,'caller_requests':t['calls'],'acceleration_probe':ap,'origin_float_words':[tail['memory'][PS+20+4*i]&0xffffffff for i in range(3)],**trace_fields({'trace':t['trace']+tail['trace']},False)}
 
40
  m={PS+32+i*4:word(v) for i,v in enumerate(velocity)};_,p=accel(ins,m,direction,speed,acceleration,dt);p['initial_velocity_float_words']=[word(v) for v in velocity];return p
41
 
42
 
43
+ def composed_probe(ins,cmd,buttons,meter,velocity,coefficient=10,dt=.016):
44
+ sp=scale_probe(ins,*cmd,buttons,meter);m={35024032:FRAME,FRAME:PS,21200:word(coefficient)};b={FRAME+25+i:v&255 for i,v in enumerate(cmd)}
45
  for i,v in enumerate([1,0,0]):m[35023892+i*4]=word(v)
46
  for i,v in enumerate([0,1,0]):m[35023904+i*4]=word(v)
47
  root=word(math.sqrt(sum(v*v for v in cmd)));t=trace_slice(ins,45647,45813,m,{},byte_memory=b,call_results={45654:sp['result_scale_float_word'],45758:root},local_base=CALLER,limit=512);m=t['memory'];request=t['calls'][-1]
48
  if request['target']!=40275:raise ValueError('acceleration request')
49
  direction=[number(m[request['args'][8]+4*i]) for i in range(3)];speed=number(request['args'][12]);coefficient=number(request['args'][16])
50
  for i,v in enumerate(velocity):m[PS+32+4*i]=word(v)
51
+ m,ap=accel(ins,m,direction,speed,coefficient,dt)
52
  for i,v in enumerate([10,20,30]):m[PS+20+4*i]=word(v)
53
  tail=trace_slice(ins,45813,45885,m,{},local_base=CALLER,limit=512)
54
  return {'commands':cmd,'buttons_word':buttons,'meter_word':meter,'initial_velocity_float_words':[word(v) for v in velocity],'scale_probe':sp,'supplied_wish_sqrt_word':root,'caller_requests':t['calls'],'acceleration_probe':ap,'origin_float_words':[tail['memory'][PS+20+4*i]&0xffffffff for i in range(3)],**trace_fields({'trace':t['trace']+tail['trace']},False)}
scripts/analyze_qvm_friction.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Bounded friction policies and velocity-only bridge into a mode caller."""
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_qvm_acceleration import FRAME,PS,word,number,f,composed_probe
12
+ from analyze_vm_extensions import verified
13
+
14
+ FRICTION_FRAME=0x43000000
15
+ MODE_FRAME=0x44000000
16
+
17
+
18
+ def length_word(v):return word(math.sqrt(f(f(f(v[0]*v[0])+f(v[1]*v[1]))+f(v[2]*v[2]))))
19
+
20
+
21
+ def friction_probe(ins,constants,velocity,grounded=0,level=0,surface=0,psflags=0,kind=0,flags220=0,override=0,dt=.016):
22
+ m=dict(constants);m.update({35024032:FRAME,FRAME:PS,35023936:grounded,FRAME+212:level,35023988:surface,PS+12:psflags,PS+4:kind,PS+220:flags220,PS+104:override,35023928:word(dt)})
23
+ for i,v in enumerate(velocity):m[PS+32+4*i]=word(v)
24
+ measured=list(map(lambda v:number(word(v)),velocity));measured[2]=0 if grounded else measured[2];root=length_word(measured);t=trace_slice(ins,39989,40273,m,{},call_results={40031:root},local_base=FRICTION_FRAME,limit=512);mem=t['memory'];return {'initial_velocity_float_words':[word(v) for v in velocity],'grounded_word':grounded,'level_word':level,'surface_word':surface,'ps_flags_word':psflags,'type_word':kind,'flags220_word':flags220,'override_word':override,'dt_float_word':word(dt),'supplied_sqrt_word':root,'drop_float_word':mem[FRICTION_FRAME+36]&0xffffffff if FRICTION_FRAME+36 in mem else None,'ratio_float_word':mem[FRICTION_FRAME+28]&0xffffffff if FRICTION_FRAME+28 in mem else None,'result_velocity_float_words':[mem[PS+32+4*i]&0xffffffff for i in range(3)],'calls':t['calls'],**trace_fields(t,False)}
25
+
26
+
27
+ def mode_probe(ins,constants,velocity,dt):
28
+ m=dict(constants);m.update({35024032:FRAME,FRAME:PS,35023928:word(dt),PS+164:0x1234})
29
+ for i,v in enumerate(velocity):m[PS+32+4*i]=word(v)
30
+ root=length_word(velocity);t=trace_slice(ins,45462,45647,m,{},call_results={45512:root},local_base=MODE_FRAME,limit=512);return {'initial_velocity_float_words':[word(v) for v in velocity],'dt_float_word':word(dt),'supplied_sqrt_word':root,'result_velocity_float_words':[t['memory'][PS+32+4*i]&0xffffffff for i in range(3)],'result_ps164_word':t['memory'][PS+164],'calls':t['calls'],**trace_fields(t,False)}
31
+
32
+
33
+ def pipeline_probe(ins,constants,velocity,cmd,dt):
34
+ prefix=mode_probe(ins,constants,velocity,dt);post=list(map(number,prefix['result_velocity_float_words']));caller=composed_probe(ins,cmd,256,11,post,coefficient=number(constants[21200]),dt=dt);return {'friction_prefix':prefix,'coefficient_float_word':constants[21200],'partial_caller':caller}
35
+
36
+
37
+ def main():
38
+ r=json.loads(Path('docs/research/qvm/qagame.json').read_text());data=verified(Path(r['source']['member']['output']).read_bytes(),r['sha256']);h,ins=decode(data);constants={};rows=[]
39
+ for address in [21160,21200,21208,21212,21220,21224,21348,21352,21356]:
40
+ offset=h['data_offset']+address;raw=data[offset:offset+4];value=struct.unpack('<I',raw)[0];constants[address]=value;rows.append({'vm_address':address,'file_offset':offset,'bytes_hex':raw.hex(),'word':value,'float_value':number(value)})
41
+ probes=[friction_probe(ins,constants,*v) for v in itertools.product([(0,0,.5),(.5,.5,100),(3,4,12),(200,0,0),(0,0,100)],[0,1],[0,1,2],[0,2],[0,64,4])]
42
+ probes += [friction_probe(ins,constants,v,grounded=1,level=l,kind=k,psflags=flags,flags220=special,override=override) for v,l,k,flags,special,override in itertools.product([(3,4,12),(200,0,0),(0,0,100)],[0,1,2],[0,2],[0,4],[0,2048],[0,1])]
43
+ probes += [friction_probe(ins,constants,v,grounded=1,override=o,dt=dt) for v,dt,o in itertools.product([(0,0,.5),(3,4,12),(200,0,0)],[0,.016,.5],[0,1])]
44
+ velocities=[(0,0,.5),(.5,.5,10),(1,0,0),(3,4,0),(200,0,0),(0,0,100),(-10,20,-30),(-.5,-.5,-.5)];out={'source':r['source'],'sha256':r['sha256'],'initialized_words':rows,'friction_instructions':ins[39988:40275],'mode_prefix_instructions':ins[45461:45647],'friction_probes':probes,'mode_probes':[mode_probe(ins,constants,v,dt) for v,dt in itertools.product(velocities,[0,.016,.5])],'pipeline_probes':[pipeline_probe(ins,constants,v,c,dt) for v,c,dt in itertools.product([(0,0,.5),(3,4,12),(200,0,0)],[(0,0,0),(127,0,0),(127,127,0)],[.016,.5])],'limitations':'Bounded synthetic shipped-QVM instruction slices only; original runtime/native callees/full QVM/collision/physics system never execute. Constants are file-backed initialized words, not proven immutable/live configuration. Sqrt returns supplied from finite ordinary-math model, not trap106. Pipeline explicitly transfers only evaluated friction velocity into the earlier partial caller with supplied identity basis/commands/button/meter and source-image coefficient; other prefix state/callee/frame/collision effects omitted. Finite arithmetic only; flag/level/surface semantics, legal ranges, writers/alias/control-state/runtime/platform contracts remain open.'};write_json(Path('docs/research/qvm-friction.json'),out);print('Recovered file-backed friction words bounded policies and velocity bridge')
45
+
46
+
47
+ if __name__=='__main__':main()
scripts/test_qvm_friction.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Independent friction drop/ratio and staged caller oracles."""
3
+ import hashlib
4
+ import json
5
+ import math
6
+ from pathlib import Path
7
+ import struct
8
+ import unittest
9
+
10
+
11
+ def word(v):return struct.unpack('<I',struct.pack('<f',v))[0]
12
+ def n(v):return struct.unpack('<f',struct.pack('<I',v&0xffffffff))[0]
13
+ def f(v):return n(word(v))
14
+ def mul(a,b):return f(a*b)
15
+ def add(a,b):return f(a+b)
16
+
17
+ def friction_oracle(p,c):
18
+ v=list(map(n,p['initial_velocity_float_words']));measured=v[:];measured[2]=0 if p['grounded_word'] else measured[2];square=add(add(mul(measured[0],measured[0]),mul(measured[1],measured[1])),mul(measured[2],measured[2]));speed=f(math.sqrt(square));dt=n(p['dt_float_word'])
19
+ if speed<1:return word(speed),None,None,[word(0),word(0),p['initial_velocity_float_words'][2]]
20
+ drop=0
21
+ if p['level_word']<=1 and p['grounded_word'] and not p['surface_word']&2 and not p['ps_flags_word']&64:drop=add(drop,mul(mul(max(speed,c[21160]),c[21208]),dt))
22
+ if p['level_word']!=0:drop=add(drop,mul(mul(mul(speed,c[21212]),f(p['level_word'])),dt))
23
+ if p['ps_flags_word']&12:drop=add(drop,mul(mul(speed,c[21224]),dt))
24
+ if p['type_word']==2 or p['flags220_word']&2048:drop=add(drop,mul(mul(speed,c[21220]),dt))
25
+ if p['override_word']&1:drop=mul(mul(dt,1000),n(1041865114))
26
+ ratio=f(max(f(speed-drop),0)/speed);return word(speed),word(drop),word(ratio),[word(mul(x,ratio)) for x in v]
27
+
28
+
29
+ def mode_oracle(p,c):
30
+ v=list(map(n,p['initial_velocity_float_words']));speed=f(math.sqrt(add(add(mul(v[0],v[0]),mul(v[1],v[1])),mul(v[2],v[2]))))
31
+ if speed<1:return word(speed),[0]*3
32
+ drop=mul(mul(max(speed,c[21160]),mul(c[21208],1.5)),n(p['dt_float_word']));ratio=f(max(f(speed-drop),0)/speed);return word(speed),[word(mul(x,ratio)) for x in v]
33
+
34
+
35
+ class QvmFrictionTests(unittest.TestCase):
36
+ @classmethod
37
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-friction.json').read_text());cls.c={p['vm_address']:p['float_value'] for p in cls.r['initialized_words']}
38
+
39
+ def test_initialized_file_words_and_provenance(self):
40
+ q=json.loads(Path('docs/research/qvm/qagame.json').read_text());raw=Path(q['source']['member']['output']).read_bytes();self.assertEqual(hashlib.sha256(raw).hexdigest(),self.r['sha256']);self.assertEqual(q['source'],self.r['source']);expected={21160:100,21200:8,21208:4,21212:1,21220:5,21224:3000,21348:0,21352:0,21356:0};self.assertEqual(self.c,expected)
41
+ for p in self.r['initialized_words']:self.assertEqual(raw[p['file_offset']:p['file_offset']+4].hex(),p['bytes_hex']);self.assertEqual(int.from_bytes(bytes.fromhex(p['bytes_hex']),'little'),p['word'])
42
+
43
+ def test_all_friction_gates_accumulation_override_and_ratios(self):
44
+ self.assertEqual(len(self.r['friction_probes']),342)
45
+ for p in self.r['friction_probes']:
46
+ root,drop,ratio,result=friction_oracle(p,self.c);self.assertEqual(p['supplied_sqrt_word'],root);self.assertEqual(p['drop_float_word'],drop);self.assertEqual(p['ratio_float_word'],ratio);self.assertEqual(p['result_velocity_float_words'],result)
47
+
48
+ def test_low_horizontal_speed_retains_vertical_and_skips_drop(self):
49
+ rows=[p for p in self.r['friction_probes'] if p['grounded_word'] and list(map(n,p['initial_velocity_float_words']))==[.5,.5,100]];self.assertTrue(rows)
50
+ for p in rows:self.assertEqual(p['result_velocity_float_words'],[0,0,word(100)]);self.assertIsNone(p['drop_float_word'])
51
+
52
+ def test_override_replaces_large_drop_instead_of_adding(self):
53
+ rows=[p for p in self.r['friction_probes'] if p['override_word']&1 and n(p['supplied_sqrt_word'])>=1];self.assertTrue(rows)
54
+ for p in rows:self.assertEqual(p['drop_float_word'],word(mul(mul(n(p['dt_float_word']),1000),n(1041865114))))
55
+
56
+ def test_mode_prefix_full_norm_zero_vector_coefficient_and_field(self):
57
+ self.assertEqual(len(self.r['mode_probes']),24)
58
+ for p in self.r['mode_probes']:
59
+ root,result=mode_oracle(p,self.c);self.assertEqual(p['supplied_sqrt_word'],root);self.assertEqual(p['result_velocity_float_words'],result);self.assertEqual(p['result_ps164_word'],38);self.assertEqual([c['target'] for c in p['calls']],[-107])
60
+
61
+ def test_velocity_only_pipeline_bridge_source_coefficient_and_acceleration(self):
62
+ self.assertEqual(len(self.r['pipeline_probes']),18)
63
+ for p in self.r['pipeline_probes']:
64
+ prefix=p['friction_prefix'];self.assertEqual(prefix['result_velocity_float_words'],mode_oracle(prefix,self.c)[1]);caller=p['partial_caller'];self.assertEqual(caller['initial_velocity_float_words'],prefix['result_velocity_float_words']);self.assertEqual(p['coefficient_float_word'],word(8));ap=caller['acceleration_probe'];self.assertEqual(ap['acceleration_float_word'],word(8));v=list(map(n,caller['initial_velocity_float_words']));d=list(map(n,ap['direction_float_words']));speed=n(ap['wish_speed_float_word']);projection=add(add(mul(v[0],d[0]),mul(v[1],d[1])),mul(v[2],d[2]));gap=f(speed-projection);dt=n(prefix['dt_float_word']);amount=min(mul(mul(8,dt),speed),gap) if gap>0 else None;result=[word(add(x,mul(amount,y))) for x,y in zip(v,d)] if amount is not None else caller['initial_velocity_float_words'];self.assertEqual(ap['result_velocity_float_words'],result);self.assertEqual(caller['origin_float_words'],[word(add(pos,mul(n(x),dt))) for pos,x in zip([10,20,30],result)])
65
+
66
+ def test_friction_sqrt_request_matches_measured_vector(self):
67
+ for p in self.r['friction_probes']:
68
+ v=list(map(n,p['initial_velocity_float_words']));v[2]=0 if p['grounded_word'] else v[2];square=add(add(mul(v[0],v[0]),mul(v[1],v[1])),mul(v[2],v[2]));self.assertEqual(p['calls'],[{'instruction':40031,'target':-107,'args':{'8':word(square)}}])
69
+
70
+ def test_retained_boundaries_and_explicit_scope(self):
71
+ self.assertEqual(self.r['friction_instructions'][0]['index'],39988);self.assertEqual(self.r['friction_instructions'][-1]['index'],40274);self.assertEqual(self.r['mode_prefix_instructions'][-1]['index'],45646);self.assertIn('transfers only evaluated friction velocity',self.r['limitations']);self.assertIn('not proven immutable/live configuration',self.r['limitations'])
72
+
73
+
74
+ if __name__=='__main__':unittest.main()