nmarshall commited on
Commit
190404e
·
1 Parent(s): cdbaa1d

qvm: firing magazine debit

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -436,3 +436,6 @@ Research progress: RE-582 [report](research/QVM_FIRE_GATE.md); remaining native/
436
 
437
 
438
  Research progress: RE-583 [report](research/QVM_FIRE_AVAILABILITY.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
436
 
437
 
438
  Research progress: RE-583 [report](research/QVM_FIRE_AVAILABILITY.md); remaining native/content/runtime/platform scope stays open.
439
+
440
+
441
+ Research progress: RE-584 [report](research/QVM_FIRE_AMMO.md); remaining native/content/runtime/platform scope stays open.
docs/FINDINGS.md CHANGED
@@ -794,3 +794,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
794
 
795
 
796
  **RE-583 — Availability failure event and timer exception:** 52085..52153 exits state1 before querying availability; unavailable ID27 returns unchanged, while other unavailable weapons zero counter204, queue29 and add500 to the timer. [Evidence](research/QVM_FIRE_AVAILABILITY.md).
 
 
 
 
794
 
795
 
796
  **RE-583 — Availability failure event and timer exception:** 52085..52153 exits state1 before querying availability; unavailable ID27 returns unchanged, while other unavailable weapons zero counter204, queue29 and add500 to the timer. [Evidence](research/QVM_FIRE_AVAILABILITY.md).
797
+
798
+
799
+ **RE-584 — Firing magazine debit and preserved mode flag:** 52154..52252 decrements only a nonzero selected magazine byte unless preserved local-mode flags4 suppress it; ID/mode/reserve and other slots remain unchanged. [Evidence](research/QVM_FIRE_AMMO.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -752,3 +752,6 @@ Research progress: RE-582 [report](research/QVM_FIRE_GATE.md); remaining native/
752
 
753
 
754
  Research progress: RE-583 [report](research/QVM_FIRE_AVAILABILITY.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
752
 
753
 
754
  Research progress: RE-583 [report](research/QVM_FIRE_AVAILABILITY.md); remaining native/content/runtime/platform scope stays open.
755
+
756
+
757
+ Research progress: RE-584 [report](research/QVM_FIRE_AMMO.md); remaining native/content/runtime/platform scope stays open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -608,3 +608,8 @@ RE-580 additional regression: all19 reports from RE-560–569 and RE-571–579 r
608
  ## 2026-10-20 — RE-583 Availability failure event and timer exception (separate task commit)
609
 
610
  52085..52153 exits state1 before querying availability; unavailable ID27 returns unchanged, while other unavailable weapons zero counter204, queue29 and add500 to the timer. 110 probes check actual50309/39238 order, flag4 versus magazine, state1 priority, ID27, signed timer overflow and queue wrap without ammo/request writes. Full suite: 847 tests pass. Report regenerates byte-identically (661634 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
 
 
 
 
 
 
608
  ## 2026-10-20 — RE-583 Availability failure event and timer exception (separate task commit)
609
 
610
  52085..52153 exits state1 before querying availability; unavailable ID27 returns unchanged, while other unavailable weapons zero counter204, queue29 and add500 to the timer. 110 probes check actual50309/39238 order, flag4 versus magazine, state1 priority, ID27, signed timer overflow and queue wrap without ammo/request writes. Full suite: 847 tests pass. Report regenerates byte-identically (661634 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
611
+
612
+
613
+ ## 2026-10-20 — RE-584 Firing magazine debit and preserved mode flag (separate task commit)
614
+
615
+ 52154..52252 decrements only a nonzero selected magazine byte unless preserved local-mode flags4 suppress it; ID/mode/reserve and other slots remain unchanged. 100 probes check slot0/15, magazines0/1/2/255, IDs1/22/27 and deliberately differing local versus packed modes with independent full-state oracles. Full suite: 851 tests pass. Report regenerates byte-identically (333406 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
@@ -793,3 +793,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
793
 
794
 
795
  - [x] RE-583 Availability failure event and timer exception ([report](research/QVM_FIRE_AVAILABILITY.md)).
 
 
 
 
793
 
794
 
795
  - [x] RE-583 Availability failure event and timer exception ([report](research/QVM_FIRE_AVAILABILITY.md)).
796
+
797
+
798
+ - [x] RE-584 Firing magazine debit and preserved mode flag ([report](research/QVM_FIRE_AMMO.md)).
docs/research/QVM_FIRE_AMMO.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Firing magazine debit and preserved mode flag
2
+
3
+ RE-584. Reproduce with `python3 scripts/analyze_qvm_fire_ammo.py`; check with `python3 scripts/test_qvm_fire_ammo.py`. The [machine report](qvm-fire-ammo.json) retains source identity and bounded fixtures.
4
+
5
+ On reaching52154, the selected packed magazine bits8..15 are read. Zero skips mutation. Otherwise preserved parent localID/localMode index the52-byte mode record; flag4 skips debit. Without it, magazine becomes(old−1)&255, retaining all other bytes and all other weapon slots. No underflow from magazine0 occurs.
6
+
7
+ 100 fixtures expose the distinction from50309 availability: availability reads packed mode, while debit uses the parent's preserved pre-switch local mode. Reconstructing that local from packed state would produce different results in explicit mismatch fixtures. The slice itself has no event/animation/native call and does not publish a shot request.
8
+
9
+ Standalone empty-magazine fixtures do not imply an earlier availability success. Next: decode the source's firing animation priority, request publication, burst counter, resource and final delay, then compose all reached branches.
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
@@ -353,3 +353,6 @@ The extension collector adds five focused tests for native tail calls, command-r
353
 
354
 
355
  [RE-583: Availability failure event and timer exception](QVM_FIRE_AVAILABILITY.md). Reproduce with `python3 scripts/analyze_qvm_fire_availability.py`; check with `python3 scripts/test_qvm_fire_availability.py`.
 
 
 
 
353
 
354
 
355
  [RE-583: Availability failure event and timer exception](QVM_FIRE_AVAILABILITY.md). Reproduce with `python3 scripts/analyze_qvm_fire_availability.py`; check with `python3 scripts/test_qvm_fire_availability.py`.
356
+
357
+
358
+ [RE-584: Firing magazine debit and preserved mode flag](QVM_FIRE_AMMO.md). Reproduce with `python3 scripts/analyze_qvm_fire_ammo.py`; check with `python3 scripts/test_qvm_fire_ammo.py`.
docs/research/qvm-fire-ammo.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_fire_ammo.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """52154..52252 selected magazine debit and preserved-local flag4 exception."""
3
+ import itertools
4
+ from pathlib import Path
5
+ from qvm_research_support import source
6
+ from qvm_firing_support import seed,segment
7
+ from analyze_qvm_weapon_prefix import LOCAL
8
+ from analyze_qvm_acceleration import PS
9
+ from analyze_qvm import write_json
10
+
11
+ def ammo(ins,m,cmd=(0,0,0),preserve_locals=False):return segment(ins,m,52154,[52253],cmd,preserve_locals)
12
+
13
+ def main():
14
+ q,raw,h,ins=source();rows=[]
15
+ for ident,mag,flags,mode,slot in itertools.product([1,22,27],[0,1,2,255],[0,4],[0,1],[0,15]):
16
+ m=seed(raw,h,ident,mag,mode);m.update({4244+432*ident+52*mode+4:flags,PS+144:slot});m[PS+312+4*slot]=m[PS+312];rows.append(ammo(ins,m)[1])
17
+ for packed_mode,local_mode in itertools.product([0,1],[0,1]):
18
+ m=seed(raw,h,22,2,packed_mode);m.update({4244+432*22+4:0,4244+432*22+52+4:4,LOCAL+12:22,LOCAL+16:local_mode});rows.append(ammo(ins,m,preserve_locals=True)[1])
19
+ write_json(Path('docs/research/qvm-fire-ammo.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Finite52154..52252 with supplied selected words/locals; no availability or preceding gate implied. Local mode flag4 differs from packed-mode availability. No request publication/native shot.'})
20
+ if __name__=='__main__':main()
scripts/test_qvm_fire_ammo.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest,copy
2
+ from pathlib import Path
3
+
4
+ def ammo_state(p):
5
+ a=copy.deepcopy(p['initial']);mag=(a['packed']>>8)&255
6
+ if mag and not p['config']['flags']&4:a['packed']=(a['packed']&~65280)|(((mag-1)&255)<<8);a['weapons'][a['slot']]=a['packed']
7
+ return a,52253
8
+ class FireAmmoTests(unittest.TestCase):
9
+ @classmethod
10
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-fire-ammo.json').read_text())
11
+ def test_exact_magazine_debit_and_full_retained_state(self):
12
+ self.assertEqual(len(self.r['probes']),100)
13
+ for p in self.r['probes']:self.assertEqual(p['final'],ammo_state(p)[0]);self.assertEqual(p['execution']['stop'],52253)
14
+ def test_id_mode_reserve_and_unselected_records_preserved(self):
15
+ for p in self.r['probes']:
16
+ self.assertEqual(p['initial']['packed']&~65280,p['final']['packed']&~65280)
17
+ for j,(a,b) in enumerate(zip(p['initial']['weapons'],p['final']['weapons'])):
18
+ if j!=p['initial']['slot']:self.assertEqual(a,b)
19
+ def test_local_mode_not_packed_mode_controls_debit(self):
20
+ rows=[p for p in self.r['probes'] if p['config']['flags']!=p['config']['packed_flags']];self.assertTrue(rows)
21
+ for p in rows:self.assertEqual(p['final']['packed']>>8&255,2 if p['config']['flags']&4 else 1)
22
+ def test_zero_magazine_does_not_wrap_and_no_calls(self):
23
+ for p in self.r['probes']:
24
+ self.assertEqual(p['execution']['calls'],[])
25
+ if not p['initial']['packed']&65280:self.assertEqual(p['initial'],p['final'])
26
+ if __name__=='__main__':unittest.main()