/* Instruction printing code for the ARM Copyright (C) 1994, 95, 96, 97, 1998 Free Software Foundation, Inc. Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) Modification by James G. Smith (jsmith@cygnus.co.uk) This file is part of libopcodes. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include // #include "dis-asm.h" #define DEFINE_TABLE #include "arm-opc.h" // #include "coff/internal.h" // #include "libcoff.h" static char* arm_conditional[] = { "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "", "nv"}; static char* arm_regnames[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc"}; static char* arm_fp_const[] = { "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0"}; static char* arm_shift[] = { "lsl", "lsr", "asr", "ror"}; static int print_insn_arm(unsigned long, long, char*); // ============================================================ // // void arm_decode_shift(long, char*) // ============================================================ // static void arm_decode_shift(long given, char* str) { char tmpStr[512]; sprintf(tmpStr, "%s", arm_regnames[given & 0xf]); str = strcat(str, tmpStr); if ((given & 0xff0) != 0) { if ((given & 0x10) == 0) { int amount = (given & 0xf80) >> 7; int shift = (given & 0x60) >> 5; if (amount == 0) { if (shift == 3) { sprintf(tmpStr, ", rrx"); str = strcat(str, tmpStr); return; } amount = 32; } sprintf(tmpStr, ", %s #%d", arm_shift[shift], amount); str = strcat(str, tmpStr); } else { sprintf(tmpStr, ", %s %s", arm_shift[(given & 0x60) >> 5], arm_regnames[(given & 0xf00) >> 8]); str = strcat(str, tmpStr); } } } // ============================================================ // // void print_insn_arm (unsigned long, long, char*) // ============================================================ // static int print_insn_arm(unsigned long pc, long given, char* str) { struct arm_opcode* insn; char tmpStr[512]; for (insn = arm_opcodes; insn->assembler; insn++) { if ((given & insn->mask) == insn->value) { char* c; for (c = insn->assembler; *c; c++) { if (*c == '%') { switch (*++c) { case '%': str = strcat(str, "%%"); break; case 'a': if (((given & 0x000f0000) == 0x000f0000) && ((given & 0x02000000) == 0)) { int offset = given & 0xfff; if ((given & 0x00800000) == 0) offset = -offset; sprintf(tmpStr, "0x%.8X", offset + pc + 8); str = strcat(str, tmpStr); } else { sprintf(tmpStr, "[%s", arm_regnames[(given >> 16) & 0xf]); str = strcat(str, tmpStr); if ((given & 0x01000000) != 0) { if ((given & 0x02000000) == 0) { int offset = given & 0xfff; if (offset) { sprintf(tmpStr, ", %s#%d", (((given & 0x00800000) == 0) ? "-" : ""), offset); str = strcat(str, tmpStr); } } else { sprintf(tmpStr, ", %s", (((given & 0x00800000) == 0) ? "-" : "")); str = strcat(str, tmpStr); arm_decode_shift(given, str); } sprintf(tmpStr, "]%s", ((given & 0x00200000) != 0) ? "!" : ""); str = strcat(str, tmpStr); } else { if ((given & 0x02000000) == 0) { int offset = given & 0xfff; if (offset) { sprintf(tmpStr, "], %s#%d", (((given & 0x00800000) == 0) ? "-" : ""), offset); str = strcat(str, tmpStr); } else str = strcat(str, "]"); } else { sprintf(tmpStr, "], %s", (((given & 0x00800000) == 0) ? "-" : "")); str = strcat(str, tmpStr); arm_decode_shift(given, str); } } } break; case 's': if ((given & 0x004f0000) == 0x004f0000) { /* PC relative with immediate offset */ int offset = ((given & 0xf00) >> 4) | (given & 0xf); if ((given & 0x00800000) == 0) offset = -offset; sprintf(tmpStr, "0x%.8X", offset + pc + 8); str = strcat(str, tmpStr); } else { sprintf(tmpStr, "[%s", arm_regnames[(given >> 16) & 0xf]); str = strcat(str, tmpStr); if ((given & 0x01000000) != 0) { /* pre-indexed */ if ((given & 0x00400000) == 0x00400000) { /* immediate */ int offset = ((given & 0xf00) >> 4) | (given & 0xf); if (offset) { sprintf(tmpStr, ", %s#%d", (((given & 0x00800000) == 0) ? "-" : ""), offset); str = strcat(str, tmpStr); } } else { /* register */ sprintf(tmpStr, ", %s%s", (((given & 0x00800000) == 0) ? "-" : ""), arm_regnames[given & 0xf]); str = strcat(str, tmpStr); } sprintf(tmpStr, "]%s", ((given & 0x00200000) != 0) ? "!" : ""); str = strcat(str, tmpStr); } else { /* post-indexed */ if ((given & 0x00400000) == 0x00400000) { /* immediate */ int offset = ((given & 0xf00) >> 4) | (given & 0xf); if (offset) { sprintf(tmpStr, "], %s#%d", (((given & 0x00800000) == 0) ? "-" : ""), offset); str = strcat(str, tmpStr); } else strcat(str, "]"); } else { /* register */ sprintf(tmpStr, "], %s%s", (((given & 0x00800000) == 0) ? "-" : ""), arm_regnames[given & 0xf]); str = strcat(str, tmpStr); } } } break; case 'b': sprintf(tmpStr, "0x%.8X", BDISP(given) * 4 + pc + 8); str = strcat(str, tmpStr); break; case 'c': sprintf(tmpStr, "%s", arm_conditional[(given >> 28) & 0xf]); str = strcat(str, tmpStr); break; case 'm': { int started = 0; int RangeEnd = -2; int RangeFirst = -2; int reg; // Register List. str = strcat(str, "{"); for (reg = 0; reg < 16; reg++) if ((given & (1 << reg)) != 0) { if (RangeEnd + 1 == reg) { // I am in a block. if (reg == 15) { // I should finish it anyway. sprintf(tmpStr, "-%s", arm_regnames[reg]); str = strcat(str, tmpStr); } else { RangeEnd++; } } else { // I am not in a block. // The block has been finished when processing the first reg out of it. if (started) str = strcat(str, ", "); started = 1; // Let's print this register & set both RangeEnd and RangeFirst. RangeEnd = RangeFirst = reg; sprintf(tmpStr, "%s", arm_regnames[reg]); str = strcat(str, tmpStr); } } else { // This register is not here. Hence, I finish the old block. if (started) { if (RangeEnd > RangeFirst) { if (RangeEnd == RangeFirst + 1) { // Two registers: I do comma. sprintf(tmpStr, ", %s", arm_regnames[RangeEnd]); } else { // More: I do dash. sprintf(tmpStr, "-%s", arm_regnames[RangeEnd]); } str = strcat(str, tmpStr); } } RangeEnd = RangeFirst = -2; } str = strcat(str, "}"); } break; case 'o': if ((given & 0x02000000) != 0) { int rotate = (given & 0xf00) >> 7; int immed = (given & 0xff); sprintf(tmpStr, "#%d", ((immed << (32 - rotate)) | (immed >> rotate)) & 0xffffffff); str = strcat(str, tmpStr); } else arm_decode_shift(given, str); break; case 'p': if ((given & 0x0000f000) == 0x0000f000) str = strcat(str, "p"); break; case 't': if ((given & 0x01200000) == 0x00200000) str = strcat(str, "t"); break; case 'h': if ((given & 0x00000020) == 0x00000020) str = strcat(str, "h"); else str = strcat(str, "b"); break; case 'A': sprintf(tmpStr, "[%s", arm_regnames[(given >> 16) & 0xf]); str = strcat(str, tmpStr); if ((given & 0x01000000) != 0) { int offset = given & 0xff; if (offset) { sprintf(tmpStr, ", %s#%d]%s", ((given & 0x00800000) == 0 ? "-" : ""), offset * 4, ((given & 0x00200000) != 0 ? "!" : "")); str = strcat(str, tmpStr); } else str = strcat(str, "]"); } else { int offset = given & 0xff; if (offset) { sprintf(tmpStr, "], %s#%d", ((given & 0x00800000) == 0 ? "-" : ""), offset * 4); str = strcat(str, tmpStr); } else str = strcat(str, "]"); } break; case 'C': switch (given & 0x00090000) { case 0: str = strcat(str, "_???"); break; case 0x10000: str = strcat(str, "_ctl"); break; case 0x80000: str = strcat(str, "_flg"); break; } break; case 'F': switch (given & 0x00408000) { case 0: str = strcat(str, "4"); break; case 0x8000: str = strcat(str, "1"); break; case 0x00400000: str = strcat(str, "2"); break; default: str = strcat(str, "3"); } break; case 'P': switch (given & 0x00080080) { case 0: str = strcat(str, "s"); break; case 0x80: str = strcat(str, "d"); break; case 0x00080000: str = strcat(str, "e"); break; default: str = strcat(str, ""); break; } break; case 'Q': switch (given & 0x00408000) { case 0: str = strcat(str, "s"); break; case 0x8000: str = strcat(str, "d"); break; case 0x00400000: str = strcat(str, "e"); break; default: str = strcat(str, "p"); break; } break; case 'R': switch (given & 0x60) { case 0: break; case 0x20: str = strcat(str, "p"); break; case 0x40: str = strcat(str, "m"); break; default: str = strcat(str, "z"); break; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int bitstart = *c++ - '0'; int bitend = 0; while (*c >= '0' && *c <= '9') bitstart = (bitstart * 10) + *c++ - '0'; switch (*c) { case '-': c++; while (*c >= '0' && *c <= '9') bitend = (bitend * 10) + *c++ - '0'; if (!bitend) abort(); switch (*c) { case 'r': { long reg; reg = given >> bitstart; reg &= (2 << (bitend - bitstart)) - 1; sprintf(tmpStr, "%s", arm_regnames[reg]); str = strcat(str, tmpStr); } break; case 'd': { long reg; reg = given >> bitstart; reg &= (2 << (bitend - bitstart)) - 1; sprintf(tmpStr, "%d", reg); str = strcat(str, tmpStr); } break; case 'x': { long reg; reg = given >> bitstart; reg &= (2 << (bitend - bitstart)) - 1; sprintf(tmpStr, "0x%08x", reg); str = strcat(str, tmpStr); } break; case 'f': { long reg; reg = given >> bitstart; reg &= (2 << (bitend - bitstart)) - 1; if (reg > 7) sprintf(tmpStr, "#%s", arm_fp_const[reg & 7]); else sprintf(tmpStr, "f%d", reg); str = strcat(str, tmpStr); } break; default: abort(); } break; case '`': c++; if ((given & (1 << bitstart)) == 0) { sprintf(tmpStr, "%c", *c); str = strcat(str, tmpStr); } break; case '\'': c++; if ((given & (1 << bitstart)) != 0) { sprintf(tmpStr, "%c", *c); str = strcat(str, tmpStr); } break; case '?': ++c; if ((given & (1 << bitstart)) != 0) sprintf(tmpStr, "%c", *c++); else sprintf(tmpStr, "%c", *++c); str = strcat(str, tmpStr); break; default: abort(); } break; default: abort(); } } } else { sprintf(tmpStr, "%c", *c); str = strcat(str, tmpStr); } } return 4; } } abort(); return 0; } int main(int argc, char** argv, char** envp) { #pragma unused (envp) // Let's disasm the whole file (from stdin) long given; char theString[5]; char theDisasmLine[512]; char theChar; char theMnemonic[32]; char theOperand[128]; int i; long pc = 0; int readStdIn = 1; theString[4] = '\0'; if (argc > 1) { // Correct parameters: // -pcHEXAWORD: sets the pc // -hexaHEXAWORD: disasm just this word. int argn; if (argc > 3) { fprintf(stderr, "### %s - Syntax error\n# Too many parameters", argv[0]);// not very friendly. return 1; } for (argn = 1; argn < argc; argn++) { if (sscanf(argv[argn], "-pc%X", &pc) == 0) { if (sscanf(argv[argn], "-hexa%X", &given)) { readStdIn = 0; } else { fprintf(stderr, "### %s - Syntax error\n# Unknown parameter %s", argv[0], argv[argn]);// not very friendly. return 1; } } } } if (readStdIn) { while (fread(&given, sizeof(long), /* nmemb */ 1, stdin)) { // Let's transform the long into a string. for (i = 0; i < 4; i++) { theChar = ((char*) & given)[i]; if (theChar < '!') theChar = '.'; theString[i] = theChar; } theDisasmLine[0] = '\0'; fprintf(stdout, "%.8X ", pc); pc += print_insn_arm(pc, given, theDisasmLine); if (sscanf(theDisasmLine, "%s\t", theMnemonic)) { // Let's get the operand string. sprintf(theOperand, "%s", &theDisasmLine[strlen(theMnemonic) + 1]); fprintf(stdout, "%-8s %-31s |Ý%.8X - %s\n", theMnemonic, theOperand, given, theString); } else { fprintf(stdout, "%-40s |Ý%.8X - %-10s\n", theDisasmLine, given, theString); } SpinCursor(1); } } else { for (i = 0; i < 4; i++) { theChar = ((char*) & given)[i]; if (theChar < '!') theChar = '.'; theString[i] = theChar; } theDisasmLine[0] = '\0'; fprintf(stdout, "%.8X ", pc); pc += print_insn_arm(pc, given, theDisasmLine); if (sscanf(theDisasmLine, "%s\t", theMnemonic)) { // Let's get the operand string. sprintf(theOperand, "%s", &theDisasmLine[strlen(theMnemonic) + 1]); fprintf(stdout, "%-8s %-31s |Ý%.8X - %s\n", theMnemonic, theOperand, given, theString); } else { fprintf(stdout, "%-40s |Ý%.8X - %-10s\n", theDisasmLine, given, theString); } } fflush(stdout); return 0; }