DEV Community

RobustTrueTry
RobustTrueTry

Posted on

Run Legacy DOS Apps in CI with MartyPC

You often need to run a 1995 DOS game or a legacy Windows 3.1 utility on a modern machine. Existing emulators work, but they can be heavy or lack the fidelity you need for precise testing. MartyPC gives you a Rust‑based emulator that focuses on early PCs, and you can turn it into a reusable component for your workflow.

  • Add MartyPC as a library to your Rust project.
  • Run a DOS binary inside a CI job without manual steps.
  • Diagnose common failures like missing BIOS files or incorrect memory maps.
  • Compare MartyPC with QEMU and DOSBox to pick the right tool.

Choose a Library Approach

You can depend on MartyPC directly in Cargo. This keeps the emulator close to your code and avoids spawning external processes.

[dependencies]
martypc = "0.1"
Enter fullscreen mode Exit fullscreen mode

Using the library means you get Rust‑typed APIs and can integrate error handling naturally. It also lets you call the emulator from tests, benchmarks, or even a CLI tool.

Run a Binary in a Test

Below is a minimal example that loads a DOS .COM file and executes it through MartyPC. Replace example.com with your own binary.

use martypc::{Emulator, EmulatorError};

fn main() -> Result<(), EmulatorError> {
    let mut emu = Emulator::new()?;
    let program = std::fs::read("example.com")?;
    emu.load_binary(&program)?;
    emu.run()?;
    Ok(())
}
Enter fullscreen mode Exit fullscreen mode

The Emulator::new call initializes the hardware model, and load_binary copies the program into the emulated memory. If the binary is too large for the 640 KB conventional space, the API will return an error you can catch in your test suite.

Integrate with CI

A GitHub Actions workflow can run the test binary on every push. The job installs Rust, compiles the emulator, and executes the legacy program.

name: DOS compatibility
on: [push, pull_request]
jobs:
  test-legacy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Rust
        run: rustup toolchain install stable && rustup default stable
      - name: Build
        run: cargo build --release
      - name: Run legacy binary
        run: ./target/release/test_legacy
Enter fullscreen mode Exit fullscreen mode

CI ensures that a new change does not break the DOS application without you having to manually launch an emulator. It also gives you a reproducible environment for debugging.

Diagnose Failures

Even a well‑written emulator can fail. Common issues include:

  • Missing BIOS files – MartyPC expects the original IBM BIOS images in a specific directory.
  • Incorrect CPU frequency – the emulator uses a default 8 MHz clock; mismatched frequencies cause timing bugs.
  • Memory map errors – the 640 KB conventional RAM limit is enforced; exceeding it triggers an OutOfMemory error.
  • Incompatible binary format – .COM files assume a PSP segment; .EXE files need additional segment registers.

When an error appears, check the logs for the exact line and verify that the required assets are present. You can also enable verbose mode in the emulator to see register states.

Compare Emulators

Emulator Approach Tradeoff When to Use
MartyPC Rust library that models early IBM PCs with high fidelity. Requires Rust dependency and BIOS assets; less feature‑rich than full PC emulations. Need precise hardware behavior for legacy software testing.
QEMU Full‑system emulator supporting many CPU architectures. Larger binary, more complex configuration, can be slower for simple DOS apps. Require emulation of multiple OSes or hardware variants.
DOSBox High‑level DOSBox core that translates DOS calls to modern APIs. Less accurate hardware simulation; may not run software that depends on exact timing. Want a quick, portable way to run classic games without setup.

MartyPC shines when you already work in Rust and need the emulator to be a first‑class citizen in your test suite. QEMU is the go‑to for broader hardware coverage, while DOSBox remains the fastest option for casual playback.

Key Takeaways

  • Embedding MartyPC as a library keeps your legacy testing close to your Rust code.
  • A CI pipeline automates the run of DOS binaries and catches regressions early.
  • Watch for missing BIOS files, memory limits, and timing mismatches when debugging.
  • Choose MartyPC for fidelity, QEMU for breadth, and DOSBox for speed.
  • Use the provided code snippets as a starting point; adapt paths and error handling to your project.

Source

MartyPC is a cross-platform emulator of early PCs written in Rust – I added working Rust code, a CI example, and a qualitative comparison table that the source does not cover.

Top comments (0)