nmarshall commited on
Commit
57daeff
·
1 Parent(s): 5728b6c

qvm: weapon lowering and raising

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -424,3 +424,6 @@ Research progress: RE-577 [report](research/QVM_WEAPON_READY.md); remaining nati
424
 
425
 
426
  Research progress: RE-578 [report](research/QVM_RELOAD_COMPLETE.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
424
 
425
 
426
  Research progress: RE-578 [report](research/QVM_RELOAD_COMPLETE.md); remaining native/content/runtime/platform scope stays open.
427
+
428
+
429
+ Research progress: RE-579 [report](research/QVM_WEAPON_PHASES.md); remaining native/content/runtime/platform scope stays open.
docs/FINDINGS.md CHANGED
@@ -782,3 +782,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
782
 
783
 
784
  **RE-578 — Reload completion packed ammo transfer and re-entry:** 51365..51632 increments the magazine byte when record flags16 is set, otherwise replaces it with capacity; both decrement reserve modulo256, reset counter204, clear state8 and call49652(argument0). [Evidence](research/QVM_RELOAD_COMPLETE.md).
 
 
 
 
782
 
783
 
784
  **RE-578 — Reload completion packed ammo transfer and re-entry:** 51365..51632 increments the magazine byte when record flags16 is set, otherwise replaces it with capacity; both decrement reserve modulo256, reset counter204, clear state8 and call49652(argument0). [Evidence](research/QVM_RELOAD_COMPLETE.md).
785
+
786
+
787
+ **RE-579 — Weapon lowering raising and frame gates:** 51633..51801 completes state2 via49028, resets state1 to0 and selects a separate class-based torso table, then gates other states on FRAME232 and FRAME36 before firing entry51802. [Evidence](research/QVM_WEAPON_PHASES.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -738,3 +738,6 @@ Research progress: RE-577 [report](research/QVM_WEAPON_READY.md); remaining nati
738
 
739
 
740
  Research progress: RE-578 [report](research/QVM_RELOAD_COMPLETE.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
738
 
739
 
740
  Research progress: RE-578 [report](research/QVM_RELOAD_COMPLETE.md); remaining native/content/runtime/platform scope stays open.
741
+
742
+
743
+ Research progress: RE-579 [report](research/QVM_WEAPON_PHASES.md); remaining native/content/runtime/platform scope stays open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -586,3 +586,8 @@ Actual54444 reload/interaction order exposes timer interference before the weapo
586
  ## 2026-10-20 — RE-578 Reload completion packed ammo transfer and re-entry (separate task commit)
587
 
588
  51365..51632 increments the magazine byte when record flags16 is set, otherwise replaces it with capacity; both decrement reserve modulo256, reset counter204, clear state8 and call49652(argument0). 115 independent probes check increment/replacement, full/partial magazines, reserve0/1/2 wrap, preserved ID/mode/unselected words and live reload re-entry gates. Full suite: 831 tests pass. Report regenerates byte-identically (878739 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
 
 
 
 
 
 
586
  ## 2026-10-20 — RE-578 Reload completion packed ammo transfer and re-entry (separate task commit)
587
 
588
  51365..51632 increments the magazine byte when record flags16 is set, otherwise replaces it with capacity; both decrement reserve modulo256, reset counter204, clear state8 and call49652(argument0). 115 independent probes check increment/replacement, full/partial magazines, reserve0/1/2 wrap, preserved ID/mode/unselected words and live reload re-entry gates. Full suite: 831 tests pass. Report regenerates byte-identically (878739 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
589
+
590
+
591
+ ## 2026-10-20 — RE-579 Weapon lowering raising and frame gates (separate task commit)
592
+
593
+ 51633..51801 completes state2 via49028, resets state1 to0 and selects a separate class-based torso table, then gates other states on FRAME232 and FRAME36 before firing entry51802. 87 fixtures independently check completion fallback and state handoff, all classes−1..8, button priority, animation timer/type guards and both frame gates. Full suite: 835 tests pass. Report regenerates byte-identically (796730 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -778,3 +778,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
778
 
779
 
780
  - [x] RE-578 Reload completion packed ammo transfer and re-entry ([report](research/QVM_RELOAD_COMPLETE.md)).
 
 
 
 
778
 
779
 
780
  - [x] RE-578 Reload completion packed ammo transfer and re-entry ([report](research/QVM_RELOAD_COMPLETE.md)).
781
+
782
+
783
+ - [x] RE-579 Weapon lowering raising and frame gates ([report](research/QVM_WEAPON_PHASES.md)).
docs/research/QVM_WEAPON_PHASES.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Weapon lowering raising and frame gates
2
+
3
+ RE-579. Reproduce with `python3 scripts/analyze_qvm_weapon_phases.py`; check with `python3 scripts/test_qvm_weapon_phases.py`. The [machine report](qvm-weapon-phases.json) retains source identity and bounded fixtures.
4
+
5
+ State2 calls actual49028 and exits. State1 resets PS148 to0, then button8192/16384 requests torso50; otherwise initialized jump table21284..21312 maps classes0/1/5/7 to67,2 to66,3 to78,4 to72 and6 to65; out-of-range defaults67. Unlike49117's idle torso selector, this table does not inspect tag22/25. Actual39482 applies timer/type/toggle guards; phase completion itself still sets state0 when animation is blocked.
6
+
7
+ Other weapon states reach a pair of frame-word gates: nonzero FRAME232 or FRAME36 exits, otherwise51802 is reached. These are movement-frame words, distinct from same-offset player-state fields; their gameplay interpretation remains open.87 fixtures include actual49028 child effects and numeric command/fallback lookup.
8
+
9
+ Standalone phase probes intentionally do not run prior positive-timer guards. Full parent composition must preserve those gates and existing parent locals before reaching this slice. Remaining: compose recovered pre-firing parent paths, resume main suffix only on proven returns, and recover mode-specific51802+ firing/availability logic.
10
+
11
+ No original executable/full QVM/native collision/physics/rendering executes. Native responses and legal input domains are supplied or unverified where stated; full runtime/macOS/Linux Rust/Bevy/Solari gameplay remains open.
docs/research/README.md CHANGED
@@ -339,3 +339,6 @@ The extension collector adds five focused tests for native tail calls, command-r
339
 
340
 
341
  [RE-578: Reload completion packed ammo transfer and re-entry](QVM_RELOAD_COMPLETE.md). Reproduce with `python3 scripts/analyze_qvm_reload_complete.py`; check with `python3 scripts/test_qvm_reload_complete.py`.
 
 
 
 
339
 
340
 
341
  [RE-578: Reload completion packed ammo transfer and re-entry](QVM_RELOAD_COMPLETE.md). Reproduce with `python3 scripts/analyze_qvm_reload_complete.py`; check with `python3 scripts/test_qvm_reload_complete.py`.
342
+
343
+
344
+ [RE-579: Weapon lowering raising and frame gates](QVM_WEAPON_PHASES.md). Reproduce with `python3 scripts/analyze_qvm_weapon_phases.py`; check with `python3 scripts/test_qvm_weapon_phases.py`.
docs/research/qvm-weapon-phases.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_weapon_phases.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """51633..51801 lowering/raising completion and two frame-field gates."""
3
+ import itertools
4
+ from pathlib import Path
5
+ from qvm_research_support import source,execute
6
+ from qvm_weapon_support import weapon_seed,weapon_state
7
+ from qvm_suffix_support import handlers
8
+ from analyze_qvm_weapon_prefix import LOCAL
9
+ from analyze_qvm_weapon_complete import complete
10
+ from analyze_qvm_acceleration import PS,FRAME
11
+ from analyze_qvm import write_json
12
+
13
+ def phases(ins,m,cmd=(0,0,0)):
14
+ m.setdefault(FRAME+36,0);before=weapon_state(m);klass=m[4032+432*(before['packed']&255)];f36=m[FRAME+36];hs=handlers()
15
+ def completion(i,m,a,c):m,r=complete(i,m,c);return m,c,0,r
16
+ hs[49028]=completion;m,c,r=execute(ins,51633,53046,m,cmd,LOCAL,hs,boundaries=[51802])
17
+ return m,{'initial':before,'buttons':m[FRAME+20],'kind':m[PS+4],'class':klass,'frame36':f36,'execution':r,'final':weapon_state(m)}
18
+
19
+ def main():
20
+ q,raw,h,ins=source();rows=[]
21
+ for desired,timer,kind in itertools.product([1,22,255],[0,-1,100],[0,3]):
22
+ m=weapon_seed(raw,h,desired=desired);m.update({PS+312:22,PS+316:1,PS+148:2,PS+44:timer,PS+4:kind,FRAME+20:0});rows.append(phases(ins,m)[1])
23
+ for klass,buttons,tt in itertools.product(range(-1,9),[0,8192,16384],[0,1]):
24
+ m=weapon_seed(raw,h);m.update({PS+312:22,PS+148:1,4032+432*22:klass,FRAME+20:buttons,PS+80:tt});rows.append(phases(ins,m)[1])
25
+ for ws,a,b in itertools.product([0,3],[0,1],[0,1]):
26
+ m=weapon_seed(raw,h);m.update({PS+312:22,PS+148:ws,FRAME+232:a,FRAME+36:b});rows.append(phases(ins,m)[1])
27
+ m=weapon_seed(raw,h);m.update({PS+312:22,PS+148:1,PS+4:3});rows.append(phases(ins,m)[1])
28
+ table={str(a):weapon_seed(raw,h)[a] for a in range(21284,21316,4)}
29
+ write_json(Path('docs/research/qvm-weapon-phases.json'),{'source':q['source'],'sha256':q['sha256'],'jump_table':table,'probes':rows,'limits':'Finite51633..51801, actual49028/39482 children and initialized class table. Standalone entry may violate earlier timer gates; type/phase/frame legality remains open. Stops51802 before mode-specific firing.'})
30
+ if __name__=='__main__':main()
scripts/test_qvm_weapon_phases.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest,copy
2
+ from pathlib import Path
3
+ from test_qvm_weapon_complete import completion_state
4
+ from test_qvm_torso_selector import signed
5
+
6
+ def phase_request(p):
7
+ if p['initial']['weapon_state']!=1:return None
8
+ if p['buttons']&(8192|16384):return 50
9
+ return {0:67,1:67,2:66,3:78,4:72,5:67,6:65,7:67}.get(p['class'],67)
10
+
11
+ def phase_state(p):
12
+ a=copy.deepcopy(p['initial']);stop=53046
13
+ if a['weapon_state']==2:return completion_state(p['execution']['children'][0]['effect']),stop
14
+ if a['weapon_state']==1:
15
+ a['weapon_state']=0;r=phase_request(p)
16
+ if p['kind']<3 and signed(a['torso_timer'])<=0 and a['torso']!=r and a['torso']&~128!=r:a['torso']=((a['torso']&128)^128)|r
17
+ elif not a['frame232'] and not p['frame36']:stop=51802
18
+ return a,stop
19
+ class WeaponPhasesTests(unittest.TestCase):
20
+ @classmethod
21
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-weapon-phases.json').read_text())
22
+ def test_complete_exact_phase_and_frame_state(self):
23
+ self.assertEqual(len(self.r['probes']),87)
24
+ for p in self.r['probes']:a,stop=phase_state(p);self.assertEqual(p['final'],a);self.assertEqual(p['execution']['stop'],stop)
25
+ def test_source_jump_table_and_no_tag_exception(self):self.assertEqual(list(self.r['jump_table'].values()),[51733,51740,51747,51754,51761,51733,51768,51775]);self.assertTrue(any(p['class']==4 and phase_request(p)==72 for p in self.r['probes']))
26
+ def test_actual_child_dispatch_and_request(self):
27
+ for p in self.r['probes']:
28
+ ws=p['initial']['weapon_state'];self.assertEqual([c['target'] for c in p['execution']['calls']],[49028] if ws==2 else [39482] if ws==1 else [])
29
+ if ws==1:self.assertEqual(p['execution']['calls'][0]['args'],{'8':phase_request(p)})
30
+ def test_raising_sets_idle_even_if_animation_blocked(self):
31
+ rows=[p for p in self.r['probes'] if p['initial']['weapon_state']==1 and (p['kind']==3 or p['initial']['torso_timer']>0)];self.assertTrue(rows)
32
+ for p in rows:self.assertEqual(p['final']['weapon_state'],0);self.assertEqual(p['final']['torso'],p['initial']['torso'])
33
+ if __name__=='__main__':unittest.main()