Jack One

Six months ago, I set out to build my own CPU. I’m a software developer and had no hardware or low level experience. It was the most complicated project I’ve ever done and took months of painstaking design and construction to realise. But to my continued surprise — I ended up with a working computer.

Built from simple logic chips, it runs at a blistering 4MHz, has 32kb of RAM, a 16x4 LCD display and reads programs from a ROM chip which can be programmed by a PC. In a slightly egotistical move, I named it “Jack One.”

There was just one problem — I didn’t have any software to run on it.

Hello World seems like the simplest possible program you can write for a computer so it seemed like the obvious choice for the first Jack One program.

Surprisingly, it’s 100 lines long and required new compiler features to be able to even start. This really shows how many layers of abstraction we rely on to write software efficiently on mainstream computers.

Why is it so complicated? Software needs to be ported to different architectures and Jack One is a completely unique architecture. There are no compilers for it and no existing software can run on it. That means no kernel, no libc, no drivers and no hope of a high level language like Java or Python.

In order to be able to program it at all, I wrote a very simple compiler (strictly speaking it’s actually an assembler) which converts assembly like this into a format that can be uploaded to Jack One:

loop:

LOAD count ; get the value of the variable count

ADD =1 ; add one to it

STORE count ; save it XOR =10 ; compare to 10

BNZ loop ; if not 10, goto top of loop

This program is the equivalent of:

while(count < 10) {

count++;

}

Writing software at such a low level means starting from nothing. In order to display “Hello World” on the screen we need loads of boilerplate:

LCD display driver

Array support

Use array support to implement string support

By the time you’ve done all this, in a language this verbose, it’s easy to see how you could get to 100 lines.

But someone somewhere had to write this kind of code for real platforms — x86 or ARM. They have the advantage of a CPU that takes care of more things for you and a very mature toolchain, but in order for you to read this article code like this had to execute — probably hundreds of thousands of times.

What did I do to solve this? I abstracted. I introduced a basic preprocessor into my compiler which allows chunks of code to be inserted from a single line. Now Hello World looks like this:

hello: ASCII “Hello World!”

LcdInit

PrintStr hello

As software developers, we often forget the tens of abstraction layers below us. As computers get faster we continue to add further layers, each making it even quicker to write software but sometimes at the expense of performance.

Ultimately though, this is a good thing. It makes the field more accessible and new features more achievable. But take a moment to thank the guys below — here’s to the systems programmers.