There’s always some debate around what style of architecture is best for certain computing applications, with some on the RISC side citing performance per watt and some on the CISC side citing performance per line of code. But when looking at instruction sets it’s actually possible to eliminate every instruction except one and still have a working, Turing-complete computer. This instruction is called subleq or “subtract and branch if less-than or equal to zero“. [Michael] has built a computer that does this out of discrete components from scratch.
We’ll save a lot of the details of the computer science for [Michael] or others to explain, but at its core this is a computer running with a 1 kHz clock with around 700 transistors total. Since the goal of a single-instruction computer like this is simplicity, the tradeoff is that many more instructions need to be executed for equivalent operations. For this computer it takes six clock cycles to execute one instruction, for a total of about 170 instructions per second. [Michael] also created an assembler for this computer, so with an LCD screen connected and mapped to memory he can write and execute a simple “hello world” program just like any other computer.
[Michael] does note that since he was building this from Logisim directly he doesn’t have a circuit schematic, but due to some intermittent wiring issues might have something in the future if he decides to make PCBs for this instead of using wire on a cardboard substrate. There’s plenty of other information on his GitHub page though. It’s a unique project that gets to the core of what’s truly needed for a working computer. There are a few programming languages out there that are built on a similar idea.
Anyone crazy enough to want to try writing code for a single instruction computer can go try the free Steam game called SIC-1. (Disclaimer: I’m in no way affiliated with the game, just tried to play it a while back.) Doing the simplest of things using only subleq rapidly becomes painful. Just adding two numbers involves subtracting from zero twice. Any kind of useful loop (for/while) involves self-modifying code.
I suppose a compiler can help with the drudgery.
A macro assembler helps a lot, and is much simpler than a full blown compiler. Once you’ve contained all the ridiculous self modifying stack pointers and whatnot, you can write fairly normal and comprehensible assembly.
One could start with Shenzhen IO, same devs as SIC and a funnier game. SIC is way more complex. Also there was android version of SIC, iirc
I wonder if an single-instruction execution unit could be realized as an enzyme acting on a program that is a polypeptide sequence…
Since I don’t know much CS having used Fortran a tiny amount ages ago I can see this “computer” running on an array of disc valve and diaphragm “transistors” like a player piano of which I have rebuilt many. It would be desk or bed sized and a little suction of ambient air would power it.
Nice job, is there a way of importing the logisim file into kicad, I want to make a 74xxx logic version of this…
I love this, so inspiring simple yet complex :-)
Next-level challenge: now build a 1-bit subleq computer. :D
And then build it with electron tubes!
But I’d guess that you would need a serious amount of ram with a 1-bit subleq cpu, because the number of instructions you would need for even the simplest little program would already run in the hundreds.
Just for fun I asked ChatGPT if it could estimate the length of a program for a 1-bit subleq computer, that calculates 20-10 and stores the result in memory. ChatGPT estimated about 100 instructions for readable code (using no tricks). It also told me that it could probably get it down to 70-80 instructions using a few tricks.
decades ago, i read that the new hotness in power efficiency was a subleq cpu. If you want to challenge yourself make a one instruction trinary computer, because a trit is even more power efficient. it’s right at the cutting edge of coding, and if you’re going to make a useless albatross, that you don’t know how to code then why not aim high.
Well, I would know how to code it. But I would not have the patience. At all. :P
The main idea is to minimize the number of transistors and diodes. An MC14500 1-bit cpu has about 500 transistors. But it has 4-bit instructions. And if it would only have 1 instruction, that would mean fewer transistors.
The tradeoff is memory. And memory is a bunch of transistors as well.
A subleq computer can’t run from rom, because it needs to be able to modify its own code, if you want to do useful things like looping. You could preload ram from rom, at start of your program.
Self-modifying code presents another problem: after running, the code is different than before running. So if you want to run again, you’ll need to load the whole program into memory again.
So if you want to use it as a controller, with one task that needs to be run repeatedly, you have to take into consideration that every time you want to repeat the task, there will be the delay of reloading memory fresh.
Like you said, it’s a useless Albatros. Which is why I ChatGPT’d it instead of actually doing research to try to understand it. ;)
The Soviet Union had a working trinary computer but did not pursue it. It’s now back on the table as I read in a recent article.
Wonderful work and persistence! I always wondered what is the minimum number of transistors needed for a viable computer. Now we know, about 700.
Iirc you can make a Turin complete single instruction CPU
Conditional jumps, that can flip a bit or jump base on the conditional
Yea port doom using only conditional jump logic
That’s about as Risc as you probably could get
What is that single instruction. It must be some sort of conditional instruction but it also has to alter memory in some fission.
[This instruction is called subleq or “subtract and branch if less-than or equal to zero“.] -above
Would you not also need interrupt or similar mechanism to have computer that can react to events and exchange data with outside world?
It can read and write from/to memory. So it can read and write from/to registers. So it can write to output registers and poll from input registers. That is your I/O.