nmarshall commited on
Commit
22a74d4
·
1 Parent(s): 09af7e8

qvm: weapon mode prefix

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -388,3 +388,6 @@ Research progress: RE-564 [report](research/QVM_INTERACTION_REQUEST.md); remaini
388
 
389
 
390
  Research progress: RE-565 [report](research/QVM_PACKED_CONTROLS.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
388
 
389
 
390
  Research progress: RE-565 [report](research/QVM_PACKED_CONTROLS.md); remaining native/content/runtime/platform scope stays open.
391
+
392
+
393
+ Research progress: RE-566 [report](research/QVM_WEAPON_PREFIX.md); remaining native/content/runtime/platform scope stays open.
docs/FINDINGS.md CHANGED
@@ -746,3 +746,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
746
 
747
 
748
  **RE-565 — Packed mode availability edge latch and resource countdown:** 50250 edge-latches button128 into state32768/event15;50309 permits availability on mode flag4 or nonzero magazine;50397 drains with the full dt when its countdown reaches zero. [Evidence](research/QVM_PACKED_CONTROLS.md).
 
 
 
 
746
 
747
 
748
  **RE-565 — Packed mode availability edge latch and resource countdown:** 50250 edge-latches button128 into state32768/event15;50309 permits availability on mode flag4 or nonzero magazine;50397 drains with the full dt when its countdown reaches zero. [Evidence](research/QVM_PACKED_CONTROLS.md).
749
+
750
+
751
+ **RE-566 — Mode switch and ordered weapon prefix:** 49327 cycles packed mode with release latch16 and ID-specific exceptions;50746 orders button latch, weapon timer decrement, resource countdown and mode switch before weapon selection. [Evidence](research/QVM_WEAPON_PREFIX.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -700,3 +700,6 @@ Research progress: RE-564 [report](research/QVM_INTERACTION_REQUEST.md); remaini
700
 
701
 
702
  Research progress: RE-565 [report](research/QVM_PACKED_CONTROLS.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
700
 
701
 
702
  Research progress: RE-565 [report](research/QVM_PACKED_CONTROLS.md); remaining native/content/runtime/platform scope stays open.
703
+
704
+
705
+ Research progress: RE-566 [report](research/QVM_WEAPON_PREFIX.md); remaining native/content/runtime/platform scope stays open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -526,3 +526,8 @@ RE-549–558 each has its own code/tests/report/documentation commit. All759 tes
526
  ## 2026-10-20 — RE-565 Packed mode availability edge latch and resource countdown (separate task commit)
527
 
528
  50250 edge-latches button128 into state32768/event15;50309 permits availability on mode flag4 or nonzero magazine;50397 drains with the full dt when its countdown reaches zero. Seven chained latch ticks,30 availability probes and44 resource fixtures independently check release/rearm, queue wrap, packed addressing, signed multiplication/subtraction wrap and clamps. Full suite: 783 tests pass. Report regenerates byte-identically (191549 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
 
 
 
 
 
 
526
  ## 2026-10-20 — RE-565 Packed mode availability edge latch and resource countdown (separate task commit)
527
 
528
  50250 edge-latches button128 into state32768/event15;50309 permits availability on mode flag4 or nonzero magazine;50397 drains with the full dt when its countdown reaches zero. Seven chained latch ticks,30 availability probes and44 resource fixtures independently check release/rearm, queue wrap, packed addressing, signed multiplication/subtraction wrap and clamps. Full suite: 783 tests pass. Report regenerates byte-identically (191549 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
529
+
530
+
531
+ ## 2026-10-20 — RE-566 Mode switch and ordered weapon prefix (separate task commit)
532
+
533
+ 49327 cycles packed mode with release latch16 and ID-specific exceptions;50746 orders button latch, weapon timer decrement, resource countdown and mode switch before weapon selection. 82 mode fixtures and84 prefix fixtures independently check complete writes, source-order child state, death/flag early exits and negative timer preservation. Full suite: 787 tests pass. Report regenerates byte-identically (642934 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
@@ -740,3 +740,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
740
 
741
 
742
  - [x] RE-565 Packed mode availability edge latch and resource countdown ([report](research/QVM_PACKED_CONTROLS.md)).
 
 
 
 
740
 
741
 
742
  - [x] RE-565 Packed mode availability edge latch and resource countdown ([report](research/QVM_PACKED_CONTROLS.md)).
743
+
744
+
745
+ - [x] RE-566 Mode switch and ordered weapon prefix ([report](research/QVM_WEAPON_PREFIX.md)).
docs/research/QVM_WEAPON_PREFIX.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Mode switch and ordered weapon prefix
2
+
3
+ RE-566. Reproduce with `python3 scripts/analyze_qvm_weapon_prefix.py`; check with `python3 scripts/test_qvm_weapon_prefix.py`. The [machine report](qvm-weapon-prefix.json) retains source identity and bounded fixtures.
4
+
5
+ Complete49327 first handles held/released state16 before any timer gate: release clears16 and returns. Otherwise positive PS44, state1024/8 or ID0 block switching. IDs11/12/13 with state32 and mode1 block. ID1 with magazine1 exits in mode0 but automatically advances a nonzero mode without requiring button8; other cases require button8. New mode=old+1, wrapping to0 if its first record word is zero. A change sets state16, loads the new mode timer+12 into PS44, replaces only packed bits16..23, sets FRAME252=1 and FRAME256=old mode.
6
+
7
+ The recovered50746 prefix clears state32 when button1 is absent, then exits on PS12 bit256 or nonpositive health. Otherwise actual50250 runs, positive PS44 is reduced by full integer dt, actual50397 runs, then actual49327. A timer1 with dt16 can remain−15 at the bounded stop; no clamp has yet occurred. Tests independently compose each child using its live input and compare final state.
8
+
9
+ This is deliberately a prefix: evaluation stops at50872 immediately after49327, or at the actual early exit53046. Weapon-selection48798 and the remainder through firing/reload/projectile logic are omitted, not stubbed as completed behavior. Remaining: recover those bodies, live mode initialization, complete command selection and reference runtime parity.
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
@@ -301,3 +301,6 @@ The extension collector adds five focused tests for native tail calls, command-r
301
 
302
 
303
  [RE-565: Packed mode availability edge latch and resource countdown](QVM_PACKED_CONTROLS.md). Reproduce with `python3 scripts/analyze_qvm_packed_controls.py`; check with `python3 scripts/test_qvm_packed_controls.py`.
 
 
 
 
301
 
302
 
303
  [RE-565: Packed mode availability edge latch and resource countdown](QVM_PACKED_CONTROLS.md). Reproduce with `python3 scripts/analyze_qvm_packed_controls.py`; check with `python3 scripts/test_qvm_packed_controls.py`.
304
+
305
+
306
+ [RE-566: Mode switch and ordered weapon prefix](QVM_WEAPON_PREFIX.md). Reproduce with `python3 scripts/analyze_qvm_weapon_prefix.py`; check with `python3 scripts/test_qvm_weapon_prefix.py`.
docs/research/qvm-weapon-prefix.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_weapon_prefix.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """49327 mode switch and50746 weapon prefix, explicitly stopping before50872."""
3
+ import itertools
4
+ from pathlib import Path
5
+ from qvm_research_support import source,execute
6
+ from qvm_suffix_support import seed,state,handlers,weapon_config
7
+ from analyze_qvm_packed_controls import control
8
+ from analyze_qvm_acceleration import PS,FRAME
9
+ from analyze_qvm import write_json
10
+ LOCAL=0x70000000
11
+ MODE=LOCAL+0x1000
12
+
13
+ def mode_switch(ins,m,cmd=(0,0,0)):
14
+ before=state(m);packed=before['packed'];ident=packed&255;mode=(packed>>16)&255;ptr=4244+432*ident;cfg={'next_enabled':m[ptr+52*(mode+1)],'next_timer':m[ptr+52*(mode+1)+12],'zero_timer':m[ptr+12]}
15
+ m,c,r=execute(ins,49328,49650,m,cmd,MODE)
16
+ return m,{'initial':before,'buttons':m[FRAME+20],'mode_config':cfg,'execution':r,'final':state(m)}
17
+
18
+ def weapon_prefix(ins,m,cmd=(0,0,0)):
19
+ before=state(m);buttons=m[FRAME+20];dt=m[35023932]
20
+ def leaf(entry):
21
+ def apply(i,m,a,c):m,r=control(i,m,entry,c);return m,c,r['execution']['return'] or 0,r
22
+ return apply
23
+ def mode(i,m,a,c):m,r=mode_switch(i,m,c);return m,c,0,r
24
+ m,c,r=execute(ins,50747,53046,m,cmd,LOCAL,{50250:leaf(50250),50397:leaf(50397),49327:mode},boundaries=[50872])
25
+ return m,{'initial':before,'buttons':buttons,'dt':dt,'execution':r,'final':state(m)}
26
+
27
+ def main():
28
+ q,raw,h,ins=source();modes=[];prefix=[]
29
+ for ident,mode,buttons,mag in itertools.product([0,1,11,12,13,22],[0,1,2],[0,8],[1,2]):
30
+ m=weapon_config(seed(raw,h),raw,h);m.update({PS+312:ident|(mode<<16)|(mag<<8)|(3<<24),PS+44:0,FRAME+20:buttons});modes.append(mode_switch(ins,m)[1])
31
+ for changes in [{PS+220:16},{PS+220:16,FRAME+20:0},{PS+220:1024},{PS+220:8},{PS+44:1},{PS+220:32,PS+312:11|(1<<16)|(2<<8)},{PS+220:32,PS+312:12|(1<<16)|(2<<8)},{PS+220:32,PS+312:13|(1<<16)|(2<<8)},{4244+432+52:1,4244+432+52+12:123},{4244+432+52:0}]:
32
+ m=weapon_config(seed(raw,h),raw,h);m.update({PS+312:1|(2<<8),FRAME+20:8,PS+44:0});m.update(changes);p=mode_switch(ins,m)[1];p['supplied_changes']={str(k):v for k,v in changes.items()};modes.append(p)
33
+ for buttons,flags,health,timer in itertools.product([0,1,8,128,136],[0,256],[0,100],[0,1,16,17]):
34
+ m=weapon_config(seed(raw,h),raw,h);m.update({PS+312:22|(2<<8),FRAME+20:buttons,PS+12:flags,PS+208:health,PS+44:timer,PS+220:32,PS+188:1000,PS+196:15,35023932:16,4244+432*22+28:2});prefix.append(weapon_prefix(ins,m)[1])
35
+ for ident,mode,mag,buttons,flags in [(11,1,2,9,32),(1,1,1,0,32),(1,0,1,8,0),(22,0,2,9,16)]:
36
+ m=weapon_config(seed(raw,h),raw,h);m.update({PS+312:ident|(mode<<16)|(mag<<8),FRAME+20:buttons,PS+220:flags,PS+44:0});prefix.append(weapon_prefix(ins,m)[1])
37
+ write_json(Path('docs/research/qvm-weapon-prefix.json'),{'source':q['source'],'sha256':q['sha256'],'mode_switch':modes,'prefix':prefix,'limits':'49327 complete;50746 only50747..50871 or actual early exit53046. Weapon selection48798 and all subsequent firing/reload/projectile behavior intentionally omitted. No full weapon simulation/live mode legality.'})
38
+ if __name__=='__main__':main()
scripts/test_qvm_weapon_prefix.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest,copy
2
+ from pathlib import Path
3
+ from test_qvm_torso_selector import signed
4
+ from test_qvm_packed_controls import resource_state,i32
5
+ from test_qvm_water_events import enqueue
6
+
7
+ def mode_state(p):
8
+ a=copy.deepcopy(p['initial']);flags=a['state220'];buttons=p['buttons'];packed=a['packed'];ident=packed&255;mode=(packed>>16)&255;mag=(packed>>8)&255
9
+ if flags&16:
10
+ if not buttons&8:a['state220']&=~16
11
+ return a
12
+ if signed(a['weapon_timer'])>0 or flags&(1024|8) or ident==0:return a
13
+ if ident in [11,12,13] and flags&32 and mode==1:return a
14
+ if ident==1 and mag==1:
15
+ if mode==0:return a
16
+ elif not buttons&8:return a
17
+ new=mode+1 if p['mode_config']['next_enabled'] else 0
18
+ if new==mode:return a
19
+ a['state220']|=16;a['weapon_timer']=p['mode_config']['next_timer'] if new else p['mode_config']['zero_timer'];a['packed']=(packed&~0xff0000)|((new&255)<<16);a['mode_changed']=1;a['old_mode']=mode;return a
20
+ class WeaponPrefixTests(unittest.TestCase):
21
+ @classmethod
22
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-weapon-prefix.json').read_text())
23
+ def test_complete_mode_gates_and_exact_writes(self):
24
+ self.assertEqual(len(self.r['mode_switch']),82)
25
+ for p in self.r['mode_switch']:self.assertEqual(p['final'],mode_state(p));self.assertEqual(p['execution']['calls'],[])
26
+ def test_prefix_order_and_early_exit(self):
27
+ self.assertEqual(len(self.r['prefix']),84)
28
+ for p in self.r['prefix']:
29
+ live=not p['initial']['flags']&256 and signed(p['initial']['health'])>0;self.assertEqual([c['target'] for c in p['execution']['calls']],[50250,50397,49327] if live else []);self.assertEqual(p['execution']['stop'],50872 if live else 53046)
30
+ def test_live_child_composition_and_exact_final(self):
31
+ for p in self.r['prefix']:
32
+ a=copy.deepcopy(p['initial']);buttons=p['buttons']
33
+ if not buttons&1:a['state220']&=~32
34
+ if not a['flags']&256 and signed(a['health'])>0:
35
+ edge,resource,mode=[x['effect'] for x in p['execution']['children']];self.assertEqual(edge['initial'],a);pressed=bool(buttons&128);event=pressed and not a['state220']&32768;a['state220']=(a['state220']|32768) if pressed else a['state220']&~32768;a['queue']=enqueue(a['queue'],[15] if event else []);self.assertEqual(edge['final'],a)
36
+ if signed(a['weapon_timer'])>0:a['weapon_timer']=i32(signed(a['weapon_timer'])-p['dt'])&0xffffffff
37
+ self.assertEqual(resource['initial'],a);a=resource_state(resource);self.assertEqual(resource['final'],a);self.assertEqual(mode['initial'],a);a=mode_state(mode)
38
+ self.assertEqual(p['final'],a)
39
+ def test_negative_timer_survives_prefix_and_scope(self):
40
+ self.assertTrue(any(p['initial']['weapon_timer']==1 and signed(p['final']['weapon_timer'])==-15 for p in self.r['prefix']));self.assertIn('Weapon selection48798',self.r['limits']);self.assertTrue(all(48798 not in [x['target'] for x in p['execution']['calls']] for p in self.r['prefix']))
41
+ if __name__=='__main__':unittest.main()