vm and compiler fixes; some work on instance system
[gaemu.git] / gaem / runner / opcodes.d
blob8e329c436bd09dd0e1e39c4180681edb2dbcb4bc
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 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 fval, // load field value; op0: obj id; op1: int! reg (field id)
73 i1val, // load indexed value; op0: varref; op1: index
74 i2val, // load indexed value; op0: varref; op1: first index; (op1+1): second index
76 // ref+store will be replaced with this
77 lstore, // store value *from* dest into local slot; op0: slot number
78 fstore, // store value *from* dest into field; op0: obj id; op1: int! reg (field id); can create fields
79 i1store, // store value *from* dest into indexed reference; op0: varref; op1: index; can create arrays
80 i2store, // store value *from* dest into indexed reference; op0: varref; op1: first index; (op1+1): second index; can create arrays
82 // `with` is done by copying `self` to another reg, execute the code and restore `self`
84 siter, // start instance iterator; dest: iterid; op0: objid or instid
85 // this is special: it will skip next instruction if iteration has at least one item
86 // next instruction is always jump, which skips the loop
87 niter, // dest is iterreg; do jump (pc is the same as in jump) if iteration is NOT complete
88 kiter, // kill iterator, should be called to prevent memory leaks
90 // so return from `with` should call kiter for all created iterators first
92 // possible iterator management: preallocate slots for each non-overlapped "with";
93 // let VM to free all iterators from those slots on function exit
95 lirint, // dest = lrint(op0): do lrint() (or another fast float->int conversion)
97 // as we are using refloads only in the last stage of assignment, they can create values
98 // there "ref" opcodes will be never seen in the final compiled code
99 // the compiler will change 'em to the corresponding stores
100 lref, // load slot reference to dest; op0: slot number
101 fref, // load field reference; op0: obj id; op1: int! reg (field id); can create fields
102 i1ref, // load indexed reference; op0: varref; op1: index; can create arrays
103 i2ref, // load indexed reference; op0: varref; op1: first index; (op1+1): second index; can create arrays
104 //rstore, // store to op0-varref from op1
108 // ////////////////////////////////////////////////////////////////////////// //
109 ubyte opCode (uint op) pure nothrow @safe @nogc { pragma(inline, true); return (op&0xff); }
110 ubyte opDest (uint op) pure nothrow @safe @nogc { pragma(inline, true); return ((op>>8)&0xff); }
111 ubyte opOp0 (uint op) pure nothrow @safe @nogc { pragma(inline, true); return ((op>>16)&0xff); }
112 ubyte opOp1 (uint op) pure nothrow @safe @nogc { pragma(inline, true); return ((op>>24)&0xff); }
113 short opILit (uint op) pure nothrow @safe @nogc { pragma(inline, true); return cast(short)((op>>16)&0xffff); }
114 uint op3Byte (uint op) pure nothrow @safe @nogc { pragma(inline, true); return (op>>8); }
115 uint op2Byte (uint op) pure nothrow @safe @nogc { pragma(inline, true); return (op>>16); }
117 uint opMakeILit (ubyte op, byte dest, short val) pure nothrow @safe @nogc { pragma(inline, true); return ((val<<16)|((dest&0xff)<<8)|op); }
118 uint opMake3Byte (ubyte op, uint val) pure nothrow @safe @nogc { pragma(inline, true); assert(val <= 0xffffff); return (val<<8)|op; }
121 // ////////////////////////////////////////////////////////////////////////// //
122 private import std.stdio : File;
124 // returns instruction size
125 uint dumpInstr (File fo, uint pc, const(uint)[] code) {
126 fo.writef("%08X: ", pc);
127 if (pc == 0 || pc >= code.length) {
128 fo.writeln("<INVALID>");
129 return 1;
131 auto atp = opargs[code[pc].opCode];
132 if (atp == OpArgs.None) {
133 fo.writefln("%s", cast(Op)code[pc].opCode);
134 return 1;
136 fo.writef("%-8s", cast(Op)code[pc].opCode);
137 switch (atp) with (OpArgs) {
138 case Dest: fo.writefln("dest:%s", code[pc].opDest); break;
139 case DestOp0: fo.writefln("dest:%s, op0:%s", code[pc].opDest, code[pc].opOp0); break;
140 case DestOp0Op1: fo.writefln("dest:%s, op0:%s, op1:%s", code[pc].opDest, code[pc].opOp0, code[pc].opOp1); break;
141 case Dest2Bytes: fo.writefln("dest:%s; val:%s", code[pc].opDest, code[pc].op2Byte); break;
142 case Dest3Bytes: fo.writefln("dest:%s; val:%s", code[pc].opDest, code[pc].op3Byte); break;
143 case DestInt: fo.writefln("dest:%s; val:%s", code[pc].opDest, code[pc].opILit); break;
144 case DestJump: fo.writefln("0x%08x", code[pc].op3Byte); break;
145 case DestCall: fo.writefln("dest:%s; frame:%s; args:%s", code[pc].opDest, code[pc].opOp0, code[pc].opOp1); break;
146 case Op0Op1: fo.writefln("op0:%s, op1:%s", code[pc].opOp0, code[pc].opOp1); break;
147 default: assert(0);
149 return 1;
153 // ////////////////////////////////////////////////////////////////////////// //
154 private:
156 enum OpArgs {
157 None,
158 Dest,
159 DestOp0,
160 DestOp0Op1,
161 Dest2Bytes,
162 Dest3Bytes,
163 DestInt,
164 DestJump,
165 DestCall,
166 Op0Op1,
169 __gshared immutable OpArgs[ubyte] opargs;
172 shared static this () {
173 with(OpArgs) opargs = [
174 Op.nop: None,
175 Op.skip: None,
176 Op.copy: DestOp0Op1,
177 Op.lnot: DestOp0, //: lognot
178 Op.neg: DestOp0,
179 Op.bneg: DestOp0,
181 Op.add: DestOp0Op1,
182 Op.sub: DestOp0Op1,
183 Op.mul: DestOp0Op1,
184 Op.mod: DestOp0Op1,
185 Op.div: DestOp0Op1,
186 Op.rdiv: DestOp0Op1,
187 Op.bor: DestOp0Op1,
188 Op.bxor: DestOp0Op1,
189 Op.band: DestOp0Op1,
190 Op.shl: DestOp0Op1,
191 Op.shr: DestOp0Op1,
192 Op.lt: DestOp0Op1,
193 Op.le: DestOp0Op1,
194 Op.gt: DestOp0Op1,
195 Op.ge: DestOp0Op1,
196 Op.eq: DestOp0Op1,
197 Op.ne: DestOp0Op1,
198 Op.lor: DestOp0Op1,
199 Op.land: DestOp0Op1,
200 Op.lxor: DestOp0Op1,
202 Op.plit: Dest2Bytes,
203 Op.ilit: DestInt,
204 Op.xlit: DestInt,
206 Op.jump: DestJump,
207 Op.xtrue: Dest,
208 Op.xfalse: Dest,
210 Op.call: DestCall,
212 Op.enter: DestOp0Op1,
214 Op.ret: Dest,
216 Op.lstore: DestOp0Op1, // store value *from* dest into local slot; op0: slot number
217 Op.fstore: DestOp0Op1, // store value *from* dest into field; op0: obj id; op1: int! reg (field id); can create fields
218 Op.i1store: DestOp0Op1, // store value *from* dest into indexed reference; op0: varref; op1: index; can create arrays
219 Op.i2store: DestOp0Op1, // store value *from* dest into indexed reference; op0: varref; op1: first index; (op1+1): second index; can create arrays
221 Op.lref: DestOp0,
222 Op.fval: DestOp0Op1,
223 Op.i1val: DestOp0Op1,
224 Op.i2val: DestOp0Op1,
226 Op.fref: DestOp0Op1,
227 Op.i1ref: DestOp0Op1,
228 Op.i2ref: DestOp0Op1,
230 //Op.rstore: DestOp0,
232 Op.siter: DestOp0,
233 Op.niter: DestJump,
234 Op.kiter: Dest,
236 Op.lirint: DestOp0, // dest = lrint(op0): do lrint() (or another fast float->int conversion)