diff --git a/emulator/render-bridge/pod_render_bal.conf b/emulator/render-bridge/pod_render_bal.conf new file mode 100644 index 00000000..f4d44dc3 --- /dev/null +++ b/emulator/render-bridge/pod_render_bal.conf @@ -0,0 +1,85 @@ +# pod_render_norio.conf -- pod_render_rec with the RIO serial port OFF. +# +# The emulator log shows a steady stream of serial1 RX OVERRUN errors on +# the RIO pipe, and controls post their events at HighEventPriority +# UNCONDITIONALLY (CONTROLS.HPP:250) -- so a chattering RIO port would +# flood a priority the background pump always serves first, starving the +# priority-0 renderer events the load gate waits on. This conf tests that +# by removing the port entirely. Everything else is identical to the rec +# conf, so a launch here and a hang there isolates the RIO. +# +[sdl] +output=opengl +# higher,higher not highest: HIGH_PRIORITY_CLASS starved the host desktop; +# with the retry patches a rare dropout self-recovers (see gauge_rio.conf). +priority=higher,higher +[dosbox] +memsize=32 +machine=svga_s3 +[cpu] +core=dynamic +cputype=pentium +cycles=max +[sblaster] +sbtype=sb16 +sbbase=220 +irq=5 +dma=1 +hdma=5 +[mixer] +# match the EMU8000s' native rate (no resample) and buffer ~60ms so brief +# emulation-thread stalls (RIO retry recovery) don't audibly chop +rate=44100 +blocksize=1024 +prebuffer=60 +[serial] +# RIO on COM1 with the low-latency options (rxpollus/rxburst) so the board's +# few-ms ACK deadline is met; plasma display on COM2 (real pod has both). +# VWE fork namedpipe backend (com0com/realport retired -- COM1/COM2 gone): +# DOSBox = pipe client (retry), vRIO/vPLASMA apps = servers; an unconnected +# pipe behaves as an unplugged cable so the mission still runs. serialnamedpipe.h +serial1=disabled +serial2=namedpipe pipe:vplasma +# live UNBUFFERED game output: DOS char devices are not buffered, so +# redirecting stdout to COM3 lands every line immediately. A normal +# '> file' redirect stays 0 bytes until the process exits, which hides +# all progress on a run that does NOT crash. +serial3=file file:C:\VWE\TeslaRel410\emulator\render-bridge\podlog.txt +[autoexec] +mount c "C:\VWE\TeslaRel410\ALPHA_1" +c: +cd \REL410\BT +set VIDEOFORMAT=svga +rem production pod card init (PARAMETR.BAT:181-186): DIAGNOSE + AWEUTIL per +rem card -- AWEUTIL /S does the EMU8000 bring-up and DRAM detect the HMI SOS +rem driver relies on; skipping it left the cards uninitialized (silent). +rem aweutil /s SKIPPED for now: it verifies the AWE32 GM ROM, which the +rem emulated cards lack (hangs in a retry loop) -- restore once the ROM is +rem dumped from a real card. diagnose /s kept (passes, sets mixer config). +set BLASTER=A220 I5 D1 H5 P330 T6 +c:\sb16\diagnose /s +set BLASTER=A240 I7 D3 H6 P300 T6 +c:\sb16\diagnose /s +set BLASTER=A220 I5 D1 H5 P330 T6 +set TEMP=c:\ +rem arena1 city mission (TESTARN.EGG: map=arena1, time=day) with the RIO +rem attached; stdout redirected so mission-load progress survives kills. +set BT_JOINTS=1 +set L4VIEWEXT=1 +set BT_STACK_LOG=1 +set BT_MECH_LOG=1 +set BT_PRESS_BALANCE=1 +set BT_GAUGE_LOG=1 +set BT_FORCE_THROTTLE=0 +set BT_FORCE_TURN=0 +set HEAPSIZE=15000000 +set L4GAUGE=640x480x16 +call setenv.bat r s n p +32rtm.exe -x +BTL4REC.EXE -egg testarn.egg > COM3 + +echo GAME-RC=%errorlevel% >> RC.TXT +32rtm.exe -u +echo ALPHA1-RUN-DONE +pause + diff --git a/restoration/source410/BT/HEAT.CPP b/restoration/source410/BT/HEAT.CPP index 66b1ae55..128c36a7 100644 --- a/restoration/source410/BT/HEAT.CPP +++ b/restoration/source410/BT/HEAT.CPP @@ -101,6 +101,14 @@ HeatableSubsystem::HeatableSubsystem( { Check(owner); Check_Pointer(subsystem_resource); + + // + // The balance alarm arms once at birth (binary ctor @004ae568: + // FUN_0041b9ec(+0x1dc, 3)); the priority/share rest state rides + // ResetToInitialState below. + // + balanceAlarm.Initialize(3); + ResetToInitialState(); Check_Fpu(); } @@ -123,6 +131,14 @@ void Check(this); currentTemperature = 300.0f; heatLoad = 0.0f; + + // + // Binary rest state (@004ae568 ctor / the @004ae4d8-family reset): + // priority 1 (the equal split), share 0 until the owning mech's + // redistribute computes the first real fractions. + // + coolantPriority = 1; + coolantShare = 0.0f; } // diff --git a/restoration/source410/BT/HEAT.HPP b/restoration/source410/BT/HEAT.HPP index d5f1aa56..fedc6486 100644 --- a/restoration/source410/BT/HEAT.HPP +++ b/restoration/source410/BT/HEAT.HPP @@ -122,6 +122,27 @@ CurrentTemperatureOf() { Check(this); return currentTemperature; } + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // The coolant-allocation trio (binary heatable +0x1d0/+0x15c/+0x1dc, + // ctor @004ae568). PUBLIC: the owning Mech's redistribute walks the + // heatable chain and writes all three (the duckCommand precedent). + // + // coolantPriority the draw-weight rung, born 1; the per-subsystem + // priority button (@004ae464, id TBD) steps it 0 -> 1 -> 5 -> + // 50 -> 0, and the mech's BalanceCoolant (0x16) resets every + // heatable to 1 (the equal split). + // coolantShare priority / total, written ONLY by the mech's + // RedistributeCoolantShares (@0049f788); the reservoir draw + // (@004aefa4) weights each heatable's coolant feed by it. + // balanceAlarm 3 levels (binary FUN_0041b9ec(+0x1dc, 3)); the + // redistribute blips it -- 2 = share shrank or held, 1 = grew, + // then back to 0 -- the cockpit gauge flash. + // + public: + int coolantPriority; + Scalar coolantShare; + AlarmIndicator balanceAlarm; + protected: Scalar currentTemperature; Scalar heatLoad; diff --git a/restoration/source410/BT/MECH.CPP b/restoration/source410/BT/MECH.CPP index dd544bbe..70bd390e 100644 --- a/restoration/source410/BT/MECH.CPP +++ b/restoration/source410/BT/MECH.CPP @@ -90,6 +90,7 @@ const Receiver::HandlerEntry MESSAGE_ENTRY(Mech, RealMaxSpeed), MESSAGE_ENTRY(Mech, SetBurningState), MESSAGE_ENTRY(Mech, ClearBurningState), + MESSAGE_ENTRY(Mech, BalanceCoolant), MESSAGE_ENTRY(Mech, DuckRequest) }; @@ -551,6 +552,31 @@ Mech::Mech( } } + // + //----------------------------------------------------------------------- + // THE HEATABLE CHAIN (binary ctor sweep, part_012.c:15774: every roster + // subsystem derived from HeatableSubsystem -- GUID 0x50e4fc -- joins the + // mech+0x7cc chain), then the first coolant shares (the binary calls the + // redistribute at the very ctor end, after the "Mechs" group add; the + // ordering between is inert). The weapon (+0x7bc) and Myomers (+0x7ac) + // sweeps land with their consumers. + //----------------------------------------------------------------------- + // + { + for (int hs = 2; hs < subsystemCount; ++hs) + { + if ( + subsystemArray[hs] != NULL && + subsystemArray[hs]->IsDerivedFrom( + HeatableSubsystem::ClassDerivations) + ) + { + heatableSubsystems.Add(subsystemArray[hs]); + } + } + } + RedistributeCoolantShares(); + // //----------------------------------------------------------------------- // Source the authentic per-mech locomotion params from the GameModel @@ -925,6 +951,87 @@ void AlwaysExecute(); } +// +//############################################################################# +// BalanceCoolantMessageHandler -- binary @0049f728 (authentic name from the +// handler table). A positive press resets every heatable's coolant +// priority to 1 -- the equal split -- and renormalizes the shares. +//############################################################################# +// +void + Mech::BalanceCoolantMessageHandler(Receiver::Message *message) +{ + Check(this); + Check_Pointer(message); + + if (((int *)(message + 1))[0] > 0) + { + ChainIteratorOf mark_walk(heatableSubsystems); + HeatableSubsystem *heatable; + while ((heatable = (HeatableSubsystem *)mark_walk.ReadAndNext()) != NULL) + { + heatable->coolantPriority = 1; + } + RedistributeCoolantShares(); + } +} + +// +//############################################################################# +// RedistributeCoolantShares -- binary @0049f788: total the priorities, give +// each heatable share = priority / total (0.0 when the total is empty -- +// the data word @0049f850), blip its balance alarm with the direction +// (2 = shrank or held, 1 = grew, then back to 0), store the share. The +// reservoir draw weights each heatable's coolant feed by the share. +//############################################################################# +// +void + Mech::RedistributeCoolantShares() +{ + Check(this); + + int total = 0; + { + ChainIteratorOf sum_walk(heatableSubsystems); + HeatableSubsystem *heatable; + while ((heatable = (HeatableSubsystem *)sum_walk.ReadAndNext()) != NULL) + { + total += heatable->coolantPriority; + } + } + + int share_count = 0; + Scalar first_share = 0.0f; + + ChainIteratorOf share_walk(heatableSubsystems); + HeatableSubsystem *heatable; + while ((heatable = (HeatableSubsystem *)share_walk.ReadAndNext()) != NULL) + { + Scalar share = 0.0f; + if (total > 0) + { + share = (Scalar)heatable->coolantPriority / (Scalar)total; + } + heatable->balanceAlarm.SetLevel( + (share <= heatable->coolantShare) ? 2 : 1); + heatable->coolantShare = share; + heatable->balanceAlarm.SetLevel(0); + + if (share_count == 0) + { + first_share = share; + } + ++share_count; + } + + if (getenv("BT_MECH_LOG")) + { + DEBUG_STREAM << "[coolant] shares renormalized: " << share_count + << " heatables, total priority " << total + << ", first share " << first_share << endl << flush; + } +} + // //############################################################################# // The collision-volume swap (binaries @004ac04c / @004ac064): write the @@ -1371,6 +1478,12 @@ void } } + // + // The binary Reset (@0049fb74 tail) renormalizes the coolant shares + // after the roster's per-subsystem resets restored every priority. + // + RedistributeCoolantShares(); + SetPreRunFlag(); if (getenv("BT_MECH_LOG")) @@ -2169,6 +2282,19 @@ void } } // + // BT_PRESS_BALANCE: one mech-level BalanceCoolant press (0x16) + // through the normal dispatch -- the equal-split button. + // + if (getenv("BT_PRESS_BALANCE")) + { + ReceiverDataMessageOf balance_press( + Mech::BalanceCoolantMessageID, + sizeof(ReceiverDataMessageOf), + 1 + ); + Dispatch(&balance_press); + } + // // BT_PRESS_GEN=1..4: SelectGenerator at the first Emitter // (the generator panel re-tap); BT_PRESS_SEEK: ToggleSeekVoltage // at the first Emitter (the seek dial). diff --git a/restoration/source410/BT/MECH.HPP b/restoration/source410/BT/MECH.HPP index 1ff405e0..bbb3b095 100644 --- a/restoration/source410/BT/MECH.HPP +++ b/restoration/source410/BT/MECH.HPP @@ -980,6 +980,18 @@ SetBurningStateMessageHandler(Receiver::Message *message); void ClearBurningStateMessageHandler(Receiver::Message *message); + void + BalanceCoolantMessageHandler(Receiver::Message *message); + + // + // The coolant-share renormalizer (binary @0049f788): share = + // priority / total across the heatable chain, blipping each + // heatable's balance alarm with the change direction. Callers in + // the binary: the 0x16 handler, the ctor tail, Reset, and the + // per-subsystem priority stepper (@004ae464, not yet landed). + // + void + RedistributeCoolantShares(); void SetStandingCollisionVolume(); // binary @004ac04c... see .CPP void diff --git a/restoration/source410/BT/MECH4.NOTES.md b/restoration/source410/BT/MECH4.NOTES.md index ed8ed5b9..0134973d 100644 --- a/restoration/source410/BT/MECH4.NOTES.md +++ b/restoration/source410/BT/MECH4.NOTES.md @@ -299,26 +299,20 @@ simulationFlags bit 1 == DontExecuteFlag (SIMULATE.HPP) -- the binary's Still to land from the roster: -* **BalanceCoolant @0049f728** (decoded): if payload > 0, mark item+0x1d0=1 - across the mech+0x7cc roster, then the redistribute pass @0049f788: total - the marks, per item fraction = mark/total (0.0 @0049f850 when empty), - blip the item's alarm at +0x1dc (level 2 if fraction <= stored else 1, - then 0 -- the gauge flash), store fraction at item+0x15c. UNBLOCKED (same sitting): the ctor's chain sweeps - (part_012.c:15760..) pin the triple by derivation GUID, names resolved - by static-layout adjacency to each TU's string pool: **+0x7ac = - MYOMERS** (GUID 0x51155c, string @0x51166d -- so the mobility scale IS - max leg-muscle effectiveness, item+0x31c), **+0x7bc = MECHWEAPON - roster** (0x511830; the ctor ORs weapon+0x334 mappable-button masks - into mech+0x410 as it fills it), **+0x7cc = HEATABLE SUBSYSTEMS** - (0x50e4fc == our heatableSubsystems chain; 0x50e604 = the HeatSink-side - derivation whose members get a post-ctor vcall+0x38 -- the conduction - link resolve). BalanceCoolant therefore marks EVERY heatable (+0x1d0), - and the stored fraction +0x15c is the item's coolant ALLOCATION SHARE: - the reservoir draw @004aefa4 weights each item's draw by it - (rate = -(reservoir+0x224) x share x dt, clamped, with mass/capacity - bookkeeping at item+0x12c/+0x128). Land as: three HeatableSubsystem - members (balanceMark/coolantShare/balanceAlarm, +0x1d0/+0x15c/+0x1dc) - + the handler on heatableSubsystems. +* **BalanceCoolant @0049f728 -- LANDED 5.3.123** (equal-split press + + RedistributeCoolantShares @0049f788 + the heatable ctor sweep that + populates the heatableSubsystems chain + ctor/Reset redistribute calls; + soak: 20 heatables, first share 0.05, ctor + BT_PRESS_BALANCE both + renormalize, crash-sig 0). UNIFICATION OWED (5.3.124): the binary's + MoveValve handler is @004ae464 registered on the HEATABLE base (the + 0x50e534 name table in the heatable statics region), novice-locked via + mapper+0x274 == 0 (FUN_004ac9c8 -- our NoviceLockout is correct), + stepping heatable+0x1d0 through 0 -> 1 -> 5 -> 50 -> 0 then calling the + MECH redistribute -- i.e. our Condenser::MoveValve / RecomputeValves / + valveState triple is the SAME mechanism scoped to condensers only; + retire it onto coolantPriority/RedistributeCoolantShares and re-home + the handler on HeatableSubsystem (ValveSetting attribute retargets to + the share/priority cells). * **EjectPilot @0049f854** (head read): gated on payload > 0 AND **mech+0x414** -- THE EFFECTIVENESS-CENSUS CONSUMER FOUND: you can only eject a CRIPPLED mech (@0049fa1c's weapons/coolant/generator/limp