HomeAbout MeProjectsContactMeow

My Second M68k SBC

12 Apr 2025

A few years ago, when I was in university, I designed a single-board computer for fun. It was based on the Motorola 68000 CPU, and I wanted it to be kind of like the TRS-80 Model 100. This meant it had a built in keyboard and a 240x64 LCD screen.

This computer technically worked, but it had lots of problems. Most of these problems related to the stability of the machine, but there were also a few other minor bugs that were annoying. This project sat in my basement for a few years until I dug it out last Christmas and started playing with it again. I was able to solve some bugs (like the debug circuitry not working properly) but determined other problems would need a full respin of the board.

Fast forward to late January and I decided I was going to design a new one from scratch. This new board would fix the bugs on the original. I also decided to remove the onboard keyboard, and replace the glue logic with a CPLD. I've never used a CPLD before, so I was excited to learn about that.

The Board

I designed this board in about a day. It's much smaller than my first design; the lack of a keyboard and 7400-series glue logic saves a lot of space. It sports the following features:

From left to right, there's the following:

Due to my own incompetence it took about a month to get all the parts to assemble the board. Once everything I arrived, I slowly started populating the board and testing things out. I started with the CPLD, because it's critical to the whole system and also what I knew the least about.

The CPLD

I chose to go with the ATF15xx series of CPLDs, for one simple reason: they're the only CPLDs that support 5V and are still in production. Unfortunately, they're also quite old, and getting the toolchain running was a bit of a challenge. I followed the guides in this GitHub repository and got something that worked. I had to write the firmware in an HDL called WinCUPL. It sucks even more than most HDLs, plus there's no syntax highlighting for it in Emacs. I also found that certain code errors would cause the firmware to not get synthesized but it wouldn't throw a compiler error. I wasted a bunch of time trying to figure out why my code changes weren't causing behaviour changes before I figured this out. Shortly after, I updated my Makefile to blow away previous artifacts on every build, so that at least I'd know if an artifact wasn't created.

Aside from the software, the hardware was also challenging. Turns out my board's JTAG pinout is completely wrong (oops! Friendly reminder to always verify your footprints). I used jumpers to remedy this, but then I found out my JTAG programmer didn't work either. I ended up going through three programmers before I realized the stock firmware doesn't work properly. I was able to flash some open source firmware onto mine and then they both worked flawlessly! I say "both" and not "all" because the third programmer I bought was an FTDI232H which worked out-of-the-box.

I probably wasted around a week and a half getting all of this working. Eventually, though, I was able to blink an LED by dividing an 8MHz clock signal using a bunch of D flip-flops in the CPLD. I don't think I've been this excited by a blinking LED in years!

Once I got the CPLD working, it was a pretty straightforward, but tedious, process of bringing up the rest of the board. I gradually soldered more parts on, and verified everything worked correctly. Soon, I was able to write some simple software.

Board rework

Fairly early on I found 2 major problems with the board. Due to a misunderstanding of KiCad I connected the wrong address pins to the CPLD, meaning there was no way for the address decoder to function. I also neglected to connect any data pins to the CPLD. I would need these to handle interrupts properly.

Luckily, the CPLD had enough free pins that I could just run a bunch of jumpers. It was tedious getting everything soldered, but overall it wasn't too bad.

After everything was attached, I applied a generous amount of hot glue to protect everything.

Software Development

As with my first SBC, one of the first programs I wrote was designed to blink the address lights.


     .global _start
 	
     .long 0x140500        /* stack pointer */
     .long _start
 
     .org 0x400
 
 _start:
     /* Disable interrupts */
     ORI.W #0x700, %sr
     JMP LOWER_LOOP
 
     .org 0x1000
 LOWER_LOOP:
     ADD.L #1, %d0
     CMP.L #200000, %d0
     JNE LOWER_LOOP
     EOR.L %d0, %d0
     JMP UPPER_LOOP
 	
     .org 0x2000
 UPPER_LOOP:
     ADD.L #1, %d0
     CMP.L #200000, %d0
     JNE UPPER_LOOP
     EOR.L %d0, %d0
     JMP LOWER_LOOP

I didn't have working interrupts when I wrote this, so I'm using busy loops to keep track of time. The idea is to flip-flop between a loop at 0x1000 and 0x2000, such that the address lights toggle back and forth. And it worked great:

Improving development UX

The development process was very tedious. I would need to pop out the ROM chips from the board and reprogram them every time I wanted to change the code. This was also putting quite a bit of strain on the chip holders; they were rapidly falling apart.

I decided to write a memory monitor, so that I could write programs directly to RAM over serial. I called it Meowmon; its interface is similar to Wozmon. It lives here. I wrote it entirely in M68k assembler, largely while falling asleep on the subway late at night.

A memory monitor allows me to peek at sections of memory, poke bytes, and jump to a specific location. It allows me to write in a program and then execute it in RAM, without needing to physically remove anything from the board. I gave the computer the ability to write to its own ROM, so you can even use Meowmon to change itself.

I also wrote a flasher program, located here. This allows taking a binary file and uploading it to Meowmon over serial. Then it's just a matter of typing 'R' to jump to the new code.

With these new programs, my development cycle had gone from a tedious few minutes to a very easy few seconds. I was ready to start tackling some more complex software.

Mandelbrot

I was really getting into the flow at this point so I decided to write a Mandelbrot generator. I was still working in assembler, of course, plus the M68k doesn't have hardware floating point support, so I did everything with integers. I also keep track of everything in registers for additional speed. The result is I was able to generate the following in just a few seconds:


                                                           @
                                                        @@@@@@
                                                        @@@@@@
                                            @            @@@@
                                              @@    @@@@@@@@@@@@@@@
                                               @@@@@@@@@@@@@@@@@@@@@@@@@@
                                            @@@@@@@@@@@@@@@@@@@@@@@@@@@@
                                          @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                                          @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                           @@   @         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                           @@@@@@@@@@    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                          @@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                      @@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                      @  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                          @@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                           @@@@@@@@@@    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                           @@ @ @         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                                          @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                                          @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
                                             @@@@@@@@@@@@@@@@@@@@@@@@@@@@
                                              @@@@@@@@@@@@@@@@@@@@@@@@@@@
                                              @@    @@@@@@@@@@@@@@@
                                                         @@@@
                                                        @@@@@@@
                                                        @@@@@@
                                                           @

Once satisfied with the ASCII Mandelbrot, I wrote a display driver and rendered it there instead:

The Mandelbrot code lives here.

Cross-compiling

Now happy that the computer works well, I decided to upgrade to a high-level language: C! I'd need a cross-compiler that runs on x86 and spits out M68k binaries.

Getting the cross-compiler running should have been straightforward - I'm on NixOS so it should have just been a matter of installing it. There are some bugs here though, which required some changes to nixpkgs (thanks K900!). I'm installing the cross-compiler from their nixpkgs fork, as you can see here.

I was unable to get the cross-compiler working properly. The binary it spits out is almost valid, but some of the helper functions use Motorola 68030 instructions. The most glaring example is spitting out jumps with 32-bit offsets, which the Motorola 68000 doesn't support. Again, this only affects helper functions, not my code. My theory is that the helper functions are pre-compiled and default to the 68030.

Even though I wasn't able to get this working, I made a huge amount of progress thanks to Won at RC, and K900 in the NixOS support channels on Matrix. Thanks to the both of you for tolerating my excessive questions and helping me get close to the bottom of this!

Next Steps

I have many more plans for this board. Getting the cross-compiler working is pretty high on the priority list. I also want to get the board booting off an IDE hard drive. This would involve writing a simple driver and some firmware that runs in ROM.

Aside from that, I should probably do a board respin. I need to fix the interrupts properly and fix the pinout of the JTAG connector. I'd also like to make the LEDs dimmer - they're distractingly bright. I'm debating putting Ethernet on the board as well. Initially I was going to make this an expansion card, but if I'm respinning the board anyway, maybe I should just add it.

Everything related to this project lives in a single monorepo.