Turbo Pascal Toolchain, Part 2: Objects, Units, and Binary Investigation

Turbo Pascal Toolchain, Part 2: Objects, Units, and Binary Investigation

Part 1 covered workflow. Part 2 goes deeper into build artifacts and how to inspect them without mythology. If you worked in DOS long enough, you learned quickly that “it compiles” is not the same as “the binary is what we think it is.” Artifact literacy was a real engineering skill.

Artifact map: what each file means

A typical Turbo Pascal/Borland Pascal project produced or consumed:

  • .PAS - source units/programs
  • .TPU - compiled unit interface/implementation artifact
  • .OBJ - object module (from assembler or other compilers; sometimes Pascal-generated in specific flows)
  • .LIB - object library archives
  • .EXE / .COM - final executables
  • .MAP - linker symbol/address map (when enabled)
  • .OVR - overlay binary (when overlay model used)
  • .BGI / .CHR - graphics driver/font artifacts for Graph/BGI flows

Understanding this map is how you debug link failures and runtime mismatches without guesswork.

.TPU as contract and cache

.TPU files are more than “compiled units.” They encode interface contract + compiled implementation in a format tied to compiler version expectations. This is why stale or version-mismatched .TPU files can cause baffling errors.

Practical discipline:

  1. when in doubt, clean stale unit outputs
  2. rebuild dependent units after interface changes
  3. avoid mixing unit outputs across toolchain versions

If builds feel haunted, artifact hygiene is usually the first fix.

External object integration (.OBJ)

A classic use case is linking assembly routines for speed or hardware-specific behavior.

Assembler side (conceptual):

1
2
3
4
5
6
7
; FASTCPY.ASM
PUBLIC _FastCopy
_FastCopy PROC FAR
  ; ... optimized move logic ...
  ret
_FastCopy ENDP
END

Pascal side:

{$L FASTCPY.OBJ}
procedure FastCopy; external;

The important detail is calling convention and symbol naming alignment. When these disagree, you get link errors or silent runtime corruption.

Linker maps are underrated

MAP files answer practical questions quickly:

  • where did this symbol end up?
  • which module pulled this code in?
  • why did executable size jump?
  • does this code land in expected segment region?

A tiny excerpt-style interpretation:

1
2
3
 0001:03A0  _MainLoop
 0001:07C0  _DrawHud
 0002:0010  _FastCopy  (FASTCPY.OBJ)

This alone can validate whether your external object was actually linked and where.

Binary investigation workflow (non-destructive)

A disciplined sequence:

  1. keep original binary copy
  2. inspect headers and map artifacts first
  3. confirm symbol expectations
  4. only then disassemble/debug
  5. record findings with exact file hashes

DOS tooling varied across installations, but the method did not.

Practical checksum habit

Before and after any build-profile change, store checksum and size:

1
2
3
@echo off
dir APP.EXE
rem checksum utility name varies by environment

Even simple size deltas can catch accidental option drift or unexpected library pulls.

Common failure patterns and fast diagnoses

1) “Unresolved external”

Usually one of:

  • object not linked
  • symbol name mismatch
  • calling convention mismatch
  • wrong library order

2) Runtime stack corruption after external call

Usually:

  • caller/callee convention disagreement
  • far/near mismatch
  • parameter size mismatch

3) Executable bloat after “small change”

Usually:

  • option profile changed
  • debug info accidentally enabled
  • broad library module linked in due to one dependency

Link map comparison is the fastest truth source.

Investigating unit boundaries for architectural health

If a small edit triggers widespread rebuild/link churn, unit boundaries are probably too entangled. Artifact behavior becomes design feedback:

  • high churn => weak modular boundaries
  • stable local rebuilds => healthy contracts

This is one reason artifact literacy improves architecture quality.

Reverse-engineering your own binaries as QA

A useful practice in retro projects is occasional “self reverse-engineering”:

  1. build release
  2. inspect symbol map / binary layout
  3. verify critical routines present and callable paths as expected
  4. compare against previous known-good build

You catch packaging and linking mistakes earlier than functional testing alone.

Cross references

This artifact-focused view pairs well with:

Different domain, same principle: reproducible artifacts enable reliable reasoning.

Next part

Part 3 covers overlays and memory models in detail: how overlay objects worked operationally, where they helped, and where they introduced failure modes.

If Part 1 teaches workflow and Part 2 teaches artifact literacy, Part 3 teaches survivability under 640K-era constraints.

2026-02-22