The Autonomous Digital Adding Machine 1, or short ADAM/1, is an electronic digital computer based on 7400 series integrated circuits, with a word size of 16 bit (addresses and data).
The machine implements a micro programmable CISC instruction set and does not differentiate between instruction and data memory (Von Neumann architecture).
In this document, you will find an overview of the machine with hypertext links pointing to more detailed resources. Photographs are scaled for faster page loading but are linked to the original versions.
The prototype machine is currently located at the IT-Syndikat where it is being developed.
At the current time, almost all of the processing unit modules have been built and successfully tested. The micro code instruction decoder is still undergoing PCB lay-outing.
The design files and documentation can be found here.
An assortment of photos can be found here.
For the impatient computer scientists: At the bottom of this page is a simple introduction to the assembly language of the machine with an implementation of the ackermann function as an example. For more example snippets, click here.
Click here for a list of all instructions.
_____________________________________ |/////////////////////////////////////| |/| |/| <-- Backplane bus |/| ________________|\|/| |/| | ______________ |/| |/| | | |/|/| |/| ___ | | _____ |/| ____ __ |/|_|\| |______| |_|\| |/|__|\|/| |_R0_| | |/// | A |______ _ | I/O | __ |/| |_R1_| | |/| |/|___| | | |/|_____|\| |/|/| |_R2_| | General |/| | | _____ |/|/|__|\|_R3_| > Purpose |/| | |_|\| |/|__|\|/| __ |_R4_| | Registers |/| | _ | RAM | __ |/|\| |/|_R5_| | |/| | | |/|_____|\| |/|/| |_R6_| __| |/| ___ | | |/| |_R7_| __> Program Counter |/|_|\| |______|_|______________|\|/| Register |/// | B |_ ___________________ |/| File |/|___| | | | | |/|/| | | | | _______ |/| _____ | |__|_|_|\| |__|\|/|/|__|\| | | ______ | SHIFT |__ |/| __ | PSW | | | | | |/|_______| |/|/|\| |/|_____| | | | | _____ |/| | | | |_|\| | |/| _____ __ | | | _ | AND |____|\|/| |_C0__| | | | | | |/| OR |____ |/| |_C1__| | | |__|_|_|\| XOR | |/|/| |_C2__| > constants | ______ | | |/|/|__|\|_CH__| | | | | | |/|_____| |/| __ |_CL__| __| | | | | _____ |/|\| |/|_CR0_| | | | | |_|\| \ |/| |_CR1_| > control | | |___ |__ \ __|\|/| |_CR2_| __| registers | | |/ \ + \__ |/| Register | |______|\ __/ - / |/|/| File |________ | / |/|_____/ Adder/Subtractor
The diagram above outlines the logical design of the processor. The dashed line represents that physical back plane bus, aka Omnibus.
The blocks labeled A and B are internal operand register. The control logic can latch a word from the back plane bus into the operands. The operands are used for arithmetic and logic operations. The A operand is also used for addressing memory and memory mapped I/O modules.
A result from a module (or the original operand values) can be individually written back onto the bus.
A register module is provided with 8 general purpose registers. The control logic can select an arbitrary register onto the bus or latch a bus word into a register.
A second register file block is shown that contains constants and control registers. It works conceptually similar to the general purpose register file. The control logic assumes the individual control/constant registers to have certain values (e.g. +1 for increment, or the interrupt handle address).
The PSW block contains the only special purpose register, the processor status word that latches e.g. carry from the adder and other flags for conditional execution. It also contains additional control flags (user mode, interrupt enable, MMU enable) that change the behavior of the control logic.
The machine is controlled by a microcode based state machine. The current instruction opcode, various status lines and a counter controlled by the main system clock are used for addressing a big ROM. The ROM output lines are decoded and connected to the control lines on the back plane bus.
The control logic uses the 7th GP register as program counter.
To fetch an instruction, the micro program loads the value of the general purpose register R7 into the A operand, fetches an instruction word from that address, uses the ALU adder to increment the value and writes the result back to the register.
This simplifies hardware design for a number of reasons:
Similarly, the machine has no dedicated stack. Instead generic, power full, indirect load/store instructions with pre/post increment or decrement addressing modes are provided that can be used to implement a stack.
For implementing a program call stack, a SEX instruction (subroutine execute) is provided where an arbitrary register can be provided for storing the program counter with post decrement semantic and atomically loading a new value into R7. For returning to the original location, a pre-increment indirect load into R7 can be used.
This makes the instruction set very generic and adds many use full instructions while keeping the hardware simple.
However, for interrupts and traps, the CPU needs a dedicated kernel mode stack pointer. For this purpose, the GP register R6 is used. When transitioning into kernel mode, the R6 stack pointer is restored from a control register and inversely saved into the same control register when transitioning back.
The machine instructions are encoded in a uniform format as follows:
15|14 13 12 11 10 9|8 7 6|5 4 3|2 1 0 I| OPCODE |M M M| dst | src | | | | | | | | | +------- Source register index | | | | | | | +--------------- Desination register or | | | flag index | | | | | +----------------------- Condition mode | | | +------------------------------------ Operation | +--------------------------------------------- Immediate flag
Instead of providing conditional jump instructions, every instruction contains a condition mode that is decoded as follows:
Thus, any instruction can be skipped depending on previous computation results.
The immediate flag in the beginning of the instruction word specifies whether an 16 immediate argument follows after the instruction word.
Since all quantities are multiples of 3, it may be easiest for humans to work with executable code by viewing it as octal values.
The processor design is implemented by a number of RTL modules connected through a DIN41612 back plane bus (the Omnibus).
Each module has its own copy of the A and B operand registers. An output is generated asynchronously and "held back" by a tri-state buffer:
BUS . . . . . . . . . . . . . . . . . . . . . . . . . . | | . . | |/|________________________________________________ . | | ______________________________________________ | . | |\| . ___ _________ ___________ | | . | |______|\| |__|\| | | |___| | . | |______ | A |__ | |__|\| Tri-state |_____| . | | . |/|___| |/| Logic |__ | Buffer | . | |______|\| |__|\| | |/|___________| . | |______ | B |__ | | . | | . |/|___| |/|_________| . | | . . | | . . . . . . . . . . . . . . . . . . . . . . . . . . Back plane RTL Module PCB
All A operand register on all modules are triggered at the same time and all B operand registers are triggered at the same time, i.e. there is one line on the control bus to latch all A operands, and one to latch all B operands.
The output enable lines of the tri-state buffers on the modules are triggered individually by address decoders on each module mapping 5 bit addresses to module outputs.
The notable exceptions to this design are modules like the register module that contains a row of 16 bit registers, a read decoder and a write decoder and has no need for the operand registers.
The modules are plugged into a DIN41612 back plane bus with a 5 bit source address, 3 bit destination address, 16 bit tri-state data bus and various control lines:
BUS __________ _ ________ | SHIFT |/|____|\| |/|____|\| LOGIC | | REGISTER | ____ | | ____ | MODULE | |__________|\| |/| |\| |/|________| ________ | | ___________ | ADDER |/|____|\| |/|____|\| CONTROL | | MODULE | ____ | | ____ | REGISTERS | |________|\| |/| |\| |/|___________| __________ | | ______________ | REGISTER |/|____|\| |/|____|\| MEMORY & I/O | | MODULE | ____ | | ____ | MODULE | |__________|\| |/| |\| |/|______________| | | _____________ | |/|____|\| STATUS WORD | | | ____ | MODULE | | |\| |/|_____________| _| |_ ________\___/________ | | | CONTROL LOGIC | |_____________________|
Click here for more details on the hardware.
A total of 70 instructions is currently allocated. A lot of assembler directives are provided that implement instructions commonly found on other machines and translate to single opcodes. Click here for a list of all instructions.
As inidicated by the encoding above, every instruction has two register indices and an optional immediate argument. The assembler writes destination register on the left, source on the right. For instance:
MOV R0, R1 # copy R1 into R0, no immediate argument ADD R0, R1, 5 # add immediate argument 5 to R1, store the result in R0
An '@' denotes indirect addressing:
MOV R0, @R1 # store in R0 what is stored at the memory # location pointed to by R1 MOV R0, @R1+5 # same as above, but add an immediate offset # of 5 the address in R1 MOV @R0, @R1 # memory to memory copy
The skip modes can be specified with a prefix to the mnemonic:
Z.ADD R7, -3 # Add -3 to R7 if the zero flag is set. # Because R7 is the program counter, this is # effectively a relative jump. NC.SEX R6, foobar # If the carry flag is _not_ set, exececute the # subroutine foobar. Store the return address at the # location that R6 points to and decrement R6. # Other machines refere to this as 'CALL', possibly a # legacy from their origin in telephony.
The register prefix and suffix '+' and '-' can be used to denote post increment, post decrement, pre increment or pre decrement addressing modes:
MOV R7, @+R6 # Increment R6 and fetch from the resulting address. # Store the value into R7. This is the 'return' for # the above Subroutine EXecute.
This is an example on how one could implement the ackermann function without using any of the special assembler pseudo instructions:
################################################### # compute ackermann(m,n) # # R0 -> m intput, result output # R1 -> n input # # R6 -> top of push down stack # ################################################### ack: # if m == 0 -> return n + 1 TST R0, R0 NZ.MOV R7, skip1 # goto skip1 if zero INC R0, R1 # R0 := R1 + 1 MOV R7, @+R6 # return skip1: # if m > 0 && n == 0 -> return ack(m-1, 1) TST R1, R1 NZ.MOV R7, skip2 # goto skip2 if zero DEC R0, R0 # R0 := R0 - 1 LSD R1, C1 # R1 := +1 MOV R7, ack # tail call recursion skip2: # if m > 0 && n > 0 -> return ack(m-1, ack(m, n-1)) MOV @R6-, R0 # push R0 DEC R1, R1 # R1 := R1 - 1 SEX R6, ack # R1 := ackermann(m, n-1) MOV R1, R0 MOV R0, @+R6 # pop R0 DEC R0, R0 # R0 := R0 - 1 MOV R7, ack # tail call recursion
For more example snippets, click here