C:\RETRO\DOS\TP\TOOLCH~1>type turbop~4.htm
Turbo Pascal Toolchain, Part 4: Graphics Drivers, BGI, and Rendering Integration
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.
- driver assets: usually
.BGI(e.g.EGAVGA.BGI,CGA.BGI) - font assets:
.CHRstroked fonts (e.g.TRIP.CHR,GOTH.CHR) - initialization:
InitGraph(driver, mode, path) - status reporting:
GraphResult - cleanup:
CloseGraph
Two operational models exist:
- Dynamic runtime loading from filesystem path — driver and font files are
read from disk at
InitGraphtime. - Linked-driver — driver (and optionally font) binaries converted to
.OBJand linked into the executable; registration APIs make them available beforeInitGraph.
Both are valid. Pick by deployment constraints: dynamic keeps builds small and simple but requires correct runtime paths; linked reduces file dependencies and installer mistakes at the cost of executable size and build coupling. Many teams shipped dynamic for development and internal testing, then produced a linked-driver build for floppy or constrained deployments where users were unlikely to preserve directory structure correctly.
Graph unit runtime contracts and GraphResult handling
Every Graph operation that can fail updates an internal error code. GraphResult
returns that code and, in TP5, resets it to zero on read. That one-read
semantic causes subtle bugs when code checks GraphResult multiple times or
assumes it remains set across calls.
Contract rules:
- Call
GraphResultonce after any operation that may fail, store the value in a local variable, then branch on that variable. - Do not assume
GraphResultstays non-zero until the next failed operation. - Never call
GraphResultbefore the operation you intend to check — earlier successful operations clear it.
{ WRONG: second check sees zero from first read }
InitGraph(gd, gm, '.\BGI');
if GraphResult <> grOk then Halt(1);
if GraphResult <> grOk then ... { always passes; result was cleared }
{ RIGHT: single read, then use stored value }
InitGraph(gd, gm, '.\BGI');
gr := GraphResult;
if gr <> grOk then
begin
Writeln('Init failed: ', gr);
Halt(1);
end;TP5 error codes worth memorizing for triage:
| Code | Constant | Typical cause |
|---|---|---|
| 0 | grOk |
Success |
| -1 | grNoInitGraph |
Graphics not initialized |
| -2 | grNotDetected |
No compatible adapter found |
| -3 | grFileNotFound |
Driver or font file missing on path |
| -4 | grInvalidDriver |
Driver format invalid or mismatched |
| -5 | grNoLoadMem |
Not enough heap for driver/buffer |
| -8 | grFontNotFound |
Font file missing |
| -9 | grNoFontMem |
Not enough heap for font |
| -10 | grInvalidMode |
Mode not supported by driver |
| -11 | grError |
General error; often registration/order violation |
When grNoLoadMem appears, suspect overlay buffer sizing or TSR load order
before blaming hardware. When grFileNotFound appears, verify PathToDriver
resolves from the process’s current directory, not the source tree. Some TP/BP
variants may use PathStr or environment variables for default paths; the
TP5 reference is explicit that an empty path means current directory, and
documentation for later versions should be consulted if behavior differs.
Uncertainty note: Exact GraphResult semantics and numeric codes can
vary slightly between TP5, TP6, TP7, and BP7. The table above reflects TP5
reference values; when targeting multiple versions, confirm codes in your
toolchain’s GRAPH.TPU or include files.
TP5 baseline facts from the reference guide
For Turbo Pascal 5.0, the reference guide is explicit:
- compile-time dependency:
GRAPH.TPU - runtime dependency: one or more
.BGIdrivers - if stroked fonts are used: one or more
.CHRfiles
InitGraph loads the selected driver and enters graphics mode. CloseGraph
unloads/restores previous mode. This is the lifecycle baseline. After
CloseGraph, you may re-enter graphics mode with another InitGraph call, but
driver and font state are reset; any registered user drivers must be re-registered
if you use the linked model.
Dynamic model: fastest to start, easiest to break in deployment
uses Graph;
var
gd, gm, gr: Integer;
begin
gd := Detect;
InitGraph(gd, gm, 'C:\APP\BGI');
gr := GraphResult;
if gr <> grOk then
begin
Writeln('BGI init failed: ', gr);
Halt(1);
end;
{ render }
CloseGraph;
end.Expected outcome:
- works immediately in dev environment with full BGI directory
- fails fast if path/assets are missing, with actionable error code
Common failure is not code. It is wrong path assumptions after installation.
Typical mistakes: hardcoding C:\TP\BGI or .\BGI when the app runs from
A:\ or a network drive; assuming GetDir equals executable directory; using
forward slashes on systems that expect backslashes.
TP5 path behavior: if PathToDriver is empty, driver files must be in the
current directory. If you pass a path, it must end with a trailing backslash on
some implementations to be treated as a directory. Conservative practice: always
pass an explicit path built from ParamStr(0) or GetDir, and ensure it ends
with \.
Path resolution example:
uses Dos, Graph;
var
ExeDir, BgiPath: PathStr;
Name, Ext: PathStr;
begin
FSplit(ParamStr(0), ExeDir, Name, Ext);
BgiPath := ExeDir + 'BGI' + '\';
InitGraph(gd, gm, BgiPath);
gr := GraphResult;
...
end.This assumes BGI is a subdirectory next to the executable. If you ship with
BGI alongside .EXE, this pattern works regardless of where the user
installed the app.
Triage for dynamic-load failures:
- Run the diagnostic harness (see below) from the same directory and path the app will use in production.
- If harness works but app fails, compare paths and current-directory assumptions between harness and app.
- If
grFileNotFound: list directory contents, verify file names match exactly (case may matter on some setups). - If
grNoLoadMem: reduce overlay buffer, close TSRs, or switch to linked driver.
Linked-driver model: more robust runtime, tighter build coupling
Some Borland-era toolchains support converting/linking driver binaries into
object form and registering them at startup (for example via RegisterBGIdriver
and companion font registration APIs). This avoids runtime dependency on
external .BGI files but increases binary size and build complexity.
Practical pattern:
- convert/select driver object module
- link object into project (
{$L ...}or linker config) - register driver before
InitGraph - call
InitGraphwith empty or local path expectations
Exact symbol names and conversion utilities depend on installation/profile, so document your specific toolchain once and keep it version-pinned.
TP5 manual flow for linked drivers is concrete:
- convert
.BGIto.OBJwithBINOBJ - link resulting
.OBJinto the executable - call
RegisterBGIdriverbeforeInitGraph
If you call RegisterBGIdriver after graphics are already active, TP5 reports
grError (-11). Same rule applies to RegisterBGIfont: register before
first use of that font.
BINOBJ invocation (exact syntax varies by Borland install):
|
|
This produces EGAVGA.OBJ with symbols for the binary blob. The linker then
pulls it in via {$L EGAVGA.OBJ}. The two symbol names after the filename
are typically the public name and the segment/object name; consult your
BINOBJ documentation. After conversion, add the .OBJ to your build and
ensure it is linked before the unit that calls RegisterBGIdriver. If the
symbol is undefined at link time, the .OBJ was not included or the
declaration does not match BINOBJ output.
Illustrative registration shape (symbol names vary by conversion/tooling):
{$L EGAVGA.OBJ}
procedure RegisterEgaVga; external;
begin
RegisterBGIdriver(@RegisterEgaVga);
{ or InstallUserDriver + callback, depending on toolchain }
InitGraph(gd, gm, '');
gr := GraphResult;
if gr <> grOk then Halt(1);
{ ... }
end.Treat symbol names as toolchain-specific; BINOBJ output and TP/BP docs define
the exact entry. If registration order is wrong, you get grError with no
obvious message — add logging before each Graph call during integration.
Pitfalls: Forgetting to register before InitGraph; registering after
InitGraph; linking the wrong driver .OBJ for the target adapter; mixing
driver versions (e.g. TP5 vs BP7) when BGI format differs. Another pitfall:
assuming Detect returns the same driver on all VGA systems. Some VGA clones
or BIOS quirks can cause Detect to fail or return a conservative mode;
hardcoding a fallback (e.g. if gd = Detect then gd := VGA; gm := VGAHi) can
improve robustness when autodetect is unreliable. When linking multiple
drivers (e.g. VGA + CGA fallback), register all before InitGraph; the order
may matter for some toolchains — consult your Graph unit docs. A linked build
that works in the IDE can fail at standalone run if the .OBJ was not linked
or the external symbol name does not match BINOBJ output; add a build step
that verifies the linked executable size increased by the expected driver
blob size.
Asset set discipline (driver + font matrix)
For each shipping mode profile, define and freeze:
- required driver files (e.g.
EGAVGA.BGIfor VGA,CGA.BGIfor CGA fallback) - required font files (e.g.
TRIP.CHR,GOTH.CHRifSetTextStyleuses them) - fallback mode behavior (what mode to try if Detect fails or preferred mode unavailable)
- startup diagnostics text (what to print on failure)
Without this matrix, BGI deployment drifts silently between machines. One
developer ships with EGAVGA.BGI only; another’s machine has CGA.BGI in path;
field reports “black screen” and nobody knows which adapter or driver set was
used.
Driver and font packaging rules: Drivers and fonts must be version-pinned to
the toolchain that produced GRAPH.TPU. TP5 EGAVGA.BGI is not guaranteed
compatible with BP7’s Graph unit; format and entry-point layout can differ.
Package drivers as a locked set: document “EGAVGA.BGI from TP5.0 install dated
1989” in your release notes. Fonts are similarly sensitive: a .CHR from one
toolchain may load but render incorrectly with another. When upgrading the
compiler, re-validate the full driver+font matrix against your harness before
cutting a release. Include file sizes and checksums in the manifest so swapped
or corrupted copies are detectable.
Font/driver matrix discipline: Not all fonts work with all drivers. Stroked
fonts (.CHR) are driver-independent in principle, but SetTextStyle calls
before a font is loaded fall back to default. Document which fonts are required
for each UI path. If you use InstallUserFont or RegisterBGIfont, the
registration order and timing must match the matrix — register before any
SetTextStyle that selects that font. A minimal matrix might look like:
| Target adapter | Driver | Fonts used | Fallback mode |
|---|---|---|---|
| VGA | EGAVGA.BGI | TRIP, GOTH | VGAHi |
| EGA | EGAVGA.BGI | TRIP | EGALo |
| CGA | CGA.BGI | (default) | CGAC0 |
Ship only drivers and fonts listed for your supported targets. Including extra files “just in case” increases install size and the chance of path confusion. Update the matrix when adding support for new adapters (e.g. Hercules, MCGA) or when dropping support for legacy hardware.
BGI artifacts in build and deploy pipelines
BGI assets are build outputs as much as runtime dependencies. Include them in your artifact pipeline so releases are reproducible.
Package layout:
|
|
If using linked drivers, BGI/ and FONTS/ may be empty, but the layout
should still be documented so installers know what to expect.
Build script integration:
|
|
Run the diagnostic harness as a post-build step: execute it from RELEASE\
with BGI as subdirectory and assert GraphResult = grOk. If the harness
fails in clean build output, fix paths before shipping. Some teams wired the
harness as BUILD.BAT final step with if errorlevel 1 to fail the build.
Checksum discipline: Store MD5 or CRC of each .BGI and .CHR in a
manifest. When field reports “weird corruption” or mode errors, compare
checksums to rule out truncated or swapped files. A swapped CGA.BGI and
EGAVGA.BGI (e.g. misnamed copies) produces grInvalidDriver or garbled
output; checksums catch that quickly. Run checksum verification as part of
the build pipeline: after copying assets to RELEASE\, compute checksums and
append to a MANIFEST.TXT; archive that manifest with the release. During
support triage, ask the user to run a simple checksum tool (or provide a tiny
.COM that prints file sizes) and compare against the manifest — mismatches
point to installer bugs, disk errors, or manual file replacements.
Floppy and installer considerations: If distributing on floppies, put
MYAPP.EXE on disk 1 and BGI\ contents on the same or next disk. Installer
scripts should copy BGI\ into the target directory and set current-directory
expectations in a README. Avoid assuming users will run from a subdirectory;
many double-click or type MYAPP from C:\GAMES\ and expect .\BGI to mean
C:\GAMES\BGI.
Are BGI file formats fully documented?
Honest answer: not in a stable, complete way that is safe to treat as universal across all TP/BP-era variants. You can inspect BGI bytes and infer structure, but production workflows historically relied on Borland-provided drivers and APIs, not custom byte-level authoring from scratch. Third-party efforts (e.g. SDL_bgi, Free Pascal Graph unit) have reverse-engineered portions of the format for compatibility; those sources may help if you need to validate or debug driver files, but do not assume full specification coverage.
What you can reliably do today:
- verify driver/font assets exist and match expected set
- checksum assets as release artifacts
- use diagnostic harnesses to confirm runtime load path
Diagnostics pitfall: Do not assume a BGI file is valid just because it
exists. A truncated or corrupted file can produce grInvalidDriver or
unpredictable behavior. If you suspect file integrity, compare size and
checksum against a known-good copy from your toolchain installation.
“How are BGI drivers created?” practical answer
Three realistic paths:
- Use stock Borland drivers (most common historical path). Ship
EGAVGA.BGI,CGA.BGI, etc. from your TP/BP installation. Ensure version consistency: TP5 drivers are not guaranteed compatible with BP7 Graph unit and vice versa. When in doubt, use drivers from the same toolchain that producedGRAPH.TPU. - Link stock drivers into executable for deployment robustness. Convert
with
BINOBJ, link, register. Same version-pinning rule applies. - Author custom driver only if you own/understand ABI details and tooling. The BGI format includes device-specific entry points, mode tables, and drawing primitives. Third-party documentation (e.g. from replacement BGI projects) exists but varies in accuracy. Treat custom drivers as a separate maintenance burden.
Path 3 is advanced reverse-engineering/ABI work. It is possible, but not the right default for project delivery unless driver capabilities are missing.
BGI startup diagnostics harness (must-have)
program BgiDiag;
uses Graph, Crt;
var
gd, gm, gr: Integer;
begin
gd := Detect;
InitGraph(gd, gm, '.\BGI');
gr := GraphResult;
Writeln('Driver=', gd, ' Mode=', gm, ' GraphResult=', gr);
if gr = grOk then
begin
SetColor(15);
OutTextXY(8, 8, 'BGI init OK');
Line(0, 0, GetMaxX, GetMaxY);
ReadKey;
CloseGraph;
end
else
Writeln('Init failed. Check path, files, memory.');
end.Run this before debugging your game engine. It isolates path/driver faults from
rendering logic faults. Keep it as a separate program in your tree; do not
embed it inside the main app, because you want to run it in isolation when
the full app crashes before any output. A harness that runs to completion
proves the BGI stack works; if the harness fails, fix that before debugging
renderer code. Extend the harness when you encounter new failure modes: add a
font-load test if grFontNotFound appears in the field, or a Detect-then-
forced-mode variant if adapter detection is unreliable. The harness becomes
your regression suite for the BGI layer; document each variant and when to
run it. Some teams maintained a BGIDIAG.EXE in the release package so
support could ask users to run it and report the printed codes — a single
number (GraphResult) often suffices to distinguish path, memory, and driver
issues without requiring logs or repro steps.
Triage procedure with the harness:
- Run from project root with
.\BGIcontaining drivers — expectgrOk. - Run from same directory but rename
BGItoBGI_BACKUP— expectgrFileNotFound; confirm printed code matches. - Run from a directory without
BGIsubfolder — expectgrFileNotFound. - On a TSR-heavy config (mouse, network driver, etc.), run harness — if
grNoLoadMem, document minimum free conventional memory for your build.
Important TP5 behavior: GraphResult resets to zero after it has been called.
Store it in a variable once and evaluate that variable.
Font handling is a real subsystem
If UI layout depends on font metrics, .CHR assets are first-class artifacts:
- version and checksum them
- package with same discipline as executables
- test fallback behavior explicitly
Silent fallback to default font can break coordinates, clipping, and hit zones.
A menu rendered with GOTH.CHR has different line heights than the default;
if the font fails to load, text may overlap or clip incorrectly.
TP5 adds two separate extension points:
InstallUserFont(register by name/file path)RegisterBGIfont(register loaded or linked-in font pointer)
As with drivers, registration must be done before normal graphics workflow relies
on those resources. After SetTextStyle(...), check GraphResult if the font
was user-installed; grFontNotFound or grNoFontMem indicate load failure.
Memory interaction: Stroked fonts are loaded into heap. A large .CHR plus
graphics buffer plus overlay buffer can exhaust conventional memory. If
grNoFontMem appears only with certain fonts, try smaller fonts or linked-font
approach for the critical path. Font packaging parallels driver packaging:
ship only the fonts your UI actually uses, version-pin them to your toolchain,
and include checksums in the release manifest. A common mistake is bundling
every .CHR from the TP install “for completeness” — this bloats the package
and increases the chance of loading the wrong font by typo or path confusion.
If SetTextStyle references a font that was never loaded or registered, the
unit falls back to default; the fallback is silent, so layout assumptions
(lower height, different metrics) can break. Add an explicit font-load check
after SetTextStyle for user fonts and log GraphResult during development.
BGI + overlays + memory budget interaction
Graphics initialization and overlays interact with memory pressure. If startup becomes unstable after enabling overlays or TSR-heavy profiles, validate:
- available memory headroom before
InitGraph - overlay manager buffer size (
OvrSetBuf) - order of subsystem initialization
Treat graphics bugs and memory bugs as potentially coupled until proven otherwise.
Memory interplay with overlays is the most common source of “works in dev,
fails in release” BGI bugs: the overlay manager allocates a contiguous buffer
from the heap; Graph allocates its own buffers from the same heap. If the overlay
buffer is carved out first, Graph gets what remains; if Graph allocates first
and overlays later try to grow, the heap can be fragmented or exhausted.
grNoLoadMem often appears when overlay and Graph compete for the same pool
without a clear initialization order.
TP5 memory detail: Graph uses heap for graphics buffer, loaded drivers, and
loaded stroked fonts (unless linked/registered path is used). This is why
overlay buffer sizing (OvrSetBuf) and InitGraph order can conflict.
Order rule: Call OvrSetBuf (and OvrInit) before InitGraph. The
overlay manager carves its buffer from the heap; Graph then allocates from what
remains. Reversing the order can leave insufficient room for either. A typical
failure: InitGraph succeeds, then OvrSetBuf shrinks the heap, and a later
overlay load or Graph operation fails with grNoLoadMem or overlay error. The
fix is to establish overlay buffer first, then let Graph allocate from the
remaining free memory.
OvrInit(OvrFile);
if OvrResult <> ovrOk then Halt(1);
OvrSetBuf(50000); { before InitGraph }
InitGraph(gd, gm, '.\BGI');
gr := GraphResult;Memory budgeting: On a 640 KB DOS machine, TSRs and DOS typically consume 50–150 KB. Your app gets the rest. A VGA buffer (640×480×1 byte) is ~300 KB; EGAVGA.BGI adds tens of KB when loaded; stroked fonts add more. If you use overlays, their buffer comes from the same pool. Document a minimum free-memory requirement (e.g. “400 KB free conventional memory”) and test at that threshold. Boot with a minimal CONFIG.SYS and AUTOEXEC.BAT to simulate a memory-tight environment; if your app runs there, it will usually run on richer systems. This simple test catches many “works on my machine” deployment failures.
Overlay–Graph allocation interplay: The heap layout after OvrSetBuf and
InitGraph is toolchain- and order-dependent. A rough rule of thumb: VGA
graphics buffer (~300 KB) + EGAVGA driver (~30–50 KB when loaded) + one stroked
font (~20–40 KB) leaves little for overlays on a 640 KB system with 400 KB free.
If you use both overlays and Graph, establish the overlay buffer first with a
conservative size, then init Graph; measure free memory before and after each
step during integration. Some teams added a startup banner (“Free mem: XXXXX”)
before InitGraph to catch regressions. Uncertainty note: Exact allocation
order and sizes can vary between TP5, TP6, and BP7; when in doubt, instrument
and measure on your target configuration.
Debugging rendering failures on real DOS systems
When graphics fail in the field but work in development, systematic triage narrows the cause. Use a rendering triage loop: run the diagnostic harness first; if it passes, the fault is in application rendering logic or asset loading, not BGI init. If the harness fails, iterate on path, memory, or driver until it passes, then return to the full app. Do not debug a complex renderer while BGI fundamentals are still failing — you will waste time chasing symptoms (e.g. “Line draws wrong”) that stem from an earlier init or mode problem.
Release verification on real hardware: Before signing off a build, run the
full checklist on at least one physical DOS machine (or a well-configured
emulator that matches period behavior). Boot from floppy or minimal HD; run from
A:\, C:\GAMES, and a nested subdirectory; try with and without common TSRs
(mouse, sound driver). Known problematic configurations include: VGA clones with
nonstandard BIOS mode tables, EGA systems with 256 KB vs 64 KB variants, and
machines with < 400 KB free conventional memory. Document which adapter and
memory profile you verified; when field reports arrive, compare against that
baseline. A build that has never been run on real hardware is a risk.
Triage steps:
- Black screen, no message: Program may be exiting before any output.
Add a
Writeln('Starting...')beforeInitGraph; if it never appears, the crash is earlier (e.g. overlay init, missing.OVR). On some DOS configurations, mode switch can also blank the screen before text output is visible; redirect output to a file (MYAPP > LOG.TXT 2>&1) to confirm whether any text was produced. - Black screen, message appeared then vanished: Mode switch may have
failed, or the program exited immediately. Ensure
GraphResultis checked and stored before any cleanup; addReadKeyorDelaybeforeCloseGraphin harness to confirm. If the message flashes too quickly to read, write it to a file as well. - Wrong resolution or garbled display: Driver/mode mismatch.
Detectmay pick a different mode on different adapters; loggdandgmand compare to adapter documentation. Force a known mode (e.g.gd := VGA; gm := VGAHi) for compatibility testing. - Works once, fails on second run: TSR or environment pollution. Reboot
to clean state; disable TSRs one by one. Some drivers leave video state
inconsistent after
CloseGraph. - grNoLoadMem on target only: Conventional memory too low. Run
MEMbefore app; compare to dev machine. Reduce overlay buffer or ship linked driver build.
Keep a triage log: adapter type, driver set, free memory, TSR list, and exact
GraphResult value. Reproducible cases go into the test matrix. When a new
symptom appears (e.g. “screen flickers then goes black”), add a minimal
reproducer to the harness: if you can trigger it there, debug there; if only
the full app exhibits it, the cause is likely in overlay loading order, asset
sequencing, or interaction with game/UI logic. This divide-and-conquer approach
keeps triage loops short and deterministic.
Team checklists and release hardening
Before release, the team should complete:
Pre-build:
- Unit search path, object path, and BGI asset path documented and version-pinned
- Build script produces deterministic output (clean build, no stale artifacts)
Build:
- All required
.BGIand.CHRcopied to release layout - Diagnostic harness runs successfully from release directory
- Checksums recorded for
.EXE,.OVR(if used),.BGI,.CHR
Post-build verification:
- Test from directory different from source (e.g.
C:\TEST\,A:\) - Intentionally remove one driver, run app — verify error message, no crash
- Test with overlay file missing (if applicable) — verify controlled exit
- One memory-stressed run (e.g. with
MEMreporting < 400 KB free) - Run diagnostic harness from same release layout; assert it reports
grOk - If distributing on floppy: boot from disk 1, run from
A:\, confirm BGI path resolves correctly (e.g.A:\BGI\when app is onA:\)
Release package:
- README includes BGI path requirements and minimum memory
- Build manifest (checksums, compiler version, options) archived with artifacts
Expected outcome: actionable startup message on any failure, never black-screen
ambiguity. When a user reports “does not work,” the checklist gives you
questions to ask (adapter, path, memory, exact error text) instead of blind
guesswork. Teams that shipped without this discipline often spent hours on
support calls trying to reproduce “black screen” with no data. A single
Writeln('BGI error: ', gr) before Halt(1) can save days of debugging.
Useful TP5 InitGraph failure codes to log:
grNotDetected(-2)grFileNotFound(-3)grInvalidDriver(-4)grNoLoadMem(-5)grInvalidMode(-10)
Where to go deeper
- Turbo Pascal BGI Tutorial: Dynamic Drivers, Linked Drivers, and Diagnostic Harnesses
- Mode 13h Graphics in Turbo Pascal
- Mode X Part 1: Planar Memory Model
Next: