Michael Walle | 79368f4 | 2012-03-31 19:54:20 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Simple LatticeMico32 disassembler. |
| 3 | * |
| 4 | * Copyright (c) 2012 Michael Walle <michael@walle.cc> |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| 18 | * |
| 19 | */ |
| 20 | |
| 21 | #include <stdio.h> |
| 22 | #include "dis-asm.h" |
| 23 | |
| 24 | typedef enum { |
| 25 | LM32_OP_SRUI = 0, LM32_OP_NORI, LM32_OP_MULI, LM32_OP_SH, LM32_OP_LB, |
| 26 | LM32_OP_SRI, LM32_OP_XORI, LM32_OP_LH, LM32_OP_ANDI, LM32_OP_XNORI, |
| 27 | LM32_OP_LW, LM32_OP_LHU, LM32_OP_SB, LM32_OP_ADDI, LM32_OP_ORI, |
| 28 | LM32_OP_SLI, LM32_OP_LBU, LM32_OP_BE, LM32_OP_BG, LM32_OP_BGE, |
| 29 | LM32_OP_BGEU, LM32_OP_BGU, LM32_OP_SW, LM32_OP_BNE, LM32_OP_ANDHI, |
| 30 | LM32_OP_CMPEI, LM32_OP_CMPGI, LM32_OP_CMPGEI, LM32_OP_CMPGEUI, |
| 31 | LM32_OP_CMPGUI, LM32_OP_ORHI, LM32_OP_CMPNEI, LM32_OP_SRU, LM32_OP_NOR, |
| 32 | LM32_OP_MUL, LM32_OP_DIVU, LM32_OP_RCSR, LM32_OP_SR, LM32_OP_XOR, |
| 33 | LM32_OP_ILL0, LM32_OP_AND, LM32_OP_XNOR, LM32_OP_ILL1, LM32_OP_SCALL, |
| 34 | LM32_OP_SEXTB, LM32_OP_ADD, LM32_OP_OR, LM32_OP_SL, LM32_OP_B, |
| 35 | LM32_OP_MODU, LM32_OP_SUB, LM32_OP_ILL2, LM32_OP_WCSR, LM32_OP_ILL3, |
| 36 | LM32_OP_CALL, LM32_OP_SEXTH, LM32_OP_BI, LM32_OP_CMPE, LM32_OP_CMPG, |
| 37 | LM32_OP_CMPGE, LM32_OP_CMPGEU, LM32_OP_CMPGU, LM32_OP_CALLI, LM32_OP_CMPNE, |
| 38 | } Lm32Opcode; |
| 39 | |
| 40 | typedef enum { |
| 41 | FMT_INVALID = 0, FMT_RRI5, FMT_RRI16, FMT_IMM26, FMT_LOAD, FMT_STORE, |
| 42 | FMT_RRR, FMT_R, FMT_RNR, FMT_CRN, FMT_CNR, FMT_BREAK, |
| 43 | } Lm32OpcodeFmt; |
| 44 | |
| 45 | typedef enum { |
| 46 | LM32_CSR_IE = 0, LM32_CSR_IM, LM32_CSR_IP, LM32_CSR_ICC, LM32_CSR_DCC, |
| 47 | LM32_CSR_CC, LM32_CSR_CFG, LM32_CSR_EBA, LM32_CSR_DC, LM32_CSR_DEBA, |
| 48 | LM32_CSR_CFG2, LM32_CSR_JTX = 0xe, LM32_CSR_JRX, LM32_CSR_BP0, |
| 49 | LM32_CSR_BP1, LM32_CSR_BP2, LM32_CSR_BP3, LM32_CSR_WP0 = 0x18, |
| 50 | LM32_CSR_WP1, LM32_CSR_WP2, LM32_CSR_WP3, |
| 51 | } Lm32CsrNum; |
| 52 | |
| 53 | typedef struct { |
| 54 | int csr; |
| 55 | const char *name; |
| 56 | } Lm32CsrInfo; |
| 57 | |
| 58 | static const Lm32CsrInfo lm32_csr_info[] = { |
| 59 | {LM32_CSR_IE, "ie", }, |
| 60 | {LM32_CSR_IM, "im", }, |
| 61 | {LM32_CSR_IP, "ip", }, |
| 62 | {LM32_CSR_ICC, "icc", }, |
| 63 | {LM32_CSR_DCC, "dcc", }, |
| 64 | {LM32_CSR_CC, "cc", }, |
| 65 | {LM32_CSR_CFG, "cfg", }, |
| 66 | {LM32_CSR_EBA, "eba", }, |
| 67 | {LM32_CSR_DC, "dc", }, |
| 68 | {LM32_CSR_DEBA, "deba", }, |
| 69 | {LM32_CSR_CFG2, "cfg2", }, |
| 70 | {LM32_CSR_JTX, "jtx", }, |
| 71 | {LM32_CSR_JRX, "jrx", }, |
| 72 | {LM32_CSR_BP0, "bp0", }, |
| 73 | {LM32_CSR_BP1, "bp1", }, |
| 74 | {LM32_CSR_BP2, "bp2", }, |
| 75 | {LM32_CSR_BP3, "bp3", }, |
| 76 | {LM32_CSR_WP0, "wp0", }, |
| 77 | {LM32_CSR_WP1, "wp1", }, |
| 78 | {LM32_CSR_WP2, "wp2", }, |
| 79 | {LM32_CSR_WP3, "wp3", }, |
| 80 | }; |
| 81 | |
| 82 | static const Lm32CsrInfo *find_csr_info(int csr) |
| 83 | { |
| 84 | const Lm32CsrInfo *info; |
| 85 | int i; |
| 86 | |
| 87 | for (i = 0; i < ARRAY_SIZE(lm32_csr_info); i++) { |
| 88 | info = &lm32_csr_info[i]; |
| 89 | if (csr == info->csr) { |
| 90 | return info; |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | return NULL; |
| 95 | } |
| 96 | |
| 97 | typedef struct { |
| 98 | int reg; |
| 99 | const char *name; |
| 100 | } Lm32RegInfo; |
| 101 | |
| 102 | typedef enum { |
| 103 | LM32_REG_R0 = 0, LM32_REG_R1, LM32_REG_R2, LM32_REG_R3, LM32_REG_R4, |
| 104 | LM32_REG_R5, LM32_REG_R6, LM32_REG_R7, LM32_REG_R8, LM32_REG_R9, |
| 105 | LM32_REG_R10, LM32_REG_R11, LM32_REG_R12, LM32_REG_R13, LM32_REG_R14, |
| 106 | LM32_REG_R15, LM32_REG_R16, LM32_REG_R17, LM32_REG_R18, LM32_REG_R19, |
| 107 | LM32_REG_R20, LM32_REG_R21, LM32_REG_R22, LM32_REG_R23, LM32_REG_R24, |
| 108 | LM32_REG_R25, LM32_REG_GP, LM32_REG_FP, LM32_REG_SP, LM32_REG_RA, |
| 109 | LM32_REG_EA, LM32_REG_BA, |
| 110 | } Lm32RegNum; |
| 111 | |
| 112 | static const Lm32RegInfo lm32_reg_info[] = { |
| 113 | {LM32_REG_R0, "r0", }, |
| 114 | {LM32_REG_R1, "r1", }, |
| 115 | {LM32_REG_R2, "r2", }, |
| 116 | {LM32_REG_R3, "r3", }, |
| 117 | {LM32_REG_R4, "r4", }, |
| 118 | {LM32_REG_R5, "r5", }, |
| 119 | {LM32_REG_R6, "r6", }, |
| 120 | {LM32_REG_R7, "r7", }, |
| 121 | {LM32_REG_R8, "r8", }, |
| 122 | {LM32_REG_R9, "r9", }, |
| 123 | {LM32_REG_R10, "r10", }, |
| 124 | {LM32_REG_R11, "r11", }, |
| 125 | {LM32_REG_R12, "r12", }, |
| 126 | {LM32_REG_R13, "r13", }, |
| 127 | {LM32_REG_R14, "r14", }, |
| 128 | {LM32_REG_R15, "r15", }, |
| 129 | {LM32_REG_R16, "r16", }, |
| 130 | {LM32_REG_R17, "r17", }, |
| 131 | {LM32_REG_R18, "r18", }, |
| 132 | {LM32_REG_R19, "r19", }, |
| 133 | {LM32_REG_R20, "r20", }, |
| 134 | {LM32_REG_R21, "r21", }, |
| 135 | {LM32_REG_R22, "r22", }, |
| 136 | {LM32_REG_R23, "r23", }, |
| 137 | {LM32_REG_R24, "r24", }, |
| 138 | {LM32_REG_R25, "r25", }, |
| 139 | {LM32_REG_GP, "gp", }, |
| 140 | {LM32_REG_FP, "fp", }, |
| 141 | {LM32_REG_SP, "sp", }, |
| 142 | {LM32_REG_RA, "ra", }, |
| 143 | {LM32_REG_EA, "ea", }, |
| 144 | {LM32_REG_BA, "ba", }, |
| 145 | }; |
| 146 | |
| 147 | static const Lm32RegInfo *find_reg_info(int reg) |
| 148 | { |
| 149 | assert(ARRAY_SIZE(lm32_reg_info) == 32); |
| 150 | return &lm32_reg_info[reg & 0x1f]; |
| 151 | } |
| 152 | |
| 153 | typedef struct { |
| 154 | struct { |
| 155 | uint32_t code; |
| 156 | uint32_t mask; |
| 157 | } op; |
| 158 | const char *name; |
| 159 | const char *args_fmt; |
| 160 | } Lm32OpcodeInfo; |
| 161 | |
| 162 | static const Lm32OpcodeInfo lm32_opcode_info[] = { |
| 163 | /* pseudo instructions */ |
| 164 | {{0x34000000, 0xffffffff}, "nop", NULL}, |
| 165 | {{0xac000002, 0xffffffff}, "break", NULL}, |
| 166 | {{0xac000003, 0xffffffff}, "scall", NULL}, |
| 167 | {{0xc3e00000, 0xffffffff}, "bret", NULL}, |
| 168 | {{0xc3c00000, 0xffffffff}, "eret", NULL}, |
| 169 | {{0xc3a00000, 0xffffffff}, "ret", NULL}, |
| 170 | {{0xa4000000, 0xfc1f07ff}, "not", "%2, %0"}, |
| 171 | {{0xb8000000, 0xfc1f07ff}, "mv", "%2, %0"}, |
| 172 | {{0x71e00000, 0xffe00000}, "mvhi", "%1, %u"}, |
| 173 | {{0x34000000, 0xffe00000}, "mvi", "%1, %s"}, |
| 174 | |
| 175 | #define _O(op) {op << 26, 0x3f << 26} |
| 176 | /* regular opcodes */ |
| 177 | {_O(LM32_OP_ADD), "add", "%2, %0, %1" }, |
| 178 | {_O(LM32_OP_ADDI), "addi", "%1, %0, %s" }, |
| 179 | {_O(LM32_OP_AND), "and", "%2, %0, %1" }, |
| 180 | {_O(LM32_OP_ANDHI), "andhi", "%1, %0, %u" }, |
| 181 | {_O(LM32_OP_ANDI), "andi", "%1, %0, %u" }, |
| 182 | {_O(LM32_OP_B), "b", "%0", }, |
| 183 | {_O(LM32_OP_BE), "be", "%1, %0, %r" }, |
| 184 | {_O(LM32_OP_BG), "bg", "%1, %0, %r" }, |
| 185 | {_O(LM32_OP_BGE), "bge", "%1, %0, %r" }, |
| 186 | {_O(LM32_OP_BGEU), "bgeu", "%1, %0, %r" }, |
| 187 | {_O(LM32_OP_BGU), "bgu", "%1, %0, %r" }, |
| 188 | {_O(LM32_OP_BI), "bi", "%R", }, |
| 189 | {_O(LM32_OP_BNE), "bne", "%1, %0, %r" }, |
| 190 | {_O(LM32_OP_CALL), "call", "%0", }, |
| 191 | {_O(LM32_OP_CALLI), "calli", "%R", }, |
| 192 | {_O(LM32_OP_CMPE), "cmpe", "%2, %0, %1" }, |
| 193 | {_O(LM32_OP_CMPEI), "cmpei", "%1, %0, %s" }, |
| 194 | {_O(LM32_OP_CMPG), "cmpg", "%2, %0, %1" }, |
| 195 | {_O(LM32_OP_CMPGE), "cmpge", "%2, %0, %1" }, |
| 196 | {_O(LM32_OP_CMPGEI), "cmpgei", "%1, %0, %s" }, |
| 197 | {_O(LM32_OP_CMPGEU), "cmpgeu", "%2, %0, %1" }, |
| 198 | {_O(LM32_OP_CMPGEUI), "cmpgeui", "%1, %0, %s" }, |
| 199 | {_O(LM32_OP_CMPGI), "cmpgi", "%1, %0, %s" }, |
| 200 | {_O(LM32_OP_CMPGU), "cmpgu", "%2, %0, %1" }, |
| 201 | {_O(LM32_OP_CMPGUI), "cmpgui", "%1, %0, %s" }, |
| 202 | {_O(LM32_OP_CMPNE), "cmpne", "%2, %0, %1" }, |
| 203 | {_O(LM32_OP_CMPNEI), "cmpnei", "%1, %0, %s" }, |
| 204 | {_O(LM32_OP_DIVU), "divu", "%2, %0, %1" }, |
| 205 | {_O(LM32_OP_LB), "lb", "%1, (%0+%s)" }, |
| 206 | {_O(LM32_OP_LBU), "lbu", "%1, (%0+%s)" }, |
| 207 | {_O(LM32_OP_LH), "lh", "%1, (%0+%s)" }, |
| 208 | {_O(LM32_OP_LHU), "lhu", "%1, (%0+%s)" }, |
| 209 | {_O(LM32_OP_LW), "lw", "%1, (%0+%s)" }, |
| 210 | {_O(LM32_OP_MODU), "modu", "%2, %0, %1" }, |
| 211 | {_O(LM32_OP_MULI), "muli", "%1, %0, %s" }, |
| 212 | {_O(LM32_OP_MUL), "mul", "%2, %0, %1" }, |
| 213 | {_O(LM32_OP_NORI), "nori", "%1, %0, %u" }, |
| 214 | {_O(LM32_OP_NOR), "nor", "%2, %0, %1" }, |
| 215 | {_O(LM32_OP_ORHI), "orhi", "%1, %0, %u" }, |
| 216 | {_O(LM32_OP_ORI), "ori", "%1, %0, %u" }, |
| 217 | {_O(LM32_OP_OR), "or", "%2, %0, %1" }, |
| 218 | {_O(LM32_OP_RCSR), "rcsr", "%2, %c", }, |
| 219 | {_O(LM32_OP_SB), "sb", "(%0+%s), %1" }, |
| 220 | {_O(LM32_OP_SEXTB), "sextb", "%2, %0", }, |
| 221 | {_O(LM32_OP_SEXTH), "sexth", "%2, %0", }, |
| 222 | {_O(LM32_OP_SH), "sh", "(%0+%s), %1" }, |
| 223 | {_O(LM32_OP_SLI), "sli", "%1, %0, %h" }, |
| 224 | {_O(LM32_OP_SL), "sl", "%2, %0, %1" }, |
| 225 | {_O(LM32_OP_SRI), "sri", "%1, %0, %h" }, |
| 226 | {_O(LM32_OP_SR), "sr", "%2, %0, %1" }, |
| 227 | {_O(LM32_OP_SRUI), "srui", "%1, %0, %d" }, |
| 228 | {_O(LM32_OP_SRU), "sru", "%2, %0, %s" }, |
| 229 | {_O(LM32_OP_SUB), "sub", "%2, %0, %s" }, |
| 230 | {_O(LM32_OP_SW), "sw", "(%0+%s), %1" }, |
| 231 | {_O(LM32_OP_WCSR), "wcsr", "%c, %1", }, |
| 232 | {_O(LM32_OP_XNORI), "xnori", "%1, %0, %u" }, |
| 233 | {_O(LM32_OP_XNOR), "xnor", "%2, %0, %1" }, |
| 234 | {_O(LM32_OP_XORI), "xori", "%1, %0, %u" }, |
| 235 | {_O(LM32_OP_XOR), "xor", "%2, %0, %1" }, |
| 236 | #undef _O |
| 237 | }; |
| 238 | |
| 239 | static const Lm32OpcodeInfo *find_opcode_info(uint32_t opcode) |
| 240 | { |
| 241 | const Lm32OpcodeInfo *info; |
| 242 | int i; |
| 243 | for (i = 0; i < ARRAY_SIZE(lm32_opcode_info); i++) { |
| 244 | info = &lm32_opcode_info[i]; |
| 245 | if ((opcode & info->op.mask) == info->op.code) { |
| 246 | return info; |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | return NULL; |
| 251 | } |
| 252 | |
| 253 | int print_insn_lm32(bfd_vma memaddr, struct disassemble_info *info) |
| 254 | { |
| 255 | fprintf_function fprintf_fn = info->fprintf_func; |
| 256 | void *stream = info->stream; |
| 257 | int rc; |
| 258 | uint8_t insn[4]; |
| 259 | const Lm32OpcodeInfo *opc_info; |
| 260 | uint32_t op; |
| 261 | const char *args_fmt; |
| 262 | |
| 263 | rc = info->read_memory_func(memaddr, insn, 4, info); |
| 264 | if (rc != 0) { |
| 265 | info->memory_error_func(rc, memaddr, info); |
| 266 | return -1; |
| 267 | } |
| 268 | |
| 269 | fprintf_fn(stream, "%02x %02x %02x %02x ", |
| 270 | insn[0], insn[1], insn[2], insn[3]); |
| 271 | |
| 272 | op = bfd_getb32(insn); |
| 273 | opc_info = find_opcode_info(op); |
| 274 | if (opc_info) { |
| 275 | fprintf_fn(stream, "%-8s ", opc_info->name); |
| 276 | args_fmt = opc_info->args_fmt; |
| 277 | while (args_fmt && *args_fmt) { |
| 278 | if (*args_fmt == '%') { |
| 279 | switch (*(++args_fmt)) { |
| 280 | case '0': { |
| 281 | uint8_t r0; |
| 282 | const char *r0_name; |
| 283 | r0 = (op >> 21) & 0x1f; |
| 284 | r0_name = find_reg_info(r0)->name; |
| 285 | fprintf_fn(stream, "%s", r0_name); |
| 286 | break; |
| 287 | } |
| 288 | case '1': { |
| 289 | uint8_t r1; |
| 290 | const char *r1_name; |
| 291 | r1 = (op >> 16) & 0x1f; |
| 292 | r1_name = find_reg_info(r1)->name; |
| 293 | fprintf_fn(stream, "%s", r1_name); |
| 294 | break; |
| 295 | } |
| 296 | case '2': { |
| 297 | uint8_t r2; |
| 298 | const char *r2_name; |
| 299 | r2 = (op >> 11) & 0x1f; |
| 300 | r2_name = find_reg_info(r2)->name; |
| 301 | fprintf_fn(stream, "%s", r2_name); |
| 302 | break; |
| 303 | } |
| 304 | case 'c': { |
| 305 | uint8_t csr; |
| 306 | const char *csr_name; |
| 307 | csr = (op >> 21) & 0x1f; |
| 308 | csr_name = find_csr_info(csr)->name; |
| 309 | if (csr_name) { |
| 310 | fprintf_fn(stream, "%s", csr_name); |
| 311 | } else { |
| 312 | fprintf_fn(stream, "0x%x", csr); |
| 313 | } |
| 314 | break; |
| 315 | } |
| 316 | case 'u': { |
| 317 | uint16_t u16; |
| 318 | u16 = op & 0xffff; |
| 319 | fprintf_fn(stream, "0x%x", u16); |
| 320 | break; |
| 321 | } |
| 322 | case 's': { |
| 323 | int16_t s16; |
| 324 | s16 = (int16_t)(op & 0xffff); |
| 325 | fprintf_fn(stream, "%d", s16); |
| 326 | break; |
| 327 | } |
| 328 | case 'r': { |
| 329 | uint32_t rela; |
| 330 | rela = memaddr + (((int16_t)(op & 0xffff)) << 2); |
| 331 | fprintf_fn(stream, "%x", rela); |
| 332 | break; |
| 333 | } |
| 334 | case 'R': { |
| 335 | uint32_t rela; |
| 336 | int32_t imm26; |
| 337 | imm26 = (int32_t)((op & 0x3ffffff) << 6) >> 4; |
| 338 | rela = memaddr + imm26; |
| 339 | fprintf_fn(stream, "%x", rela); |
| 340 | break; |
| 341 | } |
| 342 | case 'h': { |
| 343 | uint8_t u5; |
| 344 | u5 = (op & 0x1f); |
| 345 | fprintf_fn(stream, "%d", u5); |
| 346 | break; |
| 347 | } |
| 348 | default: |
| 349 | break; |
| 350 | } |
| 351 | } else { |
| 352 | fprintf_fn(stream, "%c", *args_fmt); |
| 353 | } |
| 354 | args_fmt++; |
| 355 | } |
| 356 | } else { |
| 357 | fprintf_fn(stream, ".word 0x%x", op); |
| 358 | } |
| 359 | |
| 360 | return 4; |
| 361 | } |