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*/