Graphics drivers and stroked fonts for Borland's BGI (Borland Graphics Interface) — the graphics layer used by Turbo Pascal, Turbo C and Borland C++ for DOS.
Two things ship here, and they come from very different places:
- The drivers are original work — written from scratch in assembly, heavily optimised for the 8086, containing zero Borland code.
- The fonts are third-party typefaces converted mechanically to BGI's
.CHRstroke format. Each one keeps its own licence.
CGA2.BGI CGA 640x200, 2 colours (BIOS mode 06h)
CGA4.BGI CGA 320x200, 4 colours (BIOS mode 04h, plus a grey mode on 06h)
CGA16.BGI CGA 160x200, 16 colours (BIOS mode 06h, composite artifact colour)
VGA16.BGI VGA 640x480, 16 colours (BIOS mode 12h)
FONTS/ 24 stroked fonts (.CHR) + licence texts (.TXT)
SRC/DRIVERS/ driver sources (NASM)
SRC/MKBGI/ .BIN -> .BGI packager
SRC/CHRFIX/ .CHR font checker / repairer
DOCS/BGI.MD .BGI driver format — the driver ABI, in full
DOCS/CHR.MD .CHR stroked-font format — the font format, in full
Both file formats are documented from the bytes up, with worked examples:
- DOCS/BGI.MD — the
.BGIdriver format: header layout, theDDOVECdispatch table, the entry stub, the Device Information Table, and a step-by-step guide to writing your own driver. - DOCS/CHR.MD — the
.CHRstroked-font format: every header field, the signed 7-bit stroke encoding, glyphs decoded byte by byte, working readers in C and Oberon, and a validation checklist covering the format's several silent failure modes.
These drivers are a clean implementation of the BGI driver ABI. Nothing is
disassembled, copied, or derived from Borland's own CGA.BGI /
EGAVGA.BGI binaries — the published interface is implemented directly,
from the device side up. That is why they can be public domain: there
is no Borland code inside to encumber them.
They are ordinary BGI drivers, so they drop straight into any Turbo Pascal
or Turbo C program through initgraph / registerbgidriver.
The drivers target a real 8086 and are written to its cost model, not to
what a modern CPU would forgive. About 10,800 lines of assembly across the
four, with cpu 8086 enforced — no 186+ instructions, so they run on the
oldest hardware BGI ever supported.
The optimisations that matter:
- No
MULin the hot paths. An 8086 multiply costs well over a hundred cycles. Address calculation uses a precomputed row table and glyph lookup uses three shifts — each replacing a multiply that would otherwise run per pixel or per character. - Whole-byte fast paths. A horizontal run of 8 same-coloured pixels is a single byte write, not eight pixel writes. Fills, bars and clipped runs all take this path.
- Byte-aligned text fast path. Unmagnified horizontal text on a byte boundary bypasses the general glyph blitter entirely.
- No
JCXZ/LOOP. Both are slow on real silicon; equivalent sequences are used instead. - Clipping hoisted out of inner loops. Runs are clamped to the clip box up front, so the fast path never re-tests per pixel.
On VGA the planar path uses Graphics Controller write mode 2 with a bit mask, so all four colour planes are written in one masked read-modify-write through the latches rather than four separate passes.
CGA4 also exposes a third mode, grey (320x200x4@Grey), which issues
BIOS mode 06h instead of 04h at init. The framebuffer layout is
identical to the two colour palettes — same 2bpp packing, same
interleave — so CalcAddr, PlotOne and every drawing routine are
shared unchanged; only the BIOS mode switch differs, and mode 06h simply
displays the same 2-bit image without colour.
CGA16 gets its 16 colours from composite artifact colour, not a wider
palette register: it drives BIOS mode 06h (640×200, 1 bit/pixel) with the
colour-burst bit enabled, and each BGI pixel is a 4-hardware-bit repeating
cell that an NTSC composite decoder reads as one of 16 colours. The
framebuffer is otherwise CGA2/CGA4's own layout (B800h, 80 bytes/row,
even/odd interleave); only init and the 4-bit-per-pixel packing differ.
It needs a composite-capable display or emulator (e.g. DOSBox-X's
machine=cga_composite) — on plain RGBI hardware it still runs, but the
16 colours collapse to whatever monochrome pattern the bits happen to
draw.
one screen byte (2 BGI pixels, 8 hardware bits)
┌───────────────┬───────────────┐
│ high nibble │ low nibble │
│ (even X) │ (odd X) │
│ ┌─┬─┬─┬─┐ │ ┌─┬─┬─┬─┐ │ b = one hardware bit; the 4-bit
│ │b│b│b│b│ │ │b│b│b│b│ │ pattern repeats every BGI pixel
│ └─┴─┴─┴─┘ │ └─┴─┴─┴─┘ │ and is what the composite decoder
└───────────────┴───────────────┘ reads as a colour
BGI pixel X BGI pixel X+1
The image-buffer size formula is mode-dependent — worth knowing if you allocate buffers yourself:
| Driver | Depth | Pixels/byte | getimage size |
|---|---|---|---|
| CGA2 (640×200×2) | 1 bpp | 8 | 4 + ((w+7)>>3) * h |
| CGA4 (320×200×4) | 2 bpp | 4 | 4 + ((w+3)>>2) * h |
| CGA16 (160×200×16) | 4 bpp | 2 | 4 + ((w+1)>>1) * h |
| VGA16 (640×480×16) | 4 bpp | 2 | 4 + ((w+1)>>1) * h |
Each driver stores images packed at its own native colour depth, with
every row restarting on a byte boundary — the 4 leading bytes hold width−1
and height−1. On VGA16 the buffer is packed 4bpp even though the
framebuffer is planar, so getimage/putimage convert between the two.
24 stroked fonts in BGI .CHR format, converted mechanically from freely
available typefaces — mostly pixel/bitmap faces, which suit BGI's stroke
model and low resolutions well.
These fonts are not ours. Each came from its original author under a free licence, and converting a typeface does not change who owns it. Every font ships with the licence text and source URL it arrived with:
| Licence file | Covers | Terms |
|---|---|---|
PO.TXT |
PO* — 12 fonts, Pixel Operator family |
CC0 1.0 (public domain) |
PRESS2P.TXT |
PRESS2P |
SIL Open Font License 1.1 |
CANTERB.TXT |
CANTERB |
1001Fonts Free For Commercial Use (FFC) |
YOSTER.TXT |
YOSTER |
Free use; may not be re-sold in a font collection |
PIXEL.TXT |
PIXEL |
Free personal/commercial; may not be sold |
NBSANS.TXT |
NBSANS3, NBSANS4, NBSANS5 |
See file (itch.io release) |
BITTY.TXT |
BITTY |
See file (itch.io release) |
VCROSD.TXT |
VCROSD |
See file (dafont release) |
Read the licence for any font you ship. Most permit commercial use, but
some restrict redistribution as part of a font collection. If you want no
conditions at all, the PO* family is CC0.
Open any .CHR here and its internal 4-character name reads TRIP,
LITT, SANS — one of Borland's ten. This is deliberate and unavoidable.
The BGI runtime matches that name, case-insensitively, against a hardcoded table of its own fonts:
BOLD EURO GOTH LCOM LITT SANS SCRI SIMP TRIP TSCR
Anything else is rejected with grInvalidFont (-13) before a single
byte of glyph data is read, so a custom font has to present itself as one
of the ten. The name is only a table lookup — it does not select a slot and
does not affect rendering — so these fonts are assigned round-robin across
the ten names. The real typeface is the filename.
A driver is built in two steps: NASM assembles the source to a flat 16-bit
binary, then MKBGI wraps that binary in the .BGI container.
nasm -f bin -o CGA4.BIN CGA4.ASM
mkbgi CGA4 CGA/320x200x4
SRC/DRIVERS/BUILD.BAT builds all four.
SRC/MKBGI/ — the packager. NASM emits a flat 16-bit image and knows
nothing about the BGI container, so MKBGI reads DRIVER.BIN and writes
DRIVER.BGI with the byte-exact v2.0 header the loader expects: the
pk 08 08 magic, an ASCII banner, and the 10-byte header block giving
the header size, driver number, code size and version.
mkbgi DRIVER [DESCRIPTION]
mkbgi CGA4 CGA/320x200x4
mkbgi CGA2 CGA/640x200x2
mkbgi CGA16 CGA/160x200x16@C
mkbgi VGA16 VGA/640x480x16
DESCRIPTION must be a single word — DOS splits the command tail on
whitespace and does no quote processing, so CGA/320x200x4 works but a
quoted multi-word string would not survive. It is appended to the banner as
<DRIVER> BGI Device Driver - <DESCRIPTION>. The banner is cosmetic —
Borland's loader does not parse it — but it identifies the driver to anyone
inspecting the file.
One structural trap the tool handles for you: the header block is written
twice. The first copy must follow the 00 1A end-of-banner marker
immediately, at a variable offset; only the second sits at the fixed
offset 0x80. Place the first block at a fixed offset instead and
Borland's loader rejects the file with "driver has invalid structure".
The driver image itself begins at 0xA0.
The full layout is documented field by field in DOCS/BGI.MD, so you can build the container yourself if you would rather not use the tool.
SRC/CHRFIX/ holds the tool used to validate and repair every font in this
pack. It is worth having if you convert your own: the .CHR format has
several failure modes that produce no error message at all — the font
loads happily and simply draws wrong.
chrfix FILE.CHR check only, report problems (default)
chrfix /F FILE.CHR repair in place
chrfix /F /N=SANS FILE.CHR repair and set the 4-char font name
chrfix /F /S=1 FILE.CHR repair and add 1 pixel of letter spacing
What it checks:
- Font name — must be one of Borland's ten, or the loader returns -13.
- Baseline alignment — capitals must bottom out at exactly
y=0, otherwise the whole text line sits off the baseline. - Coordinate overflow — stroke coordinates are signed 7-bit
(
-64..63). A converter that emits anything larger wraps it negative, and BGI draws that as a wild backwards stroke. - Glyph terminator — every glyph must end with a
move-to (width, 0). Itsyreturns the pen to the baseline; itsxis the advance to the next character. Omit it and the text line drifts a little further with every glyph; setxto zero and characters overlap — a space with zero advance disappears entirely, running words together. - Metrics —
orgpositive, descender zero or negative. - Header fields — sizes, version, table offsets, signature byte.
BGI has no separate letter-spacing setting: the gap between characters is
whatever the advance leaves over after the ink, so the side bearing has to
be built into the advance. That is what /S=N adjusts (negative values
tighten).
Exit codes: 0 valid or repaired, 1 problems found in check-only mode,
2 usage error, 3 I/O error or not a stroked font.
Each of these checks is explained in full — with the byte layout behind it — in DOCS/CHR.MD.
- Drivers, MKBGI and CHRFIX — public domain. (Unlicense) No Borland code is present in any of them; use them for anything. See LICENSE.
- Fonts — each font keeps its own licence. See
FONTS/*.TXTand the table above.