<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Turbo Pascal on TurboVision</title>
    <link>https://turbovision.in6-addr.net/retro/dos/tp/</link>
    <description>Recent content in Turbo Pascal on TurboVision</description>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Tue, 21 Apr 2026 14:06:12 +0000</lastBuildDate>
    <atom:link href="https://turbovision.in6-addr.net/retro/dos/tp/index.xml" rel="self" type="application/rss&#43;xml" />
    
    
    
    <item>
      <title>Mode 13h in Turbo Pascal: Graphics Programming Without Illusions</title>
      <link>https://turbovision.in6-addr.net/retro/dos/tp/mode-13h-graphics-in-turbo-pascal/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 22:23:45 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/retro/dos/tp/mode-13h-graphics-in-turbo-pascal/</guid>
      <description>&lt;p&gt;Turbo Pascal graphics programming is one of the cleanest ways to learn what a frame actually is. In modern stacks, rendering often passes through layers that hide timing, memory layout, and write costs. In DOS Mode 13h, almost nothing is hidden. You get 320x200, 256 colors, and a linear framebuffer at segment &lt;code&gt;$A000&lt;/code&gt;. Every pixel you draw is your responsibility.&lt;/p&gt;
&lt;p&gt;Mode 13h became a favorite because it removed complexity that earlier VGA modes imposed. No planar bit operations, no complicated bank switching for this resolution, and no mystery about where bytes go. Pixel &lt;code&gt;(x, y)&lt;/code&gt; maps to offset &lt;code&gt;y * 320 + x&lt;/code&gt;. That directness made it ideal for demos, games, and educational experiments. It rewarded people who could reason about memory as geometry.&lt;/p&gt;
&lt;p&gt;A minimal setup in Turbo Pascal is refreshingly explicit: switch video mode via BIOS interrupt, get access to VGA memory, write bytes, wait for input, restore text mode. There is no rendering engine to configure. You control lifecycle directly. That means you also own failure states. Forget to restore mode and you leave the user in graphics. Corrupt memory and artifacts appear instantly.&lt;/p&gt;
&lt;p&gt;Early experiments usually start with single-pixel writes and quickly hit performance limits. Calling a procedure per pixel is expressive but expensive. The first optimization lesson is batching and locality: draw contiguous spans, avoid repeated multiplies, precompute line offsets, and minimize branch-heavy inner loops. Mode 13h teaches a truth that still holds in GPU-heavy times: throughput loves predictable memory access.&lt;/p&gt;
&lt;p&gt;Palette control is another powerful concept students often miss today. In 256-color mode, pixel values are indices, not direct RGB triples. By writing DAC registers, you can change global color mappings without touching framebuffer bytes. This enables palette cycling, day-night transitions, and cheap animation effects that look far richer than their computational cost. You are effectively animating interpretation, not data.&lt;/p&gt;
&lt;p&gt;The classic water or fire effects in DOS demos relied on exactly this trick. The framebuffer stayed mostly stable while the palette rotated across carefully constructed ramps. What looked dynamic and expensive was often elegant indirection. When people say old graphics programmers were “clever,” this is the kind of system-level cleverness they mean: using hardware semantics to trade bandwidth for perception.&lt;/p&gt;
&lt;p&gt;Flicker management introduces the next lesson: page or buffer discipline. If you draw directly to visible memory while the beam is scanning, partial updates can tear. So many projects used software backbuffers in conventional memory, composed full frames there, then copied to &lt;code&gt;$A000&lt;/code&gt; in one pass. With tight loops and occasional retrace synchronization, output became dramatically cleaner. This is conceptually the same as modern double buffering.&lt;/p&gt;
&lt;p&gt;Collision and sprite systems further sharpen design. Transparent blits require skipping designated color indices. Masking introduces branch costs. Dirty-rectangle approaches reduce full-screen copies at the price of bookkeeping complexity. Developers learned to choose trade-offs based on scene characteristics instead of blindly applying one pattern. That mindset remains essential in performance engineering: no optimization is universal.&lt;/p&gt;
&lt;p&gt;Turbo Pascal itself played a practical role in this loop. You could prototype an effect in high-level Pascal, profile by observation, then move only hotspot routines to inline assembly where needed. That incremental path is important. It discouraged premature optimization while still allowing low-level control when measurable bottlenecks appeared. Good systems work often looks like this staircase: clarity first, precision optimization second.&lt;/p&gt;
&lt;p&gt;Debugging graphics bugs in Mode 13h was brutally educational. Off-by-one writes painted diagonal scars. Incorrect stride assumptions created skewed images. Overflow in offset arithmetic wrapped into nonsense that looked artistic until it crashed. You learned to verify bounds, separate coordinate transforms from blitting, and build tiny visual test patterns. A checkerboard routine can reveal more than pages of logging.&lt;/p&gt;
&lt;p&gt;One underused exercise for modern learners is implementing the same tiny scene three ways: naive per-pixel draw, scanline-optimized draw, and buffered blit with palette animation. The visual output can be identical while performance differs radically. This makes optimization tangible. You are not guessing from profiler flames alone; you see smoothness and latency with your own eyes.&lt;/p&gt;
&lt;p&gt;Mode 13h also teaches humility about hardware assumptions. Not every machine behaves the same under load. Timing differences, cache behavior, and peripheral quirks affect results. The cleanest DOS codebases separated device assumptions from scene logic and made fallbacks possible. That sounds like old wisdom, but it maps directly to current cross-platform rendering work.&lt;/p&gt;
&lt;p&gt;There is a reason this environment remains compelling decades later. It compresses core graphics principles into a small, understandable box: memory addressing, color representation, buffering strategy, and frame pacing. You can hold the whole pipeline in your head. Once you can do that, modern APIs feel less magical and more like powerful abstractions built on familiar physics.&lt;/p&gt;
&lt;p&gt;Turbo Pascal in Mode 13h is therefore not a relic exercise. It is a precision training ground. It teaches you to respect data movement, to decouple representation from display, to optimize where evidence points, and to treat visual correctness as testable behavior. Those lessons survive every framework trend because they are not about tools. They are about first principles.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Turbo Pascal Before the Web: The IDE That Trained a Generation</title>
      <link>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-before-the-web/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 22:23:12 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-before-the-web/</guid>
      <description>&lt;p&gt;Turbo Pascal was more than a compiler. In practice it was a compact school for software engineering, hidden inside a blue screen and distributed on disks you could hold in one hand. Long before tutorials were streamed and before package managers automated everything, Turbo Pascal taught an entire generation how to think about code, failure, and iteration. It did that through constraints, speed, and ruthless clarity.&lt;/p&gt;
&lt;p&gt;The first shock for modern developers is startup time. Turbo Pascal did not boot with ceremony. It appeared. You opened the IDE, typed, compiled, and got feedback almost instantly. This changed behavior at a deep level. When feedback loops are short, people experiment. They test tiny ideas. They refactor because trying an alternative costs almost nothing. Slow builds do not just waste minutes; they discourage curiosity. Turbo Pascal accidentally optimized curiosity.&lt;/p&gt;
&lt;p&gt;The second shock is the integrated workflow. Editor, compiler, linker, and debugger were not separate worlds stitched together by fragile scripts. They were one coherent environment. Error output was not a scroll of disconnected text; it brought you to the line, in context, immediately. That matters. Good tools reduce the distance between cause and effect. Turbo Pascal reduced that distance aggressively.&lt;/p&gt;
&lt;p&gt;Historically, Borland’s positioning was almost subversive. At a time when serious development tools were expensive and often tied to slower workflows, Turbo Pascal arrived fast and comparatively affordable. That democratized real software creation. Hobbyists could ship utilities. Students could build complete projects. Small consultancies could move quickly without enterprise-sized budgets. This was not just a product strategy; it was a distribution of capability.&lt;/p&gt;
&lt;p&gt;The language itself also helped. Pascal’s structure encouraged readable programs: explicit blocks, strong typing, and a style that pushed developers toward deliberate design rather than accidental scripts that grew wild. In education, that discipline was gold. In practical DOS development, it reduced whole categories of mistakes that were common in looser environments. People sometimes remember Pascal as “academic,” but in Turbo Pascal form it was deeply practical.&lt;/p&gt;
&lt;p&gt;Another underappreciated element was the culture of units. Reusable code packaged in units gave developers a mental model close to modern modular design: separate concerns, publish interfaces, hide implementation details, and reuse tested logic. You felt the architecture, not as a theory chapter, but as something your compiler enforced. If interfaces drifted, builds failed. If dependencies tangled, you noticed immediately. The tool taught architecture by refusing to ignore boundaries.&lt;/p&gt;
&lt;p&gt;Debugging was similarly educational. You stepped through code, watched variables, and saw control flow in a way that made program state tangible. On constrained DOS machines, this was not an abstract “observability platform.” It was intimate and local. You learned what your code &lt;em&gt;actually&lt;/em&gt; did, not what you hoped it did. That habit scales from small Pascal programs to large distributed systems: inspect state, verify assumptions, narrow uncertainty.&lt;/p&gt;
&lt;p&gt;The ecosystem around Turbo Pascal mattered too. Books, magazine listings, BBS uploads, and disk-swapped snippets formed an early social network of practical knowledge. You did not import giant frameworks by default. You copied a unit, read it, understood it, and adapted it. That fostered code literacy. Developers were expected to read source, not just configure dependencies. The result was slower abstraction growth but stronger individual understanding.&lt;/p&gt;
&lt;p&gt;Of course, there were trade-offs. DOS memory models were real pain. Hardware diversity meant edge cases. Portability was weaker than today’s expectations. Yet those constraints produced useful engineering habits: explicit resource budgeting, defensive error handling, and careful initialization order. When you had 640K concerns and no rescue layer above you, discipline was not optional.&lt;/p&gt;
&lt;p&gt;A subtle historical contribution of Turbo Pascal is that it made tooling aesthetics matter. The environment felt intentional. Keyboard-driven operations, predictable menus, and consistent status information created confidence. Good UI for developers is not cosmetic; it changes throughput and cognitive load. Turbo Pascal proved that decades before “developer experience” became a buzzword.&lt;/p&gt;
&lt;p&gt;Why does this still matter? Because many modern teams are relearning the same lessons under different names. We call it “fast feedback,” “inner loop optimization,” “modular design,” “shift-left debugging,” and “operational clarity.” Turbo Pascal users lived these principles daily because the environment rewarded them and punished sloppy alternatives quickly.&lt;/p&gt;
&lt;p&gt;If you revisit Turbo Pascal today, don’t treat it as museum nostalgia. Treat it as instrumentation for your own habits. Notice how quickly you can move with fewer layers. Notice how explicit interfaces reduce surprises. Notice how much easier decisions become when tools expose cause and effect immediately. You may not return to DOS workflows, but you will bring back better instincts.&lt;/p&gt;
&lt;p&gt;In that sense, Turbo Pascal’s legacy is not a language market share story. It is a craft story. It taught people to build small, test often, structure code, and respect constraints. Those are still the foundations of reliable software, whether your target is a DOS executable, a firmware image, or a cloud service spanning continents.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Turbo Pascal BGI Tutorial: Dynamic Drivers, Linked Drivers, and Diagnostic Harnesses</title>
      <link>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-bgi-tutorial-dynamic-drivers-linked-drivers-and-diagnostic-harnesses/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Mon, 09 Mar 2026 09:46:27 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-bgi-tutorial-dynamic-drivers-linked-drivers-and-diagnostic-harnesses/</guid>
      <description>&lt;p&gt;This tutorial gives you a practical BGI workflow that survives deployment:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;dynamic driver loading from filesystem&lt;/li&gt;
&lt;li&gt;linked-driver strategy for lower runtime dependency risk&lt;/li&gt;
&lt;li&gt;a minimal diagnostics harness for startup failures&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;preflight-what-you-need&#34;&gt;Preflight: what you need&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Turbo Pascal / Borland Pascal environment with &lt;code&gt;Graph&lt;/code&gt; unit&lt;/li&gt;
&lt;li&gt;one known-good BGI driver set and required &lt;code&gt;.CHR&lt;/code&gt; fonts&lt;/li&gt;
&lt;li&gt;a test machine/profile where paths are not identical to dev directories&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TP5 baseline reminder:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;compile needs &lt;code&gt;GRAPH.TPU&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;runtime needs &lt;code&gt;.BGI&lt;/code&gt; drivers&lt;/li&gt;
&lt;li&gt;stroked fonts need &lt;code&gt;.CHR&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;step-1-dynamic-loading-baseline&#34;&gt;Step 1: dynamic loading baseline&lt;/h2&gt;
&lt;p&gt;Create &lt;code&gt;BGITEST.PAS&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;program BgiTest;

uses
  Graph, Crt;

var
  gd, gm, gr: Integer;

begin
  gd := Detect;
  InitGraph(gd, gm, &amp;#39;.\BGI&amp;#39;);
  gr := GraphResult;
  Writeln(&amp;#39;Driver=&amp;#39;, gd, &amp;#39; Mode=&amp;#39;, gm, &amp;#39; GraphResult=&amp;#39;, gr);
  if gr &amp;lt;&amp;gt; grOk then
    Halt(1);

  SetColor(15);
  OutTextXY(8, 8, &amp;#39;BGI OK&amp;#39;);
  Rectangle(20, 20, 200, 120);
  ReadKey;
  CloseGraph;
end.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Expected outcome:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;with correct path/assets: startup succeeds and simple frame draws&lt;/li&gt;
&lt;li&gt;with missing assets: &lt;code&gt;GraphResult&lt;/code&gt; indicates error and program exits cleanly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Important TP5 behavior: &lt;code&gt;GraphResult&lt;/code&gt; resets to zero after being called. Always
store it to a variable once, then evaluate that value.&lt;/p&gt;
&lt;p&gt;Path behavior detail: if &lt;code&gt;InitGraph(..., PathToDriver)&lt;/code&gt; gets an empty path, the
driver files must be in the current directory.&lt;/p&gt;
&lt;h2 id=&#34;step-2-deployment-discipline-for-dynamic-model&#34;&gt;Step 2: deployment discipline for dynamic model&lt;/h2&gt;
&lt;p&gt;Package checklist:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;executable&lt;/li&gt;
&lt;li&gt;all required &lt;code&gt;.BGI&lt;/code&gt; files for target adapters&lt;/li&gt;
&lt;li&gt;all required &lt;code&gt;.CHR&lt;/code&gt; fonts&lt;/li&gt;
&lt;li&gt;documented runtime path policy&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most &amp;ldquo;BGI bugs&amp;rdquo; are missing files or wrong path assumptions.&lt;/p&gt;
&lt;h2 id=&#34;step-3-linked-driver-strategy-when-you-need-robustness&#34;&gt;Step 3: linked-driver strategy (when you need robustness)&lt;/h2&gt;
&lt;p&gt;Some Borland-era setups support converting/linking BGI driver binaries into
object modules and registering them before &lt;code&gt;InitGraph&lt;/code&gt; (for example through
&lt;code&gt;RegisterBGIdriver&lt;/code&gt; and related registration APIs).&lt;/p&gt;
&lt;p&gt;General workflow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;run &lt;code&gt;BINOBJ&lt;/code&gt; on &lt;code&gt;.BGI&lt;/code&gt; file(s) to get &lt;code&gt;.OBJ&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;link &lt;code&gt;.OBJ&lt;/code&gt; file(s) into program&lt;/li&gt;
&lt;li&gt;call &lt;code&gt;RegisterBGIdriver&lt;/code&gt; before &lt;code&gt;InitGraph&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;call &lt;code&gt;InitGraph&lt;/code&gt; and verify &lt;code&gt;GraphResult&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Why teams did this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fewer runtime file dependencies&lt;/li&gt;
&lt;li&gt;simpler deployment to constrained/chaotic DOS installations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tradeoff:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;larger executable and tighter build coupling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ordering constraint from TP5 docs: calling &lt;code&gt;RegisterBGIdriver&lt;/code&gt; after graphics
are already active yields &lt;code&gt;grError&lt;/code&gt; (&lt;code&gt;-11&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;If you use &lt;code&gt;InstallUserDriver&lt;/code&gt; with an autodetect callback, TP5 expects that
callback to be a FAR-call function with no parameters returning an integer mode
or &lt;code&gt;grError&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;step-4-diagnostics-harness-you-should-keep-forever&#34;&gt;Step 4: diagnostics harness you should keep forever&lt;/h2&gt;
&lt;p&gt;Keep a dedicated harness separate from game/app engine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;prints detected driver/mode and &lt;code&gt;GraphResult&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;renders one line, one rectangle, one text string&lt;/li&gt;
&lt;li&gt;exits on keypress&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This lets you quickly answer: &amp;ldquo;is graphics stack alive?&amp;rdquo; before debugging your
full renderer.&lt;/p&gt;
&lt;p&gt;Add one negative test here too: intentionally pass wrong mode for a known
driver and verify expected &lt;code&gt;grInvalidMode&lt;/code&gt; (&lt;code&gt;-10&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&#34;step-5-test-matrix-predict-first-then-run&#34;&gt;Step 5: test matrix (predict first, then run)&lt;/h2&gt;
&lt;p&gt;Define expected outcomes before running each case:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;correct BGI path&lt;/li&gt;
&lt;li&gt;missing driver file&lt;/li&gt;
&lt;li&gt;missing font file&lt;/li&gt;
&lt;li&gt;wrong current directory&lt;/li&gt;
&lt;li&gt;TSR-heavy memory profile&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For each case, record:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;startup status&lt;/li&gt;
&lt;li&gt;exact error code/output&lt;/li&gt;
&lt;li&gt;whether fallback path triggers correctly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recommended TP5 error codes to classify in logs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;grNotDetected&lt;/code&gt; (&lt;code&gt;-2&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;grFileNotFound&lt;/code&gt; (&lt;code&gt;-3&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;grInvalidDriver&lt;/code&gt; (&lt;code&gt;-4&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;grNoLoadMem&lt;/code&gt; (&lt;code&gt;-5&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;grFontNotFound&lt;/code&gt; (&lt;code&gt;-8&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;grNoFontMem&lt;/code&gt; (&lt;code&gt;-9&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;grInvalidMode&lt;/code&gt; (&lt;code&gt;-10&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;step-6-fallback-policy-for-production-ish-dos-apps&#34;&gt;Step 6: fallback policy for production-ish DOS apps&lt;/h2&gt;
&lt;p&gt;Never rely on detect-only logic without fallback:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;try preferred mode&lt;/li&gt;
&lt;li&gt;fallback to known-safe mode&lt;/li&gt;
&lt;li&gt;print actionable error if both fail&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A black screen is a product bug, even in retro projects.&lt;/p&gt;
&lt;h2 id=&#34;about-creating-custom-bgi-drivers&#34;&gt;About creating custom BGI drivers&lt;/h2&gt;
&lt;p&gt;Writing full custom BGI drivers is advanced and depends on ABI/tooling details
that are often version-specific and poorly documented. Practical teams usually
ship stock drivers (dynamic or linked) unless there is a hard requirement for
new hardware support.&lt;/p&gt;
&lt;p&gt;If you must go custom, treat it as a separate reverse-engineering project with
its own test harnesses and compatibility matrix.&lt;/p&gt;
&lt;h2 id=&#34;integration-notes-with-overlays-and-memory-strategy&#34;&gt;Integration notes with overlays and memory strategy&lt;/h2&gt;
&lt;p&gt;If graphics startup becomes unstable after enabling overlays:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;verify overlay initialization order&lt;/li&gt;
&lt;li&gt;verify memory headroom before &lt;code&gt;InitGraph&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;test graphics harness independently from overlayed application paths&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This avoids mixing two failure domains during triage.&lt;/p&gt;
&lt;p&gt;Memory interaction note from TP5 docs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Graph allocates heap memory for graphics buffer/driver/font paths&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OvrSetBuf&lt;/code&gt; also reshapes memory by shrinking heap&lt;/li&gt;
&lt;li&gt;call order matters (&lt;code&gt;OvrSetBuf&lt;/code&gt; before &lt;code&gt;InitGraph&lt;/code&gt; when both are used)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-4-graphics-drivers-bgi-and-rendering-integration/&#34;&gt;Turbo Pascal Toolchain, Part 4: Graphics Drivers, BGI, and Rendering Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-3-overlays-memory-models-and-link-strategy/&#34;&gt;Turbo Pascal Toolchain, Part 3: Overlays, Memory Models, and Link Strategy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/mode-13h-graphics-in-turbo-pascal/&#34;&gt;Mode 13h Graphics in Turbo Pascal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Turbo Pascal History Through Tooling Decisions</title>
      <link>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-history-through-tooling/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Mon, 09 Mar 2026 09:46:27 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-history-through-tooling/</guid>
      <description>&lt;p&gt;People often tell Turbo Pascal history as a sequence of versions and release dates. That timeline matters, but it misses why the tool changed habits so deeply. The real story is tooling ergonomics under constraints: compile speed, predictable output, integrated editing, and a workflow that kept intention intact from keystroke to executable.&lt;/p&gt;
&lt;p&gt;In other words, Turbo Pascal was not only a language product. It was a decision system.&lt;/p&gt;
&lt;h2 id=&#34;why-that-era-felt-so-productive&#34;&gt;Why that era felt so productive&lt;/h2&gt;
&lt;p&gt;The key loop was short and visible:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;edit in integrated environment&lt;/li&gt;
&lt;li&gt;compile in seconds&lt;/li&gt;
&lt;li&gt;run immediately&lt;/li&gt;
&lt;li&gt;inspect result and repeat&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No hidden dependency graph. No plugin negotiation. No remote service in the critical path. This reduced context switching in ways modern teams still struggle to recover through process design.&lt;/p&gt;
&lt;p&gt;The historical importance is not nostalgia. It is evidence that feedback-loop economics shape software quality more than fashionable architecture slogans.&lt;/p&gt;
&lt;h2 id=&#34;distribution-shaped-engineering-choices&#34;&gt;Distribution shaped engineering choices&lt;/h2&gt;
&lt;p&gt;In floppy-era ecosystems, distribution size and hardware variability were not side concerns. They drove design:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;smaller executables reduced install friction&lt;/li&gt;
&lt;li&gt;deterministic startup mattered on mixed hardware&lt;/li&gt;
&lt;li&gt;clear error paths mattered without telemetry backends&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Turbo Pascal&amp;rsquo;s model rewarded explicit interfaces and compact runtime assumptions. Teams that wanted software to survive wild machine diversity had to be precise.&lt;/p&gt;
&lt;h2 id=&#34;unit-system-as-collaboration-contract&#34;&gt;Unit system as collaboration contract&lt;/h2&gt;
&lt;p&gt;Turbo Pascal units gave teams strong boundaries without heavy ceremony. A unit interface section became a living contract, and the implementation section held the details. This mirrors modern module design principles, but with less boilerplate and fewer moving parts.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;unit ClockFmt;

interface
function IsoTime: string;

implementation
function IsoTime: string;
begin
  IsoTime := &amp;#39;2026-02-22T12:34:56&amp;#39;;
end;

end.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Simple pattern, strong effect: contracts became visible and stable.&lt;/p&gt;
&lt;h2 id=&#34;build-behavior-and-trust&#34;&gt;Build behavior and trust&lt;/h2&gt;
&lt;p&gt;One under-discussed historical factor is trust in the build result. Turbo Pascal gave developers strong confidence that what compiled now would run now on the same target profile. This reliability reduced defensive ritual and encouraged experimentation.&lt;/p&gt;
&lt;p&gt;When build systems are unpredictable, teams compensate with process overhead: additional reviews, duplicated staging checks, expanded manual validation. Predictable tooling is not just convenience; it is organizational cost control.&lt;/p&gt;
&lt;h2 id=&#34;debugging-as-craft-not-ceremony&#34;&gt;Debugging as craft, not ceremony&lt;/h2&gt;
&lt;p&gt;Classic debugging in this ecosystem leaned on watch windows, deterministic repro paths, and explicit state inspection. Because the runtime stack was smaller, developers were closer to cause and effect. Failures were painful, but usually legible.&lt;/p&gt;
&lt;p&gt;That legibility is historically important. It built strong mental models in generations of engineers who later carried those habits into network systems, embedded work, and security tooling.&lt;/p&gt;
&lt;h2 id=&#34;what-modern-teams-can-still-steal&#34;&gt;What modern teams can still steal&lt;/h2&gt;
&lt;p&gt;You do not need to abandon modern stacks to learn from this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;optimize for short local feedback loops&lt;/li&gt;
&lt;li&gt;keep module contracts obvious&lt;/li&gt;
&lt;li&gt;reduce hidden build indirection&lt;/li&gt;
&lt;li&gt;separate policy from mechanism in config files&lt;/li&gt;
&lt;li&gt;document assumptions where runtime variability is high&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the same themes behind &lt;a href=&#34;https://turbovision.in6-addr.net/musings/clarity-is-an-operational-advantage/&#34;&gt;Clarity Is an Operational Advantage&lt;/a&gt; and &lt;a href=&#34;https://turbovision.in6-addr.net/hacking/tools/terminal-kits-for-incident-triage/&#34;&gt;Terminal Kits for Incident Triage&lt;/a&gt;, just seen through retro tooling history.&lt;/p&gt;
&lt;h2 id=&#34;tooling-history-as-systems-history&#34;&gt;Tooling history as systems history&lt;/h2&gt;
&lt;p&gt;Turbo Pascal&amp;rsquo;s relevance endures because it compresses essential engineering lessons into a small environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;architecture is influenced by tool friction&lt;/li&gt;
&lt;li&gt;reliability is influenced by startup discipline&lt;/li&gt;
&lt;li&gt;collaboration quality is influenced by interface clarity&lt;/li&gt;
&lt;li&gt;speed is influenced by feedback-loop latency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those lessons are historical facts and current strategy at the same time.&lt;/p&gt;
&lt;h2 id=&#34;practical-way-to-study-it-now&#34;&gt;Practical way to study it now&lt;/h2&gt;
&lt;p&gt;If you want something concrete, recreate one small project with strict boundaries:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;one executable&lt;/li&gt;
&lt;li&gt;three units max&lt;/li&gt;
&lt;li&gt;explicit config file&lt;/li&gt;
&lt;li&gt;measured compile-run cycle&lt;/li&gt;
&lt;li&gt;one regression checklist file&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then compare your decision speed and bug triage quality against a similar modern project. Treat this as an experiment, not ideology.&lt;/p&gt;
&lt;p&gt;Cross-reference starting points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-in-2025/&#34;&gt;Writing Turbo Pascal in 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-before-the-web/&#34;&gt;Turbo Pascal Before the Web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/mode-13h-graphics-in-turbo-pascal/&#34;&gt;Mode 13h Graphics in Turbo Pascal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;History is most useful when it changes present behavior. Turbo Pascal still does that unusually well because the system is small enough to understand and strict enough to teach.&lt;/p&gt;
&lt;p&gt;A useful closing exercise is to measure your own feedback loop in minutes, not feelings. When teams quantify loop time, tooling discussions become clearer and less ideological.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Turbo Pascal Overlay Tutorial: Build, Package, and Debug an OVR Application</title>
      <link>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-overlay-tutorial-build-package-and-debug-an-ovr-application/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Mon, 09 Mar 2026 09:46:27 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-overlay-tutorial-build-package-and-debug-an-ovr-application/</guid>
      <description>&lt;p&gt;This tutorial is intentionally practical. You will build a small Turbo Pascal
program with one resident path and one overlayed path, then test deployment and
failure behavior.&lt;/p&gt;
&lt;p&gt;If your install names/options differ, keep the process and adapt the exact menu
or command names.&lt;/p&gt;
&lt;h2 id=&#34;goal-and-expected-outcomes&#34;&gt;Goal and expected outcomes&lt;/h2&gt;
&lt;p&gt;Goal: move a cold code path out of always-resident memory and verify it loads
on demand from &lt;code&gt;.OVR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Expected outcomes before you start:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;build output includes both &lt;code&gt;.EXE&lt;/code&gt; and &lt;code&gt;.OVR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;startup succeeds only when overlay initialization succeeds&lt;/li&gt;
&lt;li&gt;cold feature call has first-hit latency and warm-hit improvement&lt;/li&gt;
&lt;li&gt;removing &lt;code&gt;.OVR&lt;/code&gt; produces controlled error path, not random crash&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;minimal-project-layout&#34;&gt;Minimal project layout&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OVRDEMO/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  MAIN.PAS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  REPORTS.PAS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  BUILD.BAT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;step-1-write-resident-core-and-cold-module&#34;&gt;Step 1: write resident core and cold module&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;REPORTS.PAS&lt;/code&gt; (cold path candidate):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;{$O+}  { TP5 requirement: unit may be overlaid }
{$F+}  { TP5 requirement for safe calls in overlaid programs }
unit Reports;

interface
procedure RunMonthlyReport;

implementation

procedure RunMonthlyReport;
var
  I: Integer;
  S: LongInt;
begin
  S := 0;
  for I := 1 to 25000 do
    S := S + I;
end;

end.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;MAIN.PAS&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;program OvrDemo;
{$F+}  { TP5: use FAR call model in non-overlaid code as well }
{$O+}  { keep overlay directives enabled in this module }

uses
  Overlay, Crt, Dos, Reports;
{$O Reports}  { select this used unit for overlay linking }

var
  Ch: Char;
  ExeDir, ExeName, ExeExt: PathStr;
  OvrFile: PathStr;

procedure InitOverlays;
begin
  FSplit(ParamStr(0), ExeDir, ExeName, ExeExt);
  OvrFile := ExeDir + ExeName + &amp;#39;.OVR&amp;#39;;
  OvrInit(OvrFile);
  if OvrResult &amp;lt;&amp;gt; ovrOk then
  begin
    Writeln(&amp;#39;Overlay init failed for &amp;#39;, OvrFile, &amp;#39;, code=&amp;#39;, OvrResult);
    Halt(1);
  end;
  OvrSetBuf(60000);
end;

begin
  InitOverlays;
  Writeln(&amp;#39;Press R to run report, ESC to exit&amp;#39;);
  repeat
    Ch := ReadKey;
    case UpCase(Ch) of
      &amp;#39;R&amp;#39;:
        begin
          Writeln(&amp;#39;Running report...&amp;#39;);
          RunMonthlyReport;
          Writeln(&amp;#39;Done.&amp;#39;);
        end;
    end;
  until Ch = #27;
end.&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;step-2-enable-overlay-policy&#34;&gt;Step 2: enable overlay policy&lt;/h2&gt;
&lt;p&gt;Overlay output is not triggered by &lt;code&gt;uses Overlay&lt;/code&gt; alone. You need both:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;mark unit as overlay-eligible at compile time&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;select unit for overlaying from the main program&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For &lt;strong&gt;Turbo Pascal 5.0&lt;/strong&gt; (per Reference Guide), these are hard rules:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;all overlaid units must be compiled with &lt;code&gt;{$O+}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;active call chain must use FAR call model in overlaid programs&lt;/li&gt;
&lt;li&gt;practical safe pattern: &lt;code&gt;{$O+,F+}&lt;/code&gt; in overlaid units, &lt;code&gt;{$F+}&lt;/code&gt; in other units and main&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{$O UnitName}&lt;/code&gt; must appear after &lt;code&gt;uses&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uses&lt;/code&gt; must name &lt;code&gt;Overlay&lt;/code&gt; before any overlaid unit&lt;/li&gt;
&lt;li&gt;build must be to disk (not memory)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The full &lt;code&gt;REPORTS.PAS&lt;/code&gt; and &lt;code&gt;MAIN.PAS&lt;/code&gt; examples above include these directives
directly.&lt;/p&gt;
&lt;h3 id=&#34;why-o-exists-tp5-technical-reason&#34;&gt;Why &lt;code&gt;{$O+}&lt;/code&gt; exists (TP5 technical reason)&lt;/h3&gt;
&lt;p&gt;In TP5, &lt;code&gt;{$O+}&lt;/code&gt; is not just a &amp;ldquo;permission bit&amp;rdquo; for overlaying. It also changes
code generation for calls between overlaid units to keep parameter pointers safe.&lt;/p&gt;
&lt;p&gt;Classic hazard:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;caller unit passes pointer to a code-segment-based constant (for example a
string/set constant)&lt;/li&gt;
&lt;li&gt;callee is in another overlaid unit&lt;/li&gt;
&lt;li&gt;overlay swap can overwrite caller code segment region&lt;/li&gt;
&lt;li&gt;raw pointer becomes invalid&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TP5 &lt;code&gt;{$O+}&lt;/code&gt;-aware code generation mitigates this by copying such constants into
stack temporaries before passing pointers in overlaid-to-overlaid scenarios.&lt;/p&gt;
&lt;p&gt;Typical source-level shape:&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;REPORTS.PAS&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;{$O+}  { TP5 mandatory for overlaid units }
{$F+}  { TP5 FAR-call requirement }
unit Reports;
...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In &lt;code&gt;MAIN.PAS&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;program OvrDemo;
uses Overlay, Crt, Dos, Reports;
{$O Reports}  { overlay unit-name directive: mark Reports for overlay link }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Without the unit-name selection (&lt;code&gt;{$O Reports}&lt;/code&gt; or equivalent IDE setting), the
unit can stay fully linked into the EXE even if &lt;code&gt;{$O+}&lt;/code&gt; is present.&lt;/p&gt;
&lt;p&gt;TP5 constraint from the same documentation set: among standard units, only &lt;code&gt;Dos&lt;/code&gt;
is overlayable; &lt;code&gt;System&lt;/code&gt;, &lt;code&gt;Overlay&lt;/code&gt;, &lt;code&gt;Crt&lt;/code&gt;, &lt;code&gt;Graph&lt;/code&gt;, &lt;code&gt;Turbo3&lt;/code&gt;, and &lt;code&gt;Graph3&lt;/code&gt;
cannot be overlaid.&lt;/p&gt;
&lt;h2 id=&#34;step-25-when-the-ovr-file-is-actually-created&#34;&gt;Step 2.5: when the &lt;code&gt;.OVR&lt;/code&gt; file is actually created&lt;/h2&gt;
&lt;p&gt;This is the key technical point that is often misunderstood:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;REPORTS.PAS&lt;/code&gt; compiles to &lt;code&gt;REPORTS.TPU&lt;/code&gt; (unit artifact).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MAIN.PAS&lt;/code&gt; is compiled and then linked with all used units.&lt;/li&gt;
&lt;li&gt;During &lt;strong&gt;link&lt;/strong&gt;, overlay-managed code is split out and written to one overlay file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So &lt;code&gt;.OVR&lt;/code&gt; is a &lt;strong&gt;link-time output&lt;/strong&gt;, not a unit-compile output.&lt;/p&gt;
&lt;h3 id=&#34;how-code-is-selected-into-ovr&#34;&gt;How code is selected into &lt;code&gt;.OVR&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Selection is not by &amp;ldquo;file extension magic&amp;rdquo; and not by &lt;code&gt;uses Overlay&lt;/code&gt;. The link
pipeline does this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;mark used code blocks from reachable entry points&lt;/li&gt;
&lt;li&gt;check units marked for overlaying (via overlay unit-name directive/options)&lt;/li&gt;
&lt;li&gt;for callable routines in those units, emit call stubs in EXE and write
overlayed code blocks to &lt;code&gt;.OVR&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unused routines can be omitted entirely&lt;/li&gt;
&lt;li&gt;selected routines from one or more units can end up in the same &lt;code&gt;.OVR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;unit selection is explicit, routine placement is linker-driven from that set&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;naming-rule&#34;&gt;Naming rule&lt;/h3&gt;
&lt;p&gt;The overlay file is tied to the final executable base name, not to a single unit.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;compile/link target &lt;code&gt;MAIN.EXE&lt;/code&gt; -&amp;gt; overlay file &lt;code&gt;MAIN.OVR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;compile/link target &lt;code&gt;APP.EXE&lt;/code&gt; -&amp;gt; overlay file &lt;code&gt;APP.OVR&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;REPORTS.OVR&lt;/code&gt; just because &lt;code&gt;Reports&lt;/code&gt; contains overlayed routines.
One executable can include overlayed code from multiple units, and they are
packed into that executable&amp;rsquo;s single overlay payload.&lt;/p&gt;
&lt;h3 id=&#34;when-ovr-may-not-appear&#34;&gt;When &lt;code&gt;.OVR&lt;/code&gt; may not appear&lt;/h3&gt;
&lt;p&gt;If no code is actually emitted as overlayed in the final link result, no &lt;code&gt;.OVR&lt;/code&gt;
file is produced. In that case, check project options/directives first.&lt;/p&gt;
&lt;h2 id=&#34;step-3-build-and-verify-artifacts&#34;&gt;Step 3: build and verify artifacts&lt;/h2&gt;
&lt;p&gt;Build with your normal tool path (IDE or CLI). After successful build:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;verify your output executable exists (for example &lt;code&gt;MAIN.EXE&lt;/code&gt; if compiling &lt;code&gt;MAIN.PAS&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;verify matching overlay file exists with the same base name (for example &lt;code&gt;MAIN.OVR&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;record file sizes and timestamp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If &lt;code&gt;.OVR&lt;/code&gt; is missing, your overlay profile is not active.&lt;/p&gt;
&lt;h2 id=&#34;step-4-runtime-tests&#34;&gt;Step 4: runtime tests&lt;/h2&gt;
&lt;h3 id=&#34;test-a---healthy-run&#34;&gt;Test A - healthy run&lt;/h3&gt;
&lt;p&gt;Expected:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;startup prints no overlay error&lt;/li&gt;
&lt;li&gt;first &lt;code&gt;R&lt;/code&gt; call may be slower&lt;/li&gt;
&lt;li&gt;repeated &lt;code&gt;R&lt;/code&gt; calls are often faster (buffer reuse)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;test-b---missing-ovr&#34;&gt;Test B - missing OVR&lt;/h3&gt;
&lt;p&gt;Temporarily rename the generated overlay file (for example &lt;code&gt;MAIN.OVR&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Expected:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;startup exits with explicit overlay init error&lt;/li&gt;
&lt;li&gt;no undefined behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If it crashes instead, fix error handling before continuing.&lt;/p&gt;
&lt;h2 id=&#34;step-45-initialization-variants-ovrinit-ovrinitems-ovrsetbuf&#34;&gt;Step 4.5: initialization variants (&lt;code&gt;OvrInit&lt;/code&gt;, &lt;code&gt;OvrInitEMS&lt;/code&gt;, &lt;code&gt;OvrSetBuf&lt;/code&gt;)&lt;/h2&gt;
&lt;p&gt;Minimal initialization:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;OvrInit(OvrFile);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If initialization fails and you still call an overlaid routine, TP5 behavior is
runtime failure (the reference guide calls out runtime error 208).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OvrInit&lt;/code&gt; practical lookup behavior (TP5): if &lt;code&gt;OvrFile&lt;/code&gt; has no drive/path, the
manager searches current directory, then EXE directory (DOS 3.x), then &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OvrInit&lt;/code&gt; result handling (&lt;code&gt;OvrResult&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ovrOk&lt;/code&gt;: initialized&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ovrNotFound&lt;/code&gt;: overlay file not found&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ovrError&lt;/code&gt;: invalid overlay format or program has no overlays&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;EMS-assisted initialization:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;OvrInit(OvrFile);
OvrInitEMS;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;OvrInitEMS&lt;/code&gt; can move overlay backing storage to EMS (when available), but
execution still requires copying overlays into the normal-memory overlay buffer.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OvrInitEMS&lt;/code&gt; result handling (&lt;code&gt;OvrResult&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ovrOk&lt;/code&gt;: overlays loaded into EMS&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ovrIOError&lt;/code&gt;: read error while loading overlay file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ovrNoEMSDriver&lt;/code&gt;: no EMS driver detected&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ovrNoEMSMemory&lt;/code&gt;: insufficient free EMS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On &lt;code&gt;OvrInitEMS&lt;/code&gt; errors, overlay manager still runs from disk-backed loading.&lt;/p&gt;
&lt;p&gt;Buffer sizing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TP5 starts with a minimal overlay buffer (large enough for largest overlay).&lt;/li&gt;
&lt;li&gt;For cross-calling overlay groups, this can cause excessive swapping.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OvrSetBuf&lt;/code&gt; increases buffer by shrinking heap.&lt;/li&gt;
&lt;li&gt;legal range (TP5): &lt;code&gt;BufSize &amp;gt;= initial&lt;/code&gt; and &lt;code&gt;BufSize &amp;lt;= MemAvail + OvrGetBuf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;if you increase buffer, adjust &lt;code&gt;{$M ...}&lt;/code&gt; heap minimum accordingly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Important ordering rule (TP5): call &lt;code&gt;OvrSetBuf&lt;/code&gt; while heap is effectively empty.
If using Graph, call &lt;code&gt;OvrSetBuf&lt;/code&gt; before &lt;code&gt;InitGraph&lt;/code&gt;, because &lt;code&gt;InitGraph&lt;/code&gt; allocates
heap memory and can prevent buffer growth.&lt;/p&gt;
&lt;h2 id=&#34;step-5-tune-overlay-buffer-with-measurement&#34;&gt;Step 5: tune overlay buffer with measurement&lt;/h2&gt;
&lt;p&gt;Run the same interaction script while changing &lt;code&gt;OvrSetBuf&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;small buffer (for example 16K)&lt;/li&gt;
&lt;li&gt;medium buffer (for example 32K)&lt;/li&gt;
&lt;li&gt;larger buffer (for example 60K)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Expected pattern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;too small: frequent reload stalls&lt;/li&gt;
&lt;li&gt;too large: less stall, but memory pressure elsewhere&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choose by measured latency and memory headroom, not by guess.&lt;/p&gt;
&lt;h2 id=&#34;step-6-boundary-correction-when-overlay-thrashes&#34;&gt;Step 6: boundary correction when overlay thrashes&lt;/h2&gt;
&lt;p&gt;If one action triggers repeated slowdowns:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;move shared helpers from overlay unit to resident unit&lt;/li&gt;
&lt;li&gt;keep deep cold logic in overlay unit&lt;/li&gt;
&lt;li&gt;reduce cross-calls between overlay units&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Overlay design is call-graph design.&lt;/p&gt;
&lt;h2 id=&#34;troubleshooting-matrix&#34;&gt;Troubleshooting matrix&lt;/h2&gt;
&lt;h3 id=&#34;symptom-unresolved-symbol-at-link&#34;&gt;Symptom: unresolved symbol at link&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;check unit/object participation in link graph&lt;/li&gt;
&lt;li&gt;check far/near and declaration compatibility&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;symptom-startup-overlay-error&#34;&gt;Symptom: startup overlay error&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;check &lt;code&gt;.OVR&lt;/code&gt; filename/path assumptions&lt;/li&gt;
&lt;li&gt;check deployment directory, not just dev directory&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;symptom-intermittent-slowdown&#34;&gt;Symptom: intermittent slowdown&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;profile call path for overlay churn&lt;/li&gt;
&lt;li&gt;increase buffer or move hot helpers resident&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;what-this-tutorial-teaches-beyond-overlays&#34;&gt;What this tutorial teaches beyond overlays&lt;/h2&gt;
&lt;p&gt;You practice four skills that transfer everywhere:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;define expected behavior before test&lt;/li&gt;
&lt;li&gt;verify artifact set before runtime&lt;/li&gt;
&lt;li&gt;isolate runtime dependencies explicitly&lt;/li&gt;
&lt;li&gt;tune with measured data, not assumptions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-3-overlays-memory-models-and-link-strategy/&#34;&gt;Turbo Pascal Toolchain, Part 3: Overlays, Memory Models, and Link Strategy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-2-objects-units-and-binary-investigation/&#34;&gt;Turbo Pascal Toolchain, Part 2: Objects, Units, and Binary Investigation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Turbo Pascal Units as Architecture, Not Just Reuse</title>
      <link>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-units-as-architecture/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Mon, 09 Mar 2026 09:46:27 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-units-as-architecture/</guid>
      <description>&lt;p&gt;Most people first meet Turbo Pascal units as &amp;ldquo;how to avoid copy-pasting procedures.&amp;rdquo; That is true and incomplete. In real projects, units are architecture boundaries. They define what the rest of the system is allowed to know, hide what can change, and make refactoring survivable under pressure.&lt;/p&gt;
&lt;p&gt;In constrained DOS projects, this was not academic design purity. It was the difference between shipping and debugging forever.&lt;/p&gt;
&lt;h2 id=&#34;interface-section-is-a-contract-surface&#34;&gt;Interface section is a contract surface&lt;/h2&gt;
&lt;p&gt;A good unit interface exposes minimal, stable operations. It does not leak storage details, timing internals, or helper routines with unclear ownership. You can read the interface as a capability map.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;unit RenderCore;

interface
procedure BeginFrame;
procedure DrawSprite(X, Y, Id: Integer);
procedure EndFrame;

implementation
{ internal page selection, clipping, palette handling }
end.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice what is missing: page indices, raw VGA register details, sprite memory layout. Those remain private so callers cannot create illegal states casually.&lt;/p&gt;
&lt;h2 id=&#34;separation-patterns-that-work&#34;&gt;Separation patterns that work&lt;/h2&gt;
&lt;p&gt;A practical retro project often benefits from explicit layers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SysCfg&lt;/code&gt;: startup profile, paths, feature flags&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Input&lt;/code&gt;: keyboard state and edge detection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RenderCore&lt;/code&gt;: page lifecycle and primitives&lt;/li&gt;
&lt;li&gt;&lt;code&gt;World&lt;/code&gt;: simulation and collision&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UiHud&lt;/code&gt;: overlays independent of camera&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each layer exports what the next layer needs, and no more.&lt;/p&gt;
&lt;p&gt;This is still modern architecture wisdom, just with smaller tools.&lt;/p&gt;
&lt;h2 id=&#34;compile-time-feedback-as-architecture-feedback&#34;&gt;Compile-time feedback as architecture feedback&lt;/h2&gt;
&lt;p&gt;One advantage of strong unit boundaries: breakage appears quickly at compile time. If you change a function signature in one interface, all dependent call sites surface immediately. That pressure encourages deliberate changes rather than implicit behavior drift.&lt;/p&gt;
&lt;p&gt;When architecture boundaries are vague, breakage tends to become runtime surprises. In DOS-era loops, compile-time certainty was a strategic advantage.&lt;/p&gt;
&lt;h2 id=&#34;state-ownership-rules&#34;&gt;State ownership rules&lt;/h2&gt;
&lt;p&gt;Global variables are tempting in small projects. They also erase accountability. Better pattern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;each unit owns its state&lt;/li&gt;
&lt;li&gt;mutation happens through explicit procedures&lt;/li&gt;
&lt;li&gt;read-only queries are exposed as functions&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;unit FrameClock;

interface
procedure Tick;
function FrameCount: LongInt;

implementation
var
  GFrameCount: LongInt;

procedure Tick;
begin
  Inc(GFrameCount);
end;

function FrameCount: LongInt;
begin
  FrameCount := GFrameCount;
end;
end.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This small discipline scales surprisingly far.&lt;/p&gt;
&lt;h2 id=&#34;circular-dependencies-are-architecture-warnings&#34;&gt;Circular dependencies are architecture warnings&lt;/h2&gt;
&lt;p&gt;If Unit A needs Unit B and B needs A, the system is signaling a design issue. In Turbo Pascal this becomes obvious quickly because cycles are painful. Use that pain as feedback:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;extract shared abstractions into Unit C&lt;/li&gt;
&lt;li&gt;invert direction of calls through callback interfaces&lt;/li&gt;
&lt;li&gt;move policy decisions up a layer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The language/tooling friction nudges you toward cleaner dependency graphs.&lt;/p&gt;
&lt;h2 id=&#34;testing-mindset-without-modern-frameworks&#34;&gt;Testing mindset without modern frameworks&lt;/h2&gt;
&lt;p&gt;Even without a test framework, you can create deterministic validation by small harness units:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fixture setup procedure&lt;/li&gt;
&lt;li&gt;operation call&lt;/li&gt;
&lt;li&gt;assertion-like result check&lt;/li&gt;
&lt;li&gt;text output summary&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key is isolating seams through interfaces. If a rendering unit can be called with prepared buffers and predictable state, manual regression checks become cheap and reliable.&lt;/p&gt;
&lt;h2 id=&#34;architecture-and-performance-are-not-enemies&#34;&gt;Architecture and performance are not enemies&lt;/h2&gt;
&lt;p&gt;Some developers fear unit boundaries will cost speed. In most DOS-scale projects, the bigger performance wins come from algorithm choice and memory locality, not from collapsing all code into one monolith. Clear units help you identify hot paths accurately and optimize where it matters.&lt;/p&gt;
&lt;p&gt;For example, keeping low-level pixel paths inside &lt;code&gt;RenderCore&lt;/code&gt; makes targeted optimization straightforward while preserving clean call sites elsewhere.&lt;/p&gt;
&lt;h2 id=&#34;cross-references-in-this-project&#34;&gt;Cross references in this project&lt;/h2&gt;
&lt;p&gt;These articles show the same pattern from different angles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/modex/modex-part-1-planar-memory-model/&#34;&gt;Mode X Part 1: Planar Memory and Pages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/modex/modex-part-4-tilemaps-and-streaming/&#34;&gt;Mode X Part 4: Tilemaps and Streaming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/config-sys-as-architecture/&#34;&gt;CONFIG.SYS as Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/musings/the-cost-of-unclear-interfaces/&#34;&gt;The Cost of Unclear Interfaces&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Different domains, same operational truth: explicit boundaries reduce failure ambiguity.&lt;/p&gt;
&lt;h2 id=&#34;a-migration-strategy-for-messy-codebases&#34;&gt;A migration strategy for messy codebases&lt;/h2&gt;
&lt;p&gt;If you already have a tangled Pascal codebase, do not rewrite everything. Do staged extraction:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;identify one unstable subsystem&lt;/li&gt;
&lt;li&gt;define minimal interface for it&lt;/li&gt;
&lt;li&gt;move internals behind unit boundary&lt;/li&gt;
&lt;li&gt;replace direct global access with explicit calls&lt;/li&gt;
&lt;li&gt;repeat for next subsystem&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach keeps software running while architecture improves incrementally.&lt;/p&gt;
&lt;p&gt;Turbo Pascal units are sometimes framed as nostalgic language features. They are better understood as practical architecture tools with excellent signal-to-noise ratio. Under constraints, that ratio is everything.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Writing Turbo Pascal in 2025</title>
      <link>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-in-2025/</link>
      <pubDate>Sun, 19 Oct 2025 00:00:00 +0000</pubDate>
      <lastBuildDate>Mon, 09 Mar 2026 09:46:27 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-in-2025/</guid>
      <description>&lt;p&gt;Turbo Pascal 7.0 still compiles in under a second on a 486. On DOSBox-X
running on modern hardware, it&amp;rsquo;s instantaneous. The IDE — blue background,
yellow text, pull-down menus — is the direct ancestor of the Turbo Vision
library that inspired this site&amp;rsquo;s theme.&lt;/p&gt;
&lt;p&gt;I wrote a small unit that reads the RTC via INT 1Ah and formats it as
ISO 8601. The entire program, compiled, is 3,248 bytes. Try getting that
from a modern toolchain.&lt;/p&gt;
&lt;p&gt;What surprised me was not just speed, but focus. Turbo Pascal&amp;rsquo;s workflow is
so tight that experimentation becomes natural: edit, compile, run, inspect,
repeat. No dependency resolver, no plugin lifecycle, no hidden build graph.
You can reason about the whole stack while staying in flow.&lt;/p&gt;
&lt;h2 id=&#34;why-it-is-still-worth-touching&#34;&gt;Why it is still worth touching&lt;/h2&gt;
&lt;p&gt;Turbo Pascal remains one of the best environments for learning low-level
software discipline without drowning in tooling:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;strong typing with low ceremony&lt;/li&gt;
&lt;li&gt;explicit artifacts (&lt;code&gt;.PAS&lt;/code&gt;, &lt;code&gt;.TPU&lt;/code&gt;, &lt;code&gt;.OBJ&lt;/code&gt;, &lt;code&gt;.EXE&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;immediate compile-run feedback&lt;/li&gt;
&lt;li&gt;clear memory and ABI consequences&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to sharpen systems instincts, this is still high-return practice.&lt;/p&gt;
&lt;h2 id=&#34;practical-2025-setup-that-stays-reproducible&#34;&gt;Practical 2025 setup that stays reproducible&lt;/h2&gt;
&lt;p&gt;My baseline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;pin one DOSBox-X config per project&lt;/li&gt;
&lt;li&gt;mount a host directory as project root&lt;/li&gt;
&lt;li&gt;keep &lt;code&gt;BUILD.BAT&lt;/code&gt; for CLI parity with IDE actions&lt;/li&gt;
&lt;li&gt;version notes + build profile options in plain text&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Expected outcome:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;same source builds the same way after a long break&lt;/li&gt;
&lt;li&gt;less dependence on undocumented IDE state&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;what-to-practice-first-30-90-minute-labs&#34;&gt;What to practice first (30-90 minute labs)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Build a two-unit app and observe incremental rebuild behavior.&lt;/li&gt;
&lt;li&gt;Link one external &lt;code&gt;.OBJ&lt;/code&gt; routine and verify ABI correctness.&lt;/li&gt;
&lt;li&gt;Enable one overlayed cold path and measure first-hit latency.&lt;/li&gt;
&lt;li&gt;Initialize BGI with diagnostic harness and test broken path behavior.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These labs map directly to the deeper series below.&lt;/p&gt;
&lt;h2 id=&#34;read-this-as-a-progression&#34;&gt;Read this as a progression&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-1-anatomy-and-workflow/&#34;&gt;Turbo Pascal Toolchain, Part 1: Anatomy and Workflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-2-objects-units-and-binary-investigation/&#34;&gt;Part 2: Objects, Units, and Binary Investigation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-3-overlays-memory-models-and-link-strategy/&#34;&gt;Part 3: Overlays, Memory Models, and Link Strategy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-4-graphics-drivers-bgi-and-rendering-integration/&#34;&gt;Part 4: Graphics Drivers, BGI, and Rendering Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-5-from-6-to-7-compiler-linker-and-language-growth/&#34;&gt;Part 5: From 6.0 to 7.0 - Compiler, Linker, and Language Growth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-overlay-tutorial-build-package-and-debug-an-ovr-application/&#34;&gt;Overlay Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/tp/turbo-pascal-bgi-tutorial-dynamic-drivers-linked-drivers-and-diagnostic-harnesses/&#34;&gt;BGI Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/dos/batch-file-wizardry/&#34;&gt;Batch File Wizardry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/retro/hardware/restoring-a-286/&#34;&gt;Restoring an AT 286&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
