diff --git a/docs/dev/GE-ENV-PROBES.md b/docs/dev/GE-ENV-PROBES.md
index 4773ca05..dece97b7 100644
--- a/docs/dev/GE-ENV-PROBES.md
+++ b/docs/dev/GE-ENV-PROBES.md
@@ -20,7 +20,7 @@ Two classes:
| Env var | File:line | What it does | Status |
|---|---|---|---|
| `GE_PCDUMP` | `port/fast3d/gfx_opengl.cpp:1079` (`is_pcdump`), `gfx_opengl.cpp:700`, `port/src/video.c:188`, `port/src/config.c:46` (`configGetFrameDump`) | Per-frame framebuffer PPM dump to `$PCDUMP` dir (verification-ritual golden captures, `tools_pc/framediff.py`/`pixcount.py`). Also settable via `[Debug] FrameDump` ini (env wins). **M-33/D168: the writer now emits rows top-to-bottom — captures before that fix are vertically flipped.** | **live** |
-| `GE_INPUTSCRIPT` | `port/src/input.c:306` | Headless scripted controller-0 input: `":,…;…"` (`SUP/SDOWN/SLEFT/SRIGHT/SNONE` sustain, buttons pulse). Sole input source when set. | **live** |
+| `GE_INPUTSCRIPT` | `port/src/input.c:306` | Headless scripted controller-0 input: `":,…;…"` (`SUP/SDOWN/SLEFT/SRIGHT/SNONE` sustain, buttons pulse; `ZHOLD`/`ZREL` sustain fire, D207 M-201). Sole input source when set. | **live** |
| `GE_INPUTLOG` | `port/src/input.c:329`, `port/src/config.c:53` (`configGetInputLog`) | Logs computed pad state (buttons + stick) each poll when non-zero. Also `[Debug] InputLog` ini. Also traces D165 `menuptr est/tgt/eff/stick`. | **live** |
| `GE_STARTMENU` (+ `GE_STARTMENU_PAGE`, `GE_STARTMENU_DIFF`) | `src/game/lv.c:398-402` (`#ifdef PORT`) | Boot straight into a front-end menu id (13=MISSION_COMPLETE, 10=BRIEFING, 7=MISSION_SELECT, 12=MISSION_FAILED, 6=MODE_SELECT); `_PAGE`=folder row (def 1=Dam), `_DIFF`=0..3. Crash-test a screen fast. | **live** |
| `GE_UNLOCK_ALL` | `src/game/debugmenu_handler.c:1098` (`get_debug_enable_all_levels_flag`, `#ifdef PORT`) | Mission-select shows every solo level (playtest jump-to-any-level aid). Cached in a `static int c`. | **live** |
diff --git a/docs/dev/findings.md b/docs/dev/findings.md
index 3b632356..d4b5f58b 100644
--- a/docs/dev/findings.md
+++ b/docs/dev/findings.md
@@ -11370,6 +11370,13 @@ the Surface 1 spawn capture is unchanged across the strip.
**Status (from §F table):** **OPEN — not newly root-caused; high confidence this is D207 reproduced, not an independent bug.** Recommend folding this repro into D207 (or closing D241 as a duplicate once D207 is reopened) rather than tracking separately. Next: re-run M-77's `GE_FORCEALARM=1` harness on `-level_09` with scripted PP7 and AK47 fire (not just generic fire) against Klobb as a control, watching `g_sndAllocatedVoicesCount` / the (currently reverted) `[D207-DROP]` counters to see whether the 8-cap is actually hit for two of the three weapons and not the third — or whether it is really the sample-envelope-length distinction above. No runtime verification possible in this environment (no ROM/display).
+**2026-09-26 (M-201, v0.4.0): user still hears gunshot drop-outs "with rapid fire guns while a lot of other sound effects occur" (D322 garbling itself is gone). Headless measurements, no drop reproduced.**
+
+- **Static:** player shots (`gunfire.c:3296-3340`) and guard shots (`chraction.c:5630`) both stop the previous shot sound and start a new one, using at most 2 handles per hand, rate-limited by `SoundTriggerRate` against `g_GlobalTimer`. At 1 tick/frame a shot sound can start every `rate+1` ticks; at the N64's 2–3 ticks/frame the check rounds up to frame boundaries (rate 3: every 6 ticks at 3 ticks/frame vs 4 on PC), so the port plays up to ~1.5x more shot sounds (a D13-class divergence) but does not hold more simultaneous voices. `sndDeactivate` only posts `AL_SNDP_DEACTIVATE_EVT`; the slot frees in `sndDisposeSound`.
+- **Runtime (Dam, invincibility + all guns, `GE_FORCEALARM=1`, PP7 held in bursts via new `GE_INPUTSCRIPT` `ZHOLD`/`ZREL`, ~100 s each):** `GE_AUDIOTRACE` `[D207-DROP]` = 0 at both FpsCap 60 and 30 (peak SFX slots 8/8 and 7/8); `GE_D322` at 60 fps: worst sample `sfx` 7/8, `pv free` 9/24, `evtq` 9/64, audio `q` 720/2880, `drop=0`. Both drop paths (`preempt-scan` = 8-cap, `no-limit` = physical pool) are covered by `[D207-DROP]`; failing sounds first retry every 33 ms (audible as late, not missing).
+- **Harness limits:** `gunRequestHandWeaponChange` / the level-start equip call did not switch weapons mid-level from the harness, so the automatic-weapon case (AK47/Klobb) is untested headless.
+- **Next:** capture the user's real scenario with `GE_AUDIOTRACE=1 GE_D322=1 GE_D204=1` (`scratch/playtest_audio.bat`), noting level, weapon and time of each drop-out; any `[D207-DROP]` line names the layer. If none appear, the "drop-out" is the faithful stop-then-restart of the per-hand shot sound, not a lost voice.
+
## D242 — Player-death sequence UX regression vs N64 original: PC requires an LMB click to proceed to the mission-failed/file-select screen; user recalls N64 auto-plays…
**Player-death sequence UX regression vs N64 original: PC requires an LMB click to proceed to the mission-failed/file-select screen; user recalls N64 auto-plays 3 death-camera angles then transitions with no input needed (M-106, user QA report).** **The 3-angle auto-replay and its no-input auto-transition both exist, unmodified, in the decomp — confirmed, not a design difference removed for the port.** `bondviewSetCameraMode(CAMERAMODE_DEATH_CAM_SP)` (`src/game/bondview2.c:901-1010`) calls `pickDeathCameraAngles()` (`bondview2.c:641-782`, picks a fresh random valid camera angle each call) to stage each replay; when it returns 0 — no further valid angle found — the code calls `bossRunTitleStage()` directly, and the source comment at `bondview2.c:1006-1007` literally reads *"...or when the three death replays are finished."* The 3-cycle count is driven by `bondviewAdvanceCameraMode()` (`bondview2.c:1041-1078`): `CAMERAMODE_DEATH_CAM_SP` → `CAMERAMODE_DEATH_CAM_MP` → `camera_mode++`, looping back to `DEATH_CAM_SP` while `camera_mode < CAMERAMODE_SWIRL` (three iterations). Each individual replay's own advance is driven purely by animation/timer state (`bondview2.c:1467-1490`): the `g_CameraAfterCinema` flag that `bondviewAdvanceCameraMode()` acts on is set automatically once `modelGetAnimFrame(bodyModel) >= modelGetAnimEndFrame(bodyModel)`, or after a `camera_transition_timer >= 180.0f` tick-timeout fallback when there is no body model — **no button check in that branch; fully automatic.** **The button-press the user is hitting is a real, intentional N64 skip-ahead shortcut, not a port-added gate:** the same code block (`bondview2.c:1483-1489`) *also* fires the identical advance immediately on `(buttons & ~oldbuttons & (CONT_A | B_BUTTON | Z_TRIG | START_BUTTON))` whenever `bonddead && redbloodfinished && deathanimfinished` — i.e. on real N64 hardware, pressing A/B/Z/Start at any point during a death replay skips straight to the end, by design. **On the port, LMB (mouse fire) is mapped to `GE_CONT_G`, which is Z_TRIG** (`port/src/input.c:664-665`: `(mb & SDL_BUTTON(SDL_BUTTON_LEFT)) ... button \|= GE_CONT_G`; binding table `input.c:351`: `{"Z",GE_CONT_G}`) — exactly one of the four original skip buttons. So an LMB click during the death sequence is GE's own original cutscene-skip input, faithfully wired through the port's control remap, not a port-invented "click to continue" prompt. **What this investigation could not determine** (no build/runtime access in this worktree): whether the *automatic*, no-input path actually completes on PC in a reasonable time. `camera_transition_timer` accumulates via `g_GlobalTimerDelta` (`bondview2.c:1470,1493`), which is set from the same `g_ClockTimer`/`speedgraphframes`/`deltaFrames` chain flagged in D240 above and investigated under D155/D186/D193/D204/D209 — if any residual in that chain stalls or mis-scales specifically in this state, the auto-advance could take far longer on PC than on N64 (or effectively never complete before the player gives up and clicks), which would make an LMB click *feel* mandatory even though the ground-truth logic never requires one.
diff --git a/port/shim/stdlib.h b/port/shim/stdlib.h
index d0b49ede..71f47b50 100644
--- a/port/shim/stdlib.h
+++ b/port/shim/stdlib.h
@@ -27,6 +27,7 @@
extern double atof();
extern double strtod();
extern long int strtol();
+extern int atoi();
extern int atexit();
/* D324: getenv was previously declared only on non-Windows, on the (wrong)
* assumption that MinGW's other CRT headers leak a prototype -- config.c gets
diff --git a/port/src/input.c b/port/src/input.c
index c7de784d..e2be075d 100644
--- a/port/src/input.c
+++ b/port/src/input.c
@@ -589,7 +589,8 @@ static void inputOpenPads(void)
#define INPUTSCRIPT_PULSE 6
struct scriptEntry { long frame; unsigned mask; int sx, sy; int hasStick;
- int hasMouse, mdx, mdy; int hasHold, hold; }; /* D337 mouse/aim tokens */
+ int hasMouse, mdx, mdy; int hasHold, hold; /* D337 mouse/aim tokens */
+ int hasZHold, zhold; }; /* D207: sustained fire */
static struct scriptEntry scriptEntries[INPUTSCRIPT_MAX];
static int scriptCount = -1; /* -1 = not parsed yet, 0 = parsed empty */
static long scriptFrame = 0;
@@ -626,6 +627,10 @@ static void scriptApplyToken(struct scriptEntry *e, const char *s, int n)
if (n == 5 && SDL_strncasecmp("RHOLD", s, 5) == 0) { e->hasHold = 1; e->hold = 1; return; }
if (n == 5 && SDL_strncasecmp("LHOLD", s, 5) == 0) { e->hasHold = 1; e->hold = 2; return; }
if (n == 4 && SDL_strncasecmp("RREL", s, 4) == 0) { e->hasHold = 1; e->hold = 0; return; }
+ /* D207: ZHOLD / ZREL hold / release fire (Z) for automatic weapons;
+ * independent of RHOLD so aim + fire can be held together. */
+ if (n == 5 && SDL_strncasecmp("ZHOLD", s, 5) == 0) { e->hasZHold = 1; e->zhold = 1; return; }
+ if (n == 4 && SDL_strncasecmp("ZREL", s, 4) == 0) { e->hasZHold = 1; e->zhold = 0; return; }
e->hasStick = 1;
if (n == 3 && SDL_strncasecmp("SUP", s, 3) == 0) { e->sy = STICK_MAX; return; }
if (n == 5 && SDL_strncasecmp("SDOWN", s, 5) == 0) { e->sy = -STICK_MAX; return; }
@@ -659,6 +664,7 @@ static void scriptParse(void)
e->hasStick = 0;
e->hasMouse = e->mdx = e->mdy = 0;
e->hasHold = e->hold = 0;
+ e->hasZHold = e->zhold = 0;
while (*p && *p != ';') {
const char *tok = p;
while (*p && *p != ',' && *p != ';') ++p;
@@ -686,10 +692,10 @@ static int scriptIsActive(void)
* Returns the scripted button mask for the current frame; advances the frame
* counter (call exactly once per controller-0 read). */
/* D337: current sustained scripted mouse delta / aim hold (latest entry). */
-static int s_scriptMouseOn = 0, s_scriptMDX = 0, s_scriptMDY = 0, s_scriptHold = 0;
+static int s_scriptMouseOn = 0, s_scriptMDX = 0, s_scriptMDY = 0, s_scriptHold = 0, s_scriptZHold = 0;
static void scriptPreMouse(void)
{
- long bestM = -1, bestH = -1;
+ long bestM = -1, bestH = -1, bestZ = -1;
for (int i = 0; i < scriptCount; ++i) {
long d = scriptFrame - scriptEntries[i].frame;
if (d < 0) continue;
@@ -703,6 +709,10 @@ static void scriptPreMouse(void)
bestH = scriptEntries[i].frame;
s_scriptHold = scriptEntries[i].hold;
}
+ if (scriptEntries[i].hasZHold && scriptEntries[i].frame > bestZ) {
+ bestZ = scriptEntries[i].frame;
+ s_scriptZHold = scriptEntries[i].zhold;
+ }
}
}
@@ -711,6 +721,7 @@ static unsigned scriptApply(unsigned button)
if (!scriptIsActive()) {
return button;
}
+ scriptPreMouse(); /* D207: refresh sustained holds even off the mouse path (idempotent) */
unsigned m = 0;
long bestStickFrame = -1;
for (int i = 0; i < scriptCount; ++i) {
@@ -727,6 +738,7 @@ static unsigned scriptApply(unsigned button)
}
if (s_scriptHold == 1) m |= GE_CONT_R; /* D337 RHOLD */
if (s_scriptHold == 2) m |= GE_CONT_L; /* D337 LHOLD (Q / "LeanLeft") */
+ if (s_scriptZHold) m |= GE_CONT_G; /* D207 ZHOLD (fire) */
scriptFrame++;
return m;
}
diff --git a/port/src/optionsoverlay.c b/port/src/optionsoverlay.c
index 06c7c2e8..d3e94550 100644
--- a/port/src/optionsoverlay.c
+++ b/port/src/optionsoverlay.c
@@ -47,6 +47,11 @@
#include "optionsoverlay.h"
#include "../fast3d/gfx_api.h"
+/* D324 class: -Iinclude resolves to GE's N64 stub, which does not
+ * declare lround; without this the call is an implicit `int lround()`
+ * (GCC only rescued it via its builtin signature). */
+long lround(double x);
+
/* ---- game symbols (rendering/UI only; see input.c for the same pattern) ---- */
struct font;
struct fontchar;