Magic-16
Here's the current definition and encoding of Magic-16's instruction set
architecture. Some notes:
| There are 8 16-bit general registers and at least one control register |
| General register 0 is hardwired to always return a 0 |
| General register 7 is hardwired as the program counter. |
| General register 2 is by convention the link register |
| General register 6 is by convention the stack pointer |
| Control register 0 will contain the carry bit, mode bit, interrupt enable,
etc. |
| Other than the carry bit, Magic-16 arithmetic operations are stateless
(i.e. no Z bit, V bit, etc) |
| The skip instruction will nullify execution of the following instruction
if (op1 <cond> op2) is true. |
| Skip conditions are encoded as follows. Note that unsigned greater
than and unsigned greater than or equal are missing. The compiler will
be responsible for swapping operands in these cases and using the
corresponding unsigned less/less equal conditions.
| 000 -> equal |
| 001 -> not equal |
| 010 -> signed less than |
| 011-> signed less than or equal |
| 100 -> signed greater than |
| 101 -> signed greater than or equal |
| 110 -> unsigned less than |
| 111 -> unsigned less than or equal |
|
| The addskip instruction will add op1 to op2 and store the result in res.
Additionally, if the result condition is met, the following instruction will
be nullified. The allowed result conditions are result zero (0) and
result non-zero (1). |
| Fields marked "I/R" denote that operand 1 is either a register or an
encoded 3-bit immediate. The immediate encoding is:
| 000 -> 1 |
| 001 -> 2 |
| 010 -> 4 |
| 011 -> 8 |
| 100 -> -8 |
| 101 -> -4 |
| 110 -> -2 |
| 111 -> -1 |
|
| Instructions marked with "*" may only be executed at the highest privilge
level |
| Loading a 16-bit immediate involves a two-instruction sequence: ldi to
load the low 9 bits, and addih to add in the high 7 bits. Note the 1-bit
overlap, which assembler macros will handle properly. |
| Conditional branching will be accomplished by placing the unconditional
branch instruction in the shadow of either a skip or addskip instruction. |
| Doing a procedure call requires explicit setting of the link register
prior to branching. This was done to avoid requiring two write ports on
the register file (though I may figure out how to set the link without penalty
during implementation). For example, to call "foo
| add 4,pc,link ; materialize return
address |
| br foo
; branch to foo |
|
| Most arithmetic operations are three address, but for some of the more
uncommon ones (addc, subc, xor, etc.) I'm limiting support to two address.
This is a little unclean, but I decided that they weren't worth the opcode
space. |
| I haven't yet defined the memory management instructions, paging &
segmentation support, return from interrupt and supported traps. I'll
likely do something similar to Magic-1 there. |
| I've left plenty of room in the encoding for adding new instructions.
Should be lots of room to play. |
Overall, I'm quite pleased with the encoding. As you can see, I've
dedicated the opcode space to correspond to the expected frequency of
instruction execution. Ideally, I'd have a bit more displacement space for
the loads and stores, but I'm pretty happy with the balance here.
Opcode group |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
ldw sw7(base),tgt |
0 |
0 |
0 |
signed
7-bit word offset |
base |
tgt |
ldb sb7(base),tgt |
0 |
0 |
1 |
signed
7-bit byte offset |
base |
tgt |
stw src,sw7(base) |
0 |
1 |
0 |
signed
7-bit word offset |
base |
src |
stb src,sw7(base) |
0 |
1 |
1 |
signed
7-bit byte offset |
base |
src |
br <label> |
1 |
0 |
0 |
signed
13-bit pc relative offset |
ldi s10,tgt |
1 |
0 |
1 |
signed
10-bit immediate |
tgt |
add op1,op2,res |
1 |
1 |
0 |
0 |
0 |
0 |
I/R |
op1 |
op2 |
res |
sub op1,op2,res |
1 |
1 |
0 |
0 |
0 |
1 |
I/R |
op1 |
op2 |
res |
and op1,op2,res |
1 |
1 |
0 |
0 |
1 |
0 |
I/R |
op1 |
op2 |
res |
or op2,op2,res |
1 |
1 |
0 |
0 |
1 |
1 |
I/R |
op1 |
op2 |
res |
skip.c op1,op2 |
1 |
1 |
0 |
1 |
0 |
0 |
I/R |
op1 |
op2 |
condition |
addskp.z op1,op2,res |
1 |
1 |
0 |
1 |
0 |
1 |
I/R |
op1 |
op2 |
res |
ldw (base,idx),tgt |
1 |
1 |
0 |
1 |
1 |
0 |
0 |
idx |
base |
tgt |
ldb (base,idx),tgt |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
idx |
base |
tgt |
stw src,(base,idx) |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
idx |
base |
src |
stb src,(base,idx) |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
idx |
base |
src |
shl cnt,src,res |
1 |
1 |
1 |
0 |
0 |
0 |
shift
count |
src |
res |
shr cnt,src,res |
1 |
1 |
1 |
0 |
0 |
1 |
shift
count |
src |
res |
addih u7,res |
1 |
1 |
1 |
0 |
1 |
0 |
7-bit
imm (<< 9 & add) |
res |
addc src,tgt |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
src |
res |
subb src,tgt |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
0 |
0 |
1 |
src |
res |
xor src,tgt |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
0 |
1 |
0 |
src |
res |
addto src,tgt |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
0 |
1 |
1 |
src |
res |
subto src,tgt |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
src |
res |
[reserved 2-operand] |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
src |
res |
[reserved 2-operand] |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
0 |
src |
res |
bv (tgt) |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
0 |
0 |
tgt |
sext res |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
0 |
1 |
tgt |
[reserved 1-operand] |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
tgt |
[reserved 1-operand] |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
tgt |
[reserved 0-operand] |
1 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
X |
copy cr,tgt (*) |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
0 |
0 |
cr |
tgt |
copy src,cr (*) |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
0 |
1 |
cr |
src |
[reserved (*)] |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
1 |
X |
[reserved (*)] |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
X |
addskp.nz op1,op2,res |
1 |
1 |
1 |
1 |
0 |
0 |
I/R |
op1 |
op2 |
res |
sh1add op1,op2,res |
1 |
1 |
1 |
1 |
0 |
1 |
I/R |
op1 |
op2 |
res |
sh2add op1,op2,res |
1 |
1 |
1 |
1 |
1 |
0 |
I/R |
op1 |
op2 |
res |
sh3add op1,op2,res |
1 |
1 |
1 |
1 |
1 |
1 |
I/R |
op1 |
op2 |
res |
|