# Crusader: No Remorse — Phar Lap DOS Extender Analysis This file covers the Phar Lap 286 DOS extender code portion of `CRUSADER.EXE` — the outer MZ/P2 stub that bootstraps the NE game image. ## Architecture Notes ### Correction: The Game Ships As A Bound NE Executable **Important**: The installed copy does **not** contain a separate `.EXP` file. `CRUSADER.EXE` is a bound executable with an outer DOS `MZ` stub and an internal `NE` executable image. The Phar Lap loader/runtime code and the game's real segment layout are both described inside this same file. The flow is: 1. `entry` → checks DOS version, CPU type 2. `init_dos_extender` → sets up protected mode (VCPI/DPMI) 3. `load_exp_file` → opens the game's `.EXP` file 4. `load_executable_image` → parses P2/MZ headers, creates segments, applies relocations 5. `task_switch_to_child` → transfers control to the actual game code For the installed retail copy, this means the currently loaded Ghidra program is only one interpretation of `CRUSADER.EXE`. The next import should target the **NE layer of the same file**, not a missing external `.EXP`. ### Segment 1339: Fast Memory Operations `FUN_1339_02a8` contains an unrolled loop (Duff's device pattern with 57 iterations) — a hand-optimized **fast memory fill/add** routine, typical in DOS game graphics engines. ### EMS Memory (Segment 1677) The game uses **EMS (Expanded Memory Specification)** via INT 67h for additional memory beyond the 1MB real-mode limit. Functions in segment 1677 manage EMS page frames and handle allocation/deallocation. ## Named Functions ### Entry & Startup | Address | Name | Description | |---------|------|-------------| | `10da:7c40` | `entry` | Program entry point — checks CPU, parses command line, launches game | | `10da:1816` | `main_init_and_run` | Main initialization — loads child EXP, sets up subsystems, runs game | | `1760:1432` | `parse_cmdline_and_run` | Parses command-line args and invokes main_init_and_run | | `1760:42fa` | `init_dos_extender` | Initializes Phar Lap 286 DOS extender (CPU check, VCPI/DPMI setup) | ### Executable Loading | Address | Name | Description | |---------|------|-------------| | `1760:2cdf` | `load_exp_file` | Loads .EXP executable — opens file, reads headers, allocates memory | | `1760:1dfc` | `load_executable_image` | Parses P2/MZ headers, loads segments, creates LDT entries | | `1760:24a6` | `apply_relocations` | Applies segment relocations to loaded executable | | `1760:5eca` | `exec_child_process` | Executes child process with command-line arguments | | `1760:5fee` | `exec_program_with_args` | Builds command line, locates and executes a program | | `10da:1f7e` | `load_and_run_child` | Wrapper: loads child EXP and initializes it | ### System Services | Address | Name | Description | |---------|------|-------------| | `10da:2330` | `dos_exit` | Calls INT 21h AH=4Ch (terminate program) | | `1760:42aa` | `detect_cpu_type` | Detects CPU: 0=8086, 2=286, 3=386+ | | `1339:04a6` | `dpmi_set_interrupt_vector` | INT 31h — DPMI set interrupt vector | | `1339:06ca` | `switch_to_real_mode` | Switches CPU from protected to real mode | | `1339:06f2` | `switch_to_protected_mode` | Switches CPU from real to protected mode | | `1339:0076` | `setup_interrupt_handlers` | Configures interrupt vectors via INT 21h | | `1339:0a38` | `dos_int21h_wrapper` | Simple INT 21h call wrapper | | `1339:0a82` | `dos_int21h_with_regs` | INT 21h call with register parameters | | `10da:2360` | `get_flags_register` | Returns CPU FLAGS register | | `10da:2363` | `set_flags_register` | Sets CPU FLAGS register | ### Memory Management | Address | Name | Description | |---------|------|-------------| | `1677:0d12` | `cleanup_ems_memory` | Frees EMS (INT 67h) memory handles | | `10da:14fc` | `init_stack_fill_cc` | Fills stack with 0xCC (INT 3) for debugging/guard | | `10da:1706` | `get_segment_base_addr` | Computes linear base address from segment descriptor | ### Task Management | Address | Name | Description | |---------|------|-------------| | `10da:19ca` | `task_switch_to_child` | Context switch to child process | | `10da:1946` | `task_switch_from_child` | Context switch back from child process | | `10da:1af4` | `call_termination_handler` | Calls registered termination callback | ### I/O & Output | Address | Name | Description | |---------|------|-------------| | `10da:00d6` | `flush_output_buffer` | Flushes buffered output via function pointer | | `10da:0132` | `putchar_buffered` | Writes character to buffer, flushes on newline | | `10da:0808` | `memcopy_to_buffer` | Copies N bytes from source to destination buffer | | `10da:178c` | `print_error_message` | Formats and prints load error (references "not loaded: %s") | | `10da:09e4` | `print_fatal_error` | Prints "Fatal Error" prefix + message | | `10da:192a` | `print_internal_error` | Prints "Internal Error" message | ### Interrupt Management | Address | Name | Description | |---------|------|-------------| | `10da:1ec0` | `restore_interrupt_vectors` | Restores INT 2Fh and INT 67h vectors | | `10da:2249` | `restore_int_2f_67` | Restores INT 15h vector if saved | | `1760:3d86` | `init_system_check` | Validates system (CPU, DOS version, VCPI/DPMI, memory) | ### Utility | Address | Name | Description | |---------|------|-------------| | `10da:15ea` | `check_ds_segment` | Returns true if DS == 0x10 (checks data segment selector) | | `1760:3c9e` | `nop_stub` | Always returns 0 (unused hook) | ## Key String References | Address | String | Context | |---------|--------|---------| | `13fc:0016` | `$Id: comhighc.c 1.1 91/08/06...` | Phar Lap C runtime source ID | | `13fc:0048` | `$Id: comutils.c 1.1 91/08/06...` | Phar Lap utility functions source ID | | `13fc:0078` | `Serial Number ` | DOS extender serial validation | | `13fc:14ca` | `Internal Error` | Error class prefix | | `13fc:14da` | `Fatal Error` | Fatal error class prefix | | `13fc:156a-1628` | File error messages | Not found, bad format, no memory, etc. | | `1760:665c` | `Copyright (C) 1986-93 Phar Lap Software, Inc.` | DOS extender copyright | | `1760:73da` | `-LDTSIZE 4096 -EXTHIGH D0_0000h -NI 18 -ISTKSIZE 3` | Default extender config | | `1760:76fc-7c5a` | Numbered error messages | System requirement errors (1000-2170) |