nkeynes@30 | 1 | /**
|
nkeynes@561 | 2 | * $Id$
|
nkeynes@30 | 3 | *
|
nkeynes@4 | 4 | * armdasm.c 21 Aug 2004 - ARM7tdmi (ARMv4) disassembler
|
nkeynes@4 | 5 | *
|
nkeynes@30 | 6 | * Copyright (c) 2005 Nathan Keynes.
|
nkeynes@30 | 7 | *
|
nkeynes@30 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@30 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@30 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@30 | 11 | * (at your option) any later version.
|
nkeynes@30 | 12 | *
|
nkeynes@30 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@30 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@30 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@30 | 16 | * GNU General Public License for more details.
|
nkeynes@4 | 17 | */
|
nkeynes@4 | 18 |
|
nkeynes@7 | 19 | #include "aica/armcore.h"
|
nkeynes@11 | 20 | #include "aica/armdasm.h"
|
nkeynes@7 | 21 | #include <stdlib.h>
|
nkeynes@4 | 22 |
|
nkeynes@4 | 23 | #define COND(ir) (ir>>28)
|
nkeynes@4 | 24 | #define OPCODE(ir) ((ir>>20)&0x1F)
|
nkeynes@4 | 25 | #define GRP(ir) ((ir>>26)&0x03)
|
nkeynes@4 | 26 | #define IFLAG(ir) (ir&0x02000000)
|
nkeynes@4 | 27 | #define SFLAG(ir) (ir&0x00100000)
|
nkeynes@4 | 28 | #define PFLAG(ir) (ir&0x01000000)
|
nkeynes@4 | 29 | #define UFLAG(ir) (ir&0x00800000)
|
nkeynes@4 | 30 | #define BFLAG(ir) (ir&0x00400000)
|
nkeynes@7 | 31 | #define WFLAG(ir) (ir&0x00200000)
|
nkeynes@4 | 32 | #define LFLAG(ir) SFLAG(ir)
|
nkeynes@4 | 33 | #define RN(ir) ((ir>>16)&0x0F)
|
nkeynes@4 | 34 | #define RD(ir) ((ir>>12)&0x0F)
|
nkeynes@4 | 35 | #define RS(ir) ((ir>>8)&0x0F)
|
nkeynes@4 | 36 | #define RM(ir) (ir&0x0F)
|
nkeynes@4 | 37 |
|
nkeynes@4 | 38 | #define IMM8(ir) (ir&0xFF)
|
nkeynes@4 | 39 | #define IMM12(ir) (ir&0xFFF)
|
nkeynes@811 | 40 | #define IMMSPLIT8(ir) (((ir&0xF00)>>4)|(ir&0x0F))
|
nkeynes@7 | 41 | #define SHIFTIMM(ir) ((ir>>7)&0x1F)
|
nkeynes@7 | 42 | #define IMMROT(ir) ((ir>>7)&0x1E)
|
nkeynes@4 | 43 | #define SHIFT(ir) ((ir>>4)&0x07)
|
nkeynes@4 | 44 | #define DISP24(ir) ((ir&0x00FFFFFF))
|
nkeynes@4 | 45 | #define FSXC(ir) msrFieldMask[RN(ir)]
|
nkeynes@4 | 46 | #define ROTIMM12(ir) ROTATE_RIGHT_LONG(IMM8(ir),IMMROT(ir))
|
nkeynes@431 | 47 | #define SIGNEXT24(n) (((n)&0x00800000) ? ((n)|0xFF000000) : ((n)&0x00FFFFFF))
|
nkeynes@13 | 48 |
|
nkeynes@4 | 49 |
|
nkeynes@11 | 50 |
|
nkeynes@11 | 51 | const struct reg_desc_struct arm_reg_map[] =
|
nkeynes@736 | 52 | { {"R0", REG_INT, &armr.r[0]}, {"R1", REG_INT, &armr.r[1]},
|
nkeynes@736 | 53 | {"R2", REG_INT, &armr.r[2]}, {"R3", REG_INT, &armr.r[3]},
|
nkeynes@736 | 54 | {"R4", REG_INT, &armr.r[4]}, {"R5", REG_INT, &armr.r[5]},
|
nkeynes@736 | 55 | {"R6", REG_INT, &armr.r[6]}, {"R7", REG_INT, &armr.r[7]},
|
nkeynes@736 | 56 | {"R8", REG_INT, &armr.r[8]}, {"R9", REG_INT, &armr.r[9]},
|
nkeynes@736 | 57 | {"R10",REG_INT, &armr.r[10]}, {"R11",REG_INT, &armr.r[11]},
|
nkeynes@736 | 58 | {"R12",REG_INT, &armr.r[12]}, {"R13",REG_INT, &armr.r[13]},
|
nkeynes@736 | 59 | {"R14",REG_INT, &armr.r[14]}, {"R15",REG_INT, &armr.r[15]},
|
nkeynes@998 | 60 |
|
nkeynes@998 | 61 | /* Block of FPA registers (arm-elf-gdb seems to expect these).
|
nkeynes@998 | 62 | * Oddly enough the ARM7TDMI doesn't have them */
|
nkeynes@998 | 63 | {"F0",REG_NONE, NULL}, {"F1",REG_NONE, NULL},
|
nkeynes@998 | 64 | {"F2",REG_NONE, NULL}, {"F3",REG_NONE, NULL},
|
nkeynes@998 | 65 | {"F4",REG_NONE, NULL}, {"F5",REG_NONE, NULL},
|
nkeynes@998 | 66 | {"F6",REG_NONE, NULL}, {"F7",REG_NONE, NULL},
|
nkeynes@998 | 67 | {"FPS",REG_NONE, NULL},
|
nkeynes@998 | 68 |
|
nkeynes@998 | 69 | /* System registers */
|
nkeynes@736 | 70 | {"CPSR", REG_INT, &armr.cpsr}, {"SPSR", REG_INT, &armr.spsr},
|
nkeynes@736 | 71 | {NULL, 0, NULL} };
|
nkeynes@11 | 72 |
|
nkeynes@998 | 73 | /* Implementation of get_register - ARM has no pseudo registers so this
|
nkeynes@998 | 74 | * is pretty simple
|
nkeynes@998 | 75 | */
|
nkeynes@998 | 76 | void *arm_get_register( int reg )
|
nkeynes@998 | 77 | {
|
nkeynes@998 | 78 | if( reg < 0 || reg >= 27 )
|
nkeynes@998 | 79 | return NULL;
|
nkeynes@998 | 80 | return arm_reg_map[reg].value;
|
nkeynes@998 | 81 | }
|
nkeynes@11 | 82 |
|
nkeynes@14 | 83 | const struct cpu_desc_struct arm_cpu_desc =
|
nkeynes@998 | 84 | { "ARM7", arm_disasm_instruction, arm_get_register, arm_has_page,
|
nkeynes@998 | 85 | arm_read_phys, arm_write_phys, arm_read_phys, arm_write_phys,
|
nkeynes@998 | 86 | arm_execute_instruction, arm_set_breakpoint, arm_clear_breakpoint,
|
nkeynes@998 | 87 | arm_get_breakpoint, 4, (char *)&armr, sizeof(armr), arm_reg_map, 26, 26,
|
nkeynes@736 | 88 | &armr.r[15] };
|
nkeynes@14 | 89 | const struct cpu_desc_struct armt_cpu_desc =
|
nkeynes@998 | 90 | { "ARM7T", armt_disasm_instruction, arm_get_register, arm_has_page,
|
nkeynes@998 | 91 | arm_read_phys, arm_write_phys, arm_read_phys, arm_write_phys,
|
nkeynes@998 | 92 | arm_execute_instruction, arm_set_breakpoint, arm_clear_breakpoint,
|
nkeynes@998 | 93 | arm_get_breakpoint, 2, (char*)&armr, sizeof(armr), arm_reg_map, 26, 26,
|
nkeynes@736 | 94 | &armr.r[15] };
|
nkeynes@11 | 95 |
|
nkeynes@11 | 96 |
|
nkeynes@11 | 97 |
|
nkeynes@11 | 98 |
|
nkeynes@4 | 99 | char *conditionNames[] = { "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
|
nkeynes@736 | 100 | "HI", "LS", "GE", "LT", "GT", "LE", " " /*AL*/, "NV" };
|
nkeynes@736 | 101 |
|
nkeynes@736 | 102 | /* fsxc */
|
nkeynes@4 | 103 | char *msrFieldMask[] = { "", "c", "x", "xc", "s", "sc", "sx", "sxc",
|
nkeynes@736 | 104 | "f", "fc", "fx", "fxc", "fs", "fsc", "fsx", "fsxc" };
|
nkeynes@7 | 105 | char *ldmModes[] = { "DA", "IA", "DB", "IB" };
|
nkeynes@4 | 106 |
|
nkeynes@4 | 107 | #define UNIMP(ir) snprintf( buf, len, "??? " )
|
nkeynes@4 | 108 |
|
nkeynes@7 | 109 | int arm_disasm_shift_operand( uint32_t ir, char *buf, int len )
|
nkeynes@4 | 110 | {
|
nkeynes@736 | 111 | uint32_t operand, tmp;
|
nkeynes@736 | 112 | if( IFLAG(ir) == 0 ) {
|
nkeynes@736 | 113 | switch(SHIFT(ir)) {
|
nkeynes@736 | 114 | case 0: /* (Rm << imm) */
|
nkeynes@736 | 115 | tmp = SHIFTIMM(ir);
|
nkeynes@736 | 116 | if( tmp != 0 ) {
|
nkeynes@736 | 117 | return snprintf(buf, len, "R%d << %d", RM(ir), tmp );
|
nkeynes@736 | 118 | } else {
|
nkeynes@736 | 119 | return snprintf(buf, len, "R%d", RM(ir));
|
nkeynes@736 | 120 | }
|
nkeynes@736 | 121 | case 1: /* (Rm << Rs) */
|
nkeynes@736 | 122 | return snprintf(buf, len, "R%d << R%d", RM(ir), RS(ir) );
|
nkeynes@736 | 123 | case 2: /* (Rm >> imm) */
|
nkeynes@736 | 124 | return snprintf(buf, len, "R%d >> %d", RM(ir), SHIFTIMM(ir) );
|
nkeynes@736 | 125 | case 3: /* (Rm >> Rs) */
|
nkeynes@736 | 126 | return snprintf(buf, len, "R%d >> R%d", RM(ir), RS(ir) );
|
nkeynes@736 | 127 | case 4: /* (Rm >>> imm) */
|
nkeynes@736 | 128 | return snprintf(buf, len, "R%d >>> %d", RM(ir), SHIFTIMM(ir) );
|
nkeynes@736 | 129 | case 5: /* (Rm >>> Rs) */
|
nkeynes@736 | 130 | return snprintf(buf, len, "R%d >>> R%d", RM(ir), RS(ir) );
|
nkeynes@736 | 131 | case 6:
|
nkeynes@736 | 132 | tmp = SHIFTIMM(ir);
|
nkeynes@736 | 133 | if( tmp == 0 ) /* RRX aka rotate with carry */
|
nkeynes@736 | 134 | return snprintf(buf, len, "R%d roc 1", RM(ir) );
|
nkeynes@736 | 135 | else
|
nkeynes@736 | 136 | return snprintf(buf, len, "R%d rot %d", RM(ir), SHIFTIMM(ir) );
|
nkeynes@736 | 137 | case 7:
|
nkeynes@736 | 138 | return snprintf(buf, len, "R%d rot R%d", RM(ir), RS(ir) );
|
nkeynes@736 | 139 | }
|
nkeynes@736 | 140 | } else {
|
nkeynes@736 | 141 | operand = IMM8(ir);
|
nkeynes@736 | 142 | tmp = IMMROT(ir);
|
nkeynes@736 | 143 | operand = ROTATE_RIGHT_LONG(operand, tmp);
|
nkeynes@736 | 144 | return snprintf(buf, len, "#%08Xh", operand );
|
nkeynes@736 | 145 | }
|
nkeynes@736 | 146 | return 0;
|
nkeynes@7 | 147 | }
|
nkeynes@7 | 148 |
|
nkeynes@7 | 149 | static int arm_disasm_address_index( uint32_t ir, char *buf, int len )
|
nkeynes@7 | 150 | {
|
nkeynes@736 | 151 | uint32_t tmp;
|
nkeynes@736 | 152 |
|
nkeynes@736 | 153 | switch(SHIFT(ir)) {
|
nkeynes@736 | 154 | case 0: /* (Rm << imm) */
|
nkeynes@736 | 155 | tmp = SHIFTIMM(ir);
|
nkeynes@736 | 156 | if( tmp != 0 ) {
|
nkeynes@736 | 157 | return snprintf( buf, len, "R%d << %d", RM(ir), tmp );
|
nkeynes@736 | 158 | } else {
|
nkeynes@736 | 159 | return snprintf( buf, len, "R%d", RM(ir) );
|
nkeynes@736 | 160 | }
|
nkeynes@736 | 161 | case 2: /* (Rm >> imm) */
|
nkeynes@736 | 162 | return snprintf( buf, len, "R%d >> %d", RM(ir), SHIFTIMM(ir) );
|
nkeynes@736 | 163 | case 4: /* (Rm >>> imm) */
|
nkeynes@736 | 164 | return snprintf( buf, len, "R%d >>> %d", RM(ir), SHIFTIMM(ir) );
|
nkeynes@736 | 165 | case 6:
|
nkeynes@736 | 166 | tmp = SHIFTIMM(ir);
|
nkeynes@736 | 167 | if( tmp == 0 ) /* RRX aka rotate with carry */
|
nkeynes@736 | 168 | return snprintf( buf, len, "R%d roc 1", RM(ir) );
|
nkeynes@736 | 169 | else
|
nkeynes@736 | 170 | return snprintf( buf, len, "R%d rot %d", RM(ir), tmp );
|
nkeynes@736 | 171 | default:
|
nkeynes@736 | 172 | return UNIMP(ir);
|
nkeynes@736 | 173 | }
|
nkeynes@7 | 174 | }
|
nkeynes@7 | 175 |
|
nkeynes@13 | 176 | static int arm_disasm_address_operand( uint32_t ir, char *buf, int len, int pc )
|
nkeynes@7 | 177 | {
|
nkeynes@7 | 178 | char shift[32];
|
nkeynes@7 | 179 |
|
nkeynes@736 | 180 | char sign = UFLAG(ir) ? '+' : '-';
|
nkeynes@736 | 181 | /* I P U . W */
|
nkeynes@736 | 182 | switch( (ir>>21)&0x19 ) {
|
nkeynes@736 | 183 | case 0: /* Rn -= imm offset (post-indexed) [5.2.8 A5-28] */
|
nkeynes@736 | 184 | case 1:
|
nkeynes@736 | 185 | return snprintf( buf, len, "[R%d], R%d %c= #%04Xh", RN(ir), RN(ir), sign, IMM12(ir) );
|
nkeynes@736 | 186 | case 8: /* Rn - imm offset [5.2.2 A5-20] */
|
nkeynes@736 | 187 | if( RN(ir) == 15 ) { /* PC relative - decode here */
|
nkeynes@736 | 188 | uint32_t addr = pc + 8 + (UFLAG(ir) ? IMM12(ir) : -IMM12(ir));
|
nkeynes@736 | 189 | return snprintf( buf, len, "[$%08Xh] <- #%08Xh", addr,
|
nkeynes@736 | 190 | arm_read_long( addr ) );
|
nkeynes@736 | 191 | } else {
|
nkeynes@736 | 192 | return snprintf( buf, len, "[R%d %c #%04Xh]", RN(ir), sign, IMM12(ir) );
|
nkeynes@736 | 193 | }
|
nkeynes@736 | 194 | case 9: /* Rn -= imm offset (pre-indexed) [5.2.5 A5-24] */
|
nkeynes@736 | 195 | return snprintf( buf, len, "[R%d %c= #%04Xh]", RN(ir), sign, IMM12(ir) );
|
nkeynes@736 | 196 | case 16: /* Rn -= Rm (post-indexed) [5.2.10 A5-32 ] */
|
nkeynes@736 | 197 | case 17:
|
nkeynes@736 | 198 | arm_disasm_address_index( ir, shift, sizeof(shift) );
|
nkeynes@736 | 199 | return snprintf( buf, len, "[R%d], R%d %c= %s", RN(ir), RN(ir), sign, shift );
|
nkeynes@736 | 200 | case 24: /* Rn - Rm [5.2.4 A5-23] */
|
nkeynes@736 | 201 | arm_disasm_address_index( ir, shift, sizeof(shift) );
|
nkeynes@736 | 202 | return snprintf( buf, len, "[R%d %c %s]", RN(ir), sign, shift );
|
nkeynes@736 | 203 | case 25: /* RN -= Rm (pre-indexed) [5.2.7 A5-26] */
|
nkeynes@736 | 204 | arm_disasm_address_index( ir, shift, sizeof(shift) );
|
nkeynes@736 | 205 | return snprintf( buf, len, "[R%d %c= %s]", RN(ir), sign, shift );
|
nkeynes@736 | 206 | default:
|
nkeynes@736 | 207 | return UNIMP(ir); /* Unreachable */
|
nkeynes@736 | 208 | }
|
nkeynes@7 | 209 | }
|
nkeynes@7 | 210 |
|
nkeynes@811 | 211 | static int arm_disasm_address3_operand( uint32_t ir, char *buf, int len, int pc )
|
nkeynes@811 | 212 | {
|
nkeynes@811 | 213 | char sign = UFLAG(ir) ? '+' : '-';
|
nkeynes@811 | 214 |
|
nkeynes@811 | 215 | switch( (ir>>21) & 0x0B) {
|
nkeynes@811 | 216 | case 0: /* Rn -= Rm (post-indexed) [5.3.7 A5-48] */
|
nkeynes@811 | 217 | case 1: /* UNPREDICTABLE */
|
nkeynes@811 | 218 | return snprintf( buf, len, "[R%d], R%d %c= R%d", RN(ir), RN(ir), sign, RM(ir) ) ;
|
nkeynes@811 | 219 | case 2: /* Rn -= imm (post-indexed) [5.3.6 A5-46] */
|
nkeynes@811 | 220 | case 3: /* UNPREDICTABLE */
|
nkeynes@811 | 221 | return snprintf( buf, len, "[R%d], R%d %c= #%04Xh", RN(ir), RN(ir), sign, IMMSPLIT8(ir) );
|
nkeynes@811 | 222 | case 8: /* Rn - Rm [5.3.3 A5-38] */
|
nkeynes@811 | 223 | return snprintf( buf, len, "[R%d %c R%d]", RN(ir), sign, RM(ir) );
|
nkeynes@811 | 224 | case 9: /* Rn -= Rm (pre-indexed) [5.3.5 A5-42] */
|
nkeynes@811 | 225 | return snprintf( buf, len, "[R%d %c= R%d]", RN(ir), sign, RM(ir) );
|
nkeynes@811 | 226 | case 10: /* Rn - imm offset [5.3.2 A5-36] */
|
nkeynes@811 | 227 | return snprintf( buf, len, "[R%d %c #%04Xh]", RN(ir), sign, IMMSPLIT8(ir) );
|
nkeynes@811 | 228 | case 11: /* Rn -= imm offset (pre-indexed) [5.3.4 A5-40] */
|
nkeynes@811 | 229 | return snprintf( buf, len, "[R%d %c= #%04Xh]", RN(ir), sign, IMMSPLIT8(ir) );
|
nkeynes@811 | 230 | default:
|
nkeynes@811 | 231 | return UNIMP(ir); /* Unreachable */
|
nkeynes@811 | 232 | }
|
nkeynes@811 | 233 | }
|
nkeynes@811 | 234 |
|
nkeynes@11 | 235 | uint32_t arm_disasm_instruction( uint32_t pc, char *buf, int len, char *opcode )
|
nkeynes@7 | 236 | {
|
nkeynes@48 | 237 | char operand[64];
|
nkeynes@11 | 238 | uint32_t ir = arm_read_long(pc);
|
nkeynes@11 | 239 | int i,j;
|
nkeynes@736 | 240 |
|
nkeynes@813 | 241 | sprintf( opcode, "%08X", ir );
|
nkeynes@11 | 242 |
|
nkeynes@4 | 243 | if( COND(ir) == 0x0F ) {
|
nkeynes@736 | 244 | UNIMP(ir);
|
nkeynes@736 | 245 | return pc+4;
|
nkeynes@4 | 246 | }
|
nkeynes@4 | 247 | char *cond = conditionNames[COND(ir)];
|
nkeynes@4 | 248 |
|
nkeynes@736 | 249 | switch( GRP(ir) ) {
|
nkeynes@736 | 250 | case 0:
|
nkeynes@736 | 251 | if( (ir & 0x0D900000) == 0x01000000 ) {
|
nkeynes@736 | 252 | /* Instructions that aren't actual data processing */
|
nkeynes@736 | 253 | switch( ir & 0x0FF000F0 ) {
|
nkeynes@736 | 254 | case 0x01200010: /* BXcc */
|
nkeynes@736 | 255 | snprintf(buf, len, "BX%s R%d", cond, RM(ir));
|
nkeynes@736 | 256 | break;
|
nkeynes@736 | 257 | case 0x01000000: /* MRS Rd, CPSR */
|
nkeynes@736 | 258 | snprintf(buf, len, "MRS%s R%d, CPSR", cond, RD(ir));
|
nkeynes@736 | 259 | break;
|
nkeynes@736 | 260 | case 0x01400000: /* MRS Rd, SPSR */
|
nkeynes@736 | 261 | snprintf(buf, len, "MRS%s R%d, SPSR", cond, RD(ir));
|
nkeynes@736 | 262 | break;
|
nkeynes@736 | 263 | case 0x01200000: /* MSR CPSR, Rm */
|
nkeynes@736 | 264 | snprintf(buf, len, "MSR%s CPSR_%s, R%d", cond, FSXC(ir), RM(ir));
|
nkeynes@736 | 265 | break;
|
nkeynes@736 | 266 | case 0x01600000: /* MSR SPSR, Rm */
|
nkeynes@736 | 267 | snprintf(buf, len, "MSR%s SPSR_%s, R%d", cond, FSXC(ir), RM(ir));
|
nkeynes@736 | 268 | break;
|
nkeynes@736 | 269 | case 0x03200000: /* MSR CPSR, imm */
|
nkeynes@736 | 270 | snprintf(buf, len, "MSR%s CPSR_%s, #%08X", cond, FSXC(ir), ROTIMM12(ir));
|
nkeynes@736 | 271 | break;
|
nkeynes@736 | 272 | case 0x03600000: /* MSR SPSR, imm */
|
nkeynes@736 | 273 | snprintf(buf, len, "MSR%s SPSR_%s, #%08X", cond, FSXC(ir), ROTIMM12(ir));
|
nkeynes@736 | 274 | break;
|
nkeynes@736 | 275 | default:
|
nkeynes@736 | 276 | UNIMP();
|
nkeynes@736 | 277 | }
|
nkeynes@736 | 278 | } else if( (ir & 0x0E000090) == 0x00000090 ) {
|
nkeynes@736 | 279 | /* Neither are these */
|
nkeynes@736 | 280 | switch( (ir>>5)&0x03 ) {
|
nkeynes@736 | 281 | case 0:
|
nkeynes@736 | 282 | /* Arithmetic extension area */
|
nkeynes@736 | 283 | switch(OPCODE(ir)) {
|
nkeynes@736 | 284 | case 0: /* MUL */
|
nkeynes@736 | 285 | snprintf(buf,len, "MUL%s R%d, R%d, R%d", cond, RN(ir), RM(ir), RS(ir) );
|
nkeynes@736 | 286 | break;
|
nkeynes@736 | 287 | case 1: /* MULS */
|
nkeynes@736 | 288 | break;
|
nkeynes@736 | 289 | case 2: /* MLA */
|
nkeynes@736 | 290 | snprintf(buf,len, "MLA%s R%d, R%d, R%d, R%d", cond, RN(ir), RM(ir), RS(ir), RD(ir) );
|
nkeynes@736 | 291 | break;
|
nkeynes@736 | 292 | case 3: /* MLAS */
|
nkeynes@736 | 293 | break;
|
nkeynes@736 | 294 | case 8: /* UMULL */
|
nkeynes@736 | 295 | snprintf(buf,len, "UMULL%s R%d, R%d, R%d, R%d", cond, RD(ir), RN(ir), RM(ir), RS(ir) );
|
nkeynes@736 | 296 | break;
|
nkeynes@736 | 297 | case 9: /* UMULLS */
|
nkeynes@736 | 298 | break;
|
nkeynes@736 | 299 | case 10: /* UMLAL */
|
nkeynes@736 | 300 | snprintf(buf,len, "UMLAL%s R%d, R%d, R%d, R%d", cond, RD(ir), RN(ir), RM(ir), RS(ir) );
|
nkeynes@736 | 301 | break;
|
nkeynes@736 | 302 | case 11: /* UMLALS */
|
nkeynes@736 | 303 | break;
|
nkeynes@736 | 304 | case 12: /* SMULL */
|
nkeynes@736 | 305 | snprintf(buf,len, "SMULL%s R%d, R%d, R%d, R%d", cond, RD(ir), RN(ir), RM(ir), RS(ir) );
|
nkeynes@736 | 306 | break;
|
nkeynes@736 | 307 | case 13: /* SMULLS */
|
nkeynes@736 | 308 | break;
|
nkeynes@736 | 309 | case 14: /* SMLAL */
|
nkeynes@736 | 310 | snprintf(buf,len, "SMLAL%s R%d, R%d, R%d, R%d", cond, RD(ir), RN(ir), RM(ir), RS(ir) );
|
nkeynes@736 | 311 | break;
|
nkeynes@736 | 312 | case 15: /* SMLALS */
|
nkeynes@4 | 313 |
|
nkeynes@736 | 314 | break;
|
nkeynes@736 | 315 | case 16: /* SWP */
|
nkeynes@736 | 316 | snprintf(buf,len, "SWP%s R%d, R%d, [R%d]", cond, RD(ir), RN(ir), RM(ir) );
|
nkeynes@736 | 317 | break;
|
nkeynes@736 | 318 | case 20: /* SWPB */
|
nkeynes@736 | 319 | snprintf(buf,len, "SWPB%s R%d, R%d, [R%d]", cond, RD(ir), RN(ir), RM(ir) );
|
nkeynes@736 | 320 | break;
|
nkeynes@736 | 321 | default:
|
nkeynes@736 | 322 | UNIMP(ir);
|
nkeynes@736 | 323 | }
|
nkeynes@736 | 324 | break;
|
nkeynes@736 | 325 | case 1:
|
nkeynes@811 | 326 | arm_disasm_address3_operand( ir, operand, sizeof(operand), pc );
|
nkeynes@811 | 327 | if( LFLAG(ir) ) { /* LDRH */
|
nkeynes@811 | 328 | snprintf(buf, len, "LDR%sH R%d, %s", cond, RD(ir), operand );
|
nkeynes@811 | 329 | } else { /* STRH */
|
nkeynes@811 | 330 | snprintf(buf, len, "STR%sH R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 331 | }
|
nkeynes@736 | 332 | break;
|
nkeynes@736 | 333 | case 2:
|
nkeynes@811 | 334 | if( LFLAG(ir) ) { /* LDRSB */
|
nkeynes@811 | 335 | arm_disasm_address3_operand( ir, operand, sizeof(operand), pc );
|
nkeynes@811 | 336 | snprintf(buf, len, "LDR%sSB R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 337 | } else {
|
nkeynes@811 | 338 | UNIMP(ir);
|
nkeynes@736 | 339 | }
|
nkeynes@736 | 340 | break;
|
nkeynes@736 | 341 | case 3:
|
nkeynes@811 | 342 | if( LFLAG(ir) ) { /* LDRSH */
|
nkeynes@811 | 343 | arm_disasm_address3_operand( ir, operand, sizeof(operand), pc );
|
nkeynes@811 | 344 | snprintf(buf, len, "LDR%sSH R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 345 | } else {
|
nkeynes@811 | 346 | UNIMP(ir);
|
nkeynes@736 | 347 | }
|
nkeynes@736 | 348 | break;
|
nkeynes@736 | 349 | }
|
nkeynes@736 | 350 | } else {
|
nkeynes@736 | 351 | /* Data processing */
|
nkeynes@4 | 352 |
|
nkeynes@736 | 353 | switch(OPCODE(ir)) {
|
nkeynes@736 | 354 | case 0: /* AND Rd, Rn, operand */
|
nkeynes@736 | 355 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 356 | snprintf(buf, len, "AND%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 357 | break;
|
nkeynes@736 | 358 | case 1: /* ANDS Rd, Rn, operand */
|
nkeynes@736 | 359 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 360 | snprintf(buf, len, "ANDS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 361 | break;
|
nkeynes@736 | 362 | case 2: /* EOR Rd, Rn, operand */
|
nkeynes@736 | 363 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 364 | snprintf(buf, len, "EOR%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 365 | break;
|
nkeynes@736 | 366 | case 3: /* EORS Rd, Rn, operand */
|
nkeynes@736 | 367 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 368 | snprintf(buf, len, "EORS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 369 | break;
|
nkeynes@736 | 370 | case 4: /* SUB Rd, Rn, operand */
|
nkeynes@736 | 371 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 372 | snprintf(buf, len, "SUB%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 373 | break;
|
nkeynes@736 | 374 | case 5: /* SUBS Rd, Rn, operand */
|
nkeynes@736 | 375 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 376 | snprintf(buf, len, "SUBS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 377 | break;
|
nkeynes@736 | 378 | case 6: /* RSB Rd, Rn, operand */
|
nkeynes@736 | 379 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 380 | snprintf(buf, len, "RSB%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 381 | break;
|
nkeynes@736 | 382 | case 7: /* RSBS Rd, Rn, operand */
|
nkeynes@736 | 383 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 384 | snprintf(buf, len, "RSBS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 385 | break;
|
nkeynes@736 | 386 | case 8: /* ADD Rd, Rn, operand */
|
nkeynes@736 | 387 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 388 | snprintf(buf, len, "ADD%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 389 | break;
|
nkeynes@736 | 390 | case 9: /* ADDS Rd, Rn, operand */
|
nkeynes@736 | 391 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 392 | snprintf(buf, len, "ADDS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 393 | break;
|
nkeynes@736 | 394 | case 10: /* ADC Rd, Rn, operand */
|
nkeynes@736 | 395 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 396 | snprintf(buf, len, "ADC%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 397 | break;
|
nkeynes@736 | 398 | case 11: /* ADCS Rd, Rn, operand */
|
nkeynes@736 | 399 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 400 | snprintf(buf, len, "ADCS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 401 | break;
|
nkeynes@736 | 402 | case 12: /* SBC Rd, Rn, operand */
|
nkeynes@736 | 403 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 404 | snprintf(buf, len, "SBC%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 405 | break;
|
nkeynes@736 | 406 | case 13: /* SBCS Rd, Rn, operand */
|
nkeynes@736 | 407 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 408 | snprintf(buf, len, "SBCS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 409 | break;
|
nkeynes@736 | 410 | case 14: /* RSC Rd, Rn, operand */
|
nkeynes@736 | 411 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 412 | snprintf(buf, len, "RSC%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 413 | break;
|
nkeynes@736 | 414 | case 15: /* RSCS Rd, Rn, operand */
|
nkeynes@736 | 415 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 416 | snprintf(buf, len, "RSCS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 417 | break;
|
nkeynes@736 | 418 | case 17: /* TST Rd, Rn, operand */
|
nkeynes@736 | 419 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 420 | snprintf(buf, len, "TST%s R%d, %s", cond, RN(ir), operand);
|
nkeynes@736 | 421 | break;
|
nkeynes@736 | 422 | case 19: /* TEQ Rd, Rn, operand */
|
nkeynes@736 | 423 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 424 | snprintf(buf, len, "TEQ%s R%d, %s", cond, RN(ir), operand);
|
nkeynes@736 | 425 | break;
|
nkeynes@736 | 426 | case 21: /* CMP Rd, Rn, operand */
|
nkeynes@736 | 427 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 428 | snprintf(buf, len, "CMP%s R%d, %s", cond, RN(ir), operand);
|
nkeynes@736 | 429 | break;
|
nkeynes@736 | 430 | case 23: /* CMN Rd, Rn, operand */
|
nkeynes@736 | 431 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 432 | snprintf(buf, len, "CMN%s R%d, %s", cond, RN(ir), operand);
|
nkeynes@736 | 433 | break;
|
nkeynes@736 | 434 | case 24: /* ORR Rd, Rn, operand */
|
nkeynes@736 | 435 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 436 | snprintf(buf, len, "ORR%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 437 | break;
|
nkeynes@736 | 438 | case 25: /* ORRS Rd, Rn, operand */
|
nkeynes@736 | 439 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 440 | snprintf(buf, len, "ORRS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 441 | break;
|
nkeynes@736 | 442 | case 26: /* MOV Rd, operand */
|
nkeynes@736 | 443 | if( ir == 0xE1A00000 ) {
|
nkeynes@736 | 444 | /* Not technically a different instruction,
|
nkeynes@736 | 445 | * but this one is commonly used as a NOP,
|
nkeynes@736 | 446 | * so...
|
nkeynes@736 | 447 | */
|
nkeynes@736 | 448 | snprintf(buf, len, "NOP");
|
nkeynes@736 | 449 | } else {
|
nkeynes@736 | 450 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 451 | snprintf(buf, len, "MOV%s R%d, %s", cond, RD(ir), operand);
|
nkeynes@736 | 452 | }
|
nkeynes@736 | 453 | break;
|
nkeynes@736 | 454 | case 27: /* MOVS Rd, operand */
|
nkeynes@736 | 455 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 456 | snprintf(buf, len, "MOVS%s R%d, %s", cond, RD(ir), operand);
|
nkeynes@736 | 457 | break;
|
nkeynes@736 | 458 | case 28: /* BIC Rd, Rn, operand */
|
nkeynes@736 | 459 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 460 | snprintf(buf, len, "BIC%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 461 | break;
|
nkeynes@736 | 462 | case 29: /* BICS Rd, Rn, operand */
|
nkeynes@736 | 463 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 464 | snprintf(buf, len, "BICS%s R%d, R%d, %s", cond, RD(ir), RN(ir), operand);
|
nkeynes@736 | 465 | break;
|
nkeynes@736 | 466 | case 30: /* MVN Rd, Rn, operand */
|
nkeynes@736 | 467 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 468 | snprintf(buf, len, "MVN%s R%d, %s", cond, RD(ir), operand);
|
nkeynes@736 | 469 | break;
|
nkeynes@736 | 470 | case 31: /* MVNS Rd, Rn, operand */
|
nkeynes@736 | 471 | arm_disasm_shift_operand(ir, operand, sizeof(operand));
|
nkeynes@736 | 472 | snprintf(buf, len, "MVNS%s R%d, %s", cond, RD(ir), operand);
|
nkeynes@736 | 473 | break;
|
nkeynes@736 | 474 | default:
|
nkeynes@736 | 475 | UNIMP(ir);
|
nkeynes@736 | 476 | }
|
nkeynes@736 | 477 | }
|
nkeynes@736 | 478 | break;
|
nkeynes@736 | 479 | case 1: /* Load/store */
|
nkeynes@736 | 480 | arm_disasm_address_operand( ir, operand, sizeof(operand), pc );
|
nkeynes@736 | 481 | switch( (ir>>20)&0x17 ) {
|
nkeynes@736 | 482 | case 0:
|
nkeynes@736 | 483 | case 16:
|
nkeynes@736 | 484 | case 18:
|
nkeynes@736 | 485 | snprintf(buf, len, "STR%s R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 486 | break;
|
nkeynes@736 | 487 | case 1:
|
nkeynes@736 | 488 | case 17:
|
nkeynes@736 | 489 | case 19:
|
nkeynes@736 | 490 | snprintf(buf, len, "LDR%s R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 491 | break;
|
nkeynes@736 | 492 | case 2:
|
nkeynes@736 | 493 | snprintf(buf, len, "STRT%s R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 494 | break;
|
nkeynes@736 | 495 | case 3:
|
nkeynes@736 | 496 | snprintf(buf, len, "LDRT%s R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 497 | break;
|
nkeynes@736 | 498 | case 4:
|
nkeynes@736 | 499 | case 20:
|
nkeynes@736 | 500 | case 22:
|
nkeynes@736 | 501 | snprintf(buf, len, "STRB%s R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 502 | break;
|
nkeynes@736 | 503 | case 5:
|
nkeynes@736 | 504 | case 21:
|
nkeynes@736 | 505 | case 23:
|
nkeynes@736 | 506 | snprintf(buf, len, "LDRB%s R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 507 | break;
|
nkeynes@736 | 508 | case 6:
|
nkeynes@736 | 509 | snprintf(buf, len, "STRBT%s R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 510 | break;
|
nkeynes@736 | 511 | case 7:
|
nkeynes@736 | 512 | snprintf(buf, len, "LDRBT%s R%d, %s", cond, RD(ir), operand );
|
nkeynes@736 | 513 | break;
|
nkeynes@736 | 514 | }
|
nkeynes@736 | 515 | break;
|
nkeynes@736 | 516 | case 2:
|
nkeynes@736 | 517 | if( (ir & 0x02000000) == 0x02000000 ) {
|
nkeynes@736 | 518 | int32_t offset = SIGNEXT24(ir&0x00FFFFFF) << 2;
|
nkeynes@736 | 519 | if( (ir & 0x01000000) == 0x01000000 ) {
|
nkeynes@736 | 520 | snprintf( buf, len, "BL%s $%08Xh", cond, pc + offset + 8 );
|
nkeynes@736 | 521 | } else {
|
nkeynes@736 | 522 | snprintf( buf, len, "B%s $%08Xh", cond, pc + offset + 8 );
|
nkeynes@736 | 523 | }
|
nkeynes@736 | 524 | } else {
|
nkeynes@736 | 525 | /* Load/store multiple */
|
nkeynes@736 | 526 | j = snprintf( buf, len, LFLAG(ir) ? "LDM%s%s R%d%c,":"STM%s%s R%d%c,",
|
nkeynes@736 | 527 | ldmModes[(ir>>23)&0x03], cond, RN(ir), WFLAG(ir)?'!':' ' );
|
nkeynes@736 | 528 | buf += j;
|
nkeynes@736 | 529 | len -= j;
|
nkeynes@736 | 530 | for( i = 0; i<16 && len > 2; i++ ) {
|
nkeynes@736 | 531 | if( ir & (1<<i) ) {
|
nkeynes@736 | 532 | j = snprintf( buf, len, "R%d", i );
|
nkeynes@736 | 533 | buf+=j;
|
nkeynes@736 | 534 | len-=j;
|
nkeynes@736 | 535 | }
|
nkeynes@736 | 536 | }
|
nkeynes@736 | 537 | if( BFLAG(ir) && len > 0 ) {
|
nkeynes@736 | 538 | buf[0] = '^';
|
nkeynes@736 | 539 | buf[1] = '\0';
|
nkeynes@736 | 540 | }
|
nkeynes@736 | 541 | }
|
nkeynes@736 | 542 | break;
|
nkeynes@736 | 543 | case 3: /* Copro */
|
nkeynes@736 | 544 | if( (ir & 0x0F000000) == 0x0F000000 ) {
|
nkeynes@736 | 545 | snprintf( buf, len, "SWI%s #%08Xh", cond, SIGNEXT24(ir) );
|
nkeynes@736 | 546 | } else {
|
nkeynes@736 | 547 | UNIMP(ir);
|
nkeynes@736 | 548 | }
|
nkeynes@736 | 549 | break;
|
nkeynes@736 | 550 | }
|
nkeynes@736 | 551 |
|
nkeynes@736 | 552 |
|
nkeynes@736 | 553 |
|
nkeynes@736 | 554 | return pc+4;
|
nkeynes@4 | 555 | }
|
nkeynes@11 | 556 |
|
nkeynes@11 | 557 |
|
nkeynes@11 | 558 | uint32_t armt_disasm_instruction( uint32_t pc, char *buf, int len, char *opcode )
|
nkeynes@11 | 559 | {
|
nkeynes@11 | 560 | uint32_t ir = arm_read_word(pc);
|
nkeynes@11 | 561 | sprintf( opcode, "%02X %02X", ir&0xFF, (ir>>8) );
|
nkeynes@11 | 562 | UNIMP(ir);
|
nkeynes@11 | 563 | return pc+2;
|
nkeynes@11 | 564 | }
|