basic/mach/opcode.rs
1use super::{Address, Val};
2use std::rc::Rc;
3
4/// ## Virtual machine instruction set
5///
6/// The BASIC virtual machine has no registers.
7/// Every operation is performed on the stack.
8///
9/// For example: `LET A=3*B` compiles to `[Literal(3), Push(B), Mul, Pop(A)]`
10///
11/// See <https://en.wikipedia.org/wiki/Reverse_Polish_notation>
12
13#[derive(Clone)]
14pub enum Opcode {
15 // *** Stack manipulation
16 /// Push literal value on to the stack.
17 Literal(Val),
18 /// Push stack value of named variable. Infallible.
19 Push(Rc<str>),
20 /// Pop stack value to named variable. This is the `LET` statement
21 /// and may generate errors.
22 Pop(Rc<str>),
23 PushArr(Rc<str>),
24 PopArr(Rc<str>),
25 DimArr(Rc<str>),
26 EraseArr(Rc<str>),
27
28 // *** Branch control
29 /// Pop stack and branch to Address if not zero.
30 IfNot(Address),
31 /// Unconditional branch to Address.
32 Jump(Address),
33 /// Process the FOR loop on the stack.
34 Next(Rc<str>),
35 /// ON x GOTO/GOSUB lines
36 On,
37 /// Expect Return(Address) on stack or else error: RETURN WITHOUT GOSUB.
38 /// A single assignable value before the Return(Address) will be restored to the stack.
39 /// Branch to Address.
40 Return,
41
42 // *** Statements
43 Clear,
44 Cls,
45 Cont,
46 Def(Rc<str>),
47 Defdbl,
48 Defint,
49 Defsng,
50 Defstr,
51 Delete,
52 End,
53 Fn(Rc<str>),
54 Input(Rc<str>),
55 LetMid,
56 List,
57 Load,
58 LoadRun,
59 New,
60 Print,
61 Read,
62 Renum,
63 Restore(Address),
64 Save,
65 Stop,
66 Swap,
67 Troff,
68 Tron,
69
70 // *** Expression operations
71 Neg,
72 Pow,
73 Mul,
74 Div,
75 DivInt,
76 Mod,
77 Add,
78 Sub,
79 Eq,
80 NotEq,
81 Lt,
82 LtEq,
83 Gt,
84 GtEq,
85 Not,
86 And,
87 Or,
88 Xor,
89 Imp,
90 Eqv,
91
92 // *** Built-in functions
93 Abs,
94 Asc,
95 Atn,
96 Cdbl,
97 Chr,
98 Cint,
99 Cos,
100 Csng,
101 Date,
102 Exp,
103 Fix,
104 Hex,
105 Inkey,
106 Instr,
107 Int,
108 Left,
109 Len,
110 Log,
111 Mid,
112 Oct,
113 Pos,
114 Right,
115 Rnd,
116 Sgn,
117 Sin,
118 Spc,
119 Sqr,
120 Str,
121 String,
122 Tab,
123 Tan,
124 Time,
125 Val,
126}
127
128impl std::fmt::Debug for Opcode {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 write!(f, "{}", self.to_string())
131 }
132}
133
134impl std::fmt::Display for Opcode {
135 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
136 use Opcode::*;
137 match self {
138 Literal(v) => write!(f, "PUSH({})", format!("{:?}", v).to_ascii_uppercase()),
139 Push(s) => write!(f, "PUSH({})", s),
140 Pop(s) => write!(f, "POP({})", s),
141 PushArr(s) => write!(f, "PUSHARR({})", s),
142 PopArr(s) => write!(f, "POPARR({})", s),
143 DimArr(s) => write!(f, "DIMARR({})", s),
144 EraseArr(s) => write!(f, "ERASEARR({})", s),
145
146 IfNot(a) => write!(f, "IFNOT({})", a),
147 Jump(a) => write!(f, "JUMP({})", a),
148 Next(a) => write!(f, "NEXT({})", a),
149 On => write!(f, "ON"),
150 Return => write!(f, "RETURN"),
151
152 Clear => write!(f, "CLEAR"),
153 Cls => write!(f, "CLS"),
154 Cont => write!(f, "CONT"),
155 Def(s) => write!(f, "DEF({})", s),
156 Defdbl => write!(f, "DEFDBL"),
157 Defint => write!(f, "DEFINT"),
158 Defsng => write!(f, "DEFSNG"),
159 Defstr => write!(f, "DEFSTR"),
160 Delete => write!(f, "DELETE"),
161 End => write!(f, "END"),
162 Fn(s) => write!(f, "FN({})", s),
163 Input(s) => write!(f, "INPUT({})", s),
164 LetMid => write!(f, "LETMID"),
165 List => write!(f, "LIST"),
166 Load => write!(f, "LOAD"),
167 LoadRun => write!(f, "LOADRUN"),
168 New => write!(f, "NEW"),
169 Print => write!(f, "PRINT"),
170 Read => write!(f, "READ"),
171 Renum => write!(f, "RENUM"),
172 Restore(s) => write!(f, "RESTORE({})", s),
173 Save => write!(f, "SAVE"),
174 Stop => write!(f, "STOP"),
175 Swap => write!(f, "SWAP"),
176 Troff => write!(f, "TROFF"),
177 Tron => write!(f, "TRON"),
178
179 Neg => write!(f, "NEG"),
180 Pow => write!(f, "POW"),
181 Mul => write!(f, "MUL"),
182 Div => write!(f, "DIV"),
183 DivInt => write!(f, "DIVINT"),
184 Mod => write!(f, "MOD"),
185 Add => write!(f, "ADD"),
186 Sub => write!(f, "SUB"),
187 Eq => write!(f, "EQ"),
188 NotEq => write!(f, "NOTEQ"),
189 Lt => write!(f, "LT"),
190 LtEq => write!(f, "LTEQ"),
191 Gt => write!(f, "GT"),
192 GtEq => write!(f, "GTEQ"),
193 Not => write!(f, "NOT"),
194 And => write!(f, "AND"),
195 Or => write!(f, "OR"),
196 Xor => write!(f, "XOR"),
197 Imp => write!(f, "IMP"),
198 Eqv => write!(f, "EQV"),
199
200 Abs => write!(f, "ABS"),
201 Asc => write!(f, "ASC"),
202 Atn => write!(f, "ATN"),
203 Cdbl => write!(f, "CDBL"),
204 Chr => write!(f, "CHR$"),
205 Cint => write!(f, "CINT"),
206 Cos => write!(f, "COS"),
207 Csng => write!(f, "CSNG"),
208 Date => write!(f, "DATE$"),
209 Exp => write!(f, "EXP"),
210 Fix => write!(f, "FIX"),
211 Hex => write!(f, "HEX"),
212 Inkey => write!(f, "INKEY"),
213 Instr => write!(f, "INSTR"),
214 Int => write!(f, "INT"),
215 Left => write!(f, "LEFT$"),
216 Len => write!(f, "LEN"),
217 Log => write!(f, "LOG"),
218 Mid => write!(f, "MID$"),
219 Oct => write!(f, "OCT"),
220 Pos => write!(f, "POS"),
221 Right => write!(f, "RIGHT$"),
222 Rnd => write!(f, "RND"),
223 Sgn => write!(f, "SGN"),
224 Sin => write!(f, "SIN"),
225 Spc => write!(f, "SPC"),
226 Sqr => write!(f, "SQR"),
227 Str => write!(f, "STR"),
228 String => write!(f, "STRING"),
229 Tab => write!(f, "TAB"),
230 Tan => write!(f, "TAN"),
231 Time => write!(f, "TIME$"),
232 Val => write!(f, "VAL"),
233 }
234 }
235}
236
237/*
238VM design notes. Move to docs some day.
239
240// let r = 10 + a% * 2
241Literal(10) // lhs+
242Push("A%") // lhs*
243Literal(2) // rhs*
244Mul // rhs+
245Add // result
246Pop("R")
247
248// def fnx(a%, a$) = expr
249:fnx
250Pop("fnx.a$")
251Pop("fnx.a%")
252--eval expr
253Return
254
255// a$ = fnx(10, "foo")
256Literal(10)
257Literal("foo")
258GoSub(:fnx)
259Pop("a$")
260
261// builtin function cos(3.14)
262Literal(3.14)
263FnCos
264
265// print "hello" "world"
266Literal("hello")
267Literal("world")
268Literal('\n')
269Literal(3)
270print -- pops len and reverse prints
271
272// FOR A = _from TO _to STEP _step
273--eval _from
274Pop("A")
275--eval _to
276--eval _step (or Literal(1))
277Literal("A")
278Lieral(Next(:loop_inner))
279:loop_inner
280-- loop stuff
281Next
282
283// New compiled type for loop (to before from)
284--eval STEP
285--eval TO
286--eval FROM
287Pop("A")
288Literal("A")
289Literal(0) // signal start of loop (don't step)
290:loop
291For(:done) // pop [int],var,to,step; if done goto label ; else push back without int
292-- stuff
293Goto(:loop)
294:done
295
296// Old school for-next
297--eval FROM
298Pop("A")
299--eval TO
300--eval STEP
301Literal("A")
302Literal(:foo)
303:foo
304...
305Next() pop :foo,var,step,to, if !done push all jump foo
306
307// while _expr
308:again
309-- eval _expr
310IfNot(:done)
311-- loop stuff
312GoTo(:again)
313:done
314
315//gosub
316Literal(Return(:after))
317GoTo(:thesub)
318:after
319
320// return
321:thesub
322-- stuff
323Return
324
325//if x then stuff
326-- eval x
327IfNot(:a)
328stuff
329:a
330
331//if x then a=5 else b=6
332-- eval x
333IfNot(:else)
334--exec a=5
335GoTo(:finish)
336:else
337--exec b=6
338:finish
339
340// new input
341push return addr
342lit(prompt)
343lit(#caps)
344lit(#len)
345Input(var) // pushes stuff+addr, checks len, pushes answers
346array evals
347pop var
348array evals
349pop var
350Input(nil)
351
352
353on x gosub
354
355push :return-addr
356push len 3
357push var
358On
359goto 1
360goto 2
361goto 3
362:return-addr
363
364*/