-
Notifications
You must be signed in to change notification settings - Fork 8
/
README.VM
164 lines (131 loc) · 8.51 KB
/
README.VM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
Address space and word width
----------------------------
The machine can address up to 64k of memory. The memory can be addressed in
units of single bytes, but local variables are always 16bit words.
It operates on 16bit words that are always interpreted as signed integeres in
context where signed/unsigned does make a difference (multiply/divide and
the compare operators).
The memory is always accessed using reader/writer functions that are provided
by the host environment.
All 16bit memory operations are performed in big endian byte order.
Machine State
-------------
The machine state itself is managed using three state variables:
IP (Instruction Pointer)
The address of the next instruction to execute
SP (Stack Pointer)
The address of the last pushed word on the stack.
SFP (Stack Frame Pointer)
The address of the first local variable on the stack.
Also there are three function pointers in the machine state for
embedding the VM in a host environment: a reader and a writer
for accessing the VM memory and a callback for calling user
functions.
The Stack
---------
The stack grows from high address to low addresses.
Each local variable is accessed using an SFA (Stack Frame Address).
The address of a local variable is:
SFA >= 0 ? SFP - 2*SFA - 2 : SFP - 2*SFA + 2
There is a maximum of 32 local variables in a stack frame and 32
arguments to a function (arguments have negative SFA).
Calculations are done in a stackmachine like manner (each operation
pops its operands from the stack and pushes the result).
The return address for the current function is stored in the 16-bit word
on SFP + 2 and the parent stack frame pointer at SFP + 4.
All variables on the stack must be aligned on even addresses. So the stack
pointer must be initialized to an even address (LSB not set).
Instruction Encoding
--------------------
Most instructions fit in a single byte. Some have a 1 or 2 bytes argument.
Some instructions push and/or pop values to/from the stack.
Not all of the instructions are actually used by the compiler.
+-------------------------------+----------------------------------------+----+
| MSB LSB | Instruction | Off|
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 0 | 0 | SFA | Push local variable to stack | 00 |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 0 | 1 | SFA | Pop local variable from stack | 40 |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 0 | 0 | 0 | 0 .. 0x0 | Add (pop 2, push 1) | 80 |
| 1 | 0 | 0 | 0 | 1 .. 0x1 | Sub (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 2 .. 0x2 | Mul (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 3 .. 0x3 | Div (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 4 .. 0x4 | Mod (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 5 .. 0x5 | Shift Left (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 6 .. 0x6 | Shift Right (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 7 .. 0x7 | Bitwise AND (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 8 .. 0x8 | Bitwise OR (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 9 .. 0x9 | Bitwise XOR (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 10 .. 0xa | Logic AND (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 11 .. 0xb | Logic OR (pop 2, push 1) | |
| 1 | 0 | 0 | 0 | 12 .. 0xc | Bitwise NOT (pop 1, push 1) | |
| 1 | 0 | 0 | 0 | 13 .. 0xd | Arithmetic invert (pop 1, push 1) | |
| 1 | 0 | 0 | 0 | 14 .. 0xe | Logic NOT (pop 1, push 1) | |
| 1 | 0 | 0 | 0 | 15 .. 0xf | Reserved | |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 0 | 0 | 1 | 0 | VAL | Push immediate (VAL is signed) | 90 |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | Push unsigned 1-byte argument | 98 |
| 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | Push signed 1-byte argument | 99 |
| 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | Push signed 2-byte argument | 9a |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | Return from function (pop 1) | 9b |
| 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | Return from function without value | 9c |
| 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | Drop value (pop 1) | 9d |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | Call address (pop 1) | 9e |
| 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | Jump to address (pop 1) | 9f |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 0 | 1 | 0 | 0 | 0 | Jump (1-byte rel. address) | a0 |
| 1 | 0 | 1 | 0 | 0 | 1 | Jump (2-byte rel. address) | |
| 1 | 0 | 1 | 0 | 0 | 2 | Call (1-byte rel. address) | |
| 1 | 0 | 1 | 0 | 0 | 3 | Call (2-byte rel. address) | |
| 1 | 0 | 1 | 0 | 0 | 4 | Jump IF (pop 1, 1-byte rel. address) | |
| 1 | 0 | 1 | 0 | 0 | 5 | Jump IF (pop 1, 2-byte rel. address) | |
| 1 | 0 | 1 | 0 | 0 | 6 | Jump UNLESS (pop 1, 1-byte rel. addr.) | |
| 1 | 0 | 1 | 0 | 0 | 7 | Jump UNLESS (pop 1, 2-byte rel. addr.) | |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 0 | 1 | 0 | 1 | 0 | Compare "<" (pop 2, push 1) | a8 |
| 1 | 0 | 1 | 0 | 1 | 1 | Compare "<=" (pop 2, push 1) | |
| 1 | 0 | 1 | 0 | 1 | 2 | Compare "==" (pop 2, push 1) | |
| 1 | 0 | 1 | 0 | 1 | 3 | Compare "!=" (pop 2, push 1) | |
| 1 | 0 | 1 | 0 | 1 | 4 | Compare ">=" (pop 2, push 1) | |
| 1 | 0 | 1 | 0 | 1 | 5 | Compare ">" (pop 2, push 1) | |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 0 | 1 | 0 | 1 | 6 | Stack Pointer (push 1) | ae |
| 1 | 0 | 1 | 0 | 1 | 7 | Stack Frame Pointer (push 1) | af |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 0 | 1 | 1 | Func-ID | Call User Function | b0 |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 1 | 0 | M | Load 8u (push 1, M is addr mode) | c0 |
| 1 | 1 | 1 | M | Store 8u (pop 1, M is addr mode) | c8 |
| 1 | 1 | 2 | M | Load 8s (push 1, M is addr mode) | d0 |
| 1 | 1 | 3 | M | Store 8s (pop 1, M is addr mode) | d8 |
| 1 | 1 | 4 | M | Load 16 (push 1, M is addr mode) | e0 |
| 1 | 1 | 5 | M | Store 16 (pop 1, M is addr mode) | e8 |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 1 | K | 5 | Bury dup of top in depth K (max 5) | // |
| 1 | 1 | K | 6 | Dig up the element in depth K (max 5) | // |
| 1 | 1 | K | 7 | Reserved | // |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 1 | 6 | N | Push N+1 zeros to the stack | f0 |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
| 1 | 1 | 7 | N | Pop N+1 values but keep top | f8 |
+---+---+---+---+---+---+---+---+----------------------------------------+----+
Adress Modes (M) for "Load" and "Store" instructions:
0 ..... address as 1 byte (unsigned) argument
1 ..... address as 2 byte argument
2 ..... address as popped value (pop before data)
3 ..... address as popped + 1 byte (unsigned) argument
4 ..... address as popped + 2 byte argument
5-7 ... shuffle instructions (not load/store)
Note: for 16bit load and store operations in mode 3 and 4 the popped
data from the stack is multiplied by 2.
A "bury" with K=0 simply duplicates the top element on the stack;
A "dig" with K=0 exchages the two top elements on the stack
Execution Model
---------------
The function embedvm_exec() executes one single VM instruction and then
returns. The user provided callback functions are used to access the vm
memory and call user functions.