if_decompiler/glulx/
opcodes.rs

1/*
2
3Glulx Opcodes
4=============
5
6Copyright (c) 2021 Dannii Willis
7MIT licenced
8https://github.com/curiousdannii/if-decompiler
9
10*/
11
12use super::*;
13
14pub const OP_NOP: u32 = 0x00;
15pub const OP_ADD: u32 = 0x10;
16pub const OP_SUB: u32 = 0x11;
17pub const OP_MUL: u32 = 0x12;
18pub const OP_DIV: u32 = 0x13;
19pub const OP_MOD: u32 = 0x14;
20pub const OP_NEG: u32 = 0x15;
21pub const OP_BITAND: u32 = 0x18;
22pub const OP_BITOR: u32 = 0x19;
23pub const OP_BITXOR: u32 = 0x1A;
24pub const OP_BITNOT: u32 = 0x1B;
25pub const OP_SHIFTL: u32 = 0x1C;
26pub const OP_SSHIFTR: u32 = 0x1D;
27pub const OP_USHIFTR: u32 = 0x1E;
28pub const OP_JUMP: u32 = 0x20;
29pub const OP_JZ: u32 = 0x22;
30pub const OP_JNZ: u32 = 0x23;
31pub const OP_JEQ: u32 = 0x24;
32pub const OP_JNE: u32 = 0x25;
33pub const OP_JLT: u32 = 0x26;
34pub const OP_JGE: u32 = 0x27;
35pub const OP_JGT: u32 = 0x28;
36pub const OP_JLE: u32 = 0x29;
37pub const OP_JLTU: u32 = 0x2A;
38pub const OP_JGEU: u32 = 0x2B;
39pub const OP_JGTU: u32 = 0x2C;
40pub const OP_JLEU: u32 = 0x2D;
41pub const OP_CALL: u32 = 0x30;
42pub const OP_RETURN: u32 = 0x31;
43pub const OP_CATCH: u32 = 0x32;
44pub const OP_THROW: u32 = 0x33;
45pub const OP_TAILCALL: u32 = 0x34;
46pub const OP_COPY: u32 = 0x40;
47pub const OP_COPYS: u32 = 0x41;
48pub const OP_COPYB: u32 = 0x42;
49pub const OP_SEXS: u32 = 0x44;
50pub const OP_SEXB: u32 = 0x45;
51pub const OP_ALOAD: u32 = 0x48;
52pub const OP_ALOADS: u32 = 0x49;
53pub const OP_ALOADB: u32 = 0x4A;
54pub const OP_ALOADBIT: u32 = 0x4B;
55pub const OP_ASTORE: u32 = 0x4C;
56pub const OP_ASTORES: u32 = 0x4D;
57pub const OP_ASTOREB: u32 = 0x4E;
58pub const OP_ASTOREBIT: u32 = 0x4F;
59pub const OP_STKCOUNT: u32 = 0x50;
60pub const OP_STKPEEK: u32 = 0x51;
61pub const OP_STKSWAP: u32 = 0x52;
62pub const OP_STKROLL: u32 = 0x53;
63pub const OP_STKCOPY: u32 = 0x54;
64pub const OP_STREAMCHAR: u32 = 0x70;
65pub const OP_STREAMNUM: u32 = 0x71;
66pub const OP_STREAMSTR: u32 = 0x72;
67pub const OP_STREAMUNICHAR: u32 = 0x73;
68pub const OP_GESTALT: u32 = 0x100;
69pub const OP_DEBUGTRAP: u32 = 0x101;
70pub const OP_GETMEMSIZE: u32 = 0x102;
71pub const OP_SETMEMSIZE: u32 = 0x103;
72pub const OP_JUMPABS: u32 = 0x104;
73pub const OP_RANDOM: u32 = 0x110;
74pub const OP_SETRANDOM: u32 = 0x111;
75pub const OP_QUIT: u32 = 0x120;
76pub const OP_VERIFY: u32 = 0x121;
77pub const OP_RESTART: u32 = 0x122;
78pub const OP_SAVE: u32 = 0x123;
79pub const OP_RESTORE: u32 = 0x124;
80pub const OP_SAVEUNDO: u32 = 0x125;
81pub const OP_RESTOREUNDO: u32 = 0x126;
82pub const OP_PROTECT: u32 = 0x127;
83pub const OP_GLK: u32 = 0x130;
84pub const OP_GETSTRINGTBL: u32 = 0x140;
85pub const OP_SETSTRINGTBL: u32 = 0x141;
86pub const OP_GETIOSYS: u32 = 0x148;
87pub const OP_SETIOSYS: u32 = 0x149;
88pub const OP_LINEARSEARCH: u32 = 0x150;
89pub const OP_BINARYSEARCH: u32 = 0x151;
90pub const OP_LINKEDSEARCH: u32 = 0x152;
91pub const OP_CALLF: u32 = 0x160;
92pub const OP_CALLFI: u32 = 0x161;
93pub const OP_CALLFII: u32 = 0x162;
94pub const OP_CALLFIII: u32 = 0x163;
95pub const OP_MZERO: u32 = 0x170;
96pub const OP_MCOPY: u32 = 0x171;
97pub const OP_MALLOC: u32 = 0x178;
98pub const OP_MFREE: u32 = 0x179;
99pub const OP_ACCELFUNC: u32 = 0x180;
100pub const OP_ACCELPARAM: u32 = 0x181;
101pub const OP_NUMTOF: u32 = 0x190;
102pub const OP_FTONUMZ: u32 = 0x191;
103pub const OP_FTONUMN: u32 = 0x192;
104pub const OP_CEIL: u32 = 0x198;
105pub const OP_FLOOR: u32 = 0x199;
106pub const OP_FADD: u32 = 0x1A0;
107pub const OP_FSUB: u32 = 0x1A1;
108pub const OP_FMUL: u32 = 0x1A2;
109pub const OP_FDIV: u32 = 0x1A3;
110pub const OP_FMOD: u32 = 0x1A4;
111pub const OP_SQRT: u32 = 0x1A8;
112pub const OP_EXP: u32 = 0x1A9;
113pub const OP_LOG: u32 = 0x1AA;
114pub const OP_POW: u32 = 0x1AB;
115pub const OP_SIN: u32 = 0x1B0;
116pub const OP_COS: u32 = 0x1B1;
117pub const OP_TAN: u32 = 0x1B2;
118pub const OP_ASIN: u32 = 0x1B3;
119pub const OP_ACOS: u32 = 0x1B4;
120pub const OP_ATAN: u32 = 0x1B5;
121pub const OP_ATAN2: u32 = 0x1B6;
122pub const OP_JFEQ: u32 = 0x1C0;
123pub const OP_JFNE: u32 = 0x1C1;
124pub const OP_JFLT: u32 = 0x1C2;
125pub const OP_JFLE: u32 = 0x1C3;
126pub const OP_JFGT: u32 = 0x1C4;
127pub const OP_JFGE: u32 = 0x1C5;
128pub const OP_JISNAN: u32 = 0x1C8;
129pub const OP_JISINF: u32 = 0x1C9;
130
131// Return the number of operands an opcode has
132// Also checks for unknown opcodes
133pub fn operands_count(opcode: u32) -> Option<u8> {
134    match opcode {
135        OP_NOP | OP_STKSWAP | OP_QUIT | OP_RESTART => Some(0),
136        OP_JUMP | OP_RETURN | OP_STKCOUNT | OP_STKCOPY
137            | OP_STREAMCHAR ..= OP_STREAMUNICHAR | OP_DEBUGTRAP | OP_GETMEMSIZE
138            | OP_JUMPABS | OP_SETRANDOM | OP_VERIFY | OP_SAVEUNDO | OP_RESTOREUNDO
139            | OP_GETSTRINGTBL | OP_SETSTRINGTBL | OP_MFREE => Some(1),
140        OP_NEG | OP_BITNOT | OP_JZ | OP_JNZ | OP_CATCH ..= OP_TAILCALL
141            | OP_COPY ..= OP_SEXB | OP_STKPEEK | OP_STKROLL | OP_CALLF
142            | OP_SETMEMSIZE | OP_RANDOM | OP_SAVE | OP_RESTORE | OP_PROTECT
143            | OP_GETIOSYS | OP_SETIOSYS | OP_MZERO | OP_MALLOC | OP_ACCELFUNC
144            | OP_ACCELPARAM | OP_NUMTOF ..= OP_FLOOR | OP_SQRT ..= OP_LOG
145            | OP_SIN ..= OP_ATAN | OP_JISNAN | OP_JISINF => Some(2),
146        OP_ADD ..= OP_MOD | OP_BITAND ..= OP_BITXOR  | OP_SHIFTL ..= OP_USHIFTR
147            | OP_JEQ ..= OP_CALL | OP_ALOAD ..= OP_ASTOREBIT | OP_GESTALT
148            | OP_GLK | OP_CALLFI | OP_MCOPY | OP_FADD ..= OP_FDIV | OP_POW
149            | OP_ATAN2 | OP_JFLT ..= OP_JFGE => Some(3),
150        OP_CALLFII | OP_FMOD | OP_JFEQ | OP_JFNE => Some(4),
151        OP_CALLFIII => Some(5),
152        OP_LINKEDSEARCH => Some(7),
153        OP_LINEARSEARCH | OP_BINARYSEARCH => Some(8),
154        _ => None,
155    }
156}
157
158// Whether an instruction branches or jumps
159pub fn instruction_branches(opcode: u32) -> bool {
160    match opcode {
161        OP_JUMP ..= OP_JLEU | OP_CATCH | OP_JUMPABS | OP_JFEQ ..= OP_JISINF => true,
162        _ => false,
163    }
164}
165
166// Whether an instruction calls
167pub fn instruction_calls(opcode: u32) -> bool {
168    match opcode {
169        OP_CALL | OP_TAILCALL | OP_CALLF ..= OP_CALLFIII => true,
170        _ => false,
171    }
172}
173
174// Whether an instruction halts rather than continuing at the next byte
175pub fn instruction_halts(opcode: u32) -> bool {
176    match opcode {
177        OP_JUMP | OP_RETURN | OP_THROW | OP_TAILCALL | OP_JUMPABS | OP_QUIT
178            | OP_RESTART => true,
179        _ => false,
180    }
181}
182
183// Some instructions may cause execution to resume at the next instruction at some later time
184pub fn instruction_resumes(opcode: u32) -> bool {
185    match opcode {
186        OP_CALL | OP_STREAMCHAR  ..= OP_STREAMUNICHAR | OP_SAVE | OP_SAVEUNDO
187            | OP_CALLF ..= OP_CALLFIII => true,
188        _ => false,
189    }
190}
191
192#[derive(Copy, Clone, PartialEq)]
193pub enum StoreMode {
194    DoesNotStore,
195    LastOperand,
196    LastTwoOperands,
197}
198
199// Whether an instruction stores
200pub fn instruction_stores(opcode: u32) -> StoreMode {
201    use StoreMode::*;
202    match opcode {
203        OP_ADD ..= OP_USHIFTR | OP_CALL | OP_COPY
204            /* OP_COPYS | OP_COPYB store manually */
205            | OP_SEXS ..= OP_ALOADBIT | OP_STKCOUNT ..= OP_STKPEEK
206            | OP_GESTALT | OP_GETMEMSIZE | OP_SETMEMSIZE | OP_RANDOM | OP_VERIFY
207            | OP_SAVE ..= OP_RESTOREUNDO | OP_GLK | OP_GETSTRINGTBL
208            | OP_LINEARSEARCH ..= OP_CALLFIII | OP_MALLOC | OP_NUMTOF ..= OP_FDIV
209            | OP_SQRT ..= OP_ATAN2 => LastOperand,
210        OP_GETIOSYS | OP_FMOD => LastTwoOperands,
211        _ => DoesNotStore,
212    }
213}
214
215// Return the FunctionSafety for a function's instructions
216pub fn function_safety(instructions: &Vec<Instruction>) -> FunctionSafety {
217    use FunctionSafety::*;
218    let mut result = SafetyTBD;
219    for instruction in instructions {
220        match instruction.opcode {
221            OP_THROW | OP_QUIT | OP_RESTART ..= OP_RESTOREUNDO => result = Unsafe,
222
223            // Calls to non-constants are unsafe
224            OP_CALL | OP_TAILCALL | OP_CALLF ..= OP_CALLFIII => match instruction.operands[0] {
225                Operand::Constant(_) => continue,
226                _ => result = Unsafe,
227            }
228
229            // Branches to non-constants are unsafe
230            OP_JUMP ..= OP_JLEU | OP_JUMPABS | OP_JFEQ ..= OP_JISINF => match instruction.operands.last().unwrap() {
231                Operand::Constant(_) => continue,
232                _ => return UnsafeDynamicBranches,
233            }
234
235            // CATCH could be unsafe or extra unsafe
236            OP_CATCH => match instruction.operands.last().unwrap() {
237                Operand::Constant(_) => result = Unsafe,
238                _ => return UnsafeDynamicBranches,
239            }
240
241            _ => continue,
242        };
243    }
244    result
245}