| 1 | /* |
| 2 | * Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net> |
| 3 | * |
| 4 | * This library is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU Lesser General Public |
| 6 | * License as published by the Free Software Foundation; either |
| 7 | * version 2.1 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This library 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 GNU |
| 12 | * Lesser General Public License for more details. |
| 13 | */ |
| 14 | |
| 15 | #ifndef __DISASSEMBLER_H__ |
| 16 | #define __DISASSEMBLER_H__ |
| 17 | |
| 18 | #include "debug.h" |
| 19 | #include "lightrec.h" |
| 20 | |
| 21 | #ifndef __packed |
| 22 | #define __packed __attribute__((packed)) |
| 23 | #endif |
| 24 | |
| 25 | #define LIGHTREC_DIRECT_IO (1 << 0) |
| 26 | #define LIGHTREC_NO_INVALIDATE (1 << 1) |
| 27 | #define LIGHTREC_NO_DS (1 << 2) |
| 28 | #define LIGHTREC_SMC (1 << 3) |
| 29 | #define LIGHTREC_EMULATE_BRANCH (1 << 4) |
| 30 | #define LIGHTREC_LOCAL_BRANCH (1 << 5) |
| 31 | #define LIGHTREC_HW_IO (1 << 6) |
| 32 | #define LIGHTREC_MULT32 (1 << 7) |
| 33 | |
| 34 | struct block; |
| 35 | |
| 36 | enum standard_opcodes { |
| 37 | OP_SPECIAL = 0x00, |
| 38 | OP_REGIMM = 0x01, |
| 39 | OP_J = 0x02, |
| 40 | OP_JAL = 0x03, |
| 41 | OP_BEQ = 0x04, |
| 42 | OP_BNE = 0x05, |
| 43 | OP_BLEZ = 0x06, |
| 44 | OP_BGTZ = 0x07, |
| 45 | OP_ADDI = 0x08, |
| 46 | OP_ADDIU = 0x09, |
| 47 | OP_SLTI = 0x0a, |
| 48 | OP_SLTIU = 0x0b, |
| 49 | OP_ANDI = 0x0c, |
| 50 | OP_ORI = 0x0d, |
| 51 | OP_XORI = 0x0e, |
| 52 | OP_LUI = 0x0f, |
| 53 | OP_CP0 = 0x10, |
| 54 | OP_CP2 = 0x12, |
| 55 | OP_LB = 0x20, |
| 56 | OP_LH = 0x21, |
| 57 | OP_LWL = 0x22, |
| 58 | OP_LW = 0x23, |
| 59 | OP_LBU = 0x24, |
| 60 | OP_LHU = 0x25, |
| 61 | OP_LWR = 0x26, |
| 62 | OP_SB = 0x28, |
| 63 | OP_SH = 0x29, |
| 64 | OP_SWL = 0x2a, |
| 65 | OP_SW = 0x2b, |
| 66 | OP_SWR = 0x2e, |
| 67 | OP_LWC2 = 0x32, |
| 68 | OP_SWC2 = 0x3a, |
| 69 | |
| 70 | OP_META_REG_UNLOAD = 0x11, |
| 71 | |
| 72 | OP_META_BEQZ = 0x14, |
| 73 | OP_META_BNEZ = 0x15, |
| 74 | |
| 75 | OP_META_MOV = 0x16, |
| 76 | OP_META_SYNC = 0x17, |
| 77 | }; |
| 78 | |
| 79 | enum special_opcodes { |
| 80 | OP_SPECIAL_SLL = 0x00, |
| 81 | OP_SPECIAL_SRL = 0x02, |
| 82 | OP_SPECIAL_SRA = 0x03, |
| 83 | OP_SPECIAL_SLLV = 0x04, |
| 84 | OP_SPECIAL_SRLV = 0x06, |
| 85 | OP_SPECIAL_SRAV = 0x07, |
| 86 | OP_SPECIAL_JR = 0x08, |
| 87 | OP_SPECIAL_JALR = 0x09, |
| 88 | OP_SPECIAL_SYSCALL = 0x0c, |
| 89 | OP_SPECIAL_BREAK = 0x0d, |
| 90 | OP_SPECIAL_MFHI = 0x10, |
| 91 | OP_SPECIAL_MTHI = 0x11, |
| 92 | OP_SPECIAL_MFLO = 0x12, |
| 93 | OP_SPECIAL_MTLO = 0x13, |
| 94 | OP_SPECIAL_MULT = 0x18, |
| 95 | OP_SPECIAL_MULTU = 0x19, |
| 96 | OP_SPECIAL_DIV = 0x1a, |
| 97 | OP_SPECIAL_DIVU = 0x1b, |
| 98 | OP_SPECIAL_ADD = 0x20, |
| 99 | OP_SPECIAL_ADDU = 0x21, |
| 100 | OP_SPECIAL_SUB = 0x22, |
| 101 | OP_SPECIAL_SUBU = 0x23, |
| 102 | OP_SPECIAL_AND = 0x24, |
| 103 | OP_SPECIAL_OR = 0x25, |
| 104 | OP_SPECIAL_XOR = 0x26, |
| 105 | OP_SPECIAL_NOR = 0x27, |
| 106 | OP_SPECIAL_SLT = 0x2a, |
| 107 | OP_SPECIAL_SLTU = 0x2b, |
| 108 | }; |
| 109 | |
| 110 | enum regimm_opcodes { |
| 111 | OP_REGIMM_BLTZ = 0x00, |
| 112 | OP_REGIMM_BGEZ = 0x01, |
| 113 | OP_REGIMM_BLTZAL = 0x10, |
| 114 | OP_REGIMM_BGEZAL = 0x11, |
| 115 | }; |
| 116 | |
| 117 | enum cp0_opcodes { |
| 118 | OP_CP0_MFC0 = 0x00, |
| 119 | OP_CP0_CFC0 = 0x02, |
| 120 | OP_CP0_MTC0 = 0x04, |
| 121 | OP_CP0_CTC0 = 0x06, |
| 122 | OP_CP0_RFE = 0x10, |
| 123 | }; |
| 124 | |
| 125 | enum cp2_opcodes { |
| 126 | OP_CP2_BASIC = 0x00, |
| 127 | }; |
| 128 | |
| 129 | enum cp2_basic_opcodes { |
| 130 | OP_CP2_BASIC_MFC2 = 0x00, |
| 131 | OP_CP2_BASIC_CFC2 = 0x02, |
| 132 | OP_CP2_BASIC_MTC2 = 0x04, |
| 133 | OP_CP2_BASIC_CTC2 = 0x06, |
| 134 | }; |
| 135 | |
| 136 | struct opcode_r { |
| 137 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 138 | u32 zero :6; |
| 139 | u32 rs :5; |
| 140 | u32 rt :5; |
| 141 | u32 rd :5; |
| 142 | u32 imm :5; |
| 143 | u32 op :6; |
| 144 | #else |
| 145 | u32 op :6; |
| 146 | u32 imm :5; |
| 147 | u32 rd :5; |
| 148 | u32 rt :5; |
| 149 | u32 rs :5; |
| 150 | u32 zero :6; |
| 151 | #endif |
| 152 | } __packed; |
| 153 | |
| 154 | struct opcode_i { |
| 155 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 156 | u32 op :6; |
| 157 | u32 rs :5; |
| 158 | u32 rt :5; |
| 159 | u32 imm :16; |
| 160 | #else |
| 161 | u32 imm :16; |
| 162 | u32 rt :5; |
| 163 | u32 rs :5; |
| 164 | u32 op :6; |
| 165 | #endif |
| 166 | } __packed; |
| 167 | |
| 168 | struct opcode_j { |
| 169 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 170 | u32 op :6; |
| 171 | u32 imm :26; |
| 172 | #else |
| 173 | u32 imm :26; |
| 174 | u32 op :6; |
| 175 | #endif |
| 176 | } __packed; |
| 177 | |
| 178 | union code { |
| 179 | /* Keep in sync with struct opcode */ |
| 180 | u32 opcode; |
| 181 | struct opcode_r r; |
| 182 | struct opcode_i i; |
| 183 | struct opcode_j j; |
| 184 | }; |
| 185 | |
| 186 | struct opcode { |
| 187 | /* Keep this union at the first position */ |
| 188 | union { |
| 189 | union code c; |
| 190 | |
| 191 | /* Keep in sync with union code */ |
| 192 | u32 opcode; |
| 193 | struct opcode_r r; |
| 194 | struct opcode_i i; |
| 195 | struct opcode_j j; |
| 196 | }; |
| 197 | u16 flags; |
| 198 | u16 offset; |
| 199 | struct opcode *next; |
| 200 | }; |
| 201 | |
| 202 | struct opcode * lightrec_disassemble(struct lightrec_state *state, |
| 203 | const u32 *src, unsigned int *len); |
| 204 | void lightrec_free_opcode_list(struct lightrec_state *state, |
| 205 | struct opcode *list); |
| 206 | |
| 207 | unsigned int lightrec_cycles_of_opcode(union code code); |
| 208 | |
| 209 | void lightrec_print_disassembly(const struct block *block, |
| 210 | const u32 *code, unsigned int length); |
| 211 | |
| 212 | #endif /* __DISASSEMBLER_H__ */ |