CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/ext/disarm.cpp
Views: 1401
/* disarm -- a simple disassembler for ARM instructions1* (c) 2000 Gareth McCaughan2*3* This file may be distributed and used freely provided:4* 1. You do not distribute any version that lacks this5* copyright notice (exactly as it appears here, extending6* from the start to the end of the C-language comment7* containing these words)); and,8* 2. If you distribute any modified version, its source9* contains a clear description of the ways in which10* it differs from the original version, and a clear11* indication that the changes are not mine.12* There is no restriction on your permission to use and13* distribute object code or executable code derived from14* this.15*16* The original version of this file (or perhaps a later17* version by the original author) may or may not be18* available at http://web.ukonline.co.uk/g.mccaughan/g/software.html .19*20* Share and enjoy! -- g21*/2223/* (*This* comment is NOT part of the notice mentioned in the24* distribution conditions above.)25*26* The bulk of this code was ripped brutally from the middle27* of a much more interesting piece of software whose purpose28* is to disassemble object files in the format known as AOF;29* it's quite clever at spotting blocks of non-code embedded30* in code, identifying labels, and so on.31*32* This program, on the other hand, is very much simpler.33* It simply disassembles one instruction at a time. Some34* traces of the original purpose can be seen here and there.35* You might want to make this do a two-phase disassembly,36* adding labels etc the second time around. I've made this37* work by loading the whole file into memory first, partly38* because that makes a two-pass approach easier.39*40* One word of warning: I believe that the syntax this program41* uses for the MSR instruction is now obsolete.42*43* Usage:44* disarm <filename> <base-address>45* will disassemble every word in <filename>.46*47* <base-address> should be something understood by strtol.48* So you can get hex (which is probably what you want)49* by prefixing "0x".50*51* The -r option will byte-reverse each word before it's52* disassembled.53*54* The code is rather unmaintainable. I'm sorry.55*56* Changes since original release:57* ????-??-?? v0.00 Initial release.58* 2007-09-02 v0.11 Change %X to %lX in a format string.59* (Thanks to Vincent Zweije for reporting this.)60*/6162#ifdef __clang__63#pragma GCC diagnostic push64#pragma GCC diagnostic ignored "-Wtautological-compare" //used to avoid warning, force compiler to accept it.65#pragma GCC diagnostic ignored "-Wstring-plus-int"66#endif6768#include "ppsspp_config.h"69#include <cstdio>70#include <cstdlib>71#include <cstring>7273#include "Common/ArmEmitter.h"74#include "ext/disarm.h"7576static const char *CCFlagsStr[] = {77"EQ", // Equal78"NEQ", // Not equal79"CS", // Carry Set80"CC", // Carry Clear81"MI", // Minus (Negative)82"PL", // Plus83"VS", // Overflow84"VC", // No Overflow85"HI", // Unsigned higher86"LS", // Unsigned lower or same87"GE", // Signed greater than or equal88"LT", // Signed less than89"GT", // Signed greater than90"LE", // Signed less than or equal91"", // Always (unconditional) 1492};9394int GetVd(uint32_t op, bool quad = false, bool dbl = false) {95int val;96if (!quad && !dbl) {97val = ((op >> 22) & 1) | ((op >> 11) & 0x1E);98} else {99val = ((op >> 18) & 0x10) | ((op >> 12) & 0xF);100}101if (quad)102val >>= 1;103return val;104}105106int GetVn(uint32_t op, bool quad = false, bool dbl = false) {107int val;108if (!quad && !dbl) {109val = ((op >> 7) & 1) | ((op >> 15) & 0x1E);110} else {111val = ((op >> 16) & 0xF) | ((op >> 3) & 0x10);112}113if (quad)114val >>= 1;115return val;116}117118int GetVm(uint32_t op, bool quad = false, bool dbl = false) {119int val;120if (!quad && !dbl) {121val = ((op >> 5) & 1) | ((op << 1) & 0x1E);122} else {123val = ((op >> 1) & 0x10) | (op & 0xF);124}125if (quad)126val >>= 1;127return val;128}129130131// Modern VFP disassembler, written entirely separately because I can't figure out the old stuff :P132// Horrible array of hacks but hey. Can be cleaned up later.133134bool DisasmVFP(uint32_t op, char *text) {135#if defined(__ANDROID__) && PPSSPP_ARCH(X86)136// Prevent linking errors with ArmEmitter which I've excluded on x86 android.137strcpy(text, "ARM disasm not available");138#else139const char *cond = CCFlagsStr[op >> 28];140switch ((op >> 24) & 0xF) {141case 0xC:142// VLDMIA/VSTMIA143{144bool single_reg = ((op >> 8) & 0xF) == 10;145int freg = ((op >> 11) & 0x1E) | ((op >> 22) & 1);146int base = (op >> 16) & 0xF;147bool load = (op >> 20) & 1;148bool writeback = (op >> 21) & 1;149int numregs = op & 0xF;150bool add = (op >> 23) & 1;151if (add && writeback && load && base == 13) {152if (single_reg)153sprintf(text, "VPOP%s {s%i-s%i}", cond, freg, freg-1+numregs);154else155sprintf(text, "VPOP%s {d%i-d%i}", cond, freg, freg-1+(numregs/2));156157return true;158}159if (single_reg)160sprintf(text, "%s%s r%i%s, {s%i-s%i}", load ? "VLDMIA" : "VSTMIA", cond, base, writeback ? "!":"", freg, freg-1+numregs);161else162sprintf(text, "%s%s r%i%s, {d%i-d%i}", load ? "VLDMIA" : "VSTMIA", cond, base, writeback ? "!":"", freg, freg-1+(numregs/2));163164return true;165}166case 0xD:167// VLDR/VSTR/VLDMDB/VSTMDB168{169bool single_reg = ((op >> 8) & 0xF) == 10;170int freg = ((op >> 11) & 0x1E) | ((op >> 22) & 1);171int base = (op >> 16) & 0xF;172bool load = (op >> 20) & 1;173bool add = (op >> 23) & 1;174bool writeback = (op >> 21) & 1;175if (writeback) { // Multiple176int numregs = op & 0xF;177if (!add && !load && base == 13) {178if (single_reg)179sprintf(text, "VPUSH%s {s%i-s%i}", cond, freg, freg-1+numregs);180else181sprintf(text, "VPUSH%s {d%i-d%i}", cond, freg, freg-1+(numregs/2));182183return true;184}185186if (single_reg)187sprintf(text, "%s%s r%i, {s%i-s%i}", load ? "VLDMDB" : "VSTMDB", cond, base, freg, freg-1+numregs);188else189sprintf(text, "%s%s r%i, {d%i-d%i}", load ? "VLDMDB" : "VSTMDB", cond, base, freg, freg-1+(numregs/2));190} else {191int offset = (op & 0xFF) << 2;192if (!add) offset = -offset;193sprintf(text, "%s%s s%i, [r%i, #%i]", load ? "VLDR" : "VSTR", cond, freg, base, offset);194}195196return true;197}198199case 0xE:200{201switch ((op >> 20) & 0xF) {202case 0xE: // VMSR203if ((op & 0xFFF) != 0xA10)204break;205sprintf(text, "VMSR%s r%i", cond, (op >> 12) & 0xF);206return true;207case 0xF: // VMRS208if ((op & 0xFFF) != 0xA10)209break;210if (op == 0xEEF1FA10) {211sprintf(text, "VMRS%s APSR", cond);212} else {213sprintf(text, "VMRS%s r%i", cond, (op >> 12) & 0xF);214}215return true;216default:217break;218}219220if (((op >> 19) & 0x7) == 0x7) {221// VCVT222sprintf(text, "VCVT ...");223return true;224}225226bool quad_reg = (op >> 6) & 1;227bool double_reg = (op >> 8) & 1;228char c = double_reg ? 'd' : 's';229230int part1 = ((op >> 23) & 0x1F);231int part2 = ((op >> 9) & 0x7) ;232int part3 = ((op >> 20) & 0x3) ;233if (part3 == 3 && part2 == 5 && part1 == 0x1D) {234// VMOV, VCMP235int vn = GetVn(op);236if (vn != 1 && vn != 2 && vn != 3) {237int vm = GetVm(op, false, double_reg);238int vd = GetVd(op, false, double_reg);239240const char *name = "VMOV";241if (op & 0x40000)242name = (op & 0x80) ? "VCMPE" : "VCMP";243sprintf(text, "%s%s %c%i, %c%i", name, cond, c, vd, c, vm);244return true;245}246}247248// Moves between single precision registers and GPRs249if (((op >> 20) & 0xFFE) == 0xEE0) {250int vd = ((op >> 15) & 0x1E) | ((op >> 7) & 0x1);251int src = (op >> 12) & 0xF;252253if (op & (1 << 20))254sprintf(text, "VMOV r%i, s%i", src, vd);255else256sprintf(text, "VMOV s%i, r%i", vd, src);257return true;258}259260// Arithmetic261262int opnum = -1;263int opc1 = (op >> 20) & 0xFB;264int opc2 = (op >> 4) & 0xAC;265for (int i = 0; i < 16; i++) {266// What the hell?267int fixed_opc2 = opc2;268if (!(ArmGen::VFPOps[i][0].opc2 & 0x8))269fixed_opc2 &= 0xA7;270if (ArmGen::VFPOps[i][0].opc1 == opc1 && ArmGen::VFPOps[i][0].opc2 == fixed_opc2) {271opnum = i;272break;273}274}275if (opnum < 0)276return false;277switch (opnum) {278case 8:279case 10:280case 11:281case 12:282case 13:283case 14:284{285quad_reg = false;286int vd = GetVd(op, quad_reg, double_reg);287int vn = GetVn(op, quad_reg, true);288int vm = GetVm(op, quad_reg, double_reg);289if (opnum == 8 && vn == 0x11)290opnum += 3;291sprintf(text, "%s%s %c%i, %c%i", ArmGen::VFPOpNames[opnum], cond, c, vd, c, vm);292return true;293}294default:295{296quad_reg = false;297int vd = GetVd(op, quad_reg, double_reg);298int vn = GetVn(op, quad_reg, double_reg);299int vm = GetVm(op, quad_reg, double_reg);300sprintf(text, "%s%s %c%i, %c%i, %c%i", ArmGen::VFPOpNames[opnum], cond, c, vd, c, vn, c, vm);301return true;302}303}304return true;305}306break;307}308#endif309return false;310}311312static const char *GetSizeString(int sz) {313switch (sz) {314case 0:315return "8";316case 1:317return "16";318case 2:319return "32";320case 3:321return "64";322default:323return "(err)";324}325}326327static const char *GetISizeString(int sz) {328switch (sz) {329case 0:330return "i8";331case 1:332return "i16";333case 2:334return "i32";335case 3:336return "i64";337default:338return "(err)";339}340}341342static int GetRegCount(int type) {343switch (type) {344case 7: return 1;345case 10: return 2;346case 6: return 3;347case 4: return 4;348default:349return 0;350}351}352353// VLD1 / VST1354static bool DisasmNeonLDST(uint32_t op, char *text) {355bool load = (op >> 21) & 1;356int Rn = (op >> 16) & 0xF;357int Rm = (op & 0xF);358int Vd = GetVd(op, false, true);359360const char *name = load ? "LD" : "ST";361const char *suffix = "";362if (Rm == 13)363suffix = "!";364365if ((op & (1 << 23)) == 0) {366int sz = (op >> 6) & 3;367int regCount = GetRegCount((op >> 8) & 0xF);368369int startReg = Vd;370int endReg = Vd + regCount - 1;371372if (Rm != 15 && Rm != 13) {373sprintf(text, "V%s1 - regsum", name);374} else {375if (startReg == endReg)376sprintf(text, "V%s1.%s {d%i}, [r%i]%s", name, GetSizeString(sz), startReg, Rn, suffix);377else378sprintf(text, "V%s1.%s {d%i-d%i}, [r%i]%s", name, GetSizeString(sz), startReg, endReg, Rn, suffix);379}380} else {381int reg = Vd;382int sz = (op >> 10) & 3;383int index_align = (op >> 4) & 0xF;384int lane = 0;385switch (sz) {386case 0: lane = index_align >> 1; break;387case 1: lane = index_align >> 2; break;388case 2: lane = index_align >> 3; break;389}390if (Rm != 15) {391sprintf(text, "V%s1 d[0] - regsum", name);392} else {393sprintf(text, "V%s1.%s {d%i[%i]}, [r%i]%s", name, sz == 2 ? GetSizeString(sz) : GetISizeString(sz), reg, lane, Rn, suffix);394}395}396397return true;398}399400static bool DisasmArithNeon(uint32_t op, const char *opname, char *text, bool includeSuffix = true) {401bool quad = ((op >> 6) & 1);402int size = (op >> 20) & 3;403int type = (op >> 8) & 0xF;404char r = quad ? 'q' : 'd';405const char *szname = GetISizeString(size);406if (type == 0xD || type == 0xF)407szname = "f32";408409int Vd = GetVd(op, quad, true);410int Vn = GetVn(op, quad, true);411int Vm = GetVm(op, quad, true);412sprintf(text, "V%s%s%s %c%i, %c%i, %c%i", opname, includeSuffix ? "." : "", includeSuffix ? szname : "", r, Vd, r, Vn, r, Vm);413return true;414}415416static bool DisasmNeonImmVal(uint32_t op, char *text) {417using namespace ArmGen;418int opcode = (op >> 5) & 1;419int cmode = (op >> 8) & 0xF;420int imm = ((op >> 17) & 0x80) | ((op >> 12) & 0x70) | (op & 0xF);421int quad = (op >> 6) & 1;422const char *operation = "MOV";423const char *size = "(unk)";424char temp[64] = "(unk)";425switch (cmode) {426case VIMM___x___x:427case VIMM___x___x + 1:428sprintf(temp, "000000%02x_000000%02x", imm, imm);429size = ".i32";430break;431case VIMM__x___x_:432case VIMM__x___x_ + 1:433sprintf(temp, "0000%02x00_0000%02x00", imm, imm);434size = ".i32";435break;436case VIMM_x___x__:437case VIMM_x___x__ + 1:438sprintf(temp, "00%02x0000_00%02x0000", imm, imm);439size = ".i32";440break;441case VIMMx___x___:442case VIMMx___x___ + 1:443sprintf(temp, "%02x000000_%02x000000", imm, imm);444size = ".i32";445break;446447// TODO: More448449case VIMMf000f000:450if (opcode == 0) {451// TODO: Do this properly452float f = 1337;453switch (imm) {454case 0: f = 0.0f; break;455case 0x78: f = 1.5; break;456case 0x70: f = 1.0; break;457case 0xF0: f = -1.0; break;458}459sprintf(temp, "%1.1f", f);460size = "";461break;462}463}464char c = quad ? 'q' : 'd';465sprintf(text, "V%s%s %c%i, %s", operation, size, c, GetVd(op, false, false), temp);466return true;467}468469static bool DisasmNeon2Op(uint32_t op, char *text) {470const char *opname = "(unk2op)";471472bool quad = (op >> 6) & 1;473bool quadD = quad;474bool doubleD = false;475// VNEG, VABS476if (op & (1 << 16))477opname = "NEG";478479int opcode = (op >> 6) & 0xF;480int sz = (op >> 18) & 3;481const char *size = "f32";482switch (opcode) {483case 0xE:484opname = "NEG";485size = GetISizeString(sz);486break;487case 0xD:488opname = "ABS";489size = GetISizeString(sz);490break;491case 0x7:492opname = "MVN";493size = ""; // MVN surely has no "size"?494break;495case 0x8:496opname = "MOVN"; // narrow, not negate497size = GetISizeString(sz + 1);498quad = true;499quadD = false;500doubleD = true;501break;502case 0xC:503opname = "SHLL"; // widen and shift504size = GetISizeString(sz);505quad = false;506quadD = true;507doubleD = true;508break;509}510511int Vd = GetVd(op, quadD, doubleD);512int Vm = GetVm(op, quad, false);513char cD = quadD ? 'q' : 'd';514char c = quad ? 'q' : 'd';515if (opcode == 0xC) {516sprintf(text, "V%s%s%s %c%i, %c%i, #%d", opname, strlen(size) ? "." : "", size, cD, Vd, c, Vm, 8 << sz);517} else {518sprintf(text, "V%s%s%s %c%i, %c%i", opname, strlen(size) ? "." : "", size, cD, Vd, c, Vm);519}520return true;521}522523static bool DisasmVdup(uint32_t op, char *text) {524bool quad = (op >> 6) & 1;525int imm4 = (op >> 16) & 0xF;526int Vd = GetVd(op, quad, false);527int Vm = GetVm(op, false, true);528char c = quad ? 'q' : 'd';529int index = 0;530int size = 0;531if (imm4 & 1) {532index = imm4 >> 1;533size = 0;534} else if (imm4 & 2) {535index = imm4 >> 2;536size = 1;537} else if (imm4 & 4) {538index = imm4 >> 3;539size = 2;540}541542sprintf(text, "VDUP.%s %c%i, d%i[%i]", GetSizeString(size), c, Vd, Vm, index);543return true;544}545546static bool DisasmNeonVecScalar(uint32_t op, char *text) {547bool quad = (op >> 24) & 1;548549int Vd = GetVd(op, quad, true);550int Vn = GetVn(op, quad, true);551int Vm = GetVm(op, false, false);552553char c = quad ? 'q' : 'd';554555const char *opname = "(unk)";556const char *size = "f32";557558switch ((op >> 4) & 0xFF) {559case 0x94:560case 0x9C:561opname = "VMUL";562break;563case 0x14:564case 0x1C:565case 0x1E: // Hmmm.. Should look this up :P566opname = "VMLA";567break;568}569570int part = Vm & 1;571int reg = Vm >> 1;572sprintf(text, "%s.%s %c%i, %c%i, d%i[%i]", opname, size, c, Vd, c, Vn, reg, part);573return true;574}575576// This needs a rewrite, those gotos are quite ugly...577const char *DecodeSizeAndShiftImm7(bool U, bool sign, bool inverse, int imm7, bool incSize, int *shift) {578if (imm7 & 64) {579if (inverse) {580*shift = 64 - (imm7 & 63);581} else {582*shift = imm7 & 63;583}584to64:585return U ? "u64" : (sign ? "s64" : "i64");586} else if (imm7 & 32) {587if (inverse) {588*shift = 32 - (imm7 & 31);589} else {590*shift = imm7 & 31;591}592if (incSize) goto to64;593to32:594return U ? "u32" : (sign ? "s32" : "i32");595} else if (imm7 & 16) {596if (inverse) {597*shift = 16 - (imm7 & 15);598} else {599*shift = imm7 & 15;600}601if (incSize) goto to32;602to16:603return U ? "u16" : (sign ? "s16" : "i16");604} else if (imm7 & 8) {605if (inverse) {606*shift = 8 - (imm7 & 7);607} else {608*shift = imm7 & 7;609}610if (incSize) goto to16;611return U ? "u8" : (sign ? "s8" : "i8");612} else {613// Invalid encoding614*shift = -1;615}616return "i32";617}618619// What a horror show!620static bool DisasmNeon2RegShiftImm(uint32_t op, char *text) {621bool U = (op >> 24) & 1;622bool quadDest = false;623bool quadSrc = false;624bool incSize = false;625626const char *opname = "(unk)";627int opcode = (op >> 8) & 0xF;628bool inverse = false;629bool sign = false;630switch (opcode) {631case 0x5: opname = "VSHL"; quadDest = quadSrc = ((op >> 6) & 1); break;632case 0xA: opname = "VSHLL"; quadDest = true; quadSrc = false; sign = true; break;633case 0x0: opname = "VSHR"; sign = true; quadDest = quadSrc = ((op >> 6) & 1); inverse = true; break;634case 0x8: opname = "VSHRN"; quadDest = false; quadSrc = true; inverse = true; incSize = true; break;635default:636// Immediate value ops!637return DisasmNeonImmVal(op, text);638}639640int Vd = GetVd(op, quadDest, true);641int Vm = GetVm(op, quadSrc, true);642643char c1 = quadDest ? 'q' : 'd';644char c2 = quadSrc ? 'q' : 'd';645int imm7 = ((op >> 16) & 0x3f) | ((op & 0x80) >> 1);646int shift;647648const char *size;649if (opcode == 0xA) {650if (imm7 & 0x40) {651sprintf(text, "neon2regshiftimm undefined %08x", op);652return true;653}654}655656size = DecodeSizeAndShiftImm7(U, sign, inverse, imm7, incSize, &shift);657658if (opcode == 0xA && shift == 0) {659opname = "VMOVL";660sprintf(text, "%s.%s %c%i, %c%i", opname, size, c1, Vd, c2, Vm);661} else {662sprintf(text, "%s.%s %c%i, %c%i, #%i", opname, size, c1, Vd, c2, Vm, shift);663}664return true;665}666667static bool DisasmNeonF2F3(uint32_t op, char *text) {668sprintf(text, "NEON F2");669if (((op >> 20) & 0xFF8) == 0xF20 || ((op >> 20) & 0xFF8) == 0xF30) {670const char *opname = "(unk)";671bool includeSuffix = true;672int temp;673switch ((op >> 20) & 0xFF) {674case 0x20:675temp = (op >> 4) & 0xF1;676switch (temp) {677case 0x11:678opname = "AND";679includeSuffix = false;680break;681case 0xd1:682opname = "MLA";683break;684case 0x80:685case 0xd0:686opname = "ADD";687break;688case 0xF0:689opname = "MAX";690break;691}692return DisasmArithNeon(op, opname, text, includeSuffix);693case 0x22:694case 0x24:695temp = (op >> 4) & 0xF1;696switch (temp) {697case 0xF0:698opname = "MIN";699break;700case 0x11:701opname = "ORR";702includeSuffix = false;703break;704case 0x80:705case 0xd0:706opname = "ADD";707break;708case 0xd1:709opname = "MLS";710break;711default:712opname = "???";713break;714}715return DisasmArithNeon(op, opname, text, includeSuffix);716case 0x31:717if (op & 0x100)718opname = "MLS";719else720opname = "SUB";721return DisasmArithNeon(op, opname, text);722case 0x30:723case 0x34:724temp = (op >> 4) & 0xF1;725switch (temp) {726case 0x11:727opname = "EOR";728includeSuffix = false;729break;730case 0xd0:731opname = "PADD";732break;733default:734opname = "MUL";735}736return DisasmArithNeon(op, opname, text, includeSuffix);737}738} else if ((op & 0xFE800010) == 0xF2800010) {739// Two regs and a shift amount740return DisasmNeon2RegShiftImm(op, text);741} else if ((op >> 20) == 0xF3E || (op >> 20) == 0xF2E || (op >> 20) == 0xF3A || (op >> 20) == 0xF2A) {742return DisasmNeonVecScalar(op, text);743} else if ((op >> 20) == 0xF3B && ((op >> 4) & 1) == 0) {744return DisasmNeon2Op(op, text);745} else if ((op >> 20) == 0xF3F) {746return DisasmVdup(op, text);747}748return true;749}750751static bool DisasmNeon(uint32_t op, char *text) {752switch (op >> 24) {753case 0xF4:754return DisasmNeonLDST(op, text);755case 0xF2:756case 0xF3:757return DisasmNeonF2F3(op, text);758}759return false;760}761762bool ArmAnalyzeLoadStore(uint32_t addr, uint32_t op, ArmLSInstructionInfo *info) {763*info = {};764info->instructionSize = 4;765766// TODO767768return false;769}770771772typedef unsigned int word;773typedef unsigned int address;774typedef unsigned int addrdiff;775#define W(x) ((word*)(x))776777#define declstruct(name) typedef struct name s##name, * p##name778#define defstruct(name) struct name779#define defequiv(new,old) typedef struct old s##new, * p##new780781declstruct(DisOptions);782declstruct(Instruction);783784typedef enum {785target_None, /* instruction doesn't refer to an address */786target_Data, /* instruction refers to address of data */787target_FloatS, /* instruction refers to address of single-float */788target_FloatD, /* instruction refers to address of double-float */789target_FloatE, /* blah blah extended-float */790target_FloatP, /* blah blah packed decimal float */791target_Code, /* instruction refers to address of code */792target_Unknown /* instruction refers to address of *something* */793} eTargetType;794795defstruct(Instruction) {796char text[128]; /* the disassembled instruction */797int undefined; /* non-0 iff it's an undefined instr */798int badbits; /* non-0 iff something reserved has the wrong value */799int oddbits; /* non-0 iff something unspecified isn't 0 */800int is_SWI; /* non-0 iff it's a SWI */801word swinum; /* only set for SWIs */802address target; /* address instr refers to */803eTargetType target_type; /* and what we expect to be there */804int offset; /* offset from register in LDR or STR or similar */805char * addrstart; /* start of address part of instruction, or 0 */806};807808#define disopt_SWInames 1 /* use names, not &nnnn */809#define disopt_CommaSpace 2 /* put spaces after commas */810#define disopt_FIXS 4 /* bogus FIX syntax for ObjAsm */811#define disopt_ReverseBytes 8 /* byte-reverse words first */812813defstruct(DisOptions) {814word flags;815const char * * regnames; /* pointer to 16 |char *|s: register names */816};817818static pInstruction instr_disassemble(word, address, pDisOptions);819820#define INSTR_grok_v4821822/* Preprocessor defs you can give to affect this stuff:823* INSTR_grok_v4 understand ARMv4 instructions (halfword & sign-ext LDR/STR)824* INSTR_new_msr be prepared to produce new MSR syntax if asked825* The first of these is supported; the second isn't.826*/827828/* Some important single-bit fields. */829830#define Sbit (1<<20) /* set condition codes (data processing) */831#define Lbit (1<<20) /* load, not store (data transfer) */832#define Wbit (1<<21) /* writeback (data transfer) */833#define Bbit (1<<22) /* single byte (data transfer, SWP) */834#define Ubit (1<<23) /* up, not down (data transfer) */835#define Pbit (1<<24) /* pre-, not post-, indexed (data transfer) */836#define Ibit (1<<25) /* non-immediate (data transfer) */837/* immediate (data processing) */838#define SPSRbit (1<<22) /* SPSR, not CPSR (MRS, MSR) */839840/* Some important 4-bit fields. */841842#define RD(x) ((x)<<12) /* destination register */843#define RN(x) ((x)<<16) /* operand/base register */844#define CP(x) ((x)<<8) /* coprocessor number */845#define RDbits RD(15)846#define RNbits RN(15)847#define CPbits CP(15)848#define RD_is(x) ((instr&RDbits)==RD(x))849#define RN_is(x) ((instr&RNbits)==RN(x))850#define CP_is(x) ((instr&CPbits)==CP(x))851852/* A slightly efficient way of telling whether two bits are the same853* or not. It's assumed that a<b.854*/855#define BitsDiffer(a,b) ((instr^(instr>>(b-a)))&(1<<a))856857/* op = append(op,ip) === op += sprintf(op,"%s",ip),858* except that it's faster.859*/860static char * append(char * op, const char *ip) {861char c;862while ((c=*ip++)!=0) *op++=c;863return op;864}865866/* op = hex8(op,w) === op += sprintf(op,"&%08lX",w), but faster.867*/868static char * hex8(char * op, word w) {869int i;870*op++='&';871for (i=28; i>=0; i-=4) *op++ = "0123456789ABCDEF"[(w>>i)&15];872return op;873}874875/* op = reg(op,'x',n) === op += sprintf(op,"x%lu",n&15).876*/877static char * reg(char * op, char c, word n) {878*op++=c;879n&=15;880if (n>=10) { *op++='1'; n+='0'-10; } else n+='0';881*op++=(char)n;882return op;883}884885/* op = num(op,n) appends n in decimal or &n in hex886* depending on whether n<100. It's assumed that n>=0.887*/888static char * num(char * op, word w) {889if (w>=100) {890int i;891word t;892*op++='&';893for (i=28; (t=(w>>i)&15)==0; i-=4) ;894for (; i>=0; i-=4) *op++ = "0123456789ABCDEF"[(w>>i)&15];895}896else {897/* divide by 10. You can prove this works by exhaustive search. :-) */898word t = w-(w>>2); t=(t+(t>>4)) >> 3;899{ word u = w-10*t;900if (u==10) { u=0; ++t; }901if (t) *op++=(char)(t+'0');902*op++=(char)(u+'0');903}904}905return op;906}907908/* instr_disassemble909* Disassemble a single instruction.910*911* args: instr a single ARM instruction912* addr the address it's presumed to have come from913* opts cosmetic preferences for our output914*915* reqs: opts must be filled in right. In particular, it must contain916* a list of register names.917*918* return: a pointer to a structure containing the disassembled instruction919* and some other information about it.920*921* This is basically a replacement for the SWI Debugger_Disassemble,922* but it has the following advantages:923*924* + it's 3-4 times as fast925* + it's better at identifying undefined instructions,926* and instructions not invariant under { disassemble; ObjAsm; }927* + it provides some other useful information as well928* + its output syntax is the same as ObjAsm's input syntax929* (where possible)930* + it doesn't disassemble FIX incorrectly unless you ask it to931* + it's more configurable in some respects932*933* It also has the following disadvantages:934*935* - it increases the size of ObjDism936* - it doesn't provide so many `helpful' usage comments etc937* - it's less configurable in some respects938* - it doesn't (yet) know about ARMv4 instructions939*940* This function proceeds in two phases. The first is very simple:941* it works out what sort of instruction it's looking at and sets up942* three strings:943* - |mnemonic| (the basic mnemonic: LDR or whatever)944* - |flagchars| (things to go after the cond code: B or whatever)945* - |format| (a string describing how to display the instruction)946* The second phase consists of interpreting |format|, character by947* character. Some characters (e.g., letters) just mean `append this948* character to the output string'; some mean more complicated things949* like `append the name of the register whose number is in bits 12..15'950* or, worse, `append a description of the <op2> field'.951*952* I'm afraid the magic characters in |format| are rather arbitrary.953* One criterion in choosing them was that they should form a contiguous954* subrange of the character set! Sorry.955*956* Things I still want to do:957*958* - more configurability?959* - make it much faster, if possible960* - make it much smaller, if possible961*962* Format characters:963*964* \01..\05 copro register number from nybble (\001 == nybble 0, sorry)965* $ SWI number966* % register set for LDM/STM (takes note of bit 22 for ^)967* & address for B/BL968* ' ! if bit 21 set, else nothing (mnemonic: half a !)969* ( #regs for SFM (bits 22,15 = fpn, assumed already tweaked)970* ) copro opcode in bits 20..23 (for CDP)971* * op2 (takes note of bottom 12 bits, and bit 25)972* + FP register or immediate value: bits 0..3973* , comma or comma-space974* - copro extra info in bits 5..7 preceded by , omitted if 0975* . address in ADR instruction976* / address for LDR/STR (takes note of bit 23 & reg in bits 16..19)977* 0..4 register number from nybble978* 5..9 FP register number from nybble979* : copro opcode in bits 21..23 (for MRC/MCR)980* ; copro number in bits 8..11981*982* ADDED BY HRYDGARD:983* ^ 16-bit immediate984* > 5-bit immediate at 11..7 (lsb)985* < 5-bit immediate at 20..16 with +1 or -lsb if bit 6 set986*987* NB that / takes note of bit 22, too, and does its own ! when988* appropriate.989*990* On typical instructions this seems to take about 100us on my ARM6;991* that's about 3000 cycles, which seems grossly excessive. I'm not992* sure where all those cycles are being spent. Perhaps it's possible993* to make it much, much faster. Most of this time is spent on phase 2.994*/995996extern pInstruction997instr_disassemble(word instr, address addr, pDisOptions opts) {998static char flagchars[4];999static sInstruction result;1000const char * mnemonic = 0;1001char * flagp = flagchars;1002const char * format = 0;1003word fpn;1004eTargetType poss_tt = target_None;1005#ifdef INSTR_grok_v41006int is_v4 = 0;1007#endif10081009/* PHASE 0. Set up default values for |result|. */10101011if (opts->flags & disopt_ReverseBytes) {1012instr = ((instr & 0xFF00FF00) >> 8) | ((instr & 0x00FF00FF) << 8);1013instr = (instr >> 16) | (instr << 16);1014}10151016fpn = ((instr>>15)&1) + ((instr>>21)&2);10171018result.undefined = 0;1019result.badbits = 0;1020result.oddbits = 0;1021result.is_SWI = 0;1022result.target_type = target_None;1023result.offset = 0x80000000;1024result.addrstart = 0;10251026/* PHASE 1. Decode and classify instruction. */10271028switch ((instr>>24)&15) {1029case 0:1030/* multiply or data processing, or LDRH etc */1031if ((instr&(15<<4))!=(9<<4)) goto lMaybeLDRHetc;1032/* multiply */1033if (instr&(1<<23)) {1034/* int multiply */1035mnemonic = "UMULL\0UMLAL\0SMULL\0SMLAL" + 6*((instr>>21)&3);1036format = "3,4,0,2";1037}1038else {1039if (instr&(1<<22)) goto lUndefined; /* "class C" */1040/* short multiply */1041if (instr&(1<<21)) {1042mnemonic = "MLA";1043format = "4,0,2,3";1044}1045else {1046mnemonic = "MUL";1047format = "4,0,2";1048}1049}1050if (instr&Sbit) *flagp++='S';1051break;1052case 1:1053if ((instr & 0x0FFFFFF0) == ((18 << 20) | (0xFFF << 8) | (1 << 4))) {1054mnemonic = "B";1055format = "0";1056break;1057} else if ((instr & 0x0FFFFFF0) == 0x012FFF30) {1058mnemonic = "BL";1059format = "0";1060break;1061} else if ((instr & 0x0FF000F0) == 0x01200070) {1062int imm = ((instr & 0xFFF00) >> 4) | (instr & 0xF);1063snprintf(result.text, sizeof(result.text), "BKPT %d", imm);1064result.undefined = 0;1065return &result;1066}1067case 3:1068if (instr >> 24 == 0xF3) {1069if (!DisasmNeon(instr, result.text)) {1070goto lUndefined;1071}1072result.undefined = 0;1073return &result;1074}1075/* SWP or MRS/MSR or data processing */1076// hrydgard addition: MOVW/MOVT1077if ((instr & 0x0FF00000) == 0x03000000) {1078mnemonic = "MOVW";1079format = "3,^";1080break;1081}1082else if ((instr & 0x0FF00000) == 0x03400000) {1083mnemonic = "MOVT";1084format = "3,^";1085break;1086}1087else if ((instr&0x02B00FF0)==0x00000090) {1088/* SWP */1089mnemonic = "SWP";1090format = "3,0,[4]";1091if (instr&Bbit) *flagp++='B';1092break;1093}1094else if ((instr&0x02BF0FFF)==0x000F0000) {1095/* MRS */1096mnemonic = "MRS";1097format = (instr&SPSRbit) ? "3,SPSR" : "3,CPSR";1098break;1099}1100else if ((instr&0x02BFFFF0)==0x0029F000) {1101/* MSR psr<P=0/1...>,Rs */1102mnemonic = "MSR";1103format = (instr&SPSRbit) ? "SPSR,0" : "CPSR,0";1104break;1105}1106else if ((instr&0x00BFF000)==0x0028F000) {1107/* MSR {C,S}PSR_flag,op2 */1108mnemonic = "MSR";1109format = (instr&SPSRbit) ? "SPSR_flg,*" : "CPSR_flg,*";1110if (!(instr&Ibit) && (instr&(15<<4)))1111#ifdef INSTR_grok_v41112goto lMaybeLDRHetc;1113#else1114goto lUndefined; /* shifted reg in MSR illegal */1115#endif1116break;1117}1118/* fall through here */1119lMaybeLDRHetc:1120#ifdef INSTR_grok_v41121if ((instr&(14<<24))==01122&& ((instr&(9<<4))==(9<<4))) {1123/* Might well be LDRH or similar. */1124if ((instr&(Wbit+Pbit))==Wbit) goto lUndefined; /* "class E", case 1 */1125if ((instr&(Lbit+(1<<6)))==(1<<6)) goto lUndefined; /* STRSH etc */1126mnemonic = "STR\0LDR" + ((instr&Lbit) >> 18);1127if (instr&(1<<6)) *flagp++='S';1128*flagp++ = (instr&(1<<5)) ? 'B' : 'H';1129format = "3,/";1130/* aargh: */1131if (!(instr&(1<<22))) instr |= Ibit;1132is_v4=1;1133break;1134}1135#endif1136case 2:1137if (instr >> 24 == 0xF2) {1138if (!DisasmNeon(instr, result.text)) {1139goto lUndefined;1140}1141result.undefined = 0;1142return &result;1143}1144/* data processing */1145{ word op21 = instr&(15<<21);1146if ((op21==(2<<21) || (op21==(4<<21))) /* ADD or SUB */1147&& ((instr&(RNbits+Ibit+Sbit))==RN(15)+Ibit) /* imm, no S */1148/*&& ((instr&(30<<7))==0 || (instr&3))*/) { /* normal rot */1149/* ADD ...,pc,#... or SUB ...,pc,#...: turn into ADR */1150mnemonic = "ADR";1151format = "3,.";1152if ((instr&(30<<7))!=0 && !(instr&3)) result.oddbits=1;1153break;1154}1155mnemonic = "AND\0EOR\0SUB\0RSB\0ADD\0ADC\0SBC\0RSC\0"1156"TST\0TEQ\0CMP\0CMN\0ORR\0MOV\0BIC\0MVN" /* \0 */1157+ (op21 >> 19);1158/* Rd needed for all but TST,TEQ,CMP,CMN (8..11) */1159/* Rn needed for all but MOV,MVN (13,15) */1160if (op21 < ( 8<<21)) format = "3,4,*";1161else if (op21 < (12<<21)) {1162format = "4,*";1163if (instr&RDbits) {1164if ((instr&Sbit) && RD_is(15))1165*flagp++='P';1166else result.oddbits=1;1167}1168if (!(instr&Sbit)) goto lUndefined; /* CMP etc, no S bit */1169}1170else if (op21 & (1<<21)) {1171format = "3,*";1172if (instr&RNbits) result.oddbits=1;1173}1174else format = "3,4,*";1175if (instr&Sbit && (op21<(8<<21) || op21>=(12<<21))) *flagp++='S';1176}1177break;1178case 4:1179if ((instr >> 24) == 0xF4) {1180if (!DisasmNeon(instr, result.text)) {1181goto lUndefined;1182}1183result.undefined = 0;1184return &result;1185}1186case 5:1187case 6:1188case 7:1189/* STR/LDR/BFI/BFC/UBFX/SBFX or undefined */1190if ((instr&Ibit) && (instr&(1<<4))) {1191switch ((instr >> 21) & 7) {1192case 5:1193case 7:1194/* SBFX/UBFX */1195if (((instr>>4) & 7) != 5) {1196goto lUndefined;1197}1198mnemonic = (instr & (1 << 22)) ? "UBFX" : "SBFX";1199format = "3,0,>,<";1200break;1201case 6:1202/* BFI/BFC */1203if (((instr>>4) & 7) != 1) {1204goto lUndefined;1205}1206if ((instr & 15) == 15) {1207mnemonic = "BFC";1208format = "3,>,<";1209} else {1210mnemonic = "BFI";1211format = "3,0,>,<";1212}1213break;1214default:1215goto lUndefined; /* "class A" */1216}1217} else {1218mnemonic = "STR\0LDR" + ((instr&Lbit) >> 18);1219format = "3,/";1220if (instr&Bbit) *flagp++='B';1221if ((instr&(Wbit+Pbit))==Wbit) *flagp++='T';1222poss_tt = target_Data;1223}1224break;1225case 8:1226case 9:1227/* STM/LDM */1228mnemonic = "STM\0LDM" + ((instr&Lbit) >> 18);1229if (RN_is(13)) {1230/* r13, so treat as stack */1231word x = (instr&(3<<23)) >> 22;1232if (instr&Lbit) x^=6;1233{ const char * foo = "EDEAFDFA"+x;1234*flagp++ = *foo++;1235*flagp++ = *foo;1236}1237}1238else {1239/* not r13, so don't treat as stack */1240*flagp++ = (instr&Ubit) ? 'I' : 'D';1241*flagp++ = (instr&Pbit) ? 'B' : 'A';1242}1243format = "4',%";1244break;1245case 10:1246case 11:1247/* B or BL */1248mnemonic = "B\0BL"+((instr&(1<<24))>>23);1249format = "&";1250break;1251case 12:1252case 13:1253case 14: // FPU1254{1255if (!DisasmVFP(instr, result.text)) {1256goto lUndefined;1257}1258result.undefined = 0;1259return &result;1260}1261break;1262case 15:1263/* SWI */1264mnemonic = "SWI";1265format = "$";1266break;1267/* Nasty hack: this is code that won't be reached in the normal1268* course of events, and after the last case of the switch is a1269* convenient place for it.1270*/1271lUndefined:1272strcpy(result.text, "Undefined instruction");1273result.undefined = 1;1274return &result;1275}1276*flagp=0;12771278/* PHASE 2. Produce string. */12791280{ char * op = result.text;12811282/* 2a. Mnemonic. */12831284op = append(op,mnemonic);12851286/* 2b. Condition code. */12871288{ word cond = instr>>28;1289if (cond!=14) {1290const char * ip = "EQNECSCCMIPLVSVCHILSGELTGTLEALNV"+2*cond;1291*op++ = *ip++;1292*op++ = *ip;1293}1294}12951296/* 2c. Flags. */12971298{ const char * ip = flagchars;1299while (*ip) *op++ = *ip++;1300}13011302/* 2d. A tab character. */13031304*op++ = '\t';13051306/* 2e. Other stuff, determined by format string. */13071308{ const char * ip = format;1309char c;13101311const char * * regnames = opts->regnames;1312word oflags = opts->flags;13131314while ((c=*ip++) != 0) {1315switch(c) {1316case '^': // hrydgard addition1317{1318unsigned short imm16 = ((instr & 0x000F0000) >> 4) | (instr & 0x0FFF);1319op += sprintf(op, "%04x", imm16);1320}1321break;1322case '$':1323result.is_SWI = 1;1324result.swinum = instr&0x00FFFFFF;1325result.addrstart = op;1326op += sprintf(op, "&%X", result.swinum);1327break;1328case '%':1329*op++='{';1330{ word w = instr&0xFFFF;1331int i=0;1332while (w) {1333int j;1334while (!(w&(1ul<<i))) ++i;1335for (j=i+1; w&(1ul<<j); ++j) ;1336--j;1337/* registers [i..j] */1338op = append(op, regnames[i]);1339if (j-i) {1340*op++ = (j-i>1) ? '-' : ',';1341op = append(op, regnames[j]);1342}1343i=j; w=(w>>(j+1))<<(j+1);1344if (w) *op++=',';1345}1346}1347*op++='}';1348if (instr&(1<<22)) *op++='^';1349break;1350case '&':1351{ address target = (addr+8 + ((((int)instr)<<8)>>6)) & 0x03FFFFFC;1352result.addrstart = op;1353op = hex8(op, target);1354result.target_type = target_Code;1355result.target = target;1356}1357break;1358case '\'':1359lPling:1360if (instr&Wbit) *op++='!';1361break;1362case '(':1363*op++ = (char)('0'+fpn);1364break;1365case ')':1366{ word w = (instr>>20)&15;1367if (w>=10) { *op++='1'; *op++=(char)('0'-10+w); }1368else *op++=(char)(w+'0');1369}1370break;1371case '*':1372case '.':1373if (instr&Ibit) {1374/* immediate constant */1375word imm8 = (instr&255);1376word rot = (instr>>7)&30;1377if (rot && !(imm8&3) && c=='*') {1378/* Funny immediate const. Guaranteed not '.', btw */1379*op++='#'; *op++='&';1380*op++="0123456789ABCDEF"[imm8>>4];1381*op++="0123456789ABCDEF"[imm8&15];1382*op++=',';1383op = num(op, rot);1384}1385else {1386if (rot != 0) {1387imm8 = (imm8>>rot) | (imm8<<(32-rot));1388}1389if (c=='*') {1390*op++='#';1391if (imm8>256 && ((imm8&(imm8-1))==0)) {1392/* only one bit set, and that later than bit 8.1393* Represent as 1<<... .1394*/1395op = append(op,"1<<");1396{ int n=0;1397while (!(imm8&15)) { n+=4; imm8=imm8>>4; }1398/* Now imm8 is 1, 2, 4 or 8. */1399n += (0x30002010 >> 4*(imm8-1))&15;1400op = num(op, n);1401}1402}1403else {1404if (((int)imm8)<0 && ((int)imm8)>-100) {1405*op++='-'; imm8=-(int)imm8;1406}1407op = num(op, imm8);1408}1409}1410else {1411address a = addr+8;1412if (instr&(1<<22)) a-=imm8; else a+=imm8;1413result.addrstart=op;1414op = hex8(op, a);1415result.target=a; result.target_type=target_Unknown;1416}1417}1418}1419else {1420/* rotated register */1421const char * rot = "LSL\0LSR\0ASR\0ROR" + ((instr&(3<<5)) >> 3);1422op = append(op, regnames[instr&15]);1423if (instr&(1<<4)) {1424/* register rotation */1425if (instr&(1<<7)) goto lUndefined;1426*op++=','; if (oflags&disopt_CommaSpace) *op++=' ';1427op = append(op,rot); *op++=' ';1428op = append(op,regnames[(instr&(15<<8))>>8]);1429}1430else {1431/* constant rotation */1432word n = instr&(31<<7);1433if (!n) {1434if (!(instr&(3<<5))) break;1435else if ((instr&(3<<5))==(3<<5)) {1436op = append(op, ",RRX");1437break;1438}1439else n=32<<7;1440}1441*op++ = ','; if (oflags&disopt_CommaSpace) *op++=' ';1442op = num(append(append(op,rot)," #"),n>>7);1443}1444}1445break;1446case '+':1447if (instr&(1<<3)) {1448word w = instr&7;1449*op++='#';1450if (w<6) *op++=(char)('0'+w);1451else op = append(op, w==6 ? "0.5" : "10");1452}1453else {1454*op++='f';1455*op++=(char)('0'+(instr&7));1456}1457break;1458case ',':1459*op++=',';1460if (oflags&disopt_CommaSpace) *op++=' ';1461break;1462case '-':1463{ word w = instr&(7<<5);1464if (w) {1465*op++=',';1466if (oflags&disopt_CommaSpace) *op++=' ';1467*op++ = (char)('0'+(w>>5));1468}1469}1470break;1471case '/':1472result.addrstart = op;1473*op++='[';1474op = append(op, regnames[(instr&RNbits)>>16]);1475if (!(instr&Pbit)) *op++=']';1476*op++=','; if (oflags&disopt_CommaSpace) *op++=' ';1477/* For following, NB that bit 25 is always 0 for LDC, SFM etc */1478if (instr&Ibit) {1479/* shifted offset */1480if (!(instr&Ubit)) *op++='-';1481/* We're going to transfer to '*', basically. The stupid1482* thing is that the meaning of bit 25 is reversed there;1483* I don't know why the designers of the ARM did that.1484*/1485instr ^= Ibit;1486if (instr&(1<<4)) {1487#ifdef INSTR_grok_v41488if (is_v4 && !(instr&(15<<8))) {1489ip = (instr&Pbit) ? "0]" : "0";1490break;1491}1492#else1493goto lUndefined; /* LSL r3 forbidden */1494#endif1495}1496/* Need a ] iff it was pre-indexed; and an optional ! iff1497* it's pre-indexed *or* a copro instruction,1498* except that FPU operations don't need the !. Bletch.1499*/1500if (instr&Pbit) ip="*]'";1501else if (instr&(1<<27)) {1502if (CP_is(1) || CP_is(2)) {1503if (!(instr&Wbit)) goto lUndefined;1504ip="*";1505}1506else ip="*'";1507}1508else ip="*";1509}1510else {1511/* immediate offset */1512word offset;1513if (instr&(1<<27)) {1514/* LDF or LFM or similar */1515offset = (instr&255)<<2;1516}1517#ifdef INSTR_grok_v41518else if (is_v4) offset = (instr&15) + ((instr&(15<<8))>>4);1519#endif1520else {1521/* LDR or STR */1522offset = instr&0xFFF;1523}1524*op++='#';1525if (!(instr&Ubit)) {1526if (offset) *op++='-';1527else result.oddbits=1;1528result.offset = -(int)offset;1529}1530else result.offset = offset;1531op = num(op, offset);1532if (RN_is(15) && (instr&Pbit)) {1533/* Immediate, pre-indexed and PC-relative. Set target. */1534result.target_type = poss_tt;1535result.target = (instr&Ubit) ? addr+8 + offset1536: addr+8 - offset;1537if (!(instr&Wbit)) {1538/* no writeback, either. Use friendly form. */1539op = hex8(result.addrstart, result.target);1540break;1541}1542}1543if (instr&Pbit) { *op++=']'; goto lPling; }1544else if (instr&(1<<27)) {1545if (CP_is(1) || CP_is(2)) {1546if (!(instr&Wbit)) goto lUndefined;1547}1548else goto lPling;1549}1550}1551break;1552case '0': case '1': case '2': case '3': case '4':1553op = append(op, regnames[(instr>>(4*(c-'0')))&15]);1554break;1555case '5': case '6': case '7': case '8': case '9':1556*op++='f';1557*op++=(char)('0' + ((instr>>(4*(c-'5')))&7));1558break;1559case ':':1560*op++ = (char)('0' + ((instr>>21)&7));1561break;1562case ';':1563op = reg(op, 'p', instr>>8);1564break;1565case '>':1566*op++='#';1567op = num(op, (instr >> 7) & 31);1568break;1569case '<':1570*op++='#';1571if (instr & (1 << 6)) {1572op = num(op, ((instr >> 16) & 31) + 1);1573} else {1574op = num(op, ((instr >> 16) & 31) + 1 - ((instr >> 7) & 31));1575}1576break;1577default:1578if (c<=5)1579op = reg(op, 'c', instr >> (4*(c-1)));1580else *op++ = c;1581}1582}1583*op=0;1584}1585}15861587/* DONE! */15881589return &result;1590}15911592static const char * reg_names[16] = {1593"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",1594"r8", "r9", "r10", "r11", "ip", "sp", "lr", "pc"1595};15961597static sDisOptions options = {1598disopt_CommaSpace,1599reg_names1600};16011602const char *ArmRegName(int r) {1603return reg_names[r];1604}16051606void ArmDis(unsigned int addr, unsigned int w, char *output, int bufsize, bool includeWord) {1607pInstruction instr = instr_disassemble(w, addr, &options);1608char temp[256];1609if (includeWord) {1610snprintf(output, bufsize, "%08x\t%s", w, instr->text);1611} else {1612snprintf(output, bufsize, "%s", instr->text);1613}1614if (instr->undefined || instr->badbits || instr->oddbits) {1615if (instr->undefined) snprintf(output, bufsize, "%08x\t[undefined instr]", w);1616if (instr->badbits) snprintf(output, bufsize, "%08x\t[illegal bits]", w);16171618// HUH? LDR and STR gets this a lot1619// strcat(output, " ? (extra bits)");1620if (instr->oddbits) {1621snprintf(temp, sizeof(temp), " [unexpected bits %08x]", w);1622strcat(output, temp);1623}1624}1625// zap tabs1626while (*output) {1627if (*output == '\t')1628*output = ' ';1629output++;1630}1631}16321633#ifdef __clang__1634#pragma GCC diagnostic pop1635#endif163616371638