The Blinking Computer
The Blinking Computer will be an easy-to-build educational computer. As the name suggests, the idea is that you can write code and see it execute at every level, that means discrete components and lots of LEDs. The design has evolved very many times since my Prof forbid me from canablising the old departmental Strowger switch telephone exchange in the late 1980's. Right now there are 32 words of RAM and the program memory is a tape loop, just like Colossus.
The objectives are:
Simple enough so that everyone can see how it works, that means lots of LEDs
Complex enough to demonstrate the main ideas of a computer
Small enough to be affordable by anyone with the time to build it
Quick enough to build and keep my (and those that follow me) family life
Fast enough to run some complex software
Slow enough so that everyone can see it running and understand it step by step
Flat enough so that the entire state can be seen in one go
This is the plan:
It's an 8 bit processor with only 32 bytes RAM but loads of program memory
There are no registers, only a single bank of data memory (the program memory is completely separate)
All instructions are of the form: cnd Mi = Mj OP Mk
i, j, k are 6 bit integers
cnd
indicates that all instructions are conditionally executed
Most of the bottom 5 bits of the address space (M0 to M29) are DRAM
M0 and M1 may be used as stack pointers. M30 contains the contents of the address in M0 (and M31 pairs with M1)
Reading/writing M0 or M1 works as any other memory location
Reading/writing M30 or M31 results in the contents of M0/M1 being placed on the address bus, so the memory accessed is in the DRAM range of M0 to M31
The top five bits of the address space are constants if read and control if written.
M32 to M47 are the constants 0 to 15 and M49 to M63 multiples of 16, both implemented by wiring the address to the data lines
M48 is toggle switch input
When written the address line is used to control:
Bit0 JUMPs to the value written
Bit1 outputs the value written
Bit2 sets the clock rate (potentially including HCF)
Whilst the RAM/ROM/CTRL space is addressed by 6 bits, the top half never needs to be decoded. If it used to read from ROM then the (shifted) address lines are used. If it is used to write control signals then only one address line is high at once so the address line can be used directly.
There is no program counter!
The instruction rate is intentionally slow. 1Hz is maybe typical, everything should be visible. There will be a single step button that is active after HALT so that everything can be as slow as needed.
There will be a twelve phase clock:
Phase 0: read the instruction from the tape and decode
Phase 1: read Mj to latch/register IN0
Phase 2: read Mk to latch/register IN1
Phase 3 to 10: run the ALU storing the result in latch/register OUT
Phase 11: write Mi
Phase 12: wait for tape to spin and do DRAM refresh. This is the rate limiting step and it is where the state can be seen.
There exists several status flags:
The ALU has AND, OR, XOR, ASL, ADD, ADDC, SUB, SUBC
ASL is hardwired in parallel, the rest are via a 1-bit serial ALU
There is a carry flag, ADD/SUB ignore it, ADDC/SUBC use it
Conditional execution is one of: never, always, ==0, !=0, <0, ⇐0, >=0, >0
The construction cost and time are very important. Ideally the bill of materials should come in at under £100 so that schools can buy and build it.
It's not quite a minimal processor, it is more important that it's understandable with school level electronics and be able to run complex programs (slowly!)
I expect that laminated A4 will work (print A4, laminate, punch holes, spaghetti wire through holes)
Input is likely to be from a simple scanner. A4 pages are taped into a loop, a motor runs until the label is reached, photodiodes/photoresistors read the instruction.
Output is likely to be thermal print paper and nicrome wire (alt: DIY pen plotter, eight marker pens on solonoids/springs)
Breadboard to begin with, but ideally A4 laminated paper or acrylic so that the circuit can be seen
Framed, ideally running on batteries
Some permissive
FOSS licence will be adopted (e.g. Apache2/MIT) and experimentation and evolution are encouraged
There is an emulation with some example code in Google sheets
All instructions really are if(cond) Mi = Mj OP Mk
, however the assembly language can be more more flexible and compile in constants, writing to high bit addresses and the like. We can split the generic instruction into the left hand side, Mi =
and the right hand side, Mj OP Mk
human readable | machine readable | comment |
Ri = | Ri = | the general case for 0 <= i < 32 |
JUMP | R32 = | writing to M32 is trapped and a JUMP is made to the RHS |
OUT | R48 = | output the RHS |
*R01 | R56 = | output the RHS to the 16 bits in (R0,R1) |
FREQ | R62 = | set the system clock frequency |
CPU | R63 = | other CPU behaviour (e.g. set flags) |
HCF | R63 = | every homebrew computer needs this instruction! |
human readable | machine readable | comment |
Rj OP Rk | Rj OP Rk | the general case for all OPs except LSL |
Rj » 1 | Rj »1 Rk | k is ignored in the case of LSL |
Rj | Rj | M32 | OR with zero (M32) results in only Rj |
Rj OP c | Rj OP Rk | c is a constant in the set (0, 1, … 16, 32, 64 … 240), k selects |
Rj OP c | Rj OP Rk | c is a constant in the set (0, 1, … 16, 32, 64 … 240), k selects |
c OP Rk | Rj OP Rk | c is a constant in the set (0, 1, … 16, 32, 64 … 240), j selects |
k | Rj & Rk | any 8 bit number, j is from (0, 16,…240), k from (0,1,..15) |
IN | | wait for input from toggle switch |
*M01 | input from address (M0, M1) | |
'cond' is probably the standard 3bits/8 values: Always, Never, =0, !=0, >0, >=0, 0⇐, <0. However, look at the 6502 flags, Carry, Overflow, Negative and look at what values of 'cond' were used in 'third' code.
SIMPL
SIMPL is a SIMPL IMperative Procedural Language. It's deliberately minimal and very very simple - the aim is to show how languages and complilation work in an easily ingestible way.
statements are of the form: `a = function(b c)` where a, b and c are variables. b and/or c can of course be other function calls, so 2x+1 is `add(mul(x 2) 1)`
Variables follow :
Type | size/bytes | Syntax | Example |
boolean | 1 | True|False | True |
byte | 1 | \d+[U] | 42 |
short | 2 | \d+[U]S | 65535US |
long | 4 | \d+[U]L | -2147483647L |
character | 1 | '\w' | 'a' |
string | 1+ | “\w*” | “Hello World!” |
float | 3 | \d+.\d*[e[-]\d+] | 3.1415926535 |
Only fixed sized arrays are supported, the size must be known at compile time and memory is allocated as contiguous variables of the same type.
Indentation defines a code block so allow us to define functions and implement control flow:
def mul2(x)
return mul(x 2)
and also to implement control flow:
if eq(answer 42)
print("The Answer is 42")
else
print("The Answer is not 42")
a = 0
while lt(a 42)
a = inc(a)
print(a)
The aim is to do type resolution at compile time, so types do not have to be specified. The type of a constant can be determined. The return type of a function is the return type of the last call that function makes (with a check that all returns have the same type). The types of the function arguments are determined by the usage of those arguments (again, with a consistacny check). The aim is for a minimal language. There is no automatic type promotion.
History
I've wanted to build my own computer since the late 1980's when the old departmental relay based telephone system was thrown out. I asked my professor if I could take it out of the skip, he very sensibly said “no” quite firmly.
Since then I've thought about many designs: some have been relay based, some transistor; some have included memory and other have been CPU only. Noteably:
in 2017 I started the parent of this design on
hackaday.io. The data size and address size was 16 bits with five conventional registers addressing 64k words of RAM. The idea was to use a Raspberry Pi for external memory and to sanity check the computations. A Forth like language was written and many thousands of lines of code was written, including floating point and random numbers. The code, compiler and emulator is on
github.
Even with only five registers the design of each memory cell is critical because the majority of the transistors (and other components) are taken by the memory cells. This led me down a long random search of
weird memory cells and
dippy which used DIP switches for ROM/program memory.
External memory is somewhat cheating, and from the educational point-of-view it's expensive (a Raspberry Pi is a significant proportion of the total cost). What's more, the instruction set was all register to register except the load/store instructions. Hence I've simplified everything and got rid of the external memory and increased the number of registers considerably. For ease of explanation I don't call the storage space 'registers' but RAM. Two RAM locations are read into latches/registers for presentation to the ALU and then stored back into RAM.