I amuse myself by constructing a computer almost entirely out of relays.
Relays were used to construct computers well before vacuum tubes, transistors or integrated circuits were feasible for the task. The main inspiration is the machines by Konrad Zuse of the late 30s and early 40s.
Why relays? In addition to constituting an important historical link between the mechanical and electronic
computers, relays are especially fun to work with since they
- are big and slow, with huge propagation delays and a tendency to oscillate if you hook them up wrong.
- are noisy, especially when lots of relays switch at the same time.
- consume lots of power to do even the simplest of calculations.
- subscribe to Lenz' law, i.e. generate lots of EMF and flyback current that make for all sorts of interesting
interference in places you couldn't even guess.
So all in all, relays require you to think in very new ways compared to normal solid-state devices.
The relays I use in the computer.
Quick feature list:
- 8-bit data bus and 16-bit address bus
- 3 x 8-bit accumulator registers, 2 x 12-bit registers (for index, addresses and jumps)
- 64k solid state memory, holding heap, stack and program
- 12-bit program counter and 12-bit stack pointer
- ALU capable of not, and, or, xor, add, increment, decrement, shift, and indirectly, subtraction
- Writable microprogram stored in solid-state device for sequencing
- Assembler and Microassembler running in DOS and communicating with Zusie over parallel port.
To build a relay computer, you clearly need a lot of relays.
I had the good fortune of locating about 100 discarded telephone exchange circuit boards with about
15 four-and six pole relays on each board. I bought them at scrap prices and desoldered them with a hot-air gun and wound up with some 1500 excellent-quality relays.
Architecture
Here is a block diagram of Zusie's architecture (click for larger version).
It is a fairly regular CISC, microcoded architecture.
Instruction Set
Here are some terminology used below and in Zusie assembly code:
- A literal is a constant numeric value between 0 and 255. Can be written in decimal form, in hexadecimal form preceded by h (h80) or binary preceded by b (b10000000).
- A label is a jump or call destination. In Zusie Assembly, it's written on a line preceded with a colon (such as :label). It evaluates to the address of the next instruction after the label. The label takes up no program space of its own and is only an assembly helper construct.
- Some instructions like jumps takes an address (or label) as argument. They usually come in
two versions, long and short. Shorts are prefixed or suffixed by an s.
Shorts only alter the lower byte of the program counter, and so can only jump to addresses
within the same 256-byte code segment. Always use shorts if you can (i.e. when not writing absolutely huge programs), since they are faster and have only one address byte as payload,
contrasted with two bytes for long instructions. These varieties are referred to as distance below.
Here is a comprehensive list of Zusie instructions and mnemonics:
- Load Literal Load numeric constant into register.
LDL->r v where r may be A,B or C, and v is a literal
- Load Condition Code from Register Populates the condition code register according to register r.
LDC<-r where r may be A, B or C.
- Subroutine Calls and Returns CALLd instructions saves the value of the program counter and proceeds execution at a label. A RETd instruction restores the program counter to the instruction after the last CALLd. d can be either s for a short jump or empty for a long jump. Call and return distance must match.
CALL label and RET
CALLS label and RETS
- Stack Push Pushes the value of a register onto the stack.
PUSHr where r may be A, B or C.
- Stack Pop Pops the top value off the stack and into a register
POP->r where r may be A, B or C.
- Increment/Decrement 16-bit register Atomically replaces the content of r with content+1 or content-1, or increments/decrements into another register.
INCr where r may be J or XY.
DECr where r may be J or XY.
INCr->s where r,s may be J or XY, and r not equal to s.
DECr->s where r,s may be J or XY, and r not equal to s.
- Read Memory Reads a memory byte from the address stored in r into register s
RDr->s where r may be J or XY and s may be A, B or C; or, r is J and s is X or Y
- Write Memory Write a byte from register s into memory (and, alternatively any mapped device such as the flipdot display) at the address stored in r.
WRr<-s where r may be J or XY and s may be A, B or C; or, r is J and s is X or Y
- Long Branch Continues program execution at a new label or address, possibly conditionally.
BR l Branch unconditionally to l
BRZ l Branch to l if condition code Z (zero) is set
BRNZ l Branch to l if condition code Z (zero) is not set
BRC l Branch to l if condition code C (carry) is set
BRNC l Branch to l if condition code C (carry) is not set
BRS l Branch to l if condition code S (sign) is set
BRNS l Branch to l if condition code S (sign) is not set
- Short Branch Continues program execution at a new label or address, possibly conditionally.
SBR l Branch unconditionally to l
SBRZ l Branch to l if condition code Z (zero) is set
SBRNZ l Branch to l if condition code Z (zero) is not set
SBRC l Branch to l if condition code C (carry) is set
SBRNC l Branch to l if condition code C (carry) is not set
SBRS l Branch to l if condition code S (sign) is set
SBRNS l Branch to l if condition code S (sign) is not set
- 8-Bit Moves Copies the value in r to register s.
MOVr->s, where r,s=A,B,C,X,Y and r not equal to s
- 16-Bit Moves Copies the value in r to register s.
MOVr->s, where r,s=XY,J,S,P and r not equal to s. Observe necessary precautions for fiddling with S (the stack pointer) or P (program counter) ;-)
- Unary ALU operations Performs ALU operation on the value in register r,
storing it in s. r and s may not be the same. If the "->s" construct is omitted, it does not store the value in a register, but only loads the condition code register according to the result. If s is MXY or MJ, the result is stored in memory at address given by XY or J respectively.
NOTr, NOTr->s, where r=A,B,C, s=A,B,C,MJ,MXY. Performs logical NOT.
INCr, INCr->s, where r=A,B,C, s=A,B,C,MJ,MXY. Increments r.
DECr, DECr->s, where r=A,B,C, s=A,B,C,MJ,MXY. Decrements r.
SRr, SRr->s, where r=A,B,C. Rotates the bits in r one step to the right.
- Binary heterogeneous ALU operations Performs ALU operation on the value in register r and the value in register s, storing it in t. No two of r, s and t may not be the same. If the "->t" construct is omitted, it does not store the value in a register, but only loads the condition code register according to the result. If t is MXY or MJ, the result is stored in memory at address given by XY or J respectively.
ANDrs, ANDrs->t, where r,s=A,B,C, t=A,B,C,MJ,MXY. Performs logical AND.
ORrs, ORrs->t, where r,s=A,B,C, t=A,B,C,MJ,MXY. Performs logical OR.
XORrs, XORrs->t, where r,s=A,B,C, t=A,B,C,MJ,MXY. Performs logical XOR.
- ALU addition operations Performs an addition operation on the value in register r and the value in register s, storing it in t. r and s MAY be the same register, but neither r or s may be the same as t. If the "->t" construct is omitted, it does not store the value in a register, but only loads the condition code register according to the result. If t is MXY or MJ, the result is stored in memory at address given by XY or J respectively.
Note that operations like ADDAAtt> is effectively the same as 2*A, and also A shifted to the left.
ADDrs, ADDrs->t, where r,s=A,B,C, t=A,B,C,MJ,MXY.
Fredrik Andersson - nablaman[at]nablaman.com
Back to Zuse home | Back to my home page