Many years ago, before affordable computers were available, I tried to build one out of relays. Unfortunately that never got much beyond the light-flasher stage. Recently, a friend gave me hundreds of reed relays, and that early goal got renewed. Here's the result.
Of course a relay computer can't begin to compete with modern machines, but it can reveal some of the mysteries of how they work. Mechanical relays are easy to understand. The RR6 sports 157 LEDs that bring to light the contents of its registers and the signals that manipulate those contents.
Video showing historic moment when the RR6 ran its first program.
The program memory (PROM: Programmable Read-Only Memory) consists of 32 8-bit words implemented with 32 DIP switches.
The clock speed is adjustable. The RR6 currently runs reliably up to three instructions per second (three micro MIPS). All instructions execute in two steps, and each step is four clock cycles. Clocks can also be executed one at a time using a momentary toggle switch.
User input: six front panel toggle switches (R7/IN).
User output: 7-segment display (R4 + Carry) and 157 LEDs that show the state of all registers and most of the logic.
The design is similar to a scaled down Microchip PIC16C5X. Other influences are from the 6502, x86, and John Doran's D16/M . Using a register, rather than a stack, to hold the return address for a subroutine call is from ARM processors. The RR6 has 26 instructions, described below.
About 640 diodes (1N4004 + 12 Schottky 1N5817) are used to implement OR logic circuits and to suppress kickback voltage on each relay.
Two wall-wart power adapters are used: 5 volts for relays, and 9 volts for the Clock relay flip-flops and for most of the LEDs (to reduce the load on the 5 volt 6 amp supply).
Most of the electronic components came from Jameco.com.
The computer is breadboarded on an actual breadboard that came from Target. The plastic (Hefty) case is also from Target, and it nicely fits the breadboard.
Sixteen perfboards from Radio Shack mount in slots cut in the breadboard. Radio Shack still exists on the Internet, and their perfboards at only $2 apiece were a big influence on the overall physical construction.
Labels for the boards were made using Windows WordPad (Lucinda Console with black highlighting) then glued on with rubber cement.
Bare wire is used to connect nearby components, much like a printed circuit board trace. Wire-wrap wire is used for longer connections and for the ribbon cable header connectors. The ribbon cables form a common bus that interconnects the boards.
A goal was to make a computer using pre-WWII components. Temptations to simplify things by using transistors or ICs were resisted. However, it was just too tempting to use LEDs instead of power consuming incandescent bulbs and switching power supplies instead of heavy iron transformers.
LDI = load accumulator (AC) with immediate value (0-63) JMP = jump to address (0-31) JSR = jump to subroutine at address (0-31) and store return address in link register (LR) LOD = load accumulator (AC) with value in register (R0-R7) STO = store value in accumulator (AC) into register (R0-R7) ADD = add value in register (R0-R7) to accumulator (AC) SUB = subtract value in register (REG: R0-R7) from accumulator (AC) AND = bitwise AND operation: AC <- AC & REG IOR = bitwise (inclusive) OR operation: AC <- AC | REG XOR = bitwise exclusive OR operation: AC <- AC ^ REG NOT = bitwise NOT operation: REG <- ~REG INC = increment register: REG <- REG + 1 DEC = decrement register: REG <- REG - 1 DSZ = decrement register REG and skip next instruction if result is zero CLR = clear (zero) register: REG <- 0 ROL = rotate register REG left, including Carry status bit ROR = rotate register REG right, including Carry status bit RET = return from subroutine CLC = clear Carry status bit SEC = set Carry status bit SCC = skip next instruction if Carry bit is clear SCS = skip next instruction if Carry bit is set SEQ = skip next instruction if equal zero (if Zero status bit is set) SNE = skip next instruction if not equal zero (if Zero status bit is clear) SAC = set accumulator (AC) with Carry bit (0 or 1) HLT = halt running by stopping the Clock (flip R/H switch up to resume) NOP = no operation (does nothing) Name Opcode Status Step 1 Step 2 ~~~~ ~~~~~~ ~~~~~~ ~~~~~~ ~~~~~~ PC' <- PC PC <- PC'+1 if not JMP JSR LDI 11 iii iii - AC <- PROM; TR <- 0 AC <- PROM (low 6 bits) JMP 10 1aa aaa - TR <- 0; C' <- 0 PC <- PROM (low 6 bits) JSR 10 0aa aaa - LR <- PC; TR<- 0; C'<-0 PC <- PROM (low 6 bits) LOD 01 111 rrr Z TR <- REG AC <- TR STO 01 110 rrr - TR <- AC REG <- TR ADD 01 101 rrr C,Z TR <- AC + REG; C'<-cy AC <- TR; C <- C' SUB 01 100 rrr C,Z TR <- AC - REG; C'<-cy AC <- TR; C <- C' AND 01 011 rrr Z TR <- AC & REG AC <- TR IOR 01 010 rrr Z TR <- AC | REG AC <- TR XOR 01 001 rrr Z TR <- AC ^ REG AC <- TR NOT 01 000 rrr Z TR <- ~REG REG <- TR INC 00 111 rrr Z TR <- REG + 1 REG <- TR DEC 00 110 rrr Z TR <- -1 + REG REG <- TR DSZ 00 101 rrr - TR <- -1 + REG REG <- TR; Skip if TR=0 CLR 00 100 rrr Z TR <- 0 REG <- TR ROL 00 011 rrr C TR <- REG + REG; C'<-cy REG <- TR; C <- C' ROR 00 010 rrr C TR <- REG >> 1; C'<-cy REG <- TR; C <- C' RET 00 001 rrr - TR <- LR + 1 PC <- TR CLC 00 000 111 C TR <- 0; C' <- 0; C <- C' SEC 00 000 110 C TR <- 0; C' <- 1; C <- C' SCC 00 000 101 - TR <- 0; C' <- 0; if ~C then Skip SCS 00 000 100 - TR <- 0; C' <- 0; if C then Skip SEQ 00 000 011 - TR <- 0; C' <- 0; if Z then Skip SNE 00 000 010 - TR <- 0; C' <- 0; if ~Z then Skip SAC 00 000 001 - TR <- 0; C' <- 0; AC <- C HLT 00 000 000 - TR <- 0; C' <- 0; Stop Clock NOP 01 110 000 (= STO AC) iii = immediate value (0-63) rrr = register (REG: R0-R7, except R5) C = carry status affected Z = zero status affected TR = transfer register (not accessable to programs) C' = pending carry status PC' = pending program counterThe Z status bit is set by the instructions indicated above if the result of the operation is zero, and is cleared if the result is non-zero.
The C status bit is set if the ADD instruction results in a carry-out of the most significant place (bit 5), and is cleared otherwise. For the SUB instruction the Carry bit is a "not borrow." If the result is positive, C is set; and C is cleared if the result is negative. (This works like the PIC, 6502 and ARM; but is reversed from the way the x86 operates.)
LDI 1 @ load 1 into the AC register LOOP: ROL AC @ rotate AC left one place (including carry) JMP LOOP @ jump back and do it againComputers were originally created to do calculations. This next program calculates degrees Celsius from degrees Fahrenheit using the formula C = (F-32)*5/9. F is entered in the front panel switches. It must be in the range 32 to 63 because of the 6-bit limitation. The answer is displayed in ac (r0).
ldi -32 @ r1 <- F-32 add in sto r1 ldi 5 @ r2:r1 <- r1*5 @ Multiply routine. r2:r1 <- r1*ac @ r2 @ +ac >> c >> r1 >> c mul: sto r2 @ r2 <- multiplicand ldi 6 @ r3 <- loop counter sto r3 clr ac @ ac:r1 <- r1*r2 ror r1 @ rotate LSB into carry for scc test m10: scc @ skip if carry = 0 add r2 @ else add r2 to ac if carry = 1 ror ac @ rotate right 13 bits in carry:ac:r1 ror r1 @ also sets carry for scc test dsz r3 @ loop for 6 places jmp m10 sto r2 @ return product in r2:r1 ldi 9 @ ac <- r2:r1/9 @ Divide routine. ac <- r2:r1/ac; remainder in r2 @ r2 << c << r1 << c @ -r3 div: sto r3 @ r3 <- divisor ldi 6 @ r4 <- loop counter sto r4 rol r1 @ (carry-in doesn't matter) d10: rol r2 @ shift in quotient bit lod r2 @ make trial subtraction sub r3 scc @ skip if r3 > r2 (it doesn't go) sto r2 @ else subtract r3 from r2 (it did go) rol r1 @ shift in carry bit dsz r4 @ loop for 6 bits jmp d10 lod r1 @ return quotient in ac hlt
.macro add reg:req .byte 0x68 + \reg .endmA simulator for the RR6, called simy.xpl, runs programs assembled by rasm. It was used to demonstrate the feasibility of making programs in 32 or fewer RR6 instructions before any hardware was built. Multiply and divide routines, prime and Fibonicci number generators, and a Towers of Hanoi solver are examples of programs that run.
A lower-level simulator, called siml.xpl, verified the design of the RR6 at its logic gate level. Writing statements (in XPL0), such as
if PCin & WrClk then Bus:= Bus ! Latches(PC); \JMPis simpler and can handle much larger circuits than something like PSpice. Unfortunately siml doesn't detect sneak paths, race conditions, diode voltage drops, or relay contact current overloads. Some of these had to be detected the hard way: after building the circuits.
I'm glad I paid the $15 for the crimper tool, used for the ribbon cable connectors, rather than scrimp and try to use a bench vise. I already owned a good soldering iron with a small tip, as well as many other basic tools. Oh yeah, had to buy a table saw to cut the slots in the breadboard.
How it was built.
Any comments are appreciated. You can reach me at: loren (dot) blaney (at) gmail (dot) com.