# Running the DOS tools under DOSBox — headless, at maximum speed The TI assembler, linker and compiler shipped with this SDK are DOS programs. This note records how to run them **without a window** and **as fast as DOSBox can go**, so they can be used from build scripts and batch runs. Verified 2026-10-18 against **DOSBox 0.74-3** (Debian package) on x86-64 Linux. All timings below are from that machine; the ratios matter, not the absolute numbers. ## 1. There is no `-nogui` / `-headless` flag DOSBox 0.74-3 has no headless option in the binary or in `man dosbox`. `-help` is not implemented either — it boots the emulator. Do not go looking for one. Windowless operation comes from SDL's **dummy** drivers instead. DOSBox then runs the emulation with no window and no audio device: ```sh SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \ dosbox -exit \ -conf harness/sdk-scratch/dosbox.conf \ -c "mount c harness/sdk-scratch/work" \ -c "c:" \ -c "GSPA.EXE MYFILE.ASM" \ -c exit ``` (`GSPA.EXE` is the SDK driver in `harness/sdk-scratch/work`; substitute your tool. Always keep `-c exit` last — see gotcha 2.) What was verified: * Exit status **0**, total wall time ≈ **1 s** including emulator start-up. * A mounted host directory really receives files the DOS program writes. * `-c` accepts several commands; they execute in order. * Redirection works: `dir > listing.txt` and `echo hi > out.txt` both produce files inside the mounted drive. Gotchas, all of which cost time during bring-up: 1. **The DOS console is invisible.** Shell output goes to the emulated video buffer, not to stdout. Redirect to a file when you need a transcript. The host terminal shows only DOSBox's own start-up lines. 2. **`-exit` alone does not quit** when no program file was given on the command line. End the command list with `-c exit` (or wrap the run in `timeout`). 3. **DOS uppercases 8.3 names.** `mkdir foo` creates `FOO`; `echo hi > out.txt` creates `OUT.TXT`. Checking for the lowercase name makes a successful run look like a failure. This produced a false "the commands did not run" conclusion before the uppercase names were noticed. 4. **Dummy video means no display access.** Do not use this path for anything that needs a screenshot, video recording, or visible screen output. ## 2. Maximum emulation speed The working config `harness/sdk-scratch/dosbox.conf` pins a fixed cycle count (`cycles=3000`, `core=normal`), which is slow for batch work. (The stock default is `cycles=auto`, which guesses; for a batch tool that is neither predictable nor as fast as `max`.) Maximum speed is two settings: ```ini [cpu] core=dynamic cputype=auto cycles=max ``` The comments in DOSBox's default config (`~/.dosbox/dosbox-0.74-3.conf`) document each one: * `core`: "auto will switch to dynamic if available and appropriate." The dynamic core is the recompiler. * `cputype`: "auto is the fastest choice." * `cycles`: "'max' will allocate as much cycles as your computer is able to handle." Two ways to apply it. **With a config file.** Pass an override after the base config; **later `-conf` files win** (verified): ```sh dosbox -conf harness/sdk-scratch/dosbox.conf -conf maxspeed.conf ... ``` **Without a file.** DOSBox 0.74-3 has an internal `CYCLES` command, so `-c` works too (verified — it moves the benchmark below from the fixed-3000 time to the max time): ```sh dosbox -conf harness/sdk-scratch/dosbox.conf -c "cycles max" ... ``` Use the config file for repeatable scripted runs; `-c "cycles max"` is fine for one-offs. Other useful batch settings: ```ini [mixer] nosound=true [midi] mpu401=none mididevice=none ``` `nosound=true` alone still makes DOSBox probe ALSA for MIDI (`ALSA:Can't subscribe to MIDI port ...`, `MIDI:Opened device:none`); the two `[midi]` keys stop that. ## 3. Measurements Benchmark: a `.COM` with a nested loop (`mov dx,0x100` / `mov cx,0xffff` / `loop $` / `dec dx` / `jnz` / `int 20h`), ≈16.8 M instructions. Each setting was run twice; wall time includes ≈0.93 s of DOSBox start-up (measured with an empty `int 20h` program, 0.95 s / 0.91 s). | setting | run 1 | run 2 | loop time ≈ | |---|---|---|---| | `cycles=fixed 3000`, `core=normal` (the scratch config) | 6.61 s | 6.49 s | 5.6 s | | `cycles=max`, `core=normal` | 2.00 s | 2.11 s | 1.1 s | | `cycles=max`, `core=dynamic` | 1.75 s | 1.72 s | 0.8 s | | `cycles=max`, `core=dynamic`, `frameskip=10` | 1.69 s | 1.73 s | 0.8 s | * Max + dynamic is about **3.8× faster end to end**, and about **7× faster on the loop itself**, than the fixed-3000 config. * The fixed-3000 run retires ≈3.0 M instructions/s, which matches the config's "instructions ... each millisecond": the benchmark is behaving as expected. * `frameskip=10` made **no measurable difference** with dummy video. Rendering is not the bottleneck here; do not expect a gain from it. * `core=normal` at `cycles=max` is close on this pure-loop benchmark (2.0 s vs 1.7 s), but the dynamic core is the right choice for real code. ## 4. Caveats * `cycles=max` consumes all the host CPU it can. Run one instance at a time; do not parallelise batch jobs by launching several `cycles=max` DOSBoxes unless you measure that it helps. * Max speed is not cycle-accurate. Fix the cycle count when timing matters (for example when measuring 34010 code under the simulator), and use `cycles=max` only when throughput matters, as it does for assembly/linking. * `ALT-F12` ("Unlock speed (turbo button)") and `CTRL-F11`/`CTRL-F12` change speed interactively, per `man dosbox`; they are useless in a headless run, which is why the settings above are config/`-c` based.