<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Tutorial on TurboVision</title>
    <link>https://turbovision.in6-addr.net/tags/tutorial/</link>
    <description>Recent content in Tutorial 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/tags/tutorial/index.xml" rel="self" type="application/rss&#43;xml" />
    
    
    
    <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 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>Hand-Soldering 0402 Components</title>
      <link>https://turbovision.in6-addr.net/electronics/soldering-smd-by-hand/</link>
      <pubDate>Sun, 28 Dec 2025 00:00:00 +0000</pubDate>
      <lastBuildDate>Mon, 09 Mar 2026 09:46:27 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/soldering-smd-by-hand/</guid>
      <description>&lt;p&gt;0402 passives measure 1.0 × 0.5 mm. They&amp;rsquo;re barely visible to the naked
eye, yet hand-soldering them is doable with the right technique: flux,
a fine conical tip, thin solder wire, and patience.&lt;/p&gt;
&lt;p&gt;The key is to tin one pad first, tack the component down, then solder the
other side. A stereo microscope helps but isn&amp;rsquo;t strictly necessary if you
have good lighting and steady hands.&lt;/p&gt;
&lt;p&gt;What usually fails is not dexterity, but process order. If you approach 0402
work like through-hole soldering, parts tombstone, slide, or disappear into
the carpet. If you stage the work correctly, the joints become boringly
repeatable.&lt;/p&gt;
&lt;h2 id=&#34;workflow-that-keeps-rework-low&#34;&gt;Workflow that keeps rework low&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Clean pads with isopropyl alcohol.&lt;/li&gt;
&lt;li&gt;Add liquid flux before touching solder.&lt;/li&gt;
&lt;li&gt;Pre-tin exactly one pad with a tiny amount.&lt;/li&gt;
&lt;li&gt;Hold the part with tweezers, reflow that pad, and &amp;ldquo;tack&amp;rdquo; alignment.&lt;/li&gt;
&lt;li&gt;Solder the second pad with minimal dwell time.&lt;/li&gt;
&lt;li&gt;Revisit the first pad only if wetting looks poor.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The microscope is optional, but magnification changes quality control. Even a
cheap USB scope catches bridges and cold joints before power-on.&lt;/p&gt;
&lt;h2 id=&#34;common-mistakes&#34;&gt;Common mistakes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Too much solder: creates hidden bridges under the body.&lt;/li&gt;
&lt;li&gt;Too little flux: oxidized pads and grainy joints.&lt;/li&gt;
&lt;li&gt;Too much heat: lifted pads, especially on cheap proto boards.&lt;/li&gt;
&lt;li&gt;Mechanical pressure while heating: parts shoot away or skew.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My rule is simple: if the joint takes more than a few seconds, stop, re-flux,
and try again. Fighting a dry joint with temperature only makes damage faster.&lt;/p&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/riscv-on-ch32v003/&#34;&gt;RISC-V on a 10-Cent Chip&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>
    
    <item>
      <title>Buffer Overflow 101</title>
      <link>https://turbovision.in6-addr.net/hacking/exploits/buffer-overflow-101/</link>
      <pubDate>Mon, 03 Nov 2025 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 15:49:37 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/hacking/exploits/buffer-overflow-101/</guid>
      <description>&lt;p&gt;A stack-based buffer overflow is the oldest trick in the book and still one of the
most instructive. We start with a vulnerable C program, compile it without canaries,
and walk through EIP control step by step.&lt;/p&gt;
&lt;p&gt;The target binary accepts user input via &lt;code&gt;gets()&lt;/code&gt; — a function so dangerous that
modern compilers emit a warning just for including it. We feed it a carefully
crafted payload: 64 bytes of padding, followed by the address of our shellcode
sitting on the stack.&lt;/p&gt;
&lt;p&gt;Key takeaways: always compile test binaries with &lt;code&gt;-fno-stack-protector -z execstack&lt;/code&gt;
when learning, and never on a production box.&lt;/p&gt;
&lt;p&gt;What makes this topic timeless is not the exact exploit recipe, but the mental
model it gives you: memory layout, calling convention, control-flow integrity,
and why unsafe copy primitives are dangerous by construction.&lt;/p&gt;
&lt;h2 id=&#34;reliable-lab-workflow&#34;&gt;Reliable lab workflow&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Confirm binary protections (&lt;code&gt;checksec&lt;/code&gt; style checks).&lt;/li&gt;
&lt;li&gt;Crash with pattern input to find exact overwrite offset.&lt;/li&gt;
&lt;li&gt;Validate instruction pointer control with marker values.&lt;/li&gt;
&lt;li&gt;Build payload in small increments and verify each stage.&lt;/li&gt;
&lt;li&gt;Only then attempt shellcode or return-oriented payloads.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Expected outcome before each run should be explicit. If behavior differs, do
not &amp;ldquo;try random bytes&amp;rdquo;; explain the difference first. That habit turns exploit
practice into engineering instead of cargo cult.&lt;/p&gt;
&lt;h2 id=&#34;defensive-mirror&#34;&gt;Defensive mirror&lt;/h2&gt;
&lt;p&gt;Learning offensive mechanics should immediately map to mitigation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;remove dangerous APIs (&lt;code&gt;gets&lt;/code&gt;, unchecked &lt;code&gt;strcpy&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;enable stack canaries, NX, PIE, and RELRO&lt;/li&gt;
&lt;li&gt;reduce attack surface in parser and input-heavy code paths&lt;/li&gt;
&lt;li&gt;test with sanitizers during development&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/hacking/exploits/format-string-attacks/&#34;&gt;Format String Attacks Demystified&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/hacking/tools/ghidra-first-steps/&#34;&gt;Ghidra: First Steps in Reverse Engineering&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>AVR Bare-Metal Blinking</title>
      <link>https://turbovision.in6-addr.net/electronics/microcontrollers/avr-bare-metal/</link>
      <pubDate>Wed, 20 Aug 2025 00:00:00 +0000</pubDate>
      <lastBuildDate>Sun, 22 Feb 2026 15:48:59 +0100</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/electronics/microcontrollers/avr-bare-metal/</guid>
      <description>&lt;p&gt;No Arduino libraries. No HAL. Just registers.&lt;/p&gt;
&lt;p&gt;An ATmega328P has DDRB, PORTB, and a 16-bit timer. We configure Timer1
in CTC mode with a 1 Hz compare match, toggle PB5 (the onboard LED pin)
in the ISR, and end up with a binary that fits in 176 bytes. The Makefile
uses &lt;code&gt;avr-gcc&lt;/code&gt; and &lt;code&gt;avrdude&lt;/code&gt; directly — no IDE required.&lt;/p&gt;
&lt;p&gt;This exercise looks trivial, but it trains the exact muscle many developers
skip: understanding cause and effect between register writes and hardware
behavior. You do not &amp;ldquo;ask an API&amp;rdquo; to blink. You define direction bits, timer
prescalers, compare values, and interrupt masks yourself.&lt;/p&gt;
&lt;h2 id=&#34;minimal-mental-model&#34;&gt;Minimal mental model&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DDRB&lt;/code&gt; configures PB5 as output.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TCCR1A/TCCR1B&lt;/code&gt; define timer mode and prescaler.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OCR1A&lt;/code&gt; sets compare threshold.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TIMSK1&lt;/code&gt; enables compare interrupt.&lt;/li&gt;
&lt;li&gt;ISR toggles &lt;code&gt;PORTB&lt;/code&gt; bit for the LED.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When this chain is explicit, debugging gets faster. If timing is wrong, you
inspect clock and prescaler. If the LED is dark, verify direction and pin.
Each symptom maps to a small set of causes.&lt;/p&gt;
&lt;h2 id=&#34;why-still-do-this-in-2026&#34;&gt;Why still do this in 2026&lt;/h2&gt;
&lt;p&gt;Bare-metal AVR is still a great teaching platform because feedback is fast
and tooling is mature. You can compile, flash, and verify behavior in a few
seconds, then iterate. Even if your production target is different, this
discipline transfers directly to RISC-V, ARM, and RTOS-based projects.&lt;/p&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/electronics/microcontrollers/riscv-on-ch32v003/&#34;&gt;RISC-V on a 10-Cent Chip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/musings/why-constraints-matter/&#34;&gt;Why Constraints Matter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
