nmarshall commited on
Commit
b8c9238
·
1 Parent(s): 4c9911c

qvm: final firing delay

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -451,3 +451,6 @@ Research progress: RE-587 [report](research/QVM_FIRE_COUNTER.md); remaining nati
451
 
452
 
453
  Research progress: RE-588 [report](research/QVM_FIRE_RESOURCE.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
451
 
452
 
453
  Research progress: RE-588 [report](research/QVM_FIRE_RESOURCE.md); remaining native/content/runtime/platform scope stays open.
454
+
455
+
456
+ Research progress: RE-589 [report](research/QVM_FIRE_DELAY.md); remaining native/content/runtime/platform scope stays open.
docs/FINDINGS.md CHANGED
@@ -809,3 +809,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
809
 
810
 
811
  **RE-588 — Firing resource snapshot capacity arithmetic and reset:** 52873..52988 snapshots resource188 into PS216, adds truncated30000/capacity with only an upper30000 clamp and sets a mode cooldown; mode flag16 instead zeros all three fields. [Evidence](research/QVM_FIRE_RESOURCE.md).
 
 
 
 
809
 
810
 
811
  **RE-588 — Firing resource snapshot capacity arithmetic and reset:** 52873..52988 snapshots resource188 into PS216, adds truncated30000/capacity with only an upper30000 clamp and sets a mode cooldown; mode flag16 instead zeros all three fields. [Evidence](research/QVM_FIRE_RESOURCE.md).
812
+
813
+
814
+ **RE-589 — Final firing delay and last-round exception:** 52989..53045 adds the retained selected delay to PS44 only if the post-debit magazine is nonzero or selectedID27; other empty magazines return without adding it. [Evidence](research/QVM_FIRE_DELAY.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -767,3 +767,6 @@ Research progress: RE-587 [report](research/QVM_FIRE_COUNTER.md); remaining nati
767
 
768
 
769
  Research progress: RE-588 [report](research/QVM_FIRE_RESOURCE.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
767
 
768
 
769
  Research progress: RE-588 [report](research/QVM_FIRE_RESOURCE.md); remaining native/content/runtime/platform scope stays open.
770
+
771
+
772
+ Research progress: RE-589 [report](research/QVM_FIRE_DELAY.md); remaining native/content/runtime/platform scope stays open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -633,3 +633,8 @@ RE-580 additional regression: all19 reports from RE-560–569 and RE-571–579 r
633
  ## 2026-10-20 — RE-588 Firing resource snapshot capacity arithmetic and reset (separate task commit)
634
 
635
  52873..52988 snapshots resource188 into PS216, adds truncated30000/capacity with only an upper30000 clamp and sets a mode cooldown; mode flag16 instead zeros all three fields. 101 independent probes check integer quotient, signed wrap, negative/near-limit resources, capacity7/32/255, flag16 and zero-capacity bypass; an unguarded zero divisor is explicitly rejected. Full suite: 867 tests pass. Report regenerates byte-identically (337475 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
 
 
 
 
 
 
633
  ## 2026-10-20 — RE-588 Firing resource snapshot capacity arithmetic and reset (separate task commit)
634
 
635
  52873..52988 snapshots resource188 into PS216, adds truncated30000/capacity with only an upper30000 clamp and sets a mode cooldown; mode flag16 instead zeros all three fields. 101 independent probes check integer quotient, signed wrap, negative/near-limit resources, capacity7/32/255, flag16 and zero-capacity bypass; an unguarded zero divisor is explicitly rejected. Full suite: 867 tests pass. Report regenerates byte-identically (337475 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
636
+
637
+
638
+ ## 2026-10-20 — RE-589 Final firing delay and last-round exception (separate task commit)
639
+
640
+ 52989..53045 adds the retained selected delay to PS44 only if the post-debit magazine is nonzero or selectedID27; other empty magazines return without adding it. 108 independent timer/delay/ID/magazine probes plus actual debit handoffs for IDs22/27 check wrap and last-round timer behavior without reloading the delay record. Full suite: 871 tests pass. Report regenerates byte-identically (376037 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
@@ -808,3 +808,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
808
 
809
 
810
  - [x] RE-588 Firing resource snapshot capacity arithmetic and reset ([report](research/QVM_FIRE_RESOURCE.md)).
 
 
 
 
808
 
809
 
810
  - [x] RE-588 Firing resource snapshot capacity arithmetic and reset ([report](research/QVM_FIRE_RESOURCE.md)).
811
+
812
+
813
+ - [x] RE-589 Final firing delay and last-round exception ([report](research/QVM_FIRE_DELAY.md)).
docs/research/QVM_FIRE_DELAY.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Final firing delay and last-round exception
2
+
3
+ RE-589. Reproduce with `python3 scripts/analyze_qvm_fire_delay.py`; check with `python3 scripts/test_qvm_fire_delay.py`. The [machine report](qvm-fire-delay.json) retains source identity and bounded fixtures.
4
+
5
+ The final predicate reads the current selected magazine after debit. If nonzero it adds parent local20 to PS44. If zero, only ID27 still adds it. Other empty magazines branch directly to the void epilogue53046. Addition uses32-bit integer semantics without a clamp or timer replacement.
6
+
7
+ 108 finite probes include negative/supplied delays and overflow timers, proving the delay is the retained local selected earlier rather than re-read from a mode record. Two explicit debit→delay handoffs start with magazine1: both debit to0, but ID22 retains timer10 while ID27 becomes110 with supplied selected delay100. Intermediate animation/resource slices are explicitly omitted in these two focused handoffs.
8
+
9
+ This closes the final numerical weapon-body slice. It does not validate native firing or gameplay reachability of standalone values. Next: compose all recovered50746 branches in one call, preserve locals and publish actual per-frame requests to their outer event consumer.
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
@@ -368,3 +368,6 @@ The extension collector adds five focused tests for native tail calls, command-r
368
 
369
 
370
  [RE-588: Firing resource snapshot capacity arithmetic and reset](QVM_FIRE_RESOURCE.md). Reproduce with `python3 scripts/analyze_qvm_fire_resource.py`; check with `python3 scripts/test_qvm_fire_resource.py`.
 
 
 
 
368
 
369
 
370
  [RE-588: Firing resource snapshot capacity arithmetic and reset](QVM_FIRE_RESOURCE.md). Reproduce with `python3 scripts/analyze_qvm_fire_resource.py`; check with `python3 scripts/test_qvm_fire_resource.py`.
371
+
372
+
373
+ [RE-589: Final firing delay and last-round exception](QVM_FIRE_DELAY.md). Reproduce with `python3 scripts/analyze_qvm_fire_delay.py`; check with `python3 scripts/test_qvm_fire_delay.py`.
docs/research/qvm-fire-delay.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_fire_delay.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """52989..53045 final post-debit magazine predicate and selected delay addition."""
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_fire_ammo import ammo
9
+ from analyze_qvm_acceleration import PS
10
+ from analyze_qvm import write_json
11
+
12
+ def delay(ins,m,cmd=(0,0,0),preserve_locals=False,selected_delay=None):
13
+ if selected_delay is not None:m[LOCAL+20]=selected_delay
14
+ selected=m[LOCAL+20]&0xffffffff;m,p=segment(ins,m,52989,[],cmd,preserve_locals);p['selected_delay']=selected;return m,p
15
+
16
+ def main():
17
+ q,raw,h,ins=source();rows=[]
18
+ for ident,mag,timer,selected in itertools.product([1,22,27],[0,1,2],[-1,0,123,0x7fffffff],[-100,0,100]):
19
+ m=seed(raw,h,ident,mag);m[PS+44]=timer;rows.append(delay(ins,m,selected_delay=selected)[1])
20
+ chains=[]
21
+ for ident in [22,27]:
22
+ m=seed(raw,h,ident,1);m[PS+44]=10;m,a=ammo(ins,m);m,b=delay(ins,m,selected_delay=100);chains.append({'ammo':a,'delay':b})
23
+ write_json(Path('docs/research/qvm-fire-delay.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'debit_then_delay':chains,'limits':'Finite52989..53045; selected delay supplied except retained parent-local composition. Last-round chains omit intermediate pose/resource slices explicitly. No native shot or full tick executes.'})
24
+ if __name__=='__main__':main()
scripts/test_qvm_fire_delay.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest,copy
2
+ from pathlib import Path
3
+ from test_qvm_fire_ammo import ammo_state
4
+
5
+ def delay_state(p):
6
+ a=copy.deepcopy(p['initial'])
7
+ if a['packed']&65280 or a['packed']&255==27:a['weapon_timer']=(a['weapon_timer']+p['selected_delay'])&0xffffffff
8
+ return a,53046
9
+ class FireDelayTests(unittest.TestCase):
10
+ @classmethod
11
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-fire-delay.json').read_text())
12
+ def test_exact_post_debit_timer_predicate_and_wrap(self):
13
+ self.assertEqual(len(self.r['probes']),108)
14
+ for p in self.r['probes']:self.assertEqual(p['final'],delay_state(p)[0])
15
+ def test_last_round_vs_id27_actual_debit_handoff(self):
16
+ a,b=self.r['debit_then_delay'];self.assertEqual(a['delay']['final']['weapon_timer'],10);self.assertEqual(b['delay']['final']['weapon_timer'],110)
17
+ for p in self.r['debit_then_delay']:self.assertEqual(p['ammo']['final'],ammo_state(p['ammo'])[0]);self.assertEqual(p['ammo']['final'],p['delay']['initial']);self.assertEqual(p['delay']['final']['packed']>>8&255,0)
18
+ def test_only_timer_can_mutate_and_no_calls(self):
19
+ for p in self.r['probes']:self.assertEqual(p['execution']['calls'],[]);a=dict(p['initial']);a['weapon_timer']=p['final']['weapon_timer'];self.assertEqual(a,p['final'])
20
+ def test_selected_local_delay_not_reloaded_from_record(self):self.assertTrue(any(p['selected_delay']!=p['config']['delay'] and p['initial']['packed']&65280 for p in self.r['probes']))
21
+ if __name__=='__main__':unittest.main()