added missing opcodes to VM
[gaemu.git] / gaem / runner / opcodes.d
blob483ce2c8faf3e519c70f5d0890d62a89b46dfa0f
1 /* GML runner
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module gaem.runner.opcodes is aliced;
21 // ////////////////////////////////////////////////////////////////////////// //
22 enum Op {
23 nop,
24 skip, // skip current instruction; it usually has 3-byte payload
26 copy, // copy regs; dest: dest reg; op0: first reg to copy; op1: number of regs to copy (0: no copy, lol)
28 lnot, //: lognot
29 neg,
30 bneg,
32 add,
33 sub,
34 mul,
35 mod,
36 div,
37 rdiv,
38 bor,
39 bxor,
40 band,
41 shl,
42 shr,
43 lt,
44 le,
45 gt,
46 ge,
47 eq,
48 ne,
49 lor,
50 land,
51 lxor,
53 plit, // dest becomes pool slot val (val: 2 bytes) -- load value from pool slot; if val is 0xffff, next instruction is skip
54 ilit, // dest becomes ilit val (val: short) -- load small integer literal
55 slit, // dest becomes ilit val (val: short) -- load small integer literal as string index
56 xlit, // dest becomes integer(!) val (val: short) -- load small integer literal
58 jump, // addr: 3 bytes
59 xtrue, // dest is reg to check; skip next instruction if dest is "gml true" (i.e. fabs(v) >= 0.5`)
60 xfalse, // dest is reg to check; skip next instruction if dest is "gml false" (i.e. fabs(v) >= 0.5`)
62 call, // dest is result; op0: call frame (see below); op1: number of args
63 // call frame is:
64 // new function frame
65 // int scriptid (after op1+3 slots)
66 // note that there should be no used registers after those (as that will be used as new function frame regs)
68 enter, // dest: number of arguments used; op0: number of stack slots used (including result and args); op1: number of locals
69 // any function will ALWAYS starts with this
71 ret, // dest is retvalue; it is copied to reg0; other stack items are discarded
73 oval, // load object value to dest; 2byte: object index
74 // this is used for `if (oPlayer1)` and such conditions
75 // "object" is any object here, including sprites, background, sounds...
77 fval, // load field value; op0: obj id; op1: int! reg (field id)
78 i1fval, // load indexed value; op0: obj id; op1: xslots (int! field id, first index)
79 i2fval, // load indexed value; op0: obj id; op1: xslots (int! field id, first index, second index)
81 fstore, // store value *from* dest into field; op0: obj id; op1: int! reg (field id); can create fields
82 i1fstore, // store value *from* dest into indexed reference; op0: obj id; op1: xslots (int! field id, first index)
83 i2fstore, // store value *from* dest into indexed reference; op0: obj id; op1: xslots (int! field id, first index, second index)
85 // `with` is done by copying `self` to another reg, execute the code and restore `self`
86 siter, // start instance iterator; dest: iterid; op0: objid or instid
87 // this is special: it will skip next instruction if iteration has at least one item
88 // next instruction is always jump, which skips the loop
89 niter, // dest: is iterreg; next instruction is always jump, which continues the loop
90 kiter, // kill iterator, should be called to restore `self` and `other`; dest: iterreg
92 // so return from `with` should call kiter for all created iterators first
94 // possible iterator management: preallocate slots for each non-overlapped "with";
95 // let VM to free all iterators from those slots on function exit
97 lirint, // dest = lrint(op0): do lrint() (or another fast float->int conversion)
101 // ////////////////////////////////////////////////////////////////////////// //
102 ubyte opCode (uint op) pure nothrow @safe @nogc { pragma(inline, true); return (op&0xff); }
103 ubyte opDest (uint op) pure nothrow @safe @nogc { pragma(inline, true); return ((op>>8)&0xff); }
104 ubyte opOp0 (uint op) pure nothrow @safe @nogc { pragma(inline, true); return ((op>>16)&0xff); }
105 ubyte opOp1 (uint op) pure nothrow @safe @nogc { pragma(inline, true); return ((op>>24)&0xff); }
106 short opILit (uint op) pure nothrow @safe @nogc { pragma(inline, true); return cast(short)((op>>16)&0xffff); }
107 uint op3Byte (uint op) pure nothrow @safe @nogc { pragma(inline, true); return (op>>8); }
108 uint op2Byte (uint op) pure nothrow @safe @nogc { pragma(inline, true); return (op>>16); }
110 uint opMakeILit (ubyte op, byte dest, short val) pure nothrow @safe @nogc { pragma(inline, true); return ((val<<16)|((dest&0xff)<<8)|op); }
111 uint opMake3Byte (ubyte op, uint val) pure nothrow @safe @nogc { pragma(inline, true); assert(val <= 0xffffff); return (val<<8)|op; }
114 // ////////////////////////////////////////////////////////////////////////// //
115 private import std.stdio : File;
117 // returns instruction size
118 uint dumpInstr (File fo, uint pc, const(uint)[] code) {
119 fo.writef("%08X: ", pc);
120 if (pc == 0 || pc >= code.length) {
121 fo.writeln("<INVALID>");
122 return 1;
124 auto atp = opargs[code[pc].opCode];
125 if (atp == OpArgs.None) {
126 fo.writefln("%s", cast(Op)code[pc].opCode);
127 return 1;
129 fo.writef("%-9s", cast(Op)code[pc].opCode);
130 switch (atp) with (OpArgs) {
131 case Dest: fo.writefln("dest:%s", code[pc].opDest); break;
132 case DestOp0: fo.writefln("dest:%s, op0:%s", code[pc].opDest, code[pc].opOp0); break;
133 case DestOp0Op1: fo.writefln("dest:%s, op0:%s, op1:%s", code[pc].opDest, code[pc].opOp0, code[pc].opOp1); break;
134 case Dest2Bytes: fo.writefln("dest:%s; val:%s", code[pc].opDest, code[pc].op2Byte); break;
135 case Dest3Bytes: fo.writefln("dest:%s; val:%s", code[pc].opDest, code[pc].op3Byte); break;
136 case DestInt: fo.writefln("dest:%s; val:%s", code[pc].opDest, code[pc].opILit); break;
137 case DestJump: fo.writefln("0x%08X", code[pc].op3Byte); break;
138 case DestCall: fo.writefln("dest:%s; frame:%s; args:%s", code[pc].opDest, code[pc].opOp0, code[pc].opOp1); break;
139 case Op0Op1: fo.writefln("op0:%s, op1:%s", code[pc].opOp0, code[pc].opOp1); break;
140 case Op0: fo.writefln("op0:%s", code[pc].opOp0); break;
141 default: assert(0);
143 return 1;
147 // ////////////////////////////////////////////////////////////////////////// //
148 private:
150 enum OpArgs {
151 None,
152 Dest,
153 DestOp0,
154 DestOp0Op1,
155 Dest2Bytes,
156 Dest3Bytes,
157 DestInt,
158 DestJump,
159 DestCall,
160 Op0Op1,
161 Op0,
164 __gshared immutable OpArgs[ubyte] opargs;
167 shared static this () {
168 with(OpArgs) opargs = [
169 Op.nop: None,
170 Op.skip: None,
171 Op.copy: DestOp0Op1,
172 Op.lnot: DestOp0, //: lognot
173 Op.neg: DestOp0,
174 Op.bneg: DestOp0,
176 Op.add: DestOp0Op1,
177 Op.sub: DestOp0Op1,
178 Op.mul: DestOp0Op1,
179 Op.mod: DestOp0Op1,
180 Op.div: DestOp0Op1,
181 Op.rdiv: DestOp0Op1,
182 Op.bor: DestOp0Op1,
183 Op.bxor: DestOp0Op1,
184 Op.band: DestOp0Op1,
185 Op.shl: DestOp0Op1,
186 Op.shr: DestOp0Op1,
187 Op.lt: DestOp0Op1,
188 Op.le: DestOp0Op1,
189 Op.gt: DestOp0Op1,
190 Op.ge: DestOp0Op1,
191 Op.eq: DestOp0Op1,
192 Op.ne: DestOp0Op1,
193 Op.lor: DestOp0Op1,
194 Op.land: DestOp0Op1,
195 Op.lxor: DestOp0Op1,
197 Op.plit: Dest2Bytes,
198 Op.ilit: DestInt,
199 Op.slit: DestInt,
200 Op.xlit: DestInt,
202 Op.jump: DestJump,
203 Op.xtrue: Dest,
204 Op.xfalse: Dest,
206 Op.call: DestCall,
208 Op.enter: DestOp0Op1,
210 Op.ret: Dest,
212 Op.oval: Dest2Bytes,
214 Op.fval: DestOp0Op1,
215 Op.i1fval: DestOp0Op1,
216 Op.i2fval: DestOp0Op1,
218 Op.fstore: DestOp0Op1,
220 Op.i1fstore: DestOp0Op1,
221 Op.i2fstore: DestOp0Op1,
223 Op.siter: DestOp0,
224 Op.niter: Dest,
225 Op.kiter: Dest,
227 Op.lirint: DestOp0, // dest = lrint(op0): do lrint() (or another fast float->int conversion)