Commit Graph
12 Commits
Author SHA1 Message Date
CydandClaude Fable 5 4cf09917ce BT410 5.3.57: the crash is deterministic, and the real blocker is the load gate
The previous commit's attribution was wrong and is corrected in the roadmap.

WRONG: 'the crash is the skeleton walk's' rested on 0 crashes in 2 runs with
the walk disabled -- a 25% coin flip presented as evidence.  The oldest
preserved dump has no [skl] line and ends on the old 'couldn't figure out how
to MakeEntityRenderables' fallback, so it predates the walk and faults
identically.

WRONG: 'it lands at different points each time'.  Every dump carries the same
numbers to the byte (cr2 7000FA64, EIP 66D9).  What varies is how far the log
gets, not where the fault is.

Resolving the address through btl4opt.map names it exactly:
EulerAngles::operator=(const LinearMatrix&)+0x19, a read through the matrix
reference.  A binary scan finds all three call sites of that operator pass a
stack local, so the 1.79GB pointer still has no static explanation -- that is
now its own open item rather than a guess.

Two theories killed cleanly by the new BT_STACK_LOG probe and a binary scan:
ESP drift (drift=0 over 2002 frames; exactly one callee-cleans function exists
in the whole binary) and an undersized stack (our PE and the shipped one have
identical 1MB/8K geometry).

What the probe found matters more: the application is parked in state=2, which
is LoadingMission, not WaitingForLaunch.  Priority 0 is where the interest
manager queues renderer events during load, the gate needs that priority empty,
and it never empties -- so the mission never launches and the renderer holds a
blank screen by design.  The fault arrives ~30s into that wait.  Runs that DO
launch never print a single [launch] line.  So 'crashes half the time' and
'hangs during load' are one event seen twice.

A 15x disagreement between two clocks looked like a reconstruction slip --
BTL4.CPP passes GetTicksPerSecond() where ApplicationManager wants a frame
rate.  Checked against the shipped binary before touching it: same instruction
sequence, same kind of static float pushed.  Authentic.  Documented so nobody
'fixes' it.

Also swept every subsystem DefaultData against its real C++ base.  Fifteen
chain past their immediate parent, but fourteen skip only classes that add no
handlers and no attributes, and no class's attribute-ID base disagrees with its
index chain -- so there are no gap slots there.  The one real defect: Generator
is a HeatSink but chained to Subsystem::MessageHandlers, so it ignored every
ToggleCooling message.  Fixed (compiles next build).

Tooling: podrun.sh stages the build over BTL4REC.EXE, exports the host-side VPX
board env -- without which the run dies at the iserver handshake rather than
merely rendering nothing -- and archives every run's log, marking it -CRASH
when it faulted.  Before this the only preserved dump was an accident, on a rig
where each run costs four minutes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 21:41:38 -05:00
CydandClaude Fable 5 09192367c0 BT410 5.3.51: THE 3-D WORLD RENDERS -- reconstruction to emulated board to a picture
emulator/render-bridge/first-3d-frame.png: the arena from the pod -- sky,
horizon, ground, the arena structures along the skyline -- at ~31fps on the
VelociRender bridge.

The whole chain now works in our build: MakeVideoRenderer ->
BTL4VideoRenderer over DPLRenderer -> board boot -> Renderer::LoadMission ->
DPLReadEnvironment for the art paths -> 40/40 arena objects loaded -> mission
launch -> per-frame submission over the wire.

The mistake worth remembering: the run was never stalled after
InitializePlayerLink.  I called that from a 150-second sample and it was just
too short.  CheckLoadMessageHandler reposts every second and will not advance
until the min-priority event queue drains, and with the video renderer in the
mission that takes minutes.  A BT_LAUNCH_LOG trace showed the queue draining
and then both RunMissionMessageHandler calls, the plasma display, and the
first sensor tick -- matching the shipped binary line for line.  The renderer
deliberately holds a blank screen until RunningMission (L4VIDEO.CPP:5100), so
'black' was the app still loading, not a rendering bug.

Zero unbuildable entities, zero geometry failures.  The frame is static only
because the mech is parked with no input, and the camera in the bridge title
is the bridge's own viewer, not the game's eyepoint.

Also banked the launch-gate trace (BT_LAUNCH_LOG) behind an env var.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 10:30:20 -05:00
CydandClaude Fable 5 50341b4a66 BT410 5.3.41: the plane-write hunt -- two more hypotheses dead, defect bounded
PlayerStatus (dirty-gated, blits an 8-bit pixmap -- but the widget map for a
DIRTY run contains no playerStatus at all; it belongs to cameraInit and never
exists in a mech cockpit) and heat-driven redraw (a BT_FORCE_FIRE run came
back clean).  Seven hypotheses killed by measurement so far.

It is a heisenbug: dirty on ~3 runs in 25, and every run since instrumenting
has been clean.  The pattern tracks STARTUP LOG VOLUME -- the three dirty
runs all had the lighter conf, and a chatty trace slows init enough to
suppress it.  The blit trace is therefore narrowed to fire only for pixmaps
landing on a non-palette port, which is near-silent when nothing is wrong.

That narrowed trace caught real traffic: 17x17 PixelMap8 blits onto
Mfd1/2/3 -- 8-bit pixmaps on single-bit planes.  They render correctly, so
the library thresholds them; not the culprit but the right bug class.

Also banked a hunting recipe, because two detector traps cost runs: Eng1 == 0
means the cockpit is not up (not dirty), and an early capture during mission
start caught a full 3-D cockpit frame with the gauges at pod positions
(Eng1 62927) that did not recur over the next minute.  BTDPL.INI is in the
mount and DPLRenderer is linked, so the dPL path may emit a frame during
launch even with L4VIDEO=OFF -- first evidence the 3-D path produces
anything, and explicitly NOT a claim that btl4vid works.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 22:43:13 -05:00
CydandClaude Fable 5 740ef2b0a7 BT410 Phase 5.3.32: the per-head A/B -- three engineering heads now exact
The gauge framebuffer is PLANE-PACKED: L4GAUGE.CFG gives each port a bit
mask, not a rectangle, so all six heads share the same x/y and are separated
only by bit -- the packing the VDB splits into the pod's physical screens.
Comparing RGB was therefore measuring nothing useful; the "magenta artifact"
just meant the wrong heads were lit at that pixel.

New instruments (emulator/render-bridge/gauge-ab/, with a README):
  planes.py  per-head scoreboard -- shipped vs ours, missing vs extra, per
             port mask, recovering the 16-bit word from DOSBox's RGB565
  BT_VIS_LOG a complete cockpit widget map from the single dispatch every
             gauge widget is built through (MethodDescription::Execute),
             printing name + port + authored position
  ab.sh      stages the fresh build over BTL4REC.EXE and kills any running
             DOSBox before launching -- see below

The fix: the Comm page's pilot roster is the POD roster (the viewpoint mech's
ControlsMapper pilot array, mapper attributes 15/16), not the mission's
player list.  It is zero in a solo mission, so the shipped page draws empty;
ours resolved the local player and painted its icon in colour 0xff.  The gate
belongs in the row source, not PilotList::Execute -- Execute's empty branch
still has to run, because erasing is what the shipped page draws.

  head    shipped    ours   missing   extra          (was)
  Eng1      17981   17981         0       0     (0 / 1030)
  Eng2      17981   17981         0       0
  Eng3      17981   17981         0       0
  Comm      15656   13557      2099       0     (2099 / 1253)
  Heat      21374   20913       566     105     (566 / 990)

Title-band magenta 404 -> 0.  The 2099 Comm pixels we are missing were
missing before the gate, so it is a strict improvement.

Method note, which cost more than the bug: the rig's confs run BTL4REC.EXE
out of the mount while the build writes build410/btl4opt.exe, and nothing
connected the two.  Three measurements ran an hour-old binary, so a change
that "did nothing" three times had never been tested -- and the correct first
hypothesis was discarded on that evidence.  ab.sh now re-stages every launch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 12:04:43 -05:00
CydandClaude Fable 5 64e208f121 BT410 Phase 5.3.26: THE COCKPIT GAUGE BLOCK -- the first rendered milestone
Six TUs born in one wave (4-agent re-hosting workflow from the BT411 donors
+ inline integration): BTL4GAUG (16 widget primitives + 5 connections, the
TU anchor @004c3f6c), BTL4GAU2 (13 composite panel clusters), BTL4GAU3
(PilotList/PlayerStatus/SectorDisplay/MessageBoard/mapping group), BTL4RDR
(the MapDisplay radar vs the authentic surviving header), BTL4GRND (the
real renderer: L4LampManager + L4Warehouse + BuildConfigurationFile with
the 19-entry BT registry chained onto the engine table).  btl4.lib grows
7 -> 11 members; all 50 game TUs compile; link clean.

VERIFIED LIVE: with L4GAUGE=640x480x16 the interpreter parses the full
authentic L4GAUGE.CFG, Bhk1Init runs, ColorMapperArmor binds the live
mech's damage zones by name (cross-chassis names take the authentic inert
path), and the complete mutual missile fight runs with the cockpit stack
updating from real combat -- 16/16 rounds, 138 zone hits, zero faults.
Gauges-off regressions untouched.

Engine bring-up deviation (documented): ParseAttribute normalizes
unpublished attribute names to a shared writable zero cell -- the 1995
GaugeConnectionDirectOf ctor hard-derefs its source (first boot crashed
there); widgets on unpublished names draw static zeros and light up
automatically as the attribute waves publish the 1995 CFG spellings the
bindings already carry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 19:02:04 -05:00
CydandClaude Fable 5 110ecec2ca BT410 Phase 5.3.18: flying missiles -- spawn/home/detonate LIVE, teardown open
The Missile entity family is reconstructed: Projectile : Mover gets a real
make-path ctor; Missile hosts the Seeker + MissleThruster roster (the
AUTHENTIC surviving SEEKER.HPP / MISTHRST.HPP interfaces -- the 1995
misspelling included) and flies the binary's guided integrator (@004bef78):
age/lifetime, seeker re-lead with the decoded loft constants
(200/50/300/0.1), thruster burn, steering (gain 4.0, deadband 1e-4), and
the proximity fuse delivering the cluster damage ONCE with the launcher as
inflictor.  Live-verified end-to-end: LAUNCH -> flight telemetry (thruster
visibly accelerating) -> DETONATE on the target ~0.9s out.

OPEN: the first ~Missile off the death row completes, then the frame
page-faults with EIP in the heap -- the first entity/subsystem teardown the
reconstructed sim has ever run.  The spawn path is therefore OPT-IN
(BT_MISSILE_FLIGHT=1); default SRM delivery stays the 5.3.18a instant-hit
cluster (verified 1:1, zero exceptions).  Forensics + engine truths
(SearchList crashes on non-directory ids; MakeMessages need FAMILY resource
ids; explosionModelFile is a model-list INDEX) in MISSILE.NOTES.md.

Also fixed on the way: the Subsystem NAME ctor left damageZone
UNINITIALIZED (latent garbage for every name-built subsystem).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:58:40 -05:00
CydandClaude Fable 5 a36e775c7c Mech phase 2: reconstruct HUD (: PowerWatcher) -- Mech hudSubsystem
Head-up display / targeting reticle subsystem, full named member set (reticle/
compass/lock/flicker/torso-readout state). Ctor chains PowerWatcher + inits per
the binary (visible/blinkState True, vectors zeroed, resource flicker/horizontal
copied); statusAlarm.Initialize(2). Added AlarmIndicator::Initialize(levels).
statics/dtor/test real; HudSimulation staged. BT: 29 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 00:05:00 -05:00
CydandClaude Fable 5 9a4f1c293b Mech milestone: reconstruct AlarmIndicator (helper 1/3), compile-verified
First of the 3 missing Mech embedded-helper classes. AlarmIndicator is the
multi-level status indicator the Mech carries four of (master/heat/stability/
status). Reconstructed as a StateIndicator subclass: the shipped ctor
(@0041b9ec) is byte-for-byte the surviving StateIndicator(unsigned) ctor
(stateCount=oldState=currentState=count; 3 watcher sockets NULL) with a distinct
vtable, so a derived class reproduces it exactly and adds the level API
(SetLevel/GetLevel/GetLevelCount -> SetState/GetState/stateCount). Header-only
(StateIndicator's methods are in SIMULATE.CPP, already in munga.lib).

Compile-verified standalone under BC4.52 with the authentic build flags.
Evidence + placement in ALARM.NOTES.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 20:49:26 -05:00
CydandClaude Fable 5 5b35eb973c 4.10 reconstruction: BTL4OPT.EXE links clean and BOOTS to the first staged brick
The reconstructed tree now produces a runnable binary with the authentic
1995 toolchain (BC4.52 / tlink32 / DPMI32):

- BTL4.CPP main TU reconstructed from the 4.11 Ghidra decomp (FUN_0040109c)
  + the 4.10 binary's own string pool + surviving RPL4TOOL.CPP house style;
  probe_main.cpp scaffold retired (BTL4.NOTES.md documents every decoded call)
- L4NET.CPP staged: L4NetworkManager ctor/dtor + 10 vtable-pulled virtuals
  (standalone-benign ones no-op, network ones Fail loudly) + NetNub client
  globals (Net_Common_Ptr=NULL routes L4File to its plain-DOS path)
- build410.sh: libs now built in the AUTHENTIC makefile member order
  (MUNGA.MAK / mungal4.mak / BT.MAK / BTL4.MAK). Order is load-bearing:
  tlink emits static-init records in module pull order, and alphabetical
  order booted into a null-vptr crash (IcomManager::ClassDerivations
  constructing before parent NetworkClient::ClassDerivations). Also fixed
  stage_link to the proven 32-bit lib set (SOSDBXC+SOSMBXC, no WATTCPLG)
- BOXTREE.HPP MemoryBlock unify, BTL4GRND notify stubs, remaining engine
  backfills (audio/gauge/resource/stream TUs) that closed the deep ledger

Smoke test (DOSBox-X + 32RTM, copy of the pod BT tree, our exe swapped in):
  BattleTech v4.10
  BTL4Application::BTL4Application
  l4net.cpp(22): L4NetworkManager -- l4net.cpp not yet reconstructed
Static init, main, -egg parse, BTL4.RES load (version 1.0.6 check passes),
ApplicationManager and the BTL4Application ctor chain all execute real
reconstructed code; boot halts at the first staged Fail() as designed.
Next brick: the real l4net.cpp body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 19:05:53 -05:00
CydandClaude Fable 5 af80d52e22 source410: engine COMPLETE (165/165) - link reaches PE emission; deep ledger cut
The tlink32 campaign after the checkpoint: eleven engine bodies back-dated
from BT412 (rotation player team explode dropzone terrain cultural receiver
subsystm app l4gauge - the Application core included), the WinTesla-ectomy
handled by backdate.py's new unwrap rules (accessor-fn statics -> 1995 static
objects, decl rewrites, pointer->reference Derivation ctor, 2007 windowed-
gauge/console-marshal/idle-pump excisions), staged statics TUs opened for the
game classes (MECH/BTPLAYER/BTDIRECT/PROJTILE/MISSILE/BTL4MPPR .CPP + L4APP/
NETWORK engine statics).

State: every compiled symbol short of the shallow graph RESOLVED (the 52- and
17-symbol ledgers burned to zero); with 32stub.exe in place the linker reaches
full vtable closure and emits the deep ledger (~210 symbols, UNRESOLVED-
LEDGER.txt): remaining engine bodies (objstrm cstr gauge/gaugrend graphics
pixelmap palette resfile ray scnrole explosion-table), the DOS driver extern
layer (_SVGA*/_PCSerial*/_PCSPAK*/joystick/netnub/sosMIDI), the full L4App
body, and the real game-TU frontier (Mech::Make et al.). Recipe proven for
every category.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:25:49 -05:00
CydandClaude Fable 5 6b216d0643 source410: link campaign - entire engine compiles (152/152), first true link,
52-symbol unresolved ledger

build410.sh: merged-engine mass compile + BT TUs + tlib + authentic tlink32.
Engine closure fixes: boxtree/set/l4gauge/filestrm back-dates, lamp<->gaugrend
cycle broken (1995 form), APP.HPP Shutdown default, NetNub include path, DPL
vpx shim, SOS 32-bit lib arbitration (SOSDBXC/SOSMBXC; SOSMW*=16-bit), WATTCP
excluded (16-bit NetNub TSR side). UNRESOLVED-LEDGER.txt = the measured gap to
a linking BTL4OPT.EXE.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 08:46:05 -05:00
CydandClaude Fable 5 63312e07f9 source410: literal 4.10 source reconstruction + BC++ 4.52 fleet toolchain archived
- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
  is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
  Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
  (extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
  BT game source (never mixed into CODE/). Round 1-3 state:
  * 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
    (BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
    since 1996.
  * BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
    its binary-recorded line 400 exactly.
  * BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
    (Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
    TeamScore=12 flagged [T4]).
  * MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
    (VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
    the period compiler is the drift detector).
  * Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
    (per-TU verification sweep under authentic OPT.MAK flags).
  * README: corrected roadmap - MECH.HPP is the capstone grown with the mech
    TU reconstructions; BTREG.CPP green = the header-family milestone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 07:33:26 -05:00