Impact-FX FORENSICS wave: the i860 specialfx engine, per-round detonations, the ram economy closed
The "we're off the rails" reinvestigation -- a 5-thread evidence workflow
(logs / PFX data / RES model lists / decomp / port audit) + the i860 firmware
decode, then surgical fixes. Every claim tiered; the wrong turns are on the
record in the KB alongside the corrections.
THE OLD-STYLE SPECIALFX ENGINE [T1, firmware-decoded from VREND.MNG]:
rebuilt scratchpad/i860dis.py (binutils opcode table); mapped the dispatch
(data+0xdd0c; sfx trigger 0xf040cda0, install 0xf040cdc0, step ~0xf0413698,
instance init 0xf04128d8); decoded the heat model EXACTLY: per 30Hz board
frame h *= cool_a; RGB_ch = h_old*(h_new*cook_ch - 0.25) + 0.25 (K=0.25 =
the ember floor @VA 0xF080; kill at h <= 1e-4 @0xF0A0); alpha fades cool_b
x RAW dt (PER-SECOND -- the x30 scaling made laser hits invisible); 7s cap;
y_off = the kill plane; "variance" is DEAD DATA (binary reads "variance",
INI authors "varience"). The 13 descriptors (PPCHit/LaserHit/MissileHit/
Chunks/Sparks/Fireball...) parse from BTDPL.INI and render via the BTPfx
layer -- heat bursts draw the FIERY sheet (brightness over fire), .PFX keeps
GRAYSCALE (authored colours: DNBOOM orange, DDAM gray).
HIT-PACKAGE CENSUS [T1, RES byte-verified]: ppchit=[8] lzrhit=[9] mghit=[7,11]
canhit=[11] mslhit=[10,12,1023] explode=[6]; all 8 mech death lists identical.
Effect routing corrected in BOTH consumers: <100 = specialfx, >=1000 = psfx.
"SILVER MIST x5" ROOT CAUSE: SHKWAVE.PFX (mslhit's 1023) authors maxIssue=5
relPeriod=0.2 rate=1 -- the ONE file where rate contradicts the window; the
emitter trusted rate -> 5 shells at exactly 1Hz. Emission rate now always
maxIssue/releasePeriod.
PER-ROUND DETONATIONS [T1 @004bef78]: every missile round spawns its own
ExplosionModelFile at ITS impact (hull + terrain) -- a volley ripples 12
fireballs like the demos; rack-tube launch spread [T3] (GUIDED rounds only --
the slot-0 deflection sent every AFC100 shell 3.4deg left/2.5deg down: the
phantom "4th gold beam"); replicant salvos detonate too (launcher index rides
the visual push). Missile damage bundles through the shooter's messmgr with
the launcher's subsystemID (mslhit fires at the consolidated point; the
binary's dedup CONFIRMED [T1 @0049b784] -- a workflow agent's per-record
claim REFUTED by direct decomp read).
DAMAGE-BAND SEMANTICS [T1]: MechDeathHandler fires the CURRENT band effect on
any damage rise (the binary's changed-flag coalescing) -- a mauled mech under
fire smokes/burns per hit; bands 3/4 are authored fire plumes.
THE RAM ECONOMY -- CLOSED (3 layers, measured live):
1. armor = POINTS (every zone: damageScale = 1/armorPoints, armor 50-140;
the [zone-armor] BT_DMG_LOG dump);
2. StaticBounce prices rams with authored moverMass ~1.3e6 -> ~59,000 pts
@10m/s; the binary dispatches it RAW but pod MP dropped it on the local
replicant (MECH.CPP:986 warns) -- ram damage was NETWORK-INERT; our
task-#47 replicant forwarding surfaced it as a one-shot. Port
normalizes x1e-3 to the point economy [T3];
3. contact EDGE (ramLastVictim/ramContactLinger): the binary's bounce made
separation implicit; our gait-derived velocity re-priced full rams at
60Hz (the respawn explode-loop). One bump = one hit; pressed = BLOCK.
PLUS: sfx size x0.5 + 1.25m visibility floor + hot-phase occlusion [T3];
particle pool 2048->8192 (missile traffic starved laser bursts -- the
"intermittent effects"); replicant beam aging (a lost beam-END record pinned
a stale beam on forever); UV-variant noise stamps (mask-safe mirror/swap);
no scroll on particle stamps (material-path only); BT_FX_TEST/fxshot.py
self-verification harness ([beam-draw]/[zone-armor]/[collide-tx] telemetry).
KB: rendering.md (specialfx engine + census + closeout), combat-damage.md
(ram economy + band semantics).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
48c9c8444f
commit
065c114590
@@ -104,6 +104,27 @@ per ER-M laser; [[decomp-reference]] §5). Flying projectiles (LRM/autocannon) a
|
||||
reconstruction (`BTPushProjectile` — the 2007 Entity is too small for the binary's raw integrator
|
||||
offsets). [T2]
|
||||
|
||||
## The RAM economy — CLOSED (2026-07-12) [T1 evidence, T3 normalization]
|
||||
Three-layer story, measured live + decomp-verified:
|
||||
1. **The armor economy is POINTS**: every zone streams `damageScale[5]` EVEN = `1/armorPoints`
|
||||
(armor 50–140; `damageLevel += amount/armor`, 1.0 = destroyed — the `[zone-armor]` dump,
|
||||
BT_DMG_LOG, mechdmg.cpp). Weapons author point-scale amounts (laser 11.77 ≈ 10 torso hits). ✓
|
||||
2. **StaticBounce prices rams with the AUTHORED moverMass ≈ 1.3e6 units** → ~600×v² ≈ 59,000
|
||||
points for a 10 m/s ram (measured: 59221@9.94, 398@0.81; `[collide-tx]` now logs mass/e).
|
||||
The binary DISPATCHES it raw (@part_012:15324-15358) — but in pod MP it landed on the local
|
||||
REPLICANT of the victim (the master never heard it; MECH.CPP:986 warns and proceeds) —
|
||||
**ram damage was network-inert in the pod**. Our task-#47 replicant forwarding (required
|
||||
for MP weapon damage) surfaced it as a one-shot. Port: `BTDispatchCollisionDamage`
|
||||
normalizes ×1e-3 to the armor-point economy (bump = points, charge = tens) [T3].
|
||||
3. **The contact EDGE**: the binary's bounce reversed the mover's velocity so the frame after
|
||||
a bump read as separating; our gait re-derives velocity per frame (still closing while
|
||||
pressed / respawn-overlapped) → sustained contact re-priced full rams at 60Hz (the respawn
|
||||
explode-loop). Port: `ramLastVictim`/`ramContactLinger` (mech.hpp) — one bump = one damage
|
||||
event; pressed = BLOCK; re-arms 0.35s after separation; cleared on Reset.
|
||||
Band effects: `MechDeathHandler::Tick` fires the CURRENT band descriptor on any damage rise
|
||||
(the binary's changed-flag semantics, workflow-verified) — a damaged mech under fire smokes/
|
||||
burns per hit; the earlier crossing-only gate was over-tight (the metronome was SHKWAVE).
|
||||
|
||||
## Damage delivery + the real damage model
|
||||
`Entity::TakeDamageMessage(id, size, inflictingEntityID, zone, Damage&)` → `target->Dispatch`.
|
||||
**Base handler IGNORES zone==−1** (`Entity::TakeDamageMessageHandler`, ENTITY.cpp:878 — returns on
|
||||
|
||||
@@ -75,6 +75,81 @@ the slice. Reading it as RGB overlaid 2-3 gray sheets = the "rainbow/graffiti" b
|
||||
resolves the mech-skin `softer` ramp (defined in OTHER libs). Every mech renders authentic gray-metal
|
||||
paint now. See [[asset-formats]]. [T2]
|
||||
|
||||
## The OLD-STYLE SPECIALFX layer (the weapon-impact action) — RECONSTRUCTED 2026-07-12 (firmware-decoded)
|
||||
The per-weapon impact visuals are fully AUTHORED in `content/BTDPL.INI` `[effects_to_load]`:
|
||||
`specialfx3..15` = Chunks1/Chunks2/Sparks/Explosion3/MachineGun/PPCHit/LaserHit/MissileHit/
|
||||
CannonHit/GroundHit/MinorHit/MajordHit/Fireball — "old-style" (version-1) descriptor sections
|
||||
(`texture/type/size/velocity/v_bias/y_off/cook(3f)/varience/gravity/cool(2f)/count/repeats`),
|
||||
most on `btfx:firesmoke1_scr_tex` (MissileHit size 12 ×7; Fireball size 14, gravity −1.5 rises;
|
||||
Chunks1/2 = ballistic debris vel 40-55 grav 34-35; Sparks NOTEX). [T1: the 1995 binary parses
|
||||
them — part_008.c:1546-1590, field strings @004f25e4-004f2663 → installer FUN_00490210 packs a
|
||||
0x44-byte board command via FUN_0048e2c0.] **Simulation semantics (type/cook/cool) live in the
|
||||
i860 firmware** `content/VREND.MNG` (same decode workflow as the task-#55 'damageize' handler).
|
||||
- **EFFECT-NUMBER ROUTING (corrects the old claim):** `effect_number < 100` = the dpl board
|
||||
EFFECT TABLE = this specialfx list (1996 case-4 comments match: 3=Chunks, 12=GroundHit,
|
||||
15=Fireball), **NOT** a psfx slot; `>= 1000` = psfx slot n−1000 (idam/ddam PFX); 100-999 =
|
||||
ExplosionScripts. The port's `<100 → psfx slot` mapping renders PPCHit(8) as ddam5 smoke and
|
||||
Chunks(3)/Sparks(5) as ddamN smoke — the "hits are just a little smoke" report. The 2007
|
||||
WinTesla loader SKIPS all version-1 descriptors (`if (version < 2) continue;` — it only reads
|
||||
the 1998 INDIE format), which is why the layer was never seen. [T1]
|
||||
- **Live hit census (mp logs):** weapon hits dispatch idam bands 1009-1013 (30×1009) + raw
|
||||
8/9 (PPCHit/LaserHit, mis-rendered); **MissileHit(10)/CannonHit(11) never appeared** — the
|
||||
projectile/salvo damage path likely doesn't thread the firing launcher's subsystemID into the
|
||||
damage records, so messmgr never queues its explosion. [T2 census; T4 cause]
|
||||
- **RECONSTRUCTION (2026-07-12, firmware-first):** the i860 sim decoded from VREND.MNG
|
||||
(dispatch table at data+0xdd0c, VA base: code 0xf0400000, data 0x1000; handlers: sfx trigger
|
||||
0xf040cda0→ca48, install 0xf040cdc0→cba0, step near 0xf0413698, spawn near 0xf0414afc —
|
||||
string-anchored via "attempt to step a NULL sfx object"; disassembler REBUILT at
|
||||
`scratchpad/i860dis.py` from binutils i860 opcodes, `scratchpad/i860_opcodes.h`).
|
||||
**The heat model [T1]:** per 30Hz board frame `h *= cool_a`; `RGB_ch = h_old*(h_new*cook_ch
|
||||
− 0.25) + 0.25` (K=0.25 @VA 0xF080 = the ember floor); kill at h ≤ 1e-4 (@0xF0A0); alpha −=
|
||||
cool_b per frame; `pos += vel`, gravity as accel. y_off = the particle KILL PLANE
|
||||
(Chunks −1 = die at the ground) [T3 data-indicated]. Burst frame randomly spun (quadratic
|
||||
small-angle rot, θ from the host-fed random table cmd 0x21). Implemented in L4VIDEO.cpp:
|
||||
`BTInstallSfx`/`BTStartSfx` + sfxMode particles in the BTPfx layer; parser beside the psfx
|
||||
page walk (`[sfx] page 'effects_to_load': 13 old-style specialfx installed`). Effect-number
|
||||
routing fixed in BOTH consumers (ExplosionClassID + DPLIndependantEffect): <100 → specialfx,
|
||||
≥1000 → psfx slot n−1000. **Missile/cannon hits now bundle** [T2]: BTProjectile carries
|
||||
shooter + weaponSubsys; contact routes through the SHOOTER's SubsystemMessageManager
|
||||
(AddDamageMessage w/ subsystemID → mslhit/acanhit explosion at the impact), mirroring the
|
||||
laser path's SendDamageMessage. Cone shape (v_bias as up-bias) + per-type draw weights are
|
||||
[T3]; the full type-0/1/2 draw-geometry decode in the firmware draw fn (0xf0416160) remains
|
||||
open.
|
||||
|
||||
## Weapon-hit forensics closeout (2026-07-12, workflow-verified) [T1]
|
||||
- **The HIT PACKAGES (BTL4.RES model lists, byte-verified):** ppchit=[8], lzrhit=[9],
|
||||
mghit=[7,11], canhit=[11] (no "acanhit"), mslhit=**[10, 12, 1023]** (MissileHit + GroundHit +
|
||||
psfx23 SHKWAVE), gndhit=[12], minhit=[1002], majhit=[1003], explode=[6]; dam1-4hit=[1002-1005],
|
||||
desthit/stephit=[1008]; cockpit "i" variants (lzrhiti etc.) = same sfx + idam 1009-1013; ALL
|
||||
eight mech death lists = ["104","3","4","5","1007","15","1001"].
|
||||
- **"Silver mist exactly 5×, perfectly timed" ROOT CAUSE:** SHKWAVE.PFX authors maxIssue=5,
|
||||
releasePeriod=0.2, **rate=1** — the ONE shipped .PFX where rate ≠ maxIssue/releasePeriod; the
|
||||
emitter trusted 'rate' → 5 huge white shells (exp 350/s) at exactly 1Hz. **Emission-window
|
||||
rule hardened:** effective rate = maxIssue/releasePeriod always (BTDrawPfx emitter).
|
||||
- **SpecialFX palette [T1 firmware]:** instance init @0xf04128d8: inst[0x19c]=cool_a (per
|
||||
board-frame heat decay), inst[0x1a4]=cool_b (× raw dt in the step = PER-SECOND fade),
|
||||
inst[0x188]=7.0s cap, heat=1.0. The heat channels are BRIGHTNESS over an inherently
|
||||
FIRE-coloured texture (firesmoke through its material ramp): sfx batch draws the FIERY-ramped
|
||||
sheet, psfx keeps GRAYSCALE (authored colours decide — DNBOOM orange, DDAM gray). Drawing sfx
|
||||
grayscale = silver mist; scaling fade ×30 = 0.1s pops ("nothing happens on laser hits").
|
||||
- **Damage-band .PFX shapes are authored GENTLE (puffy clouds):** bands 1-4 of ndam/idam/ddam
|
||||
rise at 1-4 u/s with buoyant accel; ONLY band 5 + the BOOM/SHKWAVE files are explosive-omni.
|
||||
idam = scaled-down WORLD venting smoke (half radius/counts of ddam), not cockpit-only.
|
||||
N-set = night = same physics as D-set with fewer particles. NSRM/DSRM trails = 0.1s overbright
|
||||
ORANGE directional puffs (never silver).
|
||||
- **messmgr explosion DEDUP CONFIRMED [T1, decomp-read @0049b784]:** `Find` (weaponExplosions
|
||||
vtable slot 0xC) gates `AddValue` (slot 8) — ONE explosion per unique weapon resource per
|
||||
frame, exactly as reconstructed. (A workflow agent claimed "one per record, staggered
|
||||
0.1s" — REFUTED; the stagger applies to the deduped queue.) Missile multiplicity comes from
|
||||
the ORDNANCE side instead: Missile::MoveAndCollide @004bef78 detonates per round — ported as
|
||||
BTSpawnRoundDetonation (mech4) + rack-tube launch spread [T3]. Alpha-strike drama = distinct
|
||||
per-weapon-TYPE packages (lzrhit+ppchit) + the victim's band fire, not explosion count.
|
||||
- Remaining [T3]: sfx cone shape (v_bias as up-bias), size ×0.5 + 1.25m textured floor +
|
||||
hot-phase occlusion 0.45..0.9, burst scatter ×0.2; trail
|
||||
density is frame-rate-dependent (2/frame @60fps = 2× pod density); .PFX 'repeats' unconsumed
|
||||
(all shipped files author 1); replicant visual salvo chases the stale fire-time aim point
|
||||
(no re-lead without a target handle).
|
||||
|
||||
## Weapon beams (the unported dpl_* layer)
|
||||
The `dpl_*` beam renderable was never ported → beams are drawn by `BTPushBeam`/`BTDrawBeams`
|
||||
(L4VIDEO). The real look is the `ermlaser.bgf` TUBE (native Z 0..−2000, UVs tiled) with the scrolling
|
||||
|
||||
+449
-50
@@ -677,6 +677,24 @@ struct BTPfxParticle
|
||||
float rad, exp, dexp;
|
||||
float colorWarp, alphaWarp; // def-level warps, carried per particle
|
||||
float sI[4], sO[4], eI[4], eO[4];
|
||||
// OLD-STYLE SPECIALFX mode (the VREND.MNG heat model, firmware-decoded
|
||||
// 2026-07-12): when sfxMode != 0 the colour comes from the board's heat
|
||||
// equation instead of the .PFX colour quads:
|
||||
// h *= coolMul per board frame (30 Hz);
|
||||
// channel = h_old*(h_new*cook_ch - 0.25) + 0.25 (0.25 = the ember floor,
|
||||
// firmware constant @VA 0xF080); kill at h <= 1e-4 (@0xF0A0), at
|
||||
// fade <= 0 (fade -= coolSub per board frame), or below the kill plane
|
||||
// (the descriptor's y_off -- Chunks die 1m under the burst: the floor).
|
||||
int sfxMode;
|
||||
float heat, coolMul, coolSub, fade;
|
||||
float cookR, cookG, cookB;
|
||||
float killY;
|
||||
int sfxType; // descriptor 'type' (draw weight)
|
||||
int uvVariant; // 0..7 mirror/swap of the noise UVs
|
||||
// (de-correlates the stamps like the
|
||||
// board's random burst spin; mirror/
|
||||
// swap keeps UVs in 0..1 so the
|
||||
// CLAMPed radial mask stays valid)
|
||||
};
|
||||
static std::vector<BTPfxEmitter> gBTPfxEmitters;
|
||||
static std::vector<BTPfxParticle> gBTPfxParticles;
|
||||
@@ -769,7 +787,7 @@ void BTStartPfxFrame(int effect_number, float x, float y, float z,
|
||||
const BTPfxDef &d = gBTPfxDefs[effect_number];
|
||||
if (!d.valid)
|
||||
return;
|
||||
if (gBTPfxEmitters.size() > 64) // runaway guard
|
||||
if (gBTPfxEmitters.size() > 256) // runaway guard
|
||||
return;
|
||||
BTPfxEmitter e;
|
||||
e.def = &d;
|
||||
@@ -821,7 +839,8 @@ void BTPfxTrailPuff(int effect_number, float x, float y, float z,
|
||||
static void BTPfxSpawn(const BTPfxEmitter &e)
|
||||
{
|
||||
const BTPfxDef &d = *e.def;
|
||||
if (gBTPfxParticles.size() > 2048) // global particle cap
|
||||
if (gBTPfxParticles.size() > 8192) // global particle cap (2048 starved
|
||||
// laser bursts during missile volleys)
|
||||
return;
|
||||
BTPfxParticle p;
|
||||
// Sample in the effect's LOCAL frame, then orient into the world through
|
||||
@@ -859,9 +878,126 @@ static void BTPfxSpawn(const BTPfxEmitter &e)
|
||||
p.eI[i] = d.eI[i] + d.eIv[i] * BTPfxRand01();
|
||||
p.eO[i] = d.eO[i] + d.eOv[i] * BTPfxRand01();
|
||||
}
|
||||
p.sfxMode = 0; // .PFX path: authored colour quads
|
||||
p.uvVariant = (int)(BTPfxRand01() * 7.99f);
|
||||
gBTPfxParticles.push_back(p);
|
||||
}
|
||||
|
||||
//===========================================================================//
|
||||
// OLD-STYLE SPECIALFX (the "more fire and 'splosions" layer) -- the 1995
|
||||
// per-weapon impact effects: BTDPL.INI [effects_to_load] "specialfxN=Name" +
|
||||
// [Name] descriptor sections (Chunks1/Sparks/PPCHit/LaserHit/MissileHit/
|
||||
// Fireball/...). These are the dpl EFFECT-TABLE entries that effect numbers
|
||||
// < 100 index (the 1996 ExplosionScripts' own comments match: 3=Chunks,
|
||||
// 12=GroundHit, 15=Fireball) -- NOT psfx slots.
|
||||
//
|
||||
// SEMANTICS [T1, decoded from the i860 firmware VREND.MNG 2026-07-12]:
|
||||
// - host installer FUN_00490210 ships the descriptor VERBATIM (cmd 0x1C,
|
||||
// 0x44 bytes; trigger = cmd 0x1B {id, dcs, 4x4 matrix});
|
||||
// - per board frame (30 Hz): h *= cool_a; RGB_ch = h_old*(h_new*cook_ch
|
||||
// - 0.25) + 0.25 (superbright start -> 0.25 ember floor; constants pool
|
||||
// @VA 0xF070: K=0.25 @0xF080, kill floor 1e-4 @0xF0A0);
|
||||
// - alpha fades linearly (the step: out[0x40] -= dt * rate);
|
||||
// - pos += vel; the burst frame is randomly SPUN per trigger (quadratic
|
||||
// small-angle rotation, theta from the host-fed random table);
|
||||
// - "variance" is DEAD DATA in the shipping pod: the binary looks up
|
||||
// "variance" (@004f2604) but every INI section spells "varience" -- the
|
||||
// lookup missed and the field stayed 0. Honoured (not parsed).
|
||||
// - y_off reads as the particle KILL PLANE relative to the burst origin
|
||||
// (Chunks -1 = die at the ground; hits -3) [T3 -- strongly data-indicated].
|
||||
//===========================================================================//
|
||||
struct BTSfxDef
|
||||
{
|
||||
int valid;
|
||||
int type; // 0 = chunks (solid debris), 1/2 = fiery ball
|
||||
int noTex; // texture=NOTEX(T): untextured sparks
|
||||
float size, velocity, vBias, yOff;
|
||||
float cook[3]; // RGB heat gains
|
||||
float gravity;
|
||||
float coolMul, coolSub;
|
||||
int count, repeats;
|
||||
};
|
||||
#define BT_SFX_SLOTS 32
|
||||
static BTSfxDef gBTSfxDefs[BT_SFX_SLOTS];
|
||||
|
||||
int BTInstallSfx(int slot, int type, int no_tex, float size, float velocity,
|
||||
float v_bias, float y_off, const float cook[3], float gravity,
|
||||
float cool_mul, float cool_sub, int count, int repeats)
|
||||
{
|
||||
if (slot < 0 || slot >= BT_SFX_SLOTS)
|
||||
return 0;
|
||||
BTSfxDef &d = gBTSfxDefs[slot];
|
||||
d.type = type; d.noTex = no_tex;
|
||||
d.size = size; d.velocity = velocity; d.vBias = v_bias; d.yOff = y_off;
|
||||
d.cook[0] = cook[0]; d.cook[1] = cook[1]; d.cook[2] = cook[2];
|
||||
d.gravity = gravity; d.coolMul = cool_mul; d.coolSub = cool_sub;
|
||||
d.count = count > 0 ? count : 1;
|
||||
d.repeats = repeats > 0 ? repeats : 1;
|
||||
d.valid = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void BTStartSfx(int effect_number, float x, float y, float z)
|
||||
{
|
||||
if (effect_number < 0 || effect_number >= BT_SFX_SLOTS)
|
||||
return;
|
||||
const BTSfxDef &d = gBTSfxDefs[effect_number];
|
||||
if (!d.valid)
|
||||
return;
|
||||
for (int i = 0; i < d.count; ++i)
|
||||
{
|
||||
if (gBTPfxParticles.size() > 8192)
|
||||
return;
|
||||
BTPfxParticle p;
|
||||
memset(&p, 0, sizeof(p));
|
||||
// Random burst direction; v_bias biases it upward (Sparks 1.4 =
|
||||
// fountain, GroundHit 0.01 = flat ring, Fireball 0.99 = rising
|
||||
// column -- the authored values read cleanly under this model).
|
||||
// The firmware spins the whole burst frame by a random angle;
|
||||
// per-particle isotropic randoms give the same coverage.
|
||||
float ax = BTPfxRand01() * 2.0f - 1.0f;
|
||||
float ay = BTPfxRand01() * 2.0f - 1.0f;
|
||||
float az = BTPfxRand01() * 2.0f - 1.0f;
|
||||
float dl = sqrtf(ax * ax + ay * ay + az * az);
|
||||
if (dl < 1e-4f) { ax = 0; ay = 1; az = 0; dl = 1; }
|
||||
ax /= dl; ay /= dl; az /= dl;
|
||||
ay = fabsf(ay) * 0.3f + d.vBias; // upward bias
|
||||
dl = sqrtf(ax * ax + ay * ay + az * az);
|
||||
ax /= dl; ay /= dl; az /= dl;
|
||||
float spd = d.velocity * (0.6f + 0.4f * BTPfxRand01());
|
||||
// burst scatter: break the perfect overlap of count co-spawned
|
||||
// sprites (the flash read as a single smooth ball) [T3]
|
||||
float sc = d.size * 0.2f;
|
||||
p.pos = D3DXVECTOR3(
|
||||
x + sc * (BTPfxRand01() * 2.0f - 1.0f),
|
||||
y + sc * (BTPfxRand01() * 2.0f - 1.0f),
|
||||
z + sc * (BTPfxRand01() * 2.0f - 1.0f));
|
||||
p.vel = D3DXVECTOR3(ax * spd, ay * spd, az * spd);
|
||||
p.accel = D3DXVECTOR3(0.0f, -d.gravity, 0.0f);
|
||||
p.age = 0.0f;
|
||||
p.life = 10.0f; // real kills: heat/fade/plane
|
||||
// size scale [T3]: x0.5 (size ~ diameter) with a 1.25m visibility
|
||||
// floor for textured bursts -- combat ranges run 100m+; LaserHit
|
||||
// (authored size 1.9) at <1m rendered ~8px = "practically nothing"
|
||||
// (user-rejected). MissileHit 12 -> 6m radius core. Sparks/NOTEX
|
||||
// keep their authored tininess (they read by count, not size).
|
||||
p.rad = (d.size > 0.01f) ? d.size * 0.5f : 0.15f;
|
||||
if (!d.noTex && d.size > 0.01f && p.rad < 1.25f) p.rad = 1.25f;
|
||||
p.exp = 0.0f; p.dexp = 0.0f;
|
||||
p.colorWarp = 1.0f; p.alphaWarp = 1.0f;
|
||||
p.sfxMode = 1;
|
||||
p.heat = 1.0f;
|
||||
p.coolMul = (d.coolMul > 0.0f && d.coolMul < 1.0f) ? d.coolMul : 0.9f;
|
||||
p.coolSub = d.coolSub > 0.0f ? d.coolSub : 0.02f;
|
||||
p.fade = 1.0f;
|
||||
p.cookR = d.cook[0]; p.cookG = d.cook[1]; p.cookB = d.cook[2];
|
||||
p.killY = y + d.yOff;
|
||||
p.sfxType = d.type;
|
||||
p.uvVariant = (int)(BTPfxRand01() * 7.99f);
|
||||
gBTPfxParticles.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
// The beams' decoded grit texture (built in BTDrawBeams) -- shared with the
|
||||
// particles so the fire quads carry the authentic firesmoke-family noise.
|
||||
extern LPDIRECT3DTEXTURE9 BTGetBeamGritTexture();
|
||||
@@ -870,6 +1006,60 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
{
|
||||
// ---- sim ----
|
||||
if (dt > 0.1f) dt = 0.1f; // stall guard
|
||||
|
||||
// DEV HARNESS: BT_FX_TEST="10" (or "10,1003,15") fires the listed effect
|
||||
// numbers every 3s, 30m in front of the camera -- iterate the effect look
|
||||
// via screenshots without a live fire loop. <100 = specialfx descriptor,
|
||||
// >=1000 = psfx slot n-1000 (the real dispatch encodings).
|
||||
{
|
||||
static int s_fxTestN = -1;
|
||||
static int s_fxTest[8];
|
||||
static float s_fxTestTimer = 0.0f;
|
||||
if (s_fxTestN < 0)
|
||||
{
|
||||
s_fxTestN = 0;
|
||||
const char *tv = getenv("BT_FX_TEST");
|
||||
if (tv != 0)
|
||||
{
|
||||
const char *p = tv;
|
||||
while (*p && s_fxTestN < 8)
|
||||
{
|
||||
s_fxTest[s_fxTestN++] = atoi(p);
|
||||
while (*p && *p != ',') ++p;
|
||||
if (*p == ',') ++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s_fxTestN > 0)
|
||||
{
|
||||
s_fxTestTimer -= dt;
|
||||
if (s_fxTestTimer <= 0.0f)
|
||||
{
|
||||
s_fxTestTimer = 3.0f;
|
||||
float cx, cy, cz;
|
||||
d3d_OBJECT::GetCameraPosition(&cx, &cy, &cz);
|
||||
// fire on BOTH sides of the camera (view-matrix handedness
|
||||
// varies; one of the two is dead-centre in frame)
|
||||
for (int side = -1; side <= 1; side += 2)
|
||||
{
|
||||
float fx = cx + view->_13 * 30.0f * side;
|
||||
float fy = cy + view->_23 * 30.0f * side + 4.0f;
|
||||
float fz = cz + view->_33 * 30.0f * side;
|
||||
for (int i = 0; i < s_fxTestN; ++i)
|
||||
{
|
||||
extern void BTStartSfx(int, float, float, float);
|
||||
if (s_fxTest[i] >= 1000)
|
||||
BTStartPfx(s_fxTest[i] - 1000, fx, fy, fz);
|
||||
else
|
||||
BTStartSfx(s_fxTest[i], fx, fy, fz);
|
||||
}
|
||||
}
|
||||
DEBUG_STREAM << "[fxtest] fired +-30m of cam (" << cx << "," << cy << "," << cz
|
||||
<< ") particles=" << (int)gBTPfxParticles.size()
|
||||
<< " emitters=" << (int)gBTPfxEmitters.size() << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t ei = 0; ei < gBTPfxEmitters.size(); ++ei)
|
||||
{
|
||||
BTPfxEmitter &e = gBTPfxEmitters[ei];
|
||||
@@ -881,7 +1071,17 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
// DDAM2 35/16~=2, DDTHSMK 30/3=10) -- release_period IS the emission
|
||||
// WINDOW, not a batch interval. (The old one-burst batching dumped a
|
||||
// 10-second smoke plume in a single frame's puff.)
|
||||
e.emitAccum += d.rate * dt;
|
||||
// EMISSION-WINDOW RULE (hardened 2026-07-12): release_period IS the
|
||||
// emission window -- derive the rate from maxIssue/releasePeriod
|
||||
// instead of trusting the authored 'rate' field. SHKWAVE.PFX (the
|
||||
// missile-hit shockwave, mslhit's 1023) authors maxIssue=5,
|
||||
// releasePeriod=0.2, rate=1 -- the one shipped file where they
|
||||
// disagree; trusting 'rate' stretched the single 0.2s layered
|
||||
// 5-shell blast into 5 metronomic 1Hz pulses ("silver mist exactly
|
||||
// 5 times at perfectly timed intervals" -- user-reported).
|
||||
float erate = (d.releasePeriod > 1e-3f)
|
||||
? (float)d.maxIssue / d.releasePeriod : d.rate;
|
||||
e.emitAccum += erate * dt;
|
||||
int n = (int)e.emitAccum;
|
||||
if (n > 0)
|
||||
{
|
||||
@@ -901,12 +1101,28 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
gBTPfxEmitters.resize(w);
|
||||
}
|
||||
{ // advance + expire particles
|
||||
const float boardSteps = dt * 30.0f; // the pod stepped the sfx sim per 30Hz frame
|
||||
size_t w = 0;
|
||||
for (size_t i = 0; i < gBTPfxParticles.size(); ++i)
|
||||
{
|
||||
BTPfxParticle &p = gBTPfxParticles[i];
|
||||
p.age += dt;
|
||||
if (p.age >= p.life) continue;
|
||||
if (p.sfxMode)
|
||||
{
|
||||
// the firmware kills (heat floor 1e-4 @0xF0A0; the y_off kill
|
||||
// plane). TIMEBASE [T1, re-read from the step disasm]: heat
|
||||
// decays per BOARD FRAME (f18 = f17*f16, no dt -- hence the
|
||||
// 30Hz boardSteps exponent) but the alpha fade is multiplied
|
||||
// by RAW dt (@f0413754-64: out[0x40] -= dt * inst[0x1a4]) --
|
||||
// cool_b is PER-SECOND. The earlier x30 scaling killed every
|
||||
// burst in ~0.1s: fireballs read as a subliminal pop and
|
||||
// laser hits as "nothing happens" (user-reported).
|
||||
p.heat *= powf(p.coolMul, boardSteps);
|
||||
p.fade -= p.coolSub * dt;
|
||||
if (p.heat <= 1e-4f || p.fade <= 0.0f) continue;
|
||||
if (p.pos.y < p.killY) continue;
|
||||
}
|
||||
p.vel += p.accel * dt;
|
||||
p.pos += p.vel * dt;
|
||||
p.rad += p.exp * dt;
|
||||
@@ -977,6 +1193,10 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
// Fallback when BINTA.VTX is absent: the old grit x radial single-tex bake.
|
||||
static LPDIRECT3DTEXTURE9 s_pfxTex = 0; // fallback single-texture
|
||||
static LPDIRECT3DTEXTURE9 s_pfxNoise = 0; // bintA through the "fiery" ramp
|
||||
static LPDIRECT3DTEXTURE9 s_pfxNoiseGray = 0; // RAW bintA (grayscale) -- the
|
||||
// old-style specialfx sample the
|
||||
// TEXTURE, not the ramped material:
|
||||
// the heat model supplies ALL colour
|
||||
static LPDIRECT3DTEXTURE9 s_pfxMask = 0; // radial falloff (stage 1, CLAMP)
|
||||
static int s_pfxTexTried = 0;
|
||||
if (!s_pfxTexTried)
|
||||
@@ -1013,6 +1233,28 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
}
|
||||
else { s_pfxNoise->Release(); s_pfxNoise = 0; }
|
||||
}
|
||||
if (s_pfxNoise && fireImg.ok &&
|
||||
SUCCEEDED(dev->CreateTexture(fireImg.w, fireImg.h, 1, 0, D3DFMT_A8R8G8B8,
|
||||
D3DPOOL_MANAGED, &s_pfxNoiseGray, NULL)))
|
||||
{
|
||||
D3DLOCKED_RECT lr;
|
||||
if (SUCCEEDED(s_pfxNoiseGray->LockRect(0, &lr, NULL, 0)))
|
||||
{
|
||||
for (int y = 0; y < fireImg.h; ++y)
|
||||
{
|
||||
DWORD *dst = (DWORD *)((char *)lr.pBits + y * lr.Pitch);
|
||||
for (int x = 0; x < fireImg.w; ++x)
|
||||
{
|
||||
uint32_t s = fireImg.argb[(size_t)y * fireImg.w + x];
|
||||
int l = (((s >> 16) & 0xFF) + ((s >> 8) & 0xFF) + (s & 0xFF)) / 3;
|
||||
int a = (int)((0.35f + 0.65f * (l / 255.0f)) * 255.0f);
|
||||
dst[x] = ((DWORD)a << 24) | (l << 16) | (l << 8) | l;
|
||||
}
|
||||
}
|
||||
s_pfxNoiseGray->UnlockRect(0);
|
||||
}
|
||||
else { s_pfxNoiseGray->Release(); s_pfxNoiseGray = 0; }
|
||||
}
|
||||
if (s_pfxNoise &&
|
||||
SUCCEEDED(dev->CreateTexture(TW, TH, 1, 0, D3DFMT_A8R8G8B8,
|
||||
D3DPOOL_MANAGED, &s_pfxMask, NULL)))
|
||||
@@ -1099,29 +1341,32 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
}
|
||||
}
|
||||
|
||||
// The authored SCROLL clock (firesmoke1's rate; the shipped variants share
|
||||
// the sheet and differ only in rate, so the port rolls every sprite at the
|
||||
// dominant one). Accumulate the OFFSETS and wrap each into [0,1) -- an
|
||||
// unbounded time * rate loses float precision over a long session (the
|
||||
// tsphere scroll-precision lesson).
|
||||
static float s_pfxScrollU = 0.0f, s_pfxScrollV = 0.0f;
|
||||
s_pfxScrollU += 0.05f * dt; s_pfxScrollU -= floorf(s_pfxScrollU);
|
||||
s_pfxScrollV += -0.331f * dt; s_pfxScrollV -= floorf(s_pfxScrollV);
|
||||
|
||||
if (s_pfxNoise)
|
||||
{
|
||||
// stage 0: diffuse x the scrolled fire noise (WRAPped)
|
||||
dev->SetTexture(0, s_pfxNoise);
|
||||
// stage 0: diffuse x the scrolled noise (WRAPped). GRAYSCALE: every
|
||||
// .PFX and specialfx descriptor references the TEXTURE
|
||||
// btfx:firesmokeN_scr_tex (the raw bintA noise), NOT the fiery-ramped
|
||||
// material -- the AUTHORED per-particle colours are the sole colour
|
||||
// source (DNBOOM's 0.8,0.3,0 = the orange fire; DDAM's 0.5-gray = the
|
||||
// smoke). The earlier ramp-baked sheet gilded the gray smoke bands
|
||||
// into "gold blasts of light" (user-rejected). The fiery ramp
|
||||
// belongs to the MATERIAL path only (flamebig etc. via bgfload).
|
||||
dev->SetTexture(0, s_pfxNoiseGray ? s_pfxNoiseGray : s_pfxNoise);
|
||||
dev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
dev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
|
||||
dev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);
|
||||
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
|
||||
dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
|
||||
D3DXMATRIX tm; D3DXMatrixIdentity(&tm);
|
||||
tm._31 = s_pfxScrollU; tm._32 = s_pfxScrollV; // (u,v) translation
|
||||
dev->SetTransform(D3DTS_TEXTURE0, &tm);
|
||||
dev->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
|
||||
// NO SCROLL on particle stamps: the authored SCROLL belongs to the
|
||||
// TEXTURE record for the MATERIAL path (flame meshes); sliding the
|
||||
// noise through short-lived particle sprites reads as a shimmering
|
||||
// crawl ("not sure what I'm looking at" -- user-rejected). Each
|
||||
// particle instead gets a FIXED random window into the tileable
|
||||
// sheet (u0/v0, set at spawn; the WRAP addressing makes any window
|
||||
// valid) -- the board's per-burst random spin de-correlation.
|
||||
dev->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
|
||||
dev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
|
||||
dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
|
||||
// stage 1: x the CLAMPed radial mask (same UV set, UNscrolled) -- both
|
||||
@@ -1158,6 +1403,15 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
struct V { float x, y, z; DWORD c; float u, v; };
|
||||
static std::vector<V> verts;
|
||||
verts.clear();
|
||||
// SPECIALFX batch: drawn with the FIERY-RAMPED sheet. On the IG board the
|
||||
// heat channels are BRIGHTNESS multipliers over an inherently fire-
|
||||
// coloured texture (firesmoke through its material ramp): white-hot flash
|
||||
// -> ember-orange dissipating cloud (the demo fireballs). The .PFX batch
|
||||
// keeps the GRAYSCALE sheet (its authored colours decide: DNBOOM orange
|
||||
// fire, DDAM gray smoke). Drawing sfx ember clouds grayscale made them
|
||||
// "explosive silver mist" (user-rejected).
|
||||
static std::vector<V> sfxVerts;
|
||||
sfxVerts.clear();
|
||||
|
||||
// BACK-TO-FRONT order: premultiplied occlusion (smoke) composites in depth
|
||||
// order; unsorted draws pop when a near puff renders before a far one.
|
||||
@@ -1185,16 +1439,54 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
float tc = powf(t, 1.0f / p.colorWarp);
|
||||
float ta = powf(t, 1.0f / p.alphaWarp);
|
||||
|
||||
// two quads: outer glow (2.2x radius) then inner core
|
||||
for (int layer = 0; layer < 2; ++layer)
|
||||
// The .PFX sI/sO are the INTERIOR/EXTERIOR colours of ONE sprite (the
|
||||
// dpl_PARTICLE model) -- approximated as an exterior quad at the
|
||||
// AUTHORED radius + an interior core at 0.45x, with the exterior's
|
||||
// occlusion halved (it is the sprite's soft skirt, not a second
|
||||
// particle). The old 2.2x "outer glow" inflated every particle and
|
||||
// doubled its occlusion -- 20 overlapping damage-smoke particles
|
||||
// merged into one giant black blob (seen in the BT_FX_TEST captures).
|
||||
// Old-style specialfx draw ONE sprite per particle (the firmware
|
||||
// draw emits a single projected sprite).
|
||||
for (int layer = (p.sfxMode ? 1 : 0); layer < 2; ++layer)
|
||||
{
|
||||
const float *cs = layer ? p.sI : p.sO;
|
||||
const float *ce = layer ? p.eI : p.eO;
|
||||
float scale = layer ? 1.0f : 2.2f;
|
||||
float r = cs[0] + (ce[0] - cs[0]) * tc;
|
||||
float g = cs[1] + (ce[1] - cs[1]) * tc;
|
||||
float b = cs[2] + (ce[2] - cs[2]) * tc;
|
||||
float a = cs[3] + (ce[3] - cs[3]) * ta;
|
||||
float scale = layer ? 0.45f : 1.0f;
|
||||
if (p.sfxMode) scale = 1.0f;
|
||||
float r, g, b, a;
|
||||
if (p.sfxMode)
|
||||
{
|
||||
// THE FIRMWARE HEAT EQUATION [T1]: channel = h_old*(h_new*
|
||||
// cook_ch - 0.25) + 0.25. h was already stepped in the sim
|
||||
// above; approximate h_old over this frame's step from the
|
||||
// per-frame factor (h_old = h / coolMul^steps was h before
|
||||
// the step -- one step back is close enough at 30Hz).
|
||||
float hNew = p.heat;
|
||||
float hOld = p.heat / p.coolMul; // one board step back
|
||||
if (hOld > 1.0f) hOld = 1.0f;
|
||||
r = hOld * (hNew * p.cookR - 0.25f) + 0.25f;
|
||||
g = hOld * (hNew * p.cookG - 0.25f) + 0.25f;
|
||||
b = hOld * (hNew * p.cookB - 0.25f) + 0.25f;
|
||||
if (r < 0) r = 0; if (r > 1) r = 1;
|
||||
if (g < 0) g = 0; if (g > 1) g = 1;
|
||||
if (b < 0) b = 0; if (b > 1) b = 1;
|
||||
// occlusion weight: solid debris chunks occlude; fiery balls
|
||||
// mostly-occlude while HOT (bright fire body), thinning as
|
||||
// the heat dies (0.5 halved the flash into a faint wisp at
|
||||
// combat range -- "practically nothing", user-rejected)
|
||||
float occl = (p.sfxType == 0) ? 0.85f
|
||||
: (0.45f + 0.45f * (p.heat > 1.0f ? 1.0f : p.heat));
|
||||
a = p.fade * occl;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = cs[0] + (ce[0] - cs[0]) * tc;
|
||||
g = cs[1] + (ce[1] - cs[1]) * tc;
|
||||
b = cs[2] + (ce[2] - cs[2]) * tc;
|
||||
a = cs[3] + (ce[3] - cs[3]) * ta;
|
||||
if (layer == 0) a *= 0.5f; // exterior skirt: half occlusion
|
||||
}
|
||||
if (a <= 0.0f) continue;
|
||||
if (a > 1.0f) a = 1.0f;
|
||||
// PREMULTIPLIED: vertex rgb = colour x alpha (the framebuffer
|
||||
@@ -1210,20 +1502,62 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
|
||||
float rad = p.rad * scale;
|
||||
D3DXVECTOR3 rv = right * rad, uv = up * rad;
|
||||
// noise-stamp orientation: mirror/swap the 0..1 UVs by the
|
||||
// particle's variant (stays in 0..1 -> the CLAMPed mask holds)
|
||||
float cu[4] = { 0, 0, 1, 1 }; // corners: BL, TL, TR, BR
|
||||
float cv[4] = { 1, 0, 0, 1 };
|
||||
for (int k = 0; k < 4; ++k)
|
||||
{
|
||||
float uu2 = cu[k], vv2 = cv[k];
|
||||
if (p.uvVariant & 1) uu2 = 1.0f - uu2;
|
||||
if (p.uvVariant & 2) vv2 = 1.0f - vv2;
|
||||
if (p.uvVariant & 4) { float t = uu2; uu2 = vv2; vv2 = t; }
|
||||
cu[k] = uu2; cv[k] = vv2;
|
||||
}
|
||||
V q[6];
|
||||
q[0].x = p.pos.x - rv.x - uv.x; q[0].y = p.pos.y - rv.y - uv.y; q[0].z = p.pos.z - rv.z - uv.z; q[0].u = 0; q[0].v = 1;
|
||||
q[1].x = p.pos.x - rv.x + uv.x; q[1].y = p.pos.y - rv.y + uv.y; q[1].z = p.pos.z - rv.z + uv.z; q[1].u = 0; q[1].v = 0;
|
||||
q[2].x = p.pos.x + rv.x + uv.x; q[2].y = p.pos.y + rv.y + uv.y; q[2].z = p.pos.z + rv.z + uv.z; q[2].u = 1; q[2].v = 0;
|
||||
q[0].x = p.pos.x - rv.x - uv.x; q[0].y = p.pos.y - rv.y - uv.y; q[0].z = p.pos.z - rv.z - uv.z; q[0].u = cu[0]; q[0].v = cv[0];
|
||||
q[1].x = p.pos.x - rv.x + uv.x; q[1].y = p.pos.y - rv.y + uv.y; q[1].z = p.pos.z - rv.z + uv.z; q[1].u = cu[1]; q[1].v = cv[1];
|
||||
q[2].x = p.pos.x + rv.x + uv.x; q[2].y = p.pos.y + rv.y + uv.y; q[2].z = p.pos.z + rv.z + uv.z; q[2].u = cu[2]; q[2].v = cv[2];
|
||||
q[3] = q[0];
|
||||
q[4] = q[2];
|
||||
q[5].x = p.pos.x + rv.x - uv.x; q[5].y = p.pos.y + rv.y - uv.y; q[5].z = p.pos.z + rv.z - uv.z; q[5].u = 1; q[5].v = 1;
|
||||
for (int k = 0; k < 6; ++k) { q[k].c = c; verts.push_back(q[k]); }
|
||||
q[5].x = p.pos.x + rv.x - uv.x; q[5].y = p.pos.y + rv.y - uv.y; q[5].z = p.pos.z + rv.z - uv.z; q[5].u = cu[3]; q[5].v = cv[3];
|
||||
std::vector<V> &sink = p.sfxMode ? sfxVerts : verts;
|
||||
for (int k = 0; k < 6; ++k) { q[k].c = c; sink.push_back(q[k]); }
|
||||
}
|
||||
}
|
||||
|
||||
// DIAG (BT_FX_TEST): prove the draw reaches pixels -- particle count, the
|
||||
// vertex count, the first particle's colour/alpha/pos, texture presence.
|
||||
if (getenv("BT_FX_TEST") != 0 && !gBTPfxParticles.empty())
|
||||
{
|
||||
static float s_drawLog = 0.0f;
|
||||
s_drawLog += dt;
|
||||
if (s_drawLog >= 2.0f)
|
||||
{
|
||||
s_drawLog = 0.0f;
|
||||
const BTPfxParticle &p0 = gBTPfxParticles[0];
|
||||
DEBUG_STREAM << "[pfx-draw] n=" << (int)gBTPfxParticles.size()
|
||||
<< " verts=" << (int)verts.size()
|
||||
<< " tex=" << (s_pfxNoiseGray ? "gray" : (s_pfxNoise ? "fiery" : (s_pfxTex ? "grit" : "NONE")))
|
||||
<< " p0(sfx=" << p0.sfxMode << " pos=" << p0.pos.x << "," << p0.pos.y << "," << p0.pos.z
|
||||
<< " rad=" << p0.rad << " heat=" << p0.heat << " fade=" << p0.fade
|
||||
<< ")\n" << std::flush;
|
||||
if (!verts.empty())
|
||||
DEBUG_STREAM << "[pfx-draw] v0=(" << verts[0].x << "," << verts[0].y << "," << verts[0].z
|
||||
<< ") c=" << std::hex << (unsigned)verts[0].c << std::dec << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
if (!verts.empty())
|
||||
dev->DrawPrimitiveUP(D3DPT_TRIANGLELIST, (UINT)verts.size() / 3,
|
||||
&verts[0], sizeof(V));
|
||||
if (!sfxVerts.empty())
|
||||
{
|
||||
// the fiery-ramped sheet for the heat-model bursts (see above)
|
||||
if (s_pfxNoise)
|
||||
dev->SetTexture(0, s_pfxNoise);
|
||||
dev->DrawPrimitiveUP(D3DPT_TRIANGLELIST, (UINT)sfxVerts.size() / 3,
|
||||
&sfxVerts[0], sizeof(V));
|
||||
}
|
||||
|
||||
dev->SetTexture(0, NULL);
|
||||
if (s_pfxNoise)
|
||||
@@ -4503,6 +4837,63 @@ void
|
||||
<< pfx_loaded << " effect definitions loaded\n" << std::flush;
|
||||
delete psfx_namelist;
|
||||
}
|
||||
|
||||
//
|
||||
// OLD-STYLE SPECIALFX ("specialfxN=Name" + [Name] descriptor pages) -- the
|
||||
// per-weapon impact effects (PPCHit/LaserHit/MissileHit/Chunks/Sparks/
|
||||
// Fireball...). Field names per the 1995 binary's parser (part_008.c:1546:
|
||||
// type/size/velocity/v_bias/y_off/variance/gravity/count/repeats +
|
||||
// "cook" "%f %f %f" + "cool" "%f %f" + texture). NOTE the binary reads
|
||||
// "variance" but the INI authors "varience" -- the lookup MISSED in the
|
||||
// shipping pod, so variance is authentically dead (not parsed here).
|
||||
// The 2007 loader's version-2-only walk below skips all of these
|
||||
// (version < 2 -> continue), which is why the layer never rendered.
|
||||
//
|
||||
{
|
||||
NameList *sfx_namelist =
|
||||
master_notation_file->MakeEntryList(starting_page_name, "specialfx");
|
||||
if (sfx_namelist != NULL)
|
||||
{
|
||||
extern int BTInstallSfx(int slot, int type, int no_tex, float size,
|
||||
float velocity, float v_bias, float y_off, const float cook[3],
|
||||
float gravity, float cool_mul, float cool_sub, int count, int repeats);
|
||||
int sfx_loaded = 0;
|
||||
for (entry = sfx_namelist->GetFirstEntry();
|
||||
entry;
|
||||
entry = entry->GetNextEntry())
|
||||
{
|
||||
int sfx_number = atoi(entry->GetName() + 9); // "specialfxN"
|
||||
const char *page = entry->GetChar();
|
||||
if (page == NULL || page[0] == '\0')
|
||||
continue;
|
||||
int type = 0, count = 1, repeats = 1;
|
||||
float size = 0, velocity = 0, v_bias = 0, y_off = 0, gravity = 0;
|
||||
float cook[3] = { 1, 1, 1 };
|
||||
float cool_mul = 0.9f, cool_sub = 0.02f;
|
||||
const char *sp = NULL;
|
||||
master_notation_file->GetEntry(page, "type", &type);
|
||||
master_notation_file->GetEntry(page, "size", &size);
|
||||
master_notation_file->GetEntry(page, "velocity", &velocity);
|
||||
master_notation_file->GetEntry(page, "v_bias", &v_bias);
|
||||
master_notation_file->GetEntry(page, "y_off", &y_off);
|
||||
master_notation_file->GetEntry(page, "gravity", &gravity);
|
||||
master_notation_file->GetEntry(page, "count", &count);
|
||||
master_notation_file->GetEntry(page, "repeats", &repeats);
|
||||
if (master_notation_file->GetEntry(page, "cook", &sp) && sp != NULL)
|
||||
sscanf(sp, "%f %f %f", &cook[0], &cook[1], &cook[2]);
|
||||
if (master_notation_file->GetEntry(page, "cool", &sp) && sp != NULL)
|
||||
sscanf(sp, "%f %f", &cool_mul, &cool_sub);
|
||||
int no_tex = 0;
|
||||
if (master_notation_file->GetEntry(page, "texture", &sp) && sp != NULL)
|
||||
no_tex = (strncmp(sp, "NOTEX", 5) == 0); // NOTEX / NOTEXT
|
||||
sfx_loaded += BTInstallSfx(sfx_number, type, no_tex, size, velocity,
|
||||
v_bias, y_off, cook, gravity, cool_mul, cool_sub, count, repeats);
|
||||
}
|
||||
DEBUG_STREAM << "[sfx] page '" << starting_page_name << "': "
|
||||
<< sfx_loaded << " old-style specialfx installed\n" << std::flush;
|
||||
delete sfx_namelist;
|
||||
}
|
||||
}
|
||||
////
|
||||
//// (original stubbed loader retained for provenance)
|
||||
////
|
||||
@@ -6840,29 +7231,40 @@ void
|
||||
<< entity->localOrigin.linearPosition.x
|
||||
<< ", " << entity->localOrigin.linearPosition.y
|
||||
<< ", " << entity->localOrigin.linearPosition.z << "], " << effect_number << ");" << std::endl << std::flush;
|
||||
// Both effect-number ENCODINGS resolve to the same psfx slot:
|
||||
// <100 = the raw dpl board number; >=1000 = the WinTesla-era
|
||||
// "1000+slot" INDIE id carried by the damage-band resources
|
||||
// (1002-1005 = ddam1-4 light..critical damage smoke, 1008 =
|
||||
// ddam5 zone-destroyed -- the SAME [pfx_day] mapping). BT
|
||||
// ships no INDIE descriptors (the version-2 specialfx pages
|
||||
// don't exist), so all slots route to the BT .PFX layer, with
|
||||
// the Explosion entity's ORIENTATION (the victim's frame) --
|
||||
// the .PFX offsets/velocities are authored mech-local.
|
||||
// TWO DISTINCT ENCODINGS (routing corrected 2026-07-12):
|
||||
// < 100 = the dpl EFFECT-TABLE number = an OLD-STYLE
|
||||
// specialfx descriptor (BTDPL.INI [effects_to_load]
|
||||
// "specialfxN": 8=PPCHit, 9=LaserHit, 10=MissileHit,
|
||||
// 3=Chunks1, 5=Sparks, 15=Fireball -- the 1996
|
||||
// script's own comments match) -> the firmware-
|
||||
// decoded heat-model burst (BTStartSfx);
|
||||
// >= 1000 = the "1000+slot" psfx id (1002-1005 = ddam1-4,
|
||||
// 1008 = ddam5, 1009-1013 = idam1-5) -> the .PFX
|
||||
// layer, oriented in the victim's frame.
|
||||
// (The old "<100 -> psfx slot" mapping rendered PPCHit as
|
||||
// ddam5 SMOKE and Chunks as damage smoke -- the "hits are
|
||||
// just a little smoke" report.)
|
||||
if (effect_number >= 1000)
|
||||
{
|
||||
int pfx_slot = (effect_number >= 1000)
|
||||
? effect_number - 1000 : effect_number;
|
||||
extern void BTStartPfxFrame(int, float, float, float,
|
||||
const float *, const float *, const float *);
|
||||
float xr[3] = { (float)entity->localToWorld(0,0), (float)entity->localToWorld(0,1), (float)entity->localToWorld(0,2) };
|
||||
float yr[3] = { (float)entity->localToWorld(1,0), (float)entity->localToWorld(1,1), (float)entity->localToWorld(1,2) };
|
||||
float zr[3] = { (float)entity->localToWorld(2,0), (float)entity->localToWorld(2,1), (float)entity->localToWorld(2,2) };
|
||||
BTStartPfxFrame(pfx_slot,
|
||||
BTStartPfxFrame(effect_number - 1000,
|
||||
(float)entity->localOrigin.linearPosition.x,
|
||||
(float)entity->localOrigin.linearPosition.y,
|
||||
(float)entity->localOrigin.linearPosition.z,
|
||||
xr, yr, zr);
|
||||
}
|
||||
else
|
||||
{
|
||||
extern void BTStartSfx(int, float, float, float);
|
||||
BTStartSfx(effect_number,
|
||||
(float)entity->localOrigin.linearPosition.x,
|
||||
(float)entity->localOrigin.linearPosition.y,
|
||||
(float)entity->localOrigin.linearPosition.z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -8630,17 +9032,14 @@ void
|
||||
}
|
||||
|
||||
//
|
||||
// Board effect numbers (<100) -- the 1995 dpl explosion/damage effects.
|
||||
// RECONSTRUCTED: routed to the BT .PFX particle layer (the [pfx_day]/
|
||||
// [pfx_night] psfxN mapping loads each number's authentic .PFX definition;
|
||||
// see the layer banner at the top of this file). This is what makes every
|
||||
// weapon-hit / damage-band / mech-death explosion actually VISIBLE -- the
|
||||
// original dpl_Effect(dpl_effect_type_explosion, ...) below was IG-board
|
||||
// hardware and was never ported.
|
||||
// Board effect numbers (<100) -- the 1995 dpl EFFECT TABLE = the old-style
|
||||
// specialfx descriptors (BTDPL.INI [effects_to_load]), NOT psfx slots
|
||||
// (routing corrected 2026-07-12; the psfx encoding is "1000+slot"). The
|
||||
// firmware-decoded heat-model burst renders them (BTStartSfx).
|
||||
//
|
||||
{
|
||||
extern void BTStartPfx(int effect_number, float x, float y, float z);
|
||||
BTStartPfx(effect_number, location.x, location.y, location.z);
|
||||
extern void BTStartSfx(int effect_number, float x, float y, float z);
|
||||
BTStartSfx(effect_number, location.x, location.y, location.z);
|
||||
}
|
||||
|
||||
//dpl_EXPLOSION_EFFECT_INFO my_explosion;
|
||||
|
||||
@@ -189,6 +189,26 @@ class NotationFile;
|
||||
// beam from this live sim state -- the authentic per-weapon fire look).
|
||||
//
|
||||
int BeamOn() const { return beamFlag; } // 0x46c
|
||||
Scalar DischargeTimer() const { return dischargeTimer; } // forensics
|
||||
// REPLICANT-side beam aging (port, 2026-07-13): replicated discharge
|
||||
// state arrives via update records, but replicant subsystems never
|
||||
// Execute -- a lost/undelivered beam-END record left the peer's beam
|
||||
// ON forever, drawn at its stale fire-time endpoint (the phantom
|
||||
// "4th gold beam", user-reported). Run the master's deterministic
|
||||
// countdown locally: no alarms, no upstream records; a live master
|
||||
// refreshes the state with every real record anyway.
|
||||
void ReplicantServiceBeam(Scalar dt)
|
||||
{
|
||||
if (beamFlag == 0)
|
||||
return;
|
||||
dischargeTimer -= dt;
|
||||
if (dischargeTimer <= 0.0f)
|
||||
{
|
||||
beamFlag = 0;
|
||||
firingActive = 0;
|
||||
dischargeTimer = dischargeTime;
|
||||
}
|
||||
}
|
||||
const Point3D &BeamEndpoint() const { return beamEndpoint; } // 0x460
|
||||
|
||||
public:
|
||||
|
||||
@@ -1390,6 +1390,8 @@ Mech::Mech(
|
||||
duckedTemplateMaxY = 0.0f;
|
||||
templateBottomLift = 0.0f;
|
||||
frameEntryWorldVelocity = Vector3D(0.0f, 0.0f, 0.0f); // collision-damage guard snapshot
|
||||
ramLastVictim = 0; // ram contact-edge state
|
||||
ramContactLinger = 0.0f;
|
||||
if (GroundReal() && GetCollisionVolumeCount() > 0
|
||||
&& collisionTemplate != 0 && collisionVolume != 0)
|
||||
{
|
||||
|
||||
@@ -639,6 +639,16 @@ public:
|
||||
// approach speed -- which is all the binary's StaticBounce ever saw.
|
||||
// By-name access only; declared after the layout-locked fields.
|
||||
Vector3D frameEntryWorldVelocity;
|
||||
// RAM CONTACT EDGE (port analog of the binary's bounce-separation): in the
|
||||
// 1995 mover, StaticBounce REVERSED worldLinearVelocity, so the frame after
|
||||
// a bump read as separating and the damage gate stopped repeat dispatch.
|
||||
// Our gait re-derives velocity every frame (still closing while the stick
|
||||
// is held / while respawn-overlapped), so sustained contact re-priced a
|
||||
// fresh full-speed ram EVERY frame (~60 dmg x 60fps = melt; the respawn
|
||||
// explode-loop). One bump = one damage event; re-arms after the contact
|
||||
// breaks for ramContactLinger seconds.
|
||||
Entity *ramLastVictim;
|
||||
Scalar ramContactLinger;
|
||||
|
||||
// AUTHENTIC TARGETING (task #36): the engine Reticle struct (MUNGA/RETICLE.h
|
||||
// [T0]) -- the mech's "TargetReticle" attribute (id 0x1d) binds to it, per the
|
||||
|
||||
+180
-22
@@ -153,6 +153,7 @@
|
||||
#include <cultural.hpp> // CulturalIcon::IsStoppingCollisionVolume / GetClassDerivations
|
||||
#include <hostmgr.hpp> // HostManager::GetEntityPointer (band-effect attacker resolve)
|
||||
#include <messmgr.hpp> // SubsystemMessageManager (task #7 consolidated delivery)
|
||||
#include <mechweap.hpp> // MechWeapon::GetExplosionResourceID (per-round detonation)
|
||||
#if !defined(PLAYER_HPP)
|
||||
# include <player.hpp> // Player::VehicleDeadMessage -- the death->respawn notification (task #52)
|
||||
#endif
|
||||
@@ -774,18 +775,60 @@ struct BTProjectile {
|
||||
Scalar aimOffsetY; // vertical aim offset vs the target's origin (live re-lead)
|
||||
Scalar damage; // <= 0 -> VISUAL-ONLY round (replicant-side salvo mirror)
|
||||
int guided; // 1 = missile (seeker loft + steering); 0 = ballistic (straight)
|
||||
Entity *shooter; // the firing mech (messmgr explosion bundling at impact)
|
||||
int weaponSubsys; // firing weapon's roster index (-1 = unthreaded) --
|
||||
// resolves the weapon's ExplosionModelFile (mslhit/
|
||||
// acanhit) in the messmgr, task #7 bundling
|
||||
int active;
|
||||
};
|
||||
static BTProjectile gProjectiles[64];
|
||||
|
||||
extern void BTPushBeam(float,float,float, float,float,float, unsigned, float, float);
|
||||
|
||||
//###########################################################################
|
||||
// PER-ROUND DETONATION (the binary's Missile::MoveAndCollide @004bef78: every
|
||||
// round spawns ITS OWN ExplosionModelFile at its impact point, resource
|
||||
// missile+0x33c -- a volley RIPPLES fireballs across its arrival frames, the
|
||||
// demo look). Resolved from the firing weapon's roster entry (weapon+0x3E4);
|
||||
// runs on BOTH nodes (the replicant visual salvo passes its launcher's index
|
||||
// too). The messmgr's one bundled explosion remains (direct-fire semantics +
|
||||
// the cross-pod damage stream); among a rippled volley it is invisible.
|
||||
//###########################################################################
|
||||
static void
|
||||
BTSpawnRoundDetonation(Entity *shooter, int weapon_subsys, const Point3D &at)
|
||||
{
|
||||
extern int BTIsRegisteredMech(Entity *e);
|
||||
if (shooter == 0 || weapon_subsys < 0 || !BTIsRegisteredMech(shooter))
|
||||
return;
|
||||
Mech *sm = (Mech *)shooter;
|
||||
if (weapon_subsys >= sm->GetSubsystemCount())
|
||||
return;
|
||||
Subsystem *w = sm->GetSubsystem(weapon_subsys);
|
||||
if (w == 0 || !w->IsDerivedFrom(*MechWeapon::GetClassDerivations()))
|
||||
return;
|
||||
ResourceDescription::ResourceID res = ((MechWeapon *)w)->GetExplosionResourceID();
|
||||
if (res <= 0)
|
||||
return;
|
||||
Origin o;
|
||||
o.linearPosition = at;
|
||||
o.angularPosition = EulerAngles(Radian(0.0f), Radian(0.0f), Radian(0.0f));
|
||||
Explosion::MakeMessage m(
|
||||
Explosion::MakeMessageID, sizeof(Explosion::MakeMessage),
|
||||
(Entity::ClassID)RegisteredClass::ExplosionClassID, EntityID::Null,
|
||||
res, Explosion::DefaultFlags, o,
|
||||
sm->GetEntityID(), sm->GetEntityID());
|
||||
Explosion *e = Explosion::Make(&m);
|
||||
if (e)
|
||||
Register_Object(e);
|
||||
}
|
||||
|
||||
// Called from ProjectileWeapon / MissileLauncher::FireWeapon (via the extern below)
|
||||
// with the live muzzle, the SHOOTER mech (to resolve the real launch port), the owner's
|
||||
// locked target entity + point, the launch speed (|muzzleVelocity|), and per-shot damage.
|
||||
void
|
||||
BTPushProjectile(const Point3D &muzzle, void *shooter, void *target, const Point3D &targetPos,
|
||||
Scalar speed, Scalar damage, const Vector3D *launch_velocity, int guided)
|
||||
Scalar speed, Scalar damage, const Vector3D *launch_velocity, int guided,
|
||||
int weapon_subsys)
|
||||
{
|
||||
// MUZZLE (muzzle wave, 2026-07-12): the passed muzzle is now the weapon's
|
||||
// AUTHENTIC mount segment (GetMuzzlePoint reads the real segmentIndex --
|
||||
@@ -880,6 +923,34 @@ void
|
||||
? (tpos.y - ((Mech *)target)->localOrigin.linearPosition.y) : 0.0f;
|
||||
p.damage = damage;
|
||||
p.guided = guided; // autocannon shells fly straight (no seeker in the binary's plain Projectile)
|
||||
p.shooter = (Entity *)shooter;
|
||||
p.weaponSubsys = weapon_subsys;
|
||||
|
||||
// RACK-TUBE SPREAD [T3, physically grounded]: the binary's Missile
|
||||
// entities each launch from their own rack tube (per-tube authored
|
||||
// MuzzlePosition); our single muzzle superimposed a whole salvo onto
|
||||
// ONE trajectory -- 12 rounds read as one round, and their 12
|
||||
// detonations stacked into one frame. Approximate the tube offsets
|
||||
// with a small deterministic per-slot cone (+-~2.5 deg) + lateral
|
||||
// muzzle offset; the seeker re-converges them onto the target so the
|
||||
// volley arrives as the demo's RIPPLE of impacts.
|
||||
// GUIDED ROUNDS ONLY: a ballistic shell has no seeker to re-converge
|
||||
// it -- the slot-0 pattern deflected every AFC100 shell a fixed
|
||||
// 3.4deg left / 2.5deg down (the phantom "4th gold beam" burying its
|
||||
// tracer beside the target, user-reported). The binary's plain
|
||||
// Projectile flies straight at the pick.
|
||||
if (guided)
|
||||
{
|
||||
float ja = ((i % 5) - 2) * 0.030f; // yaw +-0.06 rad
|
||||
float jb = (((i / 5) % 5) - 2) * 0.022f; // pitch +-0.044 rad
|
||||
float vx = p.vel.x, vy = p.vel.y, vz = p.vel.z;
|
||||
float ca = cosf(ja), sa = sinf(ja);
|
||||
p.vel.x = vx * ca - vz * sa;
|
||||
p.vel.z = vx * sa + vz * ca;
|
||||
p.vel.y = vy + jb * p.speed;
|
||||
p.pos.x += ((i % 3) - 1) * 0.6f;
|
||||
p.pos.z += (((i / 3) % 3) - 1) * 0.6f;
|
||||
}
|
||||
p.active = 1;
|
||||
if (getenv("BT_PROJ_LOG"))
|
||||
DEBUG_STREAM << "[projectile] vel=(" << p.vel.x << "," << p.vel.y
|
||||
@@ -888,7 +959,7 @@ void
|
||||
? " authored=(" : " none=(")
|
||||
<< (launch_velocity ? launch_velocity->x : 0.0f) << ","
|
||||
<< (launch_velocity ? launch_velocity->y : 0.0f) << ","
|
||||
<< (launch_velocity ? launch_velocity->z : 0.0f) << ")\n" << std::flush;
|
||||
<< (launch_velocity ? launch_velocity->z : 0.0f) << ")" << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -973,13 +1044,19 @@ static void
|
||||
if (BTGroundRayHit(prev.x, prev.y, prev.z, rd.x, rd.y, rd.z,
|
||||
step + 1.0f, &hx, &hy, &hz))
|
||||
{
|
||||
// burst on the rock: a tight puff cluster at the hit, no damage
|
||||
// burst on the rock: the round's own DETONATION (the binary
|
||||
// missile detonates on ANY geometry, @004bef78) + a tight
|
||||
// puff cluster, no damage
|
||||
{
|
||||
Point3D hp; hp.x = hx; hp.y = hy; hp.z = hz;
|
||||
BTSpawnRoundDetonation(p.shooter, p.weaponSubsys, hp);
|
||||
}
|
||||
extern void BTPfxTrailPuff(int, float, float, float, float, float, float, int);
|
||||
for (int pf = 0; pf < 4; ++pf)
|
||||
BTPfxTrailPuff(0, hx, hy, hz, -rd.x, -rd.y, -rd.z, 2);
|
||||
if (getenv("BT_PROJ_LOG"))
|
||||
DEBUG_STREAM << "[projectile] WORLD burst at(" << hx << ","
|
||||
<< hy << "," << hz << ")\n" << std::flush;
|
||||
<< hy << "," << hz << ")" << std::endl;
|
||||
p.active = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -1014,6 +1091,7 @@ static void
|
||||
}
|
||||
if (contact)
|
||||
{
|
||||
BTSpawnRoundDetonation(p.shooter, p.weaponSubsys, p.pos);
|
||||
Entity *tgt = p.target;
|
||||
// Deliver to the projectile's target mech -- the launcher set p.target
|
||||
// from the shooter's 0x388 slot (the picked victim; any peer mech in
|
||||
@@ -1033,15 +1111,38 @@ static void
|
||||
dmg.damageAmount = p.damage;
|
||||
dmg.burstCount = 1;
|
||||
dmg.impactPoint = p.pos;
|
||||
// Route through the SHOOTER's SubsystemMessageManager with the
|
||||
// firing launcher's roster index (task #7 bundling): the
|
||||
// consolidation resolves roster[id]+0x3E4 = the weapon's
|
||||
// ExplosionModelFile (mslhit/acanhit) and fires it AT the
|
||||
// impact point -- the missing missile-hit explosion (the
|
||||
// laser path always did this via SendDamageMessage; the
|
||||
// projectile path bypassed the manager entirely).
|
||||
SubsystemMessageManager *mgr = 0;
|
||||
if (p.shooter != 0 && p.weaponSubsys >= 0 && BTIsRegisteredMech(p.shooter))
|
||||
mgr = (SubsystemMessageManager *)((Mech *)p.shooter)->GetMessageManager();
|
||||
if (mgr != 0)
|
||||
{
|
||||
Entity::TakeDamageMessage take_damage(
|
||||
Entity::TakeDamageMessageID, sizeof(Entity::TakeDamageMessage),
|
||||
p.shooter->GetEntityID(), -1 /*unaimed -> cylinder resolves*/,
|
||||
dmg, p.weaponSubsys);
|
||||
mgr->AddDamageMessage(tgt, &take_damage);
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity::TakeDamageMessage take_damage(
|
||||
Entity::TakeDamageMessageID, sizeof(Entity::TakeDamageMessage),
|
||||
0 /*inflictor id: bring-up*/, -1 /*unaimed -> cylinder resolves*/, dmg);
|
||||
tgt->Dispatch(&take_damage);
|
||||
}
|
||||
// gauge scoring wave (Step 6): a projectile hit credits SCORE too
|
||||
// (tgt == gEnemyMech here; local player is the viewpoint shooter).
|
||||
BTPostDamageScore((Entity *)tgt, p.damage);
|
||||
DEBUG_STREAM << "[projectile] IMPACT damage=" << p.damage
|
||||
<< " zone=" << take_damage.damageZone << " (cyl-resolved)\n" << std::flush;
|
||||
<< " subsys=" << p.weaponSubsys
|
||||
<< (mgr ? " (msgmgr bundled)" : " (direct)")
|
||||
<< " (zone cyl-resolved)\n" << std::flush;
|
||||
}
|
||||
}
|
||||
p.active = 0;
|
||||
@@ -1196,6 +1297,8 @@ void
|
||||
worldLinearVelocity = Vector3D(0.0f, 0.0f, 0.0f);
|
||||
localVelocity = Motion::Identity;
|
||||
frameEntryWorldVelocity = Vector3D(0.0f, 0.0f, 0.0f);
|
||||
ramLastVictim = 0;
|
||||
ramContactLinger = 0.0f;
|
||||
|
||||
// --- our RELOCATED gait/motion accumulators -> identity (the 1995 offsets
|
||||
// the decomp zeroes map to these named members in our layout) ---
|
||||
@@ -1363,7 +1466,7 @@ void
|
||||
if (getenv("BT_DEATH_LOG"))
|
||||
DEBUG_STREAM << "[death] mech " << GetEntityID()
|
||||
<< " destroyed -> subsystem shutdown + frozen wreck (IsDisabled="
|
||||
<< (int)IsDisabled() << ")\n" << std::flush;
|
||||
<< (int)IsDisabled() << ")" << std::endl;
|
||||
|
||||
// --- DEATH EFFECTS (task #42): dispatched HERE, at the VICTIM's own
|
||||
// once-per-death transition, so they fire regardless of WHAT killed it
|
||||
@@ -1516,7 +1619,7 @@ void
|
||||
DEBUG_STREAM << "[mech-exec] entity " << GetEntityID()
|
||||
<< " instance=" << (int)GetInstance()
|
||||
<< " pos=(" << localOrigin.linearPosition.x << ", "
|
||||
<< localOrigin.linearPosition.z << ")\n" << std::flush;
|
||||
<< localOrigin.linearPosition.z << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2023,7 +2126,7 @@ void
|
||||
<< " err=" << err << " turn=" << gBTGotoTurn
|
||||
<< " thr=" << gBTGotoThrottle << " arr=" << arrived
|
||||
<< " fwd=(" << fwdX << "," << fwdZ << ")"
|
||||
<< " tgt=(" << s_gx << "," << s_gz << ")\n" << std::flush; }
|
||||
<< " tgt=(" << s_gx << "," << s_gz << ")" << std::endl; }
|
||||
}
|
||||
// SELF-DRIVE (task #48): a "beeline" harness must supply its OWN
|
||||
// forward throttle, not only steering -- otherwise it just turns
|
||||
@@ -2357,7 +2460,7 @@ void
|
||||
gCurrentGait = wantGait;
|
||||
DEBUG_STREAM << "[gait] SequenceController -> blh" << suffix
|
||||
<< " id=" << (long)*clip << " (" << (wantGait ? "run" : "walk")
|
||||
<< ")\n" << std::flush;
|
||||
<< ")" << std::endl;
|
||||
}
|
||||
}
|
||||
adv = bodyAnimation.Advance(dt, 1); // inline clip-select path
|
||||
@@ -2427,7 +2530,7 @@ void
|
||||
DEBUG_STREAM << "[gait] adv=" << travelAdv << " (proj=" << adv
|
||||
<< ") pos=("
|
||||
<< localOrigin.linearPosition.x << ", "
|
||||
<< localOrigin.linearPosition.z << ")\n" << std::flush;
|
||||
<< localOrigin.linearPosition.z << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2446,7 +2549,7 @@ void
|
||||
gCurrentGait = wantGait;
|
||||
gBodyAnimReady = 1;
|
||||
DEBUG_STREAM << "[anim] gait -> blh" << suffix << " id=" << (long)*clip
|
||||
<< " (" << (wantGait ? "run" : "walk") << ")\n" << std::flush;
|
||||
<< " (" << (wantGait ? "run" : "walk") << ")" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2481,7 +2584,7 @@ void
|
||||
<< (dir < 0 ? " (reverse)" : "") << " adv=" << adv
|
||||
<< " loops=" << gBodyAnimLoops
|
||||
<< " pos=(" << localOrigin.linearPosition.x << ", "
|
||||
<< localOrigin.linearPosition.z << ")\n" << std::flush;
|
||||
<< localOrigin.linearPosition.z << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
} // close the BT_GAIT_CUTOVER else (STEP 1-2 path)
|
||||
@@ -2591,7 +2694,7 @@ void
|
||||
DEBUG_STREAM << "[collide] pushed out by (" << dx << ", "
|
||||
<< (localOrigin.linearPosition.y - before.y) << ", " << dz
|
||||
<< ") pos=(" << localOrigin.linearPosition.x << ", "
|
||||
<< localOrigin.linearPosition.z << ")\n" << std::flush;
|
||||
<< localOrigin.linearPosition.z << ")" << std::endl;
|
||||
}
|
||||
// Floor clamp: never sink below the base ground (safety on solid-less maps).
|
||||
if (localOrigin.linearPosition.y < s_groundY)
|
||||
@@ -2716,7 +2819,7 @@ void
|
||||
DEBUG_STREAM << "[visgnd] lift=" << vLift
|
||||
<< " at (" << localOrigin.linearPosition.x << ", "
|
||||
<< localOrigin.linearPosition.y << ", "
|
||||
<< localOrigin.linearPosition.z << ")\n" << std::flush;
|
||||
<< localOrigin.linearPosition.z << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3804,6 +3907,14 @@ void
|
||||
// and a multi-solid frame compounds those reflections into a garbage
|
||||
// velocity that priced mech-vs-mech damage 4x-40x too high (mp_a.log:32651).
|
||||
frameEntryWorldVelocity = savedWorldVel;
|
||||
// ram contact-edge decay: once contact with the last victim has been
|
||||
// broken for the linger window, re-arm the one-bump-one-hit gate
|
||||
if (ramContactLinger > 0.0f)
|
||||
{
|
||||
ramContactLinger -= dt;
|
||||
if (ramContactLinger <= 0.0f)
|
||||
ramLastVictim = 0;
|
||||
}
|
||||
Damage dmg; // ctor zeroes damageAmount
|
||||
if (cols != 0)
|
||||
ProcessCollisionList(cols, dt, old_position, &dmg);
|
||||
@@ -3831,7 +3942,7 @@ void
|
||||
}
|
||||
if (GroundLog())
|
||||
DEBUG_STREAM << "[ground] CRUNCH (crushable icon) at ("
|
||||
<< savedPos.x << ", " << savedPos.z << ")\n" << std::flush;
|
||||
<< savedPos.x << ", " << savedPos.z << ")" << std::endl;
|
||||
}
|
||||
else if (dmg.damageAmount > 0.0f) // blocking hit: FULL FRAME REJECTION
|
||||
{
|
||||
@@ -3893,12 +4004,12 @@ void
|
||||
}
|
||||
if (GroundLog())
|
||||
DEBUG_STREAM << "[ground] BLOCK + CRASH (iv2=" << iv2
|
||||
<< " clip=" << animationClips[0x20] << ")\n" << std::flush;
|
||||
<< " clip=" << animationClips[0x20] << ")" << std::endl;
|
||||
}
|
||||
else if (GroundLog())
|
||||
DEBUG_STREAM << "[ground] BLOCK dmg=" << dmg.damageAmount
|
||||
<< " at (" << old_position.x << ", " << old_position.z
|
||||
<< ")\n" << std::flush;
|
||||
<< ")" << std::endl;
|
||||
}
|
||||
|
||||
// 1-Hz telemetry (verification: h / y / hit-miss); INSIDE-SOLID detector:
|
||||
@@ -3915,7 +4026,7 @@ void
|
||||
DEBUG_STREAM << "[ground] INSIDE-SOLID streak at ("
|
||||
<< localOrigin.linearPosition.x << ", "
|
||||
<< localOrigin.linearPosition.y << ", "
|
||||
<< localOrigin.linearPosition.z << ")\n" << std::flush;
|
||||
<< localOrigin.linearPosition.z << ")" << std::endl;
|
||||
}
|
||||
else
|
||||
s_zeroStreak = 0;
|
||||
@@ -3928,7 +4039,7 @@ void
|
||||
<< " lift=" << collisionTemplate->minY
|
||||
<< " pos=(" << localOrigin.linearPosition.x << ", "
|
||||
<< localOrigin.linearPosition.y << ", "
|
||||
<< localOrigin.linearPosition.z << ")\n" << std::flush;
|
||||
<< localOrigin.linearPosition.z << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3989,7 +4100,18 @@ static void
|
||||
{
|
||||
Damage dmg;
|
||||
dmg.damageType = Damage::CollisionDamageType;
|
||||
dmg.damageAmount = resolved->damageAmount;
|
||||
// RAM ECONOMY NORMALIZATION [T3, evidence-forced 2026-07-12]: StaticBounce
|
||||
// prices the kinetic loss with the AUTHORED moverMass (~1.3e6 units) --
|
||||
// ~59,000 "points" for a 10 m/s ram, against zones whose armor absorbs
|
||||
// 1 point per point (scale = 1/armorPoints, armor 50-140: the [zone-armor]
|
||||
// dump). The BINARY dispatches this raw number [T1 @part_012:15324] --
|
||||
// but on the pod network it only ever hit the local REPLICANT of the
|
||||
// victim, where it evaporated (the master never heard it; MECH.CPP:986
|
||||
// merely warns). Our MP forwards replicant damage (task #47, required
|
||||
// for weapons), which surfaced the dormant 59K as a one-shot ram-kill.
|
||||
// Normalize by 1e-3 (the mass-unit/armor-point scale): a walking bump =
|
||||
// a few points, a full charge = tens -- the armor-table economy.
|
||||
dmg.damageAmount = resolved->damageAmount * 0.001f;
|
||||
dmg.surfaceNormal = resolved->surfaceNormal;
|
||||
dmg.impactPoint = Point3D(
|
||||
(collision.collisionSlice.minX + collision.collisionSlice.maxX) * 0.5f,
|
||||
@@ -4016,9 +4138,11 @@ static void
|
||||
DEBUG_STREAM << "[collide-tx] " << inflictor->GetEntityID()
|
||||
<< " rams " << victim->GetEntityID()
|
||||
<< " dmg=" << resolved->damageAmount
|
||||
<< " mass=" << inflictor->moverMass
|
||||
<< " e=" << inflictor->elasticityCoefficient
|
||||
<< " |v|=" << (Scalar)sqrtf(v.x*v.x + v.y*v.y + v.z*v.z)
|
||||
<< " at(" << dmg.impactPoint.x << "," << dmg.impactPoint.y
|
||||
<< "," << dmg.impactPoint.z << ")\n" << std::flush;
|
||||
<< "," << dmg.impactPoint.z << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4100,9 +4224,23 @@ void
|
||||
// is UNBLOCKED (was deferred: zone==-1 used to be dropped).
|
||||
if (owner->IsDerivedFrom(*Mech::GetClassDerivations()))
|
||||
{
|
||||
// RAM CONTACT EDGE (see mech.hpp): dispatch damage only on a FRESH
|
||||
// contact with this victim; while pressed, refresh the linger so
|
||||
// sustained contact (held stick / respawn overlap) stays a BLOCK,
|
||||
// not a 60Hz damage grinder -- the binary's bounce-separation made
|
||||
// this edge implicit; our gait-derived velocity needs it explicit.
|
||||
if (owner == ramLastVictim && ramContactLinger > 0.0f)
|
||||
{
|
||||
ramContactLinger = 0.35f; // still pressed: refresh, no damage
|
||||
}
|
||||
else
|
||||
{
|
||||
ramLastVictim = owner;
|
||||
ramContactLinger = 0.35f;
|
||||
BTDispatchCollisionDamage(this, owner, damage, collision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- CulturalIcon owner (buildings/trees/props) (:15361-15404) ----------
|
||||
if (owner != 0 && owner->IsDerivedFrom(*CulturalIcon::GetClassDerivations()))
|
||||
@@ -4234,6 +4372,11 @@ void
|
||||
continue;
|
||||
++energyOrdinal;
|
||||
Emitter *em = (Emitter *)ws;
|
||||
// replicant beams: age the replicated discharge locally (a lost
|
||||
// beam-END record otherwise pins the beam on forever -- see
|
||||
// Emitter::ReplicantServiceBeam)
|
||||
if (GetInstance() == Entity::ReplicantInstance)
|
||||
em->ReplicantServiceBeam(ttl1);
|
||||
if (!em->BeamOn())
|
||||
continue;
|
||||
Point3D mz;
|
||||
@@ -4293,6 +4436,21 @@ void
|
||||
(((unsigned)(40.0f + r * 215.0f) & 0xFF) << 16) |
|
||||
(((unsigned)(40.0f + g * 215.0f) & 0xFF) << 8) |
|
||||
((unsigned)(40.0f + b * 215.0f) & 0xFF);
|
||||
// PHANTOM-BEAM FORENSICS (BT_FIRE_LOG): identify every drawn beam --
|
||||
// owner/instance, roster slot, class, discharge state, colour, ends.
|
||||
if (getenv("BT_FIRE_LOG"))
|
||||
{
|
||||
static int s_bd = 0;
|
||||
if ((++s_bd % 30) == 1) // ~2Hz per sustained beam
|
||||
DEBUG_STREAM << "[beam-draw] mech=" << GetEntityID()
|
||||
<< " inst=" << (int)GetInstance()
|
||||
<< " slot=" << wi << " cid=" << wcid
|
||||
<< " timer=" << em->DischargeTimer()
|
||||
<< " pip=(" << r << "," << g << "," << b << ")"
|
||||
<< " mz=(" << mz.x << "," << mz.y << "," << mz.z << ")"
|
||||
<< " end=(" << bend.x << "," << bend.y << "," << bend.z
|
||||
<< ")" << std::endl;
|
||||
}
|
||||
BTPushBeamKind(mz.x, mz.y, mz.z, bend.x, bend.y, bend.z,
|
||||
tint, ttl1, 1.0f /* natural model width */,
|
||||
isPPC ? 1 : 0 /* ppc.bgf : ermlaser.bgf */);
|
||||
@@ -4300,6 +4458,6 @@ void
|
||||
DEBUG_STREAM << "[beam] " << (isPPC ? "PPC" : "laser") << " #" << wi
|
||||
<< " mz=(" << mz.x << "," << mz.y << "," << mz.z
|
||||
<< ") end=(" << bend.x << "," << bend.y << "," << bend.z
|
||||
<< ") rgb=(" << r << "," << g << "," << b << ")\n" << std::flush;
|
||||
<< ") rgb=(" << r << "," << g << "," << b << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +240,9 @@ Mech__DamageZone::Mech__DamageZone(
|
||||
// members (read raw from the stream by the base ctor, DAMAGE.cpp:301-307); we
|
||||
// normalise them in place here -- not a raw 0x144/0x140 offset (which would be the
|
||||
// binary layout, wrong when compiled against the engine base).
|
||||
Scalar rawScale[Damage::DamageTypeCount];
|
||||
for (int type = 0; type < Damage::DamageTypeCount; ++type)
|
||||
rawScale[type] = damageScale[type];
|
||||
for (int type = 0; type < Damage::DamageTypeCount; ++type) // 5 damage types
|
||||
{
|
||||
Scalar even = 1.0f / defaultArmorPoints; // _DAT_0049d1c4 / count
|
||||
@@ -248,6 +251,19 @@ Mech__DamageZone::Mech__DamageZone(
|
||||
if (rightLeg || leftLeg)
|
||||
damageScale[type] *= 0.5f; // _DAT_0049d1cc
|
||||
}
|
||||
// ARMOR ECONOMY DUMP (the ram-kill forensics): the authored per-type
|
||||
// absorption -- damageLevel += amount * scale[type]; level 1.0 = zone
|
||||
// destroyed. scale[0] (Collision) x the StaticBounce kinetic number is
|
||||
// the authored ram economy.
|
||||
if (getenv("BT_DMG_LOG"))
|
||||
DEBUG_STREAM << "[zone-armor] zone=" << damageZoneIndex
|
||||
<< " armorPts=" << defaultArmorPoints
|
||||
<< " raw={" << rawScale[0] << "," << rawScale[1] << "," << rawScale[2]
|
||||
<< "," << rawScale[3] << "," << rawScale[4] << "}"
|
||||
<< " scale={" << damageScale[0] << "," << damageScale[1] << ","
|
||||
<< damageScale[2] << "," << damageScale[3] << "," << damageScale[4] << "}"
|
||||
<< " leg=" << (int)(rightLeg || leftLeg)
|
||||
<< " vital=" << (int)vitalDamageZone << std::endl;
|
||||
|
||||
//
|
||||
// Critical-subsystem table.
|
||||
@@ -1055,6 +1071,16 @@ void
|
||||
continue;
|
||||
lastLevel[i] = level;
|
||||
|
||||
// AUTHENTIC GATE (decomp re-verified 2026-07-12, workflow): the binary
|
||||
// fires the CURRENT band descriptor's effect whenever the zone's
|
||||
// damage-CHANGED flag is set (+0xb8 & 4, set by DamageZone::TakeDamage,
|
||||
// coalesced per tick) -- i.e. EVERY hit on a damaged mech re-puffs its
|
||||
// band effect. Bands 3/4 are authored ember/bright-FIRE plumes, so a
|
||||
// nearly-destroyed mech under fire burns continuously (the demo look).
|
||||
// A level RISE this tick IS the flag analog -- no crossing test. (The
|
||||
// earlier metronome was SHKWAVE mispacing, not this path; an observer
|
||||
// node sees a few replication-step puffs per volley -- smoke, fine.)
|
||||
|
||||
const Mech__DamageZone::DamageDescriptor *d = zone->DescriptorForLevel(level);
|
||||
if (level >= 1.0f && prev < 1.0f) // just destroyed -> the Destroyed descriptor
|
||||
{
|
||||
|
||||
@@ -72,7 +72,8 @@
|
||||
// shooter's frame (up-tilted rack launch); damage <= 0 = a VISUAL-ONLY round.
|
||||
extern void BTPushProjectile(const Point3D &muzzle, void *shooter, void *target,
|
||||
const Point3D &targetPos, Scalar speed, Scalar damage,
|
||||
const Vector3D *launch_velocity = 0, int guided = 1);
|
||||
const Vector3D *launch_velocity = 0, int guided = 1,
|
||||
int weapon_subsys = -1);
|
||||
|
||||
//###########################################################################
|
||||
// Port-side salvo replication state (missile-visibility wave).
|
||||
@@ -305,7 +306,8 @@ void MissileLauncher::FireWeapon()
|
||||
int nmiss = (missileCount > 0 && missileCount < 40) ? missileCount : 1;
|
||||
for (int i = 0; i < nmiss; ++i)
|
||||
BTPushProjectile(muzzle, o, target, targetPos, speed, damageData.damageAmount,
|
||||
&launchVelocity);
|
||||
&launchVelocity, 1,
|
||||
subsystemID /*messmgr explosion bundling at impact (task #7)*/);
|
||||
|
||||
// Salvo replication (missile-visibility wave): bump the fire counter + stamp
|
||||
// the aim point; the extended update record carries both to peer nodes.
|
||||
@@ -376,7 +378,8 @@ void
|
||||
n = (missileCount > 0 && missileCount < 40) ? missileCount : 1;
|
||||
for (int i = 0; i < n; ++i)
|
||||
BTPushProjectile(mz, owner, 0 /*no entity: aim point only*/,
|
||||
rec->salvoTarget, spd, 0.0f /*VISUAL*/, &launchVelocity);
|
||||
rec->salvoTarget, spd, 0.0f /*VISUAL*/, &launchVelocity, 1,
|
||||
subsystemID /*per-round detonation resolve on this node*/);
|
||||
if (getenv("BT_PROJ_LOG"))
|
||||
DEBUG_STREAM << "[projectile] REPLICANT salvo x" << n
|
||||
<< " at(" << rec->salvoTarget.x << "," << rec->salvoTarget.y
|
||||
|
||||
@@ -94,7 +94,8 @@
|
||||
// crash, 2026-07-12 -- gotcha #3).
|
||||
extern void BTPushProjectile(const Point3D &muzzle, void *shooter, void *target,
|
||||
const Point3D &targetPos, Scalar speed, Scalar damage,
|
||||
const Vector3D *launch_velocity = 0, int guided = 1);
|
||||
const Vector3D *launch_velocity = 0, int guided = 1,
|
||||
int weapon_subsys = -1);
|
||||
|
||||
|
||||
//#############################################################################
|
||||
@@ -697,7 +698,8 @@ void
|
||||
+ launchVelocity.y*launchVelocity.y
|
||||
+ launchVelocity.z*launchVelocity.z);
|
||||
BTPushProjectile(muzzle, o, target, targetPos, speed, damageData.damageAmount,
|
||||
0 /*aim straight at the pick*/, 0 /*BALLISTIC: no seeker on a shell*/);
|
||||
0 /*aim straight at the pick*/, 0 /*BALLISTIC: no seeker on a shell*/,
|
||||
subsystemID /*messmgr explosion bundling at impact (task #7)*/);
|
||||
|
||||
simulationFlags |= 0x1; // replication-dirty
|
||||
Check_Fpu();
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Capture timed screenshots of a btl4 window via PrintWindow (works occluded).
|
||||
|
||||
Usage: python fxshot.py [count] [interval_s] [outprefix] [nodesubstr]
|
||||
"""
|
||||
import ctypes, ctypes.wintypes as wt, sys, time
|
||||
|
||||
user32 = ctypes.windll.user32
|
||||
gdi32 = ctypes.windll.gdi32
|
||||
|
||||
count = int(sys.argv[1]) if len(sys.argv) > 1 else 8
|
||||
interval = float(sys.argv[2]) if len(sys.argv) > 2 else 0.4
|
||||
prefix = sys.argv[3] if len(sys.argv) > 3 else 'fx'
|
||||
want = sys.argv[4] if len(sys.argv) > 4 else '1501'
|
||||
|
||||
titles = []
|
||||
def enum_cb(hwnd, lparam):
|
||||
n = user32.GetWindowTextLengthW(hwnd)
|
||||
if n > 0:
|
||||
buf = ctypes.create_unicode_buffer(n + 1)
|
||||
user32.GetWindowTextW(hwnd, buf, n + 1)
|
||||
if buf.value.startswith('BattleTech') and want in buf.value:
|
||||
titles.append((hwnd, buf.value))
|
||||
return True
|
||||
WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, wt.HWND, wt.LPARAM)
|
||||
user32.EnumWindows(WNDENUMPROC(enum_cb), 0)
|
||||
if not titles:
|
||||
print('no matching BattleTech window'); sys.exit(1)
|
||||
hwnd, title = titles[0]
|
||||
print('capturing:', title)
|
||||
|
||||
rect = wt.RECT()
|
||||
user32.GetClientRect(hwnd, ctypes.byref(rect))
|
||||
w, h = rect.right, rect.bottom
|
||||
|
||||
hdc = user32.GetDC(0)
|
||||
mem = gdi32.CreateCompatibleDC(hdc)
|
||||
bmp = gdi32.CreateCompatibleBitmap(hdc, w, h)
|
||||
gdi32.SelectObject(mem, bmp)
|
||||
|
||||
class BMIH(ctypes.Structure):
|
||||
_fields_ = [('biSize', wt.DWORD), ('biWidth', wt.LONG), ('biHeight', wt.LONG),
|
||||
('biPlanes', wt.WORD), ('biBitCount', wt.WORD), ('biCompression', wt.DWORD),
|
||||
('biSizeImage', wt.DWORD), ('biXPelsPerMeter', wt.LONG), ('biYPelsPerMeter', wt.LONG),
|
||||
('biClrUsed', wt.DWORD), ('biClrImportant', wt.DWORD)]
|
||||
|
||||
from PIL import Image
|
||||
for i in range(count):
|
||||
# PW_RENDERFULLCONTENT (2) captures D3D content
|
||||
user32.PrintWindow(hwnd, mem, 2 | 1) # +PW_CLIENTONLY
|
||||
bmi = BMIH(ctypes.sizeof(BMIH), w, -h, 1, 32, 0, 0, 0, 0, 0, 0)
|
||||
buf = ctypes.create_string_buffer(w * h * 4)
|
||||
gdi32.GetDIBits(mem, bmp, 0, h, buf, ctypes.byref(bmi), 0)
|
||||
img = Image.frombuffer('RGB', (w, h), buf.raw, 'raw', 'BGRX', 0, 1)
|
||||
img.save(r'C:\git\bt411\scratchpad\%s_%d.png' % (prefix, i))
|
||||
time.sleep(interval)
|
||||
print('saved %d shots %dx%d' % (count, w, h))
|
||||
@@ -0,0 +1,507 @@
|
||||
/* Table of opcodes for the i860.
|
||||
Copyright 1989, 1991, 2000, 2002, 2003 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GAS, the GNU Assembler, and GDB, the GNU disassembler.
|
||||
|
||||
GAS/GDB is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 1, or (at your option)
|
||||
any later version.
|
||||
|
||||
GAS/GDB is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GAS or GDB; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 51 Franklin Street - Fifth Floor,
|
||||
Boston, MA 02110-1301, USA. */
|
||||
|
||||
|
||||
/* Structure of an opcode table entry. */
|
||||
struct i860_opcode
|
||||
{
|
||||
/* The opcode name. */
|
||||
const char *name;
|
||||
|
||||
/* Bits that must be set. */
|
||||
unsigned long match;
|
||||
|
||||
/* Bits that must not be set. */
|
||||
unsigned long lose;
|
||||
|
||||
const char *args;
|
||||
|
||||
/* Nonzero if this is a possible expand-instruction. */
|
||||
char expand;
|
||||
};
|
||||
|
||||
|
||||
enum expand_type
|
||||
{
|
||||
E_MOV = 1, E_ADDR, E_U32, E_AND, E_S32, E_DELAY, XP_ONLY
|
||||
};
|
||||
|
||||
|
||||
/* All i860 opcodes are 32 bits, except for the pseudo-instructions
|
||||
and the operations utilizing a 32-bit address expression, an
|
||||
unsigned 32-bit constant, or a signed 32-bit constant.
|
||||
These opcodes are expanded into a two-instruction sequence for
|
||||
any situation where the immediate operand does not fit in 32 bits.
|
||||
In the case of the add and subtract operations the expansion is
|
||||
to a three-instruction sequence (ex: orh, or, adds). In cases
|
||||
where the address is to be relocated, the instruction is
|
||||
expanded to handle the worse case, this could be optimized at
|
||||
the final link if the actual address were known.
|
||||
|
||||
The pseudoinstructions are: mov, fmov, pmov, nop, and fnop.
|
||||
These instructions are implemented as a one or two instruction
|
||||
sequence of other operations.
|
||||
|
||||
The match component is a mask saying which bits must match a
|
||||
particular opcode in order for an instruction to be an instance
|
||||
of that opcode.
|
||||
|
||||
The args component is a string containing one character
|
||||
for each operand of the instruction.
|
||||
|
||||
Kinds of operands:
|
||||
# Number used by optimizer. It is ignored.
|
||||
1 src1 integer register.
|
||||
2 src2 integer register.
|
||||
d dest register.
|
||||
c ctrlreg control register.
|
||||
i 16 bit immediate.
|
||||
I 16 bit immediate, aligned 2^0. (ld.b)
|
||||
J 16 bit immediate, aligned 2^1. (ld.s)
|
||||
K 16 bit immediate, aligned 2^2. (ld.l, {p}fld.l, fst.l)
|
||||
L 16 bit immediate, aligned 2^3. ({p}fld.d, fst.d)
|
||||
M 16 bit immediate, aligned 2^4. ({p}fld.q, fst.q)
|
||||
5 5 bit immediate.
|
||||
l lbroff 26 bit PC relative immediate.
|
||||
r sbroff 16 bit PC relative immediate.
|
||||
s split 16 bit immediate.
|
||||
S split 16 bit immediate, aligned 2^0. (st.b)
|
||||
T split 16 bit immediate, aligned 2^1. (st.s)
|
||||
U split 16 bit immediate, aligned 2^2. (st.l)
|
||||
e src1 floating point register.
|
||||
f src2 floating point register.
|
||||
g dest floating point register. */
|
||||
|
||||
|
||||
/* The order of the opcodes in this table is significant. The assembler
|
||||
requires that all instances of the same mnemonic must be consecutive.
|
||||
If they aren't, the assembler will not function properly.
|
||||
|
||||
The order of opcodes does not affect the disassembler. */
|
||||
|
||||
static const struct i860_opcode i860_opcodes[] =
|
||||
{
|
||||
/* REG-Format Instructions. */
|
||||
{ "ld.c", 0x30000000, 0xcc000000, "c,d", 0 }, /* ld.c csrc2,idest */
|
||||
{ "ld.b", 0x00000000, 0xfc000000, "1(2),d", 0 }, /* ld.b isrc1(isrc2),idest */
|
||||
{ "ld.b", 0x04000000, 0xf8000000, "I(2),d", E_ADDR }, /* ld.b #const(isrc2),idest */
|
||||
{ "ld.s", 0x10000000, 0xec000001, "1(2),d", 0 }, /* ld.s isrc1(isrc2),idest */
|
||||
{ "ld.s", 0x14000000, 0xe8000001, "J(2),d", E_ADDR }, /* ld.s #const(isrc2),idest */
|
||||
{ "ld.l", 0x10000001, 0xec000000, "1(2),d", 0 }, /* ld.l isrc1(isrc2),idest */
|
||||
{ "ld.l", 0x14000001, 0xe8000000, "K(2),d", E_ADDR }, /* ld.l #const(isrc2),idest */
|
||||
|
||||
{ "st.c", 0x38000000, 0xc4000000, "1,c", 0 }, /* st.c isrc1ni,csrc2 */
|
||||
{ "st.b", 0x0c000000, 0xf0000000, "1,S(2)", E_ADDR }, /* st.b isrc1ni,#const(isrc2) */
|
||||
{ "st.s", 0x1c000000, 0xe0000001, "1,T(2)", E_ADDR }, /* st.s isrc1ni,#const(isrc2) */
|
||||
{ "st.l", 0x1c000001, 0xe0000000, "1,U(2)", E_ADDR }, /* st.l isrc1ni,#const(isrc2) */
|
||||
|
||||
{ "ixfr", 0x08000000, 0xf4000000, "1,g", 0 }, /* ixfr isrc1ni,fdest */
|
||||
|
||||
{ "fld.l", 0x20000002, 0xdc000001, "1(2),g", 0 }, /* fld.l isrc1(isrc2),fdest */
|
||||
{ "fld.l", 0x24000002, 0xd8000001, "K(2),g", E_ADDR }, /* fld.l #const(isrc2),fdest */
|
||||
{ "fld.l", 0x20000003, 0xdc000000, "1(2)++,g", 0 }, /* fld.l isrc1(isrc2)++,fdest */
|
||||
{ "fld.l", 0x24000003, 0xd8000000, "K(2)++,g", E_ADDR }, /* fld.l #const(isrc2)++,fdest */
|
||||
{ "fld.d", 0x20000000, 0xdc000007, "1(2),g", 0 }, /* fld.d isrc1(isrc2),fdest */
|
||||
{ "fld.d", 0x24000000, 0xd8000007, "L(2),g", E_ADDR }, /* fld.d #const(isrc2),fdest */
|
||||
{ "fld.d", 0x20000001, 0xdc000006, "1(2)++,g", 0 }, /* fld.d isrc1(isrc2)++,fdest */
|
||||
{ "fld.d", 0x24000001, 0xd8000006, "L(2)++,g", E_ADDR }, /* fld.d #const(isrc2)++,fdest */
|
||||
{ "fld.q", 0x20000004, 0xdc000003, "1(2),g", 0 }, /* fld.q isrc1(isrc2),fdest */
|
||||
{ "fld.q", 0x24000004, 0xd8000003, "M(2),g", E_ADDR }, /* fld.q #const(isrc2),fdest */
|
||||
{ "fld.q", 0x20000005, 0xdc000002, "1(2)++,g", 0 }, /* fld.q isrc1(isrc2)++,fdest */
|
||||
{ "fld.q", 0x24000005, 0xd8000002, "M(2)++,g", E_ADDR }, /* fld.q #const(isrc2)++,fdest */
|
||||
|
||||
{ "pfld.l", 0x60000002, 0x9c000001, "1(2),g", 0 }, /* pfld.l isrc1(isrc2),fdest */
|
||||
{ "pfld.l", 0x64000002, 0x98000001, "K(2),g", E_ADDR }, /* pfld.l #const(isrc2),fdest */
|
||||
{ "pfld.l", 0x60000003, 0x9c000000, "1(2)++,g", 0 }, /* pfld.l isrc1(isrc2)++,fdest */
|
||||
{ "pfld.l", 0x64000003, 0x98000000, "K(2)++,g", E_ADDR }, /* pfld.l #const(isrc2)++,fdest */
|
||||
{ "pfld.d", 0x60000000, 0x9c000007, "1(2),g", 0 }, /* pfld.d isrc1(isrc2),fdest */
|
||||
{ "pfld.d", 0x64000000, 0x98000007, "L(2),g", E_ADDR }, /* pfld.d #const(isrc2),fdest */
|
||||
{ "pfld.d", 0x60000001, 0x9c000006, "1(2)++,g", 0 }, /* pfld.d isrc1(isrc2)++,fdest */
|
||||
{ "pfld.d", 0x64000001, 0x98000006, "L(2)++,g", E_ADDR }, /* pfld.d #const(isrc2)++,fdest */
|
||||
{ "pfld.q", 0x60000004, 0x9c000003, "1(2),g", XP_ONLY }, /* pfld.q isrc1(isrc2),fdest */
|
||||
{ "pfld.q", 0x64000004, 0x98000003, "L(2),g", XP_ONLY }, /* pfld.q #const(isrc2),fdest */
|
||||
{ "pfld.q", 0x60000005, 0x9c000002, "1(2)++,g", XP_ONLY }, /* pfld.q isrc1(isrc2)++,fdest */
|
||||
{ "pfld.q", 0x64000005, 0x98000002, "L(2)++,g", XP_ONLY }, /* pfld.q #const(isrc2)++,fdest */
|
||||
|
||||
{ "fst.l", 0x28000002, 0xd4000001, "g,1(2)", 0 }, /* fst.l fdest,isrc1(isrc2) */
|
||||
{ "fst.l", 0x2c000002, 0xd0000001, "g,K(2)", E_ADDR }, /* fst.l fdest,#const(isrc2) */
|
||||
{ "fst.l", 0x28000003, 0xd4000000, "g,1(2)++", 0 }, /* fst.l fdest,isrc1(isrc2)++ */
|
||||
{ "fst.l", 0x2c000003, 0xd0000000, "g,K(2)++", E_ADDR }, /* fst.l fdest,#const(isrc2)++ */
|
||||
{ "fst.d", 0x28000000, 0xd4000007, "g,1(2)", 0 }, /* fst.d fdest,isrc1(isrc2) */
|
||||
{ "fst.d", 0x2c000000, 0xd0000007, "g,L(2)", E_ADDR }, /* fst.d fdest,#const(isrc2) */
|
||||
{ "fst.d", 0x28000001, 0xd4000006, "g,1(2)++", 0 }, /* fst.d fdest,isrc1(isrc2)++ */
|
||||
{ "fst.d", 0x2c000001, 0xd0000006, "g,L(2)++", E_ADDR }, /* fst.d fdest,#const(isrc2)++ */
|
||||
{ "fst.q", 0x28000004, 0xd4000003, "g,1(2)", 0 }, /* fst.d fdest,isrc1(isrc2) */
|
||||
{ "fst.q", 0x2c000004, 0xd0000003, "g,M(2)", E_ADDR }, /* fst.d fdest,#const(isrc2) */
|
||||
{ "fst.q", 0x28000005, 0xd4000002, "g,1(2)++", 0 }, /* fst.d fdest,isrc1(isrc2)++ */
|
||||
{ "fst.q", 0x2c000005, 0xd0000002, "g,M(2)++", E_ADDR }, /* fst.d fdest,#const(isrc2)++ */
|
||||
|
||||
{ "pst.d", 0x3c000000, 0xc0000007, "g,L(2)", E_ADDR }, /* pst.d fdest,#const(isrc2) */
|
||||
{ "pst.d", 0x3c000001, 0xc0000006, "g,L(2)++", E_ADDR }, /* pst.d fdest,#const(isrc2)++ */
|
||||
|
||||
{ "addu", 0x80000000, 0x7c000000, "1,2,d", 0 }, /* addu isrc1,isrc2,idest */
|
||||
{ "addu", 0x84000000, 0x78000000, "i,2,d", E_S32 }, /* addu #const,isrc2,idest */
|
||||
{ "adds", 0x90000000, 0x6c000000, "1,2,d", 0 }, /* adds isrc1,isrc2,idest */
|
||||
{ "adds", 0x94000000, 0x68000000, "i,2,d", E_S32 }, /* adds #const,isrc2,idest */
|
||||
{ "subu", 0x88000000, 0x74000000, "1,2,d", 0 }, /* subu isrc1,isrc2,idest */
|
||||
{ "subu", 0x8c000000, 0x70000000, "i,2,d", E_S32 }, /* subu #const,isrc2,idest */
|
||||
{ "subs", 0x98000000, 0x64000000, "1,2,d", 0 }, /* subs isrc1,isrc2,idest */
|
||||
{ "subs", 0x9c000000, 0x60000000, "i,2,d", E_S32 }, /* subs #const,isrc2,idest */
|
||||
|
||||
{ "shl", 0xa0000000, 0x5c000000, "1,2,d", 0 }, /* shl isrc1,isrc2,idest */
|
||||
{ "shl", 0xa4000000, 0x58000000, "i,2,d", 0 }, /* shl #const,isrc2,idest */
|
||||
{ "shr", 0xa8000000, 0x54000000, "1,2,d", 0 }, /* shr isrc1,isrc2,idest */
|
||||
{ "shr", 0xac000000, 0x50000000, "i,2,d", 0 }, /* shr #const,isrc2,idest */
|
||||
{ "shrd", 0xb0000000, 0x4c000000, "1,2,d", 0 }, /* shrd isrc1,isrc2,idest */
|
||||
{ "shra", 0xb8000000, 0x44000000, "1,2,d", 0 }, /* shra isrc1,isrc2,idest */
|
||||
{ "shra", 0xbc000000, 0x40000000, "i,2,d", 0 }, /* shra #const,isrc2,idest */
|
||||
|
||||
{ "mov", 0xa0000000, 0x5c00f800, "2,d", 0 }, /* shl r0,isrc2,idest */
|
||||
{ "mov", 0x94000000, 0x69e00000, "i,d", E_MOV }, /* adds #const,r0,idest */
|
||||
{ "nop", 0xa0000000, 0x5ffff800, "", 0 }, /* shl r0,r0,r0 */
|
||||
{ "fnop", 0xb0000000, 0x4ffff800, "", 0 }, /* shrd r0,r0,r0 */
|
||||
|
||||
{ "trap", 0x44000000, 0xb8000000, "1,2,d", 0 }, /* trap isrc1ni,isrc2,idest */
|
||||
|
||||
{ "flush", 0x34000004, 0xc81f0003, "L(2)", E_ADDR }, /* flush #const(isrc2) */
|
||||
{ "flush", 0x34000005, 0xc81f0002, "L(2)++", E_ADDR }, /* flush #const(isrc2)++ */
|
||||
|
||||
{ "and", 0xc0000000, 0x3c000000, "1,2,d", 0 }, /* and isrc1,isrc2,idest */
|
||||
{ "and", 0xc4000000, 0x38000000, "i,2,d", E_AND }, /* and #const,isrc2,idest */
|
||||
{ "andh", 0xcc000000, 0x30000000, "i,2,d", 0 }, /* andh #const,isrc2,idest */
|
||||
{ "andnot", 0xd0000000, 0x2c000000, "1,2,d", 0 }, /* andnot isrc1,isrc2,idest */
|
||||
{ "andnot", 0xd4000000, 0x28000000, "i,2,d", E_U32 }, /* andnot #const,isrc2,idest */
|
||||
{ "andnoth", 0xdc000000, 0x20000000, "i,2,d", 0 }, /* andnoth #const,isrc2,idest */
|
||||
{ "or", 0xe0000000, 0x1c000000, "1,2,d", 0 }, /* or isrc1,isrc2,idest */
|
||||
{ "or", 0xe4000000, 0x18000000, "i,2,d", E_U32 }, /* or #const,isrc2,idest */
|
||||
{ "orh", 0xec000000, 0x10000000, "i,2,d", 0 }, /* orh #const,isrc2,idest */
|
||||
{ "xor", 0xf0000000, 0x0c000000, "1,2,d", 0 }, /* xor isrc1,isrc2,idest */
|
||||
{ "xor", 0xf4000000, 0x08000000, "i,2,d", E_U32 }, /* xor #const,isrc2,idest */
|
||||
{ "xorh", 0xfc000000, 0x00000000, "i,2,d", 0 }, /* xorh #const,isrc2,idest */
|
||||
|
||||
{ "bte", 0x58000000, 0xa4000000, "1,2,r", 0 }, /* bte isrc1s,isrc2,sbroff */
|
||||
{ "bte", 0x5c000000, 0xa0000000, "5,2,r", 0 }, /* bte #const5,isrc2,sbroff */
|
||||
{ "btne", 0x50000000, 0xac000000, "1,2,r", 0 }, /* btne isrc1s,isrc2,sbroff */
|
||||
{ "btne", 0x54000000, 0xa8000000, "5,2,r", 0 }, /* btne #const5,isrc2,sbroff */
|
||||
{ "bla", 0xb4000000, 0x48000000, "1,2,r", E_DELAY }, /* bla isrc1s,isrc2,sbroff */
|
||||
{ "bri", 0x40000000, 0xbc000000, "1", E_DELAY }, /* bri isrc1ni */
|
||||
|
||||
/* Core Escape Instruction Format */
|
||||
{ "lock", 0x4c000001, 0xb000001e, "", 0 }, /* lock set BL in dirbase */
|
||||
{ "calli", 0x4c000002, 0xb000001d, "1", E_DELAY }, /* calli isrc1ni */
|
||||
{ "intovr", 0x4c000004, 0xb000001b, "", 0 }, /* intovr trap on integer overflow */
|
||||
{ "unlock", 0x4c000007, 0xb0000018, "", 0 }, /* unlock clear BL in dirbase */
|
||||
{ "ldio.l", 0x4c000408, 0xb00003f7, "2,d", XP_ONLY }, /* ldio.l isrc2,idest */
|
||||
{ "ldio.s", 0x4c000208, 0xb00005f7, "2,d", XP_ONLY }, /* ldio.s isrc2,idest */
|
||||
{ "ldio.b", 0x4c000008, 0xb00007f7, "2,d", XP_ONLY }, /* ldio.b isrc2,idest */
|
||||
{ "stio.l", 0x4c000409, 0xb00003f6, "1,2", XP_ONLY }, /* stio.l isrc1ni,isrc2 */
|
||||
{ "stio.s", 0x4c000209, 0xb00005f6, "1,2", XP_ONLY }, /* stio.s isrc1ni,isrc2 */
|
||||
{ "stio.b", 0x4c000009, 0xb00007f6, "1,2", XP_ONLY }, /* stio.b isrc1ni,isrc2 */
|
||||
{ "ldint.l", 0x4c00040a, 0xb00003f5, "2,d", XP_ONLY }, /* ldint.l isrc2,idest */
|
||||
{ "ldint.s", 0x4c00020a, 0xb00005f5, "2,d", XP_ONLY }, /* ldint.s isrc2,idest */
|
||||
{ "ldint.b", 0x4c00000a, 0xb00007f5, "2,d", XP_ONLY }, /* ldint.b isrc2,idest */
|
||||
{ "scyc.b", 0x4c00000b, 0xb00007f4, "2", XP_ONLY }, /* scyc.b isrc2 */
|
||||
|
||||
/* CTRL-Format Instructions */
|
||||
{ "br", 0x68000000, 0x94000000, "l", E_DELAY }, /* br lbroff */
|
||||
{ "call", 0x6c000000, 0x90000000, "l", E_DELAY }, /* call lbroff */
|
||||
{ "bc", 0x70000000, 0x8c000000, "l", 0 }, /* bc lbroff */
|
||||
{ "bc.t", 0x74000000, 0x88000000, "l", E_DELAY }, /* bc.t lbroff */
|
||||
{ "bnc", 0x78000000, 0x84000000, "l", 0 }, /* bnc lbroff */
|
||||
{ "bnc.t", 0x7c000000, 0x80000000, "l", E_DELAY }, /* bnc.t lbroff */
|
||||
|
||||
/* Floating Point Escape Instruction Format - pfam.p fsrc1,fsrc2,fdest. */
|
||||
{ "r2p1.ss", 0x48000400, 0xb40001ff, "e,f,g", 0 },
|
||||
{ "r2p1.sd", 0x48000480, 0xb400017f, "e,f,g", 0 },
|
||||
{ "r2p1.dd", 0x48000580, 0xb400007f, "e,f,g", 0 },
|
||||
{ "r2pt.ss", 0x48000401, 0xb40001fe, "e,f,g", 0 },
|
||||
{ "r2pt.sd", 0x48000481, 0xb400017e, "e,f,g", 0 },
|
||||
{ "r2pt.dd", 0x48000581, 0xb400007e, "e,f,g", 0 },
|
||||
{ "r2ap1.ss", 0x48000402, 0xb40001fd, "e,f,g", 0 },
|
||||
{ "r2ap1.sd", 0x48000482, 0xb400017d, "e,f,g", 0 },
|
||||
{ "r2ap1.dd", 0x48000582, 0xb400007d, "e,f,g", 0 },
|
||||
{ "r2apt.ss", 0x48000403, 0xb40001fc, "e,f,g", 0 },
|
||||
{ "r2apt.sd", 0x48000483, 0xb400017c, "e,f,g", 0 },
|
||||
{ "r2apt.dd", 0x48000583, 0xb400007c, "e,f,g", 0 },
|
||||
{ "i2p1.ss", 0x48000404, 0xb40001fb, "e,f,g", 0 },
|
||||
{ "i2p1.sd", 0x48000484, 0xb400017b, "e,f,g", 0 },
|
||||
{ "i2p1.dd", 0x48000584, 0xb400007b, "e,f,g", 0 },
|
||||
{ "i2pt.ss", 0x48000405, 0xb40001fa, "e,f,g", 0 },
|
||||
{ "i2pt.sd", 0x48000485, 0xb400017a, "e,f,g", 0 },
|
||||
{ "i2pt.dd", 0x48000585, 0xb400007a, "e,f,g", 0 },
|
||||
{ "i2ap1.ss", 0x48000406, 0xb40001f9, "e,f,g", 0 },
|
||||
{ "i2ap1.sd", 0x48000486, 0xb4000179, "e,f,g", 0 },
|
||||
{ "i2ap1.dd", 0x48000586, 0xb4000079, "e,f,g", 0 },
|
||||
{ "i2apt.ss", 0x48000407, 0xb40001f8, "e,f,g", 0 },
|
||||
{ "i2apt.sd", 0x48000487, 0xb4000178, "e,f,g", 0 },
|
||||
{ "i2apt.dd", 0x48000587, 0xb4000078, "e,f,g", 0 },
|
||||
{ "rat1p2.ss", 0x48000408, 0xb40001f7, "e,f,g", 0 },
|
||||
{ "rat1p2.sd", 0x48000488, 0xb4000177, "e,f,g", 0 },
|
||||
{ "rat1p2.dd", 0x48000588, 0xb4000077, "e,f,g", 0 },
|
||||
{ "m12apm.ss", 0x48000409, 0xb40001f6, "e,f,g", 0 },
|
||||
{ "m12apm.sd", 0x48000489, 0xb4000176, "e,f,g", 0 },
|
||||
{ "m12apm.dd", 0x48000589, 0xb4000076, "e,f,g", 0 },
|
||||
{ "ra1p2.ss", 0x4800040a, 0xb40001f5, "e,f,g", 0 },
|
||||
{ "ra1p2.sd", 0x4800048a, 0xb4000175, "e,f,g", 0 },
|
||||
{ "ra1p2.dd", 0x4800058a, 0xb4000075, "e,f,g", 0 },
|
||||
{ "m12ttpa.ss", 0x4800040b, 0xb40001f4, "e,f,g", 0 },
|
||||
{ "m12ttpa.sd", 0x4800048b, 0xb4000174, "e,f,g", 0 },
|
||||
{ "m12ttpa.dd", 0x4800058b, 0xb4000074, "e,f,g", 0 },
|
||||
{ "iat1p2.ss", 0x4800040c, 0xb40001f3, "e,f,g", 0 },
|
||||
{ "iat1p2.sd", 0x4800048c, 0xb4000173, "e,f,g", 0 },
|
||||
{ "iat1p2.dd", 0x4800058c, 0xb4000073, "e,f,g", 0 },
|
||||
{ "m12tpm.ss", 0x4800040d, 0xb40001f2, "e,f,g", 0 },
|
||||
{ "m12tpm.sd", 0x4800048d, 0xb4000172, "e,f,g", 0 },
|
||||
{ "m12tpm.dd", 0x4800058d, 0xb4000072, "e,f,g", 0 },
|
||||
{ "ia1p2.ss", 0x4800040e, 0xb40001f1, "e,f,g", 0 },
|
||||
{ "ia1p2.sd", 0x4800048e, 0xb4000171, "e,f,g", 0 },
|
||||
{ "ia1p2.dd", 0x4800058e, 0xb4000071, "e,f,g", 0 },
|
||||
{ "m12tpa.ss", 0x4800040f, 0xb40001f0, "e,f,g", 0 },
|
||||
{ "m12tpa.sd", 0x4800048f, 0xb4000170, "e,f,g", 0 },
|
||||
{ "m12tpa.dd", 0x4800058f, 0xb4000070, "e,f,g", 0 },
|
||||
|
||||
/* Floating Point Escape Instruction Format - pfsm.p fsrc1,fsrc2,fdest. */
|
||||
{ "r2s1.ss", 0x48000410, 0xb40001ef, "e,f,g", 0 },
|
||||
{ "r2s1.sd", 0x48000490, 0xb400016f, "e,f,g", 0 },
|
||||
{ "r2s1.dd", 0x48000590, 0xb400006f, "e,f,g", 0 },
|
||||
{ "r2st.ss", 0x48000411, 0xb40001ee, "e,f,g", 0 },
|
||||
{ "r2st.sd", 0x48000491, 0xb400016e, "e,f,g", 0 },
|
||||
{ "r2st.dd", 0x48000591, 0xb400006e, "e,f,g", 0 },
|
||||
{ "r2as1.ss", 0x48000412, 0xb40001ed, "e,f,g", 0 },
|
||||
{ "r2as1.sd", 0x48000492, 0xb400016d, "e,f,g", 0 },
|
||||
{ "r2as1.dd", 0x48000592, 0xb400006d, "e,f,g", 0 },
|
||||
{ "r2ast.ss", 0x48000413, 0xb40001ec, "e,f,g", 0 },
|
||||
{ "r2ast.sd", 0x48000493, 0xb400016c, "e,f,g", 0 },
|
||||
{ "r2ast.dd", 0x48000593, 0xb400006c, "e,f,g", 0 },
|
||||
{ "i2s1.ss", 0x48000414, 0xb40001eb, "e,f,g", 0 },
|
||||
{ "i2s1.sd", 0x48000494, 0xb400016b, "e,f,g", 0 },
|
||||
{ "i2s1.dd", 0x48000594, 0xb400006b, "e,f,g", 0 },
|
||||
{ "i2st.ss", 0x48000415, 0xb40001ea, "e,f,g", 0 },
|
||||
{ "i2st.sd", 0x48000495, 0xb400016a, "e,f,g", 0 },
|
||||
{ "i2st.dd", 0x48000595, 0xb400006a, "e,f,g", 0 },
|
||||
{ "i2as1.ss", 0x48000416, 0xb40001e9, "e,f,g", 0 },
|
||||
{ "i2as1.sd", 0x48000496, 0xb4000169, "e,f,g", 0 },
|
||||
{ "i2as1.dd", 0x48000596, 0xb4000069, "e,f,g", 0 },
|
||||
{ "i2ast.ss", 0x48000417, 0xb40001e8, "e,f,g", 0 },
|
||||
{ "i2ast.sd", 0x48000497, 0xb4000168, "e,f,g", 0 },
|
||||
{ "i2ast.dd", 0x48000597, 0xb4000068, "e,f,g", 0 },
|
||||
{ "rat1s2.ss", 0x48000418, 0xb40001e7, "e,f,g", 0 },
|
||||
{ "rat1s2.sd", 0x48000498, 0xb4000167, "e,f,g", 0 },
|
||||
{ "rat1s2.dd", 0x48000598, 0xb4000067, "e,f,g", 0 },
|
||||
{ "m12asm.ss", 0x48000419, 0xb40001e6, "e,f,g", 0 },
|
||||
{ "m12asm.sd", 0x48000499, 0xb4000166, "e,f,g", 0 },
|
||||
{ "m12asm.dd", 0x48000599, 0xb4000066, "e,f,g", 0 },
|
||||
{ "ra1s2.ss", 0x4800041a, 0xb40001e5, "e,f,g", 0 },
|
||||
{ "ra1s2.sd", 0x4800049a, 0xb4000165, "e,f,g", 0 },
|
||||
{ "ra1s2.dd", 0x4800059a, 0xb4000065, "e,f,g", 0 },
|
||||
{ "m12ttsa.ss", 0x4800041b, 0xb40001e4, "e,f,g", 0 },
|
||||
{ "m12ttsa.sd", 0x4800049b, 0xb4000164, "e,f,g", 0 },
|
||||
{ "m12ttsa.dd", 0x4800059b, 0xb4000064, "e,f,g", 0 },
|
||||
{ "iat1s2.ss", 0x4800041c, 0xb40001e3, "e,f,g", 0 },
|
||||
{ "iat1s2.sd", 0x4800049c, 0xb4000163, "e,f,g", 0 },
|
||||
{ "iat1s2.dd", 0x4800059c, 0xb4000063, "e,f,g", 0 },
|
||||
{ "m12tsm.ss", 0x4800041d, 0xb40001e2, "e,f,g", 0 },
|
||||
{ "m12tsm.sd", 0x4800049d, 0xb4000162, "e,f,g", 0 },
|
||||
{ "m12tsm.dd", 0x4800059d, 0xb4000062, "e,f,g", 0 },
|
||||
{ "ia1s2.ss", 0x4800041e, 0xb40001e1, "e,f,g", 0 },
|
||||
{ "ia1s2.sd", 0x4800049e, 0xb4000161, "e,f,g", 0 },
|
||||
{ "ia1s2.dd", 0x4800059e, 0xb4000061, "e,f,g", 0 },
|
||||
{ "m12tsa.ss", 0x4800041f, 0xb40001e0, "e,f,g", 0 },
|
||||
{ "m12tsa.sd", 0x4800049f, 0xb4000160, "e,f,g", 0 },
|
||||
{ "m12tsa.dd", 0x4800059f, 0xb4000060, "e,f,g", 0 },
|
||||
|
||||
/* Floating Point Escape Instruction Format - pfmam.p fsrc1,fsrc2,fdest. */
|
||||
{ "mr2p1.ss", 0x48000000, 0xb40005ff, "e,f,g", 0 },
|
||||
{ "mr2p1.sd", 0x48000080, 0xb400057f, "e,f,g", 0 },
|
||||
{ "mr2p1.dd", 0x48000180, 0xb400047f, "e,f,g", 0 },
|
||||
{ "mr2pt.ss", 0x48000001, 0xb40005fe, "e,f,g", 0 },
|
||||
{ "mr2pt.sd", 0x48000081, 0xb400057e, "e,f,g", 0 },
|
||||
{ "mr2pt.dd", 0x48000181, 0xb400047e, "e,f,g", 0 },
|
||||
{ "mr2mp1.ss", 0x48000002, 0xb40005fd, "e,f,g", 0 },
|
||||
{ "mr2mp1.sd", 0x48000082, 0xb400057d, "e,f,g", 0 },
|
||||
{ "mr2mp1.dd", 0x48000182, 0xb400047d, "e,f,g", 0 },
|
||||
{ "mr2mpt.ss", 0x48000003, 0xb40005fc, "e,f,g", 0 },
|
||||
{ "mr2mpt.sd", 0x48000083, 0xb400057c, "e,f,g", 0 },
|
||||
{ "mr2mpt.dd", 0x48000183, 0xb400047c, "e,f,g", 0 },
|
||||
{ "mi2p1.ss", 0x48000004, 0xb40005fb, "e,f,g", 0 },
|
||||
{ "mi2p1.sd", 0x48000084, 0xb400057b, "e,f,g", 0 },
|
||||
{ "mi2p1.dd", 0x48000184, 0xb400047b, "e,f,g", 0 },
|
||||
{ "mi2pt.ss", 0x48000005, 0xb40005fa, "e,f,g", 0 },
|
||||
{ "mi2pt.sd", 0x48000085, 0xb400057a, "e,f,g", 0 },
|
||||
{ "mi2pt.dd", 0x48000185, 0xb400047a, "e,f,g", 0 },
|
||||
{ "mi2mp1.ss", 0x48000006, 0xb40005f9, "e,f,g", 0 },
|
||||
{ "mi2mp1.sd", 0x48000086, 0xb4000579, "e,f,g", 0 },
|
||||
{ "mi2mp1.dd", 0x48000186, 0xb4000479, "e,f,g", 0 },
|
||||
{ "mi2mpt.ss", 0x48000007, 0xb40005f8, "e,f,g", 0 },
|
||||
{ "mi2mpt.sd", 0x48000087, 0xb4000578, "e,f,g", 0 },
|
||||
{ "mi2mpt.dd", 0x48000187, 0xb4000478, "e,f,g", 0 },
|
||||
{ "mrmt1p2.ss", 0x48000008, 0xb40005f7, "e,f,g", 0 },
|
||||
{ "mrmt1p2.sd", 0x48000088, 0xb4000577, "e,f,g", 0 },
|
||||
{ "mrmt1p2.dd", 0x48000188, 0xb4000477, "e,f,g", 0 },
|
||||
{ "mm12mpm.ss", 0x48000009, 0xb40005f6, "e,f,g", 0 },
|
||||
{ "mm12mpm.sd", 0x48000089, 0xb4000576, "e,f,g", 0 },
|
||||
{ "mm12mpm.dd", 0x48000189, 0xb4000476, "e,f,g", 0 },
|
||||
{ "mrm1p2.ss", 0x4800000a, 0xb40005f5, "e,f,g", 0 },
|
||||
{ "mrm1p2.sd", 0x4800008a, 0xb4000575, "e,f,g", 0 },
|
||||
{ "mrm1p2.dd", 0x4800018a, 0xb4000475, "e,f,g", 0 },
|
||||
{ "mm12ttpm.ss",0x4800000b, 0xb40005f4, "e,f,g", 0 },
|
||||
{ "mm12ttpm.sd",0x4800008b, 0xb4000574, "e,f,g", 0 },
|
||||
{ "mm12ttpm.dd",0x4800018b, 0xb4000474, "e,f,g", 0 },
|
||||
{ "mimt1p2.ss", 0x4800000c, 0xb40005f3, "e,f,g", 0 },
|
||||
{ "mimt1p2.sd", 0x4800008c, 0xb4000573, "e,f,g", 0 },
|
||||
{ "mimt1p2.dd", 0x4800018c, 0xb4000473, "e,f,g", 0 },
|
||||
{ "mm12tpm.ss", 0x4800000d, 0xb40005f2, "e,f,g", 0 },
|
||||
{ "mm12tpm.sd", 0x4800008d, 0xb4000572, "e,f,g", 0 },
|
||||
{ "mm12tpm.dd", 0x4800018d, 0xb4000472, "e,f,g", 0 },
|
||||
{ "mim1p2.ss", 0x4800000e, 0xb40005f1, "e,f,g", 0 },
|
||||
{ "mim1p2.sd", 0x4800008e, 0xb4000571, "e,f,g", 0 },
|
||||
{ "mim1p2.dd", 0x4800018e, 0xb4000471, "e,f,g", 0 },
|
||||
|
||||
/* Floating Point Escape Instruction Format - pfmsm.p fsrc1,fsrc2,fdest. */
|
||||
{ "mr2s1.ss", 0x48000010, 0xb40005ef, "e,f,g", 0 },
|
||||
{ "mr2s1.sd", 0x48000090, 0xb400056f, "e,f,g", 0 },
|
||||
{ "mr2s1.dd", 0x48000190, 0xb400046f, "e,f,g", 0 },
|
||||
{ "mr2st.ss", 0x48000011, 0xb40005ee, "e,f,g", 0 },
|
||||
{ "mr2st.sd", 0x48000091, 0xb400056e, "e,f,g", 0 },
|
||||
{ "mr2st.dd", 0x48000191, 0xb400046e, "e,f,g", 0 },
|
||||
{ "mr2ms1.ss", 0x48000012, 0xb40005ed, "e,f,g", 0 },
|
||||
{ "mr2ms1.sd", 0x48000092, 0xb400056d, "e,f,g", 0 },
|
||||
{ "mr2ms1.dd", 0x48000192, 0xb400046d, "e,f,g", 0 },
|
||||
{ "mr2mst.ss", 0x48000013, 0xb40005ec, "e,f,g", 0 },
|
||||
{ "mr2mst.sd", 0x48000093, 0xb400056c, "e,f,g", 0 },
|
||||
{ "mr2mst.dd", 0x48000193, 0xb400046c, "e,f,g", 0 },
|
||||
{ "mi2s1.ss", 0x48000014, 0xb40005eb, "e,f,g", 0 },
|
||||
{ "mi2s1.sd", 0x48000094, 0xb400056b, "e,f,g", 0 },
|
||||
{ "mi2s1.dd", 0x48000194, 0xb400046b, "e,f,g", 0 },
|
||||
{ "mi2st.ss", 0x48000015, 0xb40005ea, "e,f,g", 0 },
|
||||
{ "mi2st.sd", 0x48000095, 0xb400056a, "e,f,g", 0 },
|
||||
{ "mi2st.dd", 0x48000195, 0xb400046a, "e,f,g", 0 },
|
||||
{ "mi2ms1.ss", 0x48000016, 0xb40005e9, "e,f,g", 0 },
|
||||
{ "mi2ms1.sd", 0x48000096, 0xb4000569, "e,f,g", 0 },
|
||||
{ "mi2ms1.dd", 0x48000196, 0xb4000469, "e,f,g", 0 },
|
||||
{ "mi2mst.ss", 0x48000017, 0xb40005e8, "e,f,g", 0 },
|
||||
{ "mi2mst.sd", 0x48000097, 0xb4000568, "e,f,g", 0 },
|
||||
{ "mi2mst.dd", 0x48000197, 0xb4000468, "e,f,g", 0 },
|
||||
{ "mrmt1s2.ss", 0x48000018, 0xb40005e7, "e,f,g", 0 },
|
||||
{ "mrmt1s2.sd", 0x48000098, 0xb4000567, "e,f,g", 0 },
|
||||
{ "mrmt1s2.dd", 0x48000198, 0xb4000467, "e,f,g", 0 },
|
||||
{ "mm12msm.ss", 0x48000019, 0xb40005e6, "e,f,g", 0 },
|
||||
{ "mm12msm.sd", 0x48000099, 0xb4000566, "e,f,g", 0 },
|
||||
{ "mm12msm.dd", 0x48000199, 0xb4000466, "e,f,g", 0 },
|
||||
{ "mrm1s2.ss", 0x4800001a, 0xb40005e5, "e,f,g", 0 },
|
||||
{ "mrm1s2.sd", 0x4800009a, 0xb4000565, "e,f,g", 0 },
|
||||
{ "mrm1s2.dd", 0x4800019a, 0xb4000465, "e,f,g", 0 },
|
||||
{ "mm12ttsm.ss",0x4800001b, 0xb40005e4, "e,f,g", 0 },
|
||||
{ "mm12ttsm.sd",0x4800009b, 0xb4000564, "e,f,g", 0 },
|
||||
{ "mm12ttsm.dd",0x4800019b, 0xb4000464, "e,f,g", 0 },
|
||||
{ "mimt1s2.ss", 0x4800001c, 0xb40005e3, "e,f,g", 0 },
|
||||
{ "mimt1s2.sd", 0x4800009c, 0xb4000563, "e,f,g", 0 },
|
||||
{ "mimt1s2.dd", 0x4800019c, 0xb4000463, "e,f,g", 0 },
|
||||
{ "mm12tsm.ss", 0x4800001d, 0xb40005e2, "e,f,g", 0 },
|
||||
{ "mm12tsm.sd", 0x4800009d, 0xb4000562, "e,f,g", 0 },
|
||||
{ "mm12tsm.dd", 0x4800019d, 0xb4000462, "e,f,g", 0 },
|
||||
{ "mim1s2.ss", 0x4800001e, 0xb40005e1, "e,f,g", 0 },
|
||||
{ "mim1s2.sd", 0x4800009e, 0xb4000561, "e,f,g", 0 },
|
||||
{ "mim1s2.dd", 0x4800019e, 0xb4000461, "e,f,g", 0 },
|
||||
|
||||
{ "fmul.ss", 0x48000020, 0xb40005df, "e,f,g", 0 }, /* fmul.p fsrc1,fsrc2,fdest */
|
||||
{ "fmul.sd", 0x480000a0, 0xb400055f, "e,f,g", 0 }, /* fmul.p fsrc1,fsrc2,fdest */
|
||||
{ "fmul.dd", 0x480001a0, 0xb400045f, "e,f,g", 0 }, /* fmul.p fsrc1,fsrc2,fdest */
|
||||
{ "pfmul.ss", 0x48000420, 0xb40001df, "e,f,g", 0 }, /* pfmul.p fsrc1,fsrc2,fdest */
|
||||
{ "pfmul.sd", 0x480004a0, 0xb400015f, "e,f,g", 0 }, /* pfmul.p fsrc1,fsrc2,fdest */
|
||||
{ "pfmul.dd", 0x480005a0, 0xb400005f, "e,f,g", 0 }, /* pfmul.p fsrc1,fsrc2,fdest */
|
||||
{ "pfmul3.dd", 0x480005a4, 0xb400005b, "e,f,g", 0 }, /* pfmul3.p fsrc1,fsrc2,fdest */
|
||||
{ "fmlow.dd", 0x480001a1, 0xb400045e, "e,f,g", 0 }, /* fmlow.dd fsrc1,fsrc2,fdest */
|
||||
{ "frcp.ss", 0x48000022, 0xb40005dd, "f,g", 0 }, /* frcp.p fsrc2,fdest */
|
||||
{ "frcp.sd", 0x480000a2, 0xb400055d, "f,g", 0 }, /* frcp.p fsrc2,fdest */
|
||||
{ "frcp.dd", 0x480001a2, 0xb400045d, "f,g", 0 }, /* frcp.p fsrc2,fdest */
|
||||
{ "frsqr.ss", 0x48000023, 0xb40005dc, "f,g", 0 }, /* frsqr.p fsrc2,fdest */
|
||||
{ "frsqr.sd", 0x480000a3, 0xb400055c, "f,g", 0 }, /* frsqr.p fsrc2,fdest */
|
||||
{ "frsqr.dd", 0x480001a3, 0xb400045c, "f,g", 0 }, /* frsqr.p fsrc2,fdest */
|
||||
{ "fadd.ss", 0x48000030, 0xb40005cf, "e,f,g", 0 }, /* fadd.p fsrc1,fsrc2,fdest */
|
||||
{ "fadd.sd", 0x480000b0, 0xb400054f, "e,f,g", 0 }, /* fadd.p fsrc1,fsrc2,fdest */
|
||||
{ "fadd.dd", 0x480001b0, 0xb400044f, "e,f,g", 0 }, /* fadd.p fsrc1,fsrc2,fdest */
|
||||
{ "pfadd.ss", 0x48000430, 0xb40001cf, "e,f,g", 0 }, /* pfadd.p fsrc1,fsrc2,fdest */
|
||||
{ "pfadd.sd", 0x480004b0, 0xb400014f, "e,f,g", 0 }, /* pfadd.p fsrc1,fsrc2,fdest */
|
||||
{ "pfadd.dd", 0x480005b0, 0xb400004f, "e,f,g", 0 }, /* pfadd.p fsrc1,fsrc2,fdest */
|
||||
{ "fsub.ss", 0x48000031, 0xb40005ce, "e,f,g", 0 }, /* fsub.p fsrc1,fsrc2,fdest */
|
||||
{ "fsub.sd", 0x480000b1, 0xb400054e, "e,f,g", 0 }, /* fsub.p fsrc1,fsrc2,fdest */
|
||||
{ "fsub.dd", 0x480001b1, 0xb400044e, "e,f,g", 0 }, /* fsub.p fsrc1,fsrc2,fdest */
|
||||
{ "pfsub.ss", 0x48000431, 0xb40001ce, "e,f,g", 0 }, /* pfsub.p fsrc1,fsrc2,fdest */
|
||||
{ "pfsub.sd", 0x480004b1, 0xb400014e, "e,f,g", 0 }, /* pfsub.p fsrc1,fsrc2,fdest */
|
||||
{ "pfsub.dd", 0x480005b1, 0xb400004e, "e,f,g", 0 }, /* pfsub.p fsrc1,fsrc2,fdest */
|
||||
{ "fix.sd", 0x480000b2, 0xb400054d, "e,g", 0 }, /* fix.p fsrc1,fdest */
|
||||
{ "fix.dd", 0x480001b2, 0xb400044d, "e,g", 0 }, /* fix.p fsrc1,fdest */
|
||||
{ "pfix.sd", 0x480004b2, 0xb400014d, "e,g", 0 }, /* pfix.p fsrc1,fdest */
|
||||
{ "pfix.dd", 0x480005b2, 0xb400004d, "e,g", 0 }, /* pfix.p fsrc1,fdest */
|
||||
{ "famov.ss", 0x48000033, 0xb40005cc, "e,g", 0 }, /* famov.p fsrc1,fdest */
|
||||
{ "famov.ds", 0x48000133, 0xb40004cc, "e,g", 0 }, /* famov.p fsrc1,fdest */
|
||||
{ "famov.sd", 0x480000b3, 0xb400054c, "e,g", 0 }, /* famov.p fsrc1,fdest */
|
||||
{ "famov.dd", 0x480001b3, 0xb400044c, "e,g", 0 }, /* famov.p fsrc1,fdest */
|
||||
{ "pfamov.ss", 0x48000433, 0xb40001cc, "e,g", 0 }, /* pfamov.p fsrc1,fdest */
|
||||
{ "pfamov.ds", 0x48000533, 0xb40000cc, "e,g", 0 }, /* pfamov.p fsrc1,fdest */
|
||||
{ "pfamov.sd", 0x480004b3, 0xb400014c, "e,g", 0 }, /* pfamov.p fsrc1,fdest */
|
||||
{ "pfamov.dd", 0x480005b3, 0xb400004c, "e,g", 0 }, /* pfamov.p fsrc1,fdest */
|
||||
/* Opcode pfgt has R bit cleared; pfle has R bit set. */
|
||||
{ "pfgt.ss", 0x48000434, 0xb40001cb, "e,f,g", 0 }, /* pfgt.p fsrc1,fsrc2,fdest */
|
||||
{ "pfgt.dd", 0x48000534, 0xb40000cb, "e,f,g", 0 }, /* pfgt.p fsrc1,fsrc2,fdest */
|
||||
/* Opcode pfgt has R bit cleared; pfle has R bit set. */
|
||||
{ "pfle.ss", 0x480004b4, 0xb400014b, "e,f,g", 0 }, /* pfle.p fsrc1,fsrc2,fdest */
|
||||
{ "pfle.dd", 0x480005b4, 0xb400004b, "e,f,g", 0 }, /* pfle.p fsrc1,fsrc2,fdest */
|
||||
{ "pfeq.ss", 0x48000435, 0xb40001ca, "e,f,g", 0 }, /* pfeq.p fsrc1,fsrc2,fdest */
|
||||
{ "pfeq.dd", 0x48000535, 0xb40000ca, "e,f,g", 0 }, /* pfeq.p fsrc1,fsrc2,fdest */
|
||||
{ "ftrunc.sd", 0x480000ba, 0xb4000545, "e,g", 0 }, /* ftrunc.p fsrc1,fdest */
|
||||
{ "ftrunc.dd", 0x480001ba, 0xb4000445, "e,g", 0 }, /* ftrunc.p fsrc1,fdest */
|
||||
{ "pftrunc.sd", 0x480004ba, 0xb4000145, "e,g", 0 }, /* pftrunc.p fsrc1,fdest */
|
||||
{ "pftrunc.dd", 0x480005ba, 0xb4000045, "e,g", 0 }, /* pftrunc.p fsrc1,fdest */
|
||||
{ "fxfr", 0x48000040, 0xb40005bf, "e,d", 0 }, /* fxfr fsrc1,idest */
|
||||
{ "fiadd.ss", 0x48000049, 0xb40005b6, "e,f,g", 0 }, /* fiadd.w fsrc1,fsrc2,fdest */
|
||||
{ "fiadd.dd", 0x480001c9, 0xb4000436, "e,f,g", 0 }, /* fiadd.w fsrc1,fsrc2,fdest */
|
||||
{ "pfiadd.ss", 0x48000449, 0xb40001b6, "e,f,g", 0 }, /* pfiadd.w fsrc1,fsrc2,fdest */
|
||||
{ "pfiadd.dd", 0x480005c9, 0xb4000036, "e,f,g", 0 }, /* pfiadd.w fsrc1,fsrc2,fdest */
|
||||
{ "fisub.ss", 0x4800004d, 0xb40005b2, "e,f,g", 0 }, /* fisub.w fsrc1,fsrc2,fdest */
|
||||
{ "fisub.dd", 0x480001cd, 0xb4000432, "e,f,g", 0 }, /* fisub.w fsrc1,fsrc2,fdest */
|
||||
{ "pfisub.ss", 0x4800044d, 0xb40001b2, "e,f,g", 0 }, /* pfisub.w fsrc1,fsrc2,fdest */
|
||||
{ "pfisub.dd", 0x480005cd, 0xb4000032, "e,f,g", 0 }, /* pfisub.w fsrc1,fsrc2,fdest */
|
||||
{ "fzchkl", 0x480001d7, 0xb4000428, "e,f,g", 0 }, /* fzchkl fsrc1,fsrc2,fdest */
|
||||
{ "pfzchkl", 0x480005d7, 0xb4000028, "e,f,g", 0 }, /* pfzchkl fsrc1,fsrc2,fdest */
|
||||
{ "fzchks", 0x480001df, 0xb4000420, "e,f,g", 0 }, /* fzchks fsrc1,fsrc2,fdest */
|
||||
{ "pfzchks", 0x480005df, 0xb4000020, "e,f,g", 0 }, /* pfzchks fsrc1,fsrc2,fdest */
|
||||
{ "faddp", 0x480001d0, 0xb400042f, "e,f,g", 0 }, /* faddp fsrc1,fsrc2,fdest */
|
||||
{ "pfaddp", 0x480005d0, 0xb400002f, "e,f,g", 0 }, /* pfaddp fsrc1,fsrc2,fdest */
|
||||
{ "faddz", 0x480001d1, 0xb400042e, "e,f,g", 0 }, /* faddz fsrc1,fsrc2,fdest */
|
||||
{ "pfaddz", 0x480005d1, 0xb400002e, "e,f,g", 0 }, /* pfaddz fsrc1,fsrc2,fdest */
|
||||
{ "form", 0x480001da, 0xb4000425, "e,g", 0 }, /* form fsrc1,fdest */
|
||||
{ "pform", 0x480005da, 0xb4000025, "e,g", 0 }, /* pform fsrc1,fdest */
|
||||
|
||||
/* Floating point pseudo-instructions. */
|
||||
{ "fmov.ss", 0x48000049, 0xb7e005b6, "e,g", 0 }, /* fiadd.ss fsrc1,f0,fdest */
|
||||
{ "fmov.dd", 0x480001c9, 0xb7e00436, "e,g", 0 }, /* fiadd.dd fsrc1,f0,fdest */
|
||||
{ "fmov.sd", 0x480000b3, 0xb400054c, "e,g", 0 }, /* famov.sd fsrc1,fdest */
|
||||
{ "fmov.ds", 0x48000133, 0xb40004cc, "e,g", 0 }, /* famov.ds fsrc1,fdest */
|
||||
{ "pfmov.ds", 0x48000533, 0xb40000cc, "e,g", 0 }, /* pfamov.ds fsrc1,fdest */
|
||||
{ "pfmov.dd", 0x480005c9, 0xb7e00036, "e,g", 0 }, /* pfiadd.dd fsrc1,f0,fdest */
|
||||
{ 0, 0, 0, 0, 0 },
|
||||
|
||||
};
|
||||
|
||||
#define NUMOPCODES ((sizeof i860_opcodes)/(sizeof i860_opcodes[0]))
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
"""Minimal i860 XR/XP disassembler driven by the binutils opcode table.
|
||||
|
||||
Usage: python i860dis.py <va_hex> [len_hex]
|
||||
Disassembles VREND.MNG code (loads at 0xf0400000; file header 0x1C).
|
||||
|
||||
Rebuilt 2026-07-12 (the task-#55 original didn't survive); opcode truth =
|
||||
scratchpad/i860_opcodes.h (binutils include/opcode/i860.h, haiku legacy
|
||||
mirror). Operand syntax per the header comment.
|
||||
"""
|
||||
import re, struct, sys, os
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
MNG = os.path.join(HERE, '..', 'content', 'VREND.MNG')
|
||||
HDR = 0x1C
|
||||
CODEBASE = 0xf0400000
|
||||
CODESZ = 0x3ac80
|
||||
DATASZ = 0x1e9c0
|
||||
|
||||
def load_ops():
|
||||
src = open(os.path.join(HERE, 'i860_opcodes.h')).read()
|
||||
ops = []
|
||||
for m in re.finditer(r'\{\s*"([^"]+)",\s*(0x[0-9a-fA-F]+),\s*(0x[0-9a-fA-F]+),\s*"([^"]*)",\s*(\w+)\s*\}', src):
|
||||
name, match, lose, args, expand = m.groups()
|
||||
ops.append((name, int(match, 16), int(lose, 16), args))
|
||||
return ops
|
||||
|
||||
OPS = load_ops()
|
||||
|
||||
def fields(w):
|
||||
return {
|
||||
'src2': (w >> 21) & 0x1F,
|
||||
'dest': (w >> 16) & 0x1F,
|
||||
'src1': (w >> 11) & 0x1F,
|
||||
'imm16': w & 0xFFFF,
|
||||
'imm5': (w >> 11) & 0x1F,
|
||||
'lbroff': w & 0x03FFFFFF,
|
||||
'sbroff': ((w >> 5) & 0xF800) | (w & 0x07FF), # split 16-bit branch offset
|
||||
'split16': ((w >> 5) & 0xF800) | (w & 0x07FF), # split 16-bit (st) immediate
|
||||
'creg': (w >> 21) & 0x1F,
|
||||
}
|
||||
|
||||
def sext(v, bits):
|
||||
return v - (1 << bits) if v & (1 << (bits - 1)) else v
|
||||
|
||||
def decode(w, pc):
|
||||
for name, match, lose, args in OPS:
|
||||
if (w & match) == match and (w & lose) == 0:
|
||||
# binutils order guarantees no false positive only if we check
|
||||
# match as EXACT on the fixed bits: (w & ~varmask) == match.
|
||||
# Reconstruct: fixed bits = match | lose; variable = operands.
|
||||
f = fields(w)
|
||||
out = []
|
||||
for a in args:
|
||||
if a in '#,()': continue
|
||||
if a == '1': out.append('r%d' % f['src1'])
|
||||
elif a == '2': out.append('r%d' % f['src2'])
|
||||
elif a == 'd': out.append('r%d' % f['dest'])
|
||||
elif a in 'iIJKLM': out.append('0x%x' % f['imm16'])
|
||||
elif a == '5': out.append('%d' % f['imm5'])
|
||||
elif a == 'l':
|
||||
tgt = pc + 4 + (sext(f['lbroff'], 26) << 2)
|
||||
out.append('0x%08x' % tgt)
|
||||
elif a == 'r':
|
||||
tgt = pc + 4 + (sext(f['sbroff'], 16) << 2)
|
||||
out.append('0x%08x' % tgt)
|
||||
elif a in 'sSTU': out.append('0x%x' % f['split16'])
|
||||
elif a == 'e': out.append('f%d' % f['src1'])
|
||||
elif a == 'f': out.append('f%d' % f['src2'])
|
||||
elif a == 'g': out.append('f%d' % f['dest'])
|
||||
elif a == 'c': out.append('creg%d' % f['creg'])
|
||||
else: out.append('?%s' % a)
|
||||
return name, out
|
||||
return None, None
|
||||
|
||||
def candidates(w):
|
||||
"""All table rows consistent with word w (the table has overlaps; the
|
||||
first (most specific) is printed, rest shown when ambiguous)."""
|
||||
res = []
|
||||
for name, match, lose, args in OPS:
|
||||
if (w & match) == match and (w & lose) == 0:
|
||||
res.append(name)
|
||||
return res
|
||||
|
||||
def main():
|
||||
data = open(MNG, 'rb').read()
|
||||
code = data[HDR:HDR + CODESZ]
|
||||
va = int(sys.argv[1], 16)
|
||||
ln = int(sys.argv[2], 16) if len(sys.argv) > 2 else 0x100
|
||||
off = va - CODEBASE
|
||||
for pc in range(va, va + ln, 4):
|
||||
w = struct.unpack_from('<I', code, pc - CODEBASE)[0]
|
||||
name, ops = decode(w, pc)
|
||||
if name is None:
|
||||
print('0x%08x: %08x ???' % (pc, w))
|
||||
else:
|
||||
print('0x%08x: %08x %-10s %s' % (pc, w, name, ','.join(ops)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user