Crusader_Decomp/docs/retail-debug-arg.md
2026-03-25 23:32:36 +01:00

20 KiB

Retail -debug Argument in CRUSADER.EXE

This note records the current evidence-backed read of the retail -debug command-line switch in the live NE CRUSADER.EXE database.

Short version:

  • retail CRUSADER.EXE does recognize -debug
  • the switch is not fully disabled in the parser
  • it sets a global debug-print threshold and two debug-related globals
  • one surviving -debug feature is now concretely identified as an AVI/video-player timing overlay
  • current evidence does not show it constructing or wiring the hidden seg109/seg1408 usecode debugger state object at 1478:659c/659e
  • current best read is debug-print threshold plus movie-playback timing overlay, not the lost hidden debugger bootstrap

Verified Parser Evidence

In the live NE image, HandleCommandlineArgs at 1048:09b9 contains a real branch for "-debug" at 1048:0a93.

The branch body does all of the following:

  • writes 0x000a to 1478:87e0 (g_debugMsgLevel)
  • calls ConsolePrintf(0x32, 1478:0ad6)
  • writes 1 to 1478:0845 (g_someDebugFlag)
  • writes 1 to 1478:0859 (g_someDebugFlag2)

So the parameter has exactly three direct state effects currently recovered:

  • it changes the global print threshold
  • it sets one still-unresolved debug latch at 1478:0845
  • it enables the seg1468 video timing overlay through 1478:0859

The printed string at 1478:0ad6 is:

Debugging mode ON.

That is strong evidence against the narrow claim that retail only still recognizes the string but has the feature body disabled. The retail parser still executes real side effects when -debug is present.

Nearby startup strings in the same table also confirm this is the normal command-line switch cluster:

  • CRUSADER: No Remorse
  • Cheats are active.
  • You DO need help!
  • Debugging mode ON.
  • Enabling ENHANCED mode. (NOT!)

What Still Reacts To -debug

1. Debug message threshold is live

1478:87e0 is already symbolized as g_debugMsgLevel in the live export.

Data-use recovery shows it is read by:

  • ConsolePrintf
  • DebugPrintAndWaitForInput
  • two adjacent positioned debug-print helpers in the same family (12d0:0391, 12d0:0442)

Current best read:

  • -debug sets the runtime print threshold to 10
  • the compare in the wrappers is effectively if (call_level < g_debugMsgLevel) skip
  • so -debug does not create a new console sink by itself; it only changes which existing callsites are eligible to print
  • the shared print staging buffer at 1478:45a6 is allocated independently by 12d0:0513, not by the -debug parser branch

The low-level sink path is now tighter too:

  • ProbablyPrintDebugMessage at 1000:65cc formats through the generic ProbablySomethingDebuggy / FUN_1000_67ac stream pipeline
  • that helper passes DS:1478:6c46 as the target stream object
  • the surrounding DS data at 1478:6c32..1478:6c81 is a four-entry static stdio-style table with handle words 0, 1, 2, and 3
  • the 1478:6c46 entry is therefore the handle-1 stream, i.e. the program's stdout slot

Practical implication:

  • the text side of -debug is not a hidden second debugger UI and not a newly-created in-memory log sink
  • it is ordinary formatted DOS standard-output text gated by g_debugMsgLevel
  • the main reason it is easy to miss is that Crusader spends most of its runtime in graphics mode, while many eligible callsites are startup, shutdown, or failure diagnostics

This matters because it narrows the real effect of the print side:

  • -debug definitely changes print gating
  • but it does not add a new visible on-screen text channel by itself
  • any visible text side effect depends on already-existing print callsites and on how DOS stdout is surfaced in the running environment

1a. What subsystems use that print gate

Recovered ConsolePrintf callers show that the thresholded text/debug lane is not limited to video. Current caller families include:

  • command-line handling and startup/shutdown (HandleCommandlineArgs, CheckForLaurieArg, Init_Everything, Uninitialize)
  • config parsing (LoadConfigFile)
  • cache/shape rebuild work (CacheShapeHand_1070_15a9)
  • item/glob spawning failure paths (ItemGlob_GlobEggHatch)
  • drawlist or display initialization (DList_Init)
  • audio init (Init_ASS)
  • joystick init/calibration (Joystick_Init, Joystick_Calibrate, nearby helper 1400:0c7e)
  • teleporter and several UProcess_* / 1428:* runtime helpers

The rarer blocking helper DebugPrintAndWaitForInput is much narrower. Current recovered callers are:

  • Dispatch_Init_320_0281
  • Dispatch_1320_103e
  • NewGump_Alloc

Those are all failure/debug-stop style paths rather than normal AVI playback logic.

Practical interpretation:

  • there is a non-video -debug lane
  • but the recovered non-video lane is primarily thresholded debug/error text, often in startup or failure handling
  • it is not currently a second confirmed visible feature on the level of the movie timing dots you observed

1c. Recovered printed strings and format strings

The print inventory is now tighter than the earlier subsystem-only caller list.

Recovered call levels so far:

  • 0x32
  • 0xff

Because retail -debug sets g_debugMsgLevel = 10, both recovered levels are above threshold and therefore eligible to print when their code paths execute.

Command-line and startup argument strings

Recovered directly from HandleCommandlineArgs / CheckForLaurieArg:

  • Cheats are active.
  • You DO need help!
  • Debugging mode ON.
  • Enabling ENHANCED mode. (NOT!)
  • Warping to mission %d.
  • Warping to mission %d @ x:%d y:%d z:%d.
  • Defaulting to skill level %d
  • Map offset = %d
  • Destination Egg = %d
  • Demo mode.

These are the cleanest first things to look for in DOSBox logging because they belong to deterministic early startup/argument paths.

Init / config / install-status strings

Recovered from Init_Everything and LoadConfigFile:

  • Using map patch file.
  • Loading: [
  • ]
  • .
  • Running with partial installation.
  • Running with full installation.
  • Redirecting mission %d tune to '%s'

Interpretation:

  • some of these are not human-readable status sentences so much as progress-bar fragments and single-character emitters
  • that means an INT 21h log may show very short writes rather than one tidy line of text

Cache / rebuild strings

Recovered from CacheShapeHand_1070_15a9:

  • Creating Swap file [
  • ]
  • ]
  • \n\r[
  • ]\r[
  • a single-byte progress marker at 1478:0e1e

Interpretation:

  • this lane appears to print progress scaffolding rather than rich prose
  • if DOSBox logs writes with counts but not payload text, many tiny startup writes may belong to this cache/swap-file progress path

Runtime / failure diagnostics

Recovered from narrower runtime and failure paths:

  • dl init from DList_Init
  • COULD NOT CREATE GLOB ITEM! from ItemGlob_GlobEggHatch

These are useful because they are strong “real debug text” fingerprints if they ever show up in a live log.

Audio init marker

Recovered from Init_ASS:

  • .

Current best read:

  • this is another minimal progress marker, not a descriptive sentence

Blocking DebugPrintAndWaitForInput strings

Recovered from the three currently known DebugPrintAndWaitForInput callers:

  • No room for Dispatcher Record/Playback.
  • End of script! (press any key)
  • Out of Memory! [%u]

Interpretation:

  • these are failure/debug-stop strings
  • they are good fingerprints for recognizing the lane, but they should not be expected during healthy normal gameplay

Shutdown strings

Recovered from Uninitialize and nearby startup/shutdown text:

  • CRUSADER: No Remorse
  • No pity. No mercy. No remorse.

Current best read:

  • the first is a normal shutdown-side banner
  • the second is associated with the Laurie-enabled lane and is not a generic -debug print on its own

1b. How to read that text in practice

Current best evidence-backed read is:

  • the messages go to the executable's normal DOS stdout stream, not to a bespoke debugger console
  • if stdout is left attached to CON, text may only be practically visible when Crusader is still in a text-ish startup/shutdown context or when a failure path forces a text-mode-style report
  • replacing stdout with a disk file via shell redirection is now a poor default recommendation, because live user testing showed CRUSADER.EXE -debug > DEBUG.TXT surviving startup but crashing after the intro cutscene

What this does and does not mean:

  • plain file redirection probably changes the handle-1 stream semantics enough to destabilize later stdio/device logic in this executable or runtime
  • it will not magically produce extra chatter unless existing callsites actually execute at or above the threshold
  • it does not by itself surface the hidden seg109/seg1408 debugger UI, because that is a separate control path

Remaining caution:

  • this is a strong static conclusion about the sink identity
  • runtime capture details still depend on the exact DOSBox / shell setup, because the game often runs after switching away from a normal text-console presentation
  • safest current guidance is to keep handle 1 attached to a character-device-style sink and capture around it, rather than redirecting directly to a regular file

2. g_someDebugFlag2 is live

1478:0859 (g_someDebugFlag2) is written by the -debug parser branch and read later in segment 1468.

Recovered readers:

  • VideoPlayer_AdvanceFrameAndHandleSkip (1468:2869)
  • VideoPlayer_StreamChunks (1468:2af4)

Both routines conditionally call VideoPlayer_DrawDebugTimingOverlay (1468:2de9) when g_someDebugFlag2 != 0.

The current live export already places these functions in the VideoPlayer_* neighborhood:

  • preceding function 1468:283f..2868 is typed as VideoPlayer_Run(struct Process * p_proc)
  • AVI_1468_2188 is an AVI-header parser that recognizes AVI / LIST / strl / strh / strf
  • FUN_1468_3904 is a later movi-chunk setup/prime path that calls VideoPlayer_StreamChunks(..., 1)

Current best read of this lane:

  • this is part of a video / presentation / media-processing subsystem
  • -debug leaves behind an extra instrumentation path in that subsystem
  • the behavior does not currently look like debugger-object creation, breakpoint management, or usecode UI entry

VideoPlayer_DrawDebugTimingOverlay is now specific enough to describe concretely:

  • it clears a temporary 0x1f4-byte buffer (500 bytes)
  • it computes marker positions from AVI timing fields using a divisor of 6000
  • it writes marker bytes 8 and 9 into that temporary line buffer
  • it copies the resulting 500-byte line into a video buffer near offset 0x1de * 0x280 + 0x78
  • it builds and copies a second 500-byte line into the next scanline at +0x280

Because the helper writes into two adjacent scanlines of a 0x280-wide (640-pixel) buffer near the bottom of the frame, current best read is:

  • this is a built-in movie-playback timing overlay or marker strip
  • it is probably intended to visualize AVI/video timing state while playback is running
  • it is a practical runtime feature that may be observable during intro/cutscene playback when launched with -debug
  • it is still unrelated to the seg109/seg1408 usecode debugger object model

Runtime confirmation now matches the static read:

  • the observed moving dots at the bottom of played videos are consistent with this helper's two-line marker overlay
  • that closes the strongest user-visible effect of retail -debug

3. g_someDebugFlag is only weakly understood

1478:0845 is symbolized as g_someDebugFlag and is also written by the -debug branch.

Current evidence is weaker here:

  • the parser write is confirmed
  • no equally clear downstream reader has been recovered yet

Current safest read:

  • this is a real surviving -debug state cell
  • it may be vestigial, sparsely used, or hiding inside still-unrecovered data/indirect uses
  • it should not currently be overinterpreted as a debugger bootstrap flag

At this point the absence evidence is fairly strong:

  • direct data-use recovery still finds only the parser write
  • nearby bytes 1478:0844 and 1478:0846 do have real readers/writers, so this is not just a search blind spot across the whole region
  • current best read is orphaned or still-hidden latch, not known active second feature

Comprehensive Current Effect Summary

Based on the current live NE evidence, retail -debug enables or changes the following and no more has yet been proven:

Confirmed direct effects

  1. It prints Debugging mode ON. during command-line handling.
  2. It sets g_debugMsgLevel at 1478:87e0 to 10.
  3. It sets g_someDebugFlag at 1478:0845 to 1.
  4. It sets g_someDebugFlag2 at 1478:0859 to 1.

Confirmed runtime-visible effect

  1. It enables the seg1468 AVI/video-player timing overlay, which draws moving marker dots or traces near the bottom of played videos.

Confirmed non-video effect class

  1. It changes eligibility for existing debug/error print wrappers used across startup, config, cache, joystick, audio, item/glob, dispatch, and some runtime process code.
  2. A smaller subset of those callsites are blocking print-and-wait diagnostics used on failure/debug-stop paths in dispatch/gump allocation code.
  3. The recovered text sink for those wrappers is the program's handle-1 stdio stream at 1478:6c46, so the lane is standard-output text rather than a separate debugger-only channel.
  4. The recovered printable inventory is currently dominated by startup/status/progress strings plus a smaller number of failure-only diagnostics; it is not a rich always-on gameplay console.

Things not currently proven as practical effects

  1. A new visible text console or new text window.
  2. Any hidden usecode debugger bootstrap.
  3. Any connection to the seg109/seg1408 debugger-state pointer at 1478:659c/659e.
  4. Any second confirmed user-visible feature beyond the AVI timing dots.
  5. Any active downstream behavior for 1478:0845.

What -debug Does Not Currently Prove

The hidden retail debugger / unit-inspector work already mapped elsewhere in this repo still centers on:

  • seg109 UI wrappers such as usecode_debugger_open_for_current_unit and usecode_debugger_open_modal
  • seg1408 debugger-state helpers such as usecode_debugger_break_state_create and usecode_debugger_maybe_break_on_current_line
  • the global debugger-state far pointer at 1478:659c/659e

That 1478:659c/659e pointer is still read by the known interpreter-side debugger path, including the break callback lane around 1418:04aa..04b5 and the seg109 debugger-opening wrappers.

What has not been shown in the current -debug pass:

  • no recovered write from the -debug branch to 1478:659c/659e
  • no evidence that -debug calls usecode_debugger_break_state_create
  • no evidence that -debug enters usecode_debugger_open_for_current_unit
  • no evidence that -debug is the same switch as -laurie

So the current answer is:

  • -debug is real
  • -debug still does something
  • but it is currently not the same evidence trail as the hidden usecode debugger bootstrap

Relationship To -laurie

The two switches should stay separated.

Current repo evidence still supports:

  • -laurie / CheckForLaurieArg(...) writes the cheat/debugger gate at 1478:0844 (g_wasLaurieSet / prior cheats_enabled lane)
  • -debug is handled inside the normal command-line option loop in HandleCommandlineArgs
  • -debug writes 1478:0845, 1478:0859, and 1478:87e0, not 1478:0844

That means the old hidden debugger/UI work and the -debug switch are adjacent only at a broad debug features existed level. They are not currently the same recovered control path.

Best Current Conclusion

The wiki claim is only partly right.

Accurate part:

  • there really is a retail -debug command-line argument

Inaccurate or currently unsupported parts:

  • it is not merely a dead recognized string; the parser branch is still live
  • the current evidence does not support secondary monitor debug kernel specifically
  • the current evidence does not support -debug as the missing bootstrap for the hidden seg109/seg1408 usecode debugger

Best current evidence-backed replacement claim:

Retail CRUSADER.EXE still recognizes and executes a live -debug branch. That branch prints Debugging mode ON., raises the debug message level, and enables a concrete seg1468 AVI/video-player timing overlay that draws two 500-byte marker traces into adjacent scanlines near the bottom of the playback buffer. However, current evidence does not show it creating the seg1408 debugger-state object at 1478:659c/659e, so it should not currently be treated as the missing bootstrap for the hidden usecode debugger UI.

Claim Check: E69FB And The "Secondary Monochrome Monitor" Idea

An external claim said the potential debug instructions were at flat file offset E69FB and might only be visible on a secondary monochrome monitor.

Current evidence does not support that.

E69FB mapping

Using the local NE segment map:

  • NE segment 144 begins at file offset 0xE3C00
  • flat offset 0xE69FB therefore lands at segment-relative offset 0x2dfb
  • in the live NE image that maps to 1478:2dfb

The bytes around that location are not executable instructions for a hidden monitor-debug path. They fall inside a data/string cluster:

  • KeyboardProcess
  • KEYIO.C
  • PRIORITY.C
  • SystemTimer
  • SYSTIMER.C
  • AccWait

1478:2dfb itself lands inside the SYSTIMER.C string, not inside a code body.

Current safest read:

  • the cited flat offset is almost certainly a mistaken pointer into a data/descriptor/source-file-name region
  • it is not a useful anchor for -debug behavior and not evidence for hidden display-specific code by itself

Secondary monochrome monitor check

The current -debug evidence points elsewhere:

  • text/debug output goes through the normal stdout sink at 1478:6c46
  • the user-visible runtime feature is the seg1468 AVI timing overlay drawn into the main video buffer

Additional negative evidence from the live program:

  • no recovered text strings mentioning mono, monochrome, hercules, or MDA
  • no recovered instruction hits referencing obvious monochrome-adapter I/O ports such as 0x3b4, 0x3b5, 0x3b8, or 0x3ba
  • no recovered instruction hits referencing the B000 monochrome text-memory window

That does not mathematically prove that no historical or stripped monitor-debug code ever existed during development, but it does mean:

  • the current retail -debug evidence does not support the "secondary monochrome monitor" explanation
  • the current retail implementation is better explained as stdout-gated text plus the AVI timing overlay

Ghidra Refinements Applied

The live CRUSADER.EXE database now carries this batch's first-pass refinements too:

  • 1468:2869 -> VideoPlayer_AdvanceFrameAndHandleSkip
  • 1468:2af4 -> VideoPlayer_StreamChunks
  • 1468:2de9 -> VideoPlayer_DrawDebugTimingOverlay
  • parser and global comments at 1048:0a93, 1478:0845, 1478:0859, and 1478:87e0
  • overlay-gate comments at 1468:2920 and 1468:2dc8
  • debug-print helper comments at 12d0:0391, 12d0:03ee, and 12d0:0442
  • sink/init comments at 1000:65cc and 12d0:0513
  • stdio-table comments at 1478:6c32 and 1478:6c46

Follow-Up Targets

If this lane is revisited, the highest-value remaining questions are:

  • identify a concrete behavioral name for 1478:0845 by finding a real downstream consumer
  • classify the remaining nearby seg1468 helpers so the AVI/video-player object layout around +0x117/+0x11b/+0x11f/+0x123 can be named cleanly
  • test whether the overlay is visibly present during intro/cutscene playback in DOSBox or another live runtime
  • find meaningful callsites into the seg12d0 positioned debug-print helpers to learn whether -debug exposes additional text diagnostics beyond movie timing markers
  • determine the default runtime value path for g_debugMsgLevel more rigorously, since static initialized data alone does not yet explain the full practical print behavior
  • sample a few representative ConsolePrintf / DebugPrintAndWaitForInput format strings under live capture so the stdout lane can be characterized with runtime output rather than only static caller families