/* * File: ncode.c * * Code disassembly routines for IMP80 compiler * * Bob Eager August 2002 * */ #include "imp.h" #include "global.h" #include #include #define MAXUBUF 100 /* Size of internal output buffer */ /* Defines for extracting instruction bit fields from bytes */ #define MOD(a) (((a)>>6)&7) #define REG(a) (((a)>>3)&7) #define RM(a) ((a)&7) #define SCALE(a) (((a)>>6)&7) #define INDEX(a) (((a)>>3)&7) #define BASE(a) ((a)&7) /* Prototypes */ static PCHAR addr_to_hex(INT); static VOID decompile(VOID); static VOID do_modrm(CHAR); static VOID do_sib(INT); static VOID floating_point(INT); static UCHAR getbyte(VOID); static INT modrm(VOID); static INT nbytes(CHAR); static VOID outhex(CHAR, INT, INT, INT, INT); static VOID percent(CHAR, CHAR); static VOID reg_name(INT, CHAR); static int sib(VOID); static VOID ua_str(PCHAR); static VOID uprintf(PCHAR, ...); static VOID uputchar(CHAR); /* Local storage */ static INT addrsize; /* Size of addresses (always 32) */ static UINT col; /* Output column */ static INT do_size; /* 1 to output sizes */ static INT insl; /* Length of current instruction */ static PUCHAR instbuf; /* Start of instruction buffer */ static UINT inst_offset; /* Offset of current instruction */ static INT modrmv; /* Flag for getting modrm byte */ static INT must_do_size; /* Local 'do_size' flag */ static INT opsize; /* Operand size (always 32) */ static INT pc; /* Offset of current byte in input */ static INT prefix; /* Segment override prefix byte */ static INT sibv; /* Flag for getting SIB byte */ static CHAR ubuf[MAXUBUF]; /* Buffer for building output */ static PCHAR ubufp; /* Pointer to next byte in 'ubuf' */ static INT wordop; /* Dealing with word or byte operand */ /* Disassembly tables. The tokens signalled by a % character in the strings are interpreted as follows: First char after '%': A - direct address C - reg of r/m picks control register D - reg of r/m picks debug register E - r/m picks operand G - reg of r/m picks general register I - immediate data J - relative IP offset K - call/jmp distance M - r/m picks memory O - no r/m, offset only R - mod of r/m picks register only S - reg of r/m picks segment register T - reg of r/m picks test register X - DS:ESI Y - ES:EDI 2 - prefix of two-byte opcode e - put in 'E' if use32 (second char is part of reg name) put in 'W' for use16 or 'D' for use32 (second char is 'W') j - put in 'E' in JCXZ if prefix == 0x66 f - floating point (second char is esc value) g - do r/m group 'n', n==0..7 p - prefix s - size override (second char is a,o) d - put D if double arg, nothing otherwise (PUSHFD, POPFD &c) w - put W if word, D if double arg, nothing otherwise (LODSW/LODSD) P - simple prefix Second char after '%': a - two words in memory (BOUND) b - byte c - byte or word d - dword e - qword f - far CALL/JMP n - near CALL/JMP p - 32 or 48 bit pointer q - byte/word thingy s - six byte pseudo-descriptor v - word or dword w - word x - sign extended byte F - use floating regs in mod/rm 1-8 - group number, ESC value, etc */ /* First byte decode. Each sixteen-item group handles a different first digit in the first byte. Watch out for AAD and AAM with odd operands. */ static PCHAR opmap1[256] = { /* 0 */ "ADD %Eb,%Gb", "ADD %Ev,%Gv", "ADD %Gb,%Eb", "ADD %Gv,%Ev", "ADD AL,%Ib", "ADD %eAX,%Iv", "PUSH ES", "POP ES", "OR %Eb,%Gb", "OR %Ev,%Gv", "OR %Gb,%Eb", "OR %Gv,%Ev", "OR AL,%Ib", "OR %eAX,%Iv", "PUSH CS", "%2 ", /* 1 */ "ADC %Eb,%Gb", "ADC %Ev,%Gv", "ADC %Gb,%Eb", "ADC %Gv,%Ev", "ADC AL,%Ib", "ADC %eAX,%Iv", "PUSH SS", "POP SS", "SBB %Eb,%Gb", "SBB %Ev,%Gv", "SBB %Gb,%Eb", "SBB %Gv,%Ev", "SBB AL,%Ib", "SBB %eAX,%Iv", "PUSH DS", "POP DS", /* 2 */ "AND %Eb,%Gb", "AND %Ev,%Gv", "AND %Gb,%Eb", "AND %Gv,%Ev", "AND AL,%Ib", "AND %eAX,%Iv", "%pE", "DAA", "SUB %Eb,%Gb", "SUB %Ev,%Gv", "SUB %Gb,%Eb", "SUB %Gv,%Ev", "SUB AL,%Ib", "SUB %eAX,%Iv", "%pC", "DAS", /* 3 */ "XOR %Eb,%Gb", "XOR %Ev,%Gv", "XOR %Gb,%Eb", "XOR %Gv,%Ev", "XOR AL,%Ib", "XOR %eAX,%Iv", "%pS", "AAA", "CMP %Eb,%Gb", "CMP %Ev,%Gv", "CMP %Gb,%Eb", "CMP %Gv,%Ev", "CMP AL,%Ib", "CMP %eAX,%Iv", "%pD", "AAS", /* 4 */ "INC %eAX", "INC %eCX", "INC %eDX", "INC %eBX", "INC %eSP", "INC %eBP", "INC %eSI", "INC %eDI", "DEC %eAX", "DEC %eCX", "DEC %eDX", "DEC %eBX", "DEC %eSP", "DEC %eBP", "DEC %eSI", "DEC %eDI", /* 5 */ "PUSH %eAX", "PUSH %eCX", "PUSH %eDX", "PUSH %eBX", "PUSH %eSP", "PUSH %eBP", "PUSH %eSI", "PUSH %eDI", "POP %eAX", "POP %eCX", "POP %eDX", "POP %eBX", "POP %eSP", "POP %eBP", "POP %eSI", "POP %eDI", /* 6 */ "PUSHA%d ", "POPA%d ", "BOUND %Gv,%Ma", "ARPL %Ew,%Rw", "%pF", "%pG", "%so", "%sa", "PUSH %Iv", "IMUL %Gv,%Ev,%Iv","PUSH %Ix", "IMUL %Gv,%Ev,%Ib", "INSB", "INS%eW", "OUTSB", "OUTS%eW", /* 7 */ "JO %Jb", "JNO %Jb", "JC %Jb", "JNC %Jb", "JE %Jb", "JNE %Jb", "JBE %Jb", "JA %Jb", "JS %Jb", "JNS %Jb", "JPE %Jb", "JPO %Jb", "JL %Jb", "JGE %Jb", "JLE %Jb", "JG %Jb", /* 8 */ "%g0 %Eb,%Ib", "%g0 %Ev,%Iv", "%g0 %Ev,%Ix", "%g0 %Ev,%Ix", "TEST %Eb,%Gb", "TEST %Ev,%Gv", "XCHG %Eb,%Gb", "XCHG %Ev,%Gv", "MOV %Eb,%Gb", "MOV %Ev,%Gv", "MOV %Gb,%Eb", "MOV %Gv,%Ev", "MOV %Ew,%Sw", "LEA %Gv,%M ", "MOV %Sw,%Ew", "POP %Ev", /* 9 */ "NOP", "XCHG %eCX,%eAX", "XCHG %eDX,%eAX", "XCHG %eBX,%eAX", "XCHG %eSP,%eAX", "XCHG %eBP,%eAX", "XCHG %eSI,%eAX", "XCHG %eDI,%eAX", "CBW", "CWD", "CALL %Ap", "FWAIT", "PUSHF%d ", "POPF%d ", "SAHF", "LAHF", /* A */ "MOV AL,%Oc", "MOV %eAX,%Ov", "MOV %Oc,AL", "MOV %Ov,%eAX", "%P MOVSB", "%P MOVS%w ", "%P CMPSB", "%P CMPS%w ", "TEST AL,%Ib", "TEST %eAX,%Iv", "%P STOSB", "%P STOS%w ", "%P LODSB", "%P LODS%w ", "%P SCASB", "%P SCAS%w ", /* B */ "MOV AL,%Ib", "MOV CL,%Ib", "MOV DL,%Ib", "MOV BL,%Ib", "MOV AH,%Ib", "MOV CH,%Ib", "MOV DH,%Ib", "MOV BH,%Ib", "MOV %eAX,%Iv", "MOV %eCX,%Iv", "MOV %eDX,%Iv", "MOV %eBX,%Iv", "MOV %eSP,%Iv", "MOV %eBP,%Iv", "MOV %eSI,%Iv", "MOV %eDI,%Iv", /* C */ "%g1 %Eb,%Ib", "%g1 %Ev,%Ib", "RET %Iw", "RET", "LES %Gv,%Mp", "LDS %Gv,%Mp", "MOV %Eb,%Ib", "MOV %Ev,%Iv", "ENTER %Iw,%Ib", "LEAVE", "RETF %Iw", "RETF", "INT 03", "INT %Ib", "INTO", "IRET", /* D */ "%g1 %Eb,1", "%g1 %Ev,1", "%g1 %Eb,CL", "%g1 %Ev,CL", "AAM ; %Ib", "AAD ; %Ib", "SETALC", "XLAT", "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", /* E */ "LOOPNE %Jb", "LOOPE %Jb", "LOOP %Jb", "J%jCXZ %Jb", "IN AL,%Ib", "IN %eAX,%Ib", "OUT %Ib,AL", "OUT %Ib,%eAX", "CALL %Jv", "JMP %Jv", "JMP %Ap", "JMP %Ks%Jb", "IN AL,DX", "IN %eAX,DX", "OUT DX,AL", "OUT DX,%eAX", /* F */ "LOCK %p ", 0, "REPNE %p ", "REPE %p ", "HLT", "CMC", "%g2", "%g2", "CLC", "STC", "CLI", "STI", "CLD", "STD", "%g3", "%g4" }; /* Second byte decode for first byte of 0F. */ static PCHAR second[256] = { /* 0 */ "%g5", "%g6", "LAR %Gv,%Ew", "LSL %Gv,%Ew", 0, "LOADALL", "CLTS", "LOADALL", "INVD", "WBINVD", 0, 0, 0, 0, 0, 0, /* 1 */ "MOV %Eb,%Gb", "MOV %Ev,%Gv", "MOV %Gb,%Eb", "MOV %Gv,%Ev", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ "MOV %Rd,%Cd", "MOV %Rd,%Dd", "MOV %Cd,%Rd", "MOV %Dd,%Rd", "MOV %Rd,%Td", 0, "MOV %Td,%Rd", 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */ "WRMSR", "RDTSC", "RDMSR", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */ "JO %Jv", "JNO %Jv", "JB %Jv", "JNB %Jv", "JZ %Jv", "JNZ %Jv", "JBE %Jv", "JA %Jv", "JS %Jv", "JNS %Jv", "JP %Jv", "JNP %Jv", "JL %Jv", "JGE %Jv", "JLE %Jv", "JG %Jv", /* 9 */ "SETO %Eb", "SETNO %Eb", "SETC %Eb", "SETNC %Eb", "SETZ %Eb", "SETNZ %Eb", "SETBE %Eb", "SETNBE %Eb", "SETS %Eb", "SETNS %Eb", "SETP %Eb", "SETNP %Eb", "SETL %Eb", "SETGE %Eb", "SETLE %Eb", "SETG %Eb", /* A */ "PUSH FS", "POP FS", "CPUID", "BT %Ev,%Gv", "SHLD %Ev,%Gv,%Ib", "shld %Ev,%Gv,CL", 0, 0, "PUSH GS", "POP GS", "RSM", "BTS %Ev,%Gv", "SHRD %Ev,%Gv,%Ib", "SHRD %Ev,%Gv,CL", 0, "IMUL %Gv,%Ev", /* B */ "CMPXCHG %Eb,%Gb", "CMPXCHG %Ev,%Gv", "LSS %Mp", "BTR %Ev,%Gv", "LFS %Mp", "LGS %Mp", "MOVZX %Gv,%Eb", "MOVZX %Gv,%Ew", 0, 0, "%g7 %Ev,%Ib", "BTC %Ev,%Gv", "BSF %Gv,%Ev", "BSR %Gv,%Ev", "MOVSX %Gv,%Eb", "MOVSX %Gv,%Ew", /* C */ "XADD %Eb,%Gb", "XADD %Ev,%Gv", 0, 0, 0, 0, 0, "CMPXCHG8B %Ee", "BSWAP EAX", "BSWAP ECX", "BSWAP EDX", "BSWAP EBX", "BSWAP ESP", "BSWAP EBP", "BSWAP ESI", "BSWAP EDI", /* D */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* Group decode. */ static PCHAR groups[8][8] = { /* Group 0 is group 3 for %Ev set */ /* 0 (Group A) */ { "ADD", "OR", "ADC", "SBB", "AND", "SUB", "XOR", "CMP" }, /* 1 (Group B) */ { "ROL", "ROR", "RCL", "RCR", "SHL", "SHR", "SHL", "SAR" }, /* 2 (Group C) */ { "TEST %Eq,%Iq", "TEST %Eq,%Iq", "NOT %Ev", "NEG %Ev", "MUL %Ec", "IMUL %Ec", "DIV %Ec", "IDIV %Ec" }, /* 3 (Group D) */ { "INC %Eb", "DEC %Eb", 0, 0, 0, 0, 0, 0 }, /* 4 (Group E) */ { "INC %Ev", "DEC %Ev", "CALL %Kn%Ev", "CALL %Kf%Ep", "JMP %Kn%Ev", "JMP %Kf%Ep", "PUSH %Ev", 0 }, /* 5 (Group F) */ { "SLDT %Ew", "STR %Ew", "LLDT %Ew", "LTR %Ew", "VERR %Ew", "VERW %Ew", 0, 0 }, /* 6 (Group G) */ { "SGDT %Ms", "SIDT %Ms", "LGDT %Ms", "LIDT %Ms", "SMSW %Ew", 0, "LMSW %Ew", 0 }, /* 7 */ { 0, 0, 0, 0, "BT", "BTS", "BTR", "BTC" } }; /* Floating point tables. */ /* Zero here means invalid. If first entry starts with '*', use st(i) */ /* no assumed %EFs here. Indexed by RM(modrm()) */ static PCHAR f0[] = { 0, 0, 0, 0, 0, 0, 0, 0}; static PCHAR fop_9[] = { "*FXCH ST,%GF" }; static PCHAR fop_10[] = { "FNOP", 0, 0, 0, 0, 0, 0, 0 }; static PCHAR fop_12[] = { "FCHS", "FABS", 0, 0, "FTST", "FXAM", 0, 0 }; static PCHAR fop_13[] = { "FLD1", "FLDL2T", "FLDL2E", "FLDPI", "FLDLG2", "FLDLN2", "FLDZ", 0 }; static PCHAR fop_14[] = { "F2XM1", "FYL2X", "FPTAN", "FPATAN", "FXTRACT", "FPREM1", "FDECSTP", "FINCSTP" }; static PCHAR fop_15[] = { "FPREM", "FYL2XP1", "FSQRT", "FSINCOS", "FRNDINT", "FSCALE", "FSIN", "FCOS" }; static PCHAR fop_21[] = { 0, "FUCOMPP", 0, 0, 0, 0, 0, 0 }; static PCHAR fop_28[] = { 0, 0, "FCLEX", "FINIT", 0, 0, 0, 0 }; static PCHAR fop_32[] = { "*FADD %GF,ST" }; static PCHAR fop_33[] = { "*FMUL %GF,ST" }; static PCHAR fop_36[] = { "*FSUBR %GF,ST" }; static PCHAR fop_37[] = { "*FSUB %GF,ST" }; static PCHAR fop_38[] = { "*FDIVR %GF,ST" }; static PCHAR fop_39[] = { "*FDIV %GF,ST" }; static PCHAR fop_40[] = { "*FFREE %GF" }; static PCHAR fop_42[] = { "*FST %GF" }; static PCHAR fop_43[] = { "*FSTP %GF" }; static PCHAR fop_44[] = { "*FUCOM %GF" }; static PCHAR fop_45[] = { "*FUCOMP %GF" }; static PCHAR fop_48[] = { "*FADDP %GF,ST" }; static PCHAR fop_49[] = { "*FMULP %GF,ST" }; static PCHAR fop_51[] = { 0, "FCOMPP", 0, 0, 0, 0, 0, 0 }; static PCHAR fop_52[] = { "*FSUBRP %GF,ST" }; static PCHAR fop_53[] = { "*FSUBP %GF,ST" }; static PCHAR fop_54[] = { "*FDIVRP %GF,ST" }; static PCHAR fop_55[] = { "*FDIVP %GF,ST" }; static PCHAR fop_60[] = { "FSTSW AX",0, 0, 0, 0, 0, 0, 0 }; /* 0=use st(i), 1=undefined 0 in fop_* means undefined */ static PCHAR *fspecial[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, fop_9, fop_10, 0, fop_12, fop_13, fop_14, fop_15, f0, f0, f0, f0, f0, fop_21, f0, f0, f0, f0, f0, f0, fop_28, f0, f0, f0, fop_32, fop_33, f0, f0, fop_36, fop_37, fop_38, fop_39, fop_40, f0, fop_42, fop_43, fop_44, fop_45, f0, f0, fop_48, fop_49, f0, fop_51, fop_52, fop_53, fop_54, fop_55, f0, f0, f0, f0, fop_60, f0, f0, f0 }; /* Assumed " %EF" at end of each. mod != 3 only */ static PCHAR floatops[] = { /* 00 */ "FADD", "FMUL", "FCOM", "FCOMP", "FSUB", "FSUBR", "FDIV", "FDIVR", /* 08 */ "FLD", 0, "FST", "FSTP", "FLDENV", "FLDCW", "FSTENV", "FSTCW", /* 16 */ "FIADD", "FIMUL", "FICOMW", "FICOMPW", "FISUB", "FISUBR", "FIDIV", "FIDIVR", /* 24 */ "FILD", 0, "FIST", "FISTP", "FRSTOR", "FLDT", 0, "FSTPT", /* 32 */ "FADDQ", "FMULQ", "FCOMQ", "FCOMPQ", "FSUBQ", "FSUBRQ", "FDIVQ", "FDIVRQ", /* 40 */ "FLDQ", 0, "FSTQ", "FSTPQ", 0, 0, "FSAVE", "FSTSW", /* 48 */ "FIADDW", "FIMULW", "FICOMW", "FICOMPW", "FISUBW", "FISUBRW", "FIDIVW", "FIDIVR", /* 56 */ "FILDW", 0, "FISTW", "FISTPW", "FBLDT", "FILDQ", "FBSTPT", "FISTPQ" }; /* * Main disassembly routine; 'start' is the address of the first byte * to be disassembled, and 'finish' is the address of the first byte * not to be disassembled. 'address' is the code address, to be included * as part of the listing. * */ VOID ncode(PUCHAR start, PUCHAR finish, UINT address) { INT all = finish - start; /* Number of bytes to disassemble */ pc = 0; instbuf = start; do_size = 1; /* Always output sizes */ while(pc < all) { inst_offset = address + pc; fprintf(listfp, "%05X ", inst_offset); decompile(); } fputc('\n', listfp); } /* * Routine to output interpretation of the current instruction. * */ static VOID decompile(VOID) { INT b; PCHAR s; col = 0; prefix = 0; modrmv = -1; /* No modrm byte yet */ sibv = -1; /* No SIB byte yet */ opsize = 32; addrsize = 32; ubufp = ubuf; /* Reset internal buffer */ insl = 0; /* Reset instruction length */ b = getbyte(); wordop = b & 1; must_do_size = do_size; s = opmap1[b]; /* Do primary decode */ if(s == 0) { /* Invalid instruction? ... */ uputchar('D'); /* ... then output byte define */ uputchar('B'); uputchar('\t'); uprintf("%02X", b); } else { ua_str(s); /* Valid instruction */ } fprintf(listfp, "%*s", 15 - col, " "); col = 15 + strlen(ubuf); do { uputchar(' '); col++; } while (col < 43); fprintf(listfp, "%s\n", ubuf); } /* * Routine to return the next byte from the instruction stream. * */ static UCHAR getbyte(VOID) { UCHAR b; b = instbuf[pc++]; fprintf(listfp, "%02X", b); /* Print out byte */ insl++; inst_offset++; col += 2; return(b); } /* * Routine to output a character to 'ubuf'. * */ static VOID uputchar(CHAR ch) { *ubufp++ = ch; *ubufp = 0; } /* * Routine to output a formatted string to 'ubuf'. * */ static VOID uprintf(PCHAR s, ...) { va_list ap; va_start(ap, s); vsprintf(ubufp, s, ap); va_end(ap); while(*ubufp) ubufp++; /* Move to terminator */ } /* * Routine to place decoded instruction into 'ubuf', using the decode * string 's'. * */ static VOID ua_str(PCHAR s) { CHAR ch; if(s == (CHAR *) NULL) { uprintf(""); } else { while((ch = *s++) != 0) { if(ch == '%') { ch = *s++; percent(ch, *s++); } else { if(ch == ' ') ch = '\t'; if(ch == '@') ch = ' '; uputchar(ch); } } } } /* * Main decode driver. Uses the specifier characters 'type' and 'subtype' * (found after a '%' in the decode string) to generate the correct output * in 'ubuf'. * */ static VOID percent(CHAR type, CHAR subtype) { UINT vofs; PCHAR name; INT extend = (addrsize == 32) ? 4 : 2; UCHAR c; /* start: */ switch(type) { case 'A': /* Direct address */ outhex(subtype, extend, 0, addrsize, 0); break; case 'C': /* reg(r/m) picks control reg */ uprintf("CR%d", REG(modrm())); must_do_size = 0; break; case 'D': /* reg(r/m) picks debug reg */ uprintf("DR%d", REG(modrm())); must_do_size = 0; break; case 'E': /* r/m picks operand */ do_modrm(subtype); break; case 'G': /* reg(r/m) picks register */ if(subtype == 'f') reg_name(RM(modrm()), subtype); else reg_name(REG(modrm()), subtype); must_do_size = 0; break; case 'I': /* Immediate data */ outhex(subtype, 0, 0, opsize, 0); break; case 'J': /* Relative IP offset */ switch(nbytes(subtype)) {/* Size of offset value */ case 1: vofs = (UINT) (SCHAR) getbyte(); break; case 2: vofs = getbyte(); vofs += getbyte() << 8; vofs = (UINT) (SHORT) vofs; break; case 4: vofs = (UINT) getbyte(); vofs |= (UINT) getbyte() << 8; vofs |= (UINT) getbyte() << 16; vofs |= (UINT) getbyte() << 24; break; } name = addr_to_hex((INT) (vofs + inst_offset)); uprintf("%s", name); break; case 'K': switch(subtype) { case 'f': ua_str("FAR@"); break; case 'n': ua_str("NEAR@"); break; case 's': ua_str("SHORT@"); break; } break; case 'M': /* r/m picks memory */ do_modrm(subtype); break; case 'O': /* Offset only */ ua_str("%p:["); outhex(subtype, extend, 0, addrsize, 0); uputchar(']'); break; case 'P': /* Prefix byte */ ua_str("%p:"); break; case 'R': /* mod(r/m) picks register */ reg_name(REG(modrm()), subtype); must_do_size = 0; break; case 'S': /* reg(r/m) picks segment register */ uputchar("ECSDFG"[REG(modrm())]); uputchar('S'); must_do_size = 0; break; case 'T': /* reg(r/m) picks T reg */ uprintf("TR%d", REG(modrm())); must_do_size = 0; break; case 'X': /* DS:SI type operator */ uprintf("DS:["); if(addrsize == 32) uputchar('E'); uprintf("SI]"); break; case 'Y': /* ES:DI type operator */ uprintf("ES:["); if(addrsize == 32) uputchar('E'); uprintf("DI]"); break; case '2': /* Extended decode with second byte */ ua_str(second[getbyte()]); break; case 'g': /* modrm group 'subtype' (0--7) */ ua_str(groups[subtype-'0'][REG(modrm())]); break; case 'd': /* Size of operand == dword? */ if(opsize == 32) uputchar('D'); uputchar(subtype);/* No real subtype; following char */ break; case 'w': /* Insert explicit size specifier */ if(opsize == 32) uputchar('D'); else uputchar('W'); uputchar(subtype);/* No real subtype; following char */ break; case 'e': /* Extended reg name */ if(opsize == 32) { if(subtype == 'w') uputchar('D'); else { uputchar('E'); uputchar(subtype); } } else uputchar(subtype); break; case 'f': /* 80x87 opcode */ floating_point(subtype-'0'); break; case 'j': if(addrsize == 32 || opsize == 32) /* both of them?! */ uputchar('E'); break; case 'p': /* Prefix byte */ switch(subtype) { case 'C': case 'D': case 'E': case 'F': case 'G': case 'S': prefix = subtype; c = getbyte(); wordop = c & 1; ua_str(opmap1[c]); break; case ':': if(prefix) uprintf("%cS:", prefix); break; case ' ': c = getbyte(); wordop = c & 1; ua_str(opmap1[c]); break; } break; case 's': /* Size override */ switch(subtype) { case 'a': addrsize = 48 - addrsize; c = getbyte(); wordop = c & 1; ua_str(opmap1[c]); /* ua_str(opmap1[getbyte()]); */ break; case 'o': opsize = 48 - opsize; c = getbyte(); wordop = c & 1; ua_str(opmap1[c]); /* ua_str(opmap1[getbyte()]); */ break; } break; } } /* * Routine to output an operand in hexadecimal. * */ static VOID outhex(CHAR subtype, INT extend, INT optional, INT defsize, INT sign) { INT i; INT n = 0; INT s = 0; INT delta; UCHAR buff[6]; PCHAR name; CHAR signchar; switch(subtype) { case 'q': if(wordop) { if(opsize == 16) n = 2; else n = 4; } else n = 1; break; case 'a': break; case 'x': extend = 2; n = 1; break; case 'b': n = 1; break; case 'w': n = 2; break; case 'd': n = 4; break; case 's': n = 6; break; case 'c': case 'v': if(defsize == 32) n = 4; else n = 2; break; case 'p': if(defsize == 32) n = 6; else n = 4; s = 1; break; } for (i = 0; i < n; i++) buff[i] = getbyte(); for (; i < extend; i++) buff[i] = (UCHAR) ((buff[i-1] & (UCHAR) 0x80) ? 0xff : 0); if(s) { uprintf("%02X%02X:", buff[n-1], buff[n-2]); n -= 2; } switch(n) { case 1: delta = *(PSCHAR) buff; break; case 2: delta = *(PSHORT) buff; break; case 4: delta = *(PINT) buff; break; } if(extend > n) { if(subtype != 'x') { if(delta < 0) { delta = -delta; signchar = '-'; } else signchar = '+'; if(delta || !optional) uprintf("%c%0*X", signchar, extend, delta); } else { if(extend == 2) delta = (USHORT) delta; uprintf("%0.*X", 2*extend+1, delta); } return; } if((n == 4) && !sign) { name = addr_to_hex(delta); uprintf("%s", name); return; } switch(n) { case 1: delta = (INT) (SCHAR) delta; if(sign && delta < 0) { delta = -delta; signchar = '-'; } else signchar = '+'; if(sign) uprintf("%c%02X",signchar, (UCHAR) delta); else uprintf("%02X", (UCHAR) delta); break; case 2: if(sign && (short) delta < 0) { signchar = '-'; delta = -delta; } else signchar = '+'; if(sign) uprintf("%c%04X", signchar, (short) delta); else uprintf("%04X", (USHORT) delta); break; case 4: if(sign && delta < 0) { delta = -delta; signchar = '-'; } else signchar = '+'; if(sign) uprintf("%c%08X", signchar, delta); else uprintf("%08X", delta); break; } } /* * Routine to decode the modrm byte, and any resulting bytes. * */ static VOID do_modrm(CHAR subtype) { INT mod = MOD(modrm()); INT rm = RM(modrm()); INT extend = (addrsize == 32) ? 4 : 2; if(mod == 3) { /* Specifies two registers */ reg_name(rm, subtype); return; } if(must_do_size) { if(wordop) { if(addrsize == 32 || opsize == 32) ua_str("DWORD@PTR@"); else ua_str("WORD@PTR@"); } else ua_str("BYTE@PTR@"); } if((mod == 0) && (rm == 5) && (addrsize == 32)) { /* Mem operand with 32 bit offset */ ua_str("%p:["); outhex('d', extend, 0, addrsize, 0); uputchar(']'); return; } if((mod == 0) && (rm == 6) && (addrsize == 16)) { /* 16 bit displacement */ ua_str("%p:["); outhex('w', extend, 0, addrsize, 0); uputchar(']'); return; } if((addrsize != 32) || (rm != 4)) ua_str("%p:["); if(addrsize == 16) { switch(rm) { case 0: uprintf("BX+SI"); break; case 1: uprintf("BX+DI"); break; case 2: uprintf("BP+SI"); break; case 3: uprintf("BP+DI"); break; case 4: uprintf("SI"); break; case 5: uprintf("DI"); break; case 6: uprintf("BP"); break; case 7: uprintf("BX"); break; } } else { switch(rm) { case 0: uprintf("EAX"); break; case 1: uprintf("ECX"); break; case 2: uprintf("EDX"); break; case 3: uprintf("EBX"); break; case 4: do_sib(mod); break; case 5: uprintf("EBP"); break; case 6: uprintf("ESI"); break; case 7: uprintf("EDI"); break; } } switch(mod) { case 1: outhex('b', extend, 1, addrsize, 0); break; case 2: outhex('v', extend, 1, addrsize, 1); break; } uputchar(']'); } /* * Routine to output a register name, based on a register number and * the register size. * */ static VOID reg_name(INT regnum, CHAR size) { if(size == 'f') { /* Floating point register? */ uprintf("ST(%d)", regnum); return; } if(((size == 'v') && (opsize == 32)) || (size == 'd')) uputchar('E'); if((size == 'q' || size == 'b' || size == 'c') && !wordop) { uputchar("ACDBACDB"[regnum]); uputchar("LLLLHHHH"[regnum]); } else { uputchar("ACDBSBSD"[regnum]); uputchar("XXXXPPII"[regnum]); } } /* * Return the number of bytes in an operand described by the subtype * character 'ch'. * */ static INT nbytes(CHAR ch) { switch(ch) { case 'b': return(1); case 'w': return(2); case 'd': return(4); case 'v': if(opsize == 32) return(4); else return(2); } return(0); } /* * Convert a 32-bit address to hexadecimal. * */ static PCHAR addr_to_hex(INT addr) { static CHAR buffer[11]; sprintf(buffer, "%08XH", addr); return(buffer); } /* * Handle floating point decode. * */ static VOID floating_point(INT e1) { INT esc = e1*8 + REG(modrm()); if(MOD(modrm()) == 3) { if(fspecial[esc]) { if(fspecial[esc][0][0] == '*') { ua_str(fspecial[esc][0]+1); } else { ua_str(fspecial[esc][RM(modrm())]); } } else { ua_str(floatops[esc]); ua_str(" %EF"); } } else { ua_str(floatops[esc]); ua_str(" %EF"); } } /* * Routine to return the modrm byte. It may be requested more than once, * so we need to extract only one byte for multiple calls. * */ static INT modrm(VOID) { if(modrmv == -1) modrmv = getbyte(); return(modrmv); } /* * Routine to output the decode for the SIB byte. * */ static VOID do_sib(INT m) { INT s, i, b; s = SCALE(sib()); i = INDEX(sib()); b = BASE(sib()); switch(b) { /* Pick base */ case 0: ua_str("%p:[EAX"); break; case 1: ua_str("%p:[ECX"); break; case 2: ua_str("%p:[EDX"); break; case 3: ua_str("%p:[EBX"); break; case 4: ua_str("%p:[ESP"); break; case 5: if(m == 0) { ua_str("%p:["); outhex('d', 4, 0, addrsize, 0); } else { ua_str("%p:[EBP"); } break; case 6: ua_str("%p:[ESI"); break; case 7: ua_str("%p:[EDI"); break; } switch(i) { /* and index */ case 0: uprintf("+EAX"); break; case 1: uprintf("+ECX"); break; case 2: uprintf("+EDX"); break; case 3: uprintf("+EBX"); break; case 4: break; case 5: uprintf("+EBP"); break; case 6: uprintf("+ESI"); break; case 7: uprintf("+EDI"); break; } if(i != 4) { switch(s) { /* and scale */ case 0: uprintf(""); break; case 1: uprintf("*2"); break; case 2: uprintf("*4"); break; case 3: uprintf("*8"); break; } } } /* * Routine to return the SIB byte. It may be requested more than once, * so we need to extract only one byte for multiple calls. * */ static INT sib(VOID) { if(sibv == -1) sibv = getbyte(); return(sibv); } /* * End of file: ncode.c * */