Instruction Set

Registers are written in upper case (the order may well change to assist in address decoding):

 A 000 general purpose
 B 001 general purpose
 C 010 general purpose (Call stack)
 D 011 general purpose (Data stack)
 E 100 general purpose (Execution pointer)
 Z 101 read Zero, write ignored
 P 110 program counter
 I 111 read 1 (Identity), write Instruction

Instructions are of one of these three forms:

 [condition] Rz = Rx op Ry 0xxx register to register through the ALU (xxx=op)
 [condition] Rx = *Rz, Rz +-= Ry 100x read from memory and subtract (x=0) or add (x=1) to memory pointer
 [condition] *Rz = Rx, Rz +-= Ry 101x write from memory and subtract (x=0) or add (x=1) to memory pointer

e.g. push onto a stack if the previous result was non-zero

ne0 && *E++ = A

Note that whilst the READ may be considered to do a POP instruction, the write is not the inverse of this.   The memory access and ALU run concurrently, to have an inverse would necessitate the ALU to run first and the resulting address used for memory access.

In the first form, Rz = Rx op Ry

 Rz = Rx & Ry     000 AND
 Rz = Rx | Ry  001 OR
 Rz = Rx ^ Ry 010 XOR exclusive or
 Rz = Rx >> 1 011 ASR arithmetic shift right
 Rz = Rx + Ry 100 ADD without carry
 Rz = Rx + Ry + c 101 ADD with carry
 Rz = Rx - Ry 110 SUB without carry
 Rz = Rx - Ry - c 111 SUB with carry

There is no NOT, just compute -1 (as 0 - 1) then XOR with that.

Here are some short cuts and implementation tricks:

Z = Rx op Ry sets the flags and discards the result
Rz = Rx is a synonym for Rz = Rx | Z, i.e. OR with zero does nothing
Rz = *Rx is LOAD
*Rz = Rx is STORE
Rz = *Rx++ is a synonym for *Rz = Rx, Rx += I i.e. POP with descending full stack
Rz = *P++ means load immediate.   P is incremented whether this instruction is run or not.
P = Ry means JUMP to Ry
P = P + Ry means branch relative by Ry
P = *P means JUMP immediate

There are 8 conditions (total of three bits):

nop 000 never execute
  001 always execute
 eq0 010 last result == 0
 ne0 011 last result != 0
 lt0 100 last result < 0
 le0 101 last result <= 0
 ge0 110 last result >= 0
 gt0 111 last result > 0

There is no test of the carry bit.  If you want to know what the carry bit is then ADD zero and zero (Z = Z + Z + c) then test.

The condition normally stops the whole instruction from executing.  However, if there is a add/sub on P then it happens regardless of the condition (as otherwise it wouldn't skip over the next instruction).  This leads to the classic C conundrum, P = *P++.   If this executes then it runs as P = *P, however if it doesn't execute then P++ does so that the next instruction is lined up as expected.

To call a subroutine is only two cycles (3 words):
  • E = P + I
  • P = *P, P += I   # JUMP immediate
  • :subroutineAddress
To return from a subroutine is only one cycle:
  • P = E + I  # P is now the instruction after the address
If the subroutine calls another, or has any other use for the E register then it must push it onto the stack.