Reader

Blog

Welcome to the Blog.

TP Toolchain 4: BGI Integration

Driver packaging, linked vs dynamic assets, and rendering diagnostics

Turbo Pascal graphics was never just “call Graph and draw.” In production-ish DOS projects, graphics was an asset pipeline problem, a deployment problem, and a diagnostics problem at least as much as an API problem.

This part focuses on BGI driver mechanics, practical packaging, and the exact checks that separate real faults from folklore.

Structure map: BGI architecture and operational models → Graph unit runtime contracts and GraphResult handling → dynamic vs linked drivers, packaging and pitfalls → font/driver matrix and memory interactions → BGI artifacts in build and deploy pipelines → debugging rendering failures on real DOS → team checklists and release hardening.

BGI architecture in practical terms

The Graph unit provides the API. Under it, runtime driver/font assets do the hardware-specific work. The unit itself is statically linked; it does not contain adapter-specific code. Instead, it loads a driver binary that knows how to program the hardware (CGA, EGA, VGA, Hercules, etc.) and interpret high-level drawing calls. Fonts are separate assets because they are large and optional — you only load the ones your UI needs.

Two operational models exist: ... continue

TP Toolchain 3: Overlays

FAR calls, memory models, and real-mode link strategy under pressure

This article is rewritten to be explicitly source-grounded against the Turbo Pascal 5.0 Reference Guide (1989), Chapter 13 (“Overlays”) plus Appendix B directive entries.

Structure map. 1) Why overlays existed—mechanism, DOS memory pressure, design tradeoffs. 2) TP5 hard rules and directive semantics. 3) FAR/near call model and memory implications. 4) Build and link strategy for overlaid programs. 5) Runtime initialization: OvrInit, OvrInitEMS, OvrSetBuf usage and diagnostics. 6) Overlay buffer economics and memory budget math. 7) Failure triage and performance profiling mindset. 8) Migration from non-overlay projects. 9) Engineering checklist and boundary caveats.

Version note. This article is grounded in the TP5 Reference Guide. Borland Pascal 7 and later overlay implementations may differ in details (e.g. EMS handling, buffer API). The core rules—{$O+}, FAR chain, compile-to-disk, init-before-use—tend to hold across versions, but when in doubt, consult the manual for your specific toolchain. TP6/TP7 improvements are beyond the scope of this piece; the TP5 baseline remains the most widely documented and forms a stable reference.

Why overlays existed

In TP5 real-mode DOS workflows, overlays are a memory-management strategy: keep non-hot code out of always-resident memory and load it on demand. Conventional memory in DOS is capped at roughly 640 KB; TSRs, drivers, and stack/heap shrink the usable space. A large application can easily exceed that budget if all code is resident.

Mechanism. The overlay manager maintains a buffer in conventional memory. Overlaid routines live in a separate .OVR file on disk. On first call into an overlaid routine, the manager loads the appropriate block into the buffer and transfers control. Subsequent calls to already-loaded overlays execute in-place; no disk access. When the buffer fills and a new overlay must load, the manager discards inactive overlays first (least-recently-used policy), then loads the requested block. ... continue

TP Toolchain 2: Binary Forensics

TPU, OBJ, MAP, and EXE artifacts read like evidence

Part 1 covered workflow. Part 2 goes where practical debugging starts: the actual artifacts on disk. In Turbo Pascal, build failures and runtime bugs are often solved faster by reading files and link maps than by re-reading source. The tools are simple—TDUMP, MAP files, strings, hex diffs—but used systematically they turn “it used to work” into “here is exactly what changed.”

Structure map. This article proceeds in eleven sections: (1) artifact catalog and operational meaning, (2) TP5 unit-resolution behavior, (3) TPU constraints and version coupling, (4) TPU differential forensics and reconstruction when source is missing, (5) OBJ/LIB forensics and OMF orientation, (6) MAP file workflow and TDUMP-style inspection loops, (7) EXE-level checks before deep disassembly, (8) external OBJ integration and calling-convention cautions, (9) repeatable troubleshooting matrix with high-signal checks, (10) manipulating artifacts safely and team discipline for reproducibility, and (11) unit libraries and cross references.

Artifact catalog with operational meaning

Typical TP/BP project artifacts:

This list is not trivia. It is your debugging map. OVR files are loaded at runtime when overlay code executes; if the OVR path is wrong or the file is missing, the program may hang or crash on overlay entry rather than at startup. BGI and CHR are resolved by path at runtime—Graph unit InitGraph searches the driver path. Capture these paths in your environment documentation; “works here, fails there” often traces to BGI/OVR path differences.

Tool availability. TDUMP ships with Borland toolchains; if missing, omfdump (from the OMFutils project) or objdump with appropriate flags can suffice for OBJ/LIB inspection, though output format differs. On modern systems, strings and hexdump are standard. The workflows described here assume TDUMP is available; adapt commands if using substitutes. ... continue

TP Toolchain 1: The Full Stack

Editor, compiler, linker, debugger, units, and artifact flow

Turbo Pascal is remembered for a fast blue IDE, but that is only the surface. The real strength was a full toolchain with tight feedback loops: editor, compiler, linker, debugger, units, and predictable artifacts. Part 1 maps that system in practical terms before we dive into binary formats, overlays, BGI, and ABI-level language details.

Structure map. This article proceeds in twelve sections: (1) version and scope boundaries, (2) toolchain topology and component wiring, (3) artifact pipeline and engineering signal, (4) IDE options as architecture, (5) directory and path policy, (6) practical project layout, (7) IDE–CLI parity and reproducible builds, (8) units as compile boundaries and incremental strategy, (9) debug loop mechanics and map/debug workflow, (10) external objects and integration discipline, (11) operational checklists and failure modes, and (12) how this foundation supports the rest of the series.

Scope and version boundaries

When discussing “latest Turbo Pascal,” engineers usually mean Turbo Pascal 7.0 and, in many setups, Borland Pascal 7 tooling around it. Some executable names and switches vary by package and installation, so this article uses two rules:

That keeps the discussion accurate without pretending all distributions are identical. TP 5.x used a simpler unit format; TP 6 and 7 extended it with object-oriented support and richer metadata. Projects that must support both TP 5 and TP 7 need to avoid OOP extensions and test on both toolchains.

Technical mechanism. TP 7 and BP 7 share the same core compiler engine but differ in packaging: TURBO.EXE (IDE) vs BP.EXE (Borland Pascal IDE), and command-line variants such as TPC.EXE or BPC.EXE. The compiler emits .TPU (Turbo Pascal Unit) files or .OBJ for linkable object code; TP 5.x and TP 6.x used similar conventions with minor format changes. Knowing your actual binary set (dir *.exe in the TP install directory) prevents configuration mistakes. ... continue

Timer Capture Without an RTOS

Input-capture peripherals for pulse widths and event latency

One of the most useful embedded skills is measuring external timing accurately without hiding behind a heavy runtime stack. You do not need an RTOS to capture pulse widths, frequency drift, or event latency with high reliability. You need a clear timing model, disciplined interrupt design, and careful data handoff.

Timer input-capture peripherals are built for this job. They latch counter values on configured edges and let firmware process deltas later. The hardware does the precise timestamping; software handles interpretation.

A robust architecture starts with three decisions:

If these are vague, accuracy claims will be vague too.

Choose timer frequency from measurement goals, not convenience. Too slow and quantization error dominates. Too fast and overflow complexity increases, especially on narrow counters. A good target is where one tick is clearly below your required resolution with margin for jitter analysis. ... continue

Threat Modeling in the Small

One feature, one flow, one safer change this week

When people hear “threat modeling,” they often imagine a conference room, a wall of sticky notes, and an enterprise architecture diagram no single human fully understands. That can be useful, but it can also become theater. Most practical security wins come from smaller, tighter loops: one feature, one API path, one cron job, one queue consumer, one admin screen.

I call this “threat modeling in the small.” The goal is not to produce a perfect model. The goal is to make one change safer this week without slowing delivery into paralysis.

Start with a concrete unit. “User authentication” is too broad. “Password reset token creation and validation” is the right scale. Draw a tiny flow in plain text. List the trust boundaries. Ask where attacker-controlled data enters. Ask where privileged actions happen. Ask where logging exists and where it does not.

At this size, engineers actually participate. They can reason from code they touched yesterday. They can connect risks to implementation choices. They can estimate effort honestly. Security stops being abstract policy and becomes software design.

My default prompt set is short: ... continue

The Cost of Unclear Interfaces

Ambiguity as a compounding reliability tax

Most teams think interface problems are technical. Sometimes they are. More often, they are social problems expressed through technical artifacts.

An interface is any boundary where one thing asks another thing to behave predictably. In code, that can be a function signature, an API schema, a queue contract, or a config file format. In teams, it can be a handoff checklist, an on-call escalation rule, or a release approval process. In both cases, the cost of ambiguity is delayed, compounding, and usually paid by someone who was not in the room when the ambiguity was created.

We notice unclear interfaces first as friction:

Each sentence sounds small. Together, they create reliability tax.

The dangerous part is that unclear interfaces rarely fail loudly at first. They degrade trust slowly. One team adds defensive checks “just in case.” Another adds retries to compensate for uncertain behavior. A third adds custom adapters to normalize inconsistent outputs. Soon, the architecture looks complicated, and everyone blames complexity. But complexity was often an adaptation to interface uncertainty. ... continue

Terminal Kits for Incident Triage

Small, scriptable primitives under time pressure

During an incident, tool quality is less about features and more about reliability under pressure. A terminal kit that is small, predictable, and scriptable often beats a heavyweight platform with perfect screenshots but slow interaction. Triage is fundamentally a time-budgeted decision process: gather evidence, reduce uncertainty, choose containment, repeat. Your toolkit should optimize that loop.

Most failed triage sessions share a pattern: analysts spend early minutes assembling ad-hoc commands, searching historical snippets, and normalizing inconsistent logs. By the time they get coherent output, the window for clean containment may be gone. A prepared terminal kit solves this by standardizing primitives before incidents happen.

A strong baseline kit usually has four layers. First, acquisition tools to collect logs, process snapshots, network state, and artifact hashes without mutating evidence more than necessary. Second, normalization tools that convert varied formats into comparable records. Third, query tools for rapid filtering and aggregation. Fourth, packaging tools to export findings with reproducible command history.

The “reproducible command history” part is often neglected. If commands are not captured with context, handoff quality collapses. Teams should treat command logs as first-class incident artifacts: timestamped, host-tagged, and linked to case identifiers. This both improves collaboration and reduces postmortem reconstruction effort.

Command wrappers help enforce consistency. Instead of everyone typing bespoke variants of grep, awk, and jq pipelines, define stable entry scripts with sane defaults: UTC timestamps, strict error handling, deterministic output columns, and explicit field separators. Analysts can still drop to raw commands, but wrappers eliminate repetitive setup mistakes. ... continue

State Machines That Survive Noise

A lot of embedded bugs are not algorithm failures. They are state-management failures under imperfect signals. Inputs bounce, clocks drift, interrupts cluster, and peripherals report transitional nonsense. Firmware that assumes clean edges and ideal timing eventually fails in the field where noise is normal.

Robust systems treat noise as a design input, not a test surprise.

Why finite state machines still win

State machines are sometimes dismissed as “old-school” in modern embedded stacks. That is a mistake. They remain one of the best tools for making behavior explicit under uncertainty:

Most importantly, state machines force you to name ambiguous phases that ad-hoc boolean logic usually hides.

A practical pattern: event queue + transition table

A resilient architecture separates interrupt capture from policy:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
typedef enum { ST_IDLE, ST_ARMED, ST_ACTIVE, ST_FAULT } state_t;
typedef enum { EV_EDGE, EV_TIMEOUT, EV_CRC_FAIL, EV_RESET } event_t;

state_t step(state_t s, event_t e) {
  switch (s) {
    case ST_IDLE:   return (e == EV_EDGE)    ? ST_ARMED  : ST_IDLE;
    case ST_ARMED:  return (e == EV_TIMEOUT) ? ST_ACTIVE : (e == EV_CRC_FAIL ? ST_FAULT : ST_ARMED);
    case ST_ACTIVE: return (e == EV_RESET)   ? ST_IDLE   : ST_ACTIVE;
    case ST_FAULT:  return (e == EV_RESET)   ? ST_IDLE   : ST_FAULT;
  }
  return ST_FAULT;
}

... continue

SPI Signals That Lie

When plausible bytes hide electrical or sequencing failure

SPI looks simple on paper: clock, data out, data in, chip select. Four wires, deterministic timing, done. In real projects, SPI failures often appear as “sometimes wrong bytes,” “first transfer fails,” or “only breaks on production boards.” These are the kind of bugs that waste days because the bus seems healthy at first glance.

The core lesson is that SPI integrity is not just protocol correctness. It is electrical timing, firmware sequencing, and peripheral state management combined.

Common failure classes:

Any one can produce plausible-but-wrong data.

I start with protocol truth first. Confirm CPOL/CPHA mode from datasheets, then verify with logic analyzer captures of command/response boundaries. Do not rely on “it worked with another sensor.” Different devices tolerate different mistakes. ... continue