Mackerel 68k Computer

A 68000 series computer system designed to run Linux

Similar projects worth following
The Mackerel 68k is my series of home-built computers based on the Motorola 68000 family. I am building it from the ground up in phases starting with the baby of the family, the 68008. As my understanding and experience with the system improves, I plan to add additional functionality and support for higher-end CPUs in the 68k line-up.

Here's an outline of the major project goals:

- [x] Build the simplest usable computer with a 68008 CPU, ROM, RAM, and a serial port
- [x] Expand hardware to meet uClinux requirements - timer interrupt, more RAM
- [x] Port uClinux in any form and boot it to an interactive shell
- [ ] Design and manufacture a single-board PCB of the initial 68008 computer
- [ ] Expand the initial design to use a 68020, add persistent storage, networking, possibly DRAM
- [ ] Build a final revision using the 68030 - run full Linux, not just uClinux


This is the first (and as of now, only) iteration of Mackerel. Using the 52-pin PLCC version of the Motorola 68008, it has a 4MB address space and runs uClinux 2.0.

The hardware includes the CPU, 512KB of ROM, up to 3.5MB of RAM (2MB typically installed), and a XR68C681 DUART chip for timer and serial port. It currently runs at 10 MHz.

Address decoding and assorted glue logic is done by a handful of ATF16V8B PLD chips.

Programs can be compiled and copied into RAM over the serial port. The bootloader is responsible for this transaction and for starting the loaded programs. uClinux also just barely fits in a single ROM chip and can be started directly by the bootloader without the need for a serial transfer.

The first build of this computer uses a custom 80-pin backplane and a combination of PCBs and hand-wired component boards. A single-board PCB version is in the works.

  • 1 × Motorola MC68008FN8 PLCC-52 version of the 68008 CPU
  • 1 × SST39SF040 512 KB ROM 512 KB ROM
  • 7 × AS7C4096 512 KB SRAM 3.5MB total SRAM
  • 1 × XR68C681 Modern CMOS implementation of the classic 68681 DUART
  • 5 × ATF16V8B PLD Address decoding and glue logic

  • Linux In A Half Meg Of ROM

    Colin Maykish4 days ago 0 comments

    My port of the uClinux 2.0 kernel is now bootable from a single 512KB ROM. For active development, it's still easier to use the serial loader to get Linux into memory, but having the kernel and filesystem in ROM makes for a nice self-contained demo. It's also "instant"-on, or at least it's one command to boot Linux now instead of the back and forth of the bootloader and serial transfer program.

    Although, the kernel fits in ROM, there's very little room to spare. There are 524288 total bytes in the EEPROM and the allocation looks like this:

    Bootloader: 8192
    Linux text: 256176
    Linux data: 27696
    ROM fs:     231424
    Total:      523488 

    524288 - 523488 = 800 bytes to spare

    To be fair, this does include Colossal Cave Adventure which feels like a necessary component to me.

    Because I'm using the 52-pin variant of the 68008, I have 4 MB of address space to play with. There's nothing stopping me from adding more ROM, but that approach will only go so far. Linux at idle is only using about 200k of the 2MB of RAM currently installed, but that number is sure to go way up with more applications or a newer kernel. At some point I'll need to maximize the amount of RAM available and load Linux from external storage anyway, but I'm very happy that Mackerel is now self-contained. Connect power and serial and it's a Linux machine.

    Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne
    KERNEL -> TEXT=0x302000-0x33b3b8 DATA=0x000400-0x007030 BSS=0x007030-0x01543b
    KERNEL -> ROMFS=0x3474e0-0x37f920 MEM=0x015440-0x1fc000 STACK=0x1fc000-0x200000
    Mackerel 68k support (C) 2024, Colin Maykish
    Calibrating delay loop.. ok - 1.27 BogoMIPS
    Memory available: 1908k/2031k RAM, 0k/0k ROM (2804k kernel code, 124k data)
    Swansea University Computer Society NET3.035 for Linux 2.0
    NET3: Unix domain sockets 0.13 for Linux NET3.035.
    uClinux version 2.0.39.uc2 (colin@crm-x1-g6) (gcc version 2.95.3 20010315 (release)(ColdFire patches - 20010318 from XIP and shared lib patches from 15 Sat Jul 13 07:27:58 PM EDT 2024
    XR68C681 driver version 0.1 by Colin Maykish
    ttyS0 at 0x00000000 (irq = 1) is a XR68C681
    Ramdisk driver initialized : 16 ramdisks of 4096K size
    Blkmem copyright 1998,1999 D. Jeff Dionne
    Blkmem copyright 1998 Kenneth Albanowski
    Blkmem 1 disk images:
    0: 3474E0-37FCDF (RO)
    VFS: Mounted root (romfs filesystem) readonly.
    Shell invoked to run file: /etc/rc
    Command: hostname mackerel-68k
    Command: /bin/expand /etc/ramfs.img /dev/ram0
    Command: mount -t proc proc /proc
    Command: mount -t ext2 /dev/ram0 /var
    Command: mkdir /var/tmp
    Command: mkdir /var/log
    Command: mkdir /var/run
    Command: mkdir /var/lock
    Command: cat /etc/motd
      __  __            _                 _       __   ___  _    
     |  \/  |          | |               | |     / /  / _ \| |   
     | \  / | __ _  ___| | _____ _ __ ___| |    / /_ | (_) | | __
     | |\/| |/ _` |/ __| |/ / _ \ '__/ _ \ |   | '_ \ > _ <| |/ /
     | |  | | (_| | (__|   <  __/ | |  __/ |   | (_) | (_) |   < 
     |_|  |_|\__,_|\___|_|\_\___|_|  \___|_|    \___/ \___/|_|\_\
    Execution Finished, Exiting
    Sash command shell (version 1.1.1)
    /> free
            total:    used:    free:  shared: buffers:  cached:
    Mem:   1953792   208896  1744896        0    77824        0
    Swap:        0        0        0

    I'm part way through a PCB design for this system and I've been debating how complicated I want to get with it. The feature-creep is real, but I think I will keep the SBC to a fairly modest configuration - 1MB of ROM, 2MB of RAM, and the 68C681 DUART. As cool as it is to run Linux in any form on this thing, the ancient 2.0 kernel and similarly-aged applications are fairly limited in what they can actually do.

    Once the PCB goes off to manufacturing, I'd like to pick up my work on uClinux 4.4. I'm hopeful that a much newer kernel and userspace will result in a Linux experience more similar to a modern PC, but I don't know that it will function within the 4 MB address space limits of the 68008. Maybe the 68EC000 or 68SEC000 in 8-bit mode would be close to a drop-in upgrade boosting the address space to 16 MB.

    Either way,...

    Read more »

  • Yes, it runs Linux

    Colin Maykish06/20/2024 at 22:55 1 comment

    TL;DR: Mackerel now runs Linux! Updated pictures of the hardware and a quick video demo below.

    After struggling with my port of ucLinux 2016 for long enough, I decided I needed a clean slate. Steve Chamberlin's 68 Katy is one of the more famous examples of a homebrew 68008 machine on the internet and he managed to run ucLinux, albeit a version from 2004 with the v2.0 of the Linux kernel. I found a clean copy of the same ucLinux release he used, and with his modifications as reference, I was able to get Mackerel booting as well. This still managed to take me about a week and a lot of trial and error, but having Steve's known good configuration definitely made for an easier process than working completely blind like I was in the 2016 version.

    My hardware differs enough from the 68 Katy that quite a bit of porting still had to happen, but before all that I had to find a toolchain that would actually build a 20+ year old Linux kernel. I learned the hard way that although you can build your own toolchain from source, that may not be th best idea in this case. There's a 32-bit version of the m68k-elf-tools from 2003 that ended up working for me on my modern Debian 12 machine. I did need to enable the i386 repo and install the i386 version of libc and libgcc, but otherwise the ancient gcc 2.x cross-compiler ran perfectly. Backwards compatibility is amazing.

    There are three basic things Linux needs to boot and run: a periodic interrupt for task switching, a serial console, and a file system. I have two XR68C681 DUART chips attached to the system bus. One of them is providing this periodic interrupt function, the other is the kernel's main serial console. I hacked my way through a Linux serial driver and ended up with a kernel image and a romfs filesystem image. In theory these can be combined into one piece and loaded together into RAM or ROM, but I was not able to get this working. My solution is to load the kernel into RAM at address 0x200000 and the filesystem at 0x300000 and just hardcode the filesystem code to look for it there. This is ugly and is on the short list for cleanup tasks, but it works. You can see the bootloader loading the filesystem and kernel to these addresses in the video.

    The other "hack" in place is the memory map itself. Mackerel actually has 3.5MB of SRAM from addresses 0x00 to 0x380000 with peripherals and the bootloader ROM mapped above that. For simplicity, the Linux kernel thinks that it actually has 2MB of RAM starting at 0x00 and that the rest of the RAM is ROM. These are all implementation details and should be cleaned up, but I'm not worried about it too much at the moment.

    I'm still working on getting a more modern kernel running on this hardware, but this is a great milestone and an even better reference for future porting work. My immediate plans are to do a little bit of cleanup and documentation and then think about designing a single-board PCB of this hardware configuration. Longer term I'm still toying with the idea of jumping up to a 68030 for that sweet MMU, but for now I'm going to go play some Colossal Cave Adventure!

    Links for reference:

    Mackerel 68k project on Github:

    ucLinux port on Github:

    Toolchain I used to build:

  • Sometimes You Have To Rebuild Everything

    Colin Maykish09/22/2022 at 02:03 1 comment

    Second handmade revision of Mackerel on a backplane

    The rat's nest of wires I've been calling the Mackerel 68k has been completely rebuilt. It's now a slightly smaller rat's nest, but it's on a much nicer backplane with more thought given to the bus pinout, power delivery, and noise reduction.

    I have a few 8-bit computer designs in various states of development, so I built a backplane I could use for all of them.

    New backplane for 8-bit computers

    The big updates are more power and ground pins dispersed throughout the bus, a 4-layer PCB with solid power/ground planes internally, and a MCP2221A USB-serial converter built in.
    Since the pinout of the backplane is completely different from the first iteration of Mackerel, I rebuilt the component boards as well. The RAM/ROM board design is unlikely to change, so I committed it to a PCB. I'm actually using one and three-quarters of these boards for a total of 3.5MB of RAM. The remaining 512KB of available address space is used by the ROM and the serial port.

    2 MB SRAM, 512KB ROM with programmable address decoder

    The CPU is still the same 52-pin 68008, but the address decoder and glue logic has moved to an FPGA. This is ridiculous overkill, but I wanted to rule out some stability issues that I thought may have been caused by the EPM7064 CPLD I was using previously. Since the Upduino is a 3.3v part, I connected it to the system bus via 74HC245 bus transceivers acting as level shifters.

    Finally, I replaced the 68901 and the classic 68681 with a single XR68C681. The MFP was working alright as a system timer, but the serial port was just too slow. The 68681 can technically run at 115200, but requires a weird hack involving the timer circuit to generate the right clock. The XR68C681 has native support for 115200 baud without resorting to hacks, leaving the timer free to use as a periodic interrupt. It does this beautifully.

    On the programmable logic side, I simplified the address decoding and removed all of the DTACK generation logic (I just grounded the pin for now). All of my components are capable of running at the 8 MHz supported by the CPU without any wait states, so this just makes things simpler.

    The rebuild was a huge success. I spend some time updating my Linux kernel code to use the new DUART for serial output and the system timer, compiled, and loaded the kernel into RAM over the serial port. On the first boot, I got farther than I ever did on the old system.

    Still getting an error, but it's a new and exciting error:

    Jumping to 0x8000
    5Linux version 4.4.0-uc0 (colin@ada) (gcc version 11.3.0 (GCC) ) #3 Wed Sep 21 20:42:55 EDT 2022
    6Herring-8 68k support by Colin Maykish 
    6Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne
    7On node 0 totalpages: 888
    7free_area_init_node: node 0, pgdat 000e8064, node_mem_map 000ff100
    7  DMA zone: 7 pages used for memmap
    7  DMA zone: 0 pages reserved
    7  DMA zone: 888 pages, LIFO batch:0
    7pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
    7pcpu-alloc: [0] 0 
    6Built 1 zonelists in Zone order, mobility grouping off.  Total pages: 881
    5Kernel command line: 
    6PID hash table entries: 1024 (order: 0, 4096 bytes)
    6Dentry cache hash table entries: 1024 (order: 0, 4096 bytes)
    6Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
    6Memory: 2480K/3552K available (787K kernel code, 30K rwdata, 80K rodata, 36K init, 41K bss, 1072K reserved, 0K cma-reserved)
    5Virtual kernel memory layout:
        vector  : 0x00000000 - 0x00000400   (   1 KiB)
        kmap    : 0x00000000 - 0xffffffff   (4095 MiB)
        vmalloc : 0x00000000 - 0xffffffff   (4095 MiB)
        lowmem  : 0x00008000 - 0x00380000   (   3 MiB)
          .init : 0x000e9000 - 0x000f2000   (  36 KiB)
          .text : 0x00008000 - 0x000cced0   ( 788 KiB)
          .data : 0x000cced0 - 0x000e8a00   ( 111 KiB)
          .bss  : 0x000f2000 - 0x000fc568   (  42 KiB)
    6clocksource: timer: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1911260446275000 ns
    6Calibrating delay loop... 0.69 BogoMIPS (lpj=3459)
    6pid_max: default: 32768 minimum:...
    Read more »

  • Planning Ahead: Freerunning MC68030

    Colin Maykish08/24/2022 at 14:14 1 comment

    I've been slowly making progress toward booting Linux on the 68008 and I have some updates to share there soon, but in the meantime, I wanted to start planning for the future. As part of a recent PCB order, I made a breakout board for the 68030 CPU. I already have a pair of the processors in my collection, but no good way to to test them. Wiring up 100+ pins by hand is not my idea of fun, so I put together this simple design:

    It's hard to get much simpler. This board breaks out all the (useful) control lines and the address and data bus. Combined with a big pile of jumper wires, it's a useful test to show that my CPUs actually work. I could not find any sockets that perfectly match the 68030 footprint, so I used single row machined pin headers packed together. The CPU pins fit nicely in these and they can be arranged in a grid pattern.

    Here's a quick video of the 68030 freerunning:

  • Moving Past Memory Issues

    Colin Maykish08/07/2022 at 19:41 8 comments

    After spending many hours building various combinations of kernel versions and configurations, I've finally got Mackerel progressing past the memory initialization errors. I tried 6 six different kernels, multiple compiler versions, and every config setting I could find. Almost all of those build combinations produced the same result: either crashes in the allocator code or tons of page state errors on the console.

    Eventually I got the idea that maybe the kernel and the config were fine and something else was wrong. I still wasn't convinced I could rule out hardware issues, but I added some basic memory tests to my bootloader code and couldn't find any issues. Additionally, Stephen Moody was able to boot the same kernel code on his 68k board and got stuck in roughly the same place.

    The only thing left to blame was the binary image itself. When the Linux kernel compiles, it produces a vmlinux file as the main output. Then, based on the platform, you need to turn this into a binary that the CPU can load into RAM (or ROM) and execute. This involves using objcopy to convert the file to an executable.

    uCLinux provides a bunch of examples of this process as the final step in the Makefile. Ultimately, this was my problem. The board I selected as a template expected the kernel to run from ROM. Even though I disabled the ROM and adjusted the memory mapping in the kernel config to run from RAM, this final image generation step still produced a binary tuned for the Arcturus uCsimm, not the Mackerel.

    The solution, then, was to simplify the image generation command:

    m68k-elf-objcopy -O binary vmlinux images/image.bin

    That's it... That was a few weeks worth of debugging kernel code. I took for granted that because the image file was booting and doing something, it must have been fine, but the memory map I gave to the kernel and the placement of data in the executable were not in agreement.

    A frustrating experience, but a valuable one. Even though my kernel debugging wasn't directly productive, I learned a ton about Linux internals and that should help with the next steps: hardware timers, interrupts, serial drivers, and filesystems. That's pretty much the list of remaining tasks.

    Anyway, I'm happy to say I've got my board booting to the infamous "Calibrating delay loop..." message which means I'm ready to implement a timer and interrupt logic.

    5Linux version 3.10.108 (mackerel@4b9e0bcb9c18) (gcc version 4.9.2 (GCC) ) #28 Sun Aug 7 18:57:42 UTC 2022
    Mackerel 68k support by Colin Maykish <>
    6Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne
    7On node 0 totalpages: 496
    7free_area_init_node: node 0, pgdat 0011b84c, node_mem_map 00150100
    7  DMA zone: 4 pages used for memmap
    7  DMA zone: 0 pages reserved
    7  DMA zone: 496 pages, LIFO batch:0
    7pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
    7pcpu-alloc: [0] 0 
    Built 1 zonelists in Zone order, mobility grouping off.  Total pages: 492
    5Kernel command line: 
    6PID hash table entries: 16 (order: -6, 64 bytes)
    6Dentry cache hash table entries: 1024 (order: 0, 4096 bytes)
    6Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
    5Sorting __ex_table...
    6Memory: 528k/528k available (868k kernel code, 544k data, 44k init)
    5Virtual kernel memory layout:
        vector  : 0x00000000 - 0x00000400   (   1 KiB)
        kmap    : 0x00000000 - 0xffffffff   (4095 MiB)
        vmalloc : 0x00000000 - 0xffffffff   (4095 MiB)
        lowmem  : 0x00008000 - 0x001f8000   (   1 MiB)
          .init : 0x0011d000 - 0x00128000   (  44 KiB)
          .text : 0x00008000 - 0x000e0810   ( 867 KiB)
          .data : 0x000e0810 - 0x0011c180   ( 239 KiB)
          .bss  : 0x00128000 - 0x0014d2a8   ( 149 KiB)
    6SLUB: HWalign=16, Order=0-3, MinObjects=0, CPUs=1, Nodes=8
    6Calibrating delay loop...

    For reference, I'm using Linux v3.10 (without any uCLinux libraries or code) compiled with gcc v4.9.2 and binutils v2.25. All built on Debian Jessie. There's a Dockerfile with the full environment in the main...

    Read more »

  • Debugging the Linux Boot Process

    Colin Maykish07/22/2022 at 21:11 6 comments

    It has been many hours of compiling, booting, and debugging and I have not made much progress. In the last log, I had the very start of the Linux kernel code running on the 68008, but it was failing early and without much information. I needed more output.

    After digging through the kernel code, it became clear that printk() was the tool for the job. This function is the kernel's version of the familiar printf(), but it handles logs a little differently. Instead of directly outputting messages, printk() acts a log buffer until the kernel boots far enough to create a serial console. Once the console is available, any logs stored up from before that will be printed. I spent some time writing a simple console driver and attempting to get the kernel to use it, but my issues started before the console is even loaded.

    To get around this, I cheated. I replaced the contents of the printk() function with dumb printf()-style code, immediately writing the log messages to the serial port and skipping all the buffering and console business. Now the kernel can at least talk. This is what it says:

    5Linux version 4.4.0-uc0 (mackerel@c14f97401618) (gcc version 4.9.2 (GCC) ) #1 Fri Jul 22 20:27:01 UTC 2022
    6Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne
    7On node 0 totalpages: 512
    7free_area_init_node: node 0, pgdat 000b50cc, node_mem_map 000cd000
    7  DMA zone: 4 pages used for memmap
    7  DMA zone: 0 pages reserved
    7  DMA zone: 512 pages, LIFO batch:0
    7pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
    7pcpu-alloc: [0] 0 6Built 0 zonelists in  order, mobility grouping off.  Total pages: 0
    5Kernel command line: console=debug,earlyprintk=debug
    6PID hash table entries: 1024 (order: 0, 4096 bytes)
    6Dentry cache hash table entries: 1024 (order: 0, 4096 bytes)
    6Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
    6Memory: 1144K/0K available (613K kernel code, 31K rwdata, 80K rodata, 36K init, 45K bss, 4294966152K reserved, 0K cma-reserved)
    5Virtual kernel memory layout:
        vector  : 0x00000000 - 0x00000400   (   1 KiB)
        kmap    : 0x00000000 - 0xffffffff   (4095 MiB)
        vmalloc : 0x00000000 - 0xffffffff   (4095 MiB)
        lowmem  : 0x00000000 - 0x00200000   (   2 MiB)
          .init : 0x000b6000 - 0x000bf000   (  36 KiB)
          .text : 0x00000400 - 0x00099bc0   ( 614 KiB)
          .data : 0x00099bc0 - 0x000b5a60   ( 112 KiB)
          .bss  : 0x000bf000 - 0x000ca674   (  46 KiB) 

    This is as far as it gets. Occasionally it will also crash and show a stack trace related to the mm_init() code. It seems pretty clear that there is an issue related to the memory. My first thought is that there is something wrong with the image file I'm generating or with the kernel configuration settings related to ROM/RAM sizes and locations. Seeing the "0K available" message is concerning. I have 2 full megabytes of RAM available from 0x000000 to 0x200000. My bootloader copies the Linux image into RAM starting at 0x400 (leaving the vector addresses alone) and then jumps there.

    There is definitely more debugging work I can do in this area, but the other problem I'm having is harder to solve: hardware instability. Unsurprisingly, a bunch of vintage components on perfboards soldered together and connected on a backplane is not the pinnacle of reliability I would like. My RAM board in particular seems finicky. Rearranging the individual chips in the sockets can change the stability. The crashes I see (outside of Linux kernel problems) show up as illegal instruction exceptions or address errors, more evidence that something is off with the RAM hardware. One other possibility is that MFP or decoder CPLD is showing up on the CPU buses when it shouldn't be. This would also look a lot like a RAM problem.

    In a last ditch effort to avoid blaming the hardware, I built and booted an older version of Linux (3.10.0) with and older version of gcc, but I got almost identical results. Turns out the Linux kernel startup changes very slowly between versions. The strategy at this point is two-fold: I need to deep dive into...

    Read more »

  • Linux on the 68008: Signs of Life

    Colin Maykish03/08/2022 at 03:55 3 comments

    I’ve compiled Linux hundreds of times, but it’s almost always automated behind a build system of some sort. I don’t spend that much time configuring it once it’s working as expected and I’ve spent even less time parsing through the source code.  The last few days have been eye-opening in more ways than one. Since the last log, I’ve managed to shave the Linux image file down to 1.5MB. It will now fit comfortably in the 2MB of RAM currently installed on Mackerel, but will it run?

    CH376S USB Module
    CH376S USB Module connected to the 68008 bus

    The first thing to do was to hook up the CH376S USB module again. I haven’t used this since the first prototype build, but it’s essentially a USB-to-parallel adapter with support for FAT filesystems. The setup involves hooking it up to the CPU’s 8-bit data bus, connecting some control lines, and memory-mapping it like any other peripheral. There’s a command/response API for querying the file system and transferring data. My current implementation takes about 3 minutes to transfer the entire 1.5MB Linux image into RAM, which is terrible, but it’s a lot less terrible than sending it over a 9600 baud serial port. I added an option in the bootloader to automatically copy the image file into RAM and jump to the entry point.

    At this point, Linux should be running, but you can’t tell. There’s no console activity, but the 68901’s LEDs do eventually show the pattern for “unhandled exception”, so something happened. Let’s figure out how far it went.

    The Linux startup process varies depending on the CPU architecture. Fortunately, the m68k version is pretty straightforward. In the kernel source code, there’s a file called linux/arch/m68k/68000/head.S. This is the starting point, i.e. the assembly function called _start is defined here. This function will be put directly at the start of the compiled image. In this case it ends up at memory address 0x8000 and jumping to that address will kick off the boot process.

    Since I have no idea if this code is actually running properly, I hacked in a few assembly statements to update the LEDs on the 68901 as the code progressed through _start. This at least proves that object code in the image file is executing and it gives me a place to start debugging. I haven’t actually implemented a serial driver or any platform-specific initialization code yet, but I’m just hoping to see something work. After the slow process of recompiling the kernel and booting from the USB drive, the LED lights show the expected pattern and then quickly change to the “unhandled exception” pattern again. Something happened!

    Tracing through the code further, _start eventually calls jsr start_kernel. This is where the official jump to the Linux kernel happens. The start_kernel() function is defined in linux/init/main.c. Taking a similar approach to the assembly code, I added a few LED pattern commands in here, only this time written in C. Rinse and repeat, success! I know the CPU is at least getting to the start of the Linux kernel initialization code.

    By now, the LED patterns are becoming cumbersome, but I’m still no closer to a working serial driver. Time to cheat! My serial output code for the 68901 is really simple, so I just copy-pasted the bare minimum code right into the main.c kernel source. This does not give Linux a way to communicate, but it acts like a more useful LED display, the old “printf debugging” routine. Sprinkling mfp_puts() messages all over the place and rebuilding gave me a better idea what was happening:

    Booting from USB...
    Image loaded at 0x8000
    Booting image...
    Read more »

  • Baby Steps Towards Linux on a 68008

    Colin Maykish03/04/2022 at 01:22 0 comments

    Choosing a Version

    If you Google around for "68000 linux", it won't be too long before you find some mention of uCLinux. uCLinux is a project that supports a handful of CPUs without a memory management unit which makes it perfect for the 68008 (and most of the early 68k family). Several other homebrew 68k projects have used this “distribution” with some success, but a lot of them are now approaching a decade old and uCLinux itself seems to have been abandoned around 2016. The releases are still hosted on SourceForge, with the newest one building on v4 of the Linux kernel.

    Collecting the Tools

    So what does it actually take to compile uCLinux into something Mackerel can execute? Where does it even start? I’ve already been using a gcc-based cross-compiler for the m68k platform and that won’t change. However, I’m going to start over and rebuild the toolchain from an older version with the hope that tools from around 2016 will have better compatibility with code from the same era. I did this whole process in a Debian 9 virtual machine.

    The first step is to build binutils and gcc as a cross-compiler ( is a good reference for this). I settled on binutils v2.27 and gcc v5.5.0, both from around 2016 . Now I’m basically back to where I started but with older versions of my cross-compiler and tools.

    In theory, there’s nothing stopping me from cloning the Linux kernel source, setting up a config manually, and building any version I like that targets the 68k. The problem is that the kernel is only one part of a full Linux system. I also need a standard library, applications, and a file system set up, not to mention drivers and startup code that will be specific to the Mackerel hardware. This is where uCLinux comes in. There are a few dozen preset configurations for known hardware. For example, Atari and Amiga have prebuilt configs that should build a full bootable system image. Let’s start with one of these to make sure the toolchain is working and to iron out any build issues before attempting to customize things further.

    Let's Just Compile Something!

    The uCLinux build process is pretty straight forward: run make menuconfig and select the vendor and board. Then run make. If all goes well, there’s an image file waiting at the other end. I chose the Arcturus uCsimm board for no particular reason other than the config seemed to be pretty barebones. The first dozen or so build attempts ended in failure for various reasons. 

    At first, uCLinux was attempting to use the wrong compiler. I updated the vendors/config/m68knommu/config.arch file to point to the correct prefix, i.e. m68k-elf-, the prefix for the toolchain I built previously.

    Then the build failed due to some compiler errors in the startup code. I had to comment out most of the interrupt handling code in ints.c and remove the call to the config_BSP() function from the setup_no.c file. Obviously, this will need to be fixed at some point, but I want to see a successful build before I write a bunch of driver code.


    After a lot of trial and error with different combinations of toolchains, uCLinux releases, and configs, I have a Linux image:

    -rw-r--r-- 1 colin colin 2.4M Mar  3 12:49 image.bin
    -rwxr-xr-x 1 colin colin 204K Mar  3 12:49
    -rwxr-xr-x 1 colin colin 1.2M Mar  3 12:49 linux.text
    -rw-r--r-- 1 colin colin 997K Mar  3 12:49 romfs.img

    What does this image.bin file actually contain? According to the config, it should be a Linux kernel and a filesystem image containing busybox and some other basic Linux applications. Let’s confirm.

    Looking at the Makefile for the Arcturus uCsimm configuration, there’s a make image step that runs after all the compiling is done. This step is stripping various sections from the linux ELF file and combining it together with the ROM filesystem into a single image.bin file.

    The linux ELF is really just object code with some metadata wrapped around it. Using objdump...

    Read more »

  • Four Times the RAM, Same Great Price

    Colin Maykish02/27/2022 at 23:34 0 comments

    After spending all afternoon soldering, Mackerel now has a full 2 megabytes of super fast SRAM. I decided to go with the ISSI IS61C5128AL RAM chips for this project. They're pretty similar in function to the more common AS6C4008 series from Alliance, but they are rated for a blistering 10ns access time, more than 5x faster than the Alliance RAM. They're also slightly cheaper. The downside is that they're only available in surface mount packages. By the time the adapter boards are factored in, the cost difference is negligible, but the speed is still really nice. No wait states here!

    The SOJ-36 packages are not really that bad to solder by hand. With lots of liquid flux, I was able to get (in my humble opinion) really nice joints connecting them to the DIP breakout boards. These breakout boards won't really be necessary when I start designing PCBs, but they do make the ICs easy to reuse during prototyping.

    I've also thought about designing a SOP-36 to DIP-32 adapter which would convert the footprint of these ISSI chips to match the more standard DIP-32 SRAM pinout, allowing either style of SRAM to be used in the same socket.

    Here's the full RAM board with four chips installed for a total of 2048KB:

    The back side is a little less attractive:

    I'm not sure how well this wiring arrangement will hold up at higher clock speeds, but I was able to verify reads and writes to the entire 2MB range using a simple test program.

    One thing I don't really like with this design is that all of the chip-select logic is off-board. Every SRAM chip has its chip-select line connected to a separate pin on the backplane. I've got plenty of pins to spare at the moment, but if this is made into a PCB, I would like to move the address decoding for each individual module on board, freeing up a bunch of backplane connections.

  • Hardware Timers, Vectored Interrupts, Exception Handling in C

    Colin Maykish02/27/2022 at 01:10 0 comments

    With the MFP serial port working reliably, the next requirement for a barebones Linux system is a constant timer to allow the kernel to do context switching and multitasking. It's fortunate, then, that the MFP also provides four independent timers which can trigger interrupts on the 68008 CPU. It can also be configured to trigger interrupts for other reasons (serial port data available, GPIO input levels changing, etc.), but for now, a constant timer will be a big step forward toward the main goal of booting Linux.

    Vectored Interrupts

    The MC68000 series of CPUs supports vectored interrupts. It’s worth taking a minute to understand what that means. It took me a few days to really internalize this idea. In most 8-bit CPUs from a generation before the 68k, interrupts are not vectored, but polled. For example, when a peripheral wants to interrupt the 6502 CPU, it asserts the IRQ pin. The CPU stops what it was doing, stores some internal state to the stack, and then calls the interrupt handling code. Since there’s only one interrupt pin, all of the peripherals have to share. So when the CPU is ready to handle the interrupt, it has to ask each peripheral if it was the cause of the interrupt and then choose the appropriate action to take when the source is found. This is simple to design on the hardware side since every peripheral can connect in parallel to the IRQ pin, but it means the interrupt handling code is more complicated and potentially slower.

    Vectored interrupts, on the other hand, require more hardware, but they provide a way for the CPU to know exactly where each interrupt comes from without having to ask each potential source. On the 68k, this is done with an asynchronous interrupt system that prioritizes interrupts according to level.

    Instead of one IRQ pin, there are three IPL inputs representing a binary number from 0 to 7. These are the interrupt levels where 0 means no interrupt and 7 is a non-maskable interrupt. The minimum interrupt level can be set in software and when the CPU detects an interrupt at or above its minimum level, it will start the interrupt handling process. As with polled interrupts, it will finish executing the current instruction, store some state to the stack, and then find the appropriate handler function to call. The main advantage, though, over polled interrupts is that instead of asking every peripheral, the CPU expects the location of the interrupt handler to be exposed on the CPU bus by the peripheral. In other words, when the CPU acknowledges that it’s ready to handle the interrupt, the peripheral that made the request tells the CPU which handler to call. This has to be set up ahead of time in code of course, but this one-time setup simplifies and speeds up interrupt handling for the lifetime of the program. It also means interrupt handlers can be changed programmatically, which gives a lot of flexibility to the code.

    There are some details I left out (that will come up in the implementation section), but this is the description of vectored interrupts I wish I had when I started working on this. I hope it’s clear enough to be helpful to someone in the same position.

    Design Changes and More Logic To Implement

    The first step to getting interrupts going is to bring in some additional control lines into play. The MFP has an IRQ output that must be mapped to one of the 7 interrupt levels on the CPU. Since it is currently the only source of interrupts, the IRQ pin is connected directly to IPL2. All three IPL pins have a pullup resistor, so this means the MFP will cause a level 4 interrupt. If more than three interrupt levels are ever needed, something like a 3-to-8 decoder would be necessary to use the whole range.

    There are three pins on the 68008, FC0 through FC2, that, when all high, indicate the CPU is acknowledging an interrupt. This will be called the /IACK signal. It is used internally on the CPLD, but also connected to the MFP’s IACK pin. When the CPU is ready to handle...

    Read more »

View all 13 project logs

Enjoy this project?



teraz wrote 03/08/2022 at 19:53 point

Is possible make a mobile version? for example 24h working time?

  Are you sure? yes | no

Colin Maykish wrote 03/09/2022 at 01:03 point

Sorry, not sure what you mean. Like a portable version of the computer?

  Are you sure? yes | no

teraz wrote 03/09/2022 at 10:44 point

yes, similar , meybe solar panel too. motorola is power eficient

p.s. look this too 

  Are you sure? yes | no

Keith wrote 02/23/2022 at 22:34 point

I have designs for the 68008, 68000 and 68020. They all use SRAM so they would need hacking to exceed the 24 megabyte needed for an OS like Linux. My boards ran OS9/68K on a SCSI disk. (board only, no cct or manual)

  Are you sure? yes | no

Colin Maykish wrote 02/24/2022 at 00:32 point

Nice looking boards. I like the idea of STEbus, but I'm not sure I'd want to invest a lot into it knowing I'll be expanding the bus width when I move up the 68k family tree. I guess most of the CPUs (other than the 68000) could actually use an 8-bit bus directly though.

I think I'm going to have to tackle DRAM when I need more than the 4MB that the 68008 can address. Buying and soldering all that SRAM sounds expensive and time-consuming.

  Are you sure? yes | no

Keith wrote 02/24/2022 at 00:44 point

The STEbus is 8-bit data but that does not stop processors being any data width they wish inside the board. For example, the 68020 board makes 16-bit accesses  to the ROM and 32-bit accesses to the RAM. The 68020 has "dynamic bus sizing", and will make accesses in whatever width the board logic tells it to. The STEbus is really just for i/o, not main memory. In the olden days, memory chip capacity was so small that extra boards were the only way to carry them. These days memory is big and cheap, and can easily fit on the processor board.

  Are you sure? yes | no

Colin Maykish wrote 02/24/2022 at 00:55 point

Ah, that makes a lot more sense how you could get away with an 8-bit bus. I'm coming from a software background, so the system architecture, especially when it comes to things more complicated than SBCs, is definitely a learning opportunity for me.

  Are you sure? yes | no

bastetfurry wrote 02/23/2022 at 07:07 point

Regarding memory size, 24 MBytes seems to be the bare minimum these days for a recent kernel. How we found out? OpenComputers2 came around for Minecraft 1.18 and that one implements a RiscV64 VM with a barebone Linux on top. We tried to fire up a machine with just 16 MB inserted and got a genuine kernel panic as the userland refused to come up with just 4 MB of RAM left, the kernel alone eats up around 12 MByte. Inserted the 2*4 MByte from our very first try and voila, the machine came up to a login and we could run anything.

  Are you sure? yes | no

Colin Maykish wrote 02/23/2022 at 15:03 point

This was on kernel v5?

I'll admit I've done very little research on what it will take to run Linux on this machine so far. Might have to stretch my definition of "modern Linux" a little to accommodate the 68008 limitations.

  Are you sure? yes | no

bastetfurry wrote 03/24/2022 at 09:51 point

Yep, made with recent buildroot, here is the fork:

  Are you sure? yes | no

Boxerbomb wrote 02/05/2022 at 19:11 point

Nice man, I will be following the project. I love 68k computers and I think that you are making the right choice trying to work your way up to the 30. I have been thinking about starting up a project like this and maybe using some sort of interface to a large RAM chip to avoid having to reprogram EEPROMS.

  Are you sure? yes | no

Colin Maykish wrote 02/06/2022 at 04:46 point

Thanks, appreciate the interest. The build up from the 68008 is definitely less daunting than jumping straight into a 68030. My solution to constantly flashing ROM is a serial bootloader. I can load new programs into RAM over the serial port. The actual ROM code changes a lot less frequently that way. The only downside is you lose your program when you lose power.

I'm also working on a more direct boot-from-USB using the CH376S module. That will get its own write-up when I get it finished.

  Are you sure? yes | no

John Smith wrote 03/15/2023 at 18:16 point

Hi. Have you had a chance to complete the project and publish PCB gerbers for backplane and PLCC adapters?

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates