////////////////////////////////////////////////////////////////////// // ArmDisassembler.cpp: implementation of the CArmDisassembler class. // Part of Tarmac // By David Sharp // http://www.davidsharp.com ////////////////////////////////////////////////////////////////////// #include #include #include "ArmDisassembler.h" char *Arm_disassemble(uint32 address, uint32 instruction, char *buff) { // decode based on bits 24 - 27 of instruction switch( getField(instruction, 24, 27) ) { case 0 : return decodeMultiplyOrDataProcessing(address, instruction, buff); case 1 : return decodeSingleDataSwapOrDataProcessing(address, instruction, buff); case 2 : return decodeDataProcessing(address, instruction, buff); case 3 : return decodeDataProcessing(address, instruction, buff); case 4 : return decodeSingleDTImmOffsetPostIndex(address, instruction, buff); case 5 : return decodeSingleDTImmOffsetPreIndex(address, instruction, buff); case 6 : return decodeSingleDTRegOffsetPostIndex(address, instruction, buff); case 7 : return decodeSingleDTRegOffsetPreIndex(address, instruction, buff); case 8 : return decodeBlockDTPostIndex(address, instruction, buff); case 9 : return decodeBlockDTPreIndex(address, instruction, buff); case 10 : return decodeBranch(address, instruction, buff); case 11 : return decodeBranchWithLink(address, instruction, buff); case 12 : return decodeCoProDTPreIndex(address, instruction, buff); case 13 : return decodeCoProDTPostIndex(address, instruction, buff); case 14 : return decodeCoProRegTransferOrDataOperation(address, instruction, buff); case 15 : return decodeSoftwareInterrupt(address, instruction, buff); } strcpy(buff, "ERROR DISASSEMBLING IN Disassemble()"); return buff; } char *decodeMultiplyOrDataProcessing(uint32 address, uint32 instruction, char *buff) { // check for bit pattern 1001 in bits 4-7 if( getField(instruction, 4,7) == 0x09 ) { // multiply return decodeMultiply(address, instruction, buff); } else { // data processing return decodeDataProcessing(address, instruction, buff); } strcpy(buff, "ERROR DISASSEMBLING IN decodeMultiplyOrDataProcessing()"); return buff; } char *decodeSingleDataSwapOrDataProcessing(uint32 address, uint32 instruction, char *buff) { // check for bit pattern 0000 1001 in bits 4-11 if( getField(instruction, 4,11) == 0x09) { // single data swap return decodeSingleDataSwap(address, instruction, buff); } else { // data processing return decodeDataProcessing(address, instruction, buff); } strcpy(buff, "ERROR DISASSEMBLING IN decodeSingleDataSwapOrDataProcessing()"); return buff; } char *decodeDataProcessing(uint32 address, uint32 instruction, char *buff) { // table of opcode names char *opcodeNames[16] = { "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", "tst", "teq", "cmp", "cmn", "orr", "mov", "bic", "mvn" }; bool useRd[16] = { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE }; bool useRn[16] = { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE }; // deduce opcode for specific data processing instruction // based on bits 21-24 uint8 opcode = (uint8)( getField(instruction, 21,24) ); strcpy(buff, opcodeNames[ opcode ]); // add condition code information strcat(buff, decodeConditionCode(instruction)); // table of which flags don't need explicit S adding // so that S is not added for tst, teq, cmp and cmn bool opcodeSetsFlagsExplicitly[16] = { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE }; // test S flag (set PSR flags), based on bit 20 and whether this is implicit in the opcode if( getBit(instruction, 20) && opcodeSetsFlagsExplicitly[opcode] ) { strcat(buff, "S"); } // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } char registerNumber[10]; if(useRd[getField(instruction, 21,24)] ) { // add destination register sprintf(registerNumber, "r%d", (instruction >> 12) & 0x0F ); strcat(buff, registerNumber); } // the 'mov' and 'mvn' instructions dosn't use the first operand so don't output it if( useRn[getField(instruction, 21,24)] ) { // add comma if needed if( useRd[getField(instruction, 21, 24)] ) strcat(buff, ","); // add first operand register sprintf(registerNumber, "r%d", getField(instruction, 16, 19) ); strcat(buff, registerNumber); } // add comma strcat(buff, ","); // decide type of second operand from bit 25 if( getBit(instruction, 25) ) { // 2nd operand is immediate // get immediate value from bits 0-7 uint32 immediate = getField(instruction, 0,7); // get rotate (right) applied to immediate from bits 8-11 uint32 rotate = getField(instruction, 8,11); rotate *= 2; // rotate immediate right uint32 actualImmediate = (immediate >> rotate) | (immediate << (32 - rotate)); // add immediate value char immediateValue[12]; sprintf(immediateValue, "#0x%x", actualImmediate); strcat(buff, immediateValue); } else { // 2nd operand is register // add second operand register (to which the shift is applied) from bits 0-3 sprintf(registerNumber, "r%d", instruction & 0x0F ); strcat(buff, registerNumber); // if there's any shift at all if( getField(instruction, 4, 11) ) { strcat(buff, ","); // table of shift mnemonics char *shiftMnemonic[4] = { "lsl", "lsr", "asr", "ror" }; // ??? cope with RRX // ??? cope with LSR #0 encoding being LSR #32 // add shift mnemonic from bits 5-6 strcat(buff, shiftMnemonic[ getField(instruction, 5,6) ]); strcat(buff, " "); // decide type of shift amount based on bit 4 if( getBit(instruction, 4) ) { // shifted by amount in register // add register to shift by from bits 8-11 sprintf(registerNumber, "r%d", getField(instruction, 8,11) ); strcat(buff, registerNumber); } else { // shifted by amount in immediate // add immediate value to shift by from bits 7-11 char immediateValue[12]; sprintf(immediateValue, "#%d", getField(instruction, 7,11) ); strcat(buff, immediateValue); } } // end of if any shift } return buff; } char *decodeSingleDTImmOffsetPostIndex(uint32 address, uint32 instruction, char *buff) { // decode whether load or store from bit 20 if( getBit(instruction, 20) ) { // load from memory strcpy(buff, "ldr"); } else { // store to memory strcpy(buff, "str"); } // add condition code information strcat(buff, decodeConditionCode(instruction)); // decode whether we're dealing in byte quantities or not from bit 22 if( getBit(instruction, 22) ) { // byte quantity strcat(buff, "b"); } // check for trans bit if( getBit(instruction, 21) ) strcat(buff, "t"); // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } char registerNumber[12]; // add source/destination register from bits 12-15 sprintf(registerNumber, "r%d,[", getField(instruction, 12,15) ); strcat(buff, registerNumber); // add base register from bits 16-19 sprintf(registerNumber, "r%d],#", getField(instruction, 16,19) ); strcat(buff, registerNumber); // add up/down sign from bit 23 if( !getBit(instruction, 23) ) { // if down then add subtract symbol strcat(buff, "-"); } // add unsigned 12 bit immediate offset from bits 0-11 char immediateOffset[12]; sprintf(immediateOffset, "%d", getField(instruction, 0,11) ); strcat(buff, immediateOffset); return buff; } char *decodeSingleDTImmOffsetPreIndex(uint32 address, uint32 instruction, char *buff) { // decode whether load or store from bit 20 if( getBit(instruction, 20) ) { // load from memory strcpy(buff, "ldr"); } else { // store to memory strcpy(buff, "str"); } // add condition code information strcat(buff, decodeConditionCode(instruction)); // decode whether we're dealing in byte quantities or not from bit 22 if( getBit(instruction, 22) ) { // byte quantity strcat(buff, "b"); } // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } char registerNumber[12]; // add source/destination register from bits 12-15 sprintf(registerNumber, "r%d,[", getField(instruction, 12,15) ); strcat(buff, registerNumber); // add base register from bits 16-19 sprintf(registerNumber, "r%d,#", getField(instruction, 16,19) ); strcat(buff, registerNumber); // add up/down sign from bit 23 if( !getBit(instruction, 23) ) { // if down then add subtract symbol strcat(buff, "-"); } // add unsigned 12 bit immediate offset from bits 0-11 char immediateOffset[12]; sprintf(immediateOffset, "%d]", getField(instruction, 0,11) ); strcat(buff, immediateOffset); // decode write-back from bit 21 if( getBit(instruction, 21) ) { // write-back present strcat(buff, "!"); } return buff; } char *decodeSingleDTRegOffsetPostIndex(uint32 address, uint32 instruction, char *buff) { // decode whether load or store from bit 20 if( getBit(instruction, 20) ) { // load from memory strcpy(buff, "ldr"); } else { // store to memory strcpy(buff, "str"); } // add condition code information strcat(buff, decodeConditionCode(instruction)); // decode whether we're dealing in byte quantities or not from bit 22 if( getBit(instruction, 22) ) { // byte quantity strcat(buff, "b"); } // check for trans bit if( getBit(instruction, 21) ) strcat(buff, "t"); // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } char registerNumber[12]; // add source/destination register from bits 12-15 sprintf(registerNumber, "r%d,[", getField(instruction, 12,15) ); strcat(buff, registerNumber); // add base register from bits 16-19 sprintf(registerNumber, "r%d],", getField(instruction, 16,19) ); strcat(buff, registerNumber); // ??? it would seem perverse to have an up/down bit apply to a signed register // however this needs to be confirmed. // add offset register from bits 0-3 sprintf(registerNumber, "r%d", getField(instruction, 0,3) ); strcat(buff, registerNumber); uint32 imm = getField(instruction, 7,11); // table of shift mnemonics char *shiftMnemonic[4] = { "lsl", "lsr", "asr", "ror" }; char *shiftType = shiftMnemonic[ getField(instruction, 5,6) ]; // if not LSL #0 if( !( (strcmp(shiftType, "lsl") == 0) && imm ==0) ) { strcat(buff, ","); // add shift mnemonic from bits 5-6 strcat(buff, shiftType); strcat(buff, " "); // note, single data stores can only be shifted by an immediate amount // add immediate value to shift by from bits 7-11 strcat(buff, "#"); char immediateValue[12]; sprintf(immediateValue, "%d", imm ); strcat(buff, immediateValue); } return buff; } char *decodeSingleDTRegOffsetPreIndex(uint32 address, uint32 instruction, char *buff) { // decode whether load or store from bit 20 if( getBit(instruction, 20) ) { // load from memory strcpy(buff, "ldr"); } else { // store to memory strcpy(buff, "str"); } // add condition code information strcat(buff, decodeConditionCode(instruction)); // decode whether we're dealing in byte quantities or not from bit 22 if( getBit(instruction, 22) ) { // byte quantity strcat(buff, "b"); } // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } char registerNumber[12]; // add source/destination register from bits 12-15 sprintf(registerNumber, "r%d,[", getField(instruction, 12,15) ); strcat(buff, registerNumber); // add base register from bits 16-19 sprintf(registerNumber, "r%d,", getField(instruction, 16,19) ); strcat(buff, registerNumber); // ??? it would seem perverse to have an up/down bit apply to a signed register // however this needs to be confirmed. // add offset register from bits 0-3 sprintf(registerNumber, "r%d", getField(instruction, 0,3) ); strcat(buff, registerNumber); uint32 imm = getField(instruction, 7,11); // table of shift mnemonics char *shiftMnemonic[4] = { "lsl", "lsr", "asr", "ror" }; char *shiftType = shiftMnemonic[ getField(instruction, 5,6) ]; // if not LSL #0 if( !( (strcmp(shiftType, "lsl") == 0) && imm == 0) ) { strcat(buff, ","); // add shift mnemonic from bits 5-6 strcat(buff, shiftType); strcat(buff, " "); // note, single data stores can only be shifted by an immediate amount // add immediate value to shift by from bits 7-11 strcat(buff, "#"); char immediateValue[12]; sprintf(immediateValue, "%d", imm ); strcat(buff, immediateValue); } strcat(buff, "]"); // check for write-back from bit 21 if( getBit(instruction, 21) ) { // write-back strcat(buff, "!"); } return buff; } char *decodeBlockDTPostIndex(uint32 address, uint32 instruction, char *buff) { // decode whether to load from or store to memory from bit 20 if( getBit(instruction, 20) ) { // load from memory strcpy(buff, "ldm"); } else { // store to memory strcpy(buff, "stm"); } // add condition code information strcat(buff, decodeConditionCode(instruction)); // decode whether to increment or decrement base from bit 23 if( getBit(instruction, 23) ) { // increment strcat(buff, "i"); } else { // decrement strcat(buff, "d"); } // we already decoded from the initial lookup that it's post indexed strcat(buff, "a"); // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } char registerNumber[12]; // decode base register from bits 16-19 sprintf(registerNumber, "r%d", getField(instruction, 16,19) ); strcat(buff, registerNumber); // decode write-back from bit 21 if( getBit(instruction, 21) ) { strcat(buff, "!"); } strcat(buff, ",{"); // decode register list from bits 0-15 strcat(buff, decodeRegisterList(instruction)); // decode whether to load PSR and force user mode or not from bit 22 if( getBit(instruction, 22) ) { strcat(buff, "^"); } return buff; } char *decodeBlockDTPreIndex(uint32 address, uint32 instruction, char *buff) { // decode whether to load from or store to memory from bit 20 if( getBit(instruction, 20) ) { // load from memory strcpy(buff, "ldm"); } else { // store to memory strcpy(buff, "stm"); } // add condition code information strcat(buff, decodeConditionCode(instruction)); // decode whether to increment or decrement base from bit 23 if( getBit(instruction, 23) ) { // increment strcat(buff, "i"); } else { // decrement strcat(buff, "d"); } // we already decoded from the initial lookup that it's pre indexed strcat(buff, "b"); // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } char registerNumber[12]; // decode base register from bits 16-19 sprintf(registerNumber, "r%d", getField(instruction, 16,19) ); strcat(buff, registerNumber); // decode write-back from bit 21 if( getBit(instruction, 21) ) { strcat(buff, "!"); } strcat(buff, ",{"); // decode register list from bits 0-15 strcat(buff, decodeRegisterList(instruction)); // decode whether to load PSR and force user mode or not from bit 22 if( getBit(instruction, 22) ) { strcat(buff, "^"); } return buff; } char registerList[128]; char *decodeRegisterList(uint32 instruction) { uint8 run = 0; char registerNumber[12]; bool commaNeeded = FALSE; // comman not needed strcpy(registerList, ""); // for each of the 16 registers, r0 to r15 for(uint8 regNumber=0; regNumber<16; regNumber++) { if(run) { if(getBit(instruction, regNumber) ) { // bit set if( regNumber<15 && getBit(instruction, regNumber+1) ) { // if we're second element in the run add - if(run == 1) strcat(registerList, "-"); run++; } else { // if run was only 1 reg then need to add a comma if(run == 1) strcat(registerList, ","); // next bit not set or dealing with r15 // we're at the end of a list sprintf(registerNumber, "r%d",regNumber); strcat(registerList, registerNumber); commaNeeded = TRUE; run = 0; } } else { // last reg so end list sprintf(registerNumber, "r%d",regNumber); strcat(registerList, registerNumber); run = 0; commaNeeded =TRUE; } } else { // not in run if( getBit(instruction, regNumber) ) { if( regNumber<15 && getBit(instruction, regNumber+1) ) { // if next bit set, then we're first of run // place comma if needed if(commaNeeded) strcat(registerList, ","); sprintf(registerNumber, "r%d",regNumber); strcat(registerList, registerNumber); run++; } else { if(commaNeeded) strcat(registerList, ","); // next bit not set or we're looking at r15 (end list) sprintf(registerNumber, "r%d",regNumber); strcat(registerList, registerNumber); run = 0; commaNeeded = TRUE; } } } } strcat(registerList, "}"); return registerList; } // ??? absolute address generated is wrong char *decodeBranch(uint32 address, uint32 instruction, char *buff) { strcpy(buff, "b"); strcat(buff, decodeConditionCode(instruction)); // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } // get offset from bits 0-23 // note how shifted left 2 as instructions are aligned to 32 bit word boundaries. uint32 offset = getField(instruction, 0,23); offset <<= 2; // add offset to instruction's address and add 8 for the pipelining effect // i.e. PC is 8 bytes ahead of the currently executing instruction uint32 branchAddress = address + offset + 8; // limit size to 26 bit address branchAddress &= (1<<26)-1; // output branch address in hex char branchAddressHex[12]; sprintf(branchAddressHex, "0x%08x", branchAddress); strcat(buff, branchAddressHex); return buff; } char *decodeBranchWithLink(uint32 address, uint32 instruction, char *buff) { strcpy(buff, "bl"); strcat(buff, decodeConditionCode(instruction)); // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } // get offset from bits 0-23 // note how shifted left 2 as instructions are aligned to 32 bit word boundaries. uint32 offset = getField(instruction, 0,23); offset <<= 2; // add offset to instruction's address and add 8 for the pipelining effect // i.e. PC is 8 bytes ahead of the currently executing instruction uint32 branchAddress = address + offset + 8; // limit size to 26 bit address branchAddress &= (1<<26)-1; // output branch address in hex char branchAddressHex[12]; sprintf(branchAddressHex, "0x%08x", branchAddress); strcat(buff, branchAddressHex); return buff; } char *decodeCoProDTPreIndex(uint32 address, uint32 instruction, char *buff) { strcpy(buff, "CO PRO DATA TRANSFER PRE INDEX"); return buff; } char *decodeCoProDTPostIndex(uint32 address, uint32 instruction, char *buff) { strcpy(buff, "CO PRO DATA TRANSFER POST INDEX"); return buff; } char *decodeCoProRegTransferOrDataOperation(uint32 address, uint32 instruction, char *buff) { strcpy(buff, "CO PRO REG TRANSFER OR DATA OP"); return buff; } char *decodeSoftwareInterrupt(uint32 address, uint32 instruction, char *buff) { strcpy(buff, "swi"); strcat(buff, decodeConditionCode(instruction)); char *swiList[] = { "WriteC", "WriteS", "Write0", "NewLine", "ReadC", "CLI", "Byte", "Word", "File", "Args", "BGet", "BPut", "Multiple", "Open", "ReadLine", "Control", "GetEnv", "Exit", "SetEnv", "IntOn", "IntOff", "CallBack", "EnterOS", "BreakPT", "BreakCT", "UnUsed", "SetMEMC", "SetCallB" } ; // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } char swiNumber[12]; int ins = getField(instruction, 0,23) & 0xffff; if (ins <= 27) { strcpy(swiNumber, swiList[ins]); } else if (ins >= 256) { ins &= 255; if ( (ins >= 32) && (ins <= 126) ) { sprintf(swiNumber, "WriteI+%c%c%c", 34, ins, 34); } else { sprintf(swiNumber, "WriteI+%d", ins); } } else { sprintf(swiNumber, "0x%02x", ins); } strcat(buff, swiNumber); return buff; } char *decodeMultiply(uint32 address, uint32 instruction, char *buff) { bool accumulate = FALSE; // decide whether additional accumulate function from bit 21 if( getBit(instruction, 21) ) { // multiply and accumulate strcpy(buff, "mla"); accumulate = TRUE; } else { // standard multiply strcpy(buff, "mul"); } strcat(buff, decodeConditionCode(instruction)); // test S flag (set PSR flags), based on bit 20 if( getBit(instruction, 20) ) { strcat(buff, "S"); } // append spaces as necessary, so mnemonic with suffixes is 8 characters long while( strlen(buff) < 8 ) { strcat(buff, " "); } char registerNumber[12]; // add destination register from bits 16-19 sprintf(registerNumber, "r%d,", getField(instruction, 16,19) ); strcat(buff, registerNumber); // add 1st operand register from bits 0-3 sprintf(registerNumber, "r%d,", getField(instruction, 0,3) ); strcat(buff, registerNumber); // add 2nd operand register from bits 8-11 sprintf(registerNumber, "r%d", getField(instruction, 8,11) ); strcat(buff, registerNumber); if(accumulate) { strcat(buff, ","); // add 3rd operand register from bits 12-15 sprintf(registerNumber, "r%d", getField(instruction, 12,15) ); strcat(buff, registerNumber); } return buff; } char *decodeSingleDataSwap(uint32 address, uint32 instruction, char *buff) { // word or byte quantity, byte if bit 22 set if(getBit(instruction,22)) { strcpy(buff, "swpb"); } else { strcpy(buff, "swp"); } strcat(buff, decodeConditionCode(instruction)); while( strlen(buff) < 8 ) { strcat(buff, " "); } char registerNumber[12]; // add destination register from bits 12-15 sprintf(registerNumber, "r%d,", getField(instruction, 12,15) ); strcat(buff, registerNumber); // add rm from bits 0-3 sprintf(registerNumber, "r%d,[", getField(instruction, 0, 3) ); strcat(buff, registerNumber); // add rn from bits 16-19 sprintf(registerNumber, "r%d]", getField(instruction, 16, 19) ); strcat(buff, registerNumber); return buff; } char *decodeConditionCode(uint32 instruction) { // table of condition code meanings, note that as convention dictates, AL is blank char *conditionCodes[16] = { "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "", "nv" }; return conditionCodes[ getField(instruction, 28,31) ]; }