<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>cosninix</title>
        <link>https://cosninix.com</link>
        <description>Notes on things I build, fix and tinker with.</description>
        <language>en</language>
                    <item>
                <title>Dracula rises again: my 1980 text adventure, rebuilt from the EXE</title>
                <link>https://cosninix.com/posts/dracula-text-adventure</link>
                <guid>https://cosninix.com/posts/dracula-text-adventure</guid>
                <pubDate>Sat, 14 Nov 2026 00:00:00 +0000</pubDate>
                <description>&lt;p&gt;Yesterday I got an e-mail from Douwe Homans in Antwerp. He played my text adventure Dracula
as a child, and he had just reverse-engineered &lt;code&gt;DRACULA.EXE&lt;/code&gt; and rewritten the game in
JavaScript, as a web version. A game I wrote in 1980, whose source code I lost decades ago, was being played
again. I didn&#039;t get much sleep that night.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;dracula-1980&quot; href=&quot;#dracula-1980&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Dracula, 1980&lt;/h2&gt;
&lt;p&gt;As a student at TU Delft I played Colossal Cave Adventure and Zork (Dungeon, as the PDP-11
version was called) on a &lt;a href=&quot;https://cosninix.com/posts/pidp-11-vt100-la120&quot;&gt;PDP-11&lt;/a&gt;. I wanted one in Dutch, so I
wrote it: Dracula, in BASIC, on a Commodore PET 2001, in 8 KB. To make it fit, the texts
were very terse.&lt;/p&gt;
&lt;p&gt;Later, as a developer at Data Union, I ported it to CP/M on the 8080/Z80 and wrote out all
the texts in full. A Dutch linguist who worked there turned my fake old Dutch into real old
Dutch. You see that best in the &amp;quot;Vampier Handboeck&amp;quot; you find in the game:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vampieren sijn al sedert menschenheugenisse ende tiden die parasieten der menschen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The MS-DOS version is that CP/M source recompiled with BASCOM, Microsoft&#039;s BASIC compiler,
under PC-DOS 1.0, on the first IBM PC in the Netherlands, imported from the US: 4.77 MHz,
32 KB of memory and a 160 KB floppy drive. It was mostly a test of whether the compiler worked on that strange new
machine. It did. The EXE needs &lt;code&gt;BASRUN.EXE&lt;/code&gt;, the BASCOM runtime, next to it, and reads all
its texts from a file called &lt;code&gt;DRACULA.TXT&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In 1985 CYCO Automatisering sent it out as a New Year&#039;s card on floppy, about 200 copies.
Everybody copied it, and that is how it got popular in the Netherlands. My brother drew the
sleeve.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2026-11-14-dracula-text-adventure/floppy-sleeve.jpg&quot; alt=&quot;Hand-drawn floppy sleeve: a caricature of Dracula in front of a village and a castle under the moon, with the text &#039;Dracula Avontuur © &#039;82 incore&#039;&quot; width=&quot;250&quot;&gt;
  &lt;figcaption&gt;The floppy sleeve, drawn by my brother.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The BASIC source is on an 8-inch floppy that nothing can read anymore. In 2001, for the
game&#039;s twentieth birthday, I put the EXE online as freeware at cosninix.com/dracula. That
was all there was: a compiled program, a data file and a scan of the sleeve.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;an-e-mail-from-antwerp&quot; href=&quot;#an-e-mail-from-antwerp&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;An e-mail from Antwerp&lt;/h2&gt;
&lt;p&gt;Douwe&#039;s version runs at &lt;a href=&quot;https://dracula.apphost.be/&quot;&gt;dracula.apphost.be&lt;/a&gt;. You type your
name, and the browser remembers your game, so you can continue later. Same rooms, same
texts, same Dracula, no DOSBox needed. I played it through to the end, and it all works.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2026-11-14-dracula-text-adventure/web-version.jpg&quot; alt=&quot;Start page of the web version of Dracula Avontuur: the floppy sleeve, a short history of the game, and a box to type your name and start the adventure&quot; width=&quot;1200&quot;&gt;
  &lt;figcaption&gt;Douwe&#039;s web version.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;&lt;a id=&quot;getting-the-basic-back&quot; href=&quot;#getting-the-basic-back&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Getting the BASIC back&lt;/h2&gt;
&lt;p&gt;Douwe&#039;s mail made me want to see my own code again. That evening I asked Claude, on
claude.ai, whether it could reverse-engineer a program compiled with BASCOM. That works
better than you&#039;d think: BASCOM turns every BASIC statement into a fixed pattern of machine
code that calls routines in the runtime for &lt;code&gt;PRINT&lt;/code&gt;, &lt;code&gt;INPUT&lt;/code&gt;, &lt;code&gt;MID$&lt;/code&gt; and so on. Recognise
the calls and you get the statements back. The strings are in the EXE as plain text.&lt;/p&gt;
&lt;p&gt;Just after midnight I uploaded &lt;code&gt;DRACULA.EXE&lt;/code&gt; and &lt;code&gt;BASRUN.EXE&lt;/code&gt;. Claude recognised my own
game from the copyright string, and a quarter of an hour later there was a &lt;code&gt;DRACULA.BAS&lt;/code&gt;
of about 1,200 lines: the Dutch parser, the rooms, Dracula walking through his castle, save
and load, the bug reports and the title screen.&lt;/p&gt;
&lt;p&gt;What didn&#039;t come back is what the compiler had thrown away: variable names, comments and
line numbers. &lt;code&gt;R%&lt;/code&gt; for the room and &lt;code&gt;OL%()&lt;/code&gt; for where the objects are, are Claude&#039;s names,
not mine. Also missing: five little assembler routines that were linked into the EXE. The
game checked whether it ran on an IBM PC or a Zenith Z-100. On the IBM it cleared the screen
and positioned the cursor through those routines, on the Zenith with escape codes, and on
anything else it just printed line after line:&lt;/p&gt;
&lt;pre class=&quot;phiki phosphor&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;11940 IF IBM% &amp;lt;&amp;gt; 0 THEN CALL CLRSCR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;11950 IF ZEN% &amp;lt;&amp;gt; 0 THEN PRINT CHR$(27);&amp;quot;E&amp;quot;;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;11960 RETURN
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;11970 IF IBM% &amp;lt;&amp;gt; 0 THEN CALL CURSOR(X%,Y%)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;11980 IF ZEN% &amp;lt;&amp;gt; 0 THEN PRINT CHR$(27);&amp;quot;Y&amp;quot;;CHR$(Y% + 32);CHR$(X% + 32);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first &lt;code&gt;DRACULA.TXT&lt;/code&gt; I uploaded arrived mangled. Despite the name it is a binary file,
and the upload had treated it as text and converted the encoding. Zipped, it came through
intact: 1,289 records of 80 bytes with the exits per room, the room descriptions, the
messages and the objects, padded with zeros to a multiple of 128 bytes. That padding is a
CP/M leftover; CP/M stored files in 128-byte records.&lt;/p&gt;
&lt;p&gt;Ten minutes to one, I was standing in my own house again, in
&lt;a href=&quot;https://pc-basic.org&quot;&gt;PC-BASIC&lt;/a&gt;, a GW-BASIC emulator written in Python:&lt;/p&gt;
&lt;pre class=&quot;phiki phosphor&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Je bent nu in je eigen huis. Er is een slaapkamer op het zuiden
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;en een kleine hal oostelijk. Er is een deur die naar het westen leidt. De
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;deur staat op een kier en een gure wind blaast zand en stof naar binnen.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Door een raam kan je een bos zien.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Er zit een gat in het plafond, maar je kunt het niet bereiken.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Het gat is groot genoeg om er doorheen te klimmen.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Er is een klein briefje hier.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;-&amp;gt;z
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Je bent nu in de slaapkamer. De enige uitgang is naar het
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;noorden, waar een rommelige kamer te zien is. Het is vrij donker
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;hier, maar door een klein raampje valt net voldoende licht binnen
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;om te kunnen zien.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Er is een kleine brandende lantaren hier.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Er is een zwaar eikenhouten bed hier.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;-&amp;gt;pak lamp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Ok
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a id=&quot;1980-from-the-inside&quot; href=&quot;#1980-from-the-inside&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;1980 from the inside&lt;/h2&gt;
&lt;p&gt;This is what I love about software from that time: everything had to fit, so every byte
has a job, and 44 years later you can still see every decision. The verb parser is one long
chain of &lt;code&gt;IF&lt;/code&gt;s:&lt;/p&gt;
&lt;pre class=&quot;phiki phosphor&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;740 IF V$ = &amp;quot;GA&amp;quot; OR V$ = &amp;quot;BETRE&amp;quot; OR V$ = &amp;quot;KRUIP&amp;quot; OR V$ = &amp;quot;LOOP&amp;quot; OR V$ = &amp;quot;KLIM&amp;quot; OR V$ = &amp;quot;VOLG&amp;quot; THEN 1260
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;750 IF V$ = &amp;quot;$RVW&amp;quot; THEN PRINT &amp;quot;Room&amp;quot;;R% : INPUT &amp;quot;Room&amp;quot;; R% : GOTO 340
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;760 IF (V$ = &amp;quot;KIJK&amp;quot; OR V$ = &amp;quot;K&amp;quot;) AND N$ = &amp;quot;&amp;quot; THEN 340
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only the first five letters of a verb and the first four of a noun count, so &lt;code&gt;BETREED&lt;/code&gt;
becomes &lt;code&gt;BETRE&lt;/code&gt;. Colossal Cave Adventure also looked at only five letters. Line 750 is my
cheat: type &lt;code&gt;$RVW&lt;/code&gt; and the game asks which room you want to go to.&lt;/p&gt;
&lt;p&gt;The six magic words are in &lt;code&gt;DATA&lt;/code&gt; lines, every letter shifted by one, so they don&#039;t show up
as text in the EXE:&lt;/p&gt;
&lt;pre class=&quot;phiki phosphor&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;10 DATA &amp;quot;WBNQJSBOJB&amp;quot;,&amp;quot;GMBVXFLVMB&amp;quot;,&amp;quot;ESBLFMTUFJOJB&amp;quot;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;11 DATA &amp;quot;NJDSPTPGUFOTUFJO&amp;quot;,&amp;quot;JODPSPOJVN&amp;quot;,&amp;quot;USBOTTZMWBOJVN&amp;quot;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;WBNQJSBOJB&lt;/code&gt; is VAMPIRANIA. The fourth one is MICROSOFTENSTEIN. Every game picks one at
random.&lt;/p&gt;
&lt;p&gt;There is a tester mode that writes the testers&#039; remarks to a file, a &lt;code&gt;BUG&lt;/code&gt; command for bug
reports, a list of Dutch swear words with an answer of its own, and bugs of mine that
survived all those years: saving the game forgets the ladder in the house, &amp;quot;Illigal item
number&amp;quot; is spelled like that, and drinking the poison doesn&#039;t kill you, because the routine
that runs every turn immediately resets the death flag. Claude kept them all.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;two-bugs-that-werent-mine&quot; href=&quot;#two-bugs-that-werent-mine&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Two bugs that weren&#039;t mine&lt;/h2&gt;
&lt;p&gt;Then I moved to Claude Code, in the project folder. Under PC-BASIC the game sometimes
crashed when you opened the coffin in the tomb:&lt;/p&gt;
&lt;pre class=&quot;phiki phosphor&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;KeyError: Dereferencing detached string
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That turned out to be a bug in PC-BASIC 2.0.8. &lt;code&gt;INSTR&lt;/code&gt;, &lt;code&gt;LEFT$&lt;/code&gt;, &lt;code&gt;RIGHT$&lt;/code&gt; and &lt;code&gt;MID$&lt;/code&gt;
register their string arguments as temporary values, but skip the clean-up when they return
early, for instance when &lt;code&gt;INSTR&lt;/code&gt; finds nothing. My parser calls &lt;code&gt;INSTR&lt;/code&gt; a few times for every
command, and most of those calls find nothing. After one playthrough some 900 temporary
strings had leaked, the string space was full, and the garbage collector ran every few
statements, most of all in the busy-wait loop at the coffin. Sooner or later a leaked value
pointed to a string that had already been freed. The current master of PC-BASIC on GitHub
still has this bug.&lt;/p&gt;
&lt;p&gt;The fix is &lt;code&gt;pcbasic_fix.py&lt;/code&gt;, a launcher that wraps those four functions, cleans up after
them and then starts PC-BASIC. It also fixes a second bug: in terminal mode PC-BASIC didn&#039;t
scroll during the autoplay, so every line overwrote the one before. The crash was rare to
begin with, so I can&#039;t prove it&#039;s gone, but the leak went from 900 values to zero.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;and-then-in-c&quot; href=&quot;#and-then-in-c&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;And then in C&lt;/h2&gt;
&lt;p&gt;At two in the morning I asked for a C version. &lt;code&gt;dracula.c&lt;/code&gt; is about 2,200 lines of C99,
without the GOTO spaghetti: every verb is a function that returns where the BASIC would have
jumped to, and one main loop does the jumping. Each function names the BASIC line it came
from.&lt;/p&gt;
&lt;p&gt;The fun part was making it play exactly the same game. &lt;code&gt;RND&lt;/code&gt; in C uses the same generator as
GW-BASIC:&lt;/p&gt;
&lt;pre class=&quot;phiki language-c phosphor&quot; data-language=&quot;c&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;rnd_zaad &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;rnd_zaad &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;214013&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;2531011&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; RND_PERIODE&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; RND_PERIODE = 2^24 &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;*/&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And BASIC evaluates every term of an &lt;code&gt;AND&lt;/code&gt; or &lt;code&gt;OR&lt;/code&gt;, even when the outcome is already clear.
So when &lt;code&gt;RND&lt;/code&gt; is part of such a condition, a number is drawn every time. C stops evaluating
early, so the C version draws those numbers up front; otherwise the random sequence drifts
apart after a few turns. With that in place, the autoplay (the game plays its own solution
from the file &lt;code&gt;PLAYER&lt;/code&gt;) gives the same output line for line in C and in PC-BASIC, all the way
to the treasure, and a saved game is byte-for-byte the same. In C you can even reincarnate
after dying, which under PC-BASIC stops with &amp;quot;File already open&amp;quot;.&lt;/p&gt;
&lt;p&gt;Before three o&#039;clock the reconstructed BASIC, the C version and a README in English and
Dutch were in git.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;play-it&quot; href=&quot;#play-it&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Play it&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;In your browser: &lt;a href=&quot;https://dracula.apphost.be/&quot;&gt;Douwe&#039;s web version&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The source: &lt;a href=&quot;https://github.com/rvwoens/dracula&quot;&gt;github.com/rvwoens/dracula&lt;/a&gt;, with
&lt;code&gt;DRACULA.BAS&lt;/code&gt; for PC-BASIC, &lt;code&gt;dracula.c&lt;/code&gt; and the original EXE from 2001.&lt;/li&gt;
&lt;li&gt;The original for DOSBox: the download on &lt;a href=&quot;https://cosninix.com/dracula&quot;&gt;/dracula&lt;/a&gt;. That&#039;s the page from 2001,
back at its old address.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The C version builds on macOS and Linux:&lt;/p&gt;
&lt;pre class=&quot;phiki language-shellscript phosphor&quot; data-language=&quot;shellscript&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;https://github.com/rvwoens/dracula&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;dracula&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;cc&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;O2&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;dracula&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;dracula.c&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot;&gt;./dracula&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The game is in Dutch. Don&#039;t swear at it:&lt;/p&gt;
&lt;pre class=&quot;phiki phosphor&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;-&amp;gt;shit
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;Zeker christelijke opvoeding gehad ?   Dacht ik al.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#039;s &amp;quot;Christian upbringing, I suppose? Thought so.&amp;quot;&lt;/p&gt;
</description>
            </item>
                    <item>
                <title>Back to Unix V6, on a PiDP-11/70 with my own VT100 and LA120</title>
                <link>https://cosninix.com/posts/pidp-11-vt100-la120</link>
                <guid>https://cosninix.com/posts/pidp-11-vt100-la120</guid>
                <pubDate>Sat, 31 Oct 2026 00:00:00 +0000</pubDate>
                <description>&lt;p&gt;In 1978 or 1979 I programmed on a PDP-11/60 at the mathematics department of TU Delft. It
was a &amp;quot;low boy&amp;quot; cabinet without blinkenlights, and you could extend its instruction set in
microcode. It ran Unix V6, and what struck me most was that the complete source code of
Unix was on the machine. I printed the sources of yacc and lex. As a student project we
wrote a full-screen editor in C for the DEC VT100 terminals, called zed. Everybody loved
it. It is lost now.&lt;/p&gt;
&lt;p&gt;I still have a terminal from DEC&#039;s VT100 family, a VT131, and an LA120, DEC&#039;s printing
terminal with a keyboard. Besides the 11/60, I also worked on PDP-11/70s in those days. So
when I built a PiDP-11/70, a replica of that machine, both terminals had to be connected
to it.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;the-pidp-11&quot; href=&quot;#the-pidp-11&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;The PiDP-11&lt;/h2&gt;
&lt;p&gt;The PiDP-11/70 is a replica of the 11/70 front panel with a Raspberry Pi inside; mine is a
Pi 3B+. The Pi runs SIMH, and the switches on the panel choose what to boot:&lt;/p&gt;
&lt;pre class=&quot;phiki phosphor&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;PiDP-11/70 boot menu - Set SR switches, depress address rotary switch to reboot
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;-------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;0000    idled   0101    unixv1  0107    unix7   1001    idled
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;0001    rsx11mp 0102    211bsd  0113    sysiii  1002    blinky
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;0002    rsts7   0104    unix4   0115    sysv    2024    rsx11bq
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;0003    rt11    0105    unix5   0211    211bsd+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;0004    dos11   0106    unix6   1000    nkrvs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The installer expects the default &lt;code&gt;pi&lt;/code&gt; user. My first attempt under my own account ended in
errors about &lt;code&gt;/home/pi/pdp.sh&lt;/code&gt; and a missing &lt;code&gt;rpcbind.service&lt;/code&gt;. With a &lt;code&gt;pi&lt;/code&gt; user, a second
run of &lt;code&gt;install.sh&lt;/code&gt; was clean. A systemd unit starts SIMH at boot, straight into V6 (switch
setting 0106). Stopping SIMH can hang, so the unit gets a timeout:&lt;/p&gt;
&lt;pre class=&quot;phiki language-ini phosphor&quot; data-language=&quot;ini&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;/opt/pidp11/bin/pdp11control.sh start 106
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;TimeoutStopSec&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;10
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a id=&quot;backspace-on-v6&quot; href=&quot;#backspace-on-v6&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Backspace on V6&lt;/h2&gt;
&lt;p&gt;Out of the box, V6 erases a character with &lt;code&gt;#&lt;/code&gt; and kills the line with &lt;code&gt;@&lt;/code&gt;. Backspace (^H)
moves the cursor back, but the character stays on the screen, and you can backspace right
past the start of the line. V6 was made for paper terminals, where nothing can be erased.&lt;/p&gt;
&lt;p&gt;Terminal handling lives in the kernel, so the fix is in C. In &lt;code&gt;ttyinput()&lt;/code&gt; in
&lt;code&gt;/usr/sys/dmr/tty.c&lt;/code&gt;, the erase character now echoes &lt;code&gt;\b \b&lt;/code&gt; instead of itself, so the
character really disappears. Then rebuild the kernel:&lt;/p&gt;
&lt;pre class=&quot;phiki language-shellscript phosphor&quot; data-language=&quot;shellscript&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; chdir /usr/sys/dmr
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; cc -c -O tty.c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; ar r ../lib2 tty.o
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; chdir ../conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; mkconf &amp;lt; myconf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; cc -c c.c
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; as l.s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; ld -x a.out m40.o c.o ../lib1 ../lib2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; cp a.out /nunix
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; sync
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; sync
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-dim);&quot;&gt; sync
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;V6 has no &lt;code&gt;.profile&lt;/code&gt; to run &lt;code&gt;stty&lt;/code&gt; at every login, so I changed the defaults instead:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/usr/sys/tty.h&lt;/code&gt;: &lt;code&gt;CERASE&lt;/code&gt; and &lt;code&gt;CKILL&lt;/code&gt; to &lt;code&gt;010&lt;/code&gt; (^H) and &lt;code&gt;025&lt;/code&gt; (^U).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/usr/source/s1/getty.c&lt;/code&gt;: &lt;code&gt;ERASE&lt;/code&gt; and &lt;code&gt;KILL&lt;/code&gt; the same, because getty sets them on the
line before it starts login.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/usr/source/s1/login.c&lt;/code&gt;: login switches echo off and on again around the password, and
in doing so it put &lt;code&gt;#&lt;/code&gt; and &lt;code&gt;@&lt;/code&gt; back. Now it keeps whatever getty set.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While I was at it, the shell learned &lt;code&gt;cd&lt;/code&gt;. The V6 shell only knows &lt;code&gt;chdir&lt;/code&gt;; in
&lt;code&gt;/usr/source/s2/sh.c&lt;/code&gt; that is one line:&lt;/p&gt;
&lt;pre class=&quot;phiki language-c phosphor&quot; data-language=&quot;c&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;cp1&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;chdir&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;cp1&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compile with &lt;code&gt;cc -s -n -O sh.c&lt;/code&gt; (&lt;code&gt;-n&lt;/code&gt; gives pure text, so all running shells share the
code). To install it, &lt;code&gt;mv /bin/sh /bin/osh&lt;/code&gt; first and then copy the new one in: V6 won&#039;t
overwrite a program that is running (&amp;quot;text busy&amp;quot;), but renaming it is fine.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;the-la120&quot; href=&quot;#the-la120&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;The LA120&lt;/h2&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2026-10-31-pidp-11-vt100-la120/la120.jpg&quot; alt=&quot;A DEC LA120 printing terminal seen from above, with tractor-fed paper and its keyboard&quot;&gt;
  &lt;figcaption&gt;The LA120.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For the serial ports I use two USB-to-RS232 adapters with an FTDI FT232R chip; the first
one is Waveshare&#039;s industrial model. I measured the pins with a multimeter to see which side was which (pin 2 sits at
−5.2 V, so the adapter is wired as DCE) and soldered my own cables. A udev rule per adapter,
matched on the FTDI serial number, gives fixed names, &lt;code&gt;/dev/la120&lt;/code&gt; and &lt;code&gt;/dev/vt100&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;phiki phosphor&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;SUBSYSTEM==&amp;quot;tty&amp;quot;, ATTRS{idVendor}==&amp;quot;0403&amp;quot;, ATTRS{idProduct}==&amp;quot;6001&amp;quot;, ATTRS{serial}==&amp;quot;&amp;lt;serial&amp;gt;&amp;quot;, SYMLINK+=&amp;quot;la120&amp;quot;, GROUP=&amp;quot;dialout&amp;quot;, MODE=&amp;quot;0660&amp;quot;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In SIMH, the LA120 is line 7 of the emulated DC11:&lt;/p&gt;
&lt;pre class=&quot;phiki phosphor&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;att dci line=7,connect=/dev/la120;9600-8N1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;! stty -F /dev/la120 ixon opost onlcr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each line in that second command fixed something:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Printer runs over after about 15 lines&lt;/td&gt;
&lt;td&gt;The LA120 sends XON/XOFF, the host ignored ^S&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ixon&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No carriage returns, text comes out as a staircase&lt;/td&gt;
&lt;td&gt;SIMH turns output processing off&lt;/td&gt;
&lt;td&gt;&lt;code&gt;opost onlcr&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settings gone after SIMH starts&lt;/td&gt;
&lt;td&gt;SIMH sets the port when it attaches&lt;/td&gt;
&lt;td&gt;the &lt;code&gt;! stty&lt;/code&gt; right after the attach&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The strangest problem was an endless loop of &lt;code&gt;Line hangup&lt;/code&gt;. The V6 DC11 driver
(&lt;code&gt;/usr/sys/dmr/dc.c&lt;/code&gt;) doesn&#039;t know 9600 baud: its speed table has a 0 there, without the
bit that keeps DTR up. When getty set the line to 9600, DTR dropped, SIMH saw a hangup,
reset the line, and it started over. The fix is to let getty set 1200 baud instead:&lt;/p&gt;
&lt;pre class=&quot;phiki language-c phosphor&quot; data-language=&quot;c&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot;&gt;B1200&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and &lt;code&gt;B1200, B1200&lt;/code&gt; in getty&#039;s table &lt;code&gt;&#039;2&#039;&lt;/code&gt;. The real speed on the wire is whatever SIMH
says, &lt;code&gt;9600-8N1&lt;/code&gt;, so the printer still runs at 9600.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2026-10-31-pidp-11-vt100-la120/la120-line-hangup.jpg&quot; alt=&quot;Close-up of LA120 paper reading Line hangup, two times Login incorrect, a password prompt and a row of percent-sign prompts&quot;&gt;
  &lt;figcaption&gt;Still on the paper: a &lt;code&gt;Line hangup&lt;/code&gt;, two failed logins and finally a shell.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;&lt;a id=&quot;the-vt131&quot; href=&quot;#the-vt131&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;The VT131&lt;/h2&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2026-10-31-pidp-11-vt100-la120/vt131-terminal.jpg&quot; alt=&quot;A DEC VT131 terminal with its keyboard, standing on top of a pinball machine&quot;&gt;
  &lt;figcaption&gt;The VT131.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The VT131 went on the second adapter. The first test showed garbage, and after that,
nothing: the terminal was still in LOCAL mode.&lt;/p&gt;
&lt;p&gt;On my old Linux box the terminal had a login banner with DEC escape codes: &amp;quot;Login&amp;quot; in
double-height letters (&lt;code&gt;ESC #3&lt;/code&gt; for the top half, &lt;code&gt;ESC #4&lt;/code&gt; for the bottom half), in reverse,
bold and blinking. I wanted that on V6 too. On the Pi:&lt;/p&gt;
&lt;pre class=&quot;phiki language-shellscript phosphor&quot; data-language=&quot;shellscript&quot; style=&quot;background-color: var(--code-bg);color: var(--phos);&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;token&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;&amp;#039;&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;\033[H\033[J\033[0m\r\n\033[7m\033[1m\033[5m\r\n\033#3 Login \r\n\033#4 Login \r\n\033[0m\r\nUnix V6 on a PDP-11/70\r\n\r\n&lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;&amp;#039;&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;token&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token&quot; style=&quot;color: var(--phos-bright);&quot;&gt;/tmp/issue.v&lt;/span&gt;&lt;span class=&quot;token&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That file went into V6 through the emulated paper tape reader, &lt;code&gt;cp /dev/ppt /etc/issue.v&lt;/code&gt;:
82 bytes, the same as on the Pi. getty got a new table &lt;code&gt;&#039;v&#039;&lt;/code&gt; for the terminal and a few
lines to print the file before &lt;code&gt;login:&lt;/code&gt;. In &lt;code&gt;/etc/ttys&lt;/code&gt;, the entry for line 6 became &lt;code&gt;16v&lt;/code&gt;,
and the VT131 now shows its big blinking &amp;quot;Login&amp;quot; again, this time from Unix V6.&lt;/p&gt;
</description>
            </item>
                    <item>
                <title>Retrotractor: A head gasket story on my MF133&#039;s Perkins A3.144 fixed with AI</title>
                <link>https://cosninix.com/posts/mf133-perkins-head-gasket</link>
                <guid>https://cosninix.com/posts/mf133-perkins-head-gasket</guid>
                <pubDate>Sun, 07 Dec 2025 00:00:00 +0000</pubDate>
                <description>&lt;p&gt;In November 2025 my Massey Ferguson 133 started running badly. It had little power, idled
roughly and felt like it was running on two of its three cylinders. There was no black or
white smoke at no load, but when I switched it off, the exhaust made a strange sucking,
blowing noise. This is how I found out what was wrong.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;which-engine-is-it&quot; href=&quot;#which-engine-is-it&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Which engine is it?&lt;/h2&gt;
&lt;p&gt;Before you can order a single part for an old tractor, you need to know what is in it. The
MF133 has a three-cylinder Perkins diesel, but which one? When I first read up on
compression tests, in June 2025, I called it an A3.152. Tractordata lists the 1969–1974
MF133 with an AD3.144. On forums people say the early French-built 133s had the A3.144. And
the parts shops sell almost everything &amp;quot;for A3.144/A3.152&amp;quot;, which doesn&#039;t narrow it down.&lt;/p&gt;
&lt;p&gt;On the engine itself I found:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GKN1308&lt;/code&gt; and &lt;code&gt;J12-1&lt;/code&gt; cast into the cylinder head;&lt;/li&gt;
&lt;li&gt;covers over the injection chambers, and straight injectors;&lt;/li&gt;
&lt;li&gt;inlet and exhaust manifolds on opposite sides of the head;&lt;/li&gt;
&lt;li&gt;the head held down by 15 nuts on studs (more about those below).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Perkins AD engines have direct injection. Mine injects into a chamber in the head, so
it can&#039;t be an AD, which rules out tractordata&#039;s AD3.144. That leaves the A3.144 or the
A3.152. What settled it is that mine is a French-built MF133, and those had the A3.144:
three cylinders, 144 cubic inches.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;valves-first&quot; href=&quot;#valves-first&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Valves first&lt;/h2&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/rocker-gear.jpg&quot; alt=&quot;Top of the Perkins A3.144 with the rocker cover off: the rocker shaft with six rockers, the injectors and their pipes alongside&quot; width=&quot;450&quot;&gt;
  &lt;figcaption&gt;Rocker cover off.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The first suspect was the valve gear. With the rocker cover off I checked the clearances:
0.30 mm on all six valves, cold. I hadn&#039;t adjusted anything. Turning the engine over by
hand, all rockers, pushrods and valves moved as they should.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/checking-valve-clearance.jpg&quot; alt=&quot;A set of feeler gauges fanned out, one blade between a rocker and the top of a valve stem&quot; width=&quot;450&quot;&gt;
  &lt;figcaption&gt;Checking valve clearance with feeler gauges.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;That doesn&#039;t clear the valves, though. A valve that is burnt, or doesn&#039;t seat, still shows a
perfect clearance at the rocker; it just doesn&#039;t seal at the bottom. And the noise at
shut-down pointed that way. Once the engine stops, the injection pump stops too, so a
blowing noise in the exhaust can&#039;t come from an injector. It has to be air, getting past a
valve that doesn&#039;t close.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;compression-test&quot; href=&quot;#compression-test&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Compression test&lt;/h2&gt;
&lt;figure&gt;
  &lt;video src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/compression-test.mp4&quot; poster=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/compression-test-poster.jpg&quot; controls muted loop playsinline width=&quot;320&quot;&gt;&lt;/video&gt;
  &lt;figcaption&gt;Compression test: the gauge sits in the injector hole while the starter cranks the engine.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The gauge screws into the injector hole: injectors out, gauge in, crank on the starter.
Counting the cylinders from the radiator:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cylinder&lt;/th&gt;
&lt;th&gt;Compression&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;176 psi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;150 psi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;178 psi&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Cylinder 2 was clearly down. Then I fed compressed air into the cylinders through the
injector holes. On cylinders 1 and 2 I heard it hiss in the exhaust. For me that settled
it: an exhaust valve that didn&#039;t seal, probably a burnt one.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/compression-gauge.jpg&quot; alt=&quot;A compression gauge with psi and bar scales resting on the tractor&#039;s battery, its hose leading to the engine&quot; width=&quot;450&quot;&gt;
  &lt;figcaption&gt;The gauge, scaled in psi and bar.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;&lt;a id=&quot;a-new-head-or-the-old-one&quot; href=&quot;#a-new-head-or-the-old-one&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;A new head, or the old one?&lt;/h2&gt;
&lt;p&gt;With a bad valve the head had to come off anyway, so I looked at replacement heads. Several
shops sell a complete head with valves for the A3.144 and A3.152 under the number ZZ80048.
They are aftermarket parts, and I couldn&#039;t find out who makes them or how they are
checked. The alternative was reconditioning my own head: new guides, new valves, lapping
them in. Either way, the head had to come off first, so I could see what I was dealing with.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/stripped-down.jpg&quot; alt=&quot;The MF133 in the shed with bonnet, tank and radiator removed, the engine exposed down to the front axle&quot;&gt;
  &lt;figcaption&gt;Stripped down to get at the head.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;&lt;a id=&quot;testing-the-injectors&quot; href=&quot;#testing-the-injectors&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Testing the injectors&lt;/h2&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/injector-on-the-bench.jpg&quot; alt=&quot;A removed injector lying on the workbench next to a green rag&quot; width=&quot;450&quot;&gt;
  &lt;figcaption&gt;One of the three injectors.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;With the injectors out, I wanted to test them too. I bought a VEVOR pop tester, a hand
pump with a gauge. Its outlets are M14×1.5. The fuel inlet on these injectors is supposed
to be a male 9/16&amp;quot;-18 UNF thread, so I expected to need an adapter. ChatGPT agreed, then
looked at a photo and declared it &amp;quot;100% M14×1.5&amp;quot;, then went back to UNF.&lt;/p&gt;
&lt;p&gt;The two threads are close:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;M14×1.5&lt;/th&gt;
&lt;th&gt;9/16&amp;quot;-18 UNF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Outside diameter&lt;/td&gt;
&lt;td&gt;14.00 mm&lt;/td&gt;
&lt;td&gt;14.29 mm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pitch&lt;/td&gt;
&lt;td&gt;1.50 mm&lt;/td&gt;
&lt;td&gt;1.41 mm&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I measured 14 mm across the thread, and an M14×1.5 nut screwed on perfectly. On a real
9/16&amp;quot;-18 thread it shouldn&#039;t: the pitches differ by 0.09 mm per turn. So either this
tractor, where everything else is in inches, has metric injector inlets, or a metric nut
runs on a UNF thread after all. I still don&#039;t know which.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/injector-inlet.jpg&quot; alt=&quot;Close-up of an injector body held in a hand, showing the threaded fuel inlet&quot; width=&quot;450&quot;&gt;
  &lt;figcaption&gt;The injector&#039;s fuel inlet.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;On these injector pipes the thread doesn&#039;t seal anything. The pipe ends in a cone that
presses into a cone seat in the injector; the nut only pulls it in. So the test line was
just a short pipe with a cone and an M14×1.5 nut, and it simply fitted. If the inlet is
UNF, that is a garage trick and purists should look away; either way, no adapter needed.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/test-pipe-m14-nut.jpg&quot; alt=&quot;A short steel pipe with an M14×1.5 nut and a cone-shaped end&quot; width=&quot;450&quot;&gt;
  &lt;figcaption&gt;Test pipe with a cone end and an M14×1.5 nut.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It worked: the joint stayed dry. The injectors opened at around 2000 psi (about 140 bar),
nothing leaked, and the pressure just stayed up. All three were fine.&lt;/p&gt;
&lt;figure&gt;
  &lt;video src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/injector-test.mp4&quot; poster=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/injector-test-poster.jpg&quot; controls muted loop playsinline&gt;&lt;/video&gt;
  &lt;figcaption&gt;An injector on the pop tester, connected with the M14×1.5 test pipe.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;&lt;a id=&quot;the-head-comes-off&quot; href=&quot;#the-head-comes-off&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;The head comes off&lt;/h2&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/head-ready-to-lift.jpg&quot; alt=&quot;The cylinder head seen from above with the rocker shaft removed: six valve springs, the head nuts, and GKN cast into the head&quot; width=&quot;450&quot;&gt;
  &lt;figcaption&gt;Rocker shaft off, ready to lift the head.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When the head finally came off, it wasn&#039;t the valves. The head gasket had burnt through
between cylinders 1 and 2, the front one and the middle one: the same two that had hissed
in the air test. That explains the hiss as well. Air pushed into one of them escaped through
the gasket into the other, and out through its exhaust valve if that happened to be open.
The valves were fine.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/block-with-old-gasket.jpg&quot; alt=&quot;The engine block with the head removed: the old copper head gasket still over the three bores, with the long head studs standing up&quot; width=&quot;450&quot;&gt;
  &lt;figcaption&gt;Head off, old gasket still on the block.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/burnt-gasket.jpg&quot; alt=&quot;Close-up of the copper head gasket between two cylinder bores, burnt away where the two fire rings meet&quot; width=&quot;450&quot;&gt;
  &lt;figcaption&gt;The burnt spot between cylinders 1 and 2.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The most likely cause is a broken thermostat, or the engine having run short of oil at some
point.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;does-the-head-need-skimming&quot; href=&quot;#does-the-head-need-skimming&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Does the head need skimming?&lt;/h2&gt;
&lt;p&gt;The usual advice is to skim the head whenever it is off. I cleaned the head and the block
with a razor blade, corrosion included. The corrosion was only around the water passages,
and between the cylinders there was no pitting: both surfaces were smooth. ChatGPT&#039;s advice:
with surfaces like that and a copper head gasket, which tolerates small imperfections, it
could go back on without skimming. So it did.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;nuts-and-studs&quot; href=&quot;#nuts-and-studs&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Nuts and studs&lt;/h2&gt;
&lt;p&gt;The head is held down by 15 nuts on studs. ChatGPT spoke of head bolts and first told me 18
(three rows of six), then 11, &amp;quot;like most A3.144s&amp;quot;. Only when I showed it a drawing with 15
did it agree; I counted, and mine has 15. For the torque I read 74.8–81.6 Nm
(55–60 lbf·ft); ChatGPT gave 95 Nm for this head. As far as I remember, I went with the
95 Nm.&lt;/p&gt;
&lt;p&gt;The other fasteners are a mix too. The exhaust manifold nuts take a 1/2&amp;quot; spanner, and the
studs measure 7.9 mm: 5/16&amp;quot;. The prechamber cap nuts take 9/16&amp;quot;. Yet the Perkins part number
for the manifold stud, 37781251, is a 3/8&amp;quot; UNF × 1 5/8&amp;quot; stud, and one supplier sells a
single 3/8&amp;quot; UNF stud for both the exhaust and the prechambers. On an engine this old you
measure before you order.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;back-together&quot; href=&quot;#back-together&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Back together&lt;/h2&gt;
&lt;p&gt;New gasket set, new coolant hoses and a new thermostat. The leak-off pipe was leaking too:
the line from the injectors&#039; leak-off back to the filter, with branches to the tank and to
the heater plug. I replaced the T-piece in the middle with a compression T.&lt;/p&gt;
&lt;p&gt;With the tank on the tractor I couldn&#039;t see the engine, so for the first runs it got a
small improvised tank, mounted above the lift pump, with the return line running back into
it.&lt;/p&gt;
&lt;figure&gt;
  &lt;img loading=&quot;lazy&quot; src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/improvised-tank.jpg&quot; alt=&quot;An improvised fuel tank: a cut-open washer-fluid jug hanging on the tractor, with diesel and a fuel hose in it&quot; width=&quot;320&quot;&gt;
  &lt;figcaption&gt;The temporary tank.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After the first heat cycle the head nuts get re-torqued. I ran the engine up to
temperature with the rocker cover off, let it cool down completely, and then went over the
nuts in sequence, tightening each back to torque without slackening it first. ChatGPT
first told me to loosen every nut and re-torque it, and changed its mind when I asked
whether it was sure.&lt;/p&gt;
&lt;figure&gt;
  &lt;video src=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/running-again.mp4&quot; poster=&quot;https://cosninix.com/images/posts/2025-12-07-mf133-perkins-head-gasket/running-again-poster.jpg&quot; controls muted loop playsinline width=&quot;320&quot;&gt;&lt;/video&gt;
  &lt;figcaption&gt;Running again, rocker cover still off.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It runs like a train again, as we say in Dutch.&lt;/p&gt;
&lt;h2&gt;&lt;a id=&quot;a-note-on-chatgpt&quot; href=&quot;#a-note-on-chatgpt&quot; class=&quot;heading-permalink&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; title=&quot;Link to this section&quot;&gt;#&lt;/a&gt;A note on ChatGPT&lt;/h2&gt;
&lt;p&gt;I worked through all of this in chats with ChatGPT. That was useful for procedures and for
thinking out loud. But it gave numbers with great confidence that changed as soon as I
pushed back: 18, 11 and then 15 head bolts; the injector thread was UNF, then
&amp;quot;100% M14×1.5&amp;quot; after a photo, then UNF again (I still don&#039;t know which one was right).
The 15 I got by counting the nuts myself.&lt;/p&gt;
</description>
            </item>
            </channel>
</rss>
