docs: PPC-hit CRTC sync-distortion finding + phase-14 port spec
Disassembly of BTL4OPT.EXE (2026-08-06) recovered the PPC's authentic
secondary-display effect: an EnergyDamageType (==4) hit calls the gauge
renderer's SpecialEffect(scrambleVideo, damageType*0.2f), which detunes the
VGA CRTC Horizontal Total by -9 for 0.8s -- every secondary cockpit display
loses horizontal sync ("looks like the CRTs are being degaussed"), the main
VPX view is untouched. damageType 4 is authored on exactly the 14 PPC/ERPPC
records, so the branch is structurally PPC-exclusive.
- phases/phase-14-ppc-sync-distortion.md: the port spec (trigger + visual +
fidelity constraints + verification).
- context/gauges-hud.md: full disasm chain + the FlashPalette non-confusion.
- context/combat-damage.md: the damageType==4 branch in the damage handler.
NOT YET IMPLEMENTED -- this commit is the spec; the effect is the next work.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -637,6 +637,16 @@ Benches: `scratchpad/night12/scorekill.sh` (cross-node kill: killer `kills=1 awa
|
||||
victim respawns, death #1 single-cycle) + `scoreself.sh` (#134 negation). Collision-death tail
|
||||
fallthrough is inspection-tier [T3] — shares the benched tail code; field wall-deaths exercise it.
|
||||
|
||||
**The handler's OTHER damageType branch — `damageType==4` (Energy) = the PPC cockpit-sync glitch
|
||||
[T1, 2026-08-06].** Between the collision divert and the burst loop sits a second type test
|
||||
@`0x4a03f3`: `cmp [esi+0x2c],4 / jne 0x4a0423`. On a match it calls the gauge renderer's vtable
|
||||
slot 19 with `((float)damageType × 0.2, 0)` = `(0.8f, 0)`, which detunes the **VGA CRTC Horizontal
|
||||
Total by −9** for 0.8 s — every secondary cockpit display loses horizontal sync, the main VPX view
|
||||
is untouched. `EnergyDamageType` is authored on **exactly the 14 PPC/ERPPC records and nothing
|
||||
else**, so this is structurally PPC-exclusive. It fires **once per damage message** (outside the
|
||||
burst loop). Full chain + addresses: [[gauges-hud]] §"PPC HIT = a deliberate CRTC horizontal-sync
|
||||
DETUNE"; port spec: `phases/phase-14-ppc-sync-distortion.md`. **Not yet implemented.**
|
||||
|
||||
## (HISTORICAL — the gap as found 2026-07-29, superseded above) [T1]
|
||||
The authored crit machinery exists and is reconstructed — `Mech__DamageZone::CriticalHit @0049ccc4`
|
||||
(half the damage to armour, half to ONE critical subsystem chosen by `criticalWeight`, capped by
|
||||
|
||||
@@ -13,6 +13,7 @@ open_questions:
|
||||
- "Upper-MFD PRESET pages RESOLVED 2026-07-19 (Gitea #9): SetPresetMode table @0051dbf0 re-decoded (little-endian -> ModeMFD bits 0-14), per-MFD pod button banks identified from the .CTL dump, desktop J/K/L cycle wired"
|
||||
- "Always-active msg-4 records IDENTIFIED 2026-07-20 (glass input audit): 0x2C = Reservoir InjectCoolant (the flush button), 0x2F/0x2E/0x2D/0x2B/0x2A/0x29 = Condenser1-6 MoveValve, 0x1A-0x1D = GeneratorA-D ToggleGeneratorOnOff (@0050fb90; wired 2026-07-25, powersub.cpp); plus 0x13 = Mech DuckRequest (CROUCH -- COMPLETE 2026-08-06, [[locomotion]]), 0x28 = Mech BalanceCoolant, 0x12/0x14 = ThermalSight/Searchlight toggles (searchlight visuals done 2026-08-05) -- see pod-hardware.md + docs/GLASS_COCKPIT.md; statuses re-swept 2026-08-06"
|
||||
- "MP DEATHS resolved 2026-07-12 (observed-death tally + display clamp); remaining: verify multi-death tallies stay in sync across a long session (GAUGE_COMPOSITE.md)"
|
||||
- "PPC `scrambleVideo` NOT IMPLEMENTED (found 2026-08-06 [T1]): a PPC hit scrambles every secondary display for 0.8 s via a CRTC Horizontal-Total detune. Engine half already present under the original names (SpecialEffect/scrambleVideo, L4GREND.cpp:806+832, called per frame at :370); missing = the trigger in Mech::TakeDamageMessageHandler (damageType==4) and the visual (SVGA16::FunkyVideo is STUBBED, L4VB16.cpp:6358). Also note our SpecialEffect re-arms the timeout on a second hit — the binary latches and does NOT stack. Spec: phases/phase-14-ppc-sync-distortion.md"
|
||||
---
|
||||
|
||||
# Cockpit Gauges / MFD HUD
|
||||
@@ -755,6 +756,86 @@ pooling fix) — an alarm that cannot acquire a source is silent.
|
||||
⚠ The bench cannot confirm audibility: it runs with no audio device (`live=0
|
||||
pooled=0`), so the control chain is verified but final playback is not.
|
||||
|
||||
## PPC HIT = a deliberate CRTC horizontal-sync DETUNE on every secondary display (2026-08-06) [T1 disasm-verified]
|
||||
|
||||
**NOT YET IMPLEMENTED IN THE PORT.** Hand-off spec:
|
||||
`phases/phase-14-ppc-sync-distortion.md`. Reported by playtesters as "being hit
|
||||
by a PPC makes it look like all of the secondary CRTs were being degaussed" —
|
||||
main (VPX) view unaffected, PPC strikes only. Both observations are exactly
|
||||
what the binary does.
|
||||
|
||||
**The gate is the damage TYPE, and only the PPC has it.** A `BTL4.RES`
|
||||
subsystem census gives `damageType` 4 (`EnergyDamageType`) = **14 records,
|
||||
every one PPC or ERPPC**; everything else is Ballistic (16), Explosive (30),
|
||||
Laser (78). So a branch keyed on type 4 is structurally PPC-exclusive.
|
||||
|
||||
The chain, on the **VICTIM's** machine (all `@` from `BTL4OPT.EXE`,
|
||||
md5 `a97075bcb5634d13263e9ad5a2b96fd0`):
|
||||
|
||||
1. `Mech::TakeDamageMessageHandler` @`0x4a0230`, branch @**`0x4a03f3`** — sits
|
||||
between the collision divert and the burst loop, so it runs **once per
|
||||
damage message**, not per burst:
|
||||
```
|
||||
004a03f3 mov ecx,[esi+0x2c] ; damage.damageType
|
||||
004a03f6 cmp ecx,4 ; EnergyDamageType
|
||||
004a03f9 jne 0x4a0423 ; everything else -> burst loop
|
||||
004a03fb mov eax,[0x4efc94] ; the global `application`
|
||||
004a0400 mov eax,[eax+0x4c] ; -> gauge renderer
|
||||
004a0405 je 0x4a0423 ; null-guarded
|
||||
004a0407 fild dword [esi+0x2c] ; (float)damageType == 4.0
|
||||
004a040a fld xword [0x4a0c08] ; long double 0.2
|
||||
004a0410 fmulp st(1) ; => 0.8
|
||||
004a041d call dword [edx+0x4c] ; vtable slot 19, args (0.8f, 0)
|
||||
```
|
||||
⚠ The duration is **derived, not constant**: `(float)damageType × 0.2`.
|
||||
2. Gauge-renderer vtable @`0x51cebc`, slot 19 (`+0x4c`) = @**`0x46ffcc`**
|
||||
(an `L4GaugeRenderer` method — the 0x46xxxx MUNGA_L4 range, so the
|
||||
capability is shared-engine; BT is what wires it to Energy damage).
|
||||
Second arg must be 0 (`sub eax,1; jae ret`). Body: `svga16 = this[+0x1c52c]`
|
||||
(null-guarded — same member RP fetches for its `FlashPalette`), then
|
||||
`this[+0x1c534] = 1` (active) and `this[+0x1c538] = now + 0.8 s` in ticks.
|
||||
3. @`0x46d840` — thin wrapper, drops `this`, forwards the flag.
|
||||
4. @**`0x47d76d`** — the payload, straight VGA CRTC I/O:
|
||||
```
|
||||
out(0x3D4,0x11); v=in(0x3D5); out(0x3D5, v & 0x7F) ; unlock CRTC regs 0-7
|
||||
out(0x3D4,0x00) ; CRTC 0 = HORIZONTAL TOTAL
|
||||
if (arg==0) { out(0x3D5, saved); modified=0; } ; restore
|
||||
else if (!modified) { modified=1; saved=in(0x3D5);
|
||||
out(0x3D5, saved-9); } ; <<< shorten the scanline
|
||||
out(0x3D4,0x11); out(0x3D5, v) ; restore write-protect
|
||||
```
|
||||
Globals: `modified` @`0x4fe0fe`, `saved` @`0x4fe0ff`. The `modified` latch
|
||||
makes it **idempotent** — overlapping PPC hits do NOT stack, and a second
|
||||
hit does not re-save an already-detuned value.
|
||||
5. @**`0x47003c`** (per frame): `if (active && now >= expiry) { active = 0;
|
||||
SVGADistortSync(svga16, 0); }` — restores the saved Horizontal Total.
|
||||
|
||||
**Why it reads as a degauss, and why only the secondaries.** CRTC register 0 is
|
||||
the character-clock count per scanline — it *is* the horizontal scan frequency.
|
||||
−9 drives every attached monitor's horizontal oscillator off frequency: the
|
||||
image shears/rolls/wobbles until it re-locks, then snaps back 0.8 s later. All
|
||||
six secondary displays are derived by the VDB from that one VGA's timing, so
|
||||
they glitch **together**; the main view comes off the Division VPX card on an
|
||||
independent timing chain and is untouched. No relay, no VDB register, no
|
||||
palette work — the VDB just propagates a deliberately corrupted sync. (The
|
||||
`LampTesla1/2/3` "solid-state relays" in `L4CTRL.HPP` are NOT involved and are
|
||||
driven by nothing in the surviving tree.)
|
||||
|
||||
⚠ **Do not confuse this with the `SVGA16::FlashPalette` pixel-mask cycler**
|
||||
(`flashRate`/`mask[4]`, ports `0x302/0x30A/0x312`). That machinery is linked and
|
||||
its per-frame cycler runs in BT, but `FlashPalette` @`0x46d5f4` has **zero call
|
||||
sites and zero address-of references** in `BTL4OPT.EXE` — BT never arms it.
|
||||
**RP does**: `RPL4OPT.EXE` @`0x4addce` calls `FlashPalette(palette 1 =
|
||||
SecondaryPalette, rate 2.0, masks {FF,BF,7F,3F})` from its gauge-renderer ctor —
|
||||
hardware-assisted alarm blinking by masking off the top two pixel bits. Same
|
||||
pods, so it is an easy source of cross-game misattribution.
|
||||
|
||||
Scope note: callers of vtable slot 19 were not exhaustively enumerated (virtual
|
||||
dispatch); the PPC site was found via the three `application+0x4c` uses
|
||||
(`0x4a03fb` here, `0x4cc3be` / `0x4d1559` unrelated). The low-level path IS
|
||||
exhaustive — @`0x47d76d` has exactly one caller, and @`0x46d840` exactly two
|
||||
(set @`0x47002b`, restore @`0x470076`).
|
||||
|
||||
## Key Relationships
|
||||
- Full history: `docs/GAUGE_COMPOSITE.md`; reticle recovery: `phases/phase-02-dpl2d-reticle.md`.
|
||||
- Uses: [[attribute-pointer]] + [[reconstruction-gotchas]]; reads [[subsystems]] state.
|
||||
|
||||
Reference in New Issue
Block a user