nkeynes@4 | 1 | /*
|
nkeynes@4 | 2 | * armdasm.c 21 Aug 2004 - ARM7tdmi (ARMv4) disassembler
|
nkeynes@4 | 3 | *
|
nkeynes@4 | 4 | * Copyright (c) 2004 Nathan Keynes. Distribution and modification permitted
|
nkeynes@4 | 5 | * under the terms of the GNU General Public License version 2 or later.
|
nkeynes@4 | 6 | */
|
nkeynes@4 | 7 |
|
nkeynes@4 | 8 | #include "armcore.h"
|
nkeynes@4 | 9 |
|
nkeynes@4 | 10 | #define COND(ir) (ir>>28)
|
nkeynes@4 | 11 | #define OPCODE(ir) ((ir>>20)&0x1F)
|
nkeynes@4 | 12 | #define GRP(ir) ((ir>>26)&0x03)
|
nkeynes@4 | 13 | #define IFLAG(ir) (ir&0x02000000)
|
nkeynes@4 | 14 | #define SFLAG(ir) (ir&0x00100000)
|
nkeynes@4 | 15 | #define PFLAG(ir) (ir&0x01000000)
|
nkeynes@4 | 16 | #define UFLAG(ir) (ir&0x00800000)
|
nkeynes@4 | 17 | #define BFLAG(ir) (ir&0x00400000)
|
nkeynes@4 | 18 | #define WFLAG(ir) (IR&0x00200000)
|
nkeynes@4 | 19 | #define LFLAG(ir) SFLAG(ir)
|
nkeynes@4 | 20 | #define RN(ir) ((ir>>16)&0x0F)
|
nkeynes@4 | 21 | #define RD(ir) ((ir>>12)&0x0F)
|
nkeynes@4 | 22 | #define RS(ir) ((ir>>8)&0x0F)
|
nkeynes@4 | 23 | #define RM(ir) (ir&0x0F)
|
nkeynes@4 | 24 |
|
nkeynes@4 | 25 | #define IMM8(ir) (ir&0xFF)
|
nkeynes@4 | 26 | #define IMM12(ir) (ir&0xFFF)
|
nkeynes@4 | 27 | #define SHIFTIMM(ir) ((ir>>7)0x1F)
|
nkeynes@4 | 28 | #define IMMROT(ir) ((ir>>7)&1E)
|
nkeynes@4 | 29 | #define SHIFT(ir) ((ir>>4)&0x07)
|
nkeynes@4 | 30 | #define DISP24(ir) ((ir&0x00FFFFFF))
|
nkeynes@4 | 31 | #define FSXC(ir) msrFieldMask[RN(ir)]
|
nkeynes@4 | 32 | #define ROTIMM12(ir) ROTATE_RIGHT_LONG(IMM8(ir),IMMROT(ir))
|
nkeynes@4 | 33 |
|
nkeynes@4 | 34 | char *conditionNames[] = { "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
|
nkeynes@4 | 35 | "HI", "LS", "GE", "LT", "GT", "LE", " " /*AL*/, "NV" };
|
nkeynes@4 | 36 |
|
nkeynes@4 | 37 | /* fsxc */
|
nkeynes@4 | 38 | char *msrFieldMask[] = { "", "c", "x", "xc", "s", "sc", "sx", "sxc",
|
nkeynes@4 | 39 | "f", "fc", "fx", "fxc", "fs", "fsc", "fsx", "fsxc" };
|
nkeynes@4 | 40 |
|
nkeynes@4 | 41 | #define UNIMP(ir) snprintf( buf, len, "??? " )
|
nkeynes@4 | 42 |
|
nkeynes@4 | 43 | int arm_disasm_instruction( int pc, char *buf, int len )
|
nkeynes@4 | 44 | {
|
nkeynes@4 | 45 | uint32_t ir = arm_mem_read_long(pc);
|
nkeynes@4 | 46 |
|
nkeynes@4 | 47 | if( COND(ir) == 0x0F ) {
|
nkeynes@4 | 48 | UNIMP(ir);
|
nkeynes@4 | 49 | return pc+4;
|
nkeynes@4 | 50 | }
|
nkeynes@4 | 51 | char *cond = conditionNames[COND(ir)];
|
nkeynes@4 | 52 |
|
nkeynes@4 | 53 | switch( GRP(ir) ) {
|
nkeynes@4 | 54 | case 0:
|
nkeynes@4 | 55 | if( (ir & 0x0D900000) == 0x01000000 ) {
|
nkeynes@4 | 56 | /* Instructions that aren't actual data processing */
|
nkeynes@4 | 57 | switch( ir & 0x0FF000F0 ) {
|
nkeynes@4 | 58 | case 0x01200010: /* BXcc */
|
nkeynes@4 | 59 | snprintf(buf, len, "BX%s R%d", cond, RM(ir));
|
nkeynes@4 | 60 | break;
|
nkeynes@4 | 61 | case 0x01000000: /* MRS Rd, CPSR */
|
nkeynes@4 | 62 | snprintf(buf, len, "MRS%s R%d, CPSR", cond, RD(ir));
|
nkeynes@4 | 63 | break;
|
nkeynes@4 | 64 | case 0x01400000: /* MRS Rd, SPSR */
|
nkeynes@4 | 65 | snprintf(buf, len, "MRS%s R%d, SPSR", cond, RD(ir));
|
nkeynes@4 | 66 | break;
|
nkeynes@4 | 67 | case 0x01200000: /* MSR CPSR, Rm */
|
nkeynes@4 | 68 | snprintf(buf, len, "MSR%s CPSR_%s, R%d", cond, FSXC(ir), RM(ir));
|
nkeynes@4 | 69 | break;
|
nkeynes@4 | 70 | case 0x01600000: /* MSR SPSR, Rm */
|
nkeynes@4 | 71 | snprintf(buf, len, "MSR%s SPSR_%s, R%d", cond, FSXC(ir), RM(ir));
|
nkeynes@4 | 72 | break;
|
nkeynes@4 | 73 | case 0x03200000: /* MSR CPSR, imm */
|
nkeynes@4 | 74 | snprintf(buf, len, "MSR%s CPSR_%s, #%08X", cond, FSXC(ir), ROTIMM12(ir));
|
nkeynes@4 | 75 | break;
|
nkeynes@4 | 76 | case 0x03600000: /* MSR SPSR, imm */
|
nkeynes@4 | 77 | snprintf(buf, len, "MSR%s SPSR_%s, #%08X", cond, FSXC(ir), ROTIMM12(ir));
|
nkeynes@4 | 78 | break;
|
nkeynes@4 | 79 | default:
|
nkeynes@4 | 80 | UNIMP();
|
nkeynes@4 | 81 | }
|
nkeynes@4 | 82 | } else if( (ir & 0x0E000090) == 0x00000090 ) {
|
nkeynes@4 | 83 | /* Neither are these */
|
nkeynes@4 | 84 | switch( (ir>>5)&0x03 ) {
|
nkeynes@4 | 85 | case 0:
|
nkeynes@4 | 86 | /* Arithmetic extension area */
|
nkeynes@4 | 87 | switch(OPCODE(ir)) {
|
nkeynes@4 | 88 | case 0: /* MUL */
|
nkeynes@4 | 89 | snprintf(buf,len, "MUL%s R%d, R%d, R%d", cond, RN(ir), RM(ir), RS(ir) );
|
nkeynes@4 | 90 | break;
|
nkeynes@4 | 91 | case 1: /* MULS */
|
nkeynes@4 | 92 | break;
|
nkeynes@4 | 93 | case 2: /* MLA */
|
nkeynes@4 | 94 | snprintf(buf,len, "MLA%s R%d, R%d, R%d, R%d", cond, RN(ir), RM(ir), RS(ir), RD(ir) );
|
nkeynes@4 | 95 | break;
|
nkeynes@4 | 96 | case 3: /* MLAS */
|
nkeynes@4 | 97 | break;
|
nkeynes@4 | 98 | case 8: /* UMULL */
|
nkeynes@4 | 99 | snprintf(buf,len, "UMULL%s R%d, R%d, R%d, R%d", cond, RD(ir), RN(ir), RM(ir), RS(ir) );
|
nkeynes@4 | 100 | break;
|
nkeynes@4 | 101 | case 9: /* UMULLS */
|
nkeynes@4 | 102 | break;
|
nkeynes@4 | 103 | case 10: /* UMLAL */
|
nkeynes@4 | 104 | snprintf(buf,len, "UMLAL%s R%d, R%d, R%d, R%d", cond, RD(ir), RN(ir), RM(ir), RS(ir) );
|
nkeynes@4 | 105 | break;
|
nkeynes@4 | 106 | case 11: /* UMLALS */
|
nkeynes@4 | 107 | break;
|
nkeynes@4 | 108 | case 12: /* SMULL */
|
nkeynes@4 | 109 | snprintf(buf,len, "SMULL%s R%d, R%d, R%d, R%d", cond, RD(ir), RN(ir), RM(ir), RS(ir) );
|
nkeynes@4 | 110 | break;
|
nkeynes@4 | 111 | case 13: /* SMULLS */
|
nkeynes@4 | 112 | break;
|
nkeynes@4 | 113 | case 14: /* SMLAL */
|
nkeynes@4 | 114 | snprintf(buf,len, "SMLAL%s R%d, R%d, R%d, R%d", cond, RD(ir), RN(ir), RM(ir), RS(ir) );
|
nkeynes@4 | 115 | break;
|
nkeynes@4 | 116 | case 15: /* SMLALS */
|
nkeynes@4 | 117 |
|
nkeynes@4 | 118 | break;
|
nkeynes@4 | 119 | case 16: /* SWP */
|
nkeynes@4 | 120 | snprintf(buf,len, "SWP%s R%d, R%d, [R%d]", cond, RD(ir), RN(ir), RM(ir) );
|
nkeynes@4 | 121 | break;
|
nkeynes@4 | 122 | case 20: /* SWPB */
|
nkeynes@4 | 123 | snprintf(buf,len, "SWPB%s R%d, R%d, [R%d]", cond, RD(ir), RN(ir), RM(ir) );
|
nkeynes@4 | 124 | break;
|
nkeynes@4 | 125 | default:
|
nkeynes@4 | 126 | UNIMP(ir);
|
nkeynes@4 | 127 | }
|
nkeynes@4 | 128 | break;
|
nkeynes@4 | 129 | case 1:
|
nkeynes@4 | 130 | if( LFLAG(ir) ) {
|
nkeynes@4 | 131 | /* LDRH */
|
nkeynes@4 | 132 | } else {
|
nkeynes@4 | 133 | /* STRH */
|
nkeynes@4 | 134 | }
|
nkeynes@4 | 135 | break;
|
nkeynes@4 | 136 | case 2:
|
nkeynes@4 | 137 | if( LFLAG(ir) ) {
|
nkeynes@4 | 138 | /* LDRSB */
|
nkeynes@4 | 139 | } else {
|
nkeynes@4 | 140 | UNIMP(ir);
|
nkeynes@4 | 141 | }
|
nkeynes@4 | 142 | break;
|
nkeynes@4 | 143 | case 3:
|
nkeynes@4 | 144 | if( LFLAG(ir) ) {
|
nkeynes@4 | 145 | /* LDRSH */
|
nkeynes@4 | 146 | } else {
|
nkeynes@4 | 147 | UNIMP(ir);
|
nkeynes@4 | 148 | }
|
nkeynes@4 | 149 | break;
|
nkeynes@4 | 150 | }
|
nkeynes@4 | 151 | } else {
|
nkeynes@4 | 152 | /* Data processing */
|
nkeynes@4 | 153 |
|
nkeynes@4 | 154 | switch(OPCODE(ir)) {
|
nkeynes@4 | 155 | case 0: /* AND Rd, Rn, operand */
|
nkeynes@4 | 156 | RD(ir) = RN(ir) & arm_get_shift_operand(ir);
|
nkeynes@4 | 157 | break;
|
nkeynes@4 | 158 | case 1: /* ANDS Rd, Rn, operand */
|
nkeynes@4 | 159 | operand = arm_get_shift_operand_s(ir) & RN(ir);
|
nkeynes@4 | 160 | RD(ir) = operand;
|
nkeynes@4 | 161 | if( RDn(ir) == 15 ) {
|
nkeynes@4 | 162 | arm_restore_cpsr();
|
nkeynes@4 | 163 | } else {
|
nkeynes@4 | 164 | armr.n = operand>>31;
|
nkeynes@4 | 165 | armr.z = (operand == 0);
|
nkeynes@4 | 166 | armr.c = armr.shift_c;
|
nkeynes@4 | 167 | }
|
nkeynes@4 | 168 | break;
|
nkeynes@4 | 169 | case 2: /* EOR Rd, Rn, operand */
|
nkeynes@4 | 170 | RD(ir) = RN(ir) ^ arm_get_shift_operand(ir);
|
nkeynes@4 | 171 | break;
|
nkeynes@4 | 172 | case 3: /* EORS Rd, Rn, operand */
|
nkeynes@4 | 173 | operand = arm_get_shift_operand_s(ir) ^ RN(ir);
|
nkeynes@4 | 174 | RD(ir) = operand;
|
nkeynes@4 | 175 | if( RDn(ir) == 15 ) {
|
nkeynes@4 | 176 | arm_restore_cpsr();
|
nkeynes@4 | 177 | } else {
|
nkeynes@4 | 178 | armr.n = operand>>31;
|
nkeynes@4 | 179 | armr.z = (operand == 0);
|
nkeynes@4 | 180 | armr.c = armr.shift_c;
|
nkeynes@4 | 181 | }
|
nkeynes@4 | 182 | break;
|
nkeynes@4 | 183 | case 4: /* SUB Rd, Rn, operand */
|
nkeynes@4 | 184 | RD(ir) = RN(ir) - arm_get_shift_operand(ir);
|
nkeynes@4 | 185 | break;
|
nkeynes@4 | 186 | case 5: /* SUBS Rd, Rn, operand */
|
nkeynes@4 | 187 | operand = RN(ir);
|
nkeynes@4 | 188 | operand2 = arm_get_shift_operand(ir)
|
nkeynes@4 | 189 | tmp = operand - operand2;
|
nkeynes@4 | 190 | RD(ir) = tmp;
|
nkeynes@4 | 191 | if( RDn(ir) == 15 ) {
|
nkeynes@4 | 192 | arm_restore_cpsr();
|
nkeynes@4 | 193 | } else {
|
nkeynes@4 | 194 | armr.n = tmp>>31;
|
nkeynes@4 | 195 | armr.z = (tmp == 0);
|
nkeynes@4 | 196 | armr.c = IS_NOTBORROW(tmp,operand,operand2);
|
nkeynes@4 | 197 | armr.v = IS_SUBOVERFLOW(tmp,operand,operand2);
|
nkeynes@4 | 198 | }
|
nkeynes@4 | 199 | break;
|
nkeynes@4 | 200 | case 6: /* RSB Rd, operand, Rn */
|
nkeynes@4 | 201 | RD(ir) = arm_get_shift_operand(ir) - RN(ir);
|
nkeynes@4 | 202 | break;
|
nkeynes@4 | 203 | case 7: /* RSBS Rd, operand, Rn */
|
nkeynes@4 | 204 | operand = arm_get_shift_operand(ir);
|
nkeynes@4 | 205 | operand2 = RN(ir);
|
nkeynes@4 | 206 | tmp = operand - operand2;
|
nkeynes@4 | 207 | RD(ir) = tmp;
|
nkeynes@4 | 208 | if( RDn(ir) == 15 ) {
|
nkeynes@4 | 209 | arm_restore_cpsr();
|
nkeynes@4 | 210 | } else {
|
nkeynes@4 | 211 | armr.n = tmp>>31;
|
nkeynes@4 | 212 | armr.z = (tmp == 0);
|
nkeynes@4 | 213 | armr.c = IS_NOTBORROW(tmp,operand,operand2);
|
nkeynes@4 | 214 | armr.v = IS_SUBOVERFLOW(tmp,operand,operand2);
|
nkeynes@4 | 215 | }
|
nkeynes@4 | 216 | break;
|
nkeynes@4 | 217 | case 8: /* ADD Rd, Rn, operand */
|
nkeynes@4 | 218 | RD(ir) = RN(ir) + arm_get_shift_operand(ir);
|
nkeynes@4 | 219 | break;
|
nkeynes@4 | 220 | case 9: /* ADDS Rd, Rn, operand */
|
nkeynes@4 | 221 | operand = arm_get_shift_operand(ir);
|
nkeynes@4 | 222 | operand2 = RN(ir);
|
nkeynes@4 | 223 | tmp = operand + operand2
|
nkeynes@4 | 224 | RD(ir) = tmp;
|
nkeynes@4 | 225 | if( RDn(ir) == 15 ) {
|
nkeynes@4 | 226 | arm_restore_cpsr();
|
nkeynes@4 | 227 | } else {
|
nkeynes@4 | 228 | armr.n = tmp>>31;
|
nkeynes@4 | 229 | armr.z = (tmp == 0);
|
nkeynes@4 | 230 | armr.c = IS_CARRY(tmp,operand,operand2);
|
nkeynes@4 | 231 | armr.v = IS_ADDOVERFLOW(tmp,operand,operand2);
|
nkeynes@4 | 232 | }
|
nkeynes@4 | 233 | break;
|
nkeynes@4 | 234 | case 10: /* ADC */
|
nkeynes@4 | 235 | case 11: /* ADCS */
|
nkeynes@4 | 236 | case 12: /* SBC */
|
nkeynes@4 | 237 | case 13: /* SBCS */
|
nkeynes@4 | 238 | case 14: /* RSC */
|
nkeynes@4 | 239 | case 15: /* RSCS */
|
nkeynes@4 | 240 | break;
|
nkeynes@4 | 241 | case 17: /* TST Rn, operand */
|
nkeynes@4 | 242 | operand = arm_get_shift_operand_s(ir) & RN(ir);
|
nkeynes@4 | 243 | armr.n = operand>>31;
|
nkeynes@4 | 244 | armr.z = (operand == 0);
|
nkeynes@4 | 245 | armr.c = armr.shift_c;
|
nkeynes@4 | 246 | break;
|
nkeynes@4 | 247 | case 19: /* TEQ Rn, operand */
|
nkeynes@4 | 248 | operand = arm_get_shift_operand_s(ir) ^ RN(ir);
|
nkeynes@4 | 249 | armr.n = operand>>31;
|
nkeynes@4 | 250 | armr.z = (operand == 0);
|
nkeynes@4 | 251 | armr.c = armr.shift_c;
|
nkeynes@4 | 252 | break;
|
nkeynes@4 | 253 | case 21: /* CMP Rn, operand */
|
nkeynes@4 | 254 | operand = RN(ir);
|
nkeynes@4 | 255 | operand2 = arm_get_shift_operand(ir)
|
nkeynes@4 | 256 | tmp = operand - operand2;
|
nkeynes@4 | 257 | armr.n = tmp>>31;
|
nkeynes@4 | 258 | armr.z = (tmp == 0);
|
nkeynes@4 | 259 | armr.c = IS_NOTBORROW(tmp,operand,operand2);
|
nkeynes@4 | 260 | armr.v = IS_SUBOVERFLOW(tmp,operand,operand2);
|
nkeynes@4 | 261 | break;
|
nkeynes@4 | 262 | case 23: /* CMN Rn, operand */
|
nkeynes@4 | 263 | operand = RN(ir);
|
nkeynes@4 | 264 | operand2 = arm_get_shift_operand(ir)
|
nkeynes@4 | 265 | tmp = operand + operand2;
|
nkeynes@4 | 266 | armr.n = tmp>>31;
|
nkeynes@4 | 267 | armr.z = (tmp == 0);
|
nkeynes@4 | 268 | armr.c = IS_CARRY(tmp,operand,operand2);
|
nkeynes@4 | 269 | armr.v = IS_ADDOVERFLOW(tmp,operand,operand2);
|
nkeynes@4 | 270 | break;
|
nkeynes@4 | 271 | case 24: /* ORR Rd, Rn, operand */
|
nkeynes@4 | 272 | RD(ir) = RN(ir) | arm_get_shift_operand(ir);
|
nkeynes@4 | 273 | break;
|
nkeynes@4 | 274 | case 25: /* ORRS Rd, Rn, operand */
|
nkeynes@4 | 275 | operand = arm_get_shift_operand_s(ir) | RN(ir);
|
nkeynes@4 | 276 | RD(ir) = operand;
|
nkeynes@4 | 277 | if( RDn(ir) == 15 ) {
|
nkeynes@4 | 278 | arm_restore_cpsr();
|
nkeynes@4 | 279 | } else {
|
nkeynes@4 | 280 | armr.n = operand>>31;
|
nkeynes@4 | 281 | armr.z = (operand == 0);
|
nkeynes@4 | 282 | armr.c = armr.shift_c;
|
nkeynes@4 | 283 | }
|
nkeynes@4 | 284 | break;
|
nkeynes@4 | 285 | case 26: /* MOV Rd, operand */
|
nkeynes@4 | 286 | RD(ir) = arm_get_shift_operand(ir);
|
nkeynes@4 | 287 | break;
|
nkeynes@4 | 288 | case 27: /* MOVS Rd, operand */
|
nkeynes@4 | 289 | operand = arm_get_shift_operand_s(ir);
|
nkeynes@4 | 290 | RD(ir) = operand;
|
nkeynes@4 | 291 | if( RDn(ir) == 15 ) {
|
nkeynes@4 | 292 | arm_restore_cpsr();
|
nkeynes@4 | 293 | } else {
|
nkeynes@4 | 294 | armr.n = operand>>31;
|
nkeynes@4 | 295 | armr.z = (operand == 0);
|
nkeynes@4 | 296 | armr.c = armr.shift_c;
|
nkeynes@4 | 297 | }
|
nkeynes@4 | 298 | break;
|
nkeynes@4 | 299 | case 28: /* BIC Rd, Rn, operand */
|
nkeynes@4 | 300 | RD(ir) = RN(ir) & (~arm_get_shift_operand(ir));
|
nkeynes@4 | 301 | break;
|
nkeynes@4 | 302 | case 29: /* BICS Rd, Rn, operand */
|
nkeynes@4 | 303 | operand = RN(ir) & (~arm_get_shift_operand_s(ir));
|
nkeynes@4 | 304 | RD(ir) = operand;
|
nkeynes@4 | 305 | if( RDn(ir) == 15 ) {
|
nkeynes@4 | 306 | arm_restore_cpsr();
|
nkeynes@4 | 307 | } else {
|
nkeynes@4 | 308 | armr.n = operand>>31;
|
nkeynes@4 | 309 | armr.z = (operand == 0);
|
nkeynes@4 | 310 | armr.c = armr.shift_c;
|
nkeynes@4 | 311 | }
|
nkeynes@4 | 312 | break;
|
nkeynes@4 | 313 | case 30: /* MVN Rd, operand */
|
nkeynes@4 | 314 | RD(ir) = ~arm_get_shift_operand(ir);
|
nkeynes@4 | 315 | break;
|
nkeynes@4 | 316 | case 31: /* MVNS Rd, operand */
|
nkeynes@4 | 317 | operand = ~arm_get_shift_operand_s(ir);
|
nkeynes@4 | 318 | RD(ir) = operand;
|
nkeynes@4 | 319 | if( RDn(ir) == 15 ) {
|
nkeynes@4 | 320 | arm_restore_cpsr();
|
nkeynes@4 | 321 | } else {
|
nkeynes@4 | 322 | armr.n = operand>>31;
|
nkeynes@4 | 323 | armr.z = (operand == 0);
|
nkeynes@4 | 324 | armr.c = armr.shift_c;
|
nkeynes@4 | 325 | }
|
nkeynes@4 | 326 | break;
|
nkeynes@4 | 327 | default:
|
nkeynes@4 | 328 | UNIMP(ir);
|
nkeynes@4 | 329 | }
|
nkeynes@4 | 330 | }
|
nkeynes@4 | 331 | break;
|
nkeynes@4 | 332 | case 1: /* Load/store */
|
nkeynes@4 | 333 | break;
|
nkeynes@4 | 334 | case 2: /* Load/store multiple, branch*/
|
nkeynes@4 | 335 | break;
|
nkeynes@4 | 336 | case 3: /* Copro */
|
nkeynes@4 | 337 | break;
|
nkeynes@4 | 338 | }
|
nkeynes@4 | 339 |
|
nkeynes@4 | 340 |
|
nkeynes@4 | 341 |
|
nkeynes@4 | 342 | return pc+4;
|
nkeynes@4 | 343 | }
|