This is a WIP of my own ISA.
The architecture is:
There are 12 8 bit registers: R0 to R11 (corresponding to numbers 0 to 11). Some registers for pairs which are denoted as RX:RY where RX is the register storing the upper 8 bits and RY register storing the lower 8 bits.
Upon start/reset the computer clears all registers to 0 except for IP which is set to 2. Instructions then start to be executed until either halting instruction is encountered, in which cases the computer sets the corresponding exit code and then stops executing instructions.
The stack pointer should be initialized by the program, typically as one of the first operations.
During execution of instruction X IP will contain the address of instruction X. The instruction itself may or may not modify the value of IP. When execution of X has finished, IP is incremented by 2.
As long as the computer is running the clock tick counter (see memory layout) is incremented after every clock tick (note that execution of one instruction may take more than one clock tick).
The instruction size is fixed at 16 bits. The first 8 bits are the opcode, the following 8 bits hold the arguments.
Instruction arguments are always numerical. Bits that aren't used for arguments should always be 0 and bits used for arguments may only contain allowed values, otherwise the instruction may be considered malformed.
In the following table argument are specified from the LSB with the number of bits in the brackets.
abbr. | name | opcode | arguments | description | flags |
---|---|---|---|---|---|
ARITHMETIC | |||||
ADD | add | r1(4) r2(4) | CLC; ADD r1 r2 | CZ | |
ADC | add with carry | r1(4) r2(4) | r1 := r1 + r2 + FL[C]; | CZ | |
SUB | substract | r1(4) r2(4) | NEG r2; ADD r1 r2 | CZ | |
SUC | substract with carry | r1(4) r2(4) | NEG r2; ADC r1 r2 | CZ | |
NEG | arithmetic neg. | r(4) | r := -r (two's complement) | Z | |
MUL | multiply | ||||
INC | increment | r(4) | CZ | ||
DEC | decrement | r(4) | CZ | ||
ADI | add immediate | n(8) | r1 := r1 + n; FL[C] = carry | CZ | |
SUI | substract immediate | n(8) | r1 := r1 + neg(n); FL[C] = carry | CZ | |
LOGIC | |||||
ORR | bitwise OR | Z | |||
AND | bitwise AND | Z | |||
XOR | bitwise XOR | Z | |||
NOT | bitwise NOT | r(4) | r := bitwise_not(r) | Z | |
TOL | to logical value | r1(4) r2(4) | r1 := 1 if r2 != 0 else 0 | Z | |
MEMORY | |||||
LDM | load from memory | 00000010 | r(4) | r := mem[AP] | |
STM | store to memory | 00000011 | r(4) | mem[AP] := r | |
PSH | push onto stack | 00000010 | r(4) | mem[SP] := r; SP++ | |
POP | pop from stack | 00000011 | r(4) | r := mem[SP]; SP-- | |
LI0 | load immediate to R0 | v(8) | R0 := v | ||
LI1 | load immediate to R1 | v(8) | R1 := v | ||
MOV | move | 00000001 | r1(4) r2(4) | r1 := r2 | |
BST | set bit | r(4) b(3) | r[b] := 1 | Z | |
BCL | clear bit | r(4) b(3) | r[b] := 0 | Z | |
BNO | negate bit | r(4) b(3) | r[b] := not(r[b]) | Z | |
TESTS | |||||
TST | test bit | r(4) b(3) | FL[T] := r[b] | T | |
LUU | compare less uns. uns. | T | |||
LUS | compare less uns. sig. | T | |||
LSS | compare less sig. sig. | T | |||
EQU | equals | r1(4) r2(4) | FL[T] := R1 == R2 | T | |
OTHER | |||||
SR0 | shift right 0 | CZ | |||
SRC | shift right copy | CZ | |||
SL0 | shift left 0 | r := r << n (LSB := 0); FL[C] := r[MSB] | CZ | ||
SLC | shift left copy | r := r << n (keep LSB); FL[C] := r[MSB] | CZ | ||
SWP | swap | r1(4) r2(4) | swap(r1,r2) | ||
CONTROL | |||||
JIF | jump if | if (FL[T]) IP := AP - 2 | |||
JIN | jump if not | if (!FL[T]) IP := AP - 2 | |||
SIF | skip if | n(8) | if (FL[T]) IP := IP + offset(n) | ||
SIN | skip if | n(8) | if (!FL[T]) IP := IP + offset(n) | ||
END | end | 11111111 | |||
NOP | no operation | 00000000 | do nothing, waste a cycle |
offset(n) = 2 * (n <= 127 ? n + 1 : n - 256)
pseudo instructions:
abbr. | parameter | name | definition |
---|---|---|---|
JMP | jump | MOV 10 6; MOV 11 7 | |
CLC | clear carry | BLC 5 6 |
TODO: instructions have to be aligned???
The memory model is Von Neumann (same memory for data and program instructions). Memory also serves for I/O mapping.
Every byte has access priviledges, R (reading allowed), W (writing allowed), RW (both reading and writing allowed) or none (inaccessible). Violating the priviledge results in program halting immediately and corresponding exit code being set.
The memory layout follows:
addresses (hex) | type | description |
---|---|---|
0000 | NULL address, inaccessible, allows definition of NULL pointer to be 0. | |
0001 | Unusable, exists to make instructions word-aligned. | |
0002 | RW | Start of program instructions. |
PRG_END | RW | Last program instruction (has to be END), this is program-specific. |
RW | Stack memory. | |
STACK_END | RW | Highest possible stack address (configurable). |
RW | Program working memory | |
MEM_END | RW | Highest address of a continuous RW block since 0001, platform specific. |
SPECIAL MEMORY FOLLOWS (available regardless of memory amount) | ||
ff00 ... ff7f | ??? | Platform-specific (including priviledges) I/O mapped ports. |
Reserved. | ||
fff9 fffa | WR | STACK_END, if R12:R13 == this number, program halts. Can be 0 (unlimited stack). |
fffb fffc | R | RAM amount, contains the number MEM_END. |
fffd fffe | R | Tick count, counts (with overflow) clock ticks since program start. |
ffff | R | After program contains the exit code (see below). |
TODO: STACK???
Exits codes (at least the values 0xff and 0x00 have to be implemented):