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 // ////////////////////////////////////////////////////////////////////////// //
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)
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
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>");
123 auto atp
= opargs
[code
[pc
].opCode
];
124 if (atp
== OpArgs
.None
) {
125 fo
.writefln("%s", cast(Op
)code
[pc
].opCode
);
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;
146 // ////////////////////////////////////////////////////////////////////////// //
163 __gshared
immutable OpArgs
[ubyte] opargs
;
166 shared static this () {
167 with(OpArgs
) opargs
= [
171 Op
.lnot
: DestOp0
, //: lognot
207 Op
.enter: DestOp0Op1
,
214 Op
.i1fval
: DestOp0Op1
,
215 Op
.i2fval
: DestOp0Op1
,
217 Op
.fstore
: DestOp0Op1
,
219 Op
.i1fstore
: DestOp0Op1
,
220 Op
.i2fstore
: DestOp0Op1
,
226 Op
.lirint
: DestOp0
, // dest = lrint(op0): do lrint() (or another fast float->int conversion)