I'm posting this at
BigEd's request. Nevertheless, and as always, all questions, comments and criticisms are welcome.
RAMON5 ArchitectureSummaryRAMON5 is a simple 8-bit architecture of my own design. To the best of my belief, it is turing complete (with all the normal provisos attached to that statement).
It has the following specifications:
- Harvard Architecture
- 11 registers (8 general purpose)
- 9-bit address space for RAM (divided equally into general purpose and stack)
- 12-bit address space for Instruction ROM
- Single interrupt pin with acknowledgement
- Eight 8-bit I/O ports
- Either 32 or 46 instructions (depending on how you count them)
InstructionsAll available instructions are listed in the table below. Some non-obvious notation I've used for the Action column:
- All actions are carried out sequentially left to right and separated by a semicolon ";".
- Equals sign "=" represents both assignment and equality depending on context.
- << and >> are left and right logical shift of the left-hand operand by the number on the right-hand.
- +, -, &, ^, ~ and | are addition, subtraction, bitwise AND, XOR, NOT and OR respectively.
- xF means Flag x
- A single capital letter R or I on its own is a reference to the Register or Immediate Value, respectively, as found in the Opcode Syntax
- $x means Register x (fixed)
- y[x] means reading or writing to the location indirectly addressed by x within address space y.
Code:
Opcode Syntax Mnemonic Description Action
00000RRR LACC Load Accumulator $0 = R
00001RRR SACC Store Accumulator R = $0
00010RRR PUSH Push to Stack Stack[$SP] = R; $SP = $SP + 1
00011RRR POP Pop from Stack $SP = $SP - 1; R = Stack[$SP]
00100RRR PUT Write to Memory Memory[R] = $0
00101RRR GET Read from Memory $0 = Memory[R]
00110III OUT Write I/O Port Port[I] = $0
00111III IN Read I/O Port $0 = Port[I]
01000RRR ADD Add $0 = $0 + R
01001RRR ADC Add w/Carry $0 = $0 + R + CF; CF = Carry Out; VF = Overflow out
01010RRR SUB Subtract $0 = $0 - R
01011RRR SBC Subtract w/Carry $0 = $0 - R - 1 + CF; CF = Carry Out; VF = Overflow out
01100RRR AND Bitwise And $0 = $0 & R
01101RRR NOR Bitwise Nor $0 = ~($0 | R) (Implement using $0 = ($0 | R) ^ 0xFF)
01110RRR XOR Bitwise Xor $0 = $0 ^ R
01111RRR XACC Exchange Accumul. (temp) = $0; $0 = R; R = (temp)
10000RRR IIIIIIII ADDI Add Immediate R = R + Immediate[I]
10001RRR IIIIIIII ADCI Add Imm. w/Carry R = R + Immediate[I] + CF; CF = Carry Out; VF = Overflow Out
10010xxx IIIIxxxx CLRF Clear Flags $FL = $FL & Immediate[I]
10011xxx IIIIxxxx SETF Set Flags $FL = $FL | Immediate[I]
10100III RSH Right Shift $0 = $0 >> Immediate[I], bit-shifted in = 0.
10101III LSH Left Shift $0 = $0 << Immediate[I], bit-shifted in = 0.
10110III RSHC Right Shift w/Carry $0 = $0 >> Immediate[I], bit-shifted in = CF; CF = Carry out; VF = 0
10111III LSHC Left Shift w/Carry $0 = $0 << Immediate[I], bit-shifted in = CF; CF = Carry out; VF = 0
11000xxx xxxxIIII IIIIIIII JMP Jump $IP = Immediate[I]
11001RRR xxxxIIII IIIIIIII JMPO Jump w/Offset $IP = Immediate[I] + R
11010xxx xxxxIIII IIIIIIII CALL Call Subroutine Stack[$SP] = ($IP >> 8) | ($FL << 4); $SP = $SP + 1; Stack[$SP] = ($IP & 0xFF); $SP = $SP + 1; $IP = Immediate[I]
11011xxx IIIIIIII RET Return from Sub $SP = $SP - Immediate[I] - 1; $IP = Stack[$SP]; $SP = $SP - 1; $FL = Stack[$SP] >> 4; $IP = $IP | Stack[$SP] << 8
11100xxx IIIIIIII SKIP Skip Forward $IP = $IP + Immediate[I]
11101xxx IIIIIIII RWND Skip Backward $IP = $IP - Immediate[I]
11110000 IIIIIIII SKIPNC Skip if CF Clear If (CF = 0) Then { $IP = $IP + Immediate[I] }
11110001 IIIIIIII SKIPNV Skip if VF Clear If (VF = 0) Then { $IP = $IP + Immediate[I] }
11110010 IIIIIIII SKIPNI Skip if IF Clear If (IF = 0) Then { $IP = $IP + Immediate[I] }
11110011 IIIIIIII SKIPNU Skip if UF Clear If (UF = 0) Then { $IP = $IP + Immediate[I] }
11110100 IIIIIIII SKIPC Skip if CF Set If (CF = 1) Then { $IP = $IP + Immediate[I] }
11110101 IIIIIIII SKIPV Skip if VF Set If (VF = 1) Then { $IP = $IP + Immediate[I] }
11110110 IIIIIIII SKIPI Skip if IF Set If (IF = 1) Then { $IP = $IP + Immediate[I] }
11110111 IIIIIIII SKIPU Skip if UF Set If (UF = 1) Then { $IP = $IP + Immediate[I] }
11111000 IIIIIIII RWNDNC Skip if CF Clear If (CF = 0) Then { $IP = $IP - Immediate[I] }
11111001 IIIIIIII RWNDNV Skip if VF Clear If (VF = 0) Then { $IP = $IP - Immediate[I] }
11111010 IIIIIIII RWNDNI Skip if IF Clear If (IF = 0) Then { $IP = $IP - Immediate[I] }
11111011 IIIIIIII RWNDNU Skip if UF Clear If (UF = 0) Then { $IP = $IP - Immediate[I] }
11111100 IIIIIIII RWNDC Skip if CF Set If (CF = 1) Then { $IP = $IP - Immediate[I] }
11111101 IIIIIIII RWNDV Skip if VF Set If (VF = 1) Then { $IP = $IP - Immediate[I] }
11111110 IIIIIIII RWNDI Skip if IF Set If (IF = 1) Then { $IP = $IP - Immediate[I] }
11111111 IIIIIIII RWNDU Skip if UF Set If (UF = 1) Then { $IP = $IP - Immediate[I] }
Additionally, there are some pre-defiend Virtual Instructions which are converted into real instruction(s) by the Assembler:
Code:
Mnemonic & Operands Description Equivalent To
SUBI R, I Subtract Immediate ADDI R, (~I + 1)
SBCI R, I Subtract Imm. w/Carry ADCI R, (~I + 1)
NOOP No-operation LACC $0
HALT Wait for next Interrupt RWND 2
BIT I Bit Test Accumulator RSHC I [CF=$0's I-th bit; $0 is modified.]
TZER R Test for Zero CLRF C; ADCI R, 255 [CF=0 if Zero, else CF=1; R is modified.]
TEQU R Test for Equality SUB R; TZER $0 [Compares $0 and R, if they equal eachother then CF=0, else CF=1. R is preserved, but $0 is not]
RegistersCode:
Mnemonic Description Width State On Reset
$0 Accumulator 8 0
$1 Data 8 Undefined
$2 Data 8 Undefined
$3 Data 8 Undefined
$4 Data 8 Undefined
$5 Data 8 Undefined
$6 Data 8 Undefined
$7 Data 8 Undefined
$FL Flags 4 0
$SP Stack Pointer 8 0
$IP Instruction Pointer 12 0
$0 is the Accumulator. Many instructions implicitly use it as an operand.
$1 through $7 are general purpose data registers.
$FL stores the four flags (see section below).
$SP stores the current stack pointer. It cannot be directly accessed.
$IP stores the address of the next instruction to be executed. It cannot be directly accessed.
FlagsCode:
Bit# Mnemonic Description
0 CF Carry Flag
1 VF Overflow Flag
2 IF Interrupt Enable Flag
3 UF User-Condition Flag
CF and VF are self-explanatory.
IF: see the Interrupts section below.
UF: Not used or changed by hardware, and the programmer is free to use it as a custom state flag, complete with its own branch instructions.
* Originally UF was planned to be a Zero Flag (ZF) but due to a quirk of the hardware design, it would not be possible to update UF/ZF during an arithmetic operation without also clearing IF.Interrupts SystemOn seeing a High Level on the INT pin, if IF is also High, the processor will wait for the current instruction to complete,
then it will execute the equivalent of "CALL 3", except that $FL will be completely cleared (the saved copy of $FL on the stack will be as normal).
This is done to ensure that IF is low so that nested interrupts will not occur (unless explicitly permitted by the interrupt handler setting IF high again).
Additionally, the INTACK pin will be pulsed high for a clock cycle - this can optionally be used by the sending peripheral to clear its interrupt condition.
Reset SystemA low level on !RST will immediately reset the processor. This will guarentee that $FL, $SP, $IP and $0 are zeroed. Execution will begin once !RST is raised high again. Contents of registers $1-$7, RAM and Ports are undefined.
Example CodeThis is a very, very simple test program which outputs the numbers 0 to 9 on port 0 then loops.
The assembled bytecode is on the left, with "addresses" prefixed by a dot. This was assembled by hand and does work in the Simulator.
Code:
.00 00 00 .label %reset
C0 00 06 JMP %start # Go straight to the start of the code.
.00 00 03 .label %isr
D8 00 RET 0 # We're not using the ISR so we'll NOOPify it.
00 NOOP
.00 00 06 .label %start
7F XACC $7 # Swap registers 0 and 7's values.
70 XOR $0 # XOR Register 0 with itself = 0.
80 09 ADDI $0, 9 # Add the immediate value 9 to register 0 = 9.
7F XACC $7 # Swap registers 0 and 7 => Reg7 = 9.
.00 00 0B .label %repeat
07 LACC $7 # Copy register 7's value into Register 0.
30 OUT 0 # Output register 0 onto port 0.
90 E0 CLRF C # Ensure the carry flag is clear.
8F FF SBCI $7, 1 # Subtract with carry 1 from register 7.
FC 08 RWNDC %repeat # If Reg7 >= 1 then CF=True, so go to %repeat to decrement again.
E8 10 RWND %start # If Reg7 < 1 this will run, go back to start.
.00 00 15 .label %end
FAQWhat's with the name?RAMON - Its a very long tale which I won't go into, but it originally stood for "Rexterran Administrator's Mechanical Oracle (New)". Suffice to say that nowadays I just name my CPU designs "RAMONx" where x is the iteration. This being the 5th iteration we get
RAMON5.
Have you built it in hardware?No, not yet. Though I have a design that uses (almost) exclusively 74xx series logic and in principle should be able to work in hardware, and intend to build it at some point in the indefinite future. I do have it running in a simulator.
What kind of hardware are we talking about?Approximately 58 74xx series logic chips and five microcode ROMs which operate 40 control signals. The pinout has 40 signals: 8-bit bidirectional databus, 9-bit data address bus, 8-bit instruction databus, 12-bit instruction address bus, clock, !reset, interrupt, interrupt acknowledge, read/!write, !memory select and !port select.
What tools are there? Assembler? High-Level Compiler?Nothing complete. I am working on an assembler called RASM, but I havn't got very far as yet. I'm have trouble with some aspects of it... I'm not a computer scientist and algorithms make my brain hurt.