/***************************************************************************** * * arm7dasm.c * Portable ARM7TDMI Core Emulator - Disassembler * * Copyright Steve Ellenoff, all rights reserved. * * - This source code is released as freeware for non-commercial purposes. * - You are free to use and redistribute this code in modified or * unmodified form, provided you list me in the credits. * - If you modify this source code, you must add a notice to each modified * source file that it has been changed. If you're a nice person, you * will clearly mark each change too. :) * - If you wish to use this for commercial purposes, please contact me at * sellenoff@hotmail.com * - The author of this copywritten work reserves the right to change the * terms of its usage and license at any time, including retroactively * - This entire notice must remain in the source code. * * This work is based on: * #1) 'Atmel Corporation ARM7TDMI (Thumb) Datasheet - January 1999' * #2) Arm 2/3/6 emulator By Bryan McPhail (bmcphail@tendril.co.uk) and Phil Stroffolino (MAME CORE 0.76) * *****************************************************************************/ /****************************************************************************** * Notes: * * Because Co-Processor functions are highly specialized to the actual co-proc * implementation being used, I've setup callback handlers to allow for custom * dasm display of the co-proc functions so that the implementation specific * commands/interpretation can be used. If not used, the default handlers which * implement the ARM7TDMI guideline format is used ******************************************************************************/ #include "arm7core.h" static char *WritePadding( char *pBuf, const char *pBuf0 ) { pBuf0 += 8; while( pBuf>8)&0xf, (opcode>>21)&7, (opcode>>12)&0xf, (opcode>>16)&0xf, opcode&0xf ); if((opcode>>5)&7) pBuf += sprintf( pBuf, ", %d",(opcode>>5)&7); return pBuf; } static char *DasmCoProc_DT( char *pBuf, UINT32 opcode, const char *pConditionCode, const char *pBuf0 ) { /* co processor data transfer */ /* xxxx 111P UNWL nnnn dddd pppp oooooooo */ //todo: test this on valid instructions pBuf += sprintf(pBuf, "%s%s",(opcode&0x00100000)?"LDC":"STC",pConditionCode); //Bit 20 = 1 for Load, 0 for Store //Long Operation if(opcode & 0x400000) pBuf += sprintf(pBuf, "L"); pBuf = WritePadding( pBuf, pBuf0 ); //P# & CD # pBuf += sprintf(pBuf, "p%d, c%d, ",(opcode>>8)&0x0f,(opcode>>12)&0x0f); //Base Register (Rn) pBuf += sprintf(pBuf, "[R%d%s",(opcode>>16)&0x0f,(opcode&0x1000000)?"":"]"); //If Bit 24 = 1, Pre-increment, otherwise, Post increment so close brace //immediate value ( 8 bit value is << 2 according to manual ) if(opcode & 0xff) pBuf += sprintf(pBuf, ",%s#$%x",(opcode&0x800000)?"":"-",(opcode & 0xff)<<2); //Pre-Inc brace & Write back pBuf += sprintf(pBuf, "%s%s",(opcode&0x1000000)?"]":"",(opcode&0x200000)?"{!}":""); return pBuf; } static char *DasmCoProc_DO( char *pBuf, UINT32 opcode, const char *pConditionCode, const char *pBuf0 ) { /* co processor data operation */ /* xxxx 1110 oooo nnnn dddd cccc ppp0 mmmm */ pBuf += sprintf( pBuf, "CDP" ); pBuf += sprintf( pBuf, "%s", pConditionCode ); pBuf = WritePadding( pBuf, pBuf0 ); //p#,CPOpc,cd,cn,cm pBuf += sprintf( pBuf, "p%d, %d, c%d, c%d, c%d", (opcode>>8)&0xf, (opcode>>20)&0xf, (opcode>>12)&0xf, (opcode>>16)&0xf, opcode&0xf ); if((opcode>>5)&7) pBuf += sprintf( pBuf, ", %d",(opcode>>5)&7); return pBuf; } static char *WriteImmediateOperand( char *pBuf, UINT32 opcode ) { /* rrrrbbbbbbbb */ UINT32 imm; int r; imm = opcode&0xff; r = ((opcode>>8)&0xf)*2; imm = (imm>>r)|(imm<<(32-r)); pBuf += sprintf( pBuf, ", #$%x", imm ); return pBuf; } static char *WriteDataProcessingOperand( char *pBuf, UINT32 opcode, int printOp0, int printOp1, int printOp2 ) { /* ccccctttmmmm */ static const char *const pRegOp[4] = { "LSL","LSR","ASR","ROR" }; if (printOp0) pBuf += sprintf(pBuf,"R%d, ", (opcode>>12)&0xf); if (printOp1) pBuf += sprintf(pBuf,"R%d, ", (opcode>>16)&0xf); /* Immediate Op2 */ if( opcode&0x02000000 ) return WriteImmediateOperand(pBuf-2,opcode); /* Register Op2 */ if (printOp2) //SJE: pBuf += sprintf(pBuf,"R%d, ", (opcode>>0)&0xf); pBuf += sprintf(pBuf,"R%d ", (opcode>>0)&0xf); //SJE: ignore if LSL#0 for register shift if( ((opcode&0x2000000) == 0) && (((opcode>>4) & 0xff)==0) ) return pBuf; pBuf += sprintf(pBuf, ",%s ", pRegOp[(opcode>>5)&3] ); //SJE: pBuf += sprintf(pBuf, "%s ", pRegOp[(opcode>>5)&3] ); if( opcode&0x10 ) /* Shift amount specified in bottom bits of RS */ { pBuf += sprintf( pBuf, "R%d", (opcode>>8)&0xf ); } else /* Shift amount immediate 5 bit unsigned integer */ { int c=(opcode>>7)&0x1f; if( c==0 ) c = 32; pBuf += sprintf( pBuf, "#%d", c ); } return pBuf; } static char *WriteRegisterOperand1( char *pBuf, UINT32 opcode ) { /* ccccctttmmmm */ static const char *const pRegOp[4] = { "LSL","LSR","ASR","ROR" }; pBuf += sprintf( pBuf, ", R%d", /* Operand 1 register, Operand 2 register, shift type */ (opcode>> 0)&0xf); //check for LSL 0 if( (((opcode>>5)&3)==0) && (((opcode>>7)&0xf)==0) ) return pBuf; else //Add rotation type pBuf += sprintf(pBuf," %s ",pRegOp[(opcode>>5)&3]); if( opcode&0x10 ) /* Shift amount specified in bottom bits of RS */ { pBuf += sprintf( pBuf, "R%d", (opcode>>7)&0xf ); } else /* Shift amount immediate 5 bit unsigned integer */ { int c=(opcode>>7)&0x1f; if( c==0 ) c = 32; pBuf += sprintf( pBuf, "#%d", c ); } return pBuf; } /* WriteRegisterOperand */ static char *WriteBranchAddress( char *pBuf, UINT32 pc, UINT32 opcode ) { opcode &= 0x00ffffff; if( opcode&0x00800000 ) { opcode |= 0xff000000; /* sign-extend */ } pc += 8+4*opcode; sprintf( pBuf, "$%x", pc ); return pBuf; } /* WriteBranchAddress */ UINT32 arm7_disasm( char *pBuf, UINT32 pc, UINT32 opcode ) { const char *pBuf0; static const char *const pConditionCodeTable[16] = { "EQ","NE","CS","CC", "MI","PL","VS","VC", "HI","LS","GE","LT", "GT","LE","","NV" }; static const char *const pOperation[16] = { "AND","EOR","SUB","RSB", "ADD","ADC","SBC","RSC", "TST","TEQ","CMP","CMN", "ORR","MOV","BIC","MVN" }; const char *pConditionCode; UINT32 dasmflags = 0; pConditionCode= pConditionCodeTable[opcode>>28]; pBuf0 = pBuf; if( (opcode&0x0ffffff0)==0x012fff10 ) { //bits 27-4 == 000100101111111111110001 /* Branch and Exchange (BX) */ pBuf += sprintf( pBuf, "B"); pBuf += sprintf( pBuf, "%sX", pConditionCode ); pBuf = WritePadding( pBuf, pBuf0 ); pBuf += sprintf( pBuf, "R%d",(opcode&0xf)); if ((opcode & 0x0f) == 14) dasmflags = DASMFLAG_STEP_OUT; } else if( (opcode&0x0e000000)==0 && (opcode&0x80) && (opcode&0x10) ) //bits 27-25 == 000, bit 7=1, bit 4=1 { /* multiply or swap or half word data transfer */ if(opcode&0x60) { //bits = 6-5 != 00 /* half word data transfer */ pBuf += sprintf(pBuf, "%s%s",(opcode&0x00100000)?"LDR":"STR",pConditionCode); //Bit 20 = 1 for Load, 0 for Store //Signed? (if not, always unsigned half word) if(opcode&0x40) pBuf += sprintf(pBuf, "%s",(opcode&0x20)?"SH":"SB"); //Bit 5 = 1 for Half Word, 0 for Byte else pBuf += sprintf(pBuf, "H"); pBuf = WritePadding( pBuf, pBuf0 ); //Dest Register pBuf += sprintf(pBuf, "R%d, ",(opcode>>12)&0x0f); //Base Register pBuf += sprintf(pBuf, "[R%d%s",(opcode>>16)&0x0f,(opcode&0x1000000)?"":"]"); //If Bit 24 = 1, Pre-increment, otherwise, Post increment so close brace //Immediate or Register Offset? if(opcode&0x400000) { //Bit 22 - 1 = immediate, 0 = register //immediate ( imm. value in high nibble (bits 8-11) and lo nibble (bit 0-3) ) pBuf += sprintf(pBuf, ",%s#$%x",(opcode&0x800000)?"":"-",( (((opcode>>8)&0x0f)<<4) | (opcode&0x0f))); } else { //register pBuf += sprintf(pBuf, ",%sR%d",(opcode&0x800000)?"":"-",(opcode & 0x0f)); } //Pre-Inc brace & Write back pBuf += sprintf(pBuf, "%s%s",(opcode&0x1000000)?"]":"",(opcode&0x200000)?"{!}":""); } else { if(opcode&0x01000000) { //bit 24 = 1 /* swap */ //todo: Test on valid instructions /* xxxx 0001 0B00 nnnn dddd 0000 1001 mmmm */ pBuf += sprintf( pBuf, "SWP" ); pBuf += sprintf( pBuf, "%s%s", pConditionCode, (opcode & 0x400000)?"B":"" ); //Bit 22 = Byte/Word selection //Rd, Rm, [Rn] pBuf += sprintf( pBuf, "R%d, R%d, [R%d]", (opcode>>12)&0xf, opcode&0xf, (opcode>>16)&0xf ); } else { /* multiply or multiply long */ if( opcode&0x800000 ) //Bit 23 = 1 for Multiply Long { /* Multiply Long */ /* xxxx0001 UAShhhhllllnnnn1001mmmm */ /* Signed? */ if( opcode&0x00400000 ) pBuf += sprintf( pBuf, "S" ); else pBuf += sprintf( pBuf, "U" ); /* Multiply & Accumulate? */ if( opcode&0x00200000 ) { pBuf += sprintf( pBuf, "MLAL" ); } else { pBuf += sprintf( pBuf, "MULL" ); } pBuf += sprintf( pBuf, "%s", pConditionCode ); /* Set Status Flags */ if( opcode&0x00100000 ) { *pBuf++ = 'S'; } pBuf = WritePadding( pBuf, pBuf0 ); //Format is RLo,RHi,Rm,Rs pBuf += sprintf( pBuf, "R%d, R%d, R%d, R%d", (opcode>>12)&0xf, (opcode>>16)&0xf, (opcode&0xf), (opcode>>8)&0xf); } else { /* Multiply */ /* xxxx0000 00ASdddd nnnnssss 1001mmmm */ /* Multiply & Accumulate? */ if( opcode&0x00200000 ) { pBuf += sprintf( pBuf, "MLA" ); } /* Multiply */ else { pBuf += sprintf( pBuf, "MUL" ); } pBuf += sprintf( pBuf, "%s", pConditionCode ); if( opcode&0x00100000 ) { *pBuf++ = 'S'; } pBuf = WritePadding( pBuf, pBuf0 ); pBuf += sprintf( pBuf, "R%d, R%d, R%d", (opcode>>16)&0xf, (opcode&0xf), (opcode>>8)&0xf ); if( opcode&0x00200000 ) { pBuf += sprintf( pBuf, ", R%d", (opcode>>12)&0xf ); } } } } } else if( (opcode&0x0c000000)==0 ) //bits 27-26 == 00 - This check can only exist properly after Multiplication check above { /* Data Processing OR PSR Transfer */ //SJE: check for MRS & MSR ( S bit must be clear, and bit 24,23 = 10 ) if( ((opcode&0x00100000)==0) && ((opcode&0x01800000)==0x01000000) ) { char strpsr[6]; sprintf(strpsr, "%s",(opcode&0x400000)?"SPSR":"CPSR"); //MSR ( bit 21 set ) if( (opcode&0x00200000) ) { pBuf += sprintf(pBuf, "MSR%s",pConditionCode ); //Flag Bits Only? (Bit 16 Clear) if( (opcode&0x10000)==0) pBuf += sprintf(pBuf, "F"); pBuf = WritePadding( pBuf, pBuf0 ); pBuf += sprintf(pBuf, "%s,",strpsr); WriteDataProcessingOperand(pBuf, opcode, (opcode&0x02000000)?1:0, 0, 1); } //MRS ( bit 21 clear ) else { pBuf += sprintf(pBuf, "MRS%s",pConditionCode ); pBuf = WritePadding( pBuf, pBuf0 ); pBuf += sprintf(pBuf, "R%d,",(opcode>>12)&0x0f); pBuf += sprintf(pBuf, "%s",strpsr); } } else { /* Data Processing */ /* xxxx001a aaaSnnnn ddddrrrr bbbbbbbb */ /* xxxx000a aaaSnnnn ddddcccc ctttmmmm */ int op=(opcode>>21)&0xf; pBuf += sprintf( pBuf, "%s%s", pOperation[op], pConditionCode ); //SJE: corrected S-Bit bug here //if( (opcode&0x01000000) ) if( (opcode&0x0100000) ) { *pBuf++ = 'S'; } pBuf = WritePadding( pBuf, pBuf0 ); switch (op) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x0c: case 0x0e: WriteDataProcessingOperand(pBuf, opcode, 1, 1, 1); break; case 0x08: case 0x09: case 0x0a: case 0x0b: WriteDataProcessingOperand(pBuf, opcode, 0, 1, 1); break; case 0x0d: /* look for mov pc,lr */ if (((opcode >> 12) & 0x0f) == 15 && ((opcode >> 0) & 0x0f) == 14 && (opcode & 0x02000000) == 0) dasmflags = DASMFLAG_STEP_OUT; case 0x0f: WriteDataProcessingOperand(pBuf, opcode, 1, 0, 1); break; } } } else if( (opcode&0x0c000000)==0x04000000 ) //bits 27-26 == 01 { UINT32 rn = 0; UINT32 rnv = 0; /* Data Transfer */ /* xxxx010P UBWLnnnn ddddoooo oooooooo Immediate form */ /* xxxx011P UBWLnnnn ddddcccc ctt0mmmm Register form */ if( opcode&0x00100000 ) { pBuf += sprintf( pBuf, "LDR" ); } else { pBuf += sprintf( pBuf, "STR" ); } pBuf += sprintf( pBuf, "%s", pConditionCode ); if( opcode&0x00400000 ) { pBuf += sprintf( pBuf, "B" ); } if( opcode&0x00200000 ) { /* writeback addr */ if( opcode&0x01000000 ) { /* pre-indexed addressing */ pBuf += sprintf( pBuf, "!" ); } else { /* post-indexed addressing */ pBuf += sprintf( pBuf, "T" ); } } pBuf = WritePadding( pBuf, pBuf0 ); pBuf += sprintf( pBuf, "R%d, [R%d", (opcode>>12)&0xf, (opcode>>16)&0xf ); //grab value of pc if used as base register rn = (opcode>>16)&0xf; if(rn==15) rnv = pc+8; if( opcode&0x02000000 ) { /* register form */ pBuf += sprintf( pBuf, "%s",(opcode&0x01000000)?"":"]" ); pBuf = WriteRegisterOperand1( pBuf, opcode ); pBuf += sprintf( pBuf, "%s",(opcode&0x01000000)?"]":"" ); } else { /* immediate form */ pBuf += sprintf( pBuf, "%s",(opcode&0x01000000)?"":"]" ); //hide zero offsets if(opcode&0xfff) { if( opcode&0x00800000 ) { pBuf += sprintf( pBuf, ", #$%x", opcode&0xfff ); rnv += (rnv)?opcode&0xfff:0; } else { pBuf += sprintf( pBuf, ", -#$%x", opcode&0xfff ); rnv -= (rnv)?opcode&0xfff:0; } } pBuf += sprintf( pBuf, "%s",(opcode&0x01000000)?"]":"" ); //show where the read will occur if we found a value if(rnv) pBuf += sprintf( pBuf, " (%x)",rnv); } } else if( (opcode&0x0e000000) == 0x08000000 ) //bits 27-25 == 100 { /* xxxx100P USWLnnnn llllllll llllllll */ /* Block Data Transfer */ if( opcode&0x00100000 ) { pBuf += sprintf( pBuf, "LDM" ); } else { pBuf += sprintf( pBuf, "STM" ); } pBuf += sprintf( pBuf, "%s", pConditionCode ); if( opcode&0x01000000 ) { pBuf += sprintf( pBuf, "P" ); } if( opcode&0x00800000 ) { pBuf += sprintf( pBuf, "U" ); } if( opcode&0x00400000 ) { pBuf += sprintf( pBuf, "^" ); } if( opcode&0x00200000 ) { pBuf += sprintf( pBuf, "W" ); } pBuf = WritePadding( pBuf, pBuf0 ); pBuf += sprintf( pBuf, "[R%d], {",(opcode>>16)&0xf); { int j=0,last=0,found=0; for (j=0; j<16; j++) { if (opcode&(1<> THUMB_INSN_TYPE_SHIFT ) { case 0x0: /* Logical shifting */ if( opcode & THUMB_SHIFT_R ) /* Shift right */ { rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; offs = ( opcode & THUMB_SHIFT_AMT ) >> THUMB_SHIFT_AMT_SHIFT; pBuf += sprintf( pBuf, "LSR R%d, R%d, %d", rd, rs, offs); } else /* Shift left */ { rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; offs = ( opcode & THUMB_SHIFT_AMT ) >> THUMB_SHIFT_AMT_SHIFT; pBuf += sprintf( pBuf, "LSL R%d, R%d, %d", rd, rs, offs); } break; case 0x1: /* Arithmetic */ if( opcode & THUMB_INSN_ADDSUB ) { switch( ( opcode & THUMB_ADDSUB_TYPE ) >> THUMB_ADDSUB_TYPE_SHIFT ) { case 0x0: /* ADD Rd, Rs, Rn */ rn = ( opcode & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT; rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "ADD R%d, R%d, R%d", rd, rs, rn ); break; case 0x1: /* SUB Rd, Rs, Rn */ rn = ( opcode & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT; rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "SUB R%d, R%d, R%d", rd, rs, rn ); break; case 0x2: /* ADD Rd, Rs, #imm */ imm = ( opcode & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT; rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "ADD R%d, R%d, #%d", rd, rs, imm ); break; case 0x3: /* SUB Rd, Rs, #imm */ imm = ( opcode & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT; rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "SUB R%d, R%d, #%d", rd, rs, imm ); break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } } else { rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; offs = ( opcode & THUMB_SHIFT_AMT ) >> THUMB_SHIFT_AMT_SHIFT; pBuf += sprintf( pBuf, "ASR R%d, R%d, %d", rd, rs, offs); } break; case 0x2: /* CMP / MOV */ if( opcode & THUMB_INSN_CMP ) { rn = ( opcode & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT; op2 = ( opcode & THUMB_INSN_IMM ); pBuf += sprintf( pBuf, "CMP R%d, %02x", rn, op2 ); } else { rd = ( opcode & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT; op2 = ( opcode & THUMB_INSN_IMM ); pBuf += sprintf( pBuf, "MOV R%d, %02x", rd, op2 ); } break; case 0x3: /* ADD/SUB immediate */ if( opcode & THUMB_INSN_SUB ) /* SUB Rd, #Offset8 */ { rn = ( opcode & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT; op2 = ( opcode & THUMB_INSN_IMM ); pBuf += sprintf( pBuf, "SUB R%d, %02x", rn, op2 ); // fixed, rd -> rn } else /* ADD Rd, #Offset8 */ { rn = ( opcode & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT; op2 = opcode & THUMB_INSN_IMM; pBuf += sprintf( pBuf, "ADD R%d, %02x", rn, op2 ); } break; case 0x4: /* Rd & Rm instructions */ switch( ( opcode & THUMB_GROUP4_TYPE ) >> THUMB_GROUP4_TYPE_SHIFT ) { case 0x0: switch( ( opcode & THUMB_ALUOP_TYPE ) >> THUMB_ALUOP_TYPE_SHIFT ) { case 0x0: /* AND Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "AND R%d, R%d", rd, rs ); break; case 0x1: /* EOR Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "EOR R%d, R%d", rd, rs ); break; case 0x2: /* LSL Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "LSL R%d, R%d", rd, rs ); break; case 0x3: /* LSR Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "LSR R%d, R%d", rd, rs ); break; case 0x4: /* ASR Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "ASR R%d, R%d", rd, rs ); break; case 0x5: /* ADC Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "ADC R%d, R%d", rd, rs ); break; case 0x6: /* SBC Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "SBC R%d, R%d", rd, rs ); break; case 0x7: /* ROR Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "ROR R%d, R%d", rd, rs ); break; case 0x8: /* TST Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "TST R%d, R%d", rd, rs ); break; case 0xa: /* CMP Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "CMP R%d, R%d", rd, rs ); break; case 0xb: /* CMN Rd, Rs - check flags, add dasm */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "CMN R%d, R%d", rd, rs ); break; case 0xc: /* ORR Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "ORR R%d, R%d", rd, rs ); break; case 0xd: /* MUL Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "MUL R%d, R%d", rd, rs ); break; case 0xe: /* MUL Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "BIC R%d, R%d", rd, rs ); break; case 0xf: /* MVN Rd, Rs */ rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "MVN R%d, R%d", rd, rs ); break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } break; case 0x1: switch( ( opcode & THUMB_HIREG_OP ) >> THUMB_HIREG_OP_SHIFT ) { case 0x0: /* ADD Rd, Rs */ rs = ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; rd = opcode & THUMB_HIREG_RD; switch( ( opcode & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT ) { case 0x1: /* ADD Rd, HRs */ pBuf += sprintf( pBuf, "ADD R%d, R%d", rd, rs + 8 ); break; case 0x2: /* ADD HRd, Rs */ pBuf += sprintf( pBuf, "ADD R%d, R%d", rd + 8, rs ); break; case 0x3: /* ADD HRd, HRs */ pBuf += sprintf( pBuf, "ADD R%d, R%d", rd + 8, rs + 8 ); break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } break; case 0x1: /* CMP */ switch( ( opcode & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT ) { case 0x0: /* CMP Rd, Rs */ rs = ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; rd = opcode & THUMB_HIREG_RD; pBuf += sprintf( pBuf, "CMP R%d, R%d", rd, rs ); break; case 0x1: /* CMP Rd, HRs */ rs = ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; rd = opcode & THUMB_HIREG_RD; pBuf += sprintf( pBuf, "CMP R%d, R%d", rd, rs + 8 ); break; case 0x2: /* CMP Hd, Rs */ rs = ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; rd = opcode & THUMB_HIREG_RD; pBuf += sprintf( pBuf, "CMP R%d, R%d", rd + 8, rs ); break; case 0x3: /* CMP Hd, Hs */ rs = ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; rd = opcode & THUMB_HIREG_RD; pBuf += sprintf( pBuf, "CMP R%d, R%d", rd + 8, rs + 8 ); break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } break; case 0x2: /* MOV */ switch( ( opcode & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT ) { case 0x1: rs = ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; rd = opcode & THUMB_HIREG_RD; pBuf += sprintf( pBuf, "MOV R%d, R%d", rd, rs + 8 ); break; case 0x2: rs = ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; rd = opcode & THUMB_HIREG_RD; pBuf += sprintf( pBuf, "MOV R%d, R%d", rd + 8, rs ); break; case 0x3: rs = ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; rd = opcode & THUMB_HIREG_RD; pBuf += sprintf( pBuf, "MOV R%d, R%d", rd + 8, rs + 8 ); break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } break; case 0x3: switch( ( opcode & THUMB_HIREG_H ) >> THUMB_HIREG_H_SHIFT ) { case 0x0: rd = ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT; pBuf += sprintf( pBuf, "BX R%d", rd ); break; case 0x1: rd = ( ( opcode & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) + 8; pBuf += sprintf( pBuf, "BX R%d", rd ); if (rd == 14) dasmflags = DASMFLAG_STEP_OUT; break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } break; case 0x2: case 0x3: rd = ( opcode & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT; addr = ( opcode & THUMB_INSN_IMM ) << 2; pBuf += sprintf( pBuf, "LDR R%d, [PC, #%03x]", rd, addr ); break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } break; case 0x5: /* LDR* STR* */ switch( ( opcode & THUMB_GROUP5_TYPE ) >> THUMB_GROUP5_TYPE_SHIFT ) { case 0x0: /* STR Rd, [Rn, Rm] */ rm = ( opcode & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; rn = ( opcode & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; rd = ( opcode & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; pBuf += sprintf( pBuf, "STR R%d, [R%d, R%d]", rd, rn, rm ); break; case 0x1: /* STRH Rd, [Rn, Rm] */ rm = ( opcode & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; rn = ( opcode & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; rd = ( opcode & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; pBuf += sprintf( pBuf, "STRH R%d, [R%d, R%d]", rd, rn, rm ); break; case 0x2: /* STRB Rd, [Rn, Rm] */ /* check */ rm = ( opcode & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; rn = ( opcode & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; rd = ( opcode & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; pBuf += sprintf( pBuf, "STRB R%d, [R%d, R%d]", rd, rn, rm ); break; case 0x3: /* LDRSB Rd, [Rn, Rm] */ rm = ( opcode & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; rn = ( opcode & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; rd = ( opcode & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; pBuf += sprintf( pBuf, "LDRSB R%d, [R%d, R%d]", rd, rn, rm ); break; case 0x4: /* LDR Rd, [Rn, Rm] */ /* check */ rm = ( opcode & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; rn = ( opcode & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; rd = ( opcode & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; pBuf += sprintf( pBuf, "LDR R%d, [R%d, R%d]", rd, rn, rm ); break; case 0x5: /* LDRH Rd, [Rn, Rm] */ rm = ( opcode & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; rn = ( opcode & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; rd = ( opcode & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; pBuf += sprintf( pBuf, "LDRH R%d, [R%d, R%d]", rd, rn, rm ); break; case 0x6: /* LDRB Rd, [Rn, Rm] */ rm = ( opcode & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; rn = ( opcode & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; rd = ( opcode & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; pBuf += sprintf( pBuf, "LDRB R%d, [R%d, R%d]", rd, rn, rm ); break; case 0x7: /* LDSH Rd, [Rn, Rm] */ rm = ( opcode & THUMB_GROUP5_RM ) >> THUMB_GROUP5_RM_SHIFT; rn = ( opcode & THUMB_GROUP5_RN ) >> THUMB_GROUP5_RN_SHIFT; rd = ( opcode & THUMB_GROUP5_RD ) >> THUMB_GROUP5_RD_SHIFT; pBuf += sprintf( pBuf, "LDSH R%d, [R%d, R%d]", rd, rn, rm ); break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } break; case 0x6: /* Word Store w/ Immediate Offset */ if( opcode & THUMB_LSOP_L ) /* Load */ { rn = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = opcode & THUMB_ADDSUB_RD; offs = ( ( opcode & THUMB_LSOP_OFFS ) >> THUMB_LSOP_OFFS_SHIFT ) << 2; pBuf += sprintf( pBuf, "LDR R%d [R%d + #%02x]", rd, rn, offs ); } else /* Store */ { rn = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = opcode & THUMB_ADDSUB_RD; offs = ( ( opcode & THUMB_LSOP_OFFS ) >> THUMB_LSOP_OFFS_SHIFT ) << 2; pBuf += sprintf( pBuf, "STR R%d, [R%d + #%02x] ", rd, rn, offs ); } break; case 0x7: /* Byte Store w/ Immeidate Offset */ if( opcode & THUMB_LSOP_L ) /* Load */ { rn = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = opcode & THUMB_ADDSUB_RD; offs = ( opcode & THUMB_LSOP_OFFS ) >> THUMB_LSOP_OFFS_SHIFT; pBuf += sprintf( pBuf, "LDRB R%d, [R%d + #%02x]", rd, rn, offs ); } else /* Store */ { rn = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = opcode & THUMB_ADDSUB_RD; offs = ( opcode & THUMB_LSOP_OFFS ) >> THUMB_LSOP_OFFS_SHIFT; pBuf += sprintf( pBuf, "STRB R%d, [R%d + #%02x] ", rd, rn, offs ); } break; case 0x8: /* Load/Store Halfword */ if( opcode & THUMB_HALFOP_L ) /* Load */ { imm = ( opcode & THUMB_HALFOP_OFFS ) >> THUMB_HALFOP_OFFS_SHIFT; rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "LDRH R%d, [R%d, #%03x]", rd, rs, imm << 1 ); } else /* Store */ { imm = ( opcode & THUMB_HALFOP_OFFS ) >> THUMB_HALFOP_OFFS_SHIFT; rs = ( opcode & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT; rd = ( opcode & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT; pBuf += sprintf( pBuf, "STRH R%d, [R%d, #%03x]", rd, rs, imm << 1 ); } break; case 0x9: /* Stack-Relative Load/Store */ if( opcode & THUMB_STACKOP_L ) { rd = ( opcode & THUMB_STACKOP_RD ) >> THUMB_STACKOP_RD_SHIFT; offs = (UINT8)( opcode & THUMB_INSN_IMM ); pBuf += sprintf( pBuf, "LDR R%d, [SP, #%03x]", rd, offs << 2 ); } else { rd = ( opcode & THUMB_STACKOP_RD ) >> THUMB_STACKOP_RD_SHIFT; offs = (UINT8)( opcode & THUMB_INSN_IMM ); pBuf += sprintf( pBuf, "STR R%d, [SP, #%03x]", rd, offs << 2 ); } break; case 0xa: /* Get relative address */ if( opcode & THUMB_RELADDR_SP ) /* ADD Rd, SP, #nn */ { rd = ( opcode & THUMB_RELADDR_RD ) >> THUMB_RELADDR_RD_SHIFT; offs = (UINT8)( opcode & THUMB_INSN_IMM ) << 2; pBuf += sprintf( pBuf, "ADD R%d, SP, #%03x", rd, offs ); } else /* ADD Rd, PC, #nn */ { rd = ( opcode & THUMB_RELADDR_RD ) >> THUMB_RELADDR_RD_SHIFT; offs = (UINT8)( opcode & THUMB_INSN_IMM ) << 2; pBuf += sprintf( pBuf, "ADD R%d, PC, #%03x", rd, offs ); } break; case 0xb: /* Stack-Related Opcodes */ switch( ( opcode & THUMB_STACKOP_TYPE ) >> THUMB_STACKOP_TYPE_SHIFT ) { case 0x0: /* ADD SP, #imm */ addr = ( opcode & THUMB_INSN_IMM ); addr &= ~THUMB_INSN_IMM_S; pBuf += sprintf( pBuf, "ADD SP, #"); if( opcode & THUMB_INSN_IMM_S ) { pBuf += sprintf( pBuf, "-"); } pBuf += sprintf( pBuf, "%03x", addr << 2); break; case 0x5: /* PUSH {Rlist}{LR} */ pBuf += sprintf( pBuf, "PUSH {LR, "); for( offs = 7; offs >= 0; offs-- ) { if( opcode & ( 1 << offs ) ) { pBuf += sprintf( pBuf, "R%d, ", offs); } } pBuf += sprintf( pBuf, "}"); break; case 0x4: /* PUSH {Rlist} */ pBuf += sprintf( pBuf, "PUSH {"); for( offs = 7; offs >= 0; offs-- ) { if( opcode & ( 1 << offs ) ) { pBuf += sprintf( pBuf, "R%d, ", offs); } } pBuf += sprintf( pBuf, "}"); break; case 0xc: /* POP {Rlist} */ pBuf += sprintf( pBuf, "POP {"); for( offs = 0; offs < 8; offs++ ) { if( opcode & ( 1 << offs ) ) { pBuf += sprintf( pBuf, "R%d, ", offs); } } pBuf += sprintf( pBuf, "}"); break; case 0xd: /* POP {Rlist}{PC} */ pBuf += sprintf( pBuf, "POP {"); for( offs = 0; offs < 8; offs++ ) { if( opcode & ( 1 << offs ) ) { pBuf += sprintf( pBuf, "R%d, ", offs); } } pBuf += sprintf( pBuf, "PC}"); break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } break; case 0xc: /* Multiple Load/Store */ if( opcode & THUMB_MULTLS ) /* Load */ { rd = ( opcode & THUMB_MULTLS_BASE ) >> THUMB_MULTLS_BASE_SHIFT; pBuf += sprintf( pBuf, "LDMIA R%d!,{", rd); for( offs = 0; offs < 8; offs++ ) { if( opcode & ( 1 << offs ) ) { pBuf += sprintf( pBuf, "R%d, ", offs); } } pBuf += sprintf( pBuf, "}"); } else /* Store */ { rd = ( opcode & THUMB_MULTLS_BASE ) >> THUMB_MULTLS_BASE_SHIFT; pBuf += sprintf( pBuf, "STMIA R%d!,{", rd); for( offs = 7; offs >= 0; offs-- ) { if( opcode & ( 1 << offs ) ) { pBuf += sprintf( pBuf, "R%d, ", offs); } } pBuf += sprintf( pBuf, "}"); } break; case 0xd: /* Conditional Branch */ offs = (INT8)( opcode & THUMB_INSN_IMM ); switch( ( opcode & THUMB_COND_TYPE ) >> THUMB_COND_TYPE_SHIFT ) { case COND_EQ: pBuf += sprintf( pBuf, "BEQ %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_NE: pBuf += sprintf( pBuf, "BNE %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_CS: pBuf += sprintf( pBuf, "BCS %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_CC: pBuf += sprintf( pBuf, "BCC %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_MI: pBuf += sprintf( pBuf, "BMI %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_PL: pBuf += sprintf( pBuf, "BPL %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_VS: pBuf += sprintf( pBuf, "BVS %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_VC: pBuf += sprintf( pBuf, "BVC %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_HI: pBuf += sprintf( pBuf, "BHI %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_LS: pBuf += sprintf( pBuf, "BLS %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_GE: pBuf += sprintf( pBuf, "BGE %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_LT: pBuf += sprintf( pBuf, "BLT %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_GT: pBuf += sprintf( pBuf, "BGT %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_LE: pBuf += sprintf( pBuf, "BLE %08x (%02x)", pc + 4 + (offs << 1), offs << 1); break; case COND_AL: pBuf += sprintf( pBuf, "INVALID"); break; case COND_NV: pBuf += sprintf( pBuf, "SWI %02x\n", opcode & 0xff); break; } break; case 0xe: /* B #offs */ if( insn & THUMB_BLOP_LO ) { addr = ( ( opcode & THUMB_BLOP_OFFS ) << 1 ) & 0xfffc; pBuf += sprintf( pBuf, "BLX (LO) %08x", addr ); dasmflags = DASMFLAG_STEP_OVER; } else { offs = ( opcode & THUMB_BRANCH_OFFS ) << 1; if( offs & 0x00000800 ) { offs |= 0xfffff800; } pBuf += sprintf( pBuf, "B #%08x (%08x)", offs, pc + 4 + offs); } break; case 0xf: /* BL */ if( opcode & THUMB_BLOP_LO ) { pBuf += sprintf( pBuf, "BL (LO) %08x", ( opcode & THUMB_BLOP_OFFS ) << 1 ); dasmflags = DASMFLAG_STEP_OVER; } else { addr = ( opcode & THUMB_BLOP_OFFS ) << 12; if( addr & ( 1 << 22 ) ) { addr |= 0xff800000; } pBuf += sprintf( pBuf, "BL (HI) %08x", addr ); dasmflags = DASMFLAG_STEP_OVER; } break; default: sprintf( pBuf, "INVALID %04x", opcode); break; } return dasmflags | DASMFLAG_SUPPORTED; }