switched to GPLv3 ONLY, because i don't trust FSF anymore
[gaemu.git] / gaem / runner / opcodes.d
blobfaf39e8a438a97fda1a1003662e811ee25116916
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, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module gaem.runner.opcodes is aliced;
20 // ////////////////////////////////////////////////////////////////////////// //
21 enum Op {
22 nop,
23 skip, // skip current instruction; it usually has 3-byte payload
25 copy, // copy regs; dest: dest reg; op0: first reg to copy; op1: number of regs to copy (0: no copy, lol)
27 lnot, //: lognot
28 neg,
29 bneg,
31 add,
32 sub,
33 mul,
34 mod,
35 div,
36 rdiv,
37 bor,
38 bxor,
39 band,
40 shl,
41 shr,
42 lt,
43 le,
44 gt,
45 ge,
46 eq,
47 ne,
48 lor,
49 land,
50 lxor,
52 plit, // dest becomes pool slot val (val: 2 bytes) -- load value from pool slot; if val is 0xffff, next instruction is skip
53 ilit, // dest becomes ilit val (val: short) -- load small integer literal
54 slit, // dest becomes ilit val (val: short) -- load small integer literal as string index
55 xlit, // dest becomes integer(!) val (val: short) -- load small integer literal
57 jump, // addr: 3 bytes
58 xtrue, // dest is reg to check; skip next instruction if dest is "gml true" (i.e. fabs(v) >= 0.5`)
59 xfalse, // dest is reg to check; skip next instruction if dest is "gml false" (i.e. fabs(v) >= 0.5`)
61 call, // dest is result; op0: call frame (see below); op1: number of args
62 // call frame is:
63 // new function frame
64 // int scriptid (after op1+3 slots)
65 // note that there should be no used registers after those (as that will be used as new function frame regs)
67 enter, // dest: number of arguments used; op0: number of stack slots used (including result and args); op1: number of locals
68 // any function will ALWAYS starts with this
70 ret, // dest is retvalue; it is copied to reg0; other stack items are discarded
72 oval, // load object value to dest; 2byte: object index
73 // this is used for `if (oPlayer1)` and such conditions
74 // "object" is any object here, including sprites, background, sounds...
76 fval, // load field value; op0: obj id; op1: int! reg (field id)
77 i1fval, // load indexed value; op0: obj id; op1: xslots (int! field id, first index)
78 i2fval, // load indexed value; op0: obj id; op1: xslots (int! field id, first index, second index)
80 fstore, // store value *from* dest into field; op0: obj id; op1: int! reg (field id); can create fields
81 i1fstore, // store value *from* dest into indexed reference; op0: obj id; op1: xslots (int! field id, first index)
82 i2fstore, // store value *from* dest into indexed reference; op0: obj id; op1: xslots (int! field id, first index, second index)
84 // `with` is done by copying `self` to another reg, execute the code and restore `self`
85 siter, // start instance iterator; dest: iterid; op0: objid or instid
86 // this is special: it will skip next instruction if iteration has at least one item
87 // next instruction is always jump, which skips the loop
88 niter, // dest: is iterreg; next instruction is always jump, which continues the loop
89 kiter, // kill iterator, should be called to restore `self` and `other`; dest: iterreg
91 // so return from `with` should call kiter for all created iterators first
93 // possible iterator management: preallocate slots for each non-overlapped "with";
94 // let VM to free all iterators from those slots on function exit
96 lirint, // dest = lrint(op0): do lrint() (or another fast float->int conversion)
100 // ////////////////////////////////////////////////////////////////////////// //
101 ubyte opCode (uint op) pure nothrow @safe @nogc { pragma(inline, true); return (op&0xff); }
102 ubyte opDest (uint op) pure nothrow @safe @nogc { pragma(inline, true); return ((op>>8)&0xff); }
103 ubyte opOp0 (uint op) pure nothrow @safe @nogc { pragma(inline, true); return ((op>>16)&0xff); }
104 ubyte opOp1 (uint op) pure nothrow @safe @nogc { pragma(inline, true); return ((op>>24)&0xff); }
105 short opILit (uint op) pure nothrow @safe @nogc { pragma(inline, true); return cast(short)((op>>16)&0xffff); }
106 uint op3Byte (uint op) pure nothrow @safe @nogc { pragma(inline, true); return (op>>8); }
107 uint op2Byte (uint op) pure nothrow @safe @nogc { pragma(inline, true); return (op>>16); }
109 uint opMakeILit (ubyte op, byte dest, short val) pure nothrow @safe @nogc { pragma(inline, true); return ((val<<16)|((dest&0xff)<<8)|op); }
110 uint opMake3Byte (ubyte op, uint val) pure nothrow @safe @nogc { pragma(inline, true); assert(val <= 0xffffff); return (val<<8)|op; }
113 // ////////////////////////////////////////////////////////////////////////// //
114 private import std.stdio : File;
116 // returns instruction size
117 uint dumpInstr (File fo, uint pc, const(uint)[] code) {
118 fo.writef("%08X: ", pc);
119 if (pc == 0 || pc >= code.length) {
120 fo.writeln("<INVALID>");
121 return 1;
123 auto atp = opargs[code[pc].opCode];
124 if (atp == OpArgs.None) {
125 fo.writefln("%s", cast(Op)code[pc].opCode);
126 return 1;
128 fo.writef("%-9s", cast(Op)code[pc].opCode);
129 switch (atp) with (OpArgs) {
130 case Dest: fo.writefln("dest:%s", code[pc].opDest); break;
131 case DestOp0: fo.writefln("dest:%s, op0:%s", code[pc].opDest, code[pc].opOp0); break;
132 case DestOp0Op1: fo.writefln("dest:%s, op0:%s, op1:%s", code[pc].opDest, code[pc].opOp0, code[pc].opOp1); break;
133 case Dest2Bytes: fo.writefln("dest:%s; val:%s", code[pc].opDest, code[pc].op2Byte); break;
134 case Dest3Bytes: fo.writefln("dest:%s; val:%s", code[pc].opDest, code[pc].op3Byte); break;
135 case DestInt: fo.writefln("dest:%s; val:%s", code[pc].opDest, code[pc].opILit); break;
136 case DestJump: fo.writefln("0x%08X", code[pc].op3Byte); break;
137 case DestCall: fo.writefln("dest:%s; frame:%s; args:%s", code[pc].opDest, code[pc].opOp0, code[pc].opOp1); break;
138 case Op0Op1: fo.writefln("op0:%s, op1:%s", code[pc].opOp0, code[pc].opOp1); break;
139 case Op0: fo.writefln("op0:%s", code[pc].opOp0); break;
140 default: assert(0);
142 return 1;
146 // ////////////////////////////////////////////////////////////////////////// //
147 private:
149 enum OpArgs {
150 None,
151 Dest,
152 DestOp0,
153 DestOp0Op1,
154 Dest2Bytes,
155 Dest3Bytes,
156 DestInt,
157 DestJump,
158 DestCall,
159 Op0Op1,
160 Op0,
163 __gshared immutable OpArgs[ubyte] opargs;
166 shared static this () {
167 with(OpArgs) opargs = [
168 Op.nop: None,
169 Op.skip: None,
170 Op.copy: DestOp0Op1,
171 Op.lnot: DestOp0, //: lognot
172 Op.neg: DestOp0,
173 Op.bneg: DestOp0,
175 Op.add: DestOp0Op1,
176 Op.sub: DestOp0Op1,
177 Op.mul: DestOp0Op1,
178 Op.mod: DestOp0Op1,
179 Op.div: DestOp0Op1,
180 Op.rdiv: DestOp0Op1,
181 Op.bor: DestOp0Op1,
182 Op.bxor: DestOp0Op1,
183 Op.band: DestOp0Op1,
184 Op.shl: DestOp0Op1,
185 Op.shr: DestOp0Op1,
186 Op.lt: DestOp0Op1,
187 Op.le: DestOp0Op1,
188 Op.gt: DestOp0Op1,
189 Op.ge: DestOp0Op1,
190 Op.eq: DestOp0Op1,
191 Op.ne: DestOp0Op1,
192 Op.lor: DestOp0Op1,
193 Op.land: DestOp0Op1,
194 Op.lxor: DestOp0Op1,
196 Op.plit: Dest2Bytes,
197 Op.ilit: DestInt,
198 Op.slit: DestInt,
199 Op.xlit: DestInt,
201 Op.jump: DestJump,
202 Op.xtrue: Dest,
203 Op.xfalse: Dest,
205 Op.call: DestCall,
207 Op.enter: DestOp0Op1,
209 Op.ret: Dest,
211 Op.oval: Dest2Bytes,
213 Op.fval: DestOp0Op1,
214 Op.i1fval: DestOp0Op1,
215 Op.i2fval: DestOp0Op1,
217 Op.fstore: DestOp0Op1,
219 Op.i1fstore: DestOp0Op1,
220 Op.i2fstore: DestOp0Op1,
222 Op.siter: DestOp0,
223 Op.niter: Dest,
224 Op.kiter: Dest,
226 Op.lirint: DestOp0, // dest = lrint(op0): do lrint() (or another fast float->int conversion)