<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://www.leadedsolder.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.leadedsolder.com/" rel="alternate" type="text/html" /><updated>2026-10-15T12:25:48+00:00</updated><id>https://www.leadedsolder.com/feed.xml</id><title type="html">Leaded Solder</title><subtitle>Old computer repair, history and programming.</subtitle><entry><title type="html">Cleaning up after a Camel</title><link href="https://www.leadedsolder.com/2026/10/13/rc2026-camelforth-sg1000-fix-bugs-sn76489-words.html" rel="alternate" type="text/html" title="Cleaning up after a Camel" /><published>2026-10-13T00:00:00+00:00</published><updated>2026-10-13T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/10/13/rc2026-camelforth-sg1000-fix-bugs-sn76489-words</id><content type="html" xml:base="https://www.leadedsolder.com/2026/10/13/rc2026-camelforth-sg1000-fix-bugs-sn76489-words.html">&lt;p&gt;Now that my port of CamelForth-80 to the Sega SG-1000 is sort of working, it’s time to get it working &lt;em&gt;better&lt;/em&gt;. There’s a lot of undone odds and ends and little bugs that I need to take care of before I can try running it on real hardware, so this week’s (abbreviated) effort was mostly along those lines.&lt;/p&gt;

&lt;h2 id=&quot;vdp-delay&quot;&gt;VDP Delay&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;/tag/sg1000&quot;&gt;Sega SG-1000&lt;/a&gt; is designed around the Texas Instruments TMS9918A video display processor. A lot of great machines use chips in this family, including &lt;a href=&quot;/tag/colecovision&quot;&gt;the ColecoVision&lt;/a&gt;, the original series of &lt;a href=&quot;/tag/msx&quot;&gt;MSX&lt;/a&gt;, the &lt;a href=&quot;/tag/dick-smith-creativision-wizzard&quot;&gt;VTech CreatiVision&lt;/a&gt;, the &lt;a href=&quot;/tag/bbc-bridge-companion&quot;&gt;BBC Bridge Companion&lt;/a&gt;, and the &lt;a href=&quot;/tag/sord-m5&quot;&gt;Sord M5&lt;/a&gt;. In fact, it’s pretty common to see posts about the TMS99xx around these parts.&lt;/p&gt;

&lt;p&gt;Originally, this part was designed for the TI99/4A. Being such a complex part, it does have its little foibles, and one of them is that it takes the chip awhile to “start up” before it will respond properly to commands. We saw this problem &lt;a href=&quot;https://www.leadedsolder.com/2022/06/21/sg1000-clone-v2.html#more-ram&quot;&gt;back when I was building the Sega SG-1000 RAM test cartridge&lt;/a&gt;, where we found that if I started doing stuff as soon as the Z80 was running, some commands would get dropped or otherwise ignored.&lt;/p&gt;

&lt;p&gt;All of the original Sega games seem to include some variant of a wait. They either stall explicitly with a “busy loop” for many seconds to wait for the chip to come awake, or spend the free time doing a bunch of Z80 processing and setup for the main game. On the ColecoVision, this isn’t as much of a problem, as the ColecoVision’s BIOS is responsible for setting up the VDP and then handing control to the game. By the time you’re running on that platform, everything is already warmed up and ready to go. The SG-1000 is not as fortunate, seeing as it lacks a BIOS entirely.&lt;/p&gt;

&lt;p&gt;So why didn’t we have this problem before? Well, it’s because we’ve only been running this code on MAME, which doesn’t accurately simulate this aspect of the TMS9918A. By the time MAME starts, that 9918 is ready to go. For compatibility with real hardware, this is something that would need to be fixed.&lt;/p&gt;

&lt;p&gt;For the purposes of the CamelForth-80 port, I just copied my busy-waiting code from my RAM tester:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sg1000_cold_start:
    ; The VDP gets reset at system startup, but it takes a lot
    ; longer than the CPU to be ready for action. The BIOS on the
    ; ColecoVision/MSX/etc seems to take care of this problem for games.
    ; We could do a bunch of setup stuff here, but for now we&apos;ll just spin until
    ; we think the VDP should be willing to listen to us.
#local
BusyWait:
    ld d, $3 ; girls garden used $2 x $ffff
_busywait_outer:
    ld hl, $ffff
_busywait_inner:
    dec hl
    ld a,h
    or l
    jr nz, _busywait_inner
    dec d
    jr nz, _busywait_outer
    ret
#endlocal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, it’s a little slower to test the code in the emulator, but it should be more stable on the real SG-1000. How are we going to get it onto the real SG-1000? We’ll get to that in a second.&lt;/p&gt;

&lt;h2 id=&quot;bad-characters-hanging-around&quot;&gt;Bad characters hanging around&lt;/h2&gt;
&lt;p&gt;One of the more distinctive aspects of my port so far is that every completed command seems to end up with one of these ugly blank characters being displayed:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-doing-pi-calculation.png&quot; alt=&quot;22 is divided by 7, returning the result 3, but also with a black square on the end of the line.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Obviously, this is something that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EMIT&lt;/code&gt; isn’t filtering out. There’s some non-printable character being emitted, and we just blindly print it instead of dropping it on the floor. I was feeling particularly lazy and didn’t want to read any code, so I popped it into the MAME debugger to watch what was going on with EMIT.&lt;/p&gt;

&lt;p&gt;Turns out when I just hit the return key with nothing on the line, it first emits &lt;span class=&quot;hex-value&quot;&gt;$20 &lt;/span&gt; and then &lt;span class=&quot;hex-value&quot;&gt;$0d &lt;/span&gt;. &lt;span class=&quot;hex-value&quot;&gt;$0d &lt;/span&gt; in ASCII is, of course, carriage return. Now that I knew this, I could go back and read the code to see who was at fault. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CR&lt;/code&gt; word built into CamelForth was in fact sending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CR&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LF&lt;/code&gt; to indicate they want a new line to be printed to the terminal:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;;C CR      --               output newline
;   0D EMIT 0A EMIT ;
    head CR,2,CR,docolon
        dw lit,0dh,EMIT,lit,0ah,EMIT,EXIT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Rude.&lt;/p&gt;

&lt;p&gt;In my hack to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EMIT&lt;/code&gt;, I only handle &lt;span class=&quot;hex-value&quot;&gt;$0a &lt;/span&gt;, line-feed, as making a new line&lt;sup id=&quot;fnref:lf&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:lf&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, so &lt;span class=&quot;hex-value&quot;&gt;$0d &lt;/span&gt; ends up being printed to the screen. Since there’s no character defined here, it gets that blank tile.&lt;/p&gt;

&lt;p&gt;A further quick hack to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EMIT&lt;/code&gt; dumps the character on the floor:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;_check_for_ignored_character:
    ld a, c
    cp a, $0d ; carriage return
    jr z, _emit_exit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how the naming leaves it open to more ignorable characters? Yeah. That’s what we in the business call &lt;em&gt;extensibility&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-do-not-emit-0d.png&quot; alt=&quot;The black character is no longer appearing on new lines.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Bye, little dude.&lt;/p&gt;

&lt;h2 id=&quot;words-that-interact-with-hardware&quot;&gt;Words that interact with hardware&lt;/h2&gt;
&lt;p&gt;I started writing some words that would be the equivalent of the Z80 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OUT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IN&lt;/code&gt; instructions, only to find out that CamelForth already defined some. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PC@&lt;/code&gt; reads from a port, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PC!&lt;/code&gt; writes to a port, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C@&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C!&lt;/code&gt; do the same for memory: they’re the equivalent of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PEEK&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POKE&lt;/code&gt;. This is enough to start playing with actual hardware.&lt;/p&gt;

&lt;p&gt;To test to see if this actually worked, I grabbed the emulator, put a watchpoint on the I/O space&lt;sup id=&quot;fnref:io-watchpoint&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:io-watchpoint&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and played around with it: indeed, the format &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{data} {port} PC!&lt;/code&gt; sent a byte to the I/O port I wanted.&lt;/p&gt;

&lt;p&gt;I thought about what I wanted my first “outside world” project to be. Although the VDP would be fun to interact with directly, and &lt;a href=&quot;https://github.com/bfox9900/CAMEL99-ITC/blob/master/LIB.ITC/GRAFIX.FTH&quot;&gt;bfox9900 provided some amazing TMS9918 definitions for their TI-99/4A CamelForth port&lt;/a&gt;, I thought that it might be too complicated for a first piece of test code.&lt;/p&gt;

&lt;h3 id=&quot;why-not-the-psg&quot;&gt;Why not the PSG?&lt;/h3&gt;
&lt;p&gt;The SN76489 sound chip seemed like a way easier prospect: I could get it playing a tone just by sending two bytes.&lt;/p&gt;

&lt;p&gt;I wrote &lt;a href=&quot;https://github.com/barbeque/SN76489-Tester-Code&quot;&gt;some SN76489 code&lt;/a&gt; a long time ago, but it was mostly to just &lt;a href=&quot;https://www.leadedsolder.com/2020/07/10/colecovision-diy-part-2.html#sound-off&quot;&gt;test if a bunch of AliExpress chips worked &lt;em&gt;at all&lt;/em&gt;&lt;/a&gt;. It wasn’t even a good test, as I later found out at least one of them was bunk when I put it into a Soggy clone under construction.&lt;/p&gt;

&lt;p&gt;SN76489 has &lt;a href=&quot;https://www.smspower.org/Development/SN76489&quot;&gt;two kinds of commands&lt;/a&gt; you can send to the chip: the “latch/data” command, and the “data” command.&lt;/p&gt;

&lt;p&gt;Latch/data commands take:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A header bit indicating it’s a latch/data command (1)&lt;/li&gt;
  &lt;li&gt;A channel (tone 0, tone 1, tone 2, noise)&lt;/li&gt;
  &lt;li&gt;A &lt;em&gt;type&lt;/em&gt; (are you configuring volume or noise?)&lt;/li&gt;
  &lt;li&gt;4 bits of data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tone is a total of 10 bits of data, so it doesn’t fit into a single byte. If you’re configuring tone, you follow that 4 bits of data with another 6 bits of data in a data command:&lt;/p&gt;

&lt;p&gt;Data commands take:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A header bit indicating it’s a data command (0)&lt;/li&gt;
  &lt;li&gt;1 unused bit&lt;/li&gt;
  &lt;li&gt;6 bits of data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So in other words,&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;If you want to&lt;/th&gt;
      &lt;th&gt;Do this&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Change volume&lt;/td&gt;
      &lt;td&gt;Send a latch/data command to any channel with type bit = 1, volume value in 4-bits of data&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Change noise&lt;/td&gt;
      &lt;td&gt;Send a latch/data command to channel 3 with type bit = 0, noise data&lt;sup id=&quot;fnref:noise-data&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:noise-data&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; in lower 3-bits of data (1 ignored)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Change tone&lt;/td&gt;
      &lt;td&gt;Send a latch/data command to channels 0-2 with type bit = 0, and the first 4 bits of tone data, then a data command with the subsequent 6 bits of tone data.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Phew, that looks pretty complicated, but there’s only really 3 kinds of operations here. Can we build these up from simple Forth words?&lt;/p&gt;

&lt;h3 id=&quot;fundamental-operations&quot;&gt;Fundamental operations&lt;/h3&gt;
&lt;p&gt;Doing this, of course, required me to figure out how to do some bit masking and packing. I cheated for this first bit and wrote it in assembly.&lt;/p&gt;

&lt;h3 id=&quot;volume-word&quot;&gt;Volume word&lt;/h3&gt;
&lt;p&gt;The first word I did was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGVOL&lt;/code&gt;, which sets the volume for a given channel. This one was relatively easy, once I got my pad and pen out and started tinkering with bit math by hand. Verifying it later in the debugger, I made sure that the channel number and volume data didn’t destroy any adjacent bits.&lt;/p&gt;

&lt;h3 id=&quot;data-word&quot;&gt;Data word&lt;/h3&gt;
&lt;p&gt;This one is very easy. I just take whatever is on the top of the stack, lop off everything except the bottom 6 bits, and send it to the PSG.&lt;/p&gt;

&lt;h3 id=&quot;noise-word&quot;&gt;Noise word&lt;/h3&gt;
&lt;p&gt;Implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NOISE&lt;/code&gt; was easy, too. First, I made a copy of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGVOL&lt;/code&gt;, called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGLATCH&lt;/code&gt;, whose major difference is that it did not set the volume/&lt;span class=&quot;active-low-overbar&quot;&gt;tone/noise&lt;/span&gt; bit. It latches the data in. Then, I wrote a quick Forth word which is the equivalent of:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;: PSGNOISE 3 PSGLATCH ;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In other words, it would use whatever is on the top of the stack (data; the “first” argument in the call,) put 3 (the ID of the noise channel) on top of the stack, and call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGLATCH&lt;/code&gt; word to consume both numbers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-rocket-ship-sounds.png&quot; alt=&quot;PSGNOISE and PSGVOL are called, ending in making some noises, one of which I then recorded as the word ROCKET&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Calling that let me make rocket ship sounds, once the volume in channel 3 (noise channel) was appropriately set. Hey! Composing words is easy and fun, as long as you don’t have to do a ton of stack manipulation in the middle of ‘em.&lt;/p&gt;

&lt;p&gt;It’s almost like this is exactly what Forth is good at: iteratively building up a working system.&lt;/p&gt;

&lt;h3 id=&quot;tone-word&quot;&gt;Tone word&lt;/h3&gt;
&lt;p&gt;Now we’re in the most complex word of them all. Recall a few paragraphs ago: in order to send a 10-bit tone to the PSG, we first have to latch in the top 4 bits of our 10 bits (with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGLATCH&lt;/code&gt;) and then send the bottom 6 bits with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGDATA&lt;/code&gt;. That seems straightforward, &lt;em&gt;if&lt;/em&gt; you have local variables and aren’t working in a language you have very little familiarity with.&lt;/p&gt;

&lt;p&gt;I decided that I would rather do this in pure Forth, instead of writing an assembly word that called into Forth somehow. I was just too afraid of mangling the stack, and the recent success of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGNOISE&lt;/code&gt; gave me some hope that I could somehow figure it out by messing around in the interpreter.&lt;/p&gt;

&lt;p&gt;From reading the CamelForth source, I knew that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2*&lt;/code&gt; did an arithmetic 1-bit left shift, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LSHIFT&lt;/code&gt; did a logical &lt;em&gt;n&lt;/em&gt;-bit left shift. Why these two are not the same calling convention, I am unsure, but I had some good tools to work with. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OR&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AND&lt;/code&gt; do the obvious thing as well.&lt;/p&gt;

&lt;p&gt;First, I did the simple bitwise math to construct the bottom 4 bits of the latch command from the bottom 2 bits of the top byte and the upper 2 bits of the bottom byte. That was easy, and produced the right call, as verified in the debugger.&lt;/p&gt;

&lt;p&gt;Unfortunately, doing all this bitwise math mangled the data argument in the process, and I’d need those bits I got rid of for the second call! It consumed the channel number, as well. I’d need to make copies of them, but how could I do it without a local variable?&lt;/p&gt;

&lt;p&gt;The answer is stack manipulation. Forth has a bunch of cool words that do stuff like duplicate the value on top of the stack (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DUP&lt;/code&gt;,) swap the order of the item on the top of the stack and the item one underneath it (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SWOP&lt;/code&gt;,) rotate the order of the top three items (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ROT&lt;/code&gt;,) and inject the item at the top of the stack beneath the item beneath it (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OVER&lt;/code&gt;). I learned about all of this by reading the source code to CamelForth, and also the diagram on &lt;a href=&quot;https://www.forth.org/forth_intro/dataflow.htm&quot;&gt;the “Stack Operations” page&lt;/a&gt; in the Forth Intro.&lt;/p&gt;

&lt;p&gt;One nice aspect of Forth that I didn’t appreciate before is that the stack-modifying convention is usually specified in the comments. It’s even machine-testable in the fancier modern Forths as well. Initially, I just overlooked it as line noise, but let’s take a look and see what, for instance, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ROT&lt;/code&gt; says it does:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ROT    x1 x2 x3 -- x2 x3 x1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That seems pretty explicit! Turns out human beings write comments for other human beings to understand.&lt;/p&gt;

&lt;p&gt;Now that I was armed with the awesome power of “finally reading the documentation,” I was ready to play around. Initially, I used a &lt;em&gt;lot&lt;/em&gt; of paper to figure out the stack flipping operations: I suspected that I would be well-served with a Forth with some kind of stack visualizer. I ended up using &lt;a href=&quot;https://factorcode.org/&quot;&gt;Factor&lt;/a&gt; on my Mac to prototype the various operations.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/factor-figuring-out-doubleup.png&quot; alt=&quot;I am using the &amp;quot;data stack&amp;quot; visualizer in Factor to try and figure out exactly how to manipulate the strings &amp;quot;data&amp;quot; and &amp;quot;channel&amp;quot; on the stack.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the end, I arrived at something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;: DOUBLEUP OVER SWOP DUP ROT SWOP ;
: PSGTONE DOUBLEUP SWOP 6 RSHIFT SWOP PSGLATCHSET DROP SWOP 63 AND SWOP PSGDATASET ;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wow, a non-trivial Forth program!&lt;/p&gt;

&lt;p&gt;Clunky, isn’t it? Let’s step through what’s actually going on in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGTONE&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;As discussed previously, I manipulate the stack to make a copy of the 2 arguments I expect to be there. The stack ends up looking like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;... data channel data channel&lt;/code&gt;, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;channel&lt;/code&gt; on the top of the stack&lt;sup id=&quot;fnref:2dup&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2dup&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
  &lt;li&gt;I swap the order of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;channel&lt;/code&gt; so that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; is top of the stack, and ready for manipulation.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6 RSHIFT&lt;/code&gt; shifts the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; six bits to the right (remember that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; is a 16-bit number) so the previous top four bits are now the bottom four bits.&lt;/li&gt;
  &lt;li&gt;I swap the order of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;channel&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; back, so it’s in the format to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGLATCHSET&lt;/code&gt;, which sets the PSG latch with the first four bits of tone data.&lt;/li&gt;
  &lt;li&gt;I know that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGLATCHSET&lt;/code&gt; consumed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;channel&lt;/code&gt; on the stack, so I am now using my copies.&lt;/li&gt;
  &lt;li&gt;I do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DROP&lt;/code&gt; to get rid of whatever nonsense &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGLATCHSET&lt;/code&gt; put on the stack because I was religiously doing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pop bc&lt;/code&gt; at the end of every call when I wrote it (I need to fix this.)&lt;/li&gt;
  &lt;li&gt;As with step 2, I swap the order of the top two items on the stack again, preparing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; to be manipulated.&lt;/li&gt;
  &lt;li&gt;I &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AND&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; with 63 (&lt;span class=&quot;hex-value&quot;&gt;$3f &lt;/span&gt;), which knocks off everything except the bottom 6 bits. This is probably unnecessary since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGDATASET&lt;/code&gt; also does the same thing, but it makes me feel cool to have unexplained constants in the middle of my code.&lt;/li&gt;
  &lt;li&gt;As with step 4, I swap the order back so we’re ready to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGDATASET&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;I call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGDATASET&lt;/code&gt;, which sends a command to the PSG with the remaining six bits of data.&lt;/li&gt;
  &lt;li&gt;A tone plays.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, I can make really annoying ear-piercing sounds. If I knew how to play music, in theory I could compose these &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGVOL&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGTONE&lt;/code&gt; calls further, making words that understand something like &lt;a href=&quot;https://en.wikipedia.org/wiki/Music_Macro_Language&quot;&gt;MML notation&lt;/a&gt;. However, I don’t know MML yet, so all I can do is annoying beeps.&lt;/p&gt;

&lt;p&gt;It’s sort of annoying writing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt; in front of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGTONE&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PSGVOL&lt;/code&gt; all the time, so I did define some convenience words as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VOLx&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MUTEx&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TONEx&lt;/code&gt;. They do exactly what you think they do.&lt;/p&gt;

&lt;p&gt;This can probably be way shorter. I’m sure there’s some powerful stack-manipulation word or some classic technique that I’m just not aware of yet. Also changing the order of arguments would have made things a lot easier, but I wanted to stick with the data, port convention of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PC!&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;flash-cartridge&quot;&gt;Flash cartridge&lt;/h2&gt;
&lt;p&gt;I was sincerely hoping to not have to do any hardware stuff for this RetroChallenge. In the past, having to order a PCB and wait a few days for it (and the parts) has rudely thrown off my schedule. Thing is, I was starting to get tired of seeing my work running only in an emulator, and I really wanted to demonstrate the real hardware at a local meet by the end of the month.&lt;/p&gt;

&lt;p&gt;I’ve already designed cartridge PCBs for the SG-1000 before: in fact, at least two of them would be suitable for this purpose. I have &lt;a href=&quot;https://www.leadedsolder.com/2022/06/21/sg1000-clone-v2.html#more-carts&quot;&gt;a simple ROM cartridge, which holds up to a 32K program&lt;/a&gt;: the largest possible in the standard SG-1000 memory map.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/soggy-1k-v0.2-zif-socket-cartridge.jpg&quot; alt=&quot;A simple ROM cartridge, with a ZIF-socket for easy ROM swapping.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;More recently, &lt;a href=&quot;https://www.leadedsolder.com/2022/11/01/sega-sk1100-keyboard-pickup-test.html#gotta-get-your-basics-right&quot;&gt;I also made a BASIC ROM cartridge&lt;/a&gt;, which adds a (largely unmapped and therefore unusable) 64K SRAM to the board in order to support Sega’s Level 3 BASIC.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/soggy-1k-basic-iii-pirate-cart.jpg&quot; alt=&quot;My bootleg BASIC cart. It says &amp;quot;Basic Level 3&amp;quot; on a thermal label stuck to the ROM.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Neither one of these is &lt;em&gt;exactly&lt;/em&gt; what I want. For a dream Forth system, I would want these two features:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A flashable ROM, so I could quickly assemble the interpreter on my computer and send it to the SG-1000 for testing;&lt;/li&gt;
  &lt;li&gt;A battery-backed RAM, so the Forth system on the SG-1000 could save custom words between power-offs and resets.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both of these problems open up a huge can of worms: at least one article worth of struggle on each. I’m not sure I’m ready to tackle them, given the few weeks left in RetroChallenge. I just wanted the dopamine hit of getting this thing to work. So I set about getting my CamelForth port ready to be placed on a standard ROM cartridge, burning it to a 32kByte 27C256 EPROM that I had lying around.&lt;/p&gt;

&lt;p&gt;As built, my (no doubt highly wasteful) ROM was 7381 bytes, so I used &lt;a href=&quot;https://github.com/barbeque/camel-forth-sg1000/blob/main/prepare.py&quot;&gt;my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prepare.py&lt;/code&gt; script&lt;/a&gt; to pad it and then duplicate it four times to fill a full 32k ROM. The mirroring doesn’t really &lt;em&gt;do&lt;/em&gt; anything, and may in fact even mask bugs, but I do it out of habit because it feels weird to leave part of the ROM empty.&lt;/p&gt;

&lt;p&gt;Obviously, the iteration speed on this is a lot slower than having a dedicated flash cartridge. UV EPROMs take a long time to erase, and they’re fiddly to keep pulling in and out of sockets to program and wipe them (the legs aren’t getting any stronger with age.) At this point, though, I felt like things were “mature enough” that I hopefully wouldn’t have to do a gazillion iterations to figure out what’s wrong – I still had a pile of unerased ROMs left over from failed attempts at the RAM tester before I discovered the aforementioned “make the VDP wait” trick.&lt;/p&gt;

&lt;p&gt;Unfortunately, this part of the project will have to wait until next week! I simply ran out of time messing with all of the sound code and the hours got away from me.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Wow! I wrote an actual Forth program today, with &lt;em&gt;stack manipulation&lt;/em&gt;. Although I’m certainly not a rabid adherent to the language yet, I am starting to see the value in its very basic design.&lt;/p&gt;

&lt;p&gt;In the next part, I’ll dig out my Soggy-1000 clone, and a keyboard, and a spare cartridge PCB, and hopefully find a ROM that I can program. Then we’ll see if this works on a real console &lt;em&gt;at all&lt;/em&gt;. Boy, I wonder where all that stuff is in my junk &lt;del&gt;pile&lt;/del&gt; stack?&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:lf&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;We could argue all day about whether line feed is the appropriate character for which to begin a new line on my terminal, but C on sane platforms uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\n&lt;/code&gt; too, so I figured it was probably the right move. &lt;a href=&quot;#fnref:lf&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:io-watchpoint&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;MAME’s watchpoints let you detect accesses to I/O and memory spaces. In the case of the Z80 I/O ports, you can set a non-breaking watch on anything written to port 0 with the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wpiset 0, 1, w, 1, { printf &quot;I/O port written!\n&quot;; g }&lt;/code&gt;. Check &lt;a href=&quot;https://docs.mamedev.org/debugger/watchpoint.html&quot;&gt;the full MAME documentation&lt;/a&gt; for more details on all the cool stuff you can do even without MAME’s extensive Lua scripting support. &lt;a href=&quot;#fnref:io-watchpoint&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:noise-data&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The noise data consists of 1 bit, which decides if the noise is “periodic” or “white,” and the other 2 bits configure how fast the internal shifter that drives the noise runs. &lt;a href=&quot;#fnref:noise-data&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2dup&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;After this article went to publication, bfox9900 mentioned that it could be simplified to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;: 2DUP ( a b -- a b a b) OVER OVER ;&lt;/code&gt;. Which is totally correct… and already exists in CamelForth! &lt;a href=&quot;#fnref:2dup&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="computer" /><category term="sega" /><category term="sg1000" /><category term="sc3000" /><category term="soggy1000" /><category term="homemade-software" /><category term="retrochallenge" /><category term="sound" /><category term="retrochallenge-october-2026" /><summary type="html">Now that my port of CamelForth-80 to the Sega SG-1000 is sort of working, it’s time to get it working better. There’s a lot of undone odds and ends and little bugs that I need to take care of before I can try running it on real hardware, so this week’s (abbreviated) effort was mostly along those lines.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/camelforth-doing-pi-calculation.png" /></entry><entry><title type="html">A Typed Forth</title><link href="https://www.leadedsolder.com/2026/10/06/soggy-camelforth-part-2-keyboard-handling.html" rel="alternate" type="text/html" title="A Typed Forth" /><published>2026-10-06T00:00:00+00:00</published><updated>2026-10-06T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/10/06/soggy-camelforth-part-2-keyboard-handling</id><content type="html" xml:base="https://www.leadedsolder.com/2026/10/06/soggy-camelforth-part-2-keyboard-handling.html">&lt;p&gt;Now that we have a functioning Forth interpreter on the Sega SG-1000, it’s time to actually finish hooking up the keyboard input. Because I considered this task to be a little mind-numbing, I’ve put it off for years. How long do you think it took me to figure out? About an hour. One day, I’ll learn.&lt;/p&gt;

&lt;h2 id=&quot;the-story-thus-far&quot;&gt;The story thus far&lt;/h2&gt;
&lt;p&gt;A few years ago, I was playing around with the Sega version of BASIC for the Sega SG-1000 game console. It was sort of the celebratory lap after adding keyboard support to the Soggy-1000, my low-cost open-source clone of the SG-1000.&lt;/p&gt;

&lt;p&gt;That experience left a lot to be desired. Between the limited keywords, difficulty in extending the interpreter, and the poor runtime performance, I thought it would be possible to get a different language working on the machine.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/soggy-1k-booting-basic.jpg&quot; alt=&quot;Sega BASIC running on my SG-1000 clone. It is showing that it runs a simple program that can print to the terminal, and has nearly 26 kilobytes of free program storage available.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Forth is a super-popular language for low-level programming, as it offers just enough abstraction to keep you sane. Most implementations also offer an interactive “interpreter,” so you also get the immediacy of a good BASIC implementation. I decided to port CamelForth-80, the Z80/8080 port of the popular CamelForth interpreter, to the Sega SG-1000&lt;sup id=&quot;fnref:github-link&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:github-link&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;In the previous article, I documented how I got CamelForth to assemble on my modern assembler, and how I hooked the output from Forth to video output on the Sega SG-1000. Then, I ran into the problem of having to write some code to poll the Sega’s keyboard… and put it aside for several years.&lt;/p&gt;

&lt;p&gt;Now, with RetroChallenge well underway, the pressure was on to actually finish that keyboard handling code, even if it’s clunky or otherwise inelegant.&lt;/p&gt;

&lt;h2 id=&quot;are-you-a-scanner&quot;&gt;Are you a scanner?&lt;/h2&gt;
&lt;p&gt;Now was the time for keyboard handling – some very boring, repetitive code! Yaaay!&lt;/p&gt;

&lt;p&gt;To scan the keyboard on the SK-1100, you do this process:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Set the lower three bits of port &lt;span class=&quot;hex-value&quot;&gt;$df &lt;/span&gt; to the row of the matrix you want to look at.&lt;/li&gt;
  &lt;li&gt;Do at least two NOPs to wait for the 8255 to select the lines, and for the data from the matrix to show up on the 8255’s input ports.&lt;/li&gt;
  &lt;li&gt;Read the row from port &lt;span class=&quot;hex-value&quot;&gt;$dc &lt;/span&gt; for the first eight columns, and &lt;span class=&quot;hex-value&quot;&gt;$dd &lt;/span&gt; for the second four columns of the keyboard matrix. One bit per key will be returned; check which bit is set to know which key is pressed (“active low” in the code.)&lt;/li&gt;
  &lt;li&gt;Based on your row position and which column is lit up on the 8255, look up and return the ASCII character for that key&lt;sup id=&quot;fnref:one-shot&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:one-shot&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; (if applicable) to the calling Forth code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For starters, I ended up making a 14 x 8 2D array in the code, and then using bit tests to figure out which column to send to the array. The scanning function works something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;for row = 0 to 6: &apos; row 7 is for joysticks
        out($df, row);
        nop(); nop();

        keys_down = in($dc);
        for bit = 0 to 7:
                if bit(keys_down, bit) == 0:
                        return keymap[bit, row];
        keys_down = in($dd);
        for bit = 0 to 4:
                if bit(keys_down, bit) == 0:
                        return keymap[bit + 8, row];
        
&apos; nothing pressed
return 0;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Inelegant, maybe, but it works. The actual implementation is insanely ugly, and involves a lot of hand-unrolled loops where I have this kind of thing copy-and-pasted forever:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;_kbd_a_bit0:
        bit 0, a ; TODO: Could this be done in a macro?
        jr nz, _kbd_a_bit1
        ; handle key press at bit 0; IDX = 0
        ld c, (ix + 0)
        jp _segakey_out

_kbd_a_bit1:
        bit 1, a
        jr nz, _kbd_a_bit2
        ; handle key press at bit 1; IDX = 1
        ld c, (ix + 1)
        jp _segakey_out

_kbd_a_bit2:
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Phew. That’s a lot of wasted ROM space and a very annoying chunk of copy-and-pasted code that could probably just be a macro at the very least. An obvious improvement would be to compare &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; to a bitmask, and just do bit shifts on that mask to iterate instead of constantly checking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bit 0, a&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bit 1, a&lt;/code&gt;, etc. But it worked, as you’ll see below.&lt;/p&gt;

&lt;p&gt;The keymap looked something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;SOGGY_KEYMAP:
    ; 1, Q, A, Z, 0, ,, K, I, 8, 0, 0, 0
    .db 0x31, 0x51, 0x41, 0x5a, 0x0, 0x2c, 0x4b, 0x49, 0x38, 0x0, 0x0, 0x0
    ; 2, W, S, X,  , ., L, O, 9, 0, 0, 0
    .db 0x32, 0x57, 0x53, 0x58, 0x20, 0x2e, 0x4c, 0x4f, 0x39, 0x0, 0x0, 0x0
    ; 3, E, D, C, 0, /, ;, P, 0, 0, 0, 0
    .db 0x33, 0x45, 0x44, 0x43, 0x0, 0x2f, 0x3b, 0x50, 0x30, 0x0, 0x0, 0x0
    ; 4, R, F, V, 8, 0, :, @, -, 0, 0, 0
    .db 0x34, 0x52, 0x46, 0x56, 0x8, 0x0, 0x3a, 0x40, 0x2d, 0x0, 0x0, 0x0
    ; 5, T, G, B, 0, 0, ], [, ^, 0, 0, 0
    .db 0x35, 0x54, 0x47, 0x42, 0x0, 0x0, 0x5d, 0x5b, 0x5e, 0x0, 0x0, 0x0
    ; 6, Y, H, N, 0, 0, 13, 0, 0, 0, 0, 0
    .db 0x36, 0x59, 0x48, 0x4e, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0
    ; 7, U, J, M, 0, 0, 0, 0, 0, 0, 0, 0
    .db 0x37, 0x55, 0x4a, 0x4d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To figure out the key map, I used the fantastic diagram from &lt;a href=&quot;http://cmpslv3.stars.ne.jp/Mark3/Sk1100.htm&quot;&gt;Enri’s SK-1100 writeup&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/enri-sk1100-key-matrix.png&quot; alt=&quot;Enri&apos;s SK-1100 key matrix diagram&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now that you’ve looked at that matrix, you might notice that something is missing in my handling code up above: &lt;em&gt;modifiers&lt;/em&gt;. How am I supposed to be able to type a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!&lt;/code&gt; if I am not handling the shift key? That’s a very good question, and one that I didn’t want to solve right away. Perfect is the enemy of good, and all that.&lt;/p&gt;

&lt;p&gt;After some fumbling around – I forgot to actually increment the row counter and also overwrote the accumulator that I was using to store that counter – I managed to make it work!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-keyboard-scanning-working.png&quot; alt=&quot;The keyboard scanning is working, and I was able to type the alphabet as well as some other nonsense.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That progress feels really good after many years of procrastination. It’s not noticeably slow, either: even old 8-bit computers are good at doing stupid things really quickly, and it’s not like the computer is doing anything else when it’s waiting for input!&lt;/p&gt;

&lt;p&gt;Even return worked, letting me ask the SG-1000 if it had heard of my blog:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-keyboard-scanning-who-are-you.png&quot; alt=&quot;The &amp;quot;leaded-solder&amp;quot; word is triggered, but Forth doesn&apos;t know what that word is.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now I can do some simple math:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-doing-pi-calculation.png&quot; alt=&quot;Doing the calculation of 22 divided by 7 returns that pi is pretty close to exactly 3.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Even with all this mirth, there were two obvious bugs:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Pushing backspace didn’t, well, backspace.&lt;/li&gt;
  &lt;li&gt;Pushing the “1” key would reset the computer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Checking my notes, I realized that the second issue had been a problem on my previous keyboard-scanning code, too. That’s weird.&lt;/p&gt;

&lt;h2 id=&quot;why-does-pressing-1-reset-the-computer&quot;&gt;Why does pressing “1” reset the computer?&lt;/h2&gt;
&lt;p&gt;After awhile of fumbling around in my code, I realized it would still happen even if the entire keyboard handling were to be commented out. I began to suspect MAME had something weird bound to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; key, and indeed it did:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-pause-button-bind.png&quot; alt=&quot;The &amp;quot;1&amp;quot; key is bound to &amp;quot;Pause&amp;quot; in the MAME settings.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oops. Pushing the 1 key, even with UI keys disabled, told MAME to push the imaginary pause button on the emulated SG-1000 unit, which triggers an NMI and then crashes because I don’t have a handler for it. Whatever chunk of memory that is located where the NMI vector is expected is no doubt wreaking havoc. I deleted this keybind in the MAME settings and suddenly it stopped rebooting.&lt;/p&gt;

&lt;p&gt;The funny, or at least fascinating, part is that it would still type out the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; before crashing. I’m not sure if this “double firing” is a bug in MAME; it feels like it’s debatable whether the pause button on the front of the unit is a “UI key” or not.&lt;/p&gt;

&lt;p&gt;That said, the Pause/NMI button will have to be handled later, because not handling it is sloppy and I already have a reset button on my hardware. I don’t know &lt;em&gt;how&lt;/em&gt; to handle it yet, so I added it to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TODO.md&lt;/code&gt; and kept going. Now for the mystery of the backspace!&lt;/p&gt;

&lt;h2 id=&quot;whats-wrong-with-the-backspace&quot;&gt;What’s wrong with the backspace?&lt;/h2&gt;
&lt;p&gt;After a few more minutes of squinting and reading code, it turned out to be the same problem as 1. Well, almost the same problem. The “INS/DEL” key on the emulated SG-1000 was bound to Delete and not Backspace. With a quick rebind, I could suddenly make mistakes again.&lt;/p&gt;

&lt;h2 id=&quot;cant-type-worth-shift&quot;&gt;Can’t type worth Shift&lt;/h2&gt;
&lt;p&gt;Now that I could write a Forth program, it was time to… write a Forth program. Only one problem, though. Without a shift key, there is a lot of punctuation I can’t do, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt;, you know… unimportant things for writing programs. I would have to implement the shift key, and potentially ruin my whole day.&lt;/p&gt;

&lt;p&gt;My first whack at the problem was to do exactly what I described above: add a second keymap, and just use that instead when the shift key was held down.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-weird-double-pressing-with-shift.png&quot; alt=&quot;Every lowercase letter is being followed by the identical uppercase letter.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This caused something unexpected to happen: the lowercase letter that I intended was immediately followed by an uppercase letter. At first, I thought this might be my debounce code, but I realized that not &lt;em&gt;every&lt;/em&gt; letter was doubled up. For instance, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;j&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m&lt;/code&gt; didn’t fire twice when shifted. If you check the key matrix up above, you’ll notice they’re all on the “last” row… with the modifiers. That was even less expected. What was going on?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-double-pressing-shift-with-symbols.png&quot; alt=&quot;1, 2, 3, 4, 5 etc are also emitting double presses with their respective symbols.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I fumbled around for the better part of a day. In the meantime, I did realize that you could use backspace to remove the extraneous character and actually write some useful programs:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-do-loop-works.png&quot; alt=&quot;I have written a very simple 1 to 10 &amp;quot;do loop&amp;quot; program where I also managed to fumble how to run it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As I was trying to debug my problem, it turned out to have some particularly weird phenomena. If I didn’t scan row 6 during the regular scan, the shift key wouldn’t work &lt;em&gt;at all&lt;/em&gt;. This sent me down a bit of a rabbit hole where I began to suspect the emulator&lt;sup id=&quot;fnref:ares&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:ares&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, but we all know it’s never the emulator. It’s my fault.&lt;/p&gt;

&lt;p&gt;I had hacked up the keyboard-scanning code I just wrote to “check the shift row first,” and so I started tracing the code step by step instead of looking at the comments. Uh, turns out I forgot to actually write to the port that changes the keyboard row. Oops!&lt;/p&gt;

&lt;p&gt;I had written it like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        ld a, 6
        ld (KBD_SCAN_ROW), a ; select the accelerator row
        nop
        nop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But what I really meant to write was this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        ld a, 6
        out ($de), a ; actually write to the port you idiot
        nop
        nop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KBD_SCAN_ROW&lt;/code&gt; is a global that I used to make the scanning code a little easier to write; it just keeps track of the row index so that the contents of the accumulator don’t get destroyed during the tests. I’m still paranoid about inadvertently stomping on a register that the outer Forth needs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-working-lowercase.png&quot; alt=&quot;Lowercase is now possible, and you can enter each lowercase/shifted character a single time.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;D’oh.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Phew! I feel kind of dumb putting this off for so long when the initial implementation was so quick to do. Of course, I keep generating more bugs, but there’s always next week.&lt;/p&gt;

&lt;p&gt;Even after these little bugs are fixed, there’s still a lot more to do for keyboard handling. For starters, I want to see if I’ve got room in the character set for kana and graphics characters. It’s easy to lose track of how cool something as simple as “type characters and have them show up on the screen” is.&lt;/p&gt;

&lt;p&gt;This was a lot of work. If only there was some kind of higher-level language with loops and stuff that I could have written this keyboard driver in. Maybe it could even compile down to assembly for optimization?&lt;/p&gt;

&lt;p&gt;Thank you for reading! I hope to see you again soon.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:github-link&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;CamelForth-80 is GPLv3 licensed, so I am &lt;a href=&quot;https://github.com/barbeque/camel-forth-sg1000&quot;&gt;publishing the modifications to my GitHub repository here&lt;/a&gt;. Use it at your own risk! I have no guarantee the main branch will remain unbroken. &lt;a href=&quot;#fnref:github-link&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:one-shot&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Yes, this prevents you from pressing more than one key at once, but the SK-1100 keyboard doesn’t have any anti-ghosting diodes, so it’s likely to not be a problem. We’ll see if it actually becomes one in practice. &lt;a href=&quot;#fnref:one-shot&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:ares&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I did try &lt;a href=&quot;https://ares-emu.net/&quot;&gt;the fantastic Ares emulator&lt;/a&gt;, but for whatever reason it will load neither this Forth ROM nor my homemade RAM tester, both of which work fine in MAME, and the RAM tester even works on real hardware. I will have to do some more investigation later. &lt;a href=&quot;#fnref:ares&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="computer" /><category term="sega" /><category term="sg1000" /><category term="sc3000" /><category term="soggy1000" /><category term="homemade-software" /><category term="retrochallenge" /><category term="retrochallenge-october-2026" /><summary type="html">Now that we have a functioning Forth interpreter on the Sega SG-1000, it’s time to actually finish hooking up the keyboard input. Because I considered this task to be a little mind-numbing, I’ve put it off for years. How long do you think it took me to figure out? About an hour. One day, I’ll learn.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/enri-sk1100-key-matrix.png" /></entry><entry><title type="html">More like SC-Forth-thousand, am I right?</title><link href="https://www.leadedsolder.com/2026/10/01/rc2026-10-sg1000-forth-port.html" rel="alternate" type="text/html" title="More like SC-Forth-thousand, am I right?" /><published>2026-10-01T00:00:00+00:00</published><updated>2026-10-01T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/10/01/rc2026-10-sg1000-forth-port</id><content type="html" xml:base="https://www.leadedsolder.com/2026/10/01/rc2026-10-sg1000-forth-port.html">&lt;p&gt;The Soggy needed a good low-level programming environment with which to tinker on future hardware projects, and ideally it was one that I controlled myself, so I could include it on the onboard ROM without infringing copyright. It would be nice if it used a semi-popular programming language for embedded systems, and had an interactive development environment that I could run right on the machine. All that seems to mean I’m finally going to make my first Forth.&lt;/p&gt;

&lt;h2 id=&quot;the-soggy&quot;&gt;The Soggy?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/sega-sg-1000-evan-amos-wikimedia.jpg&quot; alt=&quot;A picture of a Sega SG-1000, taken by Evan-Amos on Wikipedia.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sega’s first home console and their first home computer are very similar machines. Although the exact reasoning hasn’t been said (to my knowledge,) Sega originally planned to release multiple home computers, at different levels of capability. This plan changed to releasing just one home computer (the SC-3000) and one game console (the SG-1000.) Unfortunately for them, the SG-1000 released on the exact same day as the Nintendo Famicom, but Sega considered it to be a success anyway.&lt;/p&gt;

&lt;p&gt;What’s interesting about the shared heritage between these two machines is that you can attach a keyboard to an SG-1000, in order to turn it into sort of a de facto SC-3000&lt;sup id=&quot;fnref:not-exactly&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:not-exactly&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. A lot of companies promised stuff like this in the 80s, and Sega delivered. As a result, you can run (a very limited form of) BASIC on your SG-1000.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/soggy-at-retrodaze.JPG&quot; alt=&quot;The version-four (or at least a modified version three) Soggy-1000 is sitting in front of a television playing Girl&apos;s Garden. There&apos;s some other cartridges as well, and a placard explaining the project history.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Although &lt;a href=&quot;https://www.leadedsolder.com/2019/08/08/sega-sc3000-cold-joints.html&quot;&gt;I have a very fragile and deteriorating SC-3000&lt;/a&gt;, I wanted something I could play with without worrying about shattering delicate keyboard plastic on. The prices of somewhat-more-durable SG-1000s are high and on the rise, especially the very Shōwa-futurist first-generation model. I also had experience with &lt;a href=&quot;https://www.leadedsolder.com/2020/02/16/colecovision-diy-part-1.html&quot;&gt;making a clone of the ColecoVision&lt;/a&gt;, which uses many similar parts to the SG-1000 (but is not cross-compatible in any way.) I ended up &lt;a href=&quot;https://www.leadedsolder.com/2022/05/20/sg1000-clone-v1.html&quot;&gt;making a clone of the SG-1000, called the Soggy-1000&lt;/a&gt;, and made sure to make it compatible with the SG-1000 keyboard.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/soggy-1k-v0.4-case-red-cartridge.jpg&quot; alt=&quot;The Soggy-1000 as it sits today, in a handsome 3D-printed red case.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Naturally, with this kind of ambition and/or excess free time, I quickly ran into the limitations of Sega’s BASIC. A real computer should be able to build programs on and for itself!&lt;/p&gt;

&lt;h2 id=&quot;retrochallenge&quot;&gt;RetroChallenge&lt;/h2&gt;
&lt;p&gt;This project is done as part of the RetroChallenge, a quasi-annual informal competition where everyone gets together and does something cool with retrocomputers for an entire month. This is for RetroChallenge 2026/10. I find that the event helps force me to work on one project, instead of bouncing around between a billion ideas, and so I can get big leaps of progress out of my projects in just one month.&lt;/p&gt;

&lt;p&gt;By the rules of RetroChallenge, you are not supposed to start your work early, so that’s why this article is coming out at the very start of the month. It’s all stuff that I have done months, and in some cases, years prior, and the real “challenge” is just getting me to finish it. Everything &lt;em&gt;from now on&lt;/em&gt; is part of RetroChallenge, and this article should serve as a primer to figure out just what it is I am trying to do.&lt;/p&gt;

&lt;p&gt;Okay, on with the Forth.&lt;/p&gt;

&lt;h2 id=&quot;why-forth&quot;&gt;Why Forth?&lt;/h2&gt;
&lt;p&gt;In case you’ve never heard of it, &lt;a href=&quot;https://en.wikipedia.org/wiki/Forth_(programming_language)&quot;&gt;Forth&lt;/a&gt; is a very minimal high-level programming language. It’s very common in embedded and small systems, mostly because it’s easy to get the base system working and then lets you incrementally build some functionality using a command shell. Think of the Python REPL, and you’re on the right path.&lt;/p&gt;

&lt;p&gt;It takes awhile to wrap your head around the stack-oriented design, but once you do, it is surprising just how elegant the language is. &lt;a href=&quot;https://blog.information-superhighway.net/what-the-hell-is-forth&quot;&gt;&lt;em&gt;What the Hell is Forth&lt;/em&gt;&lt;/a&gt; is a great read on the subject, which I saw kicking around the internet only after I started on this ridiculous project.&lt;/p&gt;

&lt;p&gt;Another big appeal of Forth to me on these limited machines is that, in many distributions, Forth often includes an integrated assembler. With a suitably equipped Forth interpreter&lt;sup id=&quot;fnref:camelforth-asm&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:camelforth-asm&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, you can bang out assembly language programs in a REPL on the real hardware, which is an experience you can’t really get anywhere else.&lt;/p&gt;

&lt;p&gt;The biggest grain of salt to take with this article, and pretty much every future one I write on the subject, is that I don’t &lt;em&gt;really&lt;/em&gt; know how to write programs in Forth. I can hack together a simple program with some serious effort, but I still need to jump back to the documentation to understand a lot of the jargon, and I am almost useless at reading code. Do not consider me as a Forth expert, or even an enthusiastic amateur. The point of this series is to show how easy it is to bring up a Forth interpreter on any random computer you may encounter. If you’re an experienced Forth programmer, please forgive me any screw-ups in advance.&lt;/p&gt;

&lt;h2 id=&quot;why-forth-on-the-sg-1000sc-3000&quot;&gt;Why Forth on the SG-1000/SC-3000?&lt;/h2&gt;
&lt;p&gt;With Forth on the SC-3000, I can write all kinds of programs, in a more space-efficient way than with BASIC&lt;sup id=&quot;fnref:basic-tms9918&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:basic-tms9918&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. It’s also, frankly, less irritating: I got more than my fill of renumbering large BASIC programs in the 90s.&lt;/p&gt;

&lt;p&gt;Forth’s higher-level and more structured than assembly, which means I can write programs much faster without stepping on myself trying to remember the semantics of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;otir&lt;/code&gt;. If I want to, I can even include an assembler, so I can write really fast, useful code right on the machine without having to involve a “real computer” to cross-assemble for Z80.&lt;/p&gt;

&lt;p&gt;Last, with the ROM-socket on the Soggy, it also provides a useful platform to make it more useful as a general-purpose computer, with a powerful programming language that’s ready to use from a cold start. I can even write device drivers for new stuff connected to the expansion slot! Like the aforementioned article says, it’s like an assembly REPL.&lt;/p&gt;

&lt;p&gt;While I was working on this project, I also found an amazing article in &lt;em&gt;Kilobaud&lt;/em&gt; magazine, called, well, &lt;a href=&quot;https://archive.org/details/kilobaudmagazine-1981-02/page/n75/mode/2up&quot;&gt;&lt;em&gt;Write Your Own FORTH Interpreter&lt;/em&gt;&lt;/a&gt;. If you’re interested in the mechanisms of how these things really work under the hood, make sure to open that up in a second tab. Or third, or 400th. We’re all friends here.&lt;/p&gt;

&lt;h2 id=&quot;port-a-forth&quot;&gt;Port a Forth&lt;/h2&gt;
&lt;p&gt;I first wanted to start out by &lt;em&gt;porting&lt;/em&gt; a Forth. This would cause me to get a lot of the “SG-1000 specific” stuff out of the way, plus maybe give me some good ideas about my implementation. &lt;a href=&quot;https://github.com/wwarthen/RomWBW/tree/master/Source/Forth&quot;&gt;The RomWBW project contains a fork of the GPL-licensed CamelForth-80&lt;/a&gt;, by Bradford J. Rodriguez.&lt;/p&gt;

&lt;p&gt;CamelForth is originally intended to run on CP/M-80, but the author has thankfully provided a thorough porting guide for how to modify it to run standalone, out of ROM. I have to write my own hardware initialization (TMS9918, stack pointer, etc,) a reset handler, move some buffers around, and replace three words that are implemented to use CP/M.&lt;/p&gt;

&lt;p&gt;Those three words, implemented in Z80 assembly, are:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY&lt;/code&gt;: Returns the key being pressed;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY?&lt;/code&gt;: Returns true if a key is waiting;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EMIT&lt;/code&gt;: Prints a single character to the screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s really nice to see a thorough porting guide like this. I read through the source code and tried to come up with a plan for attack, merging it into the codebase of my RAM testing program in order to provide a console interface and basic keyboard-reading functionality.&lt;/p&gt;

&lt;p&gt;Since the RomWBW version was modified, I ended up grabbing the original from &lt;a href=&quot;http://www.camelforth.com/news.php&quot;&gt;the CamelForth website&lt;/a&gt; and working from that.&lt;/p&gt;

&lt;h3 id=&quot;getting-it-to-assemble&quot;&gt;Getting it to Assemble&lt;/h3&gt;
&lt;p&gt;You might think all Z80 macro assemblers are pretty much the same. However, there’s a lot of differences in syntax, the order of operations, and especially in macro implementations. CamelForth-80 was originally built with the freeware/public-domain Z80MR assembler, but I have been using &lt;a href=&quot;https://k1.spdns.de/Develop/Projects/zasm/Distributions/&quot;&gt;Zasm&lt;/a&gt; for all of my Z80 projects to date.&lt;/p&gt;

&lt;p&gt;I don’t have a particular attachment to zasm, but Z80MR is meant to run in CP/M, and as far as I could tell did not have a modern Mac/*nix port. In fact, if you do a web search, the majority of surviving references to its existence are from CamelForth-80’s own readme. I had to hunt around for a little while until I found this copy of it in &lt;a href=&quot;http://discmaster.textfiles.com/browse/15405/oakcpm.iso/cpm/asmutl&quot;&gt;the DiscMaster archive of the Oakland CP/M archive CD&lt;/a&gt;, where it was distributed in (what else?) Z80 assembly.&lt;/p&gt;

&lt;p&gt;So: I’d have to port the assembly from one assembler to another. Should be easy, right?&lt;/p&gt;

&lt;h3 id=&quot;building-it&quot;&gt;Building It&lt;/h3&gt;
&lt;p&gt;The first place zasm bailed was on the very first Forth word defined in the source code, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXIT&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;in file camel80.asm:
210:         IF  .NOT.(docode=DOCODE)
                      ^ condition not evaluatable in pass1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That word expanded a macro, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;head&lt;/code&gt;, which itself expanded into a conditional define – an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#ifndef&lt;/code&gt;, if you will.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;head    MACRO   #label,#length,#name,#action
        DW link
        DB 0
link    DEFL $
        DB #length,&apos;#name&apos;
#label:
        IF  .NOT.(#action=DOCODE)
        call #action
        ENDIF
        ENDM

[..]

;C EXIT     --      exit a colon definition
    head EXIT,4,EXIT,docode
        ld e,(ix+0)    ; pop old IP from ret stk
        inc ix
        ld d,(ix+0)
        inc ix
        next
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It looks like the last argument of the macro invocation, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docode&lt;/code&gt;, is meant to signal that the word is implemented as an alternative assembly-language routine, rather than to consider it “as pure Forth.” In other words, it’s thunking out to native code.&lt;/p&gt;

&lt;p&gt;Many of the words in CamelForth are defined as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docode&lt;/code&gt;, which would then match the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DOCODE&lt;/code&gt; define at assembly-time, assembled into the binary, and then be called when that word is requested at runtime. Others will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dovar&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docolon&lt;/code&gt; handlers, which respectively define a Forth variable or composes a Forth word from other Forth words. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docode&lt;/code&gt; has no special implementation and just falls right through to the rest of the assembly language defined in the word.&lt;/p&gt;

&lt;p&gt;The exact way it’s implemented doesn’t really matter to me, but it seems that zasm does its passes differently than z80mr does, and is complaining about it. It does not want to expand the macro &lt;em&gt;and&lt;/em&gt; evaluate the macro that came out of that macro on the same pass.&lt;/p&gt;

&lt;p&gt;Quoth &lt;a href=&quot;https://k1.spdns.de/Develop/Projects/zasm/Documentation/z62.htm#A&quot;&gt;the zasm documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;‘if’ starts a block of assembler instructions, which is only assembled if the given &lt;condition&gt; is true. The &lt;condition&gt; must be evaluatable in pass 1. Conditional assembly may be nested. note: this may change.&lt;/condition&gt;&lt;/condition&gt;&lt;/p&gt;

  &lt;p&gt;[…]&lt;/p&gt;

  &lt;p&gt;Normally the assembler directives with ‘#’ should be used. Except that ‘if’ and ‘endif’ can occur in macros and the expanded macro can conditionally exclude some code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So… including this from a macro should be fine. Why can’t it figure out if our action is equal to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DOCODE&lt;/code&gt;, a constant, on pass one? I couldn’t figure it out, so I ended up copy-pasting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;head&lt;/code&gt; macro and making two versions: one that was for everything but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docode&lt;/code&gt; and one that was just for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docode&lt;/code&gt;. Then I did a search and replace, and those assembly errors went away. The same thing had to happen for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;immed&lt;/code&gt;, the macro that defines immediate words&lt;sup id=&quot;fnref:immediate&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:immediate&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;I also had to escape bare characters like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt;, as the assembler expected a matching brace, or in the case of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;gt;&lt;/code&gt;, eliminated them entirely, leaving an empty string.&lt;/p&gt;

&lt;p&gt;The next problem was some seemingly oddball names for words. Forth is extremely loose with its naming rules compared to other languages. You can name a dictionary word pretty much anything you want, as long as it doesn’t have a space in it. There’s words called things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COMPILE,&lt;/code&gt; or just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;,&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;head&lt;/code&gt; macro generates the strings that are matched to call these words automatically, as you can see in its definition above, from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;#name&lt;/code&gt;’ line.&lt;/p&gt;

&lt;p&gt;You have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;head&lt;/code&gt; macro invocations like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;immed SQUOTE,2,S&quot;,docolon
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You and I both know that that is intended to become a word called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;S&quot;&lt;/code&gt;, and that they’re not trying to define a string, but zasm is &lt;em&gt;pretty sure&lt;/em&gt; that quote mark’s the start of a string.&lt;/p&gt;

&lt;p&gt;Zasm has trouble parsing some of these lines, because it doesn’t know they’re not really going to end up being a string in the end. Presumably Z80MR doesn’t care, or its parser can deal with this somewhat unusual case more carefully.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;356:     immed SQUOTE,2,S&quot;,docolon
                                  ^ closing &apos;&quot;&apos; missing
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Words with commas in them were even worse, as the original author had pre-quoted those. Presumably Z80MR is loose enough that it didn’t mind inserting those into a quote inside the macro either:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;99:         DB 3,&apos;&apos;,CF&apos;&apos;
                   ^ closing quotes expected
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Escaping those quotes in a bare word, as I had with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;gt;&lt;/code&gt;, is a no-go either. Commas are &lt;em&gt;special&lt;/em&gt; in a zasm macro invocation:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;85:     chead COMMAXT,8,COMPILE\,
                                 ^ too many arguments: required=3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If I use double-quotes to escape it, zasm just goes ahead and crams those into the listing file verbatim, which isn’t good since I’m going to have to &lt;em&gt;type&lt;/em&gt; these later to run the words:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        DB 1,&apos;&quot;.&quot;&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Zasm’s documentation is unhelpful on this front:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: special characters in string cannot be escaped with ‘&apos;. This syntax was (probably) invented later for the programming language ‘C’.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I decided there was no good reason to do it this way, and rewrote the macro so that it doesn’t try to string-quote the words. Then I went back and changed all the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;head&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;immed&lt;/code&gt; invocations to use proper quoted strings for the “name” slot.&lt;/p&gt;

&lt;p&gt;I could have probably done this more cleanly, especially as ROM space will one day become a limitation. In order to get to the fun parts of this project faster, though, I wanted to get it out of the way.&lt;/p&gt;

&lt;p&gt;A big final hurdle is that zasm has case-sensitive labels: the CamelForth-80 code often uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lowercase&lt;/code&gt; to define a label and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPPERCASE&lt;/code&gt; to refer to it in code. Luckily, zasm also has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--casefold&lt;/code&gt; option that tells it to act more like a CP/M-80 assembler. Success!&lt;/p&gt;

&lt;p&gt;At last, I had something built. Now, I just had to make it run on the SG-1000.&lt;/p&gt;

&lt;h3 id=&quot;emit&quot;&gt;EMIT&lt;/h3&gt;
&lt;p&gt;Writing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EMIT&lt;/code&gt; function was a big challenge, but it’s one that I had expected would be coming up. As you might have been able to guess, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EMIT&lt;/code&gt; prints one character at a time to the output device. On CP/M, the BDOS handles the nitty-gritty of this, sending it to either a TTY or the screen, including scrolling and buffering. On the SG-1000, we’ll have to handle this with the TMS9918 VDP, which does not support hardware scrolling.&lt;/p&gt;

&lt;p&gt;First, let’s talk about what graphics mode we’re going to use to display the text sent to the virtual terminal. Because it’s easy to program, I chose to use the TMS9918 mode 1, or “Graphics 1.” This provides a 32x24 tile display. Although this mode presents significant drawbacks, mostly its tiny amount of visible characters, I find that it works well on a television set and also is – again – very easy to program.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-next-indicator.png&quot; alt=&quot;An example of TMS9918 mode 1 graphics: some jerk&apos;s Tetris port.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;First, I had to establish a means of communication between CamelForth and my nascent “terminal.” For starters, I needed to figure out how CamelForth was telling me which character it wanted to have printed. After some experimentation at making a native-code (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docode&lt;/code&gt;) handler that wouldn’t crash the system, I eventually found that the register &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C&lt;/code&gt; contains the character they wanted me to print. So I shifted that around to get into the area of my font, and…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-first-emit.png&quot; alt=&quot;The CamelForth-80 sign on banner reads: Z80 CamelForth v1.01 25 Jan 1995, followed by two black tiles for unknown characters.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was pretty cool, although it turns out that I had screwed up the calling convention and the system kind of halted at this point with a corrupted stack. After half-fixing that error, I now had an infinite spew of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ok&lt;/code&gt; messages as the nonexistent keyboard input was read, presumably interpreted as a null-terminator (ascii &lt;span class=&quot;hex-value&quot;&gt;$00&lt;/span&gt;,) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUIT&lt;/code&gt; was invoked.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-ok-spew.png&quot; alt=&quot;The interpreter is spamming the console with &apos;ok&apos; over and over as QUIT is repeatedly invoked&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Eventually this stream of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ok&lt;/code&gt; would run off the end of the nametable memory and start corrupting other parts of the VRAM, so this was a good excuse to implement scrolling. Sure, I could figure out and fix the problem first, but why waste a perfect scrolling test situation like this?&lt;/p&gt;

&lt;h3 id=&quot;implementing-scrolling&quot;&gt;Implementing Scrolling&lt;/h3&gt;
&lt;p&gt;At first, I thought I could do scrolling in a clever way by having a ring buffer of the screen dimensions in RAM, and then I’d just update that buffer as I am asked to print out more text. If I run off the end of the screen, I’d need to scroll. To scroll the screen in this model, I’d blank the screen, redraw all the lines one tile up, and then wipe the next line encountered and start storing characters into that.&lt;/p&gt;

&lt;p&gt;However, I ran into two problems:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Storing an entire screen buffer - 32 by 24 characters - takes up 32*24 = 768 bytes of RAM. That’s almost the entire 1K available for the original SG-1000, which I’d like to support, and doesn’t leave much room for the actual Forth interpreter&lt;sup id=&quot;fnref:basic-ram&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:basic-ram&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;;&lt;/li&gt;
  &lt;li&gt;There was no actual reason to store the screen buffer in work RAM&lt;sup id=&quot;fnref:editor&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:editor&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, other than when I scrolled, which was going to be a very slow copy operation anyway.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We &lt;em&gt;already have&lt;/em&gt; a buffer in which the characters on the screen are stored – the VDP’s video memory! The VDP has a massive 16 kilobytes of RAM available to it, and we can read and write it from the CPU. For instance, we often write to it to, uh, print messages to the screen.&lt;/p&gt;

&lt;p&gt;Although I thought about doing something fancy with the nametable memory pointer, I ultimately decided the best way to scroll would be something akin to this pseudocode:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;for y from 0 to height - 2:
    for x from 0 to width - 1:
        get character at (x, y + 1)
        store it at (x, y)
blank out last line to prepare for new input
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All this interaction with the VDP might be a little slower, but at least no system RAM is consumed by it.&lt;/p&gt;

&lt;p&gt;Because the height and width are fixed in Mode 1 as 32 columns by 24 rows, I ended up turning it into this simpler loop:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;for hl from 0000 to 32 * 23:
        get character from vram[hl + 32]
        put the result into vram[hl]
blank out last line to prepare for new input
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That was much easier to write, although I suspect I could have used the mysterious Z80 index registers as I was working with a fixed offset. I didn’t want to sweat the speed, considering I was going to spend most of my time waiting for the VDP anyway and had nothing better to do until it was ready.&lt;/p&gt;

&lt;p&gt;Because I’m not doing this inside the blanking interval, there is the risk of a little bit of tearing on real hardware. The reader is welcome to do a super smooth per-pixel scrolling routine that never flickers.&lt;/p&gt;

&lt;p&gt;Although interacting with the VDP is expensive, especially when doing a read followed by a write, the end result was good enough. In a future version, I could always speed it up by using a small buffer in RAM to store the temporary values and read a line at a time, so that I’m not constantly flipping between read/write with different addresses. Or I could do it all in the vblank interval, instead of risking tearing and ugly artifacts by doing it while the VDP is busy drawing a frame.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-scrolling-v1.gif&quot; alt=&quot;The first version of the scrolling code is running. You can see some tearing artifacts in the GIF from where MAME was capturing specific frames that hadn&apos;t finished drawing, but it looks smoother in person.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Nice.&lt;/p&gt;

&lt;h3 id=&quot;backspace-and-newline&quot;&gt;Backspace and Newline&lt;/h3&gt;
&lt;p&gt;There were, of course, other complicated aspects to doing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EMIT&lt;/code&gt;. Not every character is as simple as “draw tile at insertion point, increment insertion point, scroll if you need to.”&lt;/p&gt;

&lt;p&gt;For instance, there’s backspace, which I think we can all acknowledge is an important key. If you haven’t experienced it before, I strongly recommend trying it.&lt;/p&gt;

&lt;p&gt;Here’s why it’s difficult to implement. When “printed,” the backspace character is intended to clear the space &lt;em&gt;prior&lt;/em&gt; to the insertion point, and prevent the insertion point from advancing. Both of those are unusual behaviours for a character, which usually do the exact opposite.&lt;/p&gt;

&lt;p&gt;That’s bad enough, but because it’s moving in the other direction, that means there’s a potential for it to actually run off the &lt;em&gt;start&lt;/em&gt; of the video RAM addresses and start writing into the other end of it. So your backspace code ends up having a bunch of special cases for “if they’re trying to do backspace, don’t do the normal method and do something else entirely.” I fully admit that I could have made it cleaner by rewriting it instead of adding a bunch of spaghetti-code bodges, but that’s life.&lt;/p&gt;

&lt;p&gt;I also ended up making a clever piece of code for newline, which I’m somewhat proud of. The sticking point for me was trying to figure out which position is “the start of the next line.” After sitting and thinking about it for awhile, I decided that the cleanest way to express this in C would be something like:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_emit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;\n&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;insertion_point&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insertion_point&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a pretty simple solution. It finds the next line, and since it’s forcing it to a multiple of 32 using the modulo operator (%) then we’re sure it’s going to be the start of that line.&lt;/p&gt;

&lt;p&gt;However, modulo on Z80 is a little tricky to write, so I thought about it some more. And a few days later, in the shower, I realized that 32 is a power of two, which means I could just use a bitwise operation to knock off the bits that make up any value other than a multiple of 32:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_emit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;\n&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;insertion_point&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insertion_point&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b11100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For instance, buffer position 33 becomes 64 using this strategy (65 is 0b010&lt;strong&gt;00001&lt;/strong&gt;, which becomes 0b010&lt;strong&gt;00000&lt;/strong&gt; when you knock off the bits you don’t care about…)&lt;/p&gt;

&lt;p&gt;I am sure that this is a commonly-accepted trick with 8-bit machines, but I felt kind of clever for figuring it out for myself. This implementation of newline was pretty quick to express in Z80 assembly, which would now join backspace as the second “special case” to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EMIT&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;its-not-ok&quot;&gt;It’s not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ok&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Now I just needed to figure out why the interpreter was going bonkers and repeatedly invoking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUIT&lt;/code&gt;, which is the word that eventually puts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ok &lt;/code&gt; on the console.&lt;/p&gt;

&lt;p&gt;When I started on this project, I had a very rough idea of how interactive Forth interpretation would work, based on a dim memory of half-awake reading of some Forth books in university:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;User types some stuff.&lt;/li&gt;
  &lt;li&gt;User hits enter.&lt;/li&gt;
  &lt;li&gt;The interpreter breaks up the user’s input by splitting it along the spaces and storing the tokens in some kind of buffer – &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2 2 + .&lt;/code&gt; becomes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[&apos;2&apos;, &apos;2&apos;, &apos;+&apos;, &apos;.&apos;]&lt;/code&gt; – and starts executing from the left side.&lt;/li&gt;
  &lt;li&gt;The interpreter sees a token, then looks for it in the dictionary to see if it’s a word, and calls that word. At the end of each word’s implementation, it calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NEXT&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NEXT&lt;/code&gt; grabs the next token of the input and performs step 4 again.&lt;/li&gt;
  &lt;li&gt;Assuming there has not been an error, when there are no more tokens left, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NEXT&lt;/code&gt; calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUIT&lt;/code&gt;, which cleans up some interpreter state, prints &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ok&lt;/code&gt; to the console, and returns control to the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My skills in reading and following assembly are a little amateur-hour, especially when that assembly is interspersed with Forth. I decided to set a MAME breakpoint on the implementations of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY?&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY&lt;/code&gt;, but neither one was being called!&lt;/p&gt;

&lt;p&gt;The nice thing about CamelForth being based on CP/M is that I was able to figure out anything that was interacting with BDOS&lt;sup id=&quot;fnref:bdos&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:bdos&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; by checking what called the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BDOS&lt;/code&gt; word – a native-language call that sets up the arguments and then calls CP/M at &lt;span class=&quot;hex-value&quot;&gt;$05 &lt;/span&gt;. Naturally, since the SG-1000 doesn’t have a CP/M BDOS at that location, calling into that address would probably result in nothing, or a crash.&lt;/p&gt;

&lt;p&gt;After some more prodding around, I found a mysterious word &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CPMACCEPT&lt;/code&gt; that was repeatedly being called by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUIT&lt;/code&gt;. After reviewing, I realized that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QUIT&lt;/code&gt; was the code that handled the input buffer. So why did the readme talk about implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY?&lt;/code&gt;, but not this?&lt;/p&gt;

&lt;p&gt;I spent a good hour trying to figure out how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CPMACCEPT&lt;/code&gt; worked, and how to modify it for my purposes. Then I did a Google search, and found &lt;a href=&quot;http://www.camelforth.com/e107_plugins/forum/forum_viewtopic.php?237&quot;&gt;that the bug had already been reported by another hobbyist&lt;/a&gt;. The solution? Just change the call to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ACCEPT&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CPMACCEPT&lt;/code&gt;, which uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY&lt;/code&gt; as you might expect.&lt;/p&gt;

&lt;p&gt;Even after that discovery, I still had to wrestle with the code a little bit to try and understand it, and ended up gutting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY&lt;/code&gt; completely in order to get it to work like I thought it did (or at least, should.)&lt;/p&gt;

&lt;p&gt;I quickly banged out just enough of a keyboard driver to be able to enter the letter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; and the carriage return. Using some bodged code, I could enter a nonsense all-As word now, hit enter, and confuse the interpreter.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-nonsense-word-input.png&quot; alt=&quot;A nonsense word is entered, then followed by the enter key. For instance, AAAAA is entered, and then the interpreter goes &amp;quot;AAAAA?&amp;quot; meaning &amp;quot;I don&apos;t know what AAAAA&amp;quot; is.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Yeah, I &lt;em&gt;bet&lt;/em&gt; you don’t know what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AAAAA&lt;/code&gt; is. Not yet, at least.&lt;/p&gt;

&lt;p&gt;I defined a word in the assembly source:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;head AAAA,4,AAAA,docolon
        DW LIT, 5, LIT, 5, PLUS, DOT, EXIT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In other words, it’s the hardcoded equivalent of the Forth definition:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;: AAAA 5 5 + . ;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or in English, “when someone comes by looking for AAAA, you add 5 to 5 and then print out the result, capiche?” Maybe that’s multilingual, I don’t know.&lt;/p&gt;

&lt;p&gt;Anyway, after assembling, I typed in that new word, and called it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/camelforth-custom-word-being-emitted.png&quot; alt=&quot;The word AAAA is being executed, and printing out the result 10, as we would expect.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s &lt;em&gt;pretty cool&lt;/em&gt;!&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s NEXT?&lt;/h2&gt;
&lt;p&gt;Little Forth joke there. For the rest of RetroChallenge, I will be implementing keyboard handling. It’s been a couple years since I did the work described in this very post, and the pressure of the challenge is going to force me to actually write the boring keyboard-reading code, even if it’s inefficient or otherwise “big.”&lt;/p&gt;

&lt;p&gt;After that, my stretch goals are as follows, although I don’t plan or even hope to get to all of these this month:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Add TMS99xx/SN76489 words to Forth, so I can do graphics and sound;&lt;/li&gt;
  &lt;li&gt;Take advantage of the Soggy’s 32K of RAM and page-switching to allow you to write truly gargantuan dictionaries;&lt;/li&gt;
  &lt;li&gt;Figure out a way to do long-term storage of Forth dictionaries;&lt;/li&gt;
  &lt;li&gt;Build a &lt;em&gt;useful&lt;/em&gt; Soggy Forth cartridge that can be used by anyone silly enough to put a keyboard on their Sega;&lt;/li&gt;
  &lt;li&gt;Maybe even figure out how to write a useful Forth program.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading! See you next week for part 2.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:not-exactly&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Although an SG-1000 with SK-1100 keyboard attached can run many of the SC-3000 titles and do “computer things,” it cannot run &lt;em&gt;all&lt;/em&gt; of them due to a variety of reasons. My Soggy-1000 clone attempts to patch some of these differences over, but we won’t go into those details in this light little introduction to Sega history. &lt;a href=&quot;#fnref:not-exactly&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:camelforth-asm&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Adding assembly words to CamelForth-80 is not in the planned scope of RetroChallenge this year. Maybe I’ll do it some other time, or just go nuts and write a whole dedicated CP/M port down the road. &lt;a href=&quot;#fnref:camelforth-asm&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:basic-tms9918&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;MITEC/Sega’s BASIC implementations have a clever “shadow” framebuffer so that the graphics commands can support bitmapped graphics with the TMS9918’s tile modes, but they sacrifice a lot of RAM to do it. &lt;a href=&quot;#fnref:basic-tms9918&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:immediate&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;In Forth, an “immediate” word is one that is executed &lt;em&gt;immediately&lt;/em&gt;, instead of being compiled by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:&lt;/code&gt; keyword into a program. For end users, it can be used to &lt;a href=&quot;https://www.forth.com/starting-forth/11-forth-compiler-defining-words/&quot;&gt;alter the behaviour of the colon-compiler in the middle of its operation&lt;/a&gt;, potentially doing something like a macro expansion, but is largely unimportant if all you want to do is write simple programs in Forth and not develop a Forth interpreter like this one. &lt;a href=&quot;#fnref:immediate&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:basic-ram&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The SK-1100 BASIC cartridges, intended for SG-1000, get around this by adding some extra SRAM to handle the ‘drawing buffer.’ I constructed a knockoff of this cartridge in &lt;a href=&quot;https://www.leadedsolder.com/2022/11/01/sega-sk1100-keyboard-pickup-test.html&quot;&gt;the SK-1100 test article&lt;/a&gt;, which certainly could be used to build a Forth cartridge in the future. &lt;a href=&quot;#fnref:basic-ram&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:editor&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;A lot of 8-bit BASIC implementations let the user scroll up and edit a previously-typed line, then hit return to “send it.” This is not a feature on SC-3000 BASIC, probably for the same reason of limited RAM, so I won’t be implementing such a thing for CamelForth-SG. &lt;a href=&quot;#fnref:editor&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:bdos&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;BDOS is the pseudo-platform-independent “Basic Disk Operating System” component of CP/M, joining &lt;a href=&quot;https://www.seasip.info/Cpm/bios.html&quot;&gt;the machine-specific BIOS&lt;/a&gt;. It &lt;a href=&quot;http://elysium.filety.pl/docs/c128-cpm/cpmbdos.html&quot;&gt;provides a bunch of syscalls for things like handling files and the terminal&lt;/a&gt;. &lt;a href=&quot;#fnref:bdos&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="computer" /><category term="sega" /><category term="sg1000" /><category term="sc3000" /><category term="soggy1000" /><category term="homemade-software" /><category term="retrochallenge" /><category term="retrochallenge-october-2026" /><summary type="html">The Soggy needed a good low-level programming environment with which to tinker on future hardware projects, and ideally it was one that I controlled myself, so I could include it on the onboard ROM without infringing copyright. It would be nice if it used a semi-popular programming language for embedded systems, and had an interactive development environment that I could run right on the machine. All that seems to mean I’m finally going to make my first Forth.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/camelforth-custom-word-being-emitted.png" /></entry><entry><title type="html">Dogfighting the Sharp X68000 ACE</title><link href="https://www.leadedsolder.com/2026/09/29/x68000-grey-ace-corrosion-repair-failed-start.html" rel="alternate" type="text/html" title="Dogfighting the Sharp X68000 ACE" /><published>2026-09-29T00:00:00+00:00</published><updated>2026-09-29T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/09/29/x68000-grey-ace-corrosion-repair-failed-start</id><content type="html" xml:base="https://www.leadedsolder.com/2026/09/29/x68000-grey-ace-corrosion-repair-failed-start.html">&lt;p&gt;When &lt;a href=&quot;https://www.leadedsolder.com/2019/05/20/hello-x68000.html&quot;&gt;we were first introduced to the X68000 ACE&lt;/a&gt;, we saw that the board was very corroded from battery damage. Later on, I &lt;a href=&quot;https://www.leadedsolder.com/2019/07/04/x68k-pc98-pc88-mini-updates-4.html&quot;&gt;whacked the battery out of it and did my best to clean up the corrosion&lt;/a&gt; without completely dismantling the machine. This time, it’s personal. Workstation, that is.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-posing.jpg&quot; alt=&quot;The X68000, in sadder times&quot; /&gt;&lt;/p&gt;

&lt;p&gt;My initial assumption was that all I’d really need to do is get the I/O board out and repair the obvious battery damage. But, as we all know, old plastics can conspire to slow you down on a project. In the end, the system still isn’t working properly, but it’s come a long way.&lt;/p&gt;

&lt;p&gt;I had sat on this project for a few months/years, because it doesn’t really have a satisfying conclusion, but I decided that it would be better to publicize my struggles. Maybe it’ll help you fix your own X68000!&lt;/p&gt;

&lt;h2 id=&quot;taking-it-apart&quot;&gt;Taking It Apart&lt;/h2&gt;
&lt;p&gt;As you might recall, a twin-tower X68000 consists of two major boards:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The mainboard, which contains the RAM, BIOS ROM, various custom chips, and the CPU;&lt;/li&gt;
  &lt;li&gt;And the bottom board, which handles a lot of the 8-bit I/O (SASI, FDC, serial, etc) as well as containing the clock battery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s an number of other sub-boards in my system, such as the one that breaks out the external SASI port, the analogue video output board, and the RAM expansion, but those aren’t really important today.&lt;/p&gt;

&lt;p&gt;My goal here was to extract the bottom board from the system so it could be cleaned up.&lt;/p&gt;

&lt;p&gt;Getting the bottom K5710DE “I/O” board out of the twin-tower X68000 ACE is nowhere near as hard as I thought it was. After looking at a YouTube video, I realized all I had to do was disconnect all the cables from that bottom board, and then remove two screws hidden at the rear of the ‘spine.’ After that, the five screws on the bottom of the machine came right out, which releases the bottom plate and board from the plastic bits. The board is screwed to the bottom plate of the case with three easy-to-remove screws.&lt;/p&gt;

&lt;p&gt;Then all you have to do is awkwardly lay the rest of the system on its side, and try not to scratch the plastic any worse.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-io-board-removed.jpg&quot; alt=&quot;An overview of the I/O board, after some small cleaning.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All of the exposed copper meant for grounding to the screw holes is corroded, with some of it having gotten a big dose of battery acid and corroding even further. The one in the centre is especially bad:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-copper-hole-rust.jpg&quot; alt=&quot;The copper hole in the middle of the board is extremely rusty.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On the initial inspection, I missed that the pin header that goes to the hard drive (SASI) breakout board got corroded as well. Although the connector cleaned up with some alcohol, there’s some green crap all the way up inside the ribbon cable, which probably necessitates a brand new replacement in the future:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-sasi-corrosion.jpg&quot; alt=&quot;The 20-pin SASI connector has some corrosion blossoms on the pins&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I marked the cable with an “X,” so I wouldn’t be surprised later when it came time to put a hard drive in it.&lt;/p&gt;

&lt;p&gt;The bottom of the I/O board is surprisingly clean, with only a few spots covered in some kind of dusty white corrosion that seems to be not uncommon for machines stored in Japanese humidity.&lt;/p&gt;

&lt;p&gt;Just like on the X1turbo, Sharp’s engineers put the I/O-board silkscreens on both sides to aid in diagnostics. Thank you!&lt;/p&gt;

&lt;p&gt;Most of the actual gunk seemed to just be old flux on the ports, but I noticed a suspicious amount of dirt far away from them, especially on the bottom of this trim cap:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-trim-cap-corrosion.jpg&quot; alt=&quot;The trim cap footprint, before cleaning&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was probably a hand-soldered part. It cleaned up fairly well, and left no indication of damaged traces behind.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-trim-cap-footprint-cleaned.jpg&quot; alt=&quot;The trim cap&apos;s footprint is now cleaned&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On the topside of the board, nothing looked suspicious, although this whole assembly &lt;em&gt;is&lt;/em&gt; very close to the RP5C15 time-of-day chip that gets fingered for a large number of soft-power-related failures:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-trim-cap.jpg&quot; alt=&quot;The trim cap, on the top of the board&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;trace-you-there&quot;&gt;Trace You There&lt;/h2&gt;
&lt;p&gt;One thing that I noticed is that there was a lot of solder-resist damage on a thick trace running near the battery. When battery corrosion gets really bad, it damages the connection between the copper and the green solder mask, lifting it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/solder-mask-corrosion-diagram.png&quot; alt=&quot;A diagram showing solder mask corrosion - the corrosion inside the copper is bubbling up and damaging the solder mask above it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In other words, it rusts like a car does - from the inside, so as soon as you notice a paint bubble, it’s often too late for the metal.&lt;/p&gt;

&lt;p&gt;I started probing around here to try and figure out where this thick trace that passed under the original GB50H-3 battery goes. Eventually, I started scraping off the damaged solder mask with an old Olfa blade, until some bare copper was revealed. I checked with a multimeter to see if it was working at all. Everything was electrically connected here to its destination, so I did some more scrubbing, tinned it, and made a note to cover this in UV solder mask later. This isn’t where the fault is, but it sure looks dramatic.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-battery-damage-ground-zero.jpg&quot; alt=&quot;Battery damage ground zero. Corroded vias, corroded traces, damaged solder mask&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are, however, a lot of vias in this general area that run between the major backbone ribbon connector (I call it the “Big Ribbon,” for lack of an official service manual term) and IC13, a 100-pin Sharp proprietary QFP in the middle of the I/O-board called the “IOSC.” From what I can tell of looking at a similar chip on the official XVI schematics (the ACE ones are unavailable,) this part seems to jam together a lot of interface duties for &lt;a href=&quot;https://nfggames.com/forum2/index.php?topic=2859.msg19609#msg19609&quot;&gt;things like the floppy drives and SASI hard drive&lt;/a&gt;. Important, in other words.&lt;/p&gt;

&lt;p&gt;I wanted to make sure these traces were all OK before buttoning the machine up again and beginning on the power supply repair, which at this time I had not begun. A handful of those vias buzzed out to the IOSC directly, but a lot of them were so corroded with chalky, gritty grey solder globs inside them that they were no longer conductive. The vias themselves still seemed to be intact, as I could buzz a handful of the pins on the 68-pin “Big Ribbon” connector all the way to pins on the IOSC chip, but not all.&lt;/p&gt;

&lt;p&gt;I figured it was possible that I had corroded traces broken away from the vias, but but without being able to check all the vias, I couldn’t test them to be sure. On top of that, the compromised solder plugging those vias could be holding battery electrolyte that would eventually eat the copper in them - and then I’d definitely have to be able to clear them out to run a patch wire bottom-to-top. And that would just be a ton of work nobody wants to do, right? Right???&lt;/p&gt;

&lt;h2 id=&quot;dismantling-and-cleaning&quot;&gt;Dismantling and Cleaning&lt;/h2&gt;
&lt;p&gt;After looking at the I/O board, I put the machine back together and put it on the shelf for a few months. Without a schematic or a place to put all the guts of the machine while I had it dismantled, fixing the I/O board seemed intimidating.&lt;/p&gt;

&lt;p&gt;I moved on to the power supply, and that meant there was a lot of cleaning involved.&lt;/p&gt;

&lt;p&gt;One obvious issue is that the power supply is clogged solid with dust. It left big black streaks of grime behind on the plastic, probably because the fan is too choked to function:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-psu-fan-clog.jpg&quot; alt=&quot;The power supply fan is choked solid with grime.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Hopefully the dust absorbed some of the capacitor goo that is surely inside this thing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-psu-cable-clog.jpg&quot; alt=&quot;There&apos;s so much gunk inside the power supply that it&apos;s coming out of the &amp;quot;output&amp;quot; hole.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I noticed some other interesting things during the disassembly, like the floppy ribbon being attached with velcro:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-floppy-velcro.jpg&quot; alt=&quot;The floppy drive ribbon cable has stick-on velcro so it can adhere tightly to the floppy drive shell.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After removing the PSU and floppy drive and external hard drive breakout board, I noticed that the internal speaker is bolted to the inside of the plastics of one of the “twin towers:”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-one-side-removed.jpg&quot; alt=&quot;The internal speaker is hiding inside the plastics, attached with two short fine-thread screws.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The “pop up” handle in the middle has this cool-looking pogo stick part that also holds the two halves of the case together:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-pogo-stick.jpg&quot; alt=&quot;There&apos;s a spring loaded piston in between the two halves of the case, with a metal handle.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The piston in mine must be gummed up, as I’ve seen videos where it has a smooth easy action with some “easing.” Mine judders from position to position and won’t smoothly retract without lots of force. As you can see, I’m also missing one of the little plastic “feet” that keeps the handle action feeling good on a desktop. Maybe a good excuse to buy a lathe?&lt;/p&gt;

&lt;h2 id=&quot;motherboard-comes-out&quot;&gt;Motherboard comes out&lt;/h2&gt;
&lt;p&gt;You have to remove the front fascia to get at the motherboard, as it has a pretty complicated three-piece RF shield. Here’s an absurd-resolution picture of just how bad my case plastics are:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-front-fascia.jpg&quot; alt=&quot;The front case plastics, reading X68000 ACE Personal Workstation. It has a lot of missing paint and dings.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You also have to remove the expansion card cage. Luckily, it is easy to unscrew because they left these nice reliefs for your screwdriver.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-card-cage-screw-hole.jpg&quot; alt=&quot;A screwdriver blade can fit right into the holes left for easy unscrewing of the card cage.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However, the whole cage is on a &lt;em&gt;massive&lt;/em&gt; 100-pin 2-row pin header which is very nerve-wracking to undo:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-card-cage-header.jpg&quot; alt=&quot;With the card cage removed, a 100-pin connector sticks out of the motherboard. Miraculously, I didn&apos;t bend any pins removing it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The RF shield was bouncing around the whole time; it’s very thin metal, and seems to have warped with heat and age.&lt;/p&gt;

&lt;p&gt;The motherboard has a lot of interesting stuff too, like the Messiah. So that’s where he was hiding all this time. Someone tell the Pope.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-motherboard-messiah.jpg&quot; alt=&quot;A Sharp chip is called &apos;Messiah&apos; on the motherboard.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Our Messiah hangs out with a video controller chip called, unimaginatively, Vicon:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-motherboard-vicon.jpg&quot; alt=&quot;Another Sharp chip is called &apos;Vicon&apos; on the motherboard.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The 68K and HD63450 DMA controller are hanging out in the extreme corner of the motherboard, which seems like a weird place to put it since generally you want a CPU to be, well, central. For some reason, the 68k is thankfully socketed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-motherboard-68k-and-dma.jpg&quot; alt=&quot;The X68000&apos;s HD68HC000 and HD63450 chips.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I noticed that these custom chips near the “IMDRAM” have some solder-bridged pins, but based on their position they are probably power pins, and the bridge is deliberately caused by the solder mask.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-blobbed-chips.jpg&quot; alt=&quot;Solder bridges between two pins on these little 44-pin chips, IC93 and IC94.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also saw these empty ROM sockets, which say they’re for “512K ROM” odd and even. Like in the X68000 PRO, this lets you run an alternative BIOS&lt;sup id=&quot;fnref:exbios&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:exbios&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. The original chips are soldered in, and the nearby SW1 jumper no doubt changes which set of chips the select signal goes to.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-rom-sockets.jpg&quot; alt=&quot;Two empty ROM sockets.&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;that-sinking-feeling&quot;&gt;That sinking feeling&lt;/h2&gt;
&lt;p&gt;With all the plastics removed, I scrubbed them down in the sink. Tons of grit and grease came pouring off of every single one of them, and so I gave them a quick vacation in the sun to dry before 303ing the plastic. It’s still in not-great condition (lots of chips to the paint and damage on the edges) but at least now it’s not as gross to touch.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-plastics-sunning.jpg&quot; alt=&quot;The X68000&apos;s case plastics are sitting on my deck in the sun.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I counted over 100 screws on disassembly, which rapidly overwhelmed my ability to keep them organized. A very complicated case!&lt;/p&gt;

&lt;p&gt;While I was waiting for everything to dry, I replaced the missing and torn feet on the metal bottom of the case with &lt;a href=&quot;https://www.digikey.ca/en/products/detail/3m/SJ-5012-BLACK/30809&quot;&gt;3M SJ-5012 half-inch rubber pads in black&lt;/a&gt;. They are pretty much identical to the original feet, right down to the taper, which makes me wonder if it might actually be the original part.&lt;/p&gt;

&lt;p&gt;Since the light grey plastic had so many dings and scratches in it, I also gave it a quick treatment with Novus plastic polish. The jury is still out on whether it looks &lt;em&gt;a lot&lt;/em&gt; better, but it definitely has reduced some of the more obvious scratches and made the sides mostly shiny again.&lt;/p&gt;

&lt;p&gt;To fully restore the aesthetics would require some fresh paint, as the paint is very badly damaged in parts and down to bare plastic in several places. Then I’d need new stickers and labels! If I can put this thing back together and have it running at the end of all this, it’s good enough for me.&lt;/p&gt;

&lt;h2 id=&quot;power-supplied&quot;&gt;Power, Supplied&lt;/h2&gt;
&lt;p&gt;At this point, I put everything else aside to focus on &lt;a href=&quot;https://www.leadedsolder.com/2021/01/05/x68000-ace-power-supply-picopsu.html&quot;&gt;building a “new” power supply for the X68000&lt;/a&gt;. Feel free to take a departure and read about how that went.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-new-psu-assembled.jpg&quot; alt=&quot;The power supply is put back together. You can see a thick three-prong electrical cord sticking out of it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In brief, a PicoPSU adapter board was assembled with an onboard AC/DC converter inside the existing power supply case, so that the computer retained its original feel and layout. It worked well!&lt;/p&gt;

&lt;h2 id=&quot;hold-my-battery&quot;&gt;Hold My Battery&lt;/h2&gt;
&lt;p&gt;Another side quest was embarked upon at this point.&lt;/p&gt;

&lt;p&gt;As I mentioned before, the original battery for the X68000 is a rechargeable NiCad.&lt;/p&gt;

&lt;p&gt;Coin cell batteries are much less likely to leak than a NiCad barrel battery, although it’s still very possible and has happened to me in the past. With a holder, at least I can remove the battery if the computer goes into storage for a very long time in the future.&lt;/p&gt;

&lt;p&gt;Electrically, I was nervous about choosing a 3.0V ML2032 or 3.6V LIR2032 battery for this because the charge methods differ between the original NiCad cell and the lithium-ion chemistry. The last thing I want is to trade NiCad damage on my I/O board for much more reactive lithium corrosion.&lt;/p&gt;

&lt;p&gt;After doing some research, the datasheet for the SRAM chip (MB8464A-15L) says that I only need 2.0V to keep it alive, so a non-rechargeable CR2032 with a diode to block the recharge functionality should work very well.&lt;/p&gt;

&lt;p&gt;I spun up a quick PCB (very cheap on Osh since it’s tiny) and then put it together. It uses an 0805 Schottky diode to try and minimize the voltage drop, but even a “regular” amount of diode drop is okay because, as I determined before, the SRAM chip only needs 2.0V. This was my first time installing a surface-mount diode, so I had to squint to figure out on which side the cathode line was.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-battery-holder-pcb.jpg&quot; alt=&quot;The battery holder PCB assembled. It has a coin-cell battery holder, some legs, and an 0805 surface-mount diode in the corner. A silkscreen marking in the side says &amp;quot;+ TO X68K&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It would have been nice to make this PCB a little bigger, because it turns out to be pretty wobbly all around. The original battery holder has three points of plastic that contact with the motherboard for bracing, but the PCB only has the two electrical contacts.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-battery-holder-pcb-test-fit.jpg&quot; alt=&quot;The battery holder PCB installed into the I/O board. You can see that it has a little bit of height added over the original battery, which contributes to the wobble.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It will be annoying in the future to put a battery in or take it out of the holder because of all the wobble, but that’s better than leaking. If I get back in here, I’ll probably put a bit of sticky tack in to provide a little bit of support to the PCB.&lt;/p&gt;

&lt;p&gt;Pleased that it fit, I didn’t solder it in, because I knew I’d be messing with the board underneath. And here’s why…&lt;/p&gt;

&lt;h2 id=&quot;scraping-the-bottom&quot;&gt;Scraping The Bottom&lt;/h2&gt;
&lt;p&gt;The I/O board still needed to be repaired. I figured I would start by scratching off the contaminated solder mask, and getting the corrosion and battery electrolyte out of the copper traces.&lt;/p&gt;

&lt;p&gt;Even though this trace looks very bad, it is still continuous with extremely low resistance. The fibreglass pencil removed a lot of the contaminated solder mask, but it wasn’t good at removing the rest of the solder mask, unlike with &lt;a href=&quot;/tag/amiga2500&quot;&gt;the Amiga 2500&lt;/a&gt;, where most of the solder mask came right off within a few swipes. I ended up scraping a lot of it off with a dollar-store box-cutter, which was a harrowing experience until I got the feel for it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-68pin-battery-damage-cleaning-1.jpg&quot; alt=&quot;The copper on many vias and traces is blooming with green corrosion.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After about half an hour of very slow work, I got to see these hard brown spots in the copper. I wasn’t sure if this was contaminated copper that would start oxidizing again, or if it was just fibreglass substrate where the copper had totally worn away. It wasn’t conductive, so I suspected the latter, but I wanted to make sure.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-68pin-battery-damage-cleaning-2.jpg&quot; alt=&quot;The copper inside the big traces has been exposed by clumsy knife scratching.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The negative battery-terminal trace on top has been severed entirely by the damage - the eyelet on the top is completely corroded. There are &lt;a href=&quot;https://www.youtube.com/watch?v=FJiaTq3dDXA&quot;&gt;rivets that you can install to fix a torn plated through-hole&lt;/a&gt;, but I didn’t have any or the tools to install them (now corrected,) so a quick patch wire from the negative battery terminal to the vias will return the battery to proper function for now.&lt;/p&gt;

&lt;p&gt;We’ll worry about that when it comes time to install the aforementioned coin-cell holder. I want to keep a battery in here so that the boot SRAM (where you load drivers, not just clock and configuration - more on this later) will continue to work.&lt;/p&gt;

&lt;p&gt;Once I was somewhat satisfied that the big traces wouldn’t turn back into green nightmares, I turned my attention to the IOSC vias.&lt;/p&gt;

&lt;h2 id=&quot;plugging-away&quot;&gt;Plugging away&lt;/h2&gt;
&lt;p&gt;I worked for a couple of days on and off to try and clean out the chalky, corroded vias on the I/O board so I could test out the traces from the 68-pin “T” connector to the IOSC chip, including buying a pointier tip for my soldering iron to better direct the heat so I could “burn out” the gunk inside.&lt;/p&gt;

&lt;p&gt;This was a lot of fiddly work, and very discouraging as at the end of most days I had no concrete improvement to show for my effort. Eventually, though, the vias were cleaned enough that I could get continuity from the vias to the “T” connector, verifying that the traces on the underside of the board (and the vias themselves) were intact. All 17 vias now buzzed out just fine top-to-bottom, which made me feel a lot better.&lt;/p&gt;

&lt;p&gt;Unfortunately, things weren’t as good on the other end of the circuit. Only 8 out of the 17 vias were continuous with the IOSC chip. I would have preferred it the other way - it’s much easier to solder to the through-hole “T” connector than it is to the very fine pitch IOSC chip! Before running some patch wires, I wanted to verify that the traces were in fact bad. If there was just a small portion of the traces that I could clean up and fix, then that would be much easier than trying to bodge-wire onto the legs of the QFP.&lt;/p&gt;

&lt;p&gt;I suspected the damage on the traces must be very close to the vias, since that was where most of the battery electrolyte and blue-green corrosion was hanging out. Even a very thick copper trace must become thinner when it approaches a via, and it’s well-known in the PCB manufacturing community that &lt;a href=&quot;https://www.nextpcb.com/blog/acid-traps&quot;&gt;T-shaped intersections and 90-degree turns are notorious for trapping acid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It took me about an hour to trace out where each via went. I took a high resolution picture and used a paint program to draw lines, which took awhile but is a lot less less error-prone than doing it by hand:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-iosc-trace-out.jpg&quot; alt=&quot;The IOSC trace out. Multicoloured lines flow from the vias to the pins of the IOSC.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All of the pins that had additional vias nearby the IOSC were continuous from that via to the IOSC. Did that sentence make sense? Either way, it meant that the damage was closer to the vias, as I had assumed. Some of the pins, like the one I labelled “13,” were obviously obliterated.&lt;/p&gt;

&lt;p&gt;After scraping back a little bit of the solder mask using an Olfa AK-4 knife (I can’t believe I used a box-cutter for so long for this - the Olfa is delightful) my theory was confirmed. Part of the trace is simply missing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-iosc-via-traces-broken.jpg&quot; alt=&quot;A trace running from a via towards the IOSC is partially missing. There is some shiny exposed copper where the solder mask has been mechanically removed.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The exposed copper part of that trace buzzes to the IOSC no problem, but it &lt;em&gt;isn’t&lt;/em&gt; connected to the via. I was correct in my initial guess that it has severed itself at the via end. So at least the required patches are short… but they’ll be pretty close together, which is a hell all its own.&lt;/p&gt;

&lt;h2 id=&quot;the-way-back-out&quot;&gt;The Way Back Out&lt;/h2&gt;
&lt;p&gt;On previous repairs, I’ve &lt;a href=&quot;https://www.leadedsolder.com/2019/11/13/bad-adb-mac-lc.html&quot;&gt;patched point-to-point using Kynar wire&lt;/a&gt; or &lt;a href=&quot;https://www.leadedsolder.com/2018/03/01/corroded-genesis-ram-trace.html&quot;&gt;cut-off component leads&lt;/a&gt;. On this I/O board, however, these traces are thin and close together, so I decided to try another route.&lt;/p&gt;

&lt;p&gt;On the suggestion of some friends, I decided to buy some very thin magnet wire from AliExpress to patch these traces - since it was cheap, I ordered both 0.2mm and 0.3mm as my existing 0.5mm seemed too thick, although I ended up waiting so long for shipping on it that I ended up ordering another reel of 38 AWG (0.127mm) from Amazon, which of course immediately caused the AliExpress order to show up the next day.&lt;/p&gt;

&lt;p&gt;The moral of the story is that if you’re reading this paragraph and think you might want to repair traces in the future, go order some magnet wire from AliExpress right now and it might show up by the time you get your next broken PCB.&lt;/p&gt;

&lt;p&gt;Since it was my first time using magnet wire to patch traces, I picked up a syringe of paste flux (&lt;a href=&quot;https://www.mgchemicals.com/products/solder-and-accessories/fluxes/no-clean-flux-paste-8341&quot;&gt;MG #8341&lt;/a&gt;). The idea I had was that the paste would add some height, so that the wire would be completely immersed when it was being soldered on and it wouldn’t matter as much about being able to hold the wire firmly and flatly to the board while soldering it. This way, the flux would provides surface tension that would help the melting solder to pull the wire flat onto the board.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ref9JHUf-uw&quot;&gt;This lengthy video by Branchus Creations&lt;/a&gt; has a fantastic zoomed-in view of the wire adhering, which gave me the confidence that I was on the right path.&lt;/p&gt;

&lt;p&gt;When I actually went to do the patches, I first tried with liquid flux. It was really difficult to get a good adhesion. It only occured to me here that in addition to the height, the paste flux obviously contains way more flux per drop, because it doesn’t spread out and try to run away from the part that I’m working on. I’m a convert - I only wish it were easier to store the syringe.&lt;/p&gt;

&lt;p&gt;To do the patch wiring, I cut off a ~2cm length of magnet wire and then stabbed it through each of the vias. Then, I filled the vias with solder, so that it would be conductive with the wire and also hold it in place. With the wire held tightly, I could bend it on the top side to meet the exposed copper of the trace and solder it down.&lt;/p&gt;

&lt;p&gt;This makes it sound much easier than it actually was, because the wire is annoying to remove the coating from in practice (tin both the trace and the wire first, if you can) and everything is super small and hard to see without magnification. Before I figured out what to look for, I had a lot of trouble predicting how long I would have to keep the iron on to get rid of the coating so it would become “continuous.”&lt;/p&gt;

&lt;p&gt;It gets worse if any contamination is kicked up from the rest of the board, because that obscures the traces even further and forces you to take a break to clean. It took me about forty minutes to do the first trace, and then the subsequent ones were a little faster. Occasionally I would bridge one trace to the one I had just done by using a bit too much solder, which necessitated some re-work. At one point I actually dropped the entire wire back out of the via for the “#1” trace, and had to redo the entire job.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-trace-repair-begins.jpg&quot; alt=&quot;Six traces are patched with solder-covered wires. There are some ugly solder globs on them, and the traces are still partially exposed in places.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here is a photo after I had repaired six traces. It looks ugly zoomed in, but it’s difficult to notice this detail with human eyes. I reflowed the joints after this and tried to make it look okay with a magnifying visor, but I found it really hard to produce a good clean smooth joint.&lt;/p&gt;

&lt;p&gt;I’ve gotten a little bit better at trace repair in the years since I did this job, but not a whole lot better.&lt;/p&gt;

&lt;p&gt;One thing that really helped was to glob up the soldering iron tip with solder and then run it along the wire and pad while holding it in place with tweezers, like when I’m drag-soldering SMD chips. Unfortunately, I only figured this out near the end.&lt;/p&gt;

&lt;p&gt;I started on the vias at the bottom of the photo, and you can tell that I made the wires and their contacts too short. Part of the secret to making this look good and have a long-term reliable connection is to have a large “surface area” where the trace joins to the wire.&lt;/p&gt;

&lt;p&gt;I was sure to scrape off and patch past the black corrosion that you can see on the furthest-top trace. Although they were still continuous, you could see some gunk getting exposed as the mask was scraped off:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-trace-repair-begins-2.jpg&quot; alt=&quot;Nine traces are patched with solder-covered wire. Other traces, not yet repaired, are scraped free so you can see the bare copper.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I think a cool way to repair this (which might potentially be profitable as this damage is common on early X68000s) would be to make a flex-PCB that would pin into the vias and then let you tin some exposed traces onto the original traces. Sort of like an iron-on transfer.&lt;/p&gt;

&lt;p&gt;In any event, all of this is going under solder mask for protection, so as long as it’s got a good connection, I’m reasonably pleased.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-trace-repair-backside.jpg&quot; alt=&quot;The backside of the trace repair has a bunch of loose wires sticking out, along with a sticky black blob.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The backside produced a lot of sticky black goo which I think was a mixture of flux, leftover battery electrolyte, corrosion from the inside of the via, and the enamel coating of the magnet wire. Gross! But it wiped off with some effort. I clipped all these wires later, of course, but they were useful for testing and to keep from pulling the wire out of the via while I was soldering the other end.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-trace-repair-completed.jpg&quot; alt=&quot;All 17 wires are connected in varying quality.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At last, all of the obvious I/O board patching was done. Each wire buzzed out between the T-connector and a pin on the IOSC, the pins seemed to go in vaguely ascending order, and there were no shorts between pins. I don’t want to add up how long this took.&lt;/p&gt;

&lt;p&gt;For the first test, I didn’t seal this up with solder mask or install the battery holder. If it turns out I had missed a spot, it would really stink to have to remove all of that stuff in order to get back to these traces.&lt;/p&gt;

&lt;p&gt;Now to fire up the computer! Let’s go!&lt;/p&gt;

&lt;h2 id=&quot;reassembly&quot;&gt;Reassembly&lt;/h2&gt;
&lt;p&gt;Reassembling this thing was intimidating. As I mentioned earlier, it had been a few months since I took the computer apart, and it has dozens of screws.&lt;/p&gt;

&lt;p&gt;I did what I always do when confronting something that makes me nervous, and I made a list.&lt;/p&gt;

&lt;p&gt;Top of that list was to get the piston action to be less “juddery.” Although I didn’t plan on often using the carrying handle, what’s the point in tearing the machine all the way down to this point without touching it up while it’s easy? I dripped a bit of 3-in-1 oil into it after picking out a couple chunks of dust and hair from the track that the spring cup glides in. The same slow action continued but now it was much smoother.&lt;/p&gt;

&lt;p&gt;One big mistake that I made, because of my terrible screw organization when ripping the computer apart, was taking the three fine-thread M2 screws meant for mounting the motherboard to the threaded inserts in the case, and mixing them up with the two coarse-thread M2 screws meant for mounting the speaker to the case plastic. I ended up stripping out both of the speaker holes in the case plastic - they &lt;em&gt;must&lt;/em&gt; be the right screws, &lt;em&gt;they’re in the bag with the speaker!&lt;/em&gt; Yeah, I put the speaker on a corner of my desk before bagging it up and that’s where I &lt;em&gt;also&lt;/em&gt; placed the motherboard mounting screws.&lt;/p&gt;

&lt;p&gt;To repair the now hogged-out screw holes, I ended up filling the holes with super glue. After some more experimentation, I determined that these were “thread-forming screws” and would need a pilot hole drilled in order to work effectively. The smallest drill bit I had on hand was 1/16”, which at ~1.58mm was &lt;em&gt;pretty close&lt;/em&gt; to 2mm.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-speaker-screws-fixed.jpg&quot; alt=&quot;The speaker screws are back in. What looks like a minor roadbump on this post was actually 3 days of trial and error, 15 minutes at a time. KEEP YOUR SCREWS ORGANIZED.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once the pilot hole was drilled into the set super glue, I was able to thread in a screw. I wouldn’t trust this to hold any serious torque or weight, but it seemed to be enough for the speaker. We’ll see how this holds up - a better repair method would be to use some kind of plastic-compatible epoxy because super glue is quite brittle.&lt;/p&gt;

&lt;p&gt;When reinstalling the floppy drive, I found out that my newly-spliced floppy power cable was now actually &lt;em&gt;too long&lt;/em&gt; compared to before. This was sort of a nice problem to have; I ended up airplane-looping it behind the SASI breakout board to take up some slack. There’s quite the mess of wires in this computer in general:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-io-cable-maze-reassembled.jpg&quot; alt=&quot;Two ribbon cables (one for SASI, one for floppy), wires for speaker, floppy front panel buttons, IO board power, main board power, floppy power, etc are all crammed into a tiny space between the foot of the power supply, the floppy drives, and the SASI breakout board.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I noticed that due to the rust on the metal parts of the case, or possibly a previous owner, or more likely incautious removal on my part, some of the brass inserts were cross-threaded and difficult to get the fine-threaded screws in properly.&lt;/p&gt;

&lt;p&gt;I kept having to take screws out and put them back in, because later layers reused the same screw holes. There are some screws (namely the SASI breakout screw at the back) that hold in four or more major components. No wonder I had such an annoying time taking this thing apart! Finally I triumphed (of a sort - I was short several coarse-thread plastic screws and long several small fine-thread machine screws) and was able to stand the machine back up as one discrete unit on my desk once again.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-reassembled.jpg&quot; alt=&quot;The X68000 is sitting on my desk with no sides installed. The handle is up.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At which point it creaked like an old house for about 20 minutes, as all the plastics had expanded during their month of freedom on my workshop floor, and now were a little grumpy at being back with their partners of 30-plus years. Sure looks nice with the freshly polished case front, though!&lt;/p&gt;

&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;
&lt;p&gt;I’m not going to lie: I was intimidated. Few things would be worse than spending all this time, effort, and money, and then watching this machine smoke one of its bespoke components. It wasn’t like it was thoroughly tested in Japan, either: it could very well have been dead before it got to me and my repair of the IOSC traces wasn’t the only problem.&lt;/p&gt;

&lt;p&gt;In the initial test, since I didn’t have any high-density 5.25” diskettes, I would be looking for the following things:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Red light turns on when the machine is plugged in and switched on, but not powered on;&lt;/li&gt;
  &lt;li&gt;Green light turns on when the soft power toggle button on the front is pushed;&lt;/li&gt;
  &lt;li&gt;Video is generated;&lt;/li&gt;
  &lt;li&gt;…and a legible “insert disk” message/logo is shown!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I did have a boxed retail copy of &lt;a href=&quot;https://www.youtube.com/watch?v=YXT3Dtwcl40&quot;&gt;&lt;em&gt;Klax&lt;/em&gt;&lt;/a&gt; that I got from the Suruga-ya in Shibuya, but I didn’t really know if it actually worked, nor did I know if my untested floppy drive would just rip the magnetic coating off the disc with a misaligned head. Best not to test with irreplaceable disks.&lt;/p&gt;

&lt;p&gt;After cleaning the desk off so there was a place for it to sit, it was Halloween of all days. Perfect for some spooky behaviour!&lt;/p&gt;

&lt;p&gt;I connected it to power and turned the hard power switch on, and saw that the red power light was on.&lt;/p&gt;

&lt;p&gt;I pushed the power button on the front of the computer. The LED changed to green, and the computer leapt to life!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-first-power-on.jpg&quot; alt=&quot;The green power LED is on, indicating soft power is active&quot; /&gt;&lt;/p&gt;

&lt;p&gt;…And showed a screen of garbage amongst a message in Japanese:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-screen-garbage.jpg&quot; alt=&quot;A bunch of tiled garbage is visible on the screen. Note that it&apos;s of multiple colours, indicating the problem may come from corrupt colour planes in graphics RAM.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Garbage in… 31kHz? That can’t be right.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-screen-sync.jpg&quot; alt=&quot;The NEC 1970NX is showing what it can interpret of the video signal from the X68000. It says it is a 640x480 signal with 31.5kHz horizontal sync, 55.5Hz vertical sync, and negative vertical and horizontal sync tips.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The message basically translates to “an error has occurred, please reset.” I half-expected to see this from corrupt (battery removed) SRAM. Pushing the reset button on the top of the case, I got the message again, except now without the corrupted tiles. Hmm.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-screen-garbage-after-reset.jpg&quot; alt=&quot;The screen garbage is gone, just leaving the message, wildly off-centred&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I didn’t think the video being off-centre was that relevant; this monitor can interpret seemingly everything but sometimes it shoves the video into an inappropriate corner like it’s an Amiga or something. A little horizontal and vertical position adjustment always makes things look better.&lt;/p&gt;

&lt;p&gt;I pushed the button on the front to turn the computer off so I could plug in a keyboard, and &lt;em&gt;it didn’t turn off&lt;/em&gt;. The power LED turned completely off, rather than blinking, and the screen stayed bright and happy instead of slowly fading out to blackness like a normal soft-power-off situation should. Uh-oh.&lt;/p&gt;

&lt;p&gt;After a bit more nervous poking of the reset button, I saw that sometimes the &lt;em&gt;reset&lt;/em&gt; made the screen fade out. I wasn’t sure if this was intentional behaviour, but it didn’t always come back from that. Something’s really wrong.&lt;/p&gt;

&lt;p&gt;I shut the machine off hard, installed the keyboard, and held OPT1 on startup to reset the SRAM. Nothing changed - the LED in the “Roman characters” key would flicker for a split second on reset but this wasn’t the problem. I tinkered with the machine a bit, removing and installing parts that were easy to get to (e.g. the RAM card and floppy cables) but nothing changed. Even letting it sit for 15 minutes under power to try and reform any marginal electrolytic caps didn’t help.&lt;/p&gt;

&lt;p&gt;At this point, I decided to check if the floppy drives would work at least, and stuck a brand-new double-density disk into the primary (#0) drive as sacrifice. The drive happily slurped it up (nice) and the LEDs on the front turned on to signal a disk had been inserted (awesome) but the drives refused to eject it (oh no.) All I’d get when I pushed the eject button was a blinking LED, some motor quivering inside, and no disk ejecting. Uh-oh. My guess is that since the eject process was &lt;em&gt;doing&lt;/em&gt; something, it probably wasn’t software or the IO controller blocking it, but some kind of mechanical problem inside the drive - old grease or whatever. It can wait.&lt;/p&gt;

&lt;p&gt;So, at least four new problems:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Soft shut down doesn’t work;&lt;/li&gt;
  &lt;li&gt;Whatever that error message is;&lt;/li&gt;
  &lt;li&gt;Whatever that video garbage is&lt;/li&gt;
  &lt;li&gt;Floppy drive #0 won’t eject;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Looking on the bright side, at least there’s a visible raster and it’s loading Japanese characters from the ROM! A &lt;em&gt;lot&lt;/em&gt; of stuff has to go right to get this far.&lt;/p&gt;

&lt;p&gt;It was probably time for some actual troubleshooting. Because I had just been monkeying in that area, I immediately thought I must have missed a broken trace or some other damage on the I/O board, probably also involving battery damage on the circuits between the “T” connector and the IOSC chip. At least it’s really easy to remove the I/O board, as opposed to almost any other component of this machine.&lt;/p&gt;

&lt;h2 id=&quot;fruitless-fixing&quot;&gt;Fruitless fixing&lt;/h2&gt;
&lt;p&gt;I’ll save you the several weeks of frustration that followed. I tried a little bit of everything, with no real change in behaviour. One thing to watch out for are the aforementioned saggy RF shields: at one point I created a +5V to GND short using the motherboard’s RF shield as a jumper.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ace-ioboard-repair-1.jpg&quot; alt=&quot;ACE IOBoard repair part 1: a map of traces is shown for the top side of the board&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ace-ioboard-repair-2.jpg&quot; alt=&quot;ACE IOBoard repair part 2: a map of traces is shown for the bottom side of the board&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I did a bit more rework on the I/O board and followed some Japanese trace maps I was able to find (thank you &lt;a href=&quot;https://web.archive.org/web/20220720055438/https://twitter.com/xymiem/status/1494911987708723204&quot;&gt;Exim&lt;/a&gt;; reproduced above) and figured out that the “T” connector seemed continuous with where it was supposed to go. Doing a continuity test on such a high-density connector takes a really long time.&lt;/p&gt;

&lt;p&gt;At this point, I was starting to damage traces and pads while reworking things that turned out to be fine, which was extremely discouraging, and I shelved the machine. Thinking on it, I began to suspect the I/O board was compromised in some way I couldn’t tell.&lt;/p&gt;

&lt;p&gt;Luckily, another X68000 ACE – a black one, with a nice case – popped up on Yahoo! Auctions. I could just slam the I/O board from that in, and see if it worked! I was able to score it for more money than I paid for the grey ACE, but it was in &lt;em&gt;even worse shape&lt;/em&gt; than the thing I had just beat my head against for months.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/parts-ace-mile-high-capacitor.jpg&quot; alt=&quot;Someone has tried to replace the electrolytic capacitor at C49, and ended up just soldering it to the existing capacitor legs. &quot; /&gt;&lt;/p&gt;

&lt;p&gt;You see, someone has already tried to repair this one. They’ve done a clumsy shotgun recap, and even soldered in a &lt;em&gt;used barrel battery&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/parts-ace-battery-glob.jpg&quot; alt=&quot;The battery, with an arrow pointing to the super-globby negative terminal.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So that’s another project. Just what I needed.&lt;/p&gt;

&lt;p&gt;Desperate, I decided that I would depopulate one of the I/O boards, build a reproduction, and swap all the parts across, to rule out any further bad traces and pads and to construct a useful schematic for the ACE. Another good project would be to make a test ROM, which could go in those aforementioned sockets, and do low-level things like check if the TVRAM is okay and if the interrupt table was full of garbage.&lt;/p&gt;

&lt;p&gt;However, that’s a BIG PROJECT, and… uh… I didn’t do it. Maybe if I win the lottery, or if &lt;a href=&quot;https://www.patreon.com/leadedsolder&quot;&gt;the Leaded Solder Patreon&lt;/a&gt; replaces my day job.&lt;/p&gt;

&lt;h2 id=&quot;inconclusion&quot;&gt;Inconclusion&lt;/h2&gt;
&lt;p&gt;Thanks for reading so far! My hope from publishing this article is that someone will come to my rescue and recognize the fault.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/x68000-ace-kak-hoofd-garbage.jpg&quot; alt=&quot;The same error message, with different garbage.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Already, from showing off drafts of this project, it seems like blog friend kak-hoofd has a very similar failure (pictured above.) Although testing out the repair is in early days, so far the cause seems to have been a mechanical failure of a via up near the Messiah chip on the motherboard that only popped up intermittently, so I look forward to a lot of frustrating trace checking and via plugging.&lt;/p&gt;

&lt;p&gt;Although this machine has defeated me many times over the last couple of years, I think it is still possible to save. As you can see, it’s come a &lt;em&gt;long&lt;/em&gt; way just to give me that error message, and it surely can’t be that much farther to save this grey ACE.&lt;/p&gt;

&lt;p&gt;If you have a broken ACE, I hope this at least made you feel better about it. If it helped in any way, let me know!&lt;/p&gt;

&lt;h2 id=&quot;repair-summary&quot;&gt;Repair Summary&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Fault&lt;/th&gt;
      &lt;th&gt;Remedy&lt;/th&gt;
      &lt;th&gt;Caveats&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Power supply corroded.&lt;/td&gt;
      &lt;td&gt;Replace power supply.&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Some idiot stripped the hole for the speaker screws.&lt;/td&gt;
      &lt;td&gt;“Repair” it with super glue.&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Extensive battery corrosion has broken traces.&lt;/td&gt;
      &lt;td&gt;Conduct trace fixes and via stitching on I/O board.&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Startup error displayed with corrupt text tiles.&lt;/td&gt;
      &lt;td&gt;No fix at this time.&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:exbios&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I’ve been running EXBIOS on my X68000 PRO for quite some time, with the intent of adding a hard drive, but I haven’t actually finished the power-supply wiring to support that hard drive yet. Be patient! &lt;a href=&quot;#fnref:exbios&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="sharp" /><category term="x68000" /><category term="x68000-ace" /><category term="corrosion" /><category term="capacitors" /><category term="repair" /><category term="homemade-hardware" /><category term="trace-repair" /><category term="failure" /><summary type="html">When we were first introduced to the X68000 ACE, we saw that the board was very corroded from battery damage. Later on, I whacked the battery out of it and did my best to clean up the corrosion without completely dismantling the machine. This time, it’s personal. Workstation, that is.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/x68000-posing.jpg" /></entry><entry><title type="html">Embedding a bitmap font in your HyperCard stack</title><link href="https://www.leadedsolder.com/2026/09/08/hypercard-custom-fonts-in-resources.html" rel="alternate" type="text/html" title="Embedding a bitmap font in your HyperCard stack" /><published>2026-09-08T00:00:00+00:00</published><updated>2026-09-08T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/09/08/hypercard-custom-fonts-in-resources</id><content type="html" xml:base="https://www.leadedsolder.com/2026/09/08/hypercard-custom-fonts-in-resources.html">&lt;p&gt;I’ve loved HyperCard for a really long time now. As a kid, I made simple games in it. As what could arguably be called “an adult,” I now want to play with HyperCard itself to see what weird stuff I can make it do. Today, I’ll make it display text… in a &lt;em&gt;different font&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;HyperCard was developed for the original Macintosh as a way for anyone to quickly build interactive programs. It does this through a combination of “graphics-first” visual authoring and a very natural-like scripting language (which you very rarely have to actually use.) When I was in university, they still had a Mac Plus kicking around just to be able to use one prof’s astronomy calculator that had been banged together in HyperCard many moons ago.&lt;/p&gt;

&lt;p&gt;After its initial release, HyperCard caught on not only as a way to make nice front-ends, one-off scientific utilities, and database programs, but also as a way to build games. Adventure games were best suited to it. &lt;em&gt;Myst&lt;/em&gt; was famously developed in HyperCard, and so was &lt;a href=&quot;https://archive.org/details/hypercard_caper-in-the-castro&quot;&gt;&lt;em&gt;Caper in the Castro&lt;/em&gt;&lt;/a&gt;, which is often held up as one of the earliest queer games.&lt;/p&gt;

&lt;h2 id=&quot;an-intriguing-font&quot;&gt;An intriguing font&lt;/h2&gt;
&lt;p&gt;While reading up on some old Mac stuff, I came across a reference that &lt;a href=&quot;https://bbellina.blogspot.com/2026/02/tips-for-converting-hypercard-stacks-to.html&quot;&gt;the Concentration 2.2 stack for HyperCard by Sioux Lacy embedded a special bitmap font&lt;/a&gt;. That stack used its custom font for the purpose of displaying the card suit and value of the cards you are asked to match. For instance, you can see the 6 of diamonds and the 6 of hearts being matched in the image below.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hypercard-concentration-3.0.png&quot; alt=&quot;Concentration v3.0 is being run on the emulator. An image is being slowly exposed. Two 6s, one of hearts and another of diamonds, have been matched, and the game says &amp;quot;hooray, it&apos;s a match.&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Like many other things on the Mac, that font was no doubt embedded in the stack through a resource.&lt;/p&gt;

&lt;h2 id=&quot;the-resource-system&quot;&gt;The Resource System&lt;/h2&gt;
&lt;p&gt;Let’s talk a bit about exactly what a resource is in Mac-land. As you probably intuitively know, even if you haven’t written any computer software before, there’s more than just code that goes into them. Even your most boring business application is going to need to define stuff like icons, pictures, big blocks of text, sounds, colour palettes, and custom fonts.&lt;/p&gt;

&lt;p&gt;Historically, a lot of this stuff is just jammed into the program code in the form of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#defines&lt;/code&gt; or a similar structure. You can see this in my BBC Bridge Companion &lt;em&gt;Tetris&lt;/em&gt; clone, for instance, where I set up the graphics for all the little tiles in a block of code near the top.&lt;/p&gt;

&lt;p&gt;There’s a few big problems with this model. For one thing, you need to rebuild the entire program (or hack it up in a hex editor) to do something common, like translating English help text to Japanese&lt;sup id=&quot;fnref:translation&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:translation&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, or changing an icon. The bigger problem, at least for Apple in 1984ish, was that these things use up a lot of memory, and it would be nice to not need to have them in memory all the time.&lt;/p&gt;

&lt;p&gt;On the early versions of the Mac system software, &lt;a href=&quot;https://www.folklore.org/The_Grand_Unified_Model.html&quot;&gt;the Resource Manager&lt;/a&gt; is the chunk of the Macintosh Toolkit API that you use to ask for resources. If you’re not actively using all of the resources at a given time, the system software is allowed to recycle the memory used for them, which is important when you ship a 68000-based computer with only 128kB of RAM.&lt;/p&gt;

&lt;p&gt;To implement resources, Apple engineering split executables (and other kinds of files) into two parts: the &lt;em&gt;data fork&lt;/em&gt; and the &lt;em&gt;resource fork&lt;/em&gt;. You’ve probably heard of these before, when people are upset about why their downloaded software won’t work on a classic Mac. Although the boundaries between these two would soon blur with the introduction of code fragments, for an executable application, the data fork would contain all the code, and the resource fork would contain all the resources.&lt;/p&gt;

&lt;p&gt;A lot of old-time Mac owners probably have fond memories of opening up games and other programs in ResEdit, Apple’s official resource editor, and changing around menus and icons and whatnot.&lt;/p&gt;

&lt;p&gt;Here’s ResEdit viewing the infamous help picture from &lt;em&gt;Glider&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/resedit-contents-of-glider-iii.png&quot; alt=&quot;The Glider III help dialogue. It tells you that paper is extra gliders, a clock is for points, and there are bad things: an electrical outlet, a paper helicopter, water drops, and a &amp;quot;bad cat.&amp;quot; It warns to be careful around candles.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This model of separating the code from the “data” has been carried on to other operating systems as well, though Windows in particular implements it in a very different way.&lt;/p&gt;

&lt;p&gt;For our purposes, we’re going to concentrate only on the resources held inside a HyperCard stack. A “stack” in HyperCard is a HyperCard program. Keeping with the rapidly-aging “index card” metaphor of HyperCard, it represents a stack of cards (or screens) and contains all the code and graphics for a HyperCard program. It also has a resource fork, and contains resources!&lt;/p&gt;

&lt;p&gt;As a kid, this never occurred to me. HyperCard always felt like a walled garden that was unique compared to “real” Macintosh programs, so I just never tried to use a real tool like ResEdit on a stack. Turns out it’s incredibly consistent with the rest of 16-bit Appledom.&lt;/p&gt;

&lt;h2 id=&quot;be-resourceful&quot;&gt;Be Resourceful&lt;/h2&gt;
&lt;p&gt;On my emulated Basilisk II install, I fired up HyperCard and made a new stack called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;. In order to make sure my stack had a resource fork, I created an icon using the Icon Editor:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hypercard-icon-editor.png&quot; alt=&quot;I have drawn a terrible, lopsided spiral icon.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;My next step was to close HyperCard. Because HyperCard automatically saves, I have to be careful about trying to mess with a stack without its knowledge. Then, I opened the stack in ResEdit, and saw my newly-created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICON&lt;/code&gt; resource sitting there in the list, with the very same ID:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hypercard-icon-in-resedit.png&quot; alt=&quot;The very same icon is displayed in ResEdit as an ICON resource.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICON&lt;/code&gt; is just one of many types of icon resources available on the Mac. With the advent of System 7’s colour icons, many more types were added, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cicn&lt;/code&gt; (colour icon,) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;icl8&lt;/code&gt;, and so on. HyperCard is monochrome by nature, however, so without &lt;a href=&quot;https://archive.org/details/hypercard_plotcicn-xcmd-v10&quot;&gt;a custom XCMD such as plotcicn&lt;/a&gt;&lt;sup id=&quot;fnref:xcmd&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:xcmd&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, we’ll stick with the old-school black and white &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICON&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we know that HyperCard stacks do indeed use resources, let’s take a look at what a font contains.&lt;/p&gt;

&lt;h2 id=&quot;hello-jeff-nichols&quot;&gt;Hello Jeff-Nichols&lt;/h2&gt;
&lt;p&gt;From my vast collection of shareware CDs, I pulled out one of my favourites: &lt;a href=&quot;https://archive.org/details/Power_Tools_for_Macintosh_SoftBit_1992&quot;&gt;Power Tools for Macintosh 2.0&lt;/a&gt;. Published by SoftBit in 1992, this thing is basically a time capsule of random stuff they pulled off of an FTP server somewhere. Included in its sparsely-attributed collection are some fonts. I picked &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;J&lt;/code&gt; at random, and found a font named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Jeff-Nichols 72&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Previewing it in the built-in System 7 font viewer, it looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/jeff-nichols-72-font-preview.png&quot; alt=&quot;The font is previewed. Only the uppercase &amp;quot;H&amp;quot; from &amp;quot;How razorback...&amp;quot; is legible.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ah. An uppercase-only font, and a relatively ornate bitmap one at that&lt;sup id=&quot;fnref:the-full-text&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:the-full-text&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. That will be fine for this experiment.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/resedit-contents-of-jeff-nichols-font.png&quot; alt=&quot;The font is opened in ResEdit, showing an alis resource, a FOND resource, and an nfnt resource.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I opened the font in ResEdit. It contains three resources: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alis&lt;/code&gt;, which I don’t understand and don’t need, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FOND&lt;/code&gt;, which contains font metadata, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nfnt&lt;/code&gt;, which contains the actual characters of the font. After copying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FOND&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nfnt&lt;/code&gt; resources to my stack, I saved everything and quit ResEdit.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/resedit-contents-of-jeff-nichols-font-pasted-into-our-stack.png&quot; alt=&quot;ResEdit now shows that the test stack contains FOND and nfnt resources.&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;using-this-new-font&quot;&gt;Using This New Font&lt;/h2&gt;
&lt;p&gt;This took a little bit of work. I wanted to create a &lt;em&gt;field&lt;/em&gt;, which is akin to an InputBox in Windows land – it’s a box that the user types into. Although HyperCard has a nice UI for letting you pick which font that field uses, it didn’t pick up the “Jeff-Nichols” font. It showed me only the fonts that were installed in my operating system’s global fonts folder.&lt;/p&gt;

&lt;p&gt;HyperCard has a nice feature called the Message Box, which is accessible on the command-M shortcut. When you pick it, a little command prompt appears at the bottom of the screen, which allows you to enter HyperTalk commands. Those commands are dispatched as &lt;em&gt;messages&lt;/em&gt; to the HyperCard runtime, hence the name.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/HyperTalk&quot;&gt;HyperTalk&lt;/a&gt; is HyperCard’s built-in scripting language, and it combines natural-seeming language with the complete inability to understand actual natural language. I could make dozens of blog entries about HyperTalk alone. After doing some backflips, I finally landed on this magic command:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set textFont of card field 1 to &quot;Jeff-Nichols&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This revealed that I still had the font size set to 12, which made the ornate 72-size font indecipherably tiny. To fix this, I had to go back into the HyperCard font selector (which had now picked up on the concept of the Jeff-Nichols font) and tell it that the text in the field should be size 72. There’s probably a way to do that in HyperTalk as well, but I wanted to take the easy way out.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/hypercard-using-jeff-nichols-font.png&quot; alt=&quot;The new font displays &amp;quot;TEXT&amp;quot; in an insanely ornate fashion. Beneath it is the message box, still containing our HyperTalk command.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After that, everything worked.&lt;/p&gt;

&lt;p&gt;You’ll also notice me using the icon I previously made on a transparent button on this card. Why? I dunno.&lt;/p&gt;

&lt;p&gt;My assumption on how this works is that the Mac Toolbox, when asked to find this font, has a hierarchy of places to search for it, and “available resources” is of course one of those places. I’ve never really done much with fonts before, but now I’m thinking it would be a worthwhile thing to at least have a basic understanding of.&lt;/p&gt;

&lt;h2 id=&quot;why-do-this&quot;&gt;Why do this?&lt;/h2&gt;
&lt;p&gt;Why would you go to all this work to embed a custom font? Why not just install that font on the programmer’s computer, and use it to make a bitmap image on the card?&lt;/p&gt;

&lt;p&gt;The obvious reason would be that you want to dynamically generate something. For instance, if I were making a HyperCard stack that generates pretty greeting cards, maybe I want to apply a foofy font to the text output in order to spell out your recipient’s name. You know, a classy card, as opposed to the ones your uncle sends. HyperTalk has a ton of commands available for programmatically drawing things: in fact, all of the extant drawing tools are just sending messages to HyperCard as it is.&lt;/p&gt;

&lt;p&gt;Fonts also have abilities that icons don’t. Icons in HyperCard are limited to 32x32 in size, whereas you just saw that we can quickly draw an enormous, fancy-looking character wherever we want on screen with a custom font.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/japanEASE-cat-poking.png&quot; alt=&quot;I am poking a cat in JapanEASE, a HyperCard-based Japanese language learning program. A disembodied voice tells me that what I think is a cat is actually called a &apos;neko.&apos;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Although this discovery might seem of limited use, it’s important not to lose sight of the fact that a font is just one kind of resource.&lt;/p&gt;

&lt;p&gt;There’s lots of other stuff that could be embedded into a stack, such as custom menus, big graphics, and sounds, such as the voiceover in JapanEASE (pictured.) I wouldn’t be surprised to find out that you can retrieve string resources from a HyperTalk script, which opens up the potential of localized HyperCard stacks.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There’s a lot more to touch on with HyperCard in general, and resources in particular. Bill Atkinson (RIP) and all the other hard-working Apple engineers like Danny Goodman made a really special toolset for quickly developing software. A lot of folks, myself included, can credit their start in computer programming to it. Even though it seems everyone at the time already knew about it, it’s fun to discover it still has lots of tricks up its sleeve after all these years.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:translation&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Translation is a really annoying problem to solve in older machines. For instance, on PC-88 games, every game stores its strings differently. And making a string longer or shorter (which often happens when you do something like translate “my dog” to “私の犬”) will often mean that you need to pad it, or find space somewhere else in the binary to put the rest of the string, then modify the code that calls that string. For localization alone, a common and easy-to-use resource model makes things much easier. &lt;a href=&quot;#fnref:translation&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:xcmd&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;An XCMD is an “extension command” for HyperTalk (an XFCN is an “extension function;” yes, like Visual Basic, HyperCard makes a distinction between calls that return a value and those that do not.) They’re usually implemented in a native language such as 68K assembly language, C or Pascal, and give HyperCard additional powers past what it was designed to have. For instance, you can add colour using the Color Tools XCMD. XCMDs are embedded into the stack as – what else? – resources. Later on in HyperCard’s lifecycle, Tom Pittman’s &lt;a href=&quot;https://hypercard.org/CompileIt_Manual.pdf&quot;&gt;CompileIt!&lt;/a&gt; was developed, which compiles XCMDs defined in HyperTalk into native code. If anyone’s interested, I may do a followup article on that. &lt;a href=&quot;#fnref:xcmd&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:the-full-text&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The full preview text used by System 7, in case you’re wondering, is “How razorback-jumping frogs can level six piqued gymnasts!” &lt;a href=&quot;#fnref:the-full-text&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="computer" /><category term="apple" /><category term="hypercard" /><category term="homemade-software" /><summary type="html">I’ve loved HyperCard for a really long time now. As a kid, I made simple games in it. As what could arguably be called “an adult,” I now want to play with HyperCard itself to see what weird stuff I can make it do. Today, I’ll make it display text… in a different font.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/jeff-nichols-72-font-preview.png" /></entry><entry><title type="html">Bridge Companion Becomes Block Companion</title><link href="https://www.leadedsolder.com/2026/08/18/bbc-bridge-companion-tetris-clone-programming.html" rel="alternate" type="text/html" title="Bridge Companion Becomes Block Companion" /><published>2026-08-18T00:00:00+00:00</published><updated>2026-08-18T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/08/18/bbc-bridge-companion-tetris-clone-programming</id><content type="html" xml:base="https://www.leadedsolder.com/2026/08/18/bbc-bridge-companion-tetris-clone-programming.html">&lt;p&gt;Soon, you will know all the secrets of Z80 game programming. Or at least all the ones I didn’t know before I decided to port &lt;em&gt;Tetris&lt;/em&gt; to a game console made to teach pensioners how to play cards. Things only get weirder from here, if you can believe that.&lt;/p&gt;

&lt;h2 id=&quot;lets-all-become-development-environmentally-conscious&quot;&gt;Let’s All Become Development-Environmentally Conscious&lt;/h2&gt;
&lt;p&gt;Since I don’t live in the UK, I’m not swimming in surplus Bridge Companions. There were a few eBay UK sellers willing to ship if asked, but only for ridiculous sums like £50.&lt;/p&gt;

&lt;p&gt;When I first considered this stupid project, it seemed unlikely that I’d be able to get one of these little miracles to my door for less than $175 CAD all-in. I’d hate to spend that much money and then have the console sit while I developed the software, not to mention modifying it for NTSC composite, finding an appropriate 120V power supply, and hacking up the cartridge to use as a test module. Much better to develop the software first, and then trick someone I know over there to sending me a parcel without the eBay margins.&lt;/p&gt;

&lt;p&gt;Out of prudence for my pocketbook, I decided to do my first experiments in an emulator. If I couldn’t make software work there, there was no point in involving the messy fun of real hardware.&lt;/p&gt;

&lt;p&gt;That emulator is MAME, of course, which is usually my first stop for emulating &lt;em&gt;any&lt;/em&gt; oddball system. The driver is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bbcbc&lt;/code&gt;, and by reading over &lt;a href=&quot;https://github.com/mamedev/mame/blob/master/src/mame/drivers/bbcbc.cpp&quot;&gt;the source code for the BBC Bridge Companion driver&lt;/a&gt;, we can learn a lot about the console. Thanks for going first, MAME authors!&lt;/p&gt;

&lt;p&gt;First off, the memory map and Z80 I/O map:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Memory region&lt;/th&gt;
      &lt;th&gt;What is it?&lt;/th&gt;
      &lt;th&gt;Comments&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;span class=&quot;hex-value&quot;&gt;$0000 &lt;/span&gt;–&lt;span class=&quot;hex-value&quot;&gt;$3fff &lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;BIOS ROM&lt;/td&gt;
      &lt;td&gt;Sometimes two 8kB ROM ICs at &lt;span class=&quot;hex-value&quot;&gt;$0000 &lt;/span&gt;, &lt;span class=&quot;hex-value&quot;&gt;$2000 &lt;/span&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;span class=&quot;hex-value&quot;&gt;$4000 &lt;/span&gt;–&lt;span class=&quot;hex-value&quot;&gt;$bfff &lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;Cartridge slot&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;span class=&quot;hex-value&quot;&gt;$e000 &lt;/span&gt;–&lt;span class=&quot;hex-value&quot;&gt;$e7ff &lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;RAM&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;I/O port region&lt;/th&gt;
      &lt;th&gt;What is it?&lt;/th&gt;
      &lt;th&gt;Comments&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;span class=&quot;hex-value&quot;&gt;$00&lt;/span&gt;–&lt;span class=&quot;hex-value&quot;&gt;$7f&lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;Z80 PIO&lt;/td&gt;
      &lt;td&gt;Controller inputs, primarily. MAME source says only &lt;span class=&quot;hex-value&quot;&gt;$00&lt;/span&gt;, &lt;span class=&quot;hex-value&quot;&gt;$20&lt;/span&gt;, &lt;span class=&quot;hex-value&quot;&gt;$40&lt;/span&gt;, and &lt;span class=&quot;hex-value&quot;&gt;$60&lt;/span&gt; are actually used.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;span class=&quot;hex-value&quot;&gt;$80&lt;/span&gt;–&lt;span class=&quot;hex-value&quot;&gt;$81&lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;TMS9129&lt;/td&gt;
      &lt;td&gt;VDP registers&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;With this information, I figured I could probably set up a decent Z80 assembly setup and start writing a Hello World for the emulated Companion. I didn’t understand &lt;a href=&quot;https://electronics.stackexchange.com/questions/422051/what-exactly-does-the-z80-pio-do&quot;&gt;the PIO&lt;/a&gt; well enough to use it, but I assumed that the BIOS would have wrapped some functions for that that could be determined later by reverse-engineering the legitimate cartridges.&lt;/p&gt;

&lt;p&gt;First, I decided I would set up an emulator. After borrowing the BIOS ROM and a couple cartridge images from a certain internet library, I took an off-the-shelf build of MAME and ran the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bbcbc&lt;/code&gt; driver with it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-no-cartridge-startup.png&quot; alt=&quot;The BBC Bridge Companion startup screen. It says &amp;quot;No. Please turn off companion then plug in cartridge.&amp;quot; You&apos;re gonna see a lot of this screen in this article, so get used to it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oops! I guess I need a cartridge. This is somewhat more blunt than the ColecoVision screen for the same error was:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/leako-working-bios.jpg&quot; alt=&quot;The ColecoVision BIOS screen. It says &amp;quot;Turn game off before inserting cartridge or expansion module, © 1982 Coleco.&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;What to play…? How about &lt;em&gt;Bridge Builder&lt;/em&gt;? There, that’s much better.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-bridge-builder.png&quot; alt=&quot;The main menu of Bridge Builder. The title says WELCOME TO BRIDGE BUILDER. Menu options include INTRODUCTION, BIDDING YOUR HAND, PLAYING YOUR CARDS, DEFENSIVE PLAY, and BIDDING PRACTICE.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Great, the emulator is working. I decided I would build my first program for the Bridge Companion, but despite the bravado spouted earlier in this post, I didn’t actually know how to program a Z80 with a TMS VDP. Sure, I’d experimented with Z80 assembly in high school and college, we all did, but that was just a phase.&lt;/p&gt;

&lt;p&gt;First, I checked if it was supported by z88dk (which &lt;a href=&quot;https://z88dk.org/site/targets&quot;&gt;already supports virtually everything under the sun&lt;/a&gt;,) but unfortunately it was not.&lt;/p&gt;

&lt;p&gt;Would it be easier to port z88dk than to just write my game in assembly? In order to know how to port z88dk to something, I assumed I’d first have to learn how to write assembly for it, so I set about re-teaching myself Z80 in general and the Bridge in particular.&lt;/p&gt;

&lt;p&gt;I had some big questions:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;How do I set up a cartridge that the console would identify as legitimate?&lt;/li&gt;
  &lt;li&gt;Where do I put my code?&lt;/li&gt;
  &lt;li&gt;Does the Companion’s BIOS offer any utility code to call that would make my life much easier? The ColecoVision had &lt;a href=&quot;http://www.colecovisionzone.com/page/coleco%20industries/programmers.html&quot;&gt;tons of BIOS subroutines&lt;/a&gt;, including controller scanning and writing buffers to VRAM.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To get started, I set up the two BIOS ROMs and the cartridge ROM in Ghidra at their appropriate offsets in the memory map, using the “Add to Program” option.&lt;/p&gt;

&lt;h2 id=&quot;the-reversible-engineer&quot;&gt;The Reversible Engineer&lt;/h2&gt;
&lt;p&gt;The cartridge ROM was looking very weird in Ghidra, which refused to disassemble most of the file. After poking around a bit, I wondered if the cartridge even contained Z80 code, as opposed to some sort of high-level scripting logic like TI GPL. It is a pretty specialized machine, after all, and the BIOS ROM is relatively huge at 16kB.&lt;/p&gt;

&lt;p&gt;Maybe the best way to figure this out is to just run it and see what happens. I ran the &lt;em&gt;Advanced Defence&lt;/em&gt; cartridge in MAME, with the debugger attached, using the following arguments:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% ./mame64 &lt;span class=&quot;nt&quot;&gt;-debug&lt;/span&gt; bbcbc &lt;span class=&quot;nt&quot;&gt;-cart&lt;/span&gt; advdefnc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then I set the MAME debugger to pause execution whenever memory was accessed in the cartridge space and started the game with the following commands&lt;sup id=&quot;fnref:debugscript&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:debugscript&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wpset 4000,4000,rw
g
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used “watchpoints” (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wp&lt;/code&gt;), which are meant to detect access to arbitrary memory, instead of “breakpoints” (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bp&lt;/code&gt;), which are meant to detect the program counter hitting a specific address. This is because, in MAME, breakpoints have to be set at a specific address as opposed to a range. In effect, this meant that I had to press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g&lt;/code&gt; a whole bunch to skip past all the accesses to the cartridge &lt;em&gt;before&lt;/em&gt; the program counter jumped into it.&lt;/p&gt;

&lt;p&gt;I eventually found out that the program counter first landed in the &lt;span class=&quot;hex-value&quot;&gt;$4000 &lt;/span&gt; to &lt;span class=&quot;hex-value&quot;&gt;$7fff &lt;/span&gt; range at the address &lt;span class=&quot;hex-value&quot;&gt;$401f &lt;/span&gt;. Popping back to Ghidra and starting to disassemble from that address, I now saw something that indeed looked like sane Z80 code. I confirmed this address with another cartridge (&lt;em&gt;Advanced Bidding&lt;/em&gt;). It would appear we’ve got an entry point!&lt;/p&gt;

&lt;p&gt;Since it didn’t seem that this entry point was hardcoded, I figured it would be worth understanding how it was arrived at by the BIOS. Probably the cartridge ROM has it somewhere… and indeed the bytes &lt;span class=&quot;hex-value&quot;&gt;$1f &lt;/span&gt; and &lt;span class=&quot;hex-value&quot;&gt;$40 &lt;/span&gt; were written at &lt;span class=&quot;hex-value&quot;&gt;$401e &lt;/span&gt; and &lt;span class=&quot;hex-value&quot;&gt;$401d &lt;/span&gt;, which are the first two cartridge addresses looked at by the BIOS. Thanks, MAME debugger!&lt;/p&gt;

&lt;p&gt;I chopped the first &lt;span class=&quot;hex-value&quot;&gt;$1e &lt;/span&gt; bytes off the &lt;em&gt;Advanced Defence&lt;/em&gt; ROM and shoved them into a file and built them with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z80asm&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;; header - tell bios where we live
; copied from advanced defence, who knows what all this does?
dm 0x8b, 0xe6, 0x52, 0x1d, 0x40, 0x00, 0x00, 0x4e, 0x40, 0x26, 0x45, 0x00, 0x00, 0x1c, 0xe3, 0x00
dm 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0x00, 0x00, 0xc9, 0x00, 0x00, 0x1f, 0x40

; main loop - at 0x401f
org 0x401f
run:
ld a, 0x10
inc a
jp run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And looking at it in the MAME debugger, I was able to hit the breakpoint at 0x401f and watch the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; register get set to &lt;span class=&quot;hex-value&quot;&gt;$10 &lt;/span&gt;, increment to &lt;span class=&quot;hex-value&quot;&gt;$11 &lt;/span&gt;, and start all over again at the top of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt; loop. &lt;em&gt;Fantastic&lt;/em&gt;! I was now running my code on an (emulated) BBC Bridge Companion.&lt;/p&gt;

&lt;p&gt;I had a lot of wrestling with the assembler I chose initially, z80asm. It seemed like its macro support had broken, or was not working properly, and there was no option to relocate code inside the produced binary. Even so, it just meant that the code got hairier and uglier than it really had to be. I asked around and then switched to &lt;a href=&quot;https://k1.spdns.de/Develop/Projects/zasm/Documentation/&quot;&gt;zasm&lt;/a&gt;, which is &lt;em&gt;much&lt;/em&gt; more pleasant, albeit still with its share of confusing error messages.&lt;/p&gt;

&lt;h2 id=&quot;first-tile&quot;&gt;First Tile!&lt;/h2&gt;
&lt;p&gt;After a couple of aggravating evenings, I pulled together &lt;a href=&quot;https://www.chibiakumas.com/z80/platform.php#LessonP5&quot;&gt;some example TMS99xx initialization code from an MSX-oriented tutorial&lt;/a&gt; with &lt;a href=&quot;https://abagames.github.io/z80-repl/index.html&quot;&gt;a super-helpful Z80 REPL&lt;/a&gt; and &lt;a href=&quot;http://jgmalcolm.com/z80/beginner/inst&quot;&gt;a very solid instruction reference&lt;/a&gt;. A few hours later, I had a blank screen with a single colour-shifting tile on it, and now I was off to the races.&lt;/p&gt;

&lt;p&gt;Getting my (probably too fancy) initialization code to work just took a lot of debugging of stupid mistakes on my part, including but not limited to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dec hl&lt;/code&gt; doesn’t set any flags, especially not the zero flag; you have to dismantle the 16-bit register into two 8-bit registers and compare them in a loop counter&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp a, (hl)&lt;/code&gt; is not the same thing as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a = *hl&lt;/code&gt;; I meant to say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ld&lt;/code&gt; but presumably was instead thinking about copying files in unix&lt;/li&gt;
  &lt;li&gt;In whatever the default mode I had it in was, zasm apparently won’t assemble any instruction that isn’t indented, so outdenting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ret&lt;/code&gt; on one critical call meant that the program just shot off the end of the ROM and into outer space&lt;/li&gt;
  &lt;li&gt;A 32x24 screen means that the formula for targeting a coordinate in the VDP’s tile buffer is &lt;em&gt;not&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TILES_BASE + (y * 24 + x)&lt;/code&gt; but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TILES_BASE + (y * 32 + x)&lt;/code&gt;. I swear I used to know how to program.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s not much I can say to defend myself. Don’t write code at 3am?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-mame-tile-set.png&quot; alt=&quot;A purple screen with a green fuzzy grey tile in the top left corner. It&apos;s working!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Look!! Something is actually &lt;em&gt;on the screen&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;tiling-my-shower&quot;&gt;Tiling My Shower&lt;/h2&gt;
&lt;p&gt;Now to make up some test tiles. At first, I wanted to use the font out of the BIOS ROM, but it’s in a strange, non-contiguous order for some reason.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-bios-font.png&quot; alt=&quot;The font in the second BIOS ROM inside Tile Molester.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Tile Molester (ugh) is starting to show its age in a lot of ways, so I ended up writing a cheap-and-nasty tilemap extractor in Python instead. I wanted to do it in Nim because I had such a good result using that language’s struct sugar with the PC-6601SR disk utilities, but the available image libraries were underdocumented or just plain didn’t compile. Back to &lt;a href=&quot;https://python-pillow.org/&quot;&gt;Pillow&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Once my dumper extracted the tiles and turned them into an image, it was a lot easier to see how they are stored in the BIOS:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-bios-font-my-dumper.png&quot; alt=&quot;The output of my dumper shows that there are some odd characters in between a run of letters.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One slightly surprising aspect of the BIOS font is that there’s no character for “zero.” There are ones for “10,” “11,” and “12,” which I guess makes sense if you’re talking about cards.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-font-loaded-from-bios.png&quot; alt=&quot;The A, B, and C are loaded from the BIOS&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The order of these letters inside the ROM is very strange indeed; letters are crammed into groups of eight and symbols are seemingly jammed in between them.&lt;/p&gt;

&lt;p&gt;After thinking about it some more, though, it made sense: in the VDP mode I’m running in, you can only choose one set of colours for each set of &lt;em&gt;eight&lt;/em&gt; tile graphics. Which means that if you need both red and black numbers (or J, Q, K, A) in the same frame, you have to duplicate them into different segments of the pattern description table. The order is confusing, as you would expect all the symbols and numbers to be front-loaded and all the letters to come in a contiguous block in ROM, even if not so when stored in VRAM. Maybe it makes the pointer arithmetic to go from card face number to “tile index” a little easier somehow?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-bios-font-weird-lines.png&quot; alt=&quot;Weird lines under &amp;quot;H&amp;quot; and &amp;quot;Z&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The letter “H” at &lt;span class=&quot;hex-value&quot;&gt;$2830 &lt;/span&gt; also had a weird line underneath it, as did “Z.” It’s probable the BIOS and games do something subtle about various characters being “shifted” by a row or column in each direction, because some of the card characters are also shifted horizontally. Although reading at an eight-byte alignment seems to have worked for twenty-four of the letters, two of them just decided to be jerks. Jerks.&lt;/p&gt;

&lt;p&gt;I ended up redrawing&lt;sup id=&quot;fnref:redrawing-letters&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:redrawing-letters&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; those two letters, as truncating them to 7 rows would have meant having to tweak the copy loop to bump the VDP pointer twice (skipping over the last row.) It was a little faster (if lazy and inefficient) that way.&lt;/p&gt;

&lt;p&gt;Either way, setting up a quick pointer table and loading those into the VDP memory as well was cake itself, and only chewed up a few bytes of my (at this point) spacious ROM.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-bios-font-weird-lines-fixed.png&quot; alt=&quot;The entire alphabet is loaded from the BIOS, including my &amp;quot;fixed&amp;quot; H and Z.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I could have spent a little more time trying to find the print routine in the BIOS, but I figured it would be easy enough to just write my own, since I’m primarily dealing with hardcoded strings (outside of the eventual score display) and I was feeling too lazy to reverse engineer the VRAM layout of the original games… even though discarding it ended up taking more time than that probably would have.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-hello-world.png&quot; alt=&quot;The emulated Bridge Companion says &amp;quot;HELLO&amp;quot; as written by my print routine.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also did a little bit of guess-and-checking between the MAME source. At first, I tried to do it the “natural” way by reading the Z80 PIO datasheets and manuals, and trying to set up interrupt handlers, but I figured I would cheat and look at how MAME does things.&lt;/p&gt;

&lt;p&gt;Taking a big assumption that this was the right way, I wrote sort of the following input read code, which set the lower nibble on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; register, depending on the button state (0 = button pushed, 1 = not pushed:)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#define BBC_JOYSTICK_REGISTER $20
#define BUTTON_SET_1 $ef ; pass, spades, clubs, rdbl
[...]
    ; write $ef to the PIO
    ld a, BUTTON_SET_1
    out (BBC_JOYSTICK_REGISTER), a
    ; read the PIO&apos;s status
    in a, (BBC_JOYSTICK_REGISTER)
    bit 0, a            ; check bit zero of the register, if that bit is 0, set the &quot;Z&quot; flag
    jp z, PassWasPushed ; jump if the &quot;Z&quot; (zero) flag is set
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Probably not the most efficient method (especially if I wanted to check all 12 buttons in a loop,) but it seemed to be working so far. We have interactivity!&lt;/p&gt;

&lt;p&gt;After that, a bunch of utility-code-writing happened until I had a sizeable library and no further excuse for procrastination.&lt;/p&gt;

&lt;h3 id=&quot;a-game-has-rules&quot;&gt;A Game Has Rules&lt;/h3&gt;
&lt;p&gt;Now that I had a good base set of code, a running console with a working cartridge slot, a working cartridge PCB to take my ROMs, &lt;em&gt;and&lt;/em&gt; a composite mod so I could play the game on my bench TV, all that I had to do now was write &lt;em&gt;Tetris&lt;/em&gt;. No problem, right?&lt;/p&gt;

&lt;p&gt;I figured that I would keep the game’s feature list short, and not get too crazy. Although I’d written &lt;em&gt;Tetris&lt;/em&gt; clones before, they were in much higher-level languages such as C and Rust, so I had to think about how to program common structures in Z80.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://fightingcomputers.nl/Projects/Commodore-64/Tetris-in-6502/Tetris-in-6502---Part-5&quot;&gt;Wiebo de Wit’s blog series on writing 6502 &lt;em&gt;Tetris&lt;/em&gt;&lt;/a&gt; was very helpful. Reading it allowed me to see how someone more experienced tackled the major problems like “rotating pieces,” scoring, and collision detection.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-moving-piece.png&quot; alt=&quot;The Tetris piece can move left and right now.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I was also trying not to get mired in “analysis paralysis,” or worrying too much about doing things the fastest/coolest way. I admit that the final assembly program is very caveman-like, slow, and is probably at least three times as long as it should be, but it’s there. At least I probably don’t have to worry too much about &lt;a href=&quot;http://www.madrigaldesign.it/creativemu/files/tms99xx_timing_article&quot;&gt;making my code too fast and overwhelming the VDP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A lot of games only update the video memory during the vertical blank period, so as to avoid tearing. I didn’t notice much tearing, and I don’t need maximum performance, so I ended up doing things the lazy way.&lt;/p&gt;

&lt;p&gt;In practice, the “lazy way” was writing to video memory whenever I wanted to, but with some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NOP&lt;/code&gt;s after each write to make sure that the VDP had time to catch up before the next write attempt. This pattern is done on a lot of homebrew ColecoVision games, so I was fairly sure it would work here as well.&lt;/p&gt;

&lt;p&gt;Still, I would like to one day experiment with deferring all the video updates to vblank in the future. It seems like the way that TI wants you to do it.&lt;/p&gt;

&lt;p&gt;Collision detection was something I put off for weeks, but only took me about 45 minutes plus another 45 minutes of debugging to figure out. As with any programming, breaking it into smaller pieces is the way to go for Z80 assembly – there are just fewer structures provided with which to do so.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-colliding-piece.png&quot; alt=&quot;The Tetris piece is colliding with the rightmost wall, and is unable to move any further.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And rotation came shortly after, then stacking:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-rotate-and-stack.png&quot; alt=&quot;Some Tetris pieces are stacked onto the board in a haphazard fashion. You can see that some of them are rotated.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Piece selection is one of those things you don’t think about, but it’s actually very complex. “Real” Tetris implementations have &lt;a href=&quot;https://harddrop.com/wiki/Random_Generator&quot;&gt;a dizzying array of “grab bagging” methods&lt;/a&gt;, where they try to spread out the distribution of pieces, so that it’s more fun for the player, and less random. There are often even sub-methods inside the main method, in order to provide even more nuanced selection. I recommend reading the linked wiki page, it is very interesting.&lt;/p&gt;

&lt;p&gt;I first started with a pure random selection, just to try and get anything down on the board. Once I had all seven of the pieces going, I had a weird bug where the “S” shape would transform into the “T” shape in one of its frames. It turns out that the definition was only 15 bytes long instead of 16, due to missing a space when I was trying to define the piece in ASCII.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-piece-about-to-clear.png&quot; alt=&quot;A line is about to clear.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-triple-clear.png&quot; alt=&quot;Three lines just cleared.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Shortly after that, I got my first tetris:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-first-tetris-before.png&quot; alt=&quot;An &amp;quot;I&amp;quot; piece is dropping down a well into a perfectly-shaped hole for it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Which exposed a bug in my “shift lines down” code:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-first-tetris-after.png&quot; alt=&quot;The &amp;quot;I&amp;quot; piece has landed, four lines have cleared, and the line count has incremented by four. The second-last column has changed in colour, but didn&apos;t actually go down in height. What?&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After sleeping on it, I realized I had been using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; accumulator register to store an important value, and then calling out to a function that obliterates it. As a result, when I used that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; register a little bit later to calculate a counter, the counter value ended up being somewhat wrong.&lt;/p&gt;

&lt;p&gt;Since the accumulator is so important in an accumulator CPU like the Z80, it’s hard to write a function that &lt;em&gt;doesn’t&lt;/em&gt; overwrite it. Lesson learned, again: don’t use “A” in anything that I’m going to do a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CALL&lt;/code&gt; instruction inside.&lt;/p&gt;

&lt;p&gt;A large portion of bugs I made on this project originated in not paying attention to subfunctions stomping on my registers. I will try to use memory more often from now on, instead of being quite as ruthless of a premature optimizer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-fixed-tall-drop-before.png&quot; alt=&quot;The &amp;quot;I&amp;quot; piece is dropping down another well.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ahh, that’s better.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-fixed-tall-drop-after.png&quot; alt=&quot;The &amp;quot;I&amp;quot; piece has disappeared, and everything has shifted down properly.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Back to “grab-bagging” the pieces now. I knew it’s a fixed array of all possible piece types, shuffled. Through channelling my largely-wasted computer science education, I remembered &lt;a href=&quot;https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle&quot;&gt;the Fisher-Yates array shuffle algorithm&lt;/a&gt;, which worked out.&lt;/p&gt;

&lt;p&gt;Man, we have &lt;em&gt;algorithm names&lt;/em&gt; on this blog now? I should stitch some leather elbows on my stained car hoodie.&lt;/p&gt;

&lt;p&gt;After a bit of debugging, I got it to grab-bag successfully. And they’re right: this &lt;em&gt;is&lt;/em&gt; more fun than pure random.&lt;/p&gt;

&lt;h3 id=&quot;a-weird-problem-emerges&quot;&gt;A Weird Problem Emerges&lt;/h3&gt;
&lt;p&gt;As I started putting in the line-clearing animation, I ran into a strange phenomenon. Adding any more code past a certain point would cause the game to crash on startup, infinite-looped on the solid yellow screen of the BIOS. If I made the ROM really, really long by padding it with NOPs, the BIOS would show a weird “WHAT DO YOU BID?” screen and keyboard prompt.&lt;/p&gt;

&lt;p&gt;Through experimentation, I figured out that it seemed to be happening when the code passed the 4800-ish byte range. I made a “broken” and a “working” ROM and then tested them in the MAME debugger, by setting a watch point for any read to the cartridge space:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wpset 0x4000,2000,r&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After stepping through each one, I found that the cartridge reading was identical up until the BIOS reached out to a fixed address on the cartridge, &lt;span class=&quot;hex-value&quot;&gt;$52e6 &lt;/span&gt;. The execution differed at that point.On my bad ROM, it would read a very long array of garbage and then start executing at a strange spot inside the BIOS. On my good ROM, it would read &lt;span class=&quot;hex-value&quot;&gt;$00 &lt;/span&gt; within one or two bytes and then boot the main function in my ROM. &lt;span class=&quot;hex-value&quot;&gt;$52e6&lt;/span&gt; clearly was expected by the BIOS to contain some information.&lt;/p&gt;

&lt;p&gt;What value is &lt;span class=&quot;hex-value&quot;&gt;$52e6 &lt;/span&gt; in decimal? It’s 4,838, or about the number of bytes that my ROM was before things started to go haywire. Ah ha.&lt;/p&gt;

&lt;p&gt;I picked apart some commercially-released ROMs in the hex editor, and found that all of them – every single one – just put &lt;span class=&quot;hex-value&quot;&gt;$00 &lt;/span&gt; at this location. As a result, I’m not sure what this is for, but I’m guessing because it’s null-terminated, it is meant to be some kind of special string or perhaps even a miniature scripting language to tell the BIOS to set up some environment. Further, my guess is that because my string was “too long,” it ended up smashing the stack or causing some other malevolent state problem that confused the BIOS too much to be able to boot my game.&lt;/p&gt;

&lt;p&gt;I instructed zasm to place a &lt;span class=&quot;hex-value&quot;&gt;$00 &lt;/span&gt; value at &lt;span class=&quot;hex-value&quot;&gt;$52e6 &lt;/span&gt;, and then tested with the “bad” ROM’s source code. Now it booted reliably, even after I added a bunch of garbage to it.&lt;/p&gt;

&lt;p&gt;So why &lt;span class=&quot;hex-value&quot;&gt;$52e6 &lt;/span&gt;? It was one of the magic numbers I copied into my ROM from &lt;em&gt;Advanced Defence&lt;/em&gt;, and those values are read by the BIOS on initialization. Because I didn’t want to re-organize my program layout, I quickly changed that magic number to point to &lt;span class=&quot;hex-value&quot;&gt;$4010 &lt;/span&gt;, a part of the header that I knew would always be zero – because I &lt;em&gt;also&lt;/em&gt; copied it out of the &lt;em&gt;Advanced Defence&lt;/em&gt; ROM.&lt;/p&gt;

&lt;p&gt;Well, now we know what &lt;em&gt;that&lt;/em&gt; part of the header is: a pointer to some kind of null-terminated array for some unknown purpose. Specific, I know.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-tetris-clear-animated.gif&quot; alt=&quot;(Animated) The line-clearing animation is finally in. I score a Tetris in my debugging environment.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Adding the “Next” preview was straightforward, but laborious. Although I wanted to do things the fancy way, I ended up copy-and-pasting code and wasting a little bit of memory to make it happen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-next-indicator.png&quot; alt=&quot;The indicator for the &amp;quot;Next Piece&amp;quot; is shown in the lower right.&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;attract-mode&quot;&gt;Attract Mode&lt;/h2&gt;
&lt;p&gt;One of the biggest things I procrastinated on was the title screen. If you’ve ever done games, you know that exiting from the game into the “outer game” is a very annoying process. And I’d stacked up a bunch of ugly spaghetti code on my way to the Game Over, so returning to the title screen isn’t particularly easy.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-title-screen-v1.png&quot; alt=&quot;An early title screen. It says BRIDGETRIS and then PUSH START. The background colour has changed from the previous screenshots, as I figured out it looked way better on crappy TVs from the Soggy-1000 RAM tester&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I decided to get over this slothful hump by making the title screen pretty(ish), so I’d want to see it. Then I solved the problem by writing down where the stack pointer was when I started the game (&lt;span class=&quot;hex-value&quot;&gt;$e7fe &lt;/span&gt;) and then just forcibly resetting the stack pointer to that when I was cleaning up at the end of a game over. Who needs memory management?&lt;/p&gt;

&lt;p&gt;I think when I do my next Z80/TMS99xx game – and there will be another, but likely not for the Companion – I’ll do a much better job of organizing game states and only having one big main loop. In other words, I’ll try not to paint myself into a corner next time. Hey, if it didn’t have one or two or fifteen hack-jobs in it somewhere, it wouldn’t be a real game.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-final-screenshot.png&quot; alt=&quot;The finished sidebar, with information on how to play the game properly.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also added some (cryptic) sidebar info to explain how to play the game, because nobody is expected to look at a manual to figure it out. If I ever display this game publicly, I’ll probably put tape labels over the top of the Companion. The hand position required to play it is somewhat awkward, but believe me that it was the best I could come up with. The control panel is simply not designed for a two-handed, fast action game!&lt;/p&gt;

&lt;h2 id=&quot;it-goes-back-home&quot;&gt;It Goes Back Home&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-real-hardware-peteg.jpg&quot; alt=&quot;The BBC Bridge Tetris running on Pete Golding&apos;s TV set and Companion.&quot; /&gt;
Nearing the end of development, Pete Golding offered to test. You may remember him as the individual that started this whole mess in the first place, so it’s only fair that he helps out a little. He bravely tested the game on his BBC Bridge Companion (and UK PAL TV) and confirmed that it worked. It looks good even with the widescreen stretch and RF noise!&lt;/p&gt;

&lt;h2 id=&quot;what-about-canada&quot;&gt;What about Canada?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-on-my-tv.jpg&quot; alt=&quot;The composite-modded BBC Bridge Companion is playing &amp;quot;Bridgetris&amp;quot; on my NTSC Samsung CRT TV.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-on-my-tv-close.jpg&quot; alt=&quot;A close-up of the game being played on my TV.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Playing it on my own television, through a cheap PAL-to-NTSC AliExpress converter, is a little bit less pleasant. Although text is legible, there’s a lot of “swimming” in the patterns, and it’s hard to read things on the blue background. The game is very playable, however. I had a lot of fun!&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next?&lt;/h2&gt;
&lt;p&gt;Now that the game works to my satisfaction, I am preparing to release it to the general public. As you can tell from the copyright date in the screenshots, this thing has been five long years of mostly procrastination on my part.&lt;/p&gt;

&lt;p&gt;Because of some work and personal obligations, I haven’t quite had enough time to finish this stuff along with my originally planned release schedule. The next article on the blog will take a momentary break from the BBC Bridge Companion theme, but I’ll have a new story before the end of the year about the last push to get the ROM released and playable.&lt;/p&gt;

&lt;p&gt;I wanted to wait until the embedded MAME on the Internet Archive could play it, so that nobody would be forced to embed an emulator, but I wasn’t yet able to figure out how to get that to work.&lt;/p&gt;

&lt;p&gt;Once I do figure that out, or more realistically, ask someone for help, I will be sure to provide a link here to play it. That might be an entirely different project, too!&lt;/p&gt;

&lt;p&gt;Although I don’t plan on making any fixes unless there are bugs (and there are probably tons,) I would like to provide a variant of the game where it’s sped up quite a bit. The first couple levels &lt;em&gt;are&lt;/em&gt; a little boring, but I wanted to err on the side of letting rookies figure out how to use the awkward control panel to operate the game, rather than be instantly obliterated.&lt;/p&gt;

&lt;p&gt;As this article series was being published, porchy on BlueSky told me about &lt;a href=&quot;https://makerworld.com/en/models/2963197-bbc-bridge-companion-cartridge-shell#profileId-3321961&quot;&gt;a 3D-printable BBC Bridge Companion cartridge shell&lt;/a&gt;. This shell looks like it has most of the original measurements intact – I think I might just make myself one for this game as a shelf trophy.&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to come and read about my weird little game, on this relatively unknown little system. I hope you can also do fun stuff in unexpected places in your own life – let me know about it when you do.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:debugscript&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I didn’t find this out until the project was almost done, but you can get MAME to pre-load a bunch of commands into the debugger using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-debugscript&lt;/code&gt; command. This would have been useful for me to synthesize during the build process, rather than manually keeping notes of where various symbols ended up. You live and you learn. &lt;a href=&quot;#fnref:debugscript&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:redrawing-letters&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;“Redrawing” makes me sound less lazy than I am. I made my Python script generate the zasm directive to produce these letters, copied it into my source file, and then just changed the bad line – &lt;span class=&quot;hex-value&quot;&gt;$ff &lt;/span&gt; for H, and &lt;span class=&quot;hex-value&quot;&gt;$80 &lt;/span&gt; for Z – to &lt;span class=&quot;hex-value&quot;&gt;$00 &lt;/span&gt;. &lt;a href=&quot;#fnref:redrawing-letters&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="console" /><category term="bbc" /><category term="bbc-bridge-companion" /><category term="homemade-software" /><summary type="html">Soon, you will know all the secrets of Z80 game programming. Or at least all the ones I didn’t know before I decided to port Tetris to a game console made to teach pensioners how to play cards. Things only get weirder from here, if you can believe that.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/bbcbc-no-cartridge-startup.png" /></entry><entry><title type="html">Composite Companion</title><link href="https://www.leadedsolder.com/2026/08/11/bbc-bridge-companion-composite-mod.html" rel="alternate" type="text/html" title="Composite Companion" /><published>2026-08-11T00:00:00+00:00</published><updated>2026-08-11T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/08/11/bbc-bridge-companion-composite-mod</id><content type="html" xml:base="https://www.leadedsolder.com/2026/08/11/bbc-bridge-companion-composite-mod.html">&lt;p&gt;The BBC Bridge Companion has RF video output from the factory, but that’s not good enough for its cutting-edge pastel graphics. In order to get things looking their best, I spent a really long time making a composite video mod for the system rather than finishing off my &lt;em&gt;Tetris&lt;/em&gt; clone.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-rf-modulator-before-mod.jpg&quot; alt=&quot;The old RF modulator configuration, with the output resistor soldered onto the RCA jack on the back.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Reaching into my pile of old homemade boards, I pulled out &lt;a href=&quot;https://www.leadedsolder.com/2020/12/17/famicom-composite-mod-v1.html&quot;&gt;a second-generation Famicom AV mod board&lt;/a&gt;&lt;sup id=&quot;fnref:2ndgen&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2ndgen&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and assembled it with a THS7316 video amplifier IC. Obviously, I skipped the audio components, and then hardwired in a yellow RCA female jack. Some ultra-cheap test hooks later, and I had a pretty decent unamplified-composite-video probe!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-composite-video-probe.jpg&quot; alt=&quot;My trashy composite-video probe. It&apos;s a purple Famicom composite amplifier board, a yellow RCA jack, and red, yellow, and black test hooks.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Using this, I figured I’d be able to hook the probe into various spots on the motherboard. By trial and error, I could work out where to grab composite video from before it goes into the RF modulator.&lt;/p&gt;

&lt;h2 id=&quot;new-scope-who-dis&quot;&gt;New Scope, Who Dis?&lt;/h2&gt;
&lt;p&gt;Of course, none of my candidate points for picking up a video signal seemed to work. I was a little confused, so I shelved it for awhile and then picked up a new oscilloscope&lt;sup id=&quot;fnref:scope&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:scope&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Before we get started, it’s worth pointing out that I still do not really understand composite video termination. That will probably become really obvious as this article proceeds – I’ve gotten better, but there is so much to learn about baseband that I still feel very uncertain about all of my initial findings and conclusions. Be gentle.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-rigol-hooked-up.jpg&quot; alt=&quot;The Rigol DS1202Z-E scope hooked up to the BBC&apos;s guts. You can also see a growing junkyard of multiple in-progress projects behind the scope. Priorities.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The Tek 2246 I’d been using is great, but it’s just too big and runs too hot for little projects like this. Even in the winter, it turns the room into a sauna. I got a Rigol for cheap to help with this project. It, too, boasts a PAL video trigger mode that seems like it would come in handy.&lt;/p&gt;

&lt;p&gt;Almost immediately, I saw part of the problem with my little composite probe. When scoped, the output was only a handful of mV. When I was blindly connecting my composite video probe to everything I could find earlier, I had hooked it to pin 12 (“Video Input”) of the LM1889… but the scope told me that pin 12 had a 13.2V signal. The THS7316 amplifier datasheet says it has an absolute limit of about 5.5V input, which means I probably sent that little chip to silicon heaven. That would certainly explain why I wasn’t getting valid composite video out of it. Oops. I guess that’s why they make oscilloscopes, so you can measure first before plugging things in.&lt;/p&gt;

&lt;p&gt;On the input to the RF modulator box, I saw a signal that looked like composite video, but it had a voltage of 1.86V to 3.18V. After asking some questions about it, it was determined that I was probably measuring it wrong. Since the output of the video was not connected to a TV, the 75Ω load that a TV usually puts on the line was not present. I ended up ordering a 75Ω BNC terminator and a BNC tee to bolt onto the probe, and took the measurements again.&lt;/p&gt;

&lt;p&gt;With the terminator installed, I was now reading a more reasonable 2.49V peak-to-peak. This obviously isn’t ideal. From what I was reading, you want to aim for more like a 1Vpp signal, where 700mV of that is the colour and about 300mV of that is the sync dip when showing a pure white screen. To make matters worse, I was measuring the “No.” screen. It’s a &lt;em&gt;very&lt;/em&gt; yellow screen, so I’d have to go by the sync amplitude to dial the divider in instead of aiming for the 700mV of white or the 1V of the whole signal.&lt;/p&gt;

&lt;p&gt;I hacked up another Famicom AV board, adapting it onto a solderless breadboard with pin headers, and set up a simple output circuit. The first voltage divider I came up with (1.5kΩ/2.2kΩ) produced a 560mV sync signal, which was clearly way too strong. After asking blog friend Tianfeng for help, I put in a 1.5kΩ/470Ω divider at his recommendation, which knocked the sync down to about 300mV.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-composite-video-on-backup-camera-monitor.jpg&quot; alt=&quot;The &amp;quot;No.&amp;quot; screen is displayed on a backup camera monitor. Told you that you&apos;d see it a lot.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I couldn’t resist trying it on my backup camera monitor. You’ll notice it’s in monochrome. This monitor is super picky with signals and won’t lock on to a colour burst or vertical hold from any TMS99xx-based system, any Sega Genesis, or several other machines I’ve tried. It simply seems incapable of doing 240p at all. So why keep using it? Well, it’s small and easy to set up… and I don’t care if I blow it up.&lt;/p&gt;

&lt;h2 id=&quot;building-test-software&quot;&gt;Building Test Software&lt;/h2&gt;
&lt;p&gt;I was on my way, but now I needed a way to get a pure white screen to test against, in order to make sure my voltage divider will provide the right levels when presented with whatever output voltage the console generated for ‘pure white.’&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-colour-bars-in-emulator.png&quot; alt=&quot;The colour-test program, running in SMPTE colour bars mode, in the MAME emulated console.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With about an hour of work, I modified my in-development &lt;em&gt;Tetris&lt;/em&gt; program to generate various colour-test screens instead of addictive puzzle games. This new ROM displays solid white when it starts, and pushing the PASS key on top of the BBC Bridge cycles between screens of different solid colours. Pushing the SPADE button will toggle on some sorta-SMPTE colour bars (see above.) This new program was burned to a 27c512 EPROM, and placed into my bodge-wired new ROM board.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-composite-video-test-rom-pcb.jpg&quot; alt=&quot;A purple &amp;quot;32k&amp;quot; (sic) ROM cartridge PCB, with the colour-test program socketed into it, is installed into the victim machine. Various test hooks are running all over the scene.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-colour-bars-on-backup-camera.jpg&quot; alt=&quot;The backup camera LCD is now displaying a monochrome colour bars pattern. In the foreground is a sketchy protoboard with two buttons, and in the background is the sketchy solderless breadboard containing the composite mod under development. The logic probe, not shown, is also connected to power and ground using alligator clips. Between the backup camera monitor and the breadboard, you can see a 27sf256 that I broke the Vcc pin off of when I was trying to remove it from the PCB. I have ham for hands.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s running, so let’s break out the oscilloscope.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-composite-video-mod-breadboard-ripple.jpg&quot; alt=&quot;There are crazy amounts of wavy ripple on all parts of this signal.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oh man. That looks like &lt;em&gt;a lot&lt;/em&gt; of power-supply ripple. I had never built a video circuit on a solderless breadboard prior to this, so I wasn’t sure if the noise was coming from the extra capacitance of the breadboard, the Bridge Companion, or somehow from the bench supply powering the console’s original linear 7805. Even clamping 100µF and 0.1µF decoupling capacitors onto the 5V supply lines of the mod board didn’t make a sigificant difference.&lt;/p&gt;

&lt;h2 id=&quot;did-you-have-fun-at-the-mixer&quot;&gt;Did You Have Fun At The Mixer?&lt;/h2&gt;
&lt;p&gt;After poking around the board with the scope probe, I figured out that the ripple was coming from some of the 12V-related signals on the LM1889. 12V on this machine is generated internally from the relatively calm 5V supply, so something wasn’t quite right in the boost circuit. Bravely, I decided to ignore it until later.&lt;/p&gt;

&lt;p&gt;It was difficult to find somewhere good to snipe the signal. As already mentioned, the final stop before the RF box had very dim colour. Working backward, I found that the chroma (colour) and luma (brightness/sync) were mixed on the board just by shorting them together, which was a bit of a surprise.&lt;/p&gt;

&lt;p&gt;That chroma was still too weak when it arrived at the output circuit, because it was mixed at nearly 1:1 with the luma, which made it hard to divide into 1.0V total without making the colours way too dim. I needed something closer to a 2:1 chroma:luma ratio, in order to try and hit the 2:1 colour:sync ratio that a good composite signal should have. So I started trying to figure out how the LM1889 worked.&lt;/p&gt;

&lt;p&gt;It was at this point that I remembered this unit was the very same Bridge Companion that had screwy RF colour, a lot of video noise, and tuned itself to the wrong channel. I decided it might just be a good idea to replace the handful of aluminum electrolytic capacitors involved in the system’s operation.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Position&lt;/th&gt;
      &lt;th&gt;Capacitance&lt;/th&gt;
      &lt;th&gt;Voltage&lt;/th&gt;
      &lt;th&gt;Comment&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;C3&lt;/td&gt;
      &lt;td&gt;10µF&lt;/td&gt;
      &lt;td&gt;50V&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C10&lt;/td&gt;
      &lt;td&gt;100µF&lt;/td&gt;
      &lt;td&gt;10V&lt;/td&gt;
      &lt;td&gt;Fishy smell&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C12&lt;/td&gt;
      &lt;td&gt;10µF&lt;/td&gt;
      &lt;td&gt;50V&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C13&lt;/td&gt;
      &lt;td&gt;10µF&lt;/td&gt;
      &lt;td&gt;50V&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C15&lt;/td&gt;
      &lt;td&gt;10µF&lt;/td&gt;
      &lt;td&gt;50V&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C19&lt;/td&gt;
      &lt;td&gt;10µF&lt;/td&gt;
      &lt;td&gt;50V&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C21&lt;/td&gt;
      &lt;td&gt;10µF&lt;/td&gt;
      &lt;td&gt;50V&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C23&lt;/td&gt;
      &lt;td&gt;10µF&lt;/td&gt;
      &lt;td&gt;50V&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C25&lt;/td&gt;
      &lt;td&gt;10µF&lt;/td&gt;
      &lt;td&gt;50V&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C37&lt;/td&gt;
      &lt;td&gt;100µF&lt;/td&gt;
      &lt;td&gt;25V&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;I did this before I got my nice ESR meter, so no scientific proof of cap failure for you. Just vibes.&lt;/p&gt;

&lt;p&gt;After the recap, I lost confidence in my composite-mod building abilities and shelved the project for over a year. I didn’t even test the recap.&lt;/p&gt;

&lt;h2 id=&quot;starting-over&quot;&gt;Starting Over&lt;/h2&gt;
&lt;p&gt;When I came back to it, I tried reconstructing the composite mod on a solderless breadboard, following the Texas Instruments “AC-coupled” design specifications. This involved remaking some cables, because most of my jumper wires had been popped off and used for other projects.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-working-composite-mod-ac-coupled.jpg&quot; alt=&quot;The AV mod is showing some basic colours in pastel-o-vision.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Colours! That’s good enough video quality for me, especially considering the green gun is not really working on this PVM&lt;sup id=&quot;fnref:sony-9l2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:sony-9l2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. After some measurements to figure out the dimensions and mounting points of the original RF modulator, I made up a quick PCB that duplicated the circuit from the breadboard. I don’t think that shotgun-recapping the system was really what was keeping my composite mod from working before, but it made me feel a bit better.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-composite-video-mod-assembled-inside-board.jpg&quot; alt=&quot;The PCB is assembled and installed into the BBC Bridge Companion motherboard.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There were a lot of little things I had to mess with to get the physical alignment right. I’m glad I left room to slip it around for left/right, up/down, tilt. After all that, I was mercifully ultimately successful in getting the port to point out of the hole, such that the composite video cable could still fit into the much smaller opening when the lid of the console was put on.&lt;/p&gt;

&lt;p&gt;I also went back and touched up that fraying +5V jumper wire, don’t worry, you can trust me.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-composite-video-mod-working-inside-system.jpg&quot; alt=&quot;The lid of the BBC Bridge Companion is installed, the power light is on, and the colour bars are on the TV (admittedly, with a refresh line stuck through them.)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It fits! &lt;em&gt;Nice&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;composite-conclusion&quot;&gt;Composite Conclusion&lt;/h2&gt;
&lt;p&gt;Making an AV mod for this system wasn’t as hard as I had originally thought it was going to be. Maybe it’s because my standards slipped. But there is one solid lesson I learned: if you see a lot of ripple on the scope when you’re trying to dial something in, swap the caps already. It’s a lot easier to work from a known-good base than add that extra frustration for a repair that literally took ten minutes to do.&lt;/p&gt;

&lt;h2 id=&quot;repair-summary&quot;&gt;Repair Summary&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Fault&lt;/th&gt;
      &lt;th&gt;Remedy&lt;/th&gt;
      &lt;th&gt;Caveats&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;(1984 unit) RF video output seems unstable, noisy.&lt;/td&gt;
      &lt;td&gt;Recap system.&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:2ndgen&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The big difference between the 1st and 2nd gen Famicom composite mod is the presence of the 75Ω output resistor. The 3rd generation, which I wrote about in &lt;a href=&quot;https://www.leadedsolder.com/2021/06/15/famicom-composite-mod-v2.html&quot;&gt;Revenge of the Famicom composite video mod&lt;/a&gt;, is just a smaller version of that board with an integrated voltage divider circuit. &lt;a href=&quot;#fnref:2ndgen&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:scope&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Most long-time readers have probably seen me use this scope before. The work in this post was done almost &lt;em&gt;five years ago&lt;/em&gt; – I have greatly procrastinated on writing up and publishing this series. &lt;a href=&quot;#fnref:scope&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:sony-9l2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Long-time readers will feel reassured that in the several years since this work was done, I still haven’t fixed the PVM. Thing will keep running pink as a pig until it bothers me enough to deal with one billion caps in a box about the size of a coffee mug. &lt;a href=&quot;#fnref:sony-9l2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="console" /><category term="homemade-hardware" /><category term="bbc" /><category term="bbc-bridge-companion" /><category term="mod" /><category term="capacitors" /><summary type="html">The BBC Bridge Companion has RF video output from the factory, but that’s not good enough for its cutting-edge pastel graphics. In order to get things looking their best, I spent a really long time making a composite video mod for the system rather than finishing off my Tetris clone.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/bbcbc-rigol-hooked-up.jpg" /></entry><entry><title type="html">Companion &amp;amp; Companion’s Cartridge Complication</title><link href="https://www.leadedsolder.com/2026/08/04/bbc-bridge-companion-repair-cartridge-build.html" rel="alternate" type="text/html" title="Companion &amp; Companion&apos;s Cartridge Complication" /><published>2026-08-04T00:00:00+00:00</published><updated>2026-08-04T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/08/04/bbc-bridge-companion-repair-cartridge-build</id><content type="html" xml:base="https://www.leadedsolder.com/2026/08/04/bbc-bridge-companion-repair-cartridge-build.html">&lt;p&gt;In this phase of the BBC Bridge Companion series, we’ll talk about a few simple tasks. Buying a system. Buying another system. Fixing both systems. And developing a cartridge PCB for them from scratch. Doesn’t everyone spend their weekends this way?&lt;/p&gt;

&lt;p&gt;First, I’d been puttering along in the emulator, but had not yet taken the plunge on the real hardware yet. It’s expensive buying things from eBay, and even moreso when it’s another country’s eBay, trying to get a fair deal for their prized government-backed information technology treasures.&lt;/p&gt;

&lt;h2 id=&quot;oops-ebay&quot;&gt;Oops, eBay&lt;/h2&gt;
&lt;p&gt;Initially, I really did not want to pay the eBay asking price of $175 for a Bridge Companion, and then also pay a huge shipping fee on top. I kept looking at eBay, but missed when a boxed and complete unit was listed. PeteG from the &lt;a href=&quot;https://re-enthused.com/&quot;&gt;re-enthused&lt;/a&gt; Discord server noticed, knew I was in the market, and gave me the tip-off.&lt;/p&gt;

&lt;p&gt;After a week-long bidding war, I scored that boxed Bridge Companion for £90 (about $145 CAD at the time,) shipped. This is definitely more than I planned on spending, but the lure of “cheap shipping” is powerful indeed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-boxed.jpg&quot; alt=&quot;The BBC Bridge Companion box, sitting on my chair.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It arrived &lt;em&gt;very&lt;/em&gt; quickly and it was extremely well packed. I was unwrapping bubble wrap for nearly ten minutes just to get the box out!&lt;/p&gt;

&lt;p&gt;The overall condition was fairly good aesthetically; there was a slight discolouration of the plastic that will probably clean up with a scrub. Definitely not filthy. The buttons felt wobbly but not terrible, which is good since they were the only input mechanism in this console.&lt;/p&gt;

&lt;p&gt;Unfortunately, during inspection, I found out that the cartridge slot has had a rough past life. A handful of the leaf pin contacts are either bent backwards or have popped out of their retainers entirely. Even after some work with an angled pick, cartridge contact seemed like it would be spotty at best.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-slot-damaged.jpg&quot; alt=&quot;The bent pins of the Bridge Companion&apos;s cartridge slot. Insert your favourite &amp;quot;British teeth&amp;quot; joke here.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Check out the heavy hardware and reinforcement brace holding the slot in place! Now &lt;em&gt;that’s&lt;/em&gt; some serious strain relief, which I guess you can do when you’re charging £199. Great build quality.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-underside-warning.jpg&quot; alt=&quot;The underside markings of the case. It says &amp;quot;Use only approved AC adaptor, Designed by HEBER Ltd, Made in the UK&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There’s no marking on the system of what the AC adapter voltage or polarity was; it just had this marking saying that I should use only the &lt;em&gt;appropriate&lt;/em&gt; adapter. This lettering is very clear, and the case overall seemed like very well-done injection moulding. Again, good build quality, possibly helped by the fact that their tooling couldn’t possibly have gotten that many uses.&lt;/p&gt;

&lt;p&gt;Heber is a contract manufacturer and hardware designer in the UK; &lt;a href=&quot;http://www.heber.co.uk/&quot;&gt;they’re still around&lt;/a&gt;, and still make video game equipment, including casino game motherboards and the MiSTer Multisystem. I’m not sure what happened to Unicard.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-ac-adapter-label.jpg&quot; alt=&quot;The label of the AC adapter. It says UNICARD LTD - input 240V - 50Hz, output 9V at 1A. Double insulated. Made in England.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The 9-volt AC adapter included in the box is not really useful to me. It has a UK-style plug and expects 240VAC, and the polarity (or even whether it’s DC) isn’t indicated on the label. Additionally, the plug seems to have been added by the end-user&lt;sup id=&quot;fnref:british-cords&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:british-cords&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, as it’s “hardware-store grey” and has some repulsive sticky residue on the cord that might, at one point in the past, have been electrical tape. Surprisingly, the cord itself was still supple – it was the electrical tape’s plasticizer that was attacking the styrofoam around it.&lt;/p&gt;

&lt;p&gt;After checking inside the machine with a continuity tester, I determined that this barrel plug was centre-negative (the centre pin of the barrel plug was connected to the “common” pin of the 7805 voltage regulator.) A Sega MK-1602 adapter (Model 1 Genesis/MegaDrive) fit perfectly and provided the necessary polarity, voltage, and current ceiling. It works with everything! Gotta pick up more of these little miracles.&lt;/p&gt;

&lt;p&gt;Let’s take a look around while we’re in here. Don’t want to start if there’s something else wrong, after all…&lt;/p&gt;

&lt;h2 id=&quot;diagnosing-the-companion&quot;&gt;Diagnosing the Companion&lt;/h2&gt;
&lt;p&gt;The first thing I noticed when taking apart the Companion is that the screws were a little weird. They’re coarse-thread Phillips thread-forming screws, to be sure, but the cross head wasn’t exactly cross – more resembling a wounded bird. I’ve never really seen screws like this before; maybe they’re made of a very soft metal and bent before camming out at assembly. I had to be very careful when removing or installing them not to strip out the heads any further. A future improvement would be to go to the hardware store and just buy new  thread-chasing fasteners, as they appear to be all the same length and pitch.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-serial-number.jpg&quot; alt=&quot;The serial number on the bottom of the machine is UC001-06123. It says &amp;quot;No dealer serviceable parts.&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;No &lt;em&gt;dealer&lt;/em&gt; serviceable parts? That’s one step beyond Radio Shack. I don’t know what store you would have bought this at when it was new, but they were insufficiently skilled to repair a machine as advanced as the Bridge Companion.&lt;/p&gt;

&lt;p&gt;My serial number was 6123. If they started at 0001, I guess we can assume there are at least 6,123 Bridge Companions out there. That feels like a safe bet, because mine seemed to actually be a &lt;em&gt;revised&lt;/em&gt; version - different from the photos I’d seen on the internet beforehand of other Bridge Companion motherboards, and another Bridge Companion motherboard I would later have in my hands. But I’m getting ahead of myself.&lt;/p&gt;

&lt;p&gt;Again, it’s worth taking another stop here in the story to reinforce that the plastic panels on this thing are of very high quality. Even though they’re thin plastic, the moulding is sharp, without flash or ugly casting artifacts, and the dimensions are nicely square. It makes me wonder if they reused an injection mould from another product to keep the tooling costs down – the buttons especially make me think of photocopiers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-pulled-top-off.jpg&quot; alt=&quot;The top panel is pulled back, showing the ribbon cable connection into the motherboard. An Astec UM1233 RF modulator is visible.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once the plastic “chin” piece is removed, the top part with the buttons comes right off. It has a ribbon connecting it to the mainboard, but the pins are so thick that this ribbon pulls right out with no problems. &lt;em&gt;Nice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-control-panel-flux-goo.jpg&quot; alt=&quot;Some white residue on the backside of the &amp;quot;controls&amp;quot; panel.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The front panel with all the buttons had this chalky white crap on the backside, which I think must have been leftover flux. It didn’t move very well with alcohol, so I had to keep scrubbing at it until it did.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-control-panel-pcb.jpg&quot; alt=&quot;The control panel PCB, reading &amp;quot;HEBER LTD 1984&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The control panel PCB itself (copyrighted 1984) is just a normal carbon-contact setup, and is shorted by a membrane that is itself depressed by the funky plastic buttons. There’s a power LED here, too, which is a nice feature… on a machine that doesn’t even have a &lt;em&gt;power switch&lt;/em&gt;. I guess they expected you to &lt;a href=&quot;https://archive.org/details/BBCBridgeCompanion/BbcBridgeCompanionOperatingManual/page/n2/mode/1up&quot;&gt;switch it off at the outlet instead&lt;/a&gt;, which is not really a thing I could do here in Canada.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-main-ics.jpg&quot; alt=&quot;The main ICs of the Bridge Companion; from top to bottom: RCA CDM6116AE3, ST M27128, Zilog Z80, Zilog Z80 PIO, TMS9129NL&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The routing on the motherboard is quite good. Now that &lt;a href=&quot;/tag/leako&quot;&gt;I know how difficult it can be to do a clean route&lt;/a&gt;, the relative lack of vias is impressive.&lt;/p&gt;

&lt;p&gt;On the motherboard, we see the big important chips. A Z80, 2kB 6116 SRAM, the BIOS ROM (a single 16kB 27c128 instead of two 8kB 27c64s like in the picture I’d seen earlier), the Z80 PIO, and a TMS9129 VDP with a pair of its own TMS4416 video DRAMs offering a vast 16kB of VRAM&lt;sup id=&quot;fnref:9118&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:9118&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-pal-ic.jpg&quot; alt=&quot;A red arrow points to the PAL12L10CNS that does decoding&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This 12-input/10-output programmable logic array (PAL) chip most likely contains the memory and I/O address decoding for the system. It’s interesting that the system is using UV-eraseable EPROMs instead of mask ROMs; perhaps production was too low to justify the expense of mask ROMs&lt;sup id=&quot;fnref:pal-dumped&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:pal-dumped&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-mc7805ct.jpg&quot; alt=&quot;A three-pin 7805 5v voltage regulator&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This 7805 produces 5V for the rest of the system. It’s also strapped to this handsome black heatsink which has a thick coating of some kind.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-video-ics.jpg&quot; alt=&quot;Three video ICs are clumped together: LM1889, MC14066, MC14013.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The RF modulator is your normal UM1233 setup, with no sound hooked up to it of course. I’m sure that the reason these modulators are so common is because they had to get them certified for radio interference. By using a whole module, Heber, Sinclair, etc could outsource the analogue-TV part of the compliance – and all the legal headaches that entails – to Astec.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-rf-modulator-inside.jpg&quot; alt=&quot;Inside the Astec UM1233 RF modulator. The rearmost pin (+5V) and the middle pin (RF-ready video) are connected to the motherboard.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Near the RF modulator is a common IC in PAL game consoles of the era: the classic LM1889 composite video modulator (I know the name is a little confusing.) The area nearby also has an MC14066 quad switch, which according to the datasheet, is probably also involved in the output video circuit, and an MC14013 D-type flip-flop, which is maybe used as a clock divider or to alternate which field of the video signal is being displayed. I’m not very familiar with PAL generation, as this was the first PAL system I’d ever looked at the video output for.&lt;/p&gt;

&lt;p&gt;Let’s take a look at the cartridge before we finish up. It’s a very unusual size that reminds me more of an electric razor than a game cartridge. Thick, narrow, and stocky…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-size-comparison-1.jpg&quot; alt=&quot;The cartridge size comparison, from the top. Bonanza Bros for SMS, and Phantasy Star II for Genesis are compared to it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-size-comparison-2.jpg&quot; alt=&quot;The cartridge size comparison, from the bottom. The BBC cartridge is nearly twice the thickness of the Genesis one.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It also has a lot of unnecessary plastic “greebles” all over it that make it look a bit like a weird science fiction prop. There’s even an interesting “skirt” cutout on the back so you can see the gold-plated, non-beveled edge connector. The cartridge feels solidly-built and is made of thick plastic, which I’m sure helped sell the sense of being a quality product to the end customer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-size-comparison-3.jpg&quot; alt=&quot;The &amp;quot;skirt&amp;quot; cutout, exposing the edge connector from the corners.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’d like to thank whoever at Heber decided that the screws should be exposed without having to remove the cartridge label, unlike &lt;em&gt;some&lt;/em&gt; systems. Once the four screws are undone, the PCB pops open… look at all that empty space and &lt;em&gt;super-shiny&lt;/em&gt; solder mask!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-front.jpg&quot; alt=&quot;The front of the cartridge PCB. A 74ls08 and SGS M27256 are shown.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-rear.jpg&quot; alt=&quot;The rear of the cartridge PCB. A marking, HEBER LTD 1987, is shown.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The only ICs in here are the socketed(!) 32kB 27c256 EPROM(!) and a 74ls08 AND-gate, of which most of the inputs are grounded. It’s interesting that there’s a 1987 date on this board. Since this game was packed in with my Bridge Companion, it would mean that they were still selling these consoles for at least two years after release.&lt;/p&gt;

&lt;p&gt;That’s enough screwing around inside the machine for now; let’s move on to getting something to happen, even if it’s in PAL.&lt;/p&gt;

&lt;h3 id=&quot;video-kid&quot;&gt;Video, Kid&lt;/h3&gt;
&lt;p&gt;Blog superfriend Nicole at &lt;a href=&quot;https://nicole.express/2021/pinball-in-your-house.html&quot;&gt;Nicole Express published a great article on the &lt;em&gt;Sears Tele-Games Pinball Breakaway&lt;/em&gt; TV game&lt;/a&gt;. Not only is it a fun anachronistic read, but you’ll also notice that she hooked up a fancy Chinese-made device called the RF-to-AV in order to make it talk to her composite capture rig.&lt;/p&gt;

&lt;p&gt;Realizing an opportunity to get out of this without having to do a composite mod to the Companion on top of everything else, I sent away for my own and got it a few weeks later for under twenty bucks. The menus are completely inscrutable (all in Chinese that’s also somehow extremely blurry) but the price is right.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-rf-to-av-1.jpg&quot; alt=&quot;The RF to AV is showing the &amp;quot;No.&amp;quot; screen in red.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At first, I had some decent success locking onto a signal using my PAL-capable PVM. You can have your choice: with a hellish orange glow, or with a super-wiggly white background that appears purple because my PVM is crying for help that I refuse to give it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-rf-to-av-2.jpg&quot; alt=&quot;The RF to AV is showing the &amp;quot;No.&amp;quot; screen in white (actually purple, my PVM is dead as hell.)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Both times, the PVM identified the successful signal as 525-line PAL, which I don’t think is actually correct for UK “System I.”&lt;/p&gt;

&lt;p&gt;Eventually, I was completely unable to lock onto the signal after turning the RF-to-AV off and then on again. After wasting about an hour trying various combinations of standing on my head and cursing the gods, the best I could hope for was an out-of-sync image where I could just barely see smears that I knew were supposed to be text.&lt;/p&gt;

&lt;p&gt;It seems (from reading other sources, including the seller’s ad) that the adapter’s “autosense” population of the channel selector is based at least partially on sound coming across the RF. Since the only thing the Bridge Companion is sending on the sound channel is ambient space noise, it’s not likely to work well with this approach. I could see the autosense routine slowing down as it approached 591MHz (“Channel 36” on UK System I,) but it wouldn’t realize a channel was actually there.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-samsung-scanning.jpg&quot; alt=&quot;The Samsung 910MP is scanning for channels. It is at 305MHz and reports itself to be about 31% through the UK VHF spectrum.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I ended up carrying the whole mess to another room and testing it on my Samsung 910MP, the gently-battered&lt;sup id=&quot;fnref:lightning&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:lightning&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; LCD TV that somehow is capable of getting a crappy image out of any source – but I mean it when I say &lt;em&gt;any&lt;/em&gt; source. Since it’s a modern set, it can change its fancy little microchippy guts to understand RF from any region.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-samsung-working.jpg&quot; alt=&quot;The &amp;quot;No.&amp;quot; screen, now on the 910MP.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After switching the 910MP into “Europe/UK” mode and then auto scanning the channels, it found the Bridge Companion at 592MHz and plunked it into “Channel 1.” Now it’s a very bright &lt;em&gt;yellow&lt;/em&gt;, rather than the emulator’s tan. Just what colour is this damn thing supposed to be?&lt;/p&gt;

&lt;p&gt;I did a little more fine tuning to keep the image from wiggling, and now I finally had a sort-of-legible way to see this obscure PAL RF console in my largely NTSC composite home. But, uh, I didn’t &lt;em&gt;like&lt;/em&gt; it.&lt;/p&gt;

&lt;h3 id=&quot;winning-at-the-slots&quot;&gt;Winning At The Slots&lt;/h3&gt;
&lt;p&gt;I didn’t expect I’d have to repair the cartridge slot on this machine, but that’s life.&lt;/p&gt;

&lt;p&gt;Since the plastic shell of the slot, its long mounting pins, and the reliefs for the reinforcement braces all seem to be more than a little unique, it worried me that it would be a challenge to find a replacement part.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-slot-top.jpg&quot; alt=&quot;The cartridge slot is soldered into the board. It has a pair of rainbowy metal braces holding it in.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The solder joints on this slot were in pretty poor condition. If I hadn’t felt the screws on the case snap loose for the first time, I would have assumed someone had already tried to repair it. It’s an interesting contrast to the expensive cad-plated (or maybe just yellow zinc-plated) metal brackets holding the 2x20-pin connector down.&lt;/p&gt;

&lt;p&gt;A brand new right-angle 2x20 0.1” edge connector did indeed prove hard to get. It doesn’t really make a lot of sense; I could even find 2x21s. I first grabbed a &lt;em&gt;$10&lt;/em&gt; Sullins from Digi-Key – the only one in stock – only to find out it had a death grip on the cartridge. Just like with &lt;a href=&quot;https://www.leadedsolder.com/2020/07/10/colecovision-diy-part-2.html&quot;&gt;the second failed cartridge slot used in the Leako&lt;/a&gt;, also a Sullins, I missed the part of the datasheet that said it could only handle 1.57mm thick PCBs and not 1.6mm ones.&lt;/p&gt;

&lt;p&gt;There was a 40-pin, right-angle, 0.1” edge connector listed at Digi-Key by TE AMP, but it was listed as “submit a quote request.” For laughs, I did, and they came back and told me that they’d make some if I promised to buy a minimum of 2,688 units, at $5.10USD per connector. I can afford a lot of 44-pin connectors to cut up for $13,727.62 USD!&lt;/p&gt;

&lt;p&gt;In the meantime, I found an eBay seller in Taiwan offering some connectors of an anonymous brand, so I bought a box of ‘em. This new mystery-meat connector still had a bit tighter of a grip than I would prefer, but it was still possible to insert and remove the cartridge with a lot of force, probably due to the cheap-feeling pins.&lt;/p&gt;

&lt;p&gt;Initially, I was not comfortable with this fix, worrying that the extra force of insertion would damage the console’s board, but once it was completely soldered up it had a surprising amount of strength, and it got easier with repeated insert/remove cycles. For a little extra insurance, I also applied a thin strip of double-sided Gorilla Tape between the PCB and the cartridge slot, which is surprisingly sticky stuff, albeit quite thick.&lt;/p&gt;

&lt;p&gt;With a shrug, I figured this was good enough to keep the project moving along. Since I was planning on making my own cart anyway, beveling the edge on it would make the cartridge easier to insert and remove, and thus compensate a little bit for the tightness of the new connector, and if the slot ended up pulling up a bit anyways, I could use my garage vise to bend up a new metal bracket and push the new connector down.&lt;/p&gt;

&lt;p&gt;As for removing the slot, it wasn’t too bad. Originally, I planned on using the FR-301 desoldering gun, but I decided on a gentler approach when I realized the solder mask was damaged on the underside:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-solder-mask-damage.jpg&quot; alt=&quot;The pre-existing damage to the solder mask on the underside of the board, probably caused from forking cartridges in and out of the machine. I put some new solder mask paint on here and called it a day.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I ended up snipping each of the pins off the old cartridge slot and desoldering them individually. It’s not as pretty of a result, but the chances of pulling a pad or trace are much lower.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-slot-removed.jpg&quot; alt=&quot;The topside of the board with the cartridge slot removed. The through-holes are intact, but some are a bit dirty.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The old cartridge slot, of course, is not in the best condition after this treatment:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-big-book-of-british-smiles.jpg&quot; alt=&quot;A snap from The Simpsons where &amp;quot;The Big Book of British Smiles&amp;quot; is opened to horrify Ralph Wiggum with the crudely-Photoshopped remains of the Companion&apos;s cartridge slot. WHY MUST YOU TURN MY EXPANSION BUS INTO A HOUSE OF LIES?&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sorry, I couldn’t resist.&lt;/p&gt;

&lt;p&gt;I wasn’t sure initially if the metal brackets holding the old cartridge slot down were zinc- or cadmium-plated, but this seems pretty likely to be cad-plating to me:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cart-slot-bracket.jpg&quot; alt=&quot;This bracket from the cartridge slot has a beautiful but deadly rainbow colour, like a trout with rabies.&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;yeah-heres-the-cartridge-pinout&quot;&gt;Yeah, Here’s The Cartridge Pinout&lt;/h2&gt;
&lt;p&gt;While the slot was removed, I had easy access with my multimeter probes to help map out the cartridge slot. Here’s what I found:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;B&lt;/th&gt;
      &lt;th&gt;A&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;20&lt;/td&gt;
      &lt;td&gt;/ROM_CE Select 8kB bank 4 &lt;span class=&quot;hex-value&quot;&gt;$&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xa000&lt;/code&gt;&lt;/span&gt; (PAL pin 15)&lt;/td&gt;
      &lt;td&gt;PAL pin 16&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;19&lt;/td&gt;
      &lt;td&gt;/ROM_CE Select 8kB bank 3 &lt;span class=&quot;hex-value&quot;&gt;$&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x8000&lt;/code&gt;&lt;/span&gt; (PAL pin 14)&lt;/td&gt;
      &lt;td&gt;Z80 IORQ&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;18&lt;/td&gt;
      &lt;td&gt;/ROM_CE Select 8kB bank 2 &lt;span class=&quot;hex-value&quot;&gt;$&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x6000&lt;/code&gt;&lt;/span&gt; (PAL pin 18)&lt;/td&gt;
      &lt;td&gt;Z80 MREQ&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;17&lt;/td&gt;
      &lt;td&gt;/ROM_CE Select 8kB bank 1 &lt;span class=&quot;hex-value&quot;&gt;$&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x4000&lt;/code&gt;&lt;/span&gt; (PAL pin 19)&lt;/td&gt;
      &lt;td&gt;GND&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;16&lt;/td&gt;
      &lt;td&gt;D3&lt;/td&gt;
      &lt;td&gt;D2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;15&lt;/td&gt;
      &lt;td&gt;D4&lt;/td&gt;
      &lt;td&gt;D1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;14&lt;/td&gt;
      &lt;td&gt;D5&lt;/td&gt;
      &lt;td&gt;D0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;13&lt;/td&gt;
      &lt;td&gt;D6&lt;/td&gt;
      &lt;td&gt;A0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;12&lt;/td&gt;
      &lt;td&gt;D7&lt;/td&gt;
      &lt;td&gt;A1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;11&lt;/td&gt;
      &lt;td&gt;A10&lt;/td&gt;
      &lt;td&gt;A2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10&lt;/td&gt;
      &lt;td&gt;/ROM_OE&lt;/td&gt;
      &lt;td&gt;A3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;9&lt;/td&gt;
      &lt;td&gt;A11&lt;/td&gt;
      &lt;td&gt;A4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;A9&lt;/td&gt;
      &lt;td&gt;A5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;7&lt;/td&gt;
      &lt;td&gt;A8&lt;/td&gt;
      &lt;td&gt;A6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6&lt;/td&gt;
      &lt;td&gt;A13&lt;/td&gt;
      &lt;td&gt;A7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;A14&lt;/td&gt;
      &lt;td&gt;A12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;VCC&lt;/td&gt;
      &lt;td&gt;Z80 WAIT&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;PIO PA1&lt;/td&gt;
      &lt;td&gt;Z80 CLK&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;Z80 WR&lt;/td&gt;
      &lt;td&gt;Z80 M1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;A15&lt;/td&gt;
      &lt;td&gt;PIO PA0&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;em&gt;Tons&lt;/em&gt; of useful signals here. A&lt;sub&gt;15&lt;/sub&gt; seems to be part of an input to the PAL, as well, which probably exposes some bank-switching ability for very large cartridges. Following the pattern, it’s very likely that the unknown A20 edge pin is meant for switching in a whole other bank, but &lt;em&gt;Bridge Builder&lt;/em&gt; only goes up to a 32kB ROM and I definitely don’t need more than that for &lt;em&gt;Tetris&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Obviously, this process is made more difficult by the opaqueness of the PAL itself. Without building something like a DuPAL board, I was probably not going to easily reverse-engineer it.&lt;/p&gt;

&lt;h2 id=&quot;a-companion-companion&quot;&gt;A Companion Companion&lt;/h2&gt;
&lt;p&gt;To muddy the waters even further, I still had an email notification on eBay set up for new Bridge Companions. One came up from a house-clearing firm, was sold as totally untested, and came with only the cartridge. I made an offer for £25, which was immediately accepted. A very good discount over £90.&lt;/p&gt;

&lt;p&gt;The next day, PeteG (who, you may recall, started this whole mess) got one for &lt;em&gt;£5&lt;/em&gt;, so I didn’t even get a chance to brag about my great deal.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-second-one.jpg&quot; alt=&quot;The second BBC Bridge Companion I now own. Notice the scratch on the front cover.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Why get another? Well, as my grandfather always used to constantly say, “whenever you’re building your own circuit board and trying to jam it into an old machine that you can’t easily get a replacement for, buy another one of them to mod.” He was a weird dude, but he didn’t steer me wrong on this one. Thanks, Grandpa.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-second-one-bridge-builder-roms.jpg&quot; alt=&quot;The second (circa 1984) copy of Bridge Builder, internally. It has four TI 27C64 EPROMs, marked BBC1 1 through 4. I guess now we know why the 1987 cartridge PCB was so unncessarily large.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When it arrived, I couldn’t help noticing the differences. For instance, this one was serial number &lt;em&gt;00577&lt;/em&gt;! When I opened its copy of &lt;em&gt;Bridge Builder&lt;/em&gt; up, it had a date of 1984 and the entire GDP of Great Britain in 64Kbit socketed ROMs. This is how I figured out the “8kB” bank switching in the pinout up above.&lt;/p&gt;

&lt;h2 id=&quot;the-magic-happens&quot;&gt;The Magic Happens&lt;/h2&gt;
&lt;p&gt;Through collecting two machines, I now had:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;A working cartridge slot in one of them (the second one;)&lt;/li&gt;
  &lt;li&gt;A socketed cartridge that accepted 27c256 EPROMs (from the first one;)&lt;/li&gt;
  &lt;li&gt;A television set that sort of worked to display the video coming out of these.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I burned my in-progress test ROM, which didn’t do much more than display a couple in-memory variables and put up the basics of a playfield along with “HELLO WORLD” esque messages to a W27C257 EEPROM (originally bought for the Leako project) and slammed it into the 1987 &lt;em&gt;Bridge Builder&lt;/em&gt; cartridge. Thanks for the socket, Heber!&lt;/p&gt;

&lt;p&gt;Unfortunately, the new Bridge Companion didn’t show up on the same channel as the old one! I had to re-scan channels again, and ended up with a very bouncy, glitchy output from the new one. It will need a service, but that doesn’t mean I can’t use it to test my game… &lt;em&gt;on real hardware.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I popped the cartridge in, and then plugged in the DC power jack…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-second-one-running-my-game.jpg&quot; alt=&quot;The second BBCBC is now running my game. You can see a blurry rectangular playfield in the middle of all the blurry green.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Holy crap. It was muddy, it was swimmy, it was glitchy and unreadable, but it was &lt;em&gt;mine&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Even the controls worked! Here’s what it’s supposed to look like, without the “help” of a disintegrating RF output:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-my-test-code.png&quot; alt=&quot;The test code running in MAME. As you can see, it has a lot of test strings and weird loopy things from me trying to figure out how to write various things in Z80.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Hardly a “game,” but it’s still all my own code and VDP initialization, learned the hard way, and running on the real hardware. What a thrill! Why would anyone do drugs when they could just push one byte at a time to a Z80 I/O port?&lt;/p&gt;

&lt;h2 id=&quot;the-dumb-cartridge&quot;&gt;The Dumb Cartridge&lt;/h2&gt;
&lt;p&gt;Because I intended to keep both copies of &lt;em&gt;Bridge Builder&lt;/em&gt; intact, I quickly remade the cartridge in KiCad and sent it off for fabrication. I was pleased to find that JLCPCB has a sort of “secret menu,” where they’ll still bevel the edge on your PCBs without also paying thirty bucks for gold fingers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-dumb-rom-board.jpg&quot; alt=&quot;The &amp;quot;dumb&amp;quot; 64kB ROM board, in glorious royal purple colour. It has a 74LS08 and a socket for a 27c512.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This board was intended to support up to 64kB 27C512 ROMs, although as far as I can tell, no games this large were ever officially released. With just a cheap tin DIP socket installed for the ROM, it’s not great for iteration speed, but most of my development is done in the emulator anyway. I figured it was all I’d need for now.&lt;/p&gt;

&lt;p&gt;I decided to test out the board, and my freshly repaired cartridge slot. Unfortunately, it didn’t work. The cartridge, that is, not the repair.&lt;/p&gt;

&lt;p&gt;When trying to load my test ROM on my new cartridge PCB, all that I got was the “No.” screen, indicating that the BIOS was not able to read the first couple bytes of the cartridge ROM in order to find the entry point. Those bytes are there, since I’m using a known-good ROM, so what’s up?&lt;/p&gt;

&lt;h3 id=&quot;where-did-i-screw-up&quot;&gt;Where did I screw up?&lt;/h3&gt;
&lt;p&gt;My first check on the new cartridge PCB was the obvious one: making sure &lt;span class=&quot;active-low-overbar&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CE&lt;/code&gt;&lt;/span&gt; and &lt;span class=&quot;active-low-overbar&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OE&lt;/code&gt;&lt;/span&gt; were getting toggled. &lt;span class=&quot;active-low-overbar&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CE&lt;/code&gt;&lt;/span&gt; got bumped at startup, just before the “No.” screen appeared, so the chip-select signal was getting all the way from the Z80 to the PAL to the 74HC08 to the ROM. &lt;span class=&quot;active-low-overbar&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OE&lt;/code&gt;&lt;/span&gt; is driven by the console, and was getting pulsed periodically. Not the problem.&lt;/p&gt;

&lt;p&gt;I verified the pinout using the pin-header breakouts at the bottom of the cartridge PCB, and the Z80, and made sure there were no shorts between adjacent lines. Doing breakouts like this is a real time saver when you run into an ugly situation like this, and I’m extremely glad I do it even though it makes the routing a lot more annoying.&lt;/p&gt;

&lt;p&gt;Since the ‘256 has a programming-voltage (Vpp) pin in pin 1 where the PCB supplies A&lt;sub&gt;15&lt;/sub&gt;, I decided to cut that off and ground it out. This was of course a fruitless endeavour, because the erasing voltage of the chip I was using is 14V, and the programming voltage was 12V, and the Companion doesn’t use &lt;em&gt;twelve-volt&lt;/em&gt; logic. Nothing changed.&lt;/p&gt;

&lt;p&gt;To make absolutely sure that the chip select logic was working, I bypassed the “decoder” by removing the 74HC08 and jumping from the &lt;span class=&quot;active-low-overbar&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CS_0x4000&lt;/code&gt;&lt;/span&gt; line directly to the output that feeds the &lt;span class=&quot;active-low-overbar&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CE&lt;/code&gt;&lt;/span&gt; pin of the ROM using its empty socket. Doing so would disable paging, but I didn’t need it to read the first handful of bytes at &lt;span class=&quot;hex-value&quot;&gt;$4000 &lt;/span&gt;, which the BIOS checks before deciding to show the “No.” screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-debugging-74ls08-make-sure.jpg&quot; alt=&quot;A yellow jumper wire is stuck from pin 1 of the 74ls08 to pin 8 of the 74ls08 in order to immediately map the chip-select input onto the ROM.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, this jumper job made no difference, and since I had earlier observed a pulse on the ROM’s chip-enable pin as expected, I could reasonably assume that the decoding ‘08 was not at fault. All this check did was rule out that a chip select other than &lt;span class=&quot;active-low-overbar&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CS_0x4000&lt;/code&gt;&lt;/span&gt; was enabling the ROM.&lt;/p&gt;

&lt;p&gt;Even though I was certain that, since it was an active-low signal, the fault wasn’t caused by my use of a 74HC08, I put a brand-new 74LS08 back in, just to reduce the number of differences from the original PCB.&lt;/p&gt;

&lt;p&gt;Finally, I decided to just brute force things. If the old PCB worked, and the new PCB doesn’t, there must be some difference that keeps it from doing so. Eventually, I realized that the A&lt;sub&gt;14&lt;/sub&gt; line was &lt;em&gt;not&lt;/em&gt; run on the old PCB to the ROM. Oops.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-old-pcb-missing-a14.jpg&quot; alt=&quot;An arrow points to the A14 pin of the 27c256, which is not routed to the cartridge edge beneath.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After thinking about it, I realized this made sense. Because the address lines coming into the cartridge are the ones of the CPU, any address delivered to the cartridge is &lt;em&gt;absolute&lt;/em&gt;. As such, a read to &lt;span class=&quot;hex-value&quot;&gt;$4000 &lt;/span&gt; (the start of the cartridge in the Companion’s memory map) sets A&lt;sub&gt;14&lt;/sub&gt; and no other address line.&lt;/p&gt;

&lt;p&gt;Since we pass that A&lt;sub&gt;14&lt;/sub&gt; blindly along to the ROM, it’s like we’re asking the ROM to return the byte at &lt;span class=&quot;hex-value&quot;&gt;$4000 &lt;/span&gt; &lt;em&gt;of itself&lt;/em&gt;, when we actually wanted it to return the byte at &lt;span class=&quot;hex-value&quot;&gt;$0000 &lt;/span&gt;.&lt;/p&gt;

&lt;p&gt;No wonder it wasn’t working if the ROM was returning some garbage from 16kB into it instead of the actual start of ROM! I cut the A&lt;sub&gt;14&lt;/sub&gt; line, and now I didn’t get the “No.” screen anymore.&lt;/p&gt;

&lt;p&gt;OK – I’d have to figure out a better way of doing decoding if I want the cartridge to actually work with a big ROM, but the colour test program is small enough that I don’t have to worry about page switching for the time being.&lt;/p&gt;

&lt;p&gt;It worked, and now I could continue developing my game, knowing that I had a cartridge to put it in.&lt;/p&gt;

&lt;h2 id=&quot;repair-summary&quot;&gt;Repair Summary&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Fault&lt;/th&gt;
      &lt;th&gt;Remedy&lt;/th&gt;
      &lt;th&gt;Caveats&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;(1987 unit) Cartridge slot is damaged&lt;/td&gt;
      &lt;td&gt;Replace cartridge slot.&lt;/td&gt;
      &lt;td&gt;New slot is tighter fit than before, has really poor strain relief.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:british-cords&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The manual actually describes what kind of plug you should buy to graft onto the adapter. It was a bit shocking to me as a Canadian that a manufacturer would “trust” a customer with something like DIY power cord construction. After asking Johnny about it, it was explained that some English electronic products were sold through to the mid-80s without a plug due to competing standards and also cost savings. A standard was established in the late 80s, which also seems surprisingly late. &lt;a href=&quot;#fnref:british-cords&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:9118&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Like &lt;a href=&quot;https://www.leadedsolder.com/2020/05/04/casio-pv7-msx-tms-vdp-fix.html&quot;&gt;the NTSC TMS9118 in the PV-7&lt;/a&gt;, the PAL TMS9129 only requires two DRAMs for video RAM. The TMS9918/TMS9919/TMS9928/TMS9929 have DRAM refresh and indexing logic for eight. &lt;a href=&quot;#fnref:9118&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:pal-dumped&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Porchy on BlueSky let me know that &lt;a href=&quot;https://wiki.pldarchive.co.uk/index.php?title=BBC_Bridge_Companion&quot;&gt;they have brute-forced and dumped the BBC Bridge Companion’s PAL at IC1&lt;/a&gt;. Thank you Porchy! If anyone is feeling ambitious, it should be pretty easy to make your own clone Bridge Companion at home now. &lt;a href=&quot;#fnref:pal-dumped&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:lightning&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I’ve told this story before, but the previous owner told me, after taking the $10 cash I paid for it, that he thought it got hit by lightning. &lt;a href=&quot;#fnref:lightning&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="console" /><category term="bbc" /><category term="bbc-bridge-companion" /><category term="homemade-hardware" /><category term="homemade-software" /><category term="repair" /><summary type="html">In this phase of the BBC Bridge Companion series, we’ll talk about a few simple tasks. Buying a system. Buying another system. Fixing both systems. And developing a cartridge PCB for them from scratch. Doesn’t everyone spend their weekends this way?</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/bbcbc-boxed.jpg" /></entry><entry><title type="html">The BBC Tetris Companion</title><link href="https://www.leadedsolder.com/2026/07/28/bbc-bridge-companion-part-1-overview.html" rel="alternate" type="text/html" title="The BBC Tetris Companion" /><published>2026-07-28T00:00:00+00:00</published><updated>2026-07-28T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/07/28/bbc-bridge-companion-part-1-overview</id><content type="html" xml:base="https://www.leadedsolder.com/2026/07/28/bbc-bridge-companion-part-1-overview.html">&lt;p&gt;Ever done dumb stunts for your friends on a dare? I was hanging out with a bunch of fellow old-computer ne’er-do-wells when the topic of the BBC Bridge Companion came up. It was a useless thing to own, they said, although many of the assembled group had already owned one at some time or another. If you don’t know how to play bridge, then why even bother? Well, I know how to play &lt;em&gt;Tetris&lt;/em&gt;, and I know how to write Z80 assembly, or at least I thought I did.&lt;/p&gt;

&lt;p&gt;Over the course of the next few weeks, I’ll be publishing more in-depth articles about the details of the &lt;em&gt;Bridgetris&lt;/em&gt; project. This post is a shorter overview of the entire project, to give a rough idea of the overall tasks and progress.&lt;/p&gt;

&lt;h2 id=&quot;a-bridge-to-the-past&quot;&gt;A Bridge to the Past&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-hero.jpg&quot; alt=&quot;The Bridge Companion, sitting on my carpet. Sorry for the spoiler.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;First, a bit of history. What &lt;em&gt;is&lt;/em&gt; the BBC Bridge Companion, and why did this thing consume my attention and free hours for a frankly ridiculous and unjustifiable amount of time? Put simply, &lt;a href=&quot;https://heber.co.uk/hebers-bbc-bridge-companion/&quot;&gt;the BBC Bridge Companion&lt;/a&gt; was a game console intended to teach you how to play the card game bridge on your television set.&lt;/p&gt;

&lt;p&gt;It’s BBC-branded, because at the time they had &lt;a href=&quot;http://videogamekraken.com/bbc-bridge-companion-by-bbc-enterprises-ltd&quot;&gt;a popular bridge-instruction television show, &lt;em&gt;BBC Bridge Club&lt;/em&gt;, with some top-flight players&lt;/a&gt; looking for a new way to introduce newbies to the game. It is not, however, developed by the BBC.&lt;/p&gt;

&lt;p&gt;However, I doubt they sold many, because according to Wikipedia, it cost &lt;em&gt;£200&lt;/em&gt; when new in 1985! You could have bought a 48k ZX Spectrum for £175 (in 1982; probably for much less by 1985) and still had plenty of money left over to buy bridge games for that. I have to assume that it was severely discounted later, or at least never actually sold at that price.&lt;/p&gt;

&lt;p&gt;Software was distributed on ROM cartridges, and as far as I can tell there were only ever nine releases made for it:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;em&gt;Advanced Bidding&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Advanced Defence&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Bridge Builder&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Club Play 1&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Club Play 2&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Club Play 3&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Conventions 1&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Duplicate 1&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Master Play 1&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I was unable to find any records for how much the cartridges cost, but it couldn’t have been cheap. Each one appears to have come in a huge moulded-plastic shock box with a thick coil-bound handbook containing most of the lesson material. &lt;em&gt;Bridge Builder&lt;/em&gt; was a pack-in with the system.&lt;/p&gt;

&lt;h2 id=&quot;gimme-the-specs&quot;&gt;Gimme the specs&lt;/h2&gt;
&lt;p&gt;The hardware is what interested me. This thing is put together with a Zilog Z80 and our old friend, the TI TMS video display processor (VDP.) Specifically, it’s a Texas Instruments TMS9129 - the PAL variant of the TMS9128, which is itself the “RGB” variant of the TMS9118 that you previously saw me &lt;a href=&quot;https://www.leadedsolder.com/2020/05/04/casio-pv7-msx-tms-vdp-fix.html&quot;&gt;exorcising from the Casio PV-7 MSX&lt;/a&gt;. It even has the “full” 16kB of VRAM&lt;sup id=&quot;fnref:8kVRAM&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8kVRAM&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;!&lt;/p&gt;

&lt;p&gt;Even as late as 1985, that’s a lot of 8-bit horsepower to throw around; practically a ColecoVision. Although I haven’t tried all of the cartridges yet, it’s not like the Bridge Companion games are really taking advantage of that beefy VDP and CPU:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-advanced-defence.png&quot; alt=&quot;Advanced Defence, being played&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;robert-maxwell-not-required&quot;&gt;Robert Maxwell Not Required&lt;/h2&gt;
&lt;p&gt;I figured it would be a pity not to at least try making the console a little happier. Besides, I haven’t done a really dumb Coleco-adjacent homebrew project since &lt;a href=&quot;/tag/leako&quot;&gt;the last one&lt;/a&gt;. Worst case, I can always port my game &lt;em&gt;to&lt;/em&gt; said ColecoVision, so at least one other person on planet Earth could try it.&lt;/p&gt;

&lt;p&gt;I don’t think I’m the first one to have tried this: it looks like &lt;a href=&quot;https://atariage.com/forums/topic/313772-tms-9129-question/&quot;&gt;cdoty on AtariAge already started tinkering with their BBCBC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What game would be a good candidate to make for the Bridge Companion? Ideally, it should be something that everyone loves, with timeless rules, and a decent amount of challenge. Also, you should be able to play it with the super awkward ATM-like buttons on top of the console. The only answer is &lt;em&gt;Tetris&lt;/em&gt;. Or, to be more specific, a &lt;em&gt;Tetris&lt;/em&gt;-like game.&lt;/p&gt;

&lt;p&gt;Is this a true &lt;em&gt;Tetris&lt;/em&gt;? The Tetris Company would say no: it doesn’t follow the entire ruleset, nor am I paying them any licensing fees. That’s why my game is actually called &lt;em&gt;Bridgetris&lt;/em&gt;, which I think everyone can agree is not at all the same sequence of letters as the one that will get me in legal trouble.&lt;/p&gt;

&lt;p&gt;Now, I must confess. Although I have had a lot of experience programming games, and a lot more programming software in general, I had only very recently made a &lt;em&gt;Tetris&lt;/em&gt;-like game. Like many other projects that I’ve put off forever, it usually comes down to one mental block that turns out not to be a big issue when I actually try to work the problem. For some reason, I had it stuck in my head for decades that the piece rotation code is actually very complex and involves a lot of fiddling.&lt;/p&gt;

&lt;p&gt;While programming a &lt;em&gt;Tetris&lt;/em&gt;-like game in Rust as part of a pandemic boredom relief exercise, I discovered that, in fact, “piece rotation” in most &lt;em&gt;Tetris&lt;/em&gt; games is simply hardcoded. You define a single pivot point and just crank it over. Doesn’t fit? Don’t rotate&lt;sup id=&quot;fnref:wallkicks&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:wallkicks&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;This is that Rust &lt;em&gt;Tetris&lt;/em&gt;-like game, with a screenshot proving it looks somewhat like a dropping-blocks puzzler:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/rust-tetris-clone.png&quot; alt=&quot;The Rust Tetris clone, with a System 7.5 cat background and several falling blocks. The score is 0, the lines are 0, the level is 0, and the next block falling is a 2x2 square.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So, this was going to be easy. All I have to do now is:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Figure out how to get a game console I’ve never seen and don’t actually physically have to run my own code;&lt;/li&gt;
  &lt;li&gt;Write an entire &lt;em&gt;Tetris&lt;/em&gt; game in Z80 assembly language, without having written an assembly program larger than “hello world” since TI-83 Plus programming in high school;&lt;/li&gt;
  &lt;li&gt;Write the blog entry about it. I’m doing that part right now. Hello!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Easy!&lt;/p&gt;

&lt;h2 id=&quot;tricking-your-companions&quot;&gt;Tricking Your Companions&lt;/h2&gt;
&lt;p&gt;On some systems, making it accept a game ROM is just a matter of putting the ROM somewhere that the CPU will jump into when it starts up. That’s the case with the Sega SG-1000, which is easy to program for. As long as your game shows up at &lt;span class=&quot;hex-value&quot;&gt;$0000 &lt;/span&gt;, it will automatically get run when the Z80 CPU comes out of reset.&lt;/p&gt;

&lt;p&gt;However, like the ColecoVision, the BBC Bridge Companion contains a somewhat complex BIOS on an internal ROM. When the Bridge Companion starts up, it’s that BIOS that mounts at &lt;span class=&quot;hex-value&quot;&gt;$0000 &lt;/span&gt;. If the BIOS isn’t satisfied that a cartridge is attached, it will lecture you to turn the system off and insert a cartridge.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-rf-to-av-2.jpg&quot; alt=&quot;The screen reads: &amp;quot;No. Please turn off Companion then plug in cartridge.&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So how do we convince the BIOS that we’re a legitimate cartridge? By reverse-engineering the BIOS. I opened it up in the MAME debugger, stepping through instruction-by-instruction to figure out just what the BBC Bridge Companion was looking for from the ROM on the cartridge.&lt;/p&gt;

&lt;p&gt;After a few evenings of comparing a legitimate cartridge to my nascent homebrew one, I eventually succeeded in building a header that would convince the Companion to run a piece of code that I had written.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-mame-tile-set.png&quot; alt=&quot;A green screen with a purple fuzzy tile in the top left corner. It&apos;s working!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And from there… there was nothing the BBC could do to stop me.&lt;/p&gt;

&lt;h2 id=&quot;writing-the-game&quot;&gt;Writing The Game&lt;/h2&gt;
&lt;p&gt;Even though I’d written the previous game in Rust, there’s a large gulf between a high-level language like Rust and writing something in very low-level assembly language. Assembly is extremely pedantic: you describe each and every small step that the computer needs to undertake, in exacting detail.&lt;/p&gt;

&lt;p&gt;This is great, because it means the computer won’t take any steps you don’t want it to, but it means that you can turn a simple thought in your head (“maybe I should make the blocks animate”) into 500-plus lines of code very quickly.&lt;/p&gt;

&lt;p&gt;Plus, I didn’t really know Z80 assembly language. I had to learn it and all of its fun quirks while also making a game at the same time. Nobody would recommend you live life this way, but it worked surprisingly well. Please don’t look at my code, though. It is a spaghetti mess and about as unoptimized as you can make it.&lt;/p&gt;

&lt;p&gt;As I went through it, I made bugs.&lt;/p&gt;

&lt;p&gt;Lots, and lots, and lots of bugs.&lt;/p&gt;

&lt;p&gt;So many bugs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-tetris-fixed-tall-drop-after.png&quot; alt=&quot;A workable Tetris-like game is shown.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It turns out that assembly language is actually pretty simple and fun! You do have a slight overhead of thinking about things. After I abandoned some of the “rigour” that I expect from working in a higher-level language, and just started doing whatever I wanted, the code started flowing. That’s the real game development lesson, kids.&lt;/p&gt;

&lt;h2 id=&quot;bonus-fixing-the-bridge-companion-i-bought&quot;&gt;Bonus: Fixing the Bridge Companion I bought&lt;/h2&gt;
&lt;p&gt;Somewhere out there is a country where Bridge Companions grow on trees. &lt;em&gt;Sonic the Hedgehog&lt;/em&gt; music plays too slowly, and everything is in black and white there, because of PAL. In times long past, I would have to board a rickety airplane and fly across the world to sup of this bounty.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-boxed.jpg&quot; alt=&quot;The BBC Bridge Companion box, sitting on my chair.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Instead, I just hopped on eBay and spent way too much for a (boxed!) Bridge Companion of my very own.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-slot-damaged.jpg&quot; alt=&quot;The bent pins of the Bridge Companion&apos;s cartridge slot. Insert your favourite &amp;quot;British teeth&amp;quot; joke here.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When it got here, I was disappointed to find out the cartridge slot was mangled, with its pins bent out of shape. Even with a dental pick and some precisely-chosen profanity, I was unable to get a cartridge to read or even insert smoothly, and had to replace the slot. After a few weeks of searching, I managed to bodge an AliExpress substitute into the spot.&lt;/p&gt;

&lt;p&gt;I was finally able to play real games on my real Bridge Companion. Real games, like the one I was writing.&lt;/p&gt;

&lt;h2 id=&quot;discovering-the-real-developers&quot;&gt;Discovering the real developers&lt;/h2&gt;
&lt;p&gt;Although there’s definitely more to be written on this subject, I found out from the packaging that a company named Heber put this hardware together. Imagine my surprise when I was watching an RMC Retro video to find out that &lt;em&gt;they were filming in the old Heber headquarters&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-underside-warning.jpg&quot; alt=&quot;The bottom of the case says: &amp;quot;Designed by HEBER Ltd.&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I quickly reached out, and got ahold of some documents from Richard Horne, who was involved in engineering the thing. In the future, I think it would be worth interviewing him in order to get some more primary source information about the system, but this series of articles is already too late as it is.&lt;/p&gt;

&lt;h2 id=&quot;super-bonus-making-my-own-cartridge-pcb&quot;&gt;Super Bonus: Making my own cartridge PCB&lt;/h2&gt;
&lt;p&gt;Software for the Bridge Companion comes on cartridges, like pretty much any other game system of the 80s. I only had a couple Bridge Companion cartridges lying around, and didn’t want to tear them apart in order to debug my game.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-cartridge-front.jpg&quot; alt=&quot;The original cartridge, containing a ROM and a logic chip.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Most cartridges are simply a ROM chip that holds the program. Because the Bridge Companion cartridges used a common UV EPROM, I already knew the pinout. With my trusty multimeter’s continuity test, I could figure out which pins went to that ROM, and make a working copy of the original cartridge PCB.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-composite-video-test-rom-pcb.jpg&quot; alt=&quot;The purple PCB that I designed, showing a label marked &amp;quot;BBC Screen Test&amp;quot; on it.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I went a little bit further than that, too, but that’s a story for another time.&lt;/p&gt;

&lt;h2 id=&quot;hyper-bonus-figuring-out-a-composite-mod&quot;&gt;Hyper Bonus: Figuring out a composite mod&lt;/h2&gt;
&lt;p&gt;Being an artifact of its time, the Bridge Companion only outputs RF video from an Astec RF modulator. If I lived in the UK in the 80s, this would be really easy: connect the sucker to a switch box, run the forks to the back of my TV’s antenna input, and toggle it on when it’s time for bridge.&lt;/p&gt;

&lt;p&gt;Things are not so easy today: sure, I can plug it into a TV, but there’s a few problems with that. Canada doesn’t have the same channel frequencies, and also the world has changed a lot since terrestrial television ruled the roost in this country. RF did not look great on the Bridge Companion, especially through an adapter:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-rf-to-av-1.jpg&quot; alt=&quot;The RF to AV is showing the &amp;quot;No.&amp;quot; screen in red.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A better option is to use composite video. Even though the yellow plug is also quickly evaporating from new televisions, it’s still the best option to quickly improve the video quality of the Bridge Companion&lt;sup id=&quot;fnref:tms9929&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:tms9929&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-composite-video-mod-assembled-inside-board.jpg&quot; alt=&quot;The PCB is assembled and installed into the BBC Bridge Companion motherboard.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I spent a lot of time poring over scans of schematics – which were provided by the aforementioned Richard Horne at Heber, thank you Richard! – and working out the various levels with an oscilloscope, in the pursuit of the highest-quality mod possible. In the end, though, I just ended up doing something very simple, similar to &lt;a href=&quot;https://www.leadedsolder.com/2021/06/15/famicom-composite-mod-v2.html&quot;&gt;my earlier Nintendo Famicom composite mod series&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bbcbc-working-composite-mod-ac-coupled.jpg&quot; alt=&quot;The AV mod is showing some basic colours in pastel-o-vision.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I think it looks pretty good!&lt;/p&gt;

&lt;h2 id=&quot;whats-coming-up&quot;&gt;What’s Coming Up?&lt;/h2&gt;
&lt;p&gt;Thanks for reading this far on what is basically a glorified executive summary of the project. If you want to come back in the next few weeks to delve into the gory detail of each sub-project, I’d love to have you back.&lt;/p&gt;

&lt;p&gt;The next few chapters are:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;August 4th, 2026&lt;/strong&gt;: Fixing a broken Bridge Companion and making that cartridge PCB for it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;August 11th, 2026&lt;/strong&gt;: Building a composite mod for that Bridge Companion, so I can see it on my TV.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;August 18th, 2026&lt;/strong&gt;: The final chapter! Actually sitting down and programming a &lt;em&gt;Tetris&lt;/em&gt;-like game.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once all the entries are completed, I’ll be releasing the ROM and source code as well. I hope you enjoy reading along with this big project as much as I did working on it.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:8kVRAM&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The TMS99xx video chipset, at least the TMS9918A, is documented as being able to function with 8kB of video RAM instead of 16k. However, I don’t know of any systems that actually shipped in this configuration, and I don’t think I’d want to build one. &lt;a href=&quot;#fnref:8kVRAM&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:wallkicks&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Yes, I know that &lt;a href=&quot;https://tetris.wiki/Super_Rotation_System&quot;&gt;in many &lt;em&gt;Tetris&lt;/em&gt; games&lt;/a&gt;, rotating into a wall forces you to be pushed out, a phenomenon known as “wall kicking.” That’s not part of the ruleset of this game, please come back for &lt;em&gt;Bridgetris The BBC Master&lt;/em&gt;. &lt;a href=&quot;#fnref:wallkicks&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:tms9929&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Being a TMS9129, the Bridge Companion actually generates YCbCr (conventionally “component,” but the levels are different here) video internally, and converts it to single-channel composite before handing it to the RF modulator to be turned into “television.” An RGB mod of a Bridge Companion is not infeasible, nor would it be especially hard, modders! &lt;a href=&quot;#fnref:tms9929&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="console" /><category term="homemade-software" /><category term="homemade-hardware" /><category term="bbc" /><category term="bbc-bridge-companion" /><category term="reversing" /><category term="magic-smoke" /><category term="repair" /><summary type="html">Ever done dumb stunts for your friends on a dare? I was hanging out with a bunch of fellow old-computer ne’er-do-wells when the topic of the BBC Bridge Companion came up. It was a useless thing to own, they said, although many of the assembled group had already owned one at some time or another. If you don’t know how to play bridge, then why even bother? Well, I know how to play Tetris, and I know how to write Z80 assembly, or at least I thought I did.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/bbcbc-hero.jpg" /></entry><entry><title type="html">400-Line’s Just Fine For A Party</title><link href="https://www.leadedsolder.com/2026/07/07/fujitsu-mb27331-crt-monitor-pickup-pc9801uv.html" rel="alternate" type="text/html" title="400-Line&apos;s Just Fine For A Party" /><published>2026-07-07T00:00:00+00:00</published><updated>2026-07-07T00:00:00+00:00</updated><id>https://www.leadedsolder.com/2026/07/07/fujitsu-mb27331-crt-monitor-pickup-pc9801uv</id><content type="html" xml:base="https://www.leadedsolder.com/2026/07/07/fujitsu-mb27331-crt-monitor-pickup-pc9801uv.html">&lt;p&gt;When Suruga-ya gave me a really good deal – with free shipping to Canada – on a CRT monitor, I had to take it. Only once it arrived did I realize this monitor was a lot weirder than I thought it was, and now I have to find a way to actually make use of the thing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/fm77-mb27331-monitor.jpeg&quot; alt=&quot;The MB27331 as it arrived, sitting on top of my FM77AV2.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This monitor reminded me of the glory days of buying crap from Japan, where you could get yellowed hardware for pennies on the dollar because it was aesthetically unpleasant. Certainly this qualifies as “yellowed;” even though it came with its original Fujitsu shipping box, indicating someone really cared for it, the back of this CRT has probably spent a lot of time basking in the hot sun.&lt;/p&gt;

&lt;p&gt;If I’m honest, I bought this because it was a good deal, and tried to figure out a use for it later. As I said up above, Suruga-ya let me buy this monitor for ¥3000, with free express shipping to Canada. I’m sure they’re very mad at me, but it’s my understanding that disposing of CRTs in Japan is not free, and perhaps they lost less money mailing it to me than they would have by simply disposing of it.&lt;/p&gt;

&lt;p&gt;My original plan was to use this with my second &lt;a href=&quot;/tag/fm77av2&quot;&gt;Fujitsu FM-77AV2&lt;/a&gt;. Although its beige-office vibes don’t really match the styling of the sleek black wonder-computer, it has its own charm. I can’t resist a monitor with an anti-glare shield and a silkscreened logo on the glass.&lt;/p&gt;

&lt;p&gt;Unfortunately, it turns out that this monitor is not compatible with the FM-77AV2. After a few months of it sitting in my basement, I finally cleared enough desk space to go and test it. First, though, I checked the FM-7 Museum website, which told me that &lt;a href=&quot;https://fm-7.com/museum/products/z6evz2x2/&quot;&gt;the MB27331 is a fixed-sync “400-line” monitor&lt;/a&gt;. That means it’s only compatible with &lt;a href=&quot;https://hachibitto.com/jvcc/makers/view_model.php?id=86&amp;amp;makerid=3&quot;&gt;FM-77L4s&lt;/a&gt;, and original FM-77s equipped with a 400-line card. The AV2, despite being a newer computer, does not support such a high resolution, and neither do any of the successor AV models.&lt;/p&gt;

&lt;p&gt;I also found a scan of a brochure telling me that &lt;a href=&quot;https://poplars.world.coocan.jp/sx/photo/pc/FM-16%CE%B2.pdf&quot;&gt;the MB27331 was sold alongside the Fujitsu FM-16β&lt;/a&gt;, where it is described as an alternative to &lt;a href=&quot;https://fm-7.com/museum/products/kkvtr58v/&quot;&gt;the more expensive, but similarly “high resolution,” MB27333&lt;/a&gt;. I’m not sure what the differences are between those monitors; maybe the ‘33 has a finer dot pitch or more inputs.&lt;/p&gt;

&lt;p&gt;Okay, that’s enough market research. Let’s see if this thing can emit a phosphor. &lt;em&gt;Something&lt;/em&gt; in my voluminous pile of junk ought to be able to drive it…&lt;/p&gt;

&lt;h2 id=&quot;get-to-the-source&quot;&gt;Get to the source&lt;/h2&gt;
&lt;p&gt;I went looking for a video pinout for the FM-77L4, just in case it was wildly different from the FM-77AV2. Quickly, I found &lt;a href=&quot;https://haserin09.la.coocan.jp/difference.html#DISPLAY&quot;&gt;this great site covering the differences between the FM-7 and FM-77, which includes a comparison of monitor pinouts&lt;/a&gt;. It turns out that this monitor has an “intensity” pin, which means it’s a sixteen-colour 640x400 monitor. Very fancy!&lt;/p&gt;

&lt;p&gt;Now, I didn’t push all that carbon into the atmosphere and pull all that shipping cost out of Suruga-ya’s coffers not to use this fancy monitor. I remembered that some of my beloved NEC PC-98s have 8-colour digital RGB, &lt;a href=&quot;https://radioc.web.fc2.com/column/pc98bas/pc98crts_en.htm&quot;&gt;and PC-98s also run 640x400 in 24kHz mode&lt;/a&gt;. That ought to be pretty close to the frequency this monitor expects, if not dead on.&lt;/p&gt;

&lt;p&gt;Because the monitor is reported to expect TTL digital input, I chose a PC-98 that still has digital output. It’s not a perfect match, because, again, PC-98 digital output is only 8-colour, and this monitor supports 16 glorious tones. I chose my (second) PC-9801UV&lt;sup id=&quot;fnref:uv-specs&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:uv-specs&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, which you haven’t seen yet on the blog, mostly because my PC-9801RA was happily ensconced in my desk and not willing to come out of its cozy nest.&lt;/p&gt;

&lt;p&gt;A quick consultation of &lt;a href=&quot;https://archive.org/details/pc-9801-uv-21-owners-manual&quot;&gt;the PC-9801UV’s user manual&lt;/a&gt; gave me the video pinout that I needed to wire the male end of the adapter:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pc9801uv-video-pinouts.png&quot; alt=&quot;The pinout for the PC-9801UV&apos;s digital colour output and monochrome video outputs, from the owner&apos;s manual.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I rummaged through my bin of cable parts, and made up an adapter cable from the 8-pin DIN digital output on the early PC-98s to the female DE9 that the MB27331 expects to connect to&lt;sup id=&quot;fnref:side-bonus&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:side-bonus&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. The screw-terminal breakout here is the usual female DE9 part that you’ve seen in other posts; I was happy to see that the pin numbers on the PCB actually matched the correct pin numbers instead of just reusing the male PCB.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/mb27331-monitor-on-pc9801uv-shonky-adapter.jpg&quot; alt=&quot;A homemade adapter. A screw-terminal breakout box has a bunch of wires in it coming from a DIN cable. Some of the unused DIN wires are insulated from shorting with green masking tape. The braided shield part of the cable is dangling loose in space.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The masking tape is there in order to cover up the unused wires leading from the PC-98. One of them is a +12V line, which I didn’t want shorting to anything in the vicinity. The dangling shield jacket could also potentially have caused problems, but very few monitors &lt;em&gt;need&lt;/em&gt; the shields connected in my experience, and there are some dedicated ground pins anyway.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/mb27331-monitor-on-pc9801uv.jpg&quot; alt=&quot;The Fujitsu monitor is sitting on top of the PC-9801UV.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I think we can all agree that this monitor looks really good on this computer.&lt;/p&gt;

&lt;h2 id=&quot;pc-98-not-it&quot;&gt;PC-98 Not It?&lt;/h2&gt;
&lt;p&gt;After crossing my fingers, I fired the monitor up with the PC-9801UV running. As the set warmed up, I eventually got some raster with a stable vertical position, but the video was unintelligibly smeared horizontally.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/mb27331-monitor-on-pc9801uv-running-smeared.jpg&quot; alt=&quot;The N88-BASIC(86) &amp;quot;how many files&amp;quot; prompt is completely unreadable on the monitor. It&apos;s nice and bright though.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I remembered that the monitor has a horizontal hold setting on the back, so I grabbed my ceramic adjustment tool and went to town. I could get it &lt;em&gt;close&lt;/em&gt; to legible on the topmost text line (albeit shifted 2/3rds to the right,) but the bottom row of key help was still a smeary mess, and the pot was extremely sensitive to any adjustment in that range. It seemed impossible to get any closer, and even if I did, the video was still offset quite a bit horizontally. Some tweaks to the horizontal position didn’t seem to make any difference.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/mb27331-monitor-on-pc9801uv-garbage-basic.jpg&quot; alt=&quot;I am trying to write some BASIC but every line keeps moving further to the right.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I went to toggle the resolution DIP-switch on the front of the PC-9801UV, which changes it between 15kHz and 24kHz, but nothing changed. Wait, something should have changed.&lt;/p&gt;

&lt;h2 id=&quot;pc-98-is-it&quot;&gt;PC-98 &lt;em&gt;Is&lt;/em&gt; It&lt;/h2&gt;
&lt;p&gt;Did I actually wire this adapter properly?&lt;/p&gt;

&lt;p&gt;As soon as I touched the adapter, the wire for pin 8 fell out. That’s the horizontal sync pin. Oops. I twisted it up again and stuck it into the terminal block. Terminal blocks are not really the appropriate connector for a wire this thin; unfortunately it was this or run off a PCB.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/mb27331-monitor-on-pc9801uv-legible-basic.jpg&quot; alt=&quot;The &amp;quot;How many files&amp;quot; prompt is now totally legible. Everything is in perfect focus.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Holy cow! It works. Focus is sharp, and linearity looks perfect to my eye. There is this weird vertical turquoise line with sporadic dots inside it, and the video keeps throbbing and wiggling. Maybe the monitor needs caps, or this is something like the PC-98 not shutting off the colour while flying back that the Fujitsu doesn’t like?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/mb27331-monitor-on-pc9801uv-convergence-test.jpg&quot; alt=&quot;I forgot which letter to use for a convergence test, so it&apos;s a screen full of capital Hs.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I tried moving the line off the edge using the horizontal hold, but as soon as it fell off the edge the monitor lost sync and freaked out. Playing with the horizontal position and horizontal hold together was not able to move it far enough away either.&lt;/p&gt;

&lt;p&gt;Since I already had N88-BASIC working, I wrote a simple program to test colour:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-basic&quot;&gt;10 for i = 0 to 7
20 line(i * 80, 0)-(i * 80 + 79,399),i,bf
30 next i
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In N88-BASIC, and probably other Microsoft BASICs of the era, you can &lt;a href=&quot;https://radioc.web.fc2.com/column/pc98bas/n88func/line.htm&quot;&gt;draw a rectangle by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LINE&lt;/code&gt; instruction&lt;/a&gt; to specify the upper left and bottom right corners of the rectangle and specifying the option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BF&lt;/code&gt; is “box, filled,” as you can see below.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/mb27331-monitor-on-pc9801uv-displaying-dim-colour.jpg&quot; alt=&quot;Eight fairly dim colours are shown across the screen with the aforementioned BASIC program superimposed on them.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A rainbow of possibilities. And in the right order, too! Somehow I failed to mix up red and blue this time. Go figure I got it right on the throwaway video adapter…&lt;/p&gt;

&lt;p&gt;As I suspected, these colours are fairly dim, because I’m not driving the aforementioned Intensity line of the monitor. Unfortunately, the PC-98 only provides +12V for a power output on its video pinout, and I’m willing to bet this monitor will not appreciate being fed that. Of course, that can be regulated down to a nice +5V in order to provide a pull-up on the intensity line, but let’s first figure out what’s going on with this ugly vertical line artifact, and all this dreadful &lt;em&gt;wobbling&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;mb-stands-for-more-bfixing&quot;&gt;MB stands for More Bfixing?&lt;/h2&gt;
&lt;p&gt;Although I’m disappointed it wasn’t perfect immediately, I’m not particularly surprised that a nearly forty-year-old monitor might need a little bit of maintenance. Who knows how many hours of excitement this thing has seen in its long life before getting crammed into a box and sent to Canada?&lt;/p&gt;

&lt;p&gt;For now, the monitor has gone back in its box – I need the desk room for another broken project – but I am pumped to open it up and recap it soon. Kudos to Fujitsu for making a cool monitor.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:uv-specs&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Since it’s the first time on the blog, despite many years of failed UV-related projects, I’ll give a quick capsule history of the model. The PC-9801UV is a much earlier PC-98 than the PC-9801RA; it’s from 1986 and has &lt;a href=&quot;https://www.cpu-world.com/CPUs/V30/?&quot;&gt;an NEC V30 CPU&lt;/a&gt; (8086 equivalent, sort of) running at up to 10MHz. It also has built-in -26 FM sound and the GRCG “Graphics Charger,” making it a great option to run early games on. I’d argue that it can be considered the first “modern” PC-98, although the successor model, VX, added the “Enhanced Graphics Charger” (EGC) that most games expect, so it depends on your definition of modern. &lt;a href=&quot;#fnref:uv-specs&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:side-bonus&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;As a side bonus, the video pinout of the PC-98’s digital RGB port is shared by a bunch of other Japanese computers, including my beloved NEC PC-8801mkII, which can also do a “400” line display by scan-doubling its 200-line internal resolution. &lt;a href=&quot;#fnref:side-bonus&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name></name></author><category term="fujitsu" /><category term="computer" /><category term="crt" /><category term="mb27331" /><category term="pickups" /><category term="homemade-hardware" /><summary type="html">When Suruga-ya gave me a really good deal – with free shipping to Canada – on a CRT monitor, I had to take it. Only once it arrived did I realize this monitor was a lot weirder than I thought it was, and now I have to find a way to actually make use of the thing.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.leadedsolder.com/assets/fm77-mb27331-monitor.jpeg" /></entry></feed>