qvm: firing input gates
Browse files- docs/ASSET_PIPELINE_TODO.md +3 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +3 -0
- docs/RESEARCH_LEDGER.md +5 -0
- docs/REVERSE_ENGINEERING_TODO.md +3 -0
- docs/research/QVM_FIRE_GATE.md +11 -0
- docs/research/README.md +3 -0
- docs/research/qvm-fire-gate.json +0 -0
- scripts/analyze_qvm_fire_gate.py +21 -0
- scripts/qvm_firing_support.py +28 -0
- scripts/test_qvm_fire_gate.py +38 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -430,3 +430,6 @@ Research progress: RE-579 [report](research/QVM_WEAPON_PHASES.md); remaining nat
|
|
| 430 |
|
| 431 |
|
| 432 |
Research progress: RE-580 [report](research/QVM_WEAPON_RETURN_CHAINS.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
|
| 431 |
|
| 432 |
Research progress: RE-580 [report](research/QVM_WEAPON_RETURN_CHAINS.md); remaining native/content/runtime/platform scope stays open.
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
Research progress: RE-582 [report](research/QVM_FIRE_GATE.md); remaining native/content/runtime/platform scope stays open.
|
docs/FINDINGS.md
CHANGED
|
@@ -788,3 +788,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 788 |
|
| 789 |
|
| 790 |
**RE-580 — Pre-firing weapon return chains and actual main suffix handoff:** Recovered50746 branches now compose through51801, preserving the pre-switch mode local; actual53046 returns resume main torso/locomotion/water calls while51802 paths keep the caller suspended. [Evidence](research/QVM_WEAPON_RETURN_CHAINS.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 788 |
|
| 789 |
|
| 790 |
**RE-580 — Pre-firing weapon return chains and actual main suffix handoff:** Recovered50746 branches now compose through51801, preserving the pre-switch mode local; actual53046 returns resume main torso/locomotion/water calls while51802 paths keep the caller suspended. [Evidence](research/QVM_WEAPON_RETURN_CHAINS.md).
|
| 791 |
+
|
| 792 |
+
|
| 793 |
+
**RE-582 — Firing charge hold release and ordinary input gate:** 51802..52084 uses mode flag8 to availability-gate a state32/64 charge latch and event1, handles requested-ID exceptions11/12, and otherwise lets nonzero burst counters bypass absent button1. [Evidence](research/QVM_FIRE_GATE.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -746,3 +746,6 @@ The [RE-571–580 weapon batch](research/QVM_WEAPON_BATCH.md) closes bounded loo
|
|
| 746 |
|
| 747 |
|
| 748 |
Research progress: RE-580 [report](research/QVM_WEAPON_RETURN_CHAINS.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 746 |
|
| 747 |
|
| 748 |
Research progress: RE-580 [report](research/QVM_WEAPON_RETURN_CHAINS.md); remaining native/content/runtime/platform scope stays open.
|
| 749 |
+
|
| 750 |
+
|
| 751 |
+
Research progress: RE-582 [report](research/QVM_FIRE_GATE.md); remaining native/content/runtime/platform scope stays open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -598,3 +598,8 @@ Actual54444 reload/interaction order exposes timer interference before the weapo
|
|
| 598 |
Recovered50746 branches now compose through51801, preserving the pre-switch mode local; actual53046 returns resume main torso/locomotion/water calls while51802 paths keep the caller suspended. 16 independent source-order scenarios check every reload/interaction/prefix/selection/guard/ready/ammo/phase handoff, full final state, suspension versus resume and old-mode-local preservation. Full suite: 839 tests pass. Report regenerates byte-identically (1198369 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
| 599 |
|
| 600 |
RE-580 additional regression: all19 reports from RE-560–569 and RE-571–579 regenerate byte-identically after the explicit-byte-input and optional caller-memory/local-preservation additions. The new composition keeps the parent suspended at51802 rather than assuming a firing-body return.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 598 |
Recovered50746 branches now compose through51801, preserving the pre-switch mode local; actual53046 returns resume main torso/locomotion/water calls while51802 paths keep the caller suspended. 16 independent source-order scenarios check every reload/interaction/prefix/selection/guard/ready/ammo/phase handoff, full final state, suspension versus resume and old-mode-local preservation. Full suite: 839 tests pass. Report regenerates byte-identically (1198369 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
| 599 |
|
| 600 |
RE-580 additional regression: all19 reports from RE-560–569 and RE-571–579 regenerate byte-identically after the explicit-byte-input and optional caller-memory/local-preservation additions. The new composition keeps the parent suspended at51802 rather than assuming a firing-body return.
|
| 601 |
+
|
| 602 |
+
|
| 603 |
+
## 2026-10-20 — RE-582 Firing charge hold release and ordinary input gate (separate task commit)
|
| 604 |
+
|
| 605 |
+
51802..52084 uses mode flag8 to availability-gate a state32/64 charge latch and event1, handles requested-ID exceptions11/12, and otherwise lets nonzero burst counters bypass absent button1. 203 independent fixtures check charge start/hold/release/cancel, flags4 availability, command mismatch exceptions and signed nonzero counters without altering ammo or published request fields. Full suite: 843 tests pass. Report regenerates byte-identically (1209597 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
|
@@ -787,3 +787,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 787 |
|
| 788 |
|
| 789 |
- [x] RE-580 Pre-firing weapon return chains and actual main suffix handoff ([report](research/QVM_WEAPON_RETURN_CHAINS.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 787 |
|
| 788 |
|
| 789 |
- [x] RE-580 Pre-firing weapon return chains and actual main suffix handoff ([report](research/QVM_WEAPON_RETURN_CHAINS.md)).
|
| 790 |
+
|
| 791 |
+
|
| 792 |
+
- [x] RE-582 Firing charge hold release and ordinary input gate ([report](research/QVM_FIRE_GATE.md)).
|
docs/research/QVM_FIRE_GATE.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Firing charge hold release and ordinary input gate
|
| 2 |
+
|
| 3 |
+
RE-582. Reproduce with `python3 scripts/analyze_qvm_fire_gate.py`; check with `python3 scripts/test_qvm_fire_gate.py`. The [machine report](qvm-fire-gate.json) retains source identity and bounded fixtures.
|
| 4 |
+
|
| 5 |
+
Complete gate51802..52084 stops at52085 or actual return53046. Mode flag8 calls actual50309. Unavailable resets weapon timer/state0. A matching held button1 (IDs11/12 must match command weapon) without state64 sets32/64, loads weapon record4056 timer and emits1. A latched hold returns while32 remains set; when32 clears it clears64 and proceeds. Release can clear64 and proceed, but an unlatched release resets state/timer and exits. ID11 has an additional held-command-mismatch cancellation; ID12 follows the observed release route instead.
|
| 6 |
+
|
| 7 |
+
Without flag8, a zero PS204 counter requires button1 and sets32; any nonzero counter bypasses this gate, including explicit synthetic negative values. Availability50309 reads the packed mode, while this parent uses its preserved local mode. Reports distinguish both flags.203 finite fixtures exercise the literal predicates, not inferred legal gameplay inputs.
|
| 8 |
+
|
| 9 |
+
No magazine debit, actual shot, projectile/hitscan or native event dispatch occurs in this slice. The next tasks follow availability failure and ammo debit before firing-pose/request publication.
|
| 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
|
@@ -347,3 +347,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 347 |
|
| 348 |
|
| 349 |
[RE-580: Pre-firing weapon return chains and actual main suffix handoff](QVM_WEAPON_RETURN_CHAINS.md). Reproduce with `python3 scripts/analyze_qvm_weapon_return_chains.py`; check with `python3 scripts/test_qvm_weapon_return_chains.py`.
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
|
| 348 |
|
| 349 |
[RE-580: Pre-firing weapon return chains and actual main suffix handoff](QVM_WEAPON_RETURN_CHAINS.md). Reproduce with `python3 scripts/analyze_qvm_weapon_return_chains.py`; check with `python3 scripts/test_qvm_weapon_return_chains.py`.
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
[RE-582: Firing charge hold release and ordinary input gate](QVM_FIRE_GATE.md). Reproduce with `python3 scripts/analyze_qvm_fire_gate.py`; check with `python3 scripts/test_qvm_fire_gate.py`.
|
docs/research/qvm-fire-gate.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_qvm_fire_gate.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""51802..52084 charge/hold/release versus ordinary firing gate."""
|
| 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_acceleration import PS,FRAME
|
| 8 |
+
from analyze_qvm import write_json
|
| 9 |
+
|
| 10 |
+
def gate(ins,m,cmd=(0,0,0),preserve_locals=False):return segment(ins,m,51802,[52085],cmd,preserve_locals)
|
| 11 |
+
|
| 12 |
+
def main():
|
| 13 |
+
q,raw,h,ins=source();rows=[]
|
| 14 |
+
for ident,flags,mag,special,button in itertools.product([1,11,12,22],[0,8,12],[0,1],[0,64,32,96],[0,1]):
|
| 15 |
+
m=seed(raw,h,ident,mag);m.update({4244+432*ident+4:flags,PS+220:special,FRAME+20:button});rows.append(gate(ins,m)[1])
|
| 16 |
+
for ident,button,state in itertools.product([11,12],[0,1],[0,64]):
|
| 17 |
+
m=seed(raw,h,ident,1);m.update({4244+432*ident+4:8,PS+220:state,FRAME+20:button,FRAME+24:22});rows.append(gate(ins,m)[1])
|
| 18 |
+
for count in [-1,1,2]:
|
| 19 |
+
m=seed(raw,h);m.update({4244+432*22+4:0,PS+204:count,FRAME+20:0});rows.append(gate(ins,m)[1])
|
| 20 |
+
write_json(Path('docs/research/qvm-fire-gate.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Finite51802..52084; actual50309/event children, supplied mode flags and entry state. Stops52085 or53046; charge naming provisional and no actual firing/native dispatch.'})
|
| 21 |
+
if __name__=='__main__':main()
|
scripts/qvm_firing_support.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Finite firing segment fixtures; parent locals and published request are explicit."""
|
| 2 |
+
from qvm_research_support import constants,execute
|
| 3 |
+
from qvm_weapon_support import weapon_seed,weapon_state
|
| 4 |
+
from qvm_suffix_support import handlers
|
| 5 |
+
from analyze_qvm_weapon_prefix import LOCAL
|
| 6 |
+
from analyze_qvm_packed_controls import control
|
| 7 |
+
from analyze_qvm_acceleration import PS,FRAME
|
| 8 |
+
|
| 9 |
+
def seed(raw,h,ident=22,mag=2,mode=0):
|
| 10 |
+
m=weapon_seed(raw,h,desired=ident);m.update(constants(raw,h,range(21316,21348,4)));m.update({PS+312:ident|(mag<<8)|(mode<<16)|(3<<24),PS+204:0,PS+188:0,PS+196:0,PS+216:777,FRAME+260:91,FRAME+264:92,FRAME+20:1,FRAME+36:0,PS+68:8});return m
|
| 11 |
+
|
| 12 |
+
def state(m):return {**weapon_state(m),'field216':m[PS+216]&0xffffffff,'request260':m[FRAME+260],'request264':m[FRAME+264]}
|
| 13 |
+
|
| 14 |
+
def config(m):
|
| 15 |
+
ident=m[LOCAL+12];mode=m[LOCAL+16];ptr=4244+432*ident+52*mode
|
| 16 |
+
packed=m[PS+312+4*m[PS+144]];live=4244+432*(packed&255)+52*((packed>>16)&255)
|
| 17 |
+
return {'packed_flags':m[live+4]&0xffffffff,'charge_timer':m[4056+432*ident]&0xffffffff,**{k:m[ptr+o]&0xffffffff for k,o in [('flags',4),('delay',8),('resource_delay',24),('burst_delay',44),('burst_count',48)]},'capacity':m[4052+432*ident]&0xffffffff,'class':m[4032+432*ident],'tag':m[4196+432*ident]}
|
| 18 |
+
|
| 19 |
+
def fire_handlers():
|
| 20 |
+
hs=handlers()
|
| 21 |
+
def available(i,m,a,c):m,r=control(i,m,50309,c);return m,c,r['execution']['return'],r
|
| 22 |
+
hs[50309]=available;return hs
|
| 23 |
+
|
| 24 |
+
def segment(ins,m,start,boundaries,cmd=(0,0,0),preserve_locals=False):
|
| 25 |
+
if not preserve_locals:m[LOCAL+12]=m[PS+312+4*m[PS+144]]&255;m[LOCAL+16]=(m[PS+312+4*m[PS+144]]>>16)&255
|
| 26 |
+
before=state(m);cfg=config(m);lid=m[LOCAL+12];mode=m[LOCAL+16]
|
| 27 |
+
m,c,r=execute(ins,start,53046,m,cmd,LOCAL,fire_handlers(),boundaries=boundaries,byte_inputs={FRAME+24:m[FRAME+24]&255})
|
| 28 |
+
return m,{'initial':before,'buttons':m[FRAME+20],'kind':m[PS+4],'local_id':lid,'local_mode':mode,'config':cfg,'execution':r,'final':state(m)}
|
scripts/test_qvm_fire_gate.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json,unittest,copy
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_torso_selector import signed
|
| 4 |
+
from test_qvm_water_events import enqueue
|
| 5 |
+
|
| 6 |
+
def gate_state(p):
|
| 7 |
+
a=copy.deepcopy(p['initial']);flags=p['config']['flags'];pressed=bool(p['buttons']&1);ident=p['local_id'];stop=52085
|
| 8 |
+
if flags&8:
|
| 9 |
+
available=bool((a['packed']>>8)&255 or p['config']['packed_flags']&4)
|
| 10 |
+
if not available:a['weapon_timer']=a['weapon_state']=0;return a,53046
|
| 11 |
+
if pressed and (ident not in [11,12] or ident==a['command_weapon']):
|
| 12 |
+
if not a['state220']&64:a['state220']|=96;a['weapon_timer']=p['config']['charge_timer'];a['queue']=enqueue(a['queue'],[1]);return a,53046
|
| 13 |
+
if a['state220']&32:return a,53046
|
| 14 |
+
a['state220']&=~64
|
| 15 |
+
elif not a['state220']&64 or (ident==11 and pressed and ident!=a['command_weapon']):a['weapon_timer']=a['weapon_state']=0;return a,53046
|
| 16 |
+
else:a['state220']&=~64
|
| 17 |
+
elif a['counter204']==0:
|
| 18 |
+
if not pressed:a['weapon_timer']=a['weapon_state']=0;return a,53046
|
| 19 |
+
a['state220']|=32
|
| 20 |
+
return a,stop
|
| 21 |
+
class FireGateTests(unittest.TestCase):
|
| 22 |
+
@classmethod
|
| 23 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-fire-gate.json').read_text())
|
| 24 |
+
def test_complete_gate_state(self):
|
| 25 |
+
self.assertEqual(len(self.r['probes']),203)
|
| 26 |
+
for p in self.r['probes']:a,stop=gate_state(p);self.assertEqual(p['final'],a);self.assertEqual(p['execution']['stop'],stop)
|
| 27 |
+
def test_actual_availability_and_event_calls(self):
|
| 28 |
+
for p in self.r['probes']:
|
| 29 |
+
expected=[50309] if p['config']['flags']&8 else []
|
| 30 |
+
if p['final']['queue']!=p['initial']['queue']:expected.append(39238)
|
| 31 |
+
self.assertEqual([c['target'] for c in p['execution']['calls']],expected)
|
| 32 |
+
def test_ordinary_nonzero_counter_bypasses_button_release(self):
|
| 33 |
+
for p in self.r['probes']:
|
| 34 |
+
if not p['config']['flags']&8 and p['initial']['counter204']!=0:self.assertEqual(p['execution']['stop'],52085)
|
| 35 |
+
def test_magazine_and_request_fields_untouched(self):
|
| 36 |
+
for p in self.r['probes']:
|
| 37 |
+
for k in ['weapons','packed','request260','request264','resource']:self.assertEqual(p['initial'][k],p['final'][k])
|
| 38 |
+
if __name__=='__main__':unittest.main()
|