Background

Around mid-2020, as I surveyed the collection of 8-bit and 16-bit retro-computers from the 1980's that I'd amassed in the previous 5 years, I started to contemplate which of these lovely machines might find a home on my desk, rather than in my closet. I liked all of them, each having their unique charms and memories, but I wanted this hobby to be about enjoying them, rather than collecting them. I needed a new project.

Of the 8-bit machines, it was the Commodore 64 for which I had the greatest affinity, having grown up with one in my home. I had acquired many cool modern add-ons for the C64, which I thought was particularly amazing...proof that others really wanted to USE their retro-computers. This was the same time when I was wanting to also scratch an old itch over learning some hardware design skills.

After some contemplation, I settled on learning about the machine I never got to know in the 1980's - the Commodore 128. Sure, I knew it's basic features and that it made a really nice upgrade to my C64. But, by early 1986 I'd already embraced the thrills (and chills) of becoming an early Amiga adopter. So I sorta missed one of the best 8-bit machines ever made. Time to remedy that, I thought.

Inspiration

As I thought about connecting my C128 to my modern computing life, many things seemed obvious. I wanted the retro-internals, but not the device limitations of the C128. Floppy disks were novel to use occasionally, but normal file management needed modern media and fast access. Also, networking was essential. It at least needed to talk to my LAN, if not the broader Internet too. I started imagining about a dozen virtual devices that I'd like to be able to access from C128 software using some simple, efficient, 8-bit friendly protocol. This led me to develop an I/O multiplexer as a Rust application running on a Raspberry Pi. I was also new to Rust as a programming language at this time, and that fascination ensured Rust would also become part of the project. I wrote test code for my protocol, and prototyped it communicating with the C128 over a low-speed serial connection.

Another limiting factor was that I was not at all proficient in programming in 6502 assembly language. So where would the essential applications come from? It's simple enough to learn the 6502, but to write large and useful applications with it is a whole separate skill to hone. I needed a jumping off point to help me build something useful for my project as I worked on my proficiency. That turned out to be ACE-128. ACE ("Advanced Computing Environment") was a project of Craig Bruce in the mid-1990s to create a suitable operating system for the C128, and one that was worthy of its advanced features. What he created inspired me because it was nice, clean 6502 code that was well-organized and documented. It made sense to me, and was a great jumping off point. I knew almost immediately I could add modules to it to realize the vision of it talking to my I/O multiplexer; making modern device connectivity accessible on my C128.

Development

I was already having great fun improving my Rust and assembly proficiency when I got serious about the hardware piece. I briefly considered a direct interface to a Raspberry Pi Zero, but deemed it too costly in terms of losing much of what makes the RPi a great little computer, in its own right, and adding software complexity in adapting everything to some bare-metal development platform. I mean, would my beloved Rust programming language even be there, let alone a full set of reliable device drivers, if I ditched Linux? I decided to stick with Linux and just customize a bare bones Arch Linux ARM that would be infinitely expandable if a user wanted to add additional software to it. The first thing I wanted to add myself was the "acme" 6502 cross-assembler, so that I could build the idun-cartridge software ON THE idun-cartridge.

So I'd need something to interface between the RPi and the C128 expansion port. Again, I did not want my future options to be too limited, and I wanted my hardware to be easy to prototype and assemble myself (I'm not a soldering savant!) The Parallax Propeller 1 hit the spot for me. It's got loads of capability with its 8 independent cores and its availability as a 40-pin DIP made me grin. This part would look like it actually belonged, connected to an 8-bit computer. And it turns out that just like the 6502, the Propeller 1 is really fun to program in assembler. I designed a 2-layer through-hole PCB around the Propeller 1 and the RPi Zero by late-2020. The rest was, as they say, just a matter of software.

Getting Stuck

Well it turns out that even with the Propeller 1 (P1) overclocked at 100 MHz, it's not so simple to program a micro-controller to hit the real-time bus characteristics of even a 1 MHz 6502 CPU. You have to do many of the critical operations in a half-cycle, or 500 ns. Of course, you need some margin on each side of that - so, let's say 400 ns. A single instruction on the P1 at 100 MHz takes 40ns. So, 10 instructions <period>. Some of the code took a lot of attention to make it fit, but it eventually did.

Getting it Working

I needed to do a few things on the cartridge in real-time using P1 assembly code. I wanted a limited boot ROM capability. I also wanted to be able to generate interrupts from the RPi, and run arbitrary code in response to those interrupts. And I wanted the C128 to be able to push/pull data from the I/O multiplexer at as fast as the C128 assembly code could go. Basically, no polling. Just read from and write to the cartridge at maximum 6502 speed. I (mostly) got there using 4 of the P1's 8 cores.

For the bulk I/O data, the C128 is the boss, and sends requests to the RPi via its asynchronous serial port with the P1 acting as the protocol converter. The serial port runs at 1.5 Mbps with hardware handshaking and works reliably at that speed for the 6502, the P1, and the vanilla Linux async driver. But I needed a second line of communication where the RPi could be the boss. For this, I2C running at 400 Kbps was the answer. It's this "back-channel" that lets software running on the RPi take over  the C128 by triggering a hardware interrupt, and handing it some 6502 code to run, over I2C.

Once these low-level communications were working reliably, I started adding I/O drivers, beginning with an "RTC" service to fetch the time-of-day from the RPi during boot. From there, things progressed quickly to virtual disks, virtual floppies, virtual terminals, etc.

Getting Carried Away

This doesn't sound like much low-level capability, but it opened a wealth of possibilities for how the RPi can be an effective co-processor to the 6502. Which made me wonder "where would I start" if I wanted to quickly cobble together "hybrid" applications that used the 1 GHz ARM processor in the RPi to make C128 applications that wouldn't be so hopelessly compute bound. I thought there ought to be enough overhead to use a nice, interpreted language. I settled on Lua because of the ease with which I could embed it and customize it inside my Rust application.

One has to be careful when deciding on a radically new paradigm and platform for developing simple applications! It's really hard to know what is essential in the so-called "platform", and what ought to be left as an exercise for the application developer. You don't want to have to repeat the boiler-plate stuff for every application, and you don't want to paint developers into an awkward corner by trying to do too much either. I think the lesson is that teasing out where to draw the line between platform and application is best done through a process of creating many applications, including, (hopefully) some really complicated ones. I have to say I cannot get there on my own. So, the Lua integration is very much a work in progress, and one that will take shape only with community involvement. Should I be so lucky, I do think it will lead to something fun for both programmers and users of retro-computers.

To the Present

The project is called "Idun", after the Norse goddess of youth & rejuvenation, which seemed apropos for a project intended to "rejuvenate" an old 8-bit computer. I released it on GitHub with source code and schematic. If you want to know more, I recommend checking out the project pages. There's also an 18 minute demo video that highlights some of the features, and can be viewed on YouTube.

 It's been a rewarding learning process and a great way to get better acquainted with a unique bit of computing history; that era in the late-80s when the successful 8-bit home computers all enjoyed their final round of upgrades before yielding to a 16(/32)-bit future. The C64's upgrade was a grand one, and can still entertain us almost 40 years later.