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.EXEdoes 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
-debugfeature 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, notthe 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
0x000ato1478:87e0(g_debugMsgLevel) - calls
ConsolePrintf(0x32, 1478:0ad6) - writes
1to1478:0845(g_someDebugFlag) - writes
1to1478: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 RemorseCheats 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:
ConsolePrintfDebugPrintAndWaitForInput- two adjacent positioned debug-print helpers in the same family (
12d0:0391,12d0:0442)
Current best read:
-debugsets the runtime print threshold to10- the compare in the wrappers is effectively
if (call_level < g_debugMsgLevel) skip - so
-debugdoes 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:45a6is allocated independently by12d0:0513, not by the-debugparser branch
The low-level sink path is now tighter too:
ProbablyPrintDebugMessageat1000:65ccformats through the genericProbablySomethingDebuggy/FUN_1000_67acstream pipeline- that helper passes DS:
1478:6c46as the target stream object - the surrounding DS data at
1478:6c32..1478:6c81is a four-entry static stdio-style table with handle words0,1,2, and3 - the
1478:6c46entry is therefore the handle-1stream, i.e. the program'sstdoutslot
Practical implication:
- the text side of
-debugis 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:
-debugdefinitely 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
stdoutis 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 helper1400:0c7e) - teleporter and several
UProcess_*/1428:*runtime helpers
The rarer blocking helper DebugPrintAndWaitForInput is much narrower. Current recovered callers are:
Dispatch_Init_320_0281Dispatch_1320_103eNewGump_Alloc
Those are all failure/debug-stop style paths rather than normal AVI playback logic.
Practical interpretation:
- there is a non-video
-debuglane - 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:
0x320xff
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 %dMap offset = %dDestination Egg = %dDemo 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 21hlog 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 initfromDList_InitCOULD NOT CREATE GLOB ITEM!fromItemGlob_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 RemorseNo 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
-debugprint 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
stdoutstream, not to a bespoke debugger console - if
stdoutis left attached toCON, 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
stdoutwith a disk file via shell redirection is now a poor default recommendation, because live user testing showedCRUSADER.EXE -debug > DEBUG.TXTsurviving 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
1attached 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..2868is typed asVideoPlayer_Run(struct Process * p_proc) AVI_1468_2188is an AVI-header parser that recognizesAVI/LIST/strl/strh/strfFUN_1468_3904is a latermovi-chunk setup/prime path that callsVideoPlayer_StreamChunks(..., 1)
Current best read of this lane:
- this is part of a video / presentation / media-processing subsystem
-debugleaves 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 (500bytes) - it computes marker positions from AVI timing fields using a divisor of
6000 - it writes marker bytes
8and9into that temporary line buffer - it copies the resulting
500-byte line into a video buffer near offset0x1de * 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
-debugstate 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:0844and1478:0846do 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, notknown 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
- It prints
Debugging mode ON.during command-line handling. - It sets
g_debugMsgLevelat1478:87e0to10. - It sets
g_someDebugFlagat1478:0845to1. - It sets
g_someDebugFlag2at1478:0859to1.
Confirmed runtime-visible effect
- 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
- It changes eligibility for existing debug/error print wrappers used across startup, config, cache, joystick, audio, item/glob, dispatch, and some runtime process code.
- A smaller subset of those callsites are blocking
print-and-waitdiagnostics used on failure/debug-stop paths in dispatch/gump allocation code. - The recovered text sink for those wrappers is the program's handle-
1stdio stream at1478:6c46, so the lane is standard-output text rather than a separate debugger-only channel. - 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
- A new visible text console or new text window.
- Any hidden usecode debugger bootstrap.
- Any connection to the seg109/seg1408 debugger-state pointer at
1478:659c/659e. - Any second confirmed user-visible feature beyond the AVI timing dots.
- 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_unitandusecode_debugger_open_modal - seg1408 debugger-state helpers such as
usecode_debugger_break_state_createandusecode_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
-debugbranch to1478:659c/659e - no evidence that
-debugcallsusecode_debugger_break_state_create - no evidence that
-debugentersusecode_debugger_open_for_current_unit - no evidence that
-debugis the same switch as-laurie
So the current answer is:
-debugis real-debugstill 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 at1478:0844(g_wasLaurieSet/ priorcheats_enabledlane)-debugis handled inside the normal command-line option loop inHandleCommandlineArgs-debugwrites1478:0845,1478:0859, and1478:87e0, not1478: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
-debugcommand-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 kernelspecifically - the current evidence does not support
-debugas the missing bootstrap for the hidden seg109/seg1408 usecode debugger
Best current evidence-backed replacement claim:
Retail
CRUSADER.EXEstill recognizes and executes a live-debugbranch. That branch printsDebugging 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 at1478: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
144begins at file offset0xE3C00 - flat offset
0xE69FBtherefore lands at segment-relative offset0x2dfb - 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:
KeyboardProcessKEYIO.CPRIORITY.CSystemTimerSYSTIMER.CAccWait
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
-debugbehavior 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
stdoutsink at1478: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, orMDA - no recovered instruction hits referencing obvious monochrome-adapter I/O ports such as
0x3b4,0x3b5,0x3b8, or0x3ba - no recovered instruction hits referencing the
B000monochrome 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
-debugevidence 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_AdvanceFrameAndHandleSkip1468:2af4->VideoPlayer_StreamChunks1468:2de9->VideoPlayer_DrawDebugTimingOverlay- parser and global comments at
1048:0a93,1478:0845,1478:0859, and1478:87e0 - overlay-gate comments at
1468:2920and1468:2dc8 - debug-print helper comments at
12d0:0391,12d0:03ee, and12d0:0442 - sink/init comments at
1000:65ccand12d0:0513 - stdio-table comments at
1478:6c32and1478:6c46
Follow-Up Targets
If this lane is revisited, the highest-value remaining questions are:
- identify a concrete behavioral name for
1478:0845by finding a real downstream consumer - classify the remaining nearby seg1468 helpers so the AVI/video-player object layout around
+0x117/+0x11b/+0x11f/+0x123can 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
-debugexposes additional text diagnostics beyond movie timing markers - determine the default runtime value path for
g_debugMsgLevelmore rigorously, since static initialized data alone does not yet explain the full practical print behavior - sample a few representative
ConsolePrintf/DebugPrintAndWaitForInputformat strings under live capture so the stdout lane can be characterized with runtime output rather than only static caller families