Civilization Recomp

June 7, 2026 · View on GitHub

A static recompilation of Sid Meier's Civilization (1991) for modern Windows

"I've played a lot of Civilization in my time, I can tell you." — Literally everyone who has ever touched this game

Civilization title screen running natively via static recompilation

The recompiled "Sid Meier's CIVILIZATION" title, decoded pixel-perfect and running natively on Windows 11. The full intro (logo → birth → credits, in color) plays, then New Game drives world generation, the sprite-sheet decode, and the civilization-select screen.


What Is This?

This is a fan preservation project to bring the original Sid Meier's Civilization — the game that invented an entire genre, ruined millions of sleep schedules, and taught more people about the tech tree of human progress than any history class ever could — natively to Windows 11 via static recompilation.

No emulation. No DOSBox wrapper. Just pure, native, one-more-turn glory running on modern hardware exactly as Sid intended (minus the 640K memory limit and IRQ conflicts).

This is an unauthorized fan project. We are not affiliated with Firaxis Games, Take-Two Interactive, or MicroProse. We just think this game is a masterpiece that deserves to be preserved forever, turn after turn, civilization after civilization.

A Note About Sid Meier

Sid Meier is one of the greatest game designers who has ever lived. With Civilization (1991), he and Bruce Shelley created something genuinely transcendent — a game that is simultaneously a toy, a puzzle, a strategy engine, a history lesson, and one of the most addictive pieces of software ever compiled. Meier's design philosophy of "interesting decisions" produced a game where every single turn matters and every choice echoes across millennia. The fact that we're still talking about (and playing) this game over 30 years later speaks to the brilliance of its design.

The original Civilization was a product of MicroProse Software, the legendary studio co-founded by "Wild" Bill Stealey and Sid Meier. This project exists purely out of love and respect for their work, and a desire to ensure it remains playable forever.


Why Static Recompilation?

ApproachHow It WorksTrade-offs
DOSBoxEmulates entire x86 CPU + DOS + hardwareWorks but adds overhead, input lag, scaling artifacts
Source PortRewrite from scratch based on behaviorMassive effort, subtle differences from original
Static RecompTranslate original machine code to C, run nativelyPreserves exact original logic, runs at native speed

Static recompilation gives us the best of all worlds: the exact game logic Sid wrote, compiled fresh for modern x86-64, with a thin hardware abstraction layer replacing the DOS/VGA/AdLib interfaces with SDL2.


Binary Analysis

CIV.EXE — The Main Executable

================================================================
  Sid Meier's Civilization (1991) - Binary Analysis
  Compiled with Microsoft C 5.x (1988 Runtime)
================================================================

  File:              civ.exe (305,024 bytes / 297.9 KB)
  Architecture:      16-bit x86 real mode (DOS)
  Compiler:          Microsoft C 5.x (MSC 1988 Runtime Library)
  Overlay Manager:   Microsoft C INT 3Fh

  Resident Code:     ~174 KB (loaded at startup)
  Overlay Modules:   23 modules (~124 KB demand-loaded)
  Total Code:        ~298 KB

  Entry Point:       CS:IP = 2A10:0010
  Stack:             SS:SP = 3217:0080

Overlay Module Map

The game uses the Microsoft C Overlay Manager to fit ~298 KB of code into DOS memory constraints. Code is divided into a resident portion (always loaded) and 23 overlay modules that are demand-loaded via INT 3Fh when called:

  Overlay   File Offset   Size      Functions   Call Sites
  -------   -----------   --------  ---------   ----------
  OVL 01    0x02B800       1.5 KB       1            1
  OVL 02    0x02BE00       7.4 KB       4            4
  OVL 03    0x02DC00       3.6 KB       6            8
  OVL 04    0x02EC00       1.6 KB       4           18
  OVL 05    0x02F400       8.3 KB       7           14
  OVL 06    0x031600      10.4 KB       5           15
  OVL 07    0x034000       8.2 KB       3            4
  OVL 08    0x036200       7.0 KB       4           10
  OVL 09    0x037E00       6.3 KB       3            5
  OVL 10    0x039800       2.2 KB       2            2
  OVL 11    0x03A200       4.4 KB       2            3
  OVL 12    0x03B400       7.8 KB       6            9
  OVL 13    0x03D400       2.9 KB       2            2
  OVL 14    0x03E000       7.9 KB       8           16
  OVL 15    0x040000       2.9 KB       2            2
  OVL 16    0x040C00       1.8 KB       1            1
  OVL 17    0x041400       4.6 KB       2            3
  OVL 18    0x042800       6.7 KB       4            5
  OVL 19    0x044400       8.2 KB       4            9
  OVL 20    0x046600       6.0 KB       3            7
  OVL 21    0x048000       1.5 KB       1           18
  OVL 22    0x048800       5.3 KB       5            8
  OVL 23    0x049E00       2.4 KB       4            4
                           --------  ---------   ----------
  Total:                  ~124 KB      83 funcs    168 calls

DOS/BIOS Interface

  Interrupt   Service          Calls   Recompilation Target
  ---------   -------          -----   --------------------
  INT 21h     DOS API           120    Win32 API / C runtime
  INT 3Fh     MSC Overlay       168    Direct function calls (resolved at recomp time)
  INT 33h     Mouse driver        7    SDL2 mouse input
  INT 16h     Keyboard BIOS       4    SDL2 keyboard input
  INT 10h     Video BIOS          3    SDL2 / D3D11 rendering
  INT 09h     Keyboard HW IRQ     3    SDL2 event loop
  INT 08h     Timer IRQ            1    SDL2 timer / QueryPerformanceCounter

Support Executables

FileSizePurpose
egraphic.exe11,584 BEGA (16-color) graphics driver
mgraphic.exe7,142 BMCGA/VGA (256-color) graphics driver
tgraphic.exe9,990 BTandy (16-color) graphics driver
misc.exe980 BUtility/launcher stub

Game Data Files

TypeCountPurpose
.pic106Images (title, units, terrain, cities, diplomacy, wonders)
.pal38VGA/EGA palettes (256 or 16 color, 6-bit RGB)
.cvl4Sound data (AdLib/SB/Tandy/IBM speaker)
.txt10Game text (credits, help, intro, Civilopedia entries)
.cv1Font data
.map1Saved map data
.sve1Saved game state
.dta1Hall of Fame data

Architecture

┌─────────────────────────────────────────────────────┐
│                  Recompiled Game Code                │
│         (civ.exe → C, resident + 23 overlays)       │
├─────────────────────────────────────────────────────┤
│                  DOS Compatibility Layer             │
│    INT 21h → C runtime    Overlay mgr → direct call │
│    File I/O → stdio       Memory → malloc/heap      │
├───────────────┬───────────────┬──────────────────────┤
│   Video HAL   │   Audio HAL   │     Input HAL        │
│  VGA 320x200  │  AdLib OPL2   │  Mouse + Keyboard    │
│  256-color    │  PC Speaker   │  INT 33h/16h → SDL2  │
│  → SDL2/D3D11 │  → SDL2 Audio │                      │
├───────────────┴───────────────┴──────────────────────┤
│                     SDL2 / Win32                     │
├──────────────────────────────────────────────────────┤
│                    Windows 11 x64                    │
└──────────────────────────────────────────────────────┘

Project Structure

civ/
├── README.md                    # This file
├── CMakeLists.txt               # Root build configuration (CMake 3.20+)
├── civ.syms.toml                # Exported function symbol table
├── .gitignore                   # Excludes game files and build output
├── tools/                       # Reverse engineering & analysis tools
│   ├── CMakeLists.txt
│   ├── mzparse/                 # MZ header & overlay analyzer
│   ├── ovldump/                 # Overlay module extractor
│   ├── picdecode/               # .PIC/.PAL image format analyzer
│   └── recomp/                  # Static recompilation toolchain
│       ├── decode16.py          # 16-bit x86 instruction decoder
│       ├── analyze.py           # Function boundary & call graph analyzer
│       ├── lift.py              # x86-16 to C code lifter
│       ├── lift_from_dump.py    # EXEPACK dump lifter (decompressed code)
│       ├── recomp.py            # Main recompilation driver
│       ├── map_thunks.py        # Overlay thunk table decoder (EXEPACK + 7-byte entries)
│       ├── map_thunks2.py       # Thunk caller analysis & cross-overlay constraint solver
│       └── parse_overlays.py    # Overlay MZ header parser & function finder
├── include/                     # Public headers
│   ├── recomp/
│   │   ├── cpu.h                # CPU state struct (registers, flags, memory)
│   │   └── dos_compat.h         # DOS API compatibility layer
│   ├── hal/
│   │   ├── video.h              # VGA Mode 13h emulation
│   │   ├── input.h              # Keyboard & mouse HAL
│   │   └── timer.h              # PIT timer emulation
│   └── platform/
│       └── sdl_platform.h       # SDL2 platform layer
├── src/
│   ├── main.c                   # Entry point & main game loop
│   ├── recomp/
│   │   ├── cpu.c                # CPU state management
│   │   ├── dos_compat.c         # Full INT 21h/10h/16h/33h implementation
│   │   └── startup.c            # MSC crt0 replacement
│   ├── hal/
│   │   ├── video.c              # VGA DAC palette, mode 13h, vsync
│   │   ├── input.c              # Keyboard buffer, mouse state
│   │   └── timer.c              # PIT timer tick emulation
│   └── platform/
│       └── sdl_platform.c       # SDL2 window, rendering, input events
└── RecompiledFuncs/             # Auto-generated C output (gitignored)
    ├── civ_recomp.h             # Master header (482 function declarations)
    ├── civ_recomp_000..009.c    # Recompiled game code (132K lines)
    ├── civ_dump_lifted.c        # Functions lifted from EXEPACK dump (171 funcs)
    ├── civ_impl.c               # Hand-written implementations (tracked in git)
    ├── civ_stubs.c              # Stub functions for unresolved symbols (auto-generated)
    └── civ_aliases.c            # Overlay thunk aliases (auto-generated)

Building

Prerequisites

  • CMake 3.20+
  • MSVC (Visual Studio 2022) or MinGW-w64
  • Python 3 (for recompilation toolchain)
  • SDL2 via vcpkg: vcpkg install sdl2:x64-windows

Building

# Step 1: Run the recompiler (generates C files from CIV.EXE)
py -3 tools/recomp/recomp.py path/to/CIV.EXE RecompiledFuncs

# Step 2: Configure CMake with vcpkg
cmake -B build -G "Visual Studio 17 2022" -A x64 \
    -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake

# Step 3: Build
cmake --build build --config Release

Running

# Run from the game data directory
cd path/to/game/data
path/to/build/Release/civ.exe --gamedir . --scale 3

Running Analysis Tools

# Analyze the MZ executable structure
build/Release/mzparse.exe path/to/civ.exe

# Extract overlay modules
build/Release/ovldump.exe path/to/civ.exe output_dir/

# Analyze .PIC image files
build/Release/picdecode.exe path/to/logo.pic

Running the Recompiler

# Disassemble resident code
py -3 tools/recomp/decode16.py path/to/civ.exe --resident

# Disassemble overlay N
py -3 tools/recomp/decode16.py path/to/civ.exe --overlay 5

# Analyze function boundaries
py -3 tools/recomp/analyze.py path/to/civ.exe -symbols civ.syms.toml

# Full recompilation (generates C files)
py -3 tools/recomp/recomp.py path/to/civ.exe RecompiledFuncs

Progress

Phase 0 — Binary Analysis & Documentation

  • MZ header parsing and structure analysis
  • Microsoft C overlay manager identification (INT 3Fh)
  • Overlay module enumeration (23 modules, 83 functions)
  • DOS interrupt surface mapping
  • String table extraction (1,807 strings)
  • Support executable identification
  • Game data file inventory
  • .PIC image format reverse engineering
  • .CVL sound format reverse engineering
  • .CV font format analysis

Phase 1 — Recompilation Toolchain

  • 16-bit x86 instruction decoder (full 8086/80186 opcode coverage)
  • Function boundary detection (MSC 5.x prologue/epilogue patterns)
  • Control flow analysis and call graph extraction
  • x86-16 to C lifter (CPU state struct approach)
  • MSC overlay call resolution (INT 3Fh -> direct C function calls)
  • Segment:offset -> flat memory translation
  • Port I/O lifting (IN/OUT -> port_in8/port_out8 dispatch)
  • Batch compilation output (split across .c files)
  • Symbol table export (TOML format)
  • Stub generation for unresolved symbols
  • EXEPACK dump lifter (lift_from_dump.py) — lifts functions from decompressed memory dump

Recompilation Results:

  Functions:     482 resident/overlay + 190 dump-lifted = 672 total
  Instructions:  106,935+ (resident/overlay)
  Code bytes:    280,991 (274.4 KB) resident/overlay + dump-lifted code
  Output:        ~150K lines of C across 10 recomp files + dump_lifted + impl + stubs
  Errors:        0

Phase 2 — DOS Compatibility Layer

  • INT 21h replacement (file I/O: create/open/close/read/write/seek/delete)
  • INT 21h memory management (alloc/free/resize paragraphs)
  • INT 21h console I/O (char in/out, print string, input status)
  • INT 21h system calls (date/time, DOS version, drive/directory, interrupt vectors)
  • INT 10h Video BIOS (set mode, cursor, teletype, get mode)
  • INT 16h Keyboard BIOS (read key, check key, shift flags)
  • INT 33h Mouse Driver (reset, show/hide, position, range, handler)
  • VGA Mode 13h framebuffer (320x200, 256 colors at A000:0000)
  • VGA DAC palette emulation (ports 3C7/3C8/3C9 state machine)
  • VGA input status register (port 3DA, vsync toggle)
  • PIT timer emulation (ports 40h/43h, 18.2 Hz tick rate)
  • Port I/O dispatch (VGA, PIT, PIC, keyboard ports)
  • DOS path translation (game directory mapping)
  • SDL2 platform layer (window, renderer, streaming texture)
  • SDL2 event handling (keyboard scancode map, mouse, fullscreen toggle)
  • SDL2 VGA rendering (indexed framebuffer -> RGBA palette conversion)
  • MSC crt0 startup replacement (segment register initialization + data copy)
  • Main entry point with frame-driven game loop
  • Full project compiles and links (zero errors, zero warnings)

Phase 3 — Stub Resolution & Call Graph

  • Traced MSC crt0 startup chain: res_02A310 -> ovl05_031396 (C main) -> ovl21_048200 (game loop)
  • Discovered CIV.EXE has zero MZ relocations — MSC overlay manager patches segments at runtime
  • Reverse-engineered segment relocation formula: file_off = seg*16 + off - 0x14
  • Far call resolution (CALL FAR seg:off -> known function): 1081+ calls resolved
  • Stubs reduced from 553 -> 352 (36% reduction)
  • Stub resolver tool (resolve_stubs.py) — maps unresolved names to existing functions
  • Alias file (civ_aliases.c) — 79 wrapper functions for overlay/resident name mismatches

Phase 4 — Call Stack & Runtime Fixes

  • Call stack return address simulation (NEAR: push 0 + sp+=2, FAR: push CS + push 0 + sp+=4)
  • INT 10h rewrite (text mode cursor, mode get/set, character output)
  • Text mode VGA rendering (0xB8000 write-through, 80x25 cell display)
  • VGA mode detection (port 3DA status register emulation)
  • BIOS data area setup (cursor position at 0040:0050, tick counter at 0040:006C)

Phase 5 — EXEPACK & Overlay Runtime

  • EXEPACK decompression implemented (both in C runtime and Python analysis tools)
  • Segment relocation applied after decompression (LOAD_SEG = 0x0100)
  • DGROUP/BSS/stack initialization matching original crt0
  • INT 3F inline overlay calls scanned (148 found in resident code)
  • Title screen bypass (ovl02_02C200) -> "New Game" path
  • Timer read (far_0000_0A40) -> BIOS tick counter from wall clock
  • MSC 5.x heap allocator rewritten (correct free-list walking)
  • Game reads king.txt successfully (4 fopen/fread/fclose cycles)
  • Overlay thunk table structure decoded: 7-byte entries at offset 0x0761
  • Thunk dual-mode design: JMP FAR (bytes 0-4) + CALL NEAR dispatcher (byte +5)
  • Overlay function discovery: 155 functions across 23 modules (prologue scan + INT 3F)
  • Cross-overlay caller constraint analysis for thunk mapping
  • Fixed far_0000_07DF infinite recursion (corrected alias to ovl05_0307DA)
  • LOMEM false alarm fixed (ovl02_02C200 returns AX=0x6000 for memory check)
  • VGA mode 13h activation working
  • Complete thunk-to-overlay-function mapping (64 active EA entries -> 155 overlay functions)
  • Remaining stub resolution

Phase 6 — World Generation & Game Handler

  • Terrain seed placement (ovl07_034D88) working
  • Terrain growth scan loop working
  • Jump Table 1 (terrain type assignment, 8 climate cases) reconstructed from binary
  • Jump Table 2 (terrain feature placement, 14 cases) reconstructed from binary
  • Continent placement (ovl07_034A5C) working — finds ocean tiles correctly
  • Terrain detail pass (ovl07_034E33) working
  • World generation animation skipped (ovl07_035B6E bypass)
  • Game handler reached (res_0022DA) — main game loop active
  • Keyboard input polling (KBHIT via SDL2)
  • Timer speed multiplier (20x) for faster animation playback

Phase 7 — EXEPACK Code Lifting & CRT

  • Decompressed memory dump tool (startup.c dumps civ_decompressed.bin at runtime)
  • lift_from_dump.py — lifts functions from decompressed dump for EXEPACK-compressed code
  • 175 functions lifted from decompressed dump (display, map, CRT, game state, rendering)
  • MSC 5.x CRT functions lifted (39 functions: _open, _read, _write, _lseek, file table mgmt)
  • Display subsystem lifted (far_1DDE_* — 15 rendering/GFX context functions)
  • Map display functions lifted (far_1B05_* — 25 map rendering functions)
  • Game state functions lifted (far_15D8_* — 12 functions including main game state)
  • Blit function implemented (far_0000_07ED — 8-arg rectangle copy between GFX pages)
  • Climate/temperature stub (far_205A_2AC0 returns uniform climate for now)
  • File access working (game opens and checks .pic, .cv, .exe files)
  • First pixels rendered (nonzero framebuffer pixels detected in game loop)
  • Near function lifting support added to lift_from_dump.py (NEAR_STUBS with is_far=False)
  • Near-far alias pattern for conflicting functions (same code at same offset, different calling conventions)
  • Resident rendering functions lifted: res_01C813, res_01C605, res_01D221 (NEAR)
  • Pipeline improvements: recomp.py excludes dump-lifted funcs from stubs without affecting recomp output
  • Thunk table deep analysis: 7-byte entries, overlay manager init at 0x0B60, descriptor patching
  • World gen hang fixed — 6 bugs in ovl07_034412: 4 missing gotos, 2 broken jump tables
  • Near-far alias pattern for conflicting functions

Phase 8 — World Gen Fix & Thunk Resolution

  • World generation completes successfully (30-60 seconds depending on random seed)
  • Fixed 4 missing goto statements (lifter converted jmp to comments)
  • Fixed 2 broken indirect jump tables (CS:0x272 and CS:0x518)
  • Removed ~20 lines of garbage code (jump table data decoded as x86 instructions)
  • Created patch_worldgen.py for automatic post-recomp patching
  • Auto-inject Space key to advance past "press any key" screens

Phase 9 — Thunk Resolution & Display

  • Resolved 7 thunk table entries to overlay functions
  • Lifted 16+ new functions from dump (display, CRT file I/O)
  • Implemented _aFchkstk (far stack check) and buffer write subroutines
  • Minimap rendering with page-flip animation working (10 BLIT operations)
  • DELAY timing function active — game animates between frames

Phase 10 — File I/O Pipeline

  • Hand-implemented fopen/fclose with MSC FILE struct setup
  • Lifted file buffer fill function (far_1FB6_0642)
  • Fixed 10 UNHANDLED indirect calls in PIC decoder (call far [DS:E84A])
  • Fixed _dos_read to handle FILE* as well as raw DOS handles
  • Initialized CRT _lastiob and stack limit in startup
  • PIC file loading working — sp299.pic and planet2.pic read successfully (512B chunks)
  • 34 file open operations, 32 close operations in a single run
  • Game progresses past civilization selection into new game setup
  • FILE struct corruption fixed — moved FILE state to a host-side table, fopen returns an opaque token (0xF200 | slot*8) instead of an in-DS FILE struct

Phase 12 — Overlay Thunk Bugs & Terrain Reveal

  • Fixed the civ-select infinite recursion — far_0000_076F was wrongly aliased to its own caller ovl02_02CDD7 (the dialog), re-entering the whole dialog 24×/cell and exhausting the stack. It's a screen-grab→sprite-handle primitive; hand-implemented. planet2.pic opens dropped 32 → 0.
  • Reverse-engineered the overlay thunk-binding mechanism — the 0x0761 vectors are runtime-patched from per-overlay descriptors (0x0B62 via DOS 4B03), so the naive "overlay functions in link order" alias map is unreliable; suspect thunks must be verified by contract.
  • Un-stubbed ovl07_035B6E (terrain-reveal state machine) via the project lifter — fixed an infinite spin (CPU 98% → ~8%); the game now advances past the reveal into world-gen map writes.

Phase 13 — Rendering On Screen

  • Diagnosed the black-screen pipeline (mode 13h OK; platform_render correct)
  • Implemented the string renderer far_0000_07F4 (was mis-aliased to ovl05_0307DA$) \text{as} \text{an} 8 \times 8 \text{CP437} \text{text} \text{blit} — **\text{real} \text{game} \text{text} \text{now} \text{renders} \text{on} \text{screen}** (\text{verified}: $_kills: NONE in cyan via PrintWindow)
  • Capture note: GDI BitBlt shows SDL's GPU window as black; use PrintWindow(PW_RENDERFULLCONTENT) or the env-gated CIV_RENDERDIAG fb dump
  • Map tile / sprite blitter far_0000_083F (stub; reached only past the menu)
  • DAC palette upload (text shows via the default palette for now)

Phase 14 — Boot Flow & MSC CRT Text I/O

  • Found the real boot flow (logos/title/menu) was bypassed, not missing — un-bypassing ovl02_02C200 runs the real intro (loads logo.pic, birth0/1.pic, credits.txt)
  • Implemented the MSC 5.x CRT text-stream chain (getc res_021BC8, ungetc far_215A_16DA, width-check res_021C22, skip-ws res_021BEC, scanf field reader res_02178A) against the host FILE table — the 79M-call credits-parse spin is gone; credits.txt now parses
  • Fixed the bad-filename pointer bug — stale civ_dump_lifted.c double-pushed CS for the MSC push cs; call near <far-func> idiom, so far_1F67_01AD read cs instead of the filename; king.txt now opens cleanly
  • Implemented res_02120A (MSC _getbuf) so king.txt's 512-byte FILE buffer is allocated and the getc chain reads — the intro completes

Phase 15 — Color Rendering Pipeline (PIC → A0000 → palette)

  • PIC LZW pixel decoder un-stubbed — far_0000_11FA delegated decode to stubs; correct implementations existed under the overlay-lifted twin names (res_001284/0012F6/00124E/001205); wired the delegations
  • Fixed PIC display routing — far_0000_07E6 (row blitter) was mis-aliased to ovl05_02FFC2; hand-implemented as a proper DS:src → A0000 row copy
  • VGA palette loaded — Civ PICs are LBM-style chunked; the M0 (0x304D) chunk is the 768-byte 6-bit RGB palette. Parse it at PIC open and upload to the DAC → the intro renders in full colour (starfield, stars, red credits)
  • MCGA mode 13h presents A0000 (SDL keys off mem[0x449]==0x13)
  • Screen-grab sprite pair (far_0000_076F/083F host sprite store; title menu save/restore works)

Phase 16 — Real Main Menu, World-Gen Completion & Game Loop

  • LOMEM false alarm — real root cause fixed. far_0000_0768 (the free-memory query that gates the *LOMEM warning in res_001A66) was mis-aliased to the title screen ovl02_02C200. That hack only worked while the title was stubbed (returned AX=0x6000); with the title un-bypassed it returned a small AX → an empty *LOMEM dialog blocked startup. Fixed to report ample memory → the real main menu now builds ("Start a New Game / Load / EARTH / Customize World / View Hall of Fame")
  • New Game selection works — the menu string is king.txt message-DB text (far_1F67_01AD hash lookup → 0xC936); selecting New Game sets 6AC2=0
  • Terrain-reveal infinite spin fixed → world-gen completes. far_1DDE_007C (== res_01DE5C, a signed clamp(v,lo,hi)) was a wrong "delay returns arg3" stub → set the reveal timer target to 0x7FFF (~30 min) so ovl07_035B6E's reveal loop spun forever. Implemented the real clamp → world-gen finishes
  • The game's main loop runs — begin_frame/yield/timer/input cycling, mode 13h, palette 210/224. Full flow works end-to-end: intro → menu → New Game → world-gen → game loop
  • Map tile rendering — the in-game map is a solid fill; far_0000_083F (tile/ sprite blitter, ~64 callers) is still a stub
  • A Unicorn-engine harness (tools/recomp/uni_civ.py, modeled on the bolo recomp's uni_* tools) boots the original CIV.EXE headless — through EXEPACK, the MSC crt0, the DOS memory manager, the INT 3Fh overlay manager and graphics- driver loads — all the way to the rendered title screen, for ground-truth observation (see docs/UNI_HARNESS.md)

Phase 17 — Sprite-Sheet Decode & Civilization Select

  • sp299.pic sprite-sheet LZW decode fixed. The post-New-Game sprite sheet corrupted DS:0x686C (the refill file token) → a read handle 0 spin. Two bugs in the core LZW byte decoder (res_0012F6), found by disassembling the original (@dump 0x12F6, capstone): the KwKwK case walked the not-yet-defined dict entry (dx) instead of the previous code ([0x688A]) → a self-referential dictionary chain whose unbounded walk overran the decode stack (base 0x6A8D, grows down) into the state region and 0x686C; and the prev_code save wrote code instead of cx. Both fixed → the decode terminates cleanly
  • Civilization / difficulty-select screen reached. With the decode fixed, the game grabs the 41×58 leader-portrait sprites from the sheet, loads arch.pic (throne room) + back0a.pal/sp256.pal, and builds the *ARCH select screen
  • PIC LZW decoder now pixel-perfect on large images. A third decoder bug surfaced via the title logo.pic (clean top, garbage lower half): the dictionary-full reset wrongly called the full re-init (0x124E, which reads a fresh stream word + reseeds the bit buffer) instead of the dict-only reset (0x1262). That consumed an extra stream word mid-decode and desynced the bitstream after the first dictionary overflow — corrupting every image large enough to fill the 0x800-entry dictionary (logo / birth / title / arch). Fixed → logo.pic decodes the full "Sid Meier's CIVILIZATION" wordmark cleanly
  • Deterministic New-Game repro via CIV_KEYSCRIPT=<chars> (scripted getkey/ kbhit) — CIV_KEYSCRIPT=N reliably drives New Game → world-gen → sprite decode
  • Drive the civ-select screen → the real in-game loop (res_0023F0)
  • Map tile rendering (far_0000_083F)

Phase 17.5 — Gameplay

  • Title screen + main menu (New Game / Load / Earth / Custom)
  • Map tile rendering
  • UI chrome rendering
  • City management / diplomacy / combat / tech tree / wonders
  • Save/load game state
  • Hall of Fame

Phase 18 — Audio & Polish

  • .CVL sound data loader
  • SDL2 audio output (AdLib OPL2 synthesis or PCM playback)
  • Integer scaling (1x, 2x, 3x, 4x)
  • Windowed / fullscreen toggle
  • Modern input improvements (scroll wheel for zoom, etc.)
  • Windows 11 installer / portable build

Game Data — User Supplied

You must own a legitimate copy of Civilization. Place the game files in a game/ subdirectory (gitignored). The recompiled executable loads data from this directory at runtime.

Required files:

game/
├── civ.exe          # Original executable (for reference/verification)
├── *.pic            # All image files
├── *.pal            # All palette files
├── *.cvl            # Sound data
├── fonts.cv         # Font data
├── *.txt            # Game text files
├── fame.dta         # Hall of Fame
└── civil0.map       # Default map

Technical Notes

Why Microsoft C 5.x?

MicroProse was a Microsoft C shop in the late 80s/early 90s. The runtime signature MS Run-Time Library - Copyright (c) 1988, Microsoft Corp confirms MSC 5.x, which was the standard professional C compiler for DOS development at the time. This is consistent with other MicroProse titles of the era.

The Overlay Manager

With only 640 KB of conventional memory, a ~298 KB game needed help. Microsoft C's overlay manager uses INT 3Fh as a software interrupt to demand-load code segments from disk. When the resident code calls an overlaid function, the overlay manager:

  1. Intercepts the INT 3Fh call
  2. Reads the overlay number and offset from the instruction stream
  3. Loads the overlay module from the EXE file into memory
  4. Transfers control to the target function

For recompilation, this is actually a gift — every INT 3Fh call site is an explicit inter-module function call with a known target. We resolve these statically at recomp time into direct C function calls, eliminating the overlay manager entirely.

The Graphics Drivers

Civilization ships with separate executables for different graphics hardware:

  • egraphic.exe — EGA (640x350, 16 colors or 320x200, 16 colors)
  • mgraphic.exe — MCGA/VGA (320x200, 256 colors)
  • tgraphic.exe — Tandy 1000 (320x200, 16 colors)

The main civ.exe calls out to these as external processes for graphics initialization and rendering. For the recomp, we implement the VGA (256-color) path directly.


Credits

Original Game

  • Designed by Sid Meier with Bruce Shelley
  • Programming by Sid Meier
  • Computer Graphics by Larry Coones, Michael Haire, Harry Teasley, Barbara Bents, Todd Brizzi, Erroll Roberts, Chris Soares, Nicholas Rusko-Berger, Stacey Clark, Brian Martel
  • Original Music by Jeffery L. Briggs
  • Quality Assurance by Al Roireau, Jerry Shaffirio, Mike Corcoran, Tim Train, Michael Rea, Chris Clark, Michael Craighead, Nick Yuran, Paul Murphy
  • Published by MicroProse Software (1991)

This Project

A sp00nznet fan preservation project. Built with love, respect, and far too many turns.


This is an educational and preservation project. No original game code or assets are distributed in this repository. Users must supply their own legally obtained copy of Sid Meier's Civilization.

Civilization is a trademark of Take-Two Interactive Software, Inc. / Firaxis Games. This project is not affiliated with, endorsed by, or connected to Take-Two Interactive, Firaxis Games, or the estate of MicroProse Software in any way.

"Just one more turn..."