<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>History on TurboVision</title>
    <link>https://turbovision.in6-addr.net/tags/history/</link>
    <description>Recent content in History 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/history/index.xml" rel="self" type="application/rss&#43;xml" />
    
    
    
    <item>
      <title>The Real Historical Analogy</title>
      <link>https://turbovision.in6-addr.net/musings/ai-language-protocols/the-real-historical-analogy/</link>
      <pubDate>Mon, 20 Apr 2026 00:00:00 +0000</pubDate>
      <lastBuildDate>Mon, 20 Apr 2026 00:00:00 +0000</lastBuildDate>
      <guid>https://turbovision.in6-addr.net/musings/ai-language-protocols/the-real-historical-analogy/</guid>
      <description>&lt;p&gt;The most popular analogies around AI are usually the worst ones, because they jump straight to apocalypse, utopia, or machine rebellion and miss the transformation already happening in front of us. A far better analogy is older, less glamorous, and much more revealing: the history of writing becoming administration.&lt;/p&gt;
&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;The strongest historical analogy for LLMs is not Skynet, industrial automation, or a new species. It is the old pattern in which an expressive medium expands access and then hardens into records, templates, procedure, governance, and bureaucracy. Less cinema. More paperwork. Unfortunately that is usually where real power hides.&lt;/p&gt;
&lt;h2 id=&#34;the-question&#34;&gt;The Question&lt;/h2&gt;
&lt;p&gt;You may ask: if natural-language AI feels like a liberation from rigid interfaces, what historical pattern does it actually resemble? Is there an older moment where a flexible medium spread widely and then slowly turned into structure, procedure, and control?&lt;/p&gt;
&lt;h2 id=&#34;the-long-answer&#34;&gt;The Long Answer&lt;/h2&gt;
&lt;p&gt;Yes. Writing.&lt;/p&gt;
&lt;h3 id=&#34;the-better-analogy-is-older-and-less-glamorous&#34;&gt;The Better Analogy Is Older and Less Glamorous&lt;/h3&gt;
&lt;p&gt;Or more precisely: writing after it stopped being rare.&lt;/p&gt;
&lt;p&gt;When we romanticize writing, we think of poetry, letters, memory, literature, philosophy, scripture, and thought made durable. All of that matters. But historically, writing did not remain only an expressive medium. As soon as it became socially central, it also became a machine for legibility.&lt;/p&gt;
&lt;p&gt;It began to support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ledgers&lt;/li&gt;
&lt;li&gt;tax records&lt;/li&gt;
&lt;li&gt;property claims&lt;/li&gt;
&lt;li&gt;legal formulas&lt;/li&gt;
&lt;li&gt;decrees&lt;/li&gt;
&lt;li&gt;inventories&lt;/li&gt;
&lt;li&gt;forms&lt;/li&gt;
&lt;li&gt;standard contracts&lt;/li&gt;
&lt;li&gt;administrative routines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The same medium that enabled reflection also enabled bureaucracy.&lt;/p&gt;
&lt;p&gt;That is not an accidental corruption of writing&amp;rsquo;s pure spirit. It is what happens when an expressive medium starts carrying coordination at scale. The lyric and the ledger share a medium, and the ledger is usually better funded.&lt;/p&gt;
&lt;p&gt;This is the historical rhyme that matters for AI.&lt;/p&gt;
&lt;p&gt;Natural-language interfaces feel, at first, like a return from bureaucracy to speech. No more memorizing commands. No more obeying narrow syntactic rituals. No more learning the machine&amp;rsquo;s rigid grammar before the machine will meet you halfway. You can just speak.&lt;/p&gt;
&lt;p&gt;But the moment that speech starts doing real work, the old dynamic reappears. The free exchange has to become legible, stable, and reusable. Then come templates. Then conventions. Then control layers. Then record-keeping. Then policy.&lt;/p&gt;
&lt;p&gt;In other words, the medium begins to administrate.&lt;/p&gt;
&lt;h3 id=&#34;writing-became-administration&#34;&gt;Writing Became Administration&lt;/h3&gt;
&lt;p&gt;That is why I think the right analogy is not &amp;ldquo;AI replaces humans&amp;rdquo; but &amp;ldquo;language-to-machine interaction is becoming administratively scalable.&amp;rdquo; That phrase has none of the drama of science fiction, which is exactly why I trust it.&lt;/p&gt;
&lt;p&gt;Notice how much current AI practice already fits that pattern.&lt;/p&gt;
&lt;p&gt;At the expressive edge:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;exploratory prompting&lt;/li&gt;
&lt;li&gt;brainstorming&lt;/li&gt;
&lt;li&gt;rewriting&lt;/li&gt;
&lt;li&gt;questioning&lt;/li&gt;
&lt;li&gt;improvisation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the administrative edge:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;system prompts&lt;/li&gt;
&lt;li&gt;reusable role definitions&lt;/li&gt;
&lt;li&gt;skill files&lt;/li&gt;
&lt;li&gt;output schemas&lt;/li&gt;
&lt;li&gt;tool policies&lt;/li&gt;
&lt;li&gt;safety rules&lt;/li&gt;
&lt;li&gt;evaluation harnesses&lt;/li&gt;
&lt;li&gt;memory and trace retention&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is exactly the same medium bifurcating into two functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;expression&lt;/li&gt;
&lt;li&gt;governance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The mistake would be to think governance arrives from outside as an alien force. More often it emerges from the medium&amp;rsquo;s own success. Once too many people, too many workflows, and too many risks pass through the channel, informal use becomes too expensive.&lt;/p&gt;
&lt;p&gt;This is why the writing analogy beats the science-fiction analogy. Science fiction lets us talk about AI while keeping one eye on spectacle. Administration forces us to talk about rules, defaults, records, compliance, and who gets to decide what counts as proper use. Less fun, more dangerous.&lt;/p&gt;
&lt;p&gt;Science fiction keeps us staring at agency in the dramatic sense: rebellion, consciousness, domination, replacement. Those questions may have their place, but they are not what we are living through most directly right now.&lt;/p&gt;
&lt;p&gt;What we are living through is far more mundane and therefore far more transformative:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;who gets to issue instructions&lt;/li&gt;
&lt;li&gt;in what form&lt;/li&gt;
&lt;li&gt;with what defaults&lt;/li&gt;
&lt;li&gt;under whose hidden constraints&lt;/li&gt;
&lt;li&gt;with what record of compliance&lt;/li&gt;
&lt;li&gt;and according to which evolving norms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is administration.&lt;/p&gt;
&lt;p&gt;A government clerk, a shipping office, a medieval chancery, and a modern AI platform may look worlds apart, but they share one deep concern: turning messy human intentions into legible operations.&lt;/p&gt;
&lt;p&gt;That is why some of the current discourse feels so unserious to me. People keep asking whether the machine is becoming a person while entire companies are busy making it into procedure.&lt;/p&gt;
&lt;p&gt;Once you look through that lens, many supposedly strange features of the current AI moment become obvious.&lt;/p&gt;
&lt;p&gt;Why are people standardizing prompts?
Because legibility enables coordination.&lt;/p&gt;
&lt;p&gt;Why are teams writing internal style guides for model use?
Because institutions cannot run on charm alone.&lt;/p&gt;
&lt;p&gt;Why do skill files, tool schemas, and structured outputs proliferate?
Because the medium is being prepared for scale.&lt;/p&gt;
&lt;p&gt;Why does the language of &amp;ldquo;best practice&amp;rdquo; appear so quickly?
Because informal success always creates pressure for repeatability.&lt;/p&gt;
&lt;h3 id=&#34;freedom-and-bureaucracy-grow-together&#34;&gt;Freedom and Bureaucracy Grow Together&lt;/h3&gt;
&lt;p&gt;This is also why the present moment feels ideologically confused. We are using the rhetoric of liberation while simultaneously building new bureaucratic layers. People notice the contradiction and either celebrate one side or denounce the other. I think both reactions are too simple.&lt;/p&gt;
&lt;p&gt;The bureaucracy is not a betrayal of the freedom.
It is what the freedom becomes when it has to survive contact with institutions.&lt;/p&gt;
&lt;p&gt;That is an irritating sentence, but I think it is true.&lt;/p&gt;
&lt;p&gt;There is another historical layer worth noticing: standardization often follows democratization, not the other way around.&lt;/p&gt;
&lt;p&gt;Printing expands who can read and write, and then spelling, grammar, and editorial norms harden.
Open networks expand who can communicate, and then protocols stabilize the traffic.
Mass politics expands participation, and then bureaucracy grows to make populations administratively legible.
Natural-language computing expands who can &amp;ldquo;program,&amp;rdquo; and then prompt rules, tool contracts, and agent frameworks appear.&lt;/p&gt;
&lt;p&gt;This pattern is almost embarrassingly regular. We keep acting surprised by it anyway, which may be one of the more stable features of modernity.&lt;/p&gt;
&lt;p&gt;It should also change how we talk about power.&lt;/p&gt;
&lt;p&gt;The frightening question is not only whether AI becomes an autonomous sovereign. The more immediate question is who controls the administrative grammar of human-machine exchange. In older regimes, literacy itself was power. Later, access to legal language was power. Later still, access to code and infrastructure was power.&lt;/p&gt;
&lt;p&gt;Now the emerging power may sit in the ability to shape:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;system defaults&lt;/li&gt;
&lt;li&gt;hidden instructions&lt;/li&gt;
&lt;li&gt;moderation layers&lt;/li&gt;
&lt;li&gt;tool affordances&lt;/li&gt;
&lt;li&gt;evaluation criteria&lt;/li&gt;
&lt;li&gt;acceptable interaction styles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is a quieter kind of power than Skynet fantasies, but in practice it may matter more. It is much easier to smuggle power in through defaults than through manifestos.&lt;/p&gt;
&lt;p&gt;Because most people will not meet AI as pure model weights. They will meet it as institutionalized behavior.&lt;/p&gt;
&lt;p&gt;And institutionalized behavior is always partly political.&lt;/p&gt;
&lt;h3 id=&#34;the-real-struggle-is-over-administrative-power&#34;&gt;The Real Struggle Is Over Administrative Power&lt;/h3&gt;
&lt;p&gt;This is where the analogy becomes genuinely useful rather than merely clever. It gives you a way to organize the whole field without falling into either marketing or panic.&lt;/p&gt;
&lt;p&gt;You can ask of any AI feature:&lt;/p&gt;
&lt;p&gt;Is this expressive?
Is this administrative?
Or is it a hybrid trying to hide the transition?&lt;/p&gt;
&lt;p&gt;A freeform chat UI is expressive.
A schema-constrained workflow is administrative.
A friendly assistant with hidden system rules is a hybrid, and hybrids are where most of the real tension lives.&lt;/p&gt;
&lt;p&gt;The writing analogy also helps explain the emotional tone people bring to AI. Some are exhilarated because they feel the expressive release. Others are suspicious because they can already smell the coming bureaucracy. Both are perceiving real parts of the same transformation.&lt;/p&gt;
&lt;p&gt;The optimists are seeing the collapse of unnecessary formal barriers.
The skeptics are seeing the rise of a new governance layer.&lt;/p&gt;
&lt;p&gt;Again, both are right.&lt;/p&gt;
&lt;p&gt;And this returns us to the opening paradox. Why does a medium that promises freedom generate rules so quickly? Because freedom by itself is not enough for archives, institutions, teams, compliance, safety, memory, and distributed execution. A society can play in a medium informally for a while. It cannot run on that informality forever.&lt;/p&gt;
&lt;p&gt;That does not mean we should embrace every new layer of prompt bureaucracy with cheerful obedience. Quite the opposite. Once you recognize the administrative turn, you can ask better questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;which rules are genuinely useful?&lt;/li&gt;
&lt;li&gt;which are cargo cult?&lt;/li&gt;
&lt;li&gt;which increase transparency?&lt;/li&gt;
&lt;li&gt;which hide power?&lt;/li&gt;
&lt;li&gt;which preserve human agency?&lt;/li&gt;
&lt;li&gt;which quietly narrow it?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is the adult conversation.&lt;/p&gt;
&lt;p&gt;So if you want the real historical analogy, here is mine:&lt;/p&gt;
&lt;p&gt;LLMs are not best understood as a talking machine waiting to rebel.
They are better understood as the latest medium through which human intention becomes administratively legible at scale.&lt;/p&gt;
&lt;p&gt;That may sound less cinematic than Skynet, but it is more historically grounded and much more relevant to the systems we are actually building.&lt;/p&gt;
&lt;p&gt;The true drama is not that the machine may wake up one day and declare war. The true drama is that we may succeed in building a new universal administrative layer and barely notice how much social power gets embedded in its defaults, templates, and permitted forms of speech.&lt;/p&gt;
&lt;p&gt;An ugly example helps here. Suppose every internal assistant in a large company quietly prefers one style of project plan, one tone of escalation, one definition of risk, one preferred sequence of approvals, one acceptable way of disagreeing. Nobody declares a doctrine. Nobody publishes a manifesto. People just start adapting to what the system rewards. That is how a lot of administrative power actually enters the room.&lt;/p&gt;
&lt;p&gt;That is not a reason for panic. It is a reason for seriousness.&lt;/p&gt;
&lt;p&gt;Every civilization that learns a new medium first celebrates its expressive power.
Soon after, it learns what paperwork can do with it.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The best historical analogy for LLMs is not cinematic rebellion but administrative expansion. Like writing before them, natural-language interfaces begin as expressive tools and then harden into templates, records, procedures, and governance. That is why AI feels simultaneously liberating and bureaucratic: both experiences are true, because the same medium is serving both expression and institutional control.&lt;/p&gt;
&lt;p&gt;Seen this way, the important question is not whether structure will emerge. It is whether the coming administrative layer will stay legible, contestable, and open to public scrutiny, or whether it will arrive in the usual smiling way: convenient, useful, efficient, and already half invisible.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When AI becomes part of society’s paperwork rather than its science fiction, who will notice first that the defaults have become law-like?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Related reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/musings/ai-language-protocols/freedom-creates-protocol/&#34;&gt;Freedom Creates Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/musings/ai-language-protocols/the-myth-of-prompting-as-conversation/&#34;&gt;The Myth of Prompting as Conversation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://turbovision.in6-addr.net/musings/ai-language-protocols/is-there-a-hidden-language-beneath-english/&#34;&gt;Is There a Hidden Language Beneath English?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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 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 Toolchain, Part 5: From 6.0 to 7.0 - Compiler, Linker, and Language Growth</title>
      <link>https://turbovision.in6-addr.net/retro/dos/tp/toolchain/turbo-pascal-toolchain-part-5-from-6-to-7-compiler-linker-and-language-growth/</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/toolchain/turbo-pascal-toolchain-part-5-from-6-to-7-compiler-linker-and-language-growth/</guid>
      <description>&lt;p&gt;Parts 1-4 covered workflow, artifacts, overlays, and BGI integration. This last
part goes inside the compiler/language boundary: memory assumptions, type layout,
calling conventions, and assembler integration from TP6-era practice to TP7/BP7
scope.&lt;/p&gt;
&lt;h3 id=&#34;structure-map&#34;&gt;Structure map&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Version framing&lt;/strong&gt; — TP6 vs TP7/BP7 scope, continuity and deltas&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Execution model&lt;/strong&gt; — real-mode assumptions, segmentation, near/far&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data type layout&lt;/strong&gt; — size table, alignment, layout probe harness&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory layout consequences&lt;/strong&gt; — ShortString, sets, records, arrays&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Procedures vs functions&lt;/strong&gt; — semantics and ABI implications&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Calling conventions&lt;/strong&gt; — stack layout, parameter order, return strategy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compiler directives&lt;/strong&gt; — policy, safety controls, project-wide usage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assembler integration&lt;/strong&gt; — inline blocks, external OBJ, boundary contracts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TP6→TP7 migration&lt;/strong&gt; — pipeline evolution, artifact implications, language growth&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protected mode and OOP&lt;/strong&gt; — BP7 context, object layout, VMT considerations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migration checklist&lt;/strong&gt; — risk controls, test loops, regression traps, common pitfalls&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;version-framing-what-changed-and-what-stayed-stable&#34;&gt;Version framing: what changed and what stayed stable&lt;/h2&gt;
&lt;p&gt;The TP6 to TP7 shift was less a language revolution and more an expansion of
operational surface:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;larger project/tooling workflows became easier&lt;/li&gt;
&lt;li&gt;artifact and mixed-language integration became more central&lt;/li&gt;
&lt;li&gt;language core stayed recognizably Turbo Pascal&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the technical model below is largely continuous across this generation, with
feature breadth increasing in later packaging. Borland Pascal 7 (BP7) extended
this further with protected-mode compilation, built-in debugging support, and
richer IDE integration, while TP7 remained primarily a real-mode product.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Version-specific nuances&lt;/strong&gt; — TP7.0 (1990) stabilized the TP6 object model and
improved unit compilation speed. TP7.1 addressed bugs and refinements; some
teams held at 7.0 for compatibility with shared codebases. BP7 (1992) bundled
Turbo Debugger, expanded the RTL, and introduced DPMI target support. Exact
behavior of directives like &lt;code&gt;{$G+}&lt;/code&gt; (80286 instructions), &lt;code&gt;{$A+}&lt;/code&gt; (record
alignment), and codegen choices can vary between these builds; when precise
version behavior matters, treat claims here as a starting point and validate
against your toolchain.&lt;/p&gt;
&lt;h2 id=&#34;execution-model-assumptions-the-non-negotiables&#34;&gt;Execution model assumptions (the non-negotiables)&lt;/h2&gt;
&lt;p&gt;Real-mode DOS assumptions drive everything:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Segmented memory model&lt;/strong&gt; — 64 KB segments, selector:offset addressing, 20-bit physical address space. DS usually points at the program’s data; SS at the stack; CS at code. Overlays swap code segments in and out of a single overlay area.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;16-bit register-centric calling paths&lt;/strong&gt; — AX, BX, CX, DX, SI, DI, BP, SP; segment registers CS, DS, SS, ES.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Near vs far distinctions&lt;/strong&gt; — near calls use same segment (16-bit offset), far calls require segment:offset (32-bit); overlay units demand far entry points.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conventional memory pressure&lt;/strong&gt; — first 640 KB shared by DOS, TSRs, drivers, and your program; overlays and heap compete for the same pool.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The linker’s choice of memory model (Tiny, Small, Medium, Large, Huge) constrains code and data segment layout. TP6 and TP7 both default to Small model in typical configurations: one code segment, one data segment, with near pointers. Tiny folds code and data into one segment (for .COM output); Medium allows multiple code segments (far code, near data); Large/Huge allow multiple data segments with far pointers—changing pointer size from 2 to 4 bytes. Switching to Large model changes pointer sizes and call conventions; map-file analysis becomes essential when hunting link errors or unexpected runtime behavior.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Artifact implications&lt;/strong&gt; — Small model yields a single .EXE with code and data
in separate segments. Overlays add .OVR files; each overlay is its own code
segment loaded on demand. The linker produces a .MAP file listing segment
addresses and public symbols; use it to verify overlay boundaries and diagnose
&amp;ldquo;fixup overflow&amp;rdquo; or segment-order issues. Segment order in the map (CODE, DATA,
overlay segments) affects load addresses; changing unit compile order can shift
symbols and break code that assumes fixed offsets. A typical map lists segments
in load order with start/stop addresses; overlays appear as named segments with
their size—verify overlay sizes match expectations before debugging load
failures.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data layout and ABI&lt;/strong&gt; — Record fields, set bit layouts, and string formats
are stable within a compiler version but can differ across TP6, TP7, and BP7.
When sharing binary structures (e.g., files or shared memory) between programs
built with different toolchains, define a canonical layout and validate with
layout probes. Ignoring these constraints while reading Pascal source leads to
wrong performance and ABI conclusions. A layout-probe program that prints
&lt;code&gt;SizeOf&lt;/code&gt; for all shared types, run under each toolchain, gives a quick
compatibility report before committing to a cross-toolchain design.&lt;/p&gt;
&lt;h2 id=&#34;data-type-layout-practical-table&#34;&gt;Data type layout: practical table&lt;/h2&gt;
&lt;p&gt;Common TP-era sizes in real-mode profiles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Byte&lt;/code&gt;: 1 byte&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ShortInt&lt;/code&gt;: 1 byte&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Word&lt;/code&gt;: 2 bytes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Integer&lt;/code&gt;: 2 bytes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LongInt&lt;/code&gt;: 4 bytes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Char&lt;/code&gt;: 1 byte&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Boolean&lt;/code&gt;: 1 byte&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pointer&lt;/code&gt;: 4 bytes (segment:offset in real mode)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;String[N]&lt;/code&gt;: &lt;code&gt;N+1&lt;/code&gt; bytes (length byte + payload)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Floating-point and extended numeric types (&lt;code&gt;Real&lt;/code&gt;, &lt;code&gt;Single&lt;/code&gt;, &lt;code&gt;Double&lt;/code&gt;,
&lt;code&gt;Extended&lt;/code&gt;, &lt;code&gt;Comp&lt;/code&gt;) exist with version/profile-specific behavior and FPU/emulation
settings, so treat exact codegen cost as configuration dependent. With &lt;code&gt;{$N+}&lt;/code&gt;,
the compiler uses native FPU instructions; with &lt;code&gt;{$N-}&lt;/code&gt;, software emulation
(via runtime library) is typical. &lt;code&gt;Real&lt;/code&gt; is typically 6-byte BCD in older
profiles and can map to &lt;code&gt;Single&lt;/code&gt; or a software type in others—verify in your
build. &lt;code&gt;Extended&lt;/code&gt; is 10 bytes (80-bit); &lt;code&gt;Comp&lt;/code&gt; is 8-byte integer format often
used for currency. Set types use one bit per element; &lt;code&gt;set of 0..7&lt;/code&gt; is 1 byte,
&lt;code&gt;set of 0..15&lt;/code&gt; is 2 bytes, up to 32 bytes for &lt;code&gt;set of 0..255&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Alignment and packing&lt;/strong&gt; — Turbo Pascal generally packs record fields without
inserting padding; fields align to their natural size (1, 2, 4 bytes). The
&lt;code&gt;{$A+/-}&lt;/code&gt; (Align Records) directive, where available, can change this—&lt;code&gt;{$A+}&lt;/code&gt; may
align record fields to word boundaries for faster access on some processors.
Packed records (&lt;code&gt;packed record&lt;/code&gt;) minimize size at potential performance cost.
For structures crossing the Pascal–C–assembly boundary, explicit layout
verification is mandatory; C struct alignment rules often differ.&lt;/p&gt;
&lt;h3 id=&#34;quick-layout-probe-harness&#34;&gt;Quick layout probe harness&lt;/h3&gt;
&lt;p&gt;If binary layout matters, measure your exact compiler profile:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;program LayoutProbe;
type
  TRec = record
    B: Byte;
    W: Word;
    L: LongInt;
  end;
  TPackedRec = packed record
    B: Byte;
    W: Word;
  end;
begin
  Writeln(&amp;#39;SizeOf(Integer)=&amp;#39;, SizeOf(Integer));
  Writeln(&amp;#39;SizeOf(Pointer)=&amp;#39;, SizeOf(Pointer));
  Writeln(&amp;#39;SizeOf(String[20])=&amp;#39;, SizeOf(String[20]));
  Writeln(&amp;#39;SizeOf(TRec)=&amp;#39;, SizeOf(TRec));
  Writeln(&amp;#39;SizeOf(TPackedRec)=&amp;#39;, SizeOf(TPackedRec));
  Writeln(&amp;#39;SizeOf(Single)=&amp;#39;, SizeOf(Single), &amp;#39; SizeOf(Real)=&amp;#39;, SizeOf(Real));
end.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Expected outcome: concrete numbers for your environment. Never assume layout
from memory when ABI compatibility is at stake.&lt;/p&gt;
&lt;h2 id=&#34;memory-layout-consequences-developers-felt-daily&#34;&gt;Memory layout consequences developers felt daily&lt;/h2&gt;
&lt;h3 id=&#34;shortstring-behavior&#34;&gt;ShortString behavior&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;String&lt;/code&gt; in classic Turbo Pascal is a short string (length-prefixed), not a
null-terminated C string. Consequences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;O(1) length read via byte 0&lt;/li&gt;
&lt;li&gt;max 255 characters; &lt;code&gt;String[80]&lt;/code&gt; is 81 bytes&lt;/li&gt;
&lt;li&gt;direct interop with C APIs needs conversion: either build a null-terminated
copy or pass &lt;code&gt;Str[1]&lt;/code&gt; and ensure the C side respects the length byte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A simple conversion helper for C library calls (TP7&amp;rsquo;s Strings unit has
&lt;code&gt;StrPCopy&lt;/code&gt;; this illustrates the manual pattern):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;procedure PascalToCString(const S: String; var Buf; MaxLen: Byte);
var
  I: Byte;
  P: ^Char;
begin
  P := @Buf;
  I := 0;
  while (I &amp;lt; S[0]) and (I &amp;lt; MaxLen - 1) do begin
    P^ := S[I + 1];
    Inc(P); Inc(I);
  end;
  P^ := #0;
end;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;set-and-record-layout&#34;&gt;Set and record layout&lt;/h3&gt;
&lt;p&gt;Set/record memory footprint is compact but sensitive to declared ranges and
packing decisions. A &lt;code&gt;set of 0..255&lt;/code&gt; consumes up to 32 bytes (one bit per
element); smaller ranges use fewer bytes (e.g., &lt;code&gt;set of 0..15&lt;/code&gt; is 2 bytes).
Record alignment follows the directive and packing mode. Bit ordering within
set bytes is implementation-defined; when exchanging set values with C or
assembly, document and test the mapping. If binary compatibility matters,
verify layout with &lt;code&gt;SizeOf&lt;/code&gt; tests in a dedicated compatibility harness. TP6
and TP7 generally match on these layouts, but mixed toolchains (e.g., C object
modules) may introduce padding differences.&lt;/p&gt;
&lt;h3 id=&#34;arrays&#34;&gt;Arrays&lt;/h3&gt;
&lt;p&gt;Arrays are contiguous. High-throughput code benefits from locality, but segment
boundaries and index range checks (if enabled) influence speed and safety.
Multi-dimensional arrays are stored in row-major order. Static arrays and
open-array parameters have different calling semantics: open arrays pass a
hidden length (typically as the last parameter or in a known slot), which
affects the ABI at procedure boundaries. Example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;procedure Process(const Arr: array of Integer);  { Arr: ptr + hidden High(Len) }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;String parameters pass by reference (address of the length byte); value
parameters of record type may be copied onto the stack or via a hidden pointer,
depending on size—records larger than a few bytes often use a hidden &lt;code&gt;var&lt;/code&gt;
parameter to avoid stack bloat. When interfacing with assembly, document how
each parameter type is passed.&lt;/p&gt;
&lt;h2 id=&#34;procedures-vs-functions-not-just-syntax&#34;&gt;Procedures vs functions: not just syntax&lt;/h2&gt;
&lt;p&gt;Difference in language semantics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;procedure&lt;/code&gt;: action with no return value&lt;/li&gt;
&lt;li&gt;&lt;code&gt;function&lt;/code&gt;: returns value and can appear in expressions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Difference in engineering use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;procedures often model side-effecting operations&lt;/li&gt;
&lt;li&gt;functions often model value computation or query paths&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In low-level interop, function return strategy and calling convention details
matter for ABI compatibility with external objects. Scalars (Byte, Word, Integer,
LongInt, pointers) typically return in registers: Byte in AL, Word/Integer in
AX, LongInt in DX:AX (high word in DX, low in AX), pointers in DX:AX (segment
in DX, offset in AX). Larger types (records, arrays) may use a hidden &lt;code&gt;var&lt;/code&gt;
parameter or a caller-allocated temporary; the threshold and mechanism vary by
version and type size—commonly, records exceeding 4 bytes use a hidden
first parameter for the return buffer.&lt;/p&gt;
&lt;p&gt;When calling or implementing assembly routines that mimic Pascal functions,
match the return mechanism or corruption is likely. A function declared
&lt;code&gt;external&lt;/code&gt; in Pascal must place its return value where the Pascal caller
expects it; an inline &lt;code&gt;asm&lt;/code&gt; block that computes a &lt;code&gt;LongInt&lt;/code&gt; return should
leave the result in DX:AX before the block ends. For &lt;code&gt;Word&lt;/code&gt; returns, ensure
the high byte of AX is clean if the caller extends the value.&lt;/p&gt;
&lt;h2 id=&#34;calling-conventions-and-abi-boundaries&#34;&gt;Calling conventions and ABI boundaries&lt;/h2&gt;
&lt;p&gt;Turbo Pascal default calling convention differs from C conventions commonly used
by external modules. Pascal uses left-to-right parameter push order; C typically
uses right-to-left (cdecl). Pascal procedures usually clean the stack (ret N);
C callers often clean (cdecl). Name mangling can differ: Pascal may export
symbols with no decoration or with a leading underscore; C compilers vary.
At integration boundaries, define explicitly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Parameter order&lt;/strong&gt; — left-to-right (Pascal) vs right-to-left (C)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stack cleanup responsibility&lt;/strong&gt; — callee (Pascal-style) vs caller (cdecl)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Near vs far procedure model&lt;/strong&gt; — must match declaration and link unit&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Value return mechanism&lt;/strong&gt; — register vs stack for large returns&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If any of these is ambiguous, &amp;ldquo;link succeeds but runtime breaks&amp;rdquo; is predictable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stack frame layout&lt;/strong&gt; — The compiler sets up BP as a frame pointer; parameters
are accessed via positive offsets from BP. For a near call, the return address
occupies 2 bytes (IP only); for a far call, 4 bytes (CS:IP). Parameter offsets
shift accordingly. Typical Pascal caller view: parameters pushed left-to-right,
then call. Callee sees highest parameter at lowest address. Example frame for
&lt;code&gt;Proc(A: Word; B: LongInt)&lt;/code&gt; (near call):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;{ Stack grows down. After PUSH BP; MOV BP, SP: BP+2 = ret addr, BP+4 = first param }
{ BP+4 = A (Word), BP+6 = B low, BP+8 = B high. Callee uses RET 6. }
{ For far call: BP+4 = CS, BP+6 = IP; first param at BP+8. }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Near procedures use &lt;code&gt;CALL near ptr&lt;/code&gt; and &lt;code&gt;RET&lt;/code&gt;; far procedures use &lt;code&gt;CALL far ptr&lt;/code&gt;
and &lt;code&gt;RETF&lt;/code&gt;. The callee must not change BP, SP, or segment registers except as
permitted by the convention. For external C routines, use &lt;code&gt;cdecl&lt;/code&gt; or equivalent
where the object was built with C; otherwise stack imbalance or wrong parameter
binding occurs. Inline assembly that calls external code must replicate the
expected convention:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;function CStrLen(P: PChar): Word; cdecl; external &amp;#39;CLIB&amp;#39;;
// or, if linking C OBJ directly:
{$L mystr.obj}
function CStrLen(P: PChar): Word; cdecl; external;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In mixed-language projects, write one tiny ABI verification test per external
routine family before integrating into real logic—e.g., call with known inputs,
assert expected output. Example harness: a small program that calls
&lt;code&gt;MulAcc(100, 200, 50)&lt;/code&gt;, expects a known result, and exits with code 0 on success;
run it immediately after linking a new assembly module to catch offset or
cleanup mismatches before they surface in production.&lt;/p&gt;
&lt;h2 id=&#34;compiler-directives-as-architecture-controls&#34;&gt;Compiler directives as architecture controls&lt;/h2&gt;
&lt;p&gt;Directives are not cosmetic. They change behavior and generated code.&lt;/p&gt;
&lt;p&gt;Examples frequently used in serious projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{$R+/-}&lt;/code&gt;: range checking — array bounds, subrange&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{$Q+/-}&lt;/code&gt;: overflow checking — integer arithmetic&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{$S+/-}&lt;/code&gt;: stack checking — overflow sentinel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{$I+/-}&lt;/code&gt;: I/O checking — handle errors vs continue&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{$G+/-}&lt;/code&gt;: 80286+ instructions (in BP7/profile-dependent builds)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{$N+/-}&lt;/code&gt; and related: FPU vs software float&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Exact availability/effects vary by version/profile, so freeze directive policy
per build profile and avoid per-file drift. A project-wide policy file or
leading include can enforce consistency:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;{ GLOBAL.INC - lock directives for release build }
{$R+}  { Range check in debug only if you prefer; some teams use {$R-} for ship }
{$Q-}  { Overflow off for speed in release }
{$S+}  { Stack overflow detection recommended }
{$I+}  { I/O errors as exceptions or Check(IOResult) }
{$F+}  { FAR calls if using overlays }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;TP5/TP6/TP7 anchor points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{$F+/-}&lt;/code&gt; (Force FAR Calls) is a &lt;strong&gt;local&lt;/strong&gt; directive with default &lt;code&gt;{$F-}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;{$F-}&lt;/code&gt; state, compiler chooses FAR for interface-declared unit routines
and NEAR otherwise.&lt;/li&gt;
&lt;li&gt;Overlay-heavy programs are advised to use &lt;code&gt;{$F+}&lt;/code&gt; broadly to satisfy overlay
FAR-call requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For &lt;code&gt;{$DEFINE}&lt;/code&gt; and conditional compilation, centralize symbols (e.g.,
&lt;code&gt;DEBUG&lt;/code&gt;, &lt;code&gt;USE_OVERLAYS&lt;/code&gt;) so builds stay reproducible. Avoid scattering
version-specific &lt;code&gt;{$IFDEF}&lt;/code&gt; blocks without documentation. Use &lt;code&gt;{$IFOPT R+}&lt;/code&gt; to
check directive state rather than relying on a separate define when debugging
build configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Directive gotchas&lt;/strong&gt; — &lt;code&gt;{$R+}&lt;/code&gt; adds runtime cost; many shipped builds use
&lt;code&gt;{$R-}&lt;/code&gt;. &lt;code&gt;{$I+}&lt;/code&gt; makes I/O failures raise runtime errors; &lt;code&gt;{$I-}&lt;/code&gt; requires
explicit &lt;code&gt;IOResult&lt;/code&gt; checks. Switching these mid-project causes subtle
bugs. Directive scope matters: a unit&amp;rsquo;s directives do not always affect the main program unless inherited via include. Document the chosen directive set in a README or build script so new contributors do not override them.&lt;/p&gt;
&lt;h2 id=&#34;assembler-integration-paths&#34;&gt;Assembler integration paths&lt;/h2&gt;
&lt;p&gt;Turbo Pascal projects typically used two integration patterns:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Inline assembler blocks&lt;/strong&gt; inside Pascal source — &lt;code&gt;asm ... end&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External object modules&lt;/strong&gt; linked with &lt;code&gt;{$L filename.OBJ}&lt;/code&gt; declarations&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Inline path is great for small hot routines where Pascal symbol visibility helps;
you can reference parameters and locals by name. External path is better for
larger modules and reuse across projects. Both require strict stack discipline
and adherence to the chosen calling convention. Inline blocks cannot use
&lt;code&gt;RET&lt;/code&gt; or &lt;code&gt;RETF&lt;/code&gt; to exit the routine—control must flow to the block end so the
compiler can emit the standard epilogue. For conditional exit, use &lt;code&gt;goto&lt;/code&gt; to a
label after the block or restructure the logic.&lt;/p&gt;
&lt;h3 id=&#34;inline-assembler&#34;&gt;Inline assembler&lt;/h3&gt;
&lt;p&gt;Minimal inline shape:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;function BiosTicks: LongInt;
begin
  asm
    mov ah, $00
    int $1A
    mov word ptr [BiosTicks], dx
    mov word ptr [BiosTicks+2], cx
  end;
end;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This style keeps the function contract in Pascal while performing low-level
work in assembly. It is ideal for small hardware-touching routines. The
compiler generates prologue/epilogue; your inline block must preserve BP, SP,
and segment registers as required. Do not assume register contents on entry
except parameters passed in. DS and SS are typically valid for data/stack
access; ES may be used for string operations or be undefined—save and restore
if you modify it.&lt;/p&gt;
&lt;h3 id=&#34;external-obj-integration&#34;&gt;External OBJ integration&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;{$L FASTMATH.OBJ}
function MulAcc(A, B, C: Word): Word; external;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The OBJ must export a public symbol matching the Pascal identifier. Calling
convention (parameter order, stack cleanup, near/far) must match. If the OBJ
was built with TASM or MASM, ensure the &lt;code&gt;PROC&lt;/code&gt; declaration uses the right
model (e.g., &lt;code&gt;NEAR&lt;/code&gt;/&lt;code&gt;FAR&lt;/code&gt;) and that parameter offsets line up with Pascal’s
push order. Example TASM side for &lt;code&gt;function MulAcc(A, B, C: Word): Word&lt;/code&gt;:&lt;/p&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;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&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-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;.MODEL&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;small&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;.CODE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;PUBLIC&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;MulAcc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;MulAcc&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;PROC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;push&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;bp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;mov&lt;/span&gt;  &lt;span class=&#34;no&#34;&gt;bp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;sp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;mov&lt;/span&gt;  &lt;span class=&#34;no&#34;&gt;ax&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;bp&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;; A
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;mov&lt;/span&gt;  &lt;span class=&#34;no&#34;&gt;bx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;bp&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;; B
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;mov&lt;/span&gt;  &lt;span class=&#34;no&#34;&gt;cx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;bp&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;; C
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;; ... compute result in AX ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;pop&lt;/span&gt;  &lt;span class=&#34;no&#34;&gt;bp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;ret&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;MulAcc&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;ENDP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;END&lt;/span&gt;&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;p&gt;Pascal passes A, B, C left-to-right (A at lowest offset); callee cleans with
&lt;code&gt;RET 6&lt;/code&gt;. Mismatch in offset or cleanup causes wrong results or stack crash.
Note: BP+4 assumes a 2-byte return address for near calls; far calls use 4 bytes,
so offsets shift—for the same routine declared &lt;code&gt;far&lt;/code&gt;, A would be at BP+8.
Always verify against the generated Pascal code or map file. A quick sanity
check: compile a trivial Pascal wrapper that calls the external routine with
known values, run it, and assert the result before integrating into production.&lt;/p&gt;
&lt;h3 id=&#34;boundary-contract-checklist&#34;&gt;Boundary contract checklist&lt;/h3&gt;
&lt;p&gt;Before relying on an external routine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;symbol resolves at link (no &amp;ldquo;undefined external&amp;rdquo; or mangling mismatch)&lt;/li&gt;
&lt;li&gt;stack discipline preserved (balanced push/pop, correct &lt;code&gt;ret&lt;/code&gt; form)&lt;/li&gt;
&lt;li&gt;deterministic output under vector tests&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the third condition fails, ABI mismatch is the first suspect. Add a minimal
harness that calls the routine with known inputs and asserts the result before
integrating into production code. Record the test in the project so future
linker or compiler upgrades can re-validate. Mixed Pascal–C–assembly projects
benefit from a single &amp;ldquo;ABI smoke&amp;rdquo; program that exercises every external
boundary with canned inputs.&lt;/p&gt;
&lt;h2 id=&#34;tp6tp7-migration-pipeline-evolution-and-artifact-implications&#34;&gt;TP6→TP7 migration: pipeline evolution and artifact implications&lt;/h2&gt;
&lt;h3 id=&#34;compiler-and-linker-pipeline&#34;&gt;Compiler and linker pipeline&lt;/h3&gt;
&lt;p&gt;From TP6 to TP7, the pipeline stayed conceptually the same: compile units to OBJ,
link OBJ with RTL and any external modules to EXE. The flow is: source (.PAS) →
compiler (TPC.EXE / TPCX.EXE) → object (.OBJ) → linker (TLINK.EXE) → executable
(.EXE) and optional map (.MAP). Overlay units add an extra overlay manager and
.OVR files produced during linking. Command-line builds typically use TPC with
options for target model and overlays; the IDE invokes the same tools under the
hood. Saving OBJ files from each compile allows incremental linking and faster
iteration, but migration should start from a full clean rebuild.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Behavioral shifts&lt;/strong&gt; — TP7&amp;rsquo;s compiler produced more consistent symbol naming and
improved handling of large unit graphs. The linker remained TLINK; its /m, /s,
and overlay options work similarly across TP6 and TP7, but segment ordering and
fixup resolution can produce different map layouts. When comparing before/after
migration, expect segment addresses to change even when logic is identical.&lt;/p&gt;
&lt;p&gt;What changed was robustness and integration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Larger projects&lt;/strong&gt; — TP7 handled more units and larger dependency graphs
without tripping over internal limits. Map file output and symbol resolution
improved.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Object file compatibility&lt;/strong&gt; — TP6 OBJs generally link with TP7, but the
reverse is not guaranteed; TP7 may emit slightly different record layouts or
name mangling in edge cases. Recompile from source when migrating, do not
mix TP6 and TP7 object files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RTL and units&lt;/strong&gt; — Standard units (Crt, Dos, Graph, etc.) evolved; some
routines gained parameters or changed behavior. Re-test code that relies on
unit internals. Graph unit BGI handling, Dos unit path parsing, and Crt
screen buffer assumptions are frequent sources of minor incompatibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OBJ linkage&lt;/strong&gt; — TP7’s TLink (or TLINK) remained compatible with TASM/MASM
object format. Mixed Pascal–assembly projects typically compile Pascal to OBJ,
assemble .ASM to OBJ, then link together. Ensure segment naming and model
(SMALL, MEDIUM, etc.) match across all modules. Use &lt;code&gt;PUBLIC&lt;/code&gt; and &lt;code&gt;EXTRN&lt;/code&gt; in
assembly to mirror Pascal&amp;rsquo;s &lt;code&gt;external&lt;/code&gt; declarations; symbol names must match
exactly. A &amp;ldquo;Fixup overflow&amp;rdquo; or &amp;ldquo;Segment alignment&amp;rdquo; error often indicates
model or segment-name mismatch between modules.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;language-and-oop-growth&#34;&gt;Language and OOP growth&lt;/h3&gt;
&lt;p&gt;TP6 introduced objects; TP7 refined them. Object layout (VMT, instance size)
generally remained compatible, but virtual method tables and constructor/destructor
semantics can vary. BP7 added further extensions. For migration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recompile all object-based units under TP7.&lt;/li&gt;
&lt;li&gt;Run targeted tests on inheritance chains and virtual overrides.&lt;/li&gt;
&lt;li&gt;Avoid depending on undocumented VMT layout.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Objects store the VMT pointer at a fixed offset (often the first field); virtual
methods are dispatched through it. When writing assembly that allocates or
manipulates object instances, preserve that layout:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-pascal&#34; data-lang=&#34;pascal&#34;&gt;type
  TBase = object
    X: Integer;
    procedure DoSomething; virtual;
  end;
  PBase = ^TBase;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Instance size and VMT offset are compiler-dependent; use &lt;code&gt;SizeOf(TBase)&lt;/code&gt; and
avoid hardcoding. Constructor calls initialize the VMT pointer; manual
allocation (e.g., &lt;code&gt;New&lt;/code&gt; or heap blocks) requires proper init. Descendant objects
add their fields after the parent’s; single inheritance keeps layout predictable.
Multiple inheritance was not part of classic Turbo Pascal objects, so no VMT
merging concerns apply. When passing object instances to assembly, pass the
pointer (^TBase) and treat the first word/dword as the VMT pointer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Constructor and destructor order&lt;/strong&gt; — Turbo Pascal objects use &lt;code&gt;Constructor Init&lt;/code&gt; and &lt;code&gt;Destructor Done&lt;/code&gt; (or custom names). Call order matters: base
constructors before derived, destructors in reverse. Failing to call the
destructor on heap-allocated objects leaks memory. TP7 tightened some edge cases
around constructor chaining; if migration reveals odd behavior in object init,
compare TP6 and TP7 object code for the constructor to spot differences.&lt;/p&gt;
&lt;h2 id=&#34;protected-mode-and-bp7-context&#34;&gt;Protected mode and BP7 context&lt;/h2&gt;
&lt;p&gt;Borland Pascal 7 added protected-mode compilation, producing DPMI-compatible
executables that can access extended memory. Key implications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Segment model&lt;/strong&gt; — 32-bit selectors instead of 16-bit segments; pointer
representation and segment arithmetic differ. Code that assumes real-mode
segment:offset layout may fail. Far pointers in protected mode are selector:offset
but the selector is a DPMI descriptor, not a physical segment. Near pointers
remain 32-bit offsets within a segment; the segment limit is 4 GB in 32-bit
mode, changing allocation and pointer-arithmetic assumptions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RTL differences&lt;/strong&gt; — Protected-mode RTL uses DPMI calls for memory and
interrupts; DOS file I/O and system calls go through the DPMI host. Heap
allocation, overlay loading, and BGI driver loading all route differently than
in real mode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assembly interop&lt;/strong&gt; — Inline and external assembly must use 32-bit-safe
patterns; some real-mode tricks (segment manipulation, direct ports) require
different handling. Real-mode &lt;code&gt;int&lt;/code&gt; instructions work via DPMI emulation but
with different semantics for protected-mode interrupts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;OOP in protected mode&lt;/strong&gt; — Object and VMT layout are compatible with real-mode
BP7, but instance allocation and constructor behavior may differ when the RTL
uses DPMI memory services. Virtual method dispatch itself is unchanged;
problems typically arise from code that reads segment values or assumes
physical addresses. If your project stays in real mode, TP7 is sufficient.
Moving to BP7 protected mode is a larger migration: treat it as a separate phase
with dedicated tests. Real-mode TP7 binaries remain the norm for DOS-targeted
applications; BP7 protected-mode targets DPMI-aware environments (e.g., Windows
3.x, OS/2, or standalone DPMI hosts like 386MAX).&lt;/p&gt;
&lt;h2 id=&#34;practical-migration-checklist-technical-not-nostalgic&#34;&gt;Practical migration checklist (technical, not nostalgic)&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;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&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;1) Freeze known-good TP6 artifacts and checksums.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2) Rebuild clean under target TP7/BP7 environment.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3) Compare executable and map deltas.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4) Re-validate external OBJ ABI assumptions.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;5) Re-test overlays + graphics + TSR-heavy runtime profile.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;6) Lock directives/options into documented profile files.&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;p&gt;Each step is auditable: (1) gives a baseline; (2) isolates toolchain change;
(3) surfaces size and symbol shifts; (4) catches OBJ/ABI drift; (5) exercises
integration points; (6) prevents future drift from stray directive changes. Run
the checklist in order; skipping (1) or (2) makes later steps harder to
interpret when regressions appear.&lt;/p&gt;
&lt;h3 id=&#34;risk-controls&#34;&gt;Risk controls&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Baseline capture&lt;/strong&gt; — Checksum the TP6 EXE and map before migration;
record build date and compiler version. Store baseline outputs in a known
location; diff tools and checksum utilities should be available for
comparison.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incremental migration&lt;/strong&gt; — Migrate one unit or subsystem at a time where
possible; isolate changes to reduce debugging scope. Migrate leaf units
(those with no dependencies on other project units) first; then work
inward toward the main program.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fallback&lt;/strong&gt; — Keep TP6 build environment available until TP7 build is
validated. If TP7 regression appears, you can bisect by reverting units.
Preserve TP6 compiler, linker, and RTL paths; document them so the fallback
is reproducible.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;test-loops&#34;&gt;Test loops&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Smoke&lt;/strong&gt; — Program starts, minimal user path completes. Include at least
one path that loads overlays and one that uses BGI if the project employs
them; silent failure on init is common.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regression traps&lt;/strong&gt; — Known inputs that produced known outputs under TP6;
re-run and compare under TP7. Capture checksums or golden files for critical
outputs (reports, exports, screenshots). Automate where possible: a batch
script that runs the program with canned input and diffs output against
baseline catches many regressions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Boundary tests&lt;/strong&gt; — Overlay load/unload, BGI init/close, TSR hooks, assembly
entry points. Exercise code paths that touch segmented memory or far pointers.
Add a dedicated test that calls every external assembly routine with edge
values (0, -1, max) to uncover ABI mismatches.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;expected-outcome&#34;&gt;Expected outcome&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Same behavior with clarified build policy, or&lt;/li&gt;
&lt;li&gt;Explicit, measurable deltas you can explain and document.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not acceptable: &amp;ldquo;it feels mostly fine&amp;rdquo; without verification. Aim for a
migration report that states: baseline version, target version, checksum
deltas (or &amp;ldquo;identical&amp;rdquo;), test results (pass/fail counts), and any known
behavioral differences with root cause. Future maintainers will thank you.&lt;/p&gt;
&lt;h3 id=&#34;common-migration-pitfalls&#34;&gt;Common migration pitfalls&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mixed OBJ versions&lt;/strong&gt; — Linking TP6 units with TP7-built units can produce
subtle ABI mismatches. Clean rebuild from source.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Directive inheritance&lt;/strong&gt; — Unit A’s directives can affect units that use it;
a stray &lt;code&gt;{$R-}&lt;/code&gt; in a deeply included file can disable range checks project-wide.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Overlay entry points&lt;/strong&gt; — Overlays require far calls; if &lt;code&gt;{$F-}&lt;/code&gt; is set where
overlay code is invoked, near calls hit the wrong segment and crash.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BGI driver paths&lt;/strong&gt; — TP7 may look for .BGI files in different locations;
verify InitGraph and driver loading.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FPU detection&lt;/strong&gt; — &lt;code&gt;{$N+}&lt;/code&gt; assumes FPU present; on 8086/8088, use &lt;code&gt;{$N-}&lt;/code&gt; or
runtime detection to avoid invalid opcode traps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Map file drift&lt;/strong&gt; — After migration, diff the new map against the baseline.
Segment order and symbol addresses may shift; large or unexpected changes
warrant investigation. If overlay segment names or orders change, overlay
load addresses will differ—ensure overlay manager configuration matches the
new map.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;where-this-series-goes-next&#34;&gt;Where this series goes next&lt;/h2&gt;
&lt;p&gt;You asked for practical depth, so this series now has dedicated companion labs:&lt;/p&gt;
&lt;ul&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;Turbo Pascal Overlay Tutorial: Build, Package, and Debug an OVR Application&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;Turbo Pascal BGI Tutorial: Dynamic Drivers, Linked Drivers, and Diagnostic Harnesses&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;full-series-index&#34;&gt;Full series index&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;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;/ul&gt;
&lt;p&gt;If you want the next layer, I recommend one additional article focused only on
calling-convention lab work with map-file-backed stack tracing across Pascal and
assembly boundaries.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
