| 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| 2 | /* |
| 3 | * Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net> |
| 4 | */ |
| 5 | |
| 6 | #ifndef __DISASSEMBLER_H__ |
| 7 | #define __DISASSEMBLER_H__ |
| 8 | |
| 9 | #include "debug.h" |
| 10 | #include "lightrec.h" |
| 11 | #include "lightrec-config.h" |
| 12 | |
| 13 | #ifndef __packed |
| 14 | #define __packed __attribute__((packed)) |
| 15 | #endif |
| 16 | |
| 17 | #define BIT(x) (1ULL << (x)) |
| 18 | |
| 19 | /* Flags for all opcodes */ |
| 20 | #define LIGHTREC_NO_DS BIT(0) |
| 21 | #define LIGHTREC_SYNC BIT(1) |
| 22 | |
| 23 | /* Flags for LUI, ORI, ADDIU */ |
| 24 | #define LIGHTREC_MOVI BIT(2) |
| 25 | |
| 26 | /* Flags for load/store opcodes */ |
| 27 | #define LIGHTREC_SMC BIT(2) |
| 28 | #define LIGHTREC_NO_INVALIDATE BIT(3) |
| 29 | #define LIGHTREC_NO_MASK BIT(4) |
| 30 | #define LIGHTREC_LOAD_DELAY BIT(5) |
| 31 | |
| 32 | /* I/O mode for load/store opcodes */ |
| 33 | #define LIGHTREC_IO_MODE_LSB 6 |
| 34 | #define LIGHTREC_IO_MODE(x) ((x) << LIGHTREC_IO_MODE_LSB) |
| 35 | #define LIGHTREC_IO_UNKNOWN 0x0 |
| 36 | #define LIGHTREC_IO_DIRECT 0x1 |
| 37 | #define LIGHTREC_IO_HW 0x2 |
| 38 | #define LIGHTREC_IO_RAM 0x3 |
| 39 | #define LIGHTREC_IO_BIOS 0x4 |
| 40 | #define LIGHTREC_IO_SCRATCH 0x5 |
| 41 | #define LIGHTREC_IO_DIRECT_HW 0x6 |
| 42 | #define LIGHTREC_IO_MASK LIGHTREC_IO_MODE(0x7) |
| 43 | #define LIGHTREC_FLAGS_GET_IO_MODE(x) \ |
| 44 | (((x) & LIGHTREC_IO_MASK) >> LIGHTREC_IO_MODE_LSB) |
| 45 | |
| 46 | /* Flags for branches */ |
| 47 | #define LIGHTREC_EMULATE_BRANCH BIT(2) |
| 48 | #define LIGHTREC_LOCAL_BRANCH BIT(3) |
| 49 | |
| 50 | /* Flags for div/mult opcodes */ |
| 51 | #define LIGHTREC_NO_LO BIT(2) |
| 52 | #define LIGHTREC_NO_HI BIT(3) |
| 53 | #define LIGHTREC_NO_DIV_CHECK BIT(4) |
| 54 | |
| 55 | #define LIGHTREC_REG_RS_LSB 26 |
| 56 | #define LIGHTREC_REG_RS(x) ((x) << LIGHTREC_REG_RS_LSB) |
| 57 | #define LIGHTREC_REG_RS_MASK LIGHTREC_REG_RS(0x3) |
| 58 | #define LIGHTREC_FLAGS_GET_RS(x) \ |
| 59 | (((x) & LIGHTREC_REG_RS_MASK) >> LIGHTREC_REG_RS_LSB) |
| 60 | |
| 61 | #define LIGHTREC_REG_RT_LSB 28 |
| 62 | #define LIGHTREC_REG_RT(x) ((x) << LIGHTREC_REG_RT_LSB) |
| 63 | #define LIGHTREC_REG_RT_MASK LIGHTREC_REG_RT(0x3) |
| 64 | #define LIGHTREC_FLAGS_GET_RT(x) \ |
| 65 | (((x) & LIGHTREC_REG_RT_MASK) >> LIGHTREC_REG_RT_LSB) |
| 66 | |
| 67 | #define LIGHTREC_REG_RD_LSB 30 |
| 68 | #define LIGHTREC_REG_RD(x) ((x) << LIGHTREC_REG_RD_LSB) |
| 69 | #define LIGHTREC_REG_RD_MASK LIGHTREC_REG_RD(0x3) |
| 70 | #define LIGHTREC_FLAGS_GET_RD(x) \ |
| 71 | (((x) & LIGHTREC_REG_RD_MASK) >> LIGHTREC_REG_RD_LSB) |
| 72 | |
| 73 | #define LIGHTREC_REG_NOOP 0x0 |
| 74 | #define LIGHTREC_REG_UNLOAD 0x1 |
| 75 | #define LIGHTREC_REG_DISCARD 0x2 |
| 76 | #define LIGHTREC_REG_CLEAN 0x3 |
| 77 | |
| 78 | struct block; |
| 79 | |
| 80 | enum standard_opcodes { |
| 81 | OP_SPECIAL = 0x00, |
| 82 | OP_REGIMM = 0x01, |
| 83 | OP_J = 0x02, |
| 84 | OP_JAL = 0x03, |
| 85 | OP_BEQ = 0x04, |
| 86 | OP_BNE = 0x05, |
| 87 | OP_BLEZ = 0x06, |
| 88 | OP_BGTZ = 0x07, |
| 89 | OP_ADDI = 0x08, |
| 90 | OP_ADDIU = 0x09, |
| 91 | OP_SLTI = 0x0a, |
| 92 | OP_SLTIU = 0x0b, |
| 93 | OP_ANDI = 0x0c, |
| 94 | OP_ORI = 0x0d, |
| 95 | OP_XORI = 0x0e, |
| 96 | OP_LUI = 0x0f, |
| 97 | OP_CP0 = 0x10, |
| 98 | OP_CP2 = 0x12, |
| 99 | OP_LB = 0x20, |
| 100 | OP_LH = 0x21, |
| 101 | OP_LWL = 0x22, |
| 102 | OP_LW = 0x23, |
| 103 | OP_LBU = 0x24, |
| 104 | OP_LHU = 0x25, |
| 105 | OP_LWR = 0x26, |
| 106 | OP_SB = 0x28, |
| 107 | OP_SH = 0x29, |
| 108 | OP_SWL = 0x2a, |
| 109 | OP_SW = 0x2b, |
| 110 | OP_SWR = 0x2e, |
| 111 | OP_LWC2 = 0x32, |
| 112 | OP_SWC2 = 0x3a, |
| 113 | |
| 114 | OP_META = 0x3c, |
| 115 | |
| 116 | OP_META_MULT2 = 0x19, |
| 117 | OP_META_MULTU2 = 0x1a, |
| 118 | OP_META_LWU = 0x1b, |
| 119 | OP_META_SWU = 0x1c, |
| 120 | }; |
| 121 | |
| 122 | enum special_opcodes { |
| 123 | OP_SPECIAL_SLL = 0x00, |
| 124 | OP_SPECIAL_SRL = 0x02, |
| 125 | OP_SPECIAL_SRA = 0x03, |
| 126 | OP_SPECIAL_SLLV = 0x04, |
| 127 | OP_SPECIAL_SRLV = 0x06, |
| 128 | OP_SPECIAL_SRAV = 0x07, |
| 129 | OP_SPECIAL_JR = 0x08, |
| 130 | OP_SPECIAL_JALR = 0x09, |
| 131 | OP_SPECIAL_SYSCALL = 0x0c, |
| 132 | OP_SPECIAL_BREAK = 0x0d, |
| 133 | OP_SPECIAL_MFHI = 0x10, |
| 134 | OP_SPECIAL_MTHI = 0x11, |
| 135 | OP_SPECIAL_MFLO = 0x12, |
| 136 | OP_SPECIAL_MTLO = 0x13, |
| 137 | OP_SPECIAL_MULT = 0x18, |
| 138 | OP_SPECIAL_MULTU = 0x19, |
| 139 | OP_SPECIAL_DIV = 0x1a, |
| 140 | OP_SPECIAL_DIVU = 0x1b, |
| 141 | OP_SPECIAL_ADD = 0x20, |
| 142 | OP_SPECIAL_ADDU = 0x21, |
| 143 | OP_SPECIAL_SUB = 0x22, |
| 144 | OP_SPECIAL_SUBU = 0x23, |
| 145 | OP_SPECIAL_AND = 0x24, |
| 146 | OP_SPECIAL_OR = 0x25, |
| 147 | OP_SPECIAL_XOR = 0x26, |
| 148 | OP_SPECIAL_NOR = 0x27, |
| 149 | OP_SPECIAL_SLT = 0x2a, |
| 150 | OP_SPECIAL_SLTU = 0x2b, |
| 151 | }; |
| 152 | |
| 153 | enum regimm_opcodes { |
| 154 | OP_REGIMM_BLTZ = 0x00, |
| 155 | OP_REGIMM_BGEZ = 0x01, |
| 156 | OP_REGIMM_BLTZAL = 0x10, |
| 157 | OP_REGIMM_BGEZAL = 0x11, |
| 158 | }; |
| 159 | |
| 160 | enum cp0_opcodes { |
| 161 | OP_CP0_MFC0 = 0x00, |
| 162 | OP_CP0_CFC0 = 0x02, |
| 163 | OP_CP0_MTC0 = 0x04, |
| 164 | OP_CP0_CTC0 = 0x06, |
| 165 | OP_CP0_RFE = 0x10, |
| 166 | }; |
| 167 | |
| 168 | enum cp2_opcodes { |
| 169 | OP_CP2_BASIC = 0x00, |
| 170 | OP_CP2_RTPS = 0x01, |
| 171 | OP_CP2_NCLIP = 0x06, |
| 172 | OP_CP2_OP = 0x0c, |
| 173 | OP_CP2_DPCS = 0x10, |
| 174 | OP_CP2_INTPL = 0x11, |
| 175 | OP_CP2_MVMVA = 0x12, |
| 176 | OP_CP2_NCDS = 0x13, |
| 177 | OP_CP2_CDP = 0x14, |
| 178 | OP_CP2_NCDT = 0x16, |
| 179 | OP_CP2_NCCS = 0x1b, |
| 180 | OP_CP2_CC = 0x1c, |
| 181 | OP_CP2_NCS = 0x1e, |
| 182 | OP_CP2_NCT = 0x20, |
| 183 | OP_CP2_SQR = 0x28, |
| 184 | OP_CP2_DCPL = 0x29, |
| 185 | OP_CP2_DPCT = 0x2a, |
| 186 | OP_CP2_AVSZ3 = 0x2d, |
| 187 | OP_CP2_AVSZ4 = 0x2e, |
| 188 | OP_CP2_RTPT = 0x30, |
| 189 | OP_CP2_GPF = 0x3d, |
| 190 | OP_CP2_GPL = 0x3e, |
| 191 | OP_CP2_NCCT = 0x3f, |
| 192 | }; |
| 193 | |
| 194 | enum cp2_basic_opcodes { |
| 195 | OP_CP2_BASIC_MFC2 = 0x00, |
| 196 | OP_CP2_BASIC_CFC2 = 0x02, |
| 197 | OP_CP2_BASIC_MTC2 = 0x04, |
| 198 | OP_CP2_BASIC_CTC2 = 0x06, |
| 199 | }; |
| 200 | |
| 201 | enum meta_opcodes { |
| 202 | OP_META_MOV = 0x00, |
| 203 | |
| 204 | OP_META_EXTC = 0x01, |
| 205 | OP_META_EXTS = 0x02, |
| 206 | |
| 207 | OP_META_COM = 0x03, |
| 208 | }; |
| 209 | |
| 210 | struct opcode_r { |
| 211 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 212 | u32 zero :6; |
| 213 | u32 rs :5; |
| 214 | u32 rt :5; |
| 215 | u32 rd :5; |
| 216 | u32 imm :5; |
| 217 | u32 op :6; |
| 218 | #else |
| 219 | u32 op :6; |
| 220 | u32 imm :5; |
| 221 | u32 rd :5; |
| 222 | u32 rt :5; |
| 223 | u32 rs :5; |
| 224 | u32 zero :6; |
| 225 | #endif |
| 226 | } __packed; |
| 227 | |
| 228 | struct opcode_i { |
| 229 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 230 | u32 op :6; |
| 231 | u32 rs :5; |
| 232 | u32 rt :5; |
| 233 | u32 imm :16; |
| 234 | #else |
| 235 | u32 imm :16; |
| 236 | u32 rt :5; |
| 237 | u32 rs :5; |
| 238 | u32 op :6; |
| 239 | #endif |
| 240 | } __packed; |
| 241 | |
| 242 | struct opcode_j { |
| 243 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 244 | u32 op :6; |
| 245 | u32 imm :26; |
| 246 | #else |
| 247 | u32 imm :26; |
| 248 | u32 op :6; |
| 249 | #endif |
| 250 | } __packed; |
| 251 | |
| 252 | struct opcode_m { |
| 253 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 254 | u32 meta :6; |
| 255 | u32 rs :5; |
| 256 | u32 rt :5; |
| 257 | u32 rd :5; |
| 258 | u32 imm :6; |
| 259 | u32 op :5; |
| 260 | #else |
| 261 | u32 op :5; |
| 262 | u32 imm :6; |
| 263 | u32 rd :5; |
| 264 | u32 rt :5; |
| 265 | u32 rs :5; |
| 266 | u32 meta :6; |
| 267 | #endif |
| 268 | }; |
| 269 | |
| 270 | union code { |
| 271 | /* Keep in sync with struct opcode */ |
| 272 | u32 opcode; |
| 273 | struct opcode_r r; |
| 274 | struct opcode_i i; |
| 275 | struct opcode_j j; |
| 276 | struct opcode_m m; |
| 277 | }; |
| 278 | |
| 279 | struct opcode { |
| 280 | /* Keep this union at the first position */ |
| 281 | union { |
| 282 | union code c; |
| 283 | |
| 284 | /* Keep in sync with union code */ |
| 285 | u32 opcode; |
| 286 | struct opcode_r r; |
| 287 | struct opcode_i i; |
| 288 | struct opcode_j j; |
| 289 | struct opcode_m m; |
| 290 | }; |
| 291 | u32 flags; |
| 292 | }; |
| 293 | |
| 294 | struct opcode_list { |
| 295 | u16 nb_ops; |
| 296 | struct opcode ops[]; |
| 297 | }; |
| 298 | |
| 299 | void lightrec_print_disassembly(const struct block *block, const u32 *code); |
| 300 | |
| 301 | static inline _Bool op_flag_no_ds(u32 flags) |
| 302 | { |
| 303 | return OPT_SWITCH_DELAY_SLOTS && (flags & LIGHTREC_NO_DS); |
| 304 | } |
| 305 | |
| 306 | static inline _Bool op_flag_sync(u32 flags) |
| 307 | { |
| 308 | return OPT_LOCAL_BRANCHES && (flags & LIGHTREC_SYNC); |
| 309 | } |
| 310 | |
| 311 | static inline _Bool op_flag_smc(u32 flags) |
| 312 | { |
| 313 | return OPT_FLAG_IO && (flags & LIGHTREC_SMC); |
| 314 | } |
| 315 | |
| 316 | static inline _Bool op_flag_no_invalidate(u32 flags) |
| 317 | { |
| 318 | return OPT_FLAG_IO && (flags & LIGHTREC_NO_INVALIDATE); |
| 319 | } |
| 320 | |
| 321 | static inline _Bool op_flag_no_mask(u32 flags) |
| 322 | { |
| 323 | return OPT_FLAG_IO && (flags & LIGHTREC_NO_MASK); |
| 324 | } |
| 325 | |
| 326 | static inline _Bool op_flag_load_delay(u32 flags) |
| 327 | { |
| 328 | return OPT_HANDLE_LOAD_DELAYS && (flags & LIGHTREC_LOAD_DELAY); |
| 329 | } |
| 330 | |
| 331 | static inline _Bool op_flag_emulate_branch(u32 flags) |
| 332 | { |
| 333 | return OPT_DETECT_IMPOSSIBLE_BRANCHES && |
| 334 | (flags & LIGHTREC_EMULATE_BRANCH); |
| 335 | } |
| 336 | |
| 337 | static inline _Bool op_flag_local_branch(u32 flags) |
| 338 | { |
| 339 | return OPT_LOCAL_BRANCHES && (flags & LIGHTREC_LOCAL_BRANCH); |
| 340 | } |
| 341 | |
| 342 | static inline _Bool op_flag_no_lo(u32 flags) |
| 343 | { |
| 344 | return OPT_FLAG_MULT_DIV && (flags & LIGHTREC_NO_LO); |
| 345 | } |
| 346 | |
| 347 | static inline _Bool op_flag_no_hi(u32 flags) |
| 348 | { |
| 349 | return OPT_FLAG_MULT_DIV && (flags & LIGHTREC_NO_HI); |
| 350 | } |
| 351 | |
| 352 | static inline _Bool op_flag_no_div_check(u32 flags) |
| 353 | { |
| 354 | return OPT_FLAG_MULT_DIV && (flags & LIGHTREC_NO_DIV_CHECK); |
| 355 | } |
| 356 | |
| 357 | #endif /* __DISASSEMBLER_H__ */ |