#include #include #include "o.h" /* ** Local Prototypes */ char *reg_format( int, char *, char *, char *, char * ); int check_getc( dword * ); int check_getw( dword * ); int check_getl( dword * ); int check_getv( dword *, int ); int get_checkc( dword * ); void instr_opcode( char * ); void instr_operand( char * ); int check_forward( INST_T [], int ); int mod0( int, int, char *, char *, uchar, int, int * ); int mod1( int, int, char *, char *, uchar ); int mod2( int, int, char *, char *, uchar, int, int * ); int mod3( int, char *, uchar, int, int * ); int do_sib( char *, uchar * ); int do_mod_rm( char *, uchar, uchar, int, int, int ); int byte_immed( char *, int, int ); int word_immed( char *, int ); char *regs[4][8] = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", "ax", "cx", "dx", "bx", "sp", "bp" , "si", "di", "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "??", "??", "??", "??", "??", "??", "??", "??" }; char *sregs [] = { "es" , "cs" , "ss" , "ds" , "fs" , "gs" }; char *sregsc[] = { "es:", "cs:", "ss:", "ds:", "fs:", "gs:" }; char *cr_regs[] = { "cr0", "", "cr2", "cr3", "", "", "", "" }; char *dr_regs[] = { "dr0", "dr1", "dr2", "dr3", "", "", "dr6", "dr7" }; char *tr_regs[] = { "", "", "", "", "", "", "tr6", "tr7" }; char *addr_mode[] = { "[bx+si", "[bx+di", "[bp+si", "[bp+di", "[si", "[di", "[bp", "[bx" }; char *addr_386m[] = { "[eax", "[ecx", "[edx", "[ebx", "[esp", "[ebp", "[esi", "[edi" }; char *sib_scale[] = { "", "*2", "*4", "*8" }; char *op_grp[10][8] = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp", "rol", "ror", "rcl", "rcr", "shl", "shr", "", "sar", "test", "", "not", "neg", "mul", "imul", "div", "idiv", "inc", "dec", "", "", "", "", "", "", "inc", "dec", "call", "call", "jmp", "jmp", "push", "", "sldt", "str", "lldt", "ltr", "verr", "verw", "", "", "sgdt", "sidt", "lgdt", "lidt", "smsw", "", "lmsw", "", "", "", "", "", "bt", "bts", "btr", "btc", "pop", "", "", "", "", "", "", "", "mov", "", "", "", "", "", "", "" }; char *sz_text[] = { "byte ptr ", "word ptr ", "dword ptr ", "qword ptr ", "tbyte ptr " }; char *dir_fmt[] = { "%s,%s", /* 0 = mem,reg */ "%s,%s", /* 1 = reg,mem */ "%s", /* 2 = mem */ "%s,%s,%s", /* 3 = reg,mem,third */ "%s,%s,%s" /* 4 = mem,reg,third */ }; typedef struct modrm_s MODRM_CLASS; struct modrm_s { int dir; int size; int method; int group; int size_needed; int type; }; /* ** These mod r/m classes are not in any particular order, but the ** instruction table indexes these entries with it's mod_rm_type field. */ MODRM_CLASS modrm_class[] = { 0, 0, 1, 0, FALSE, UNKNOWN, /* 0 Math instructions (byte) M,reg */ 0, 1, 1, 0, FALSE, UNKNOWN, /* 1 Math instructions (word) M,reg (and ARPL) */ 1, 0, 1, 0, FALSE, UNKNOWN, /* 2 Math instructions (byte) reg,M */ 1, 1, 1, 0, FALSE, UNKNOWN, /* 3 Math instructions (word) reg,M */ 0, 0, 5, 1, TRUE, UNKNOWN, /* 4 Group 1 (math) byte */ 0, 1, 6, 1, TRUE, UNKNOWN, /* 5 Group 1 (math) word */ 0, 1, 7, 1, TRUE, UNKNOWN, /* 6 Group 1 (math) signed byte */ 0, 1, 2, 0, TRUE, UNKNOWN, /* 7 Segment register unloading */ 1, 1, 2, 0, TRUE, UNKNOWN, /* 8 Segment register loading */ 1, 1, 1, 0, FALSE, UNKNOWN, /* 9 LEA instruction */ 2, 1, 0, 9, FALSE, UNKNOWN, /* 10 POP mod 0 r/m instruction */ 0, 0, 5, 2, FALSE, UNKNOWN, /* 11 Group 2 (rotates) byte */ 0, 1, 5, 2, FALSE, UNKNOWN, /* 12 Group 2 (rotates) word */ 0, 0, 3, 2, TRUE, UNKNOWN, /* 13 Group 2 (rotates) byte by 1 */ 0, 1, 3, 2, TRUE, UNKNOWN, /* 14 Group 2 (rotates) word by 1 */ 0, 0, 4, 2, TRUE, UNKNOWN, /* 15 Group 2 (rotates) byte by CL */ 0, 1, 4, 2, TRUE, UNKNOWN, /* 16 Group 2 (rotates) word by CL */ 1, 2, 1, 0, FALSE, UNKNOWN, /* 17 LES,LDS,BOUND,LSS,LFS,LGS */ 0, 0, 5, 10, TRUE, UNKNOWN, /* 18 Move immediate (byte) */ 0, 1, 6, 10, TRUE, UNKNOWN, /* 19 Move immediate (word) */ 2, 0, 0, 3, FALSE, UNKNOWN, /* 20 Group 3 (special) byte */ 2, 1, 0, 3, FALSE, UNKNOWN, /* 21 Group 3 (special) word */ 2, 0, 0, 4, TRUE, UNKNOWN, /* 22 Group 4 (inc/dec) byte */ 2, 1, 0, 5, FALSE, UNKNOWN, /* 23 Group 5 (special) word/dword */ 3, 1, 8, 0, FALSE, UNKNOWN, /* 24 IMUL (3 parms [reg,mem,immed word]) */ 3, 1, 9, 0, FALSE, UNKNOWN, /* 25 IMUL (3 parms [reg,mem,immed signed byte]) */ 2, 1, 0, 6, FALSE, UNKNOWN, /* 26 Group 6 (special 286/386 instructions) */ 2, 3, 0, 7, TRUE, FWORD_PTR, /* 27 Group 7 (special 286/386 instructions) */ 1, 1, 1, 0, TRUE, UNKNOWN, /* 28 LAR,LSL reg,M (word) */ 0, 0, 5, 8, TRUE, BYTE_PTR, /* 29 Group 8 (386-bit) byte */ 2, 0, 0, 0, TRUE, BYTE_PTR, /* 30 386 Set cc Instructions */ 0, 2, 10, 0, FALSE, UNKNOWN, /* 31 386 Mov r32,crX */ 1, 2, 10, 0, FALSE, UNKNOWN, /* 32 386 Mov crX,r32 */ 0, 2, 11, 0, FALSE, UNKNOWN, /* 33 386 Mov r32,drX */ 1, 2, 11, 0, FALSE, UNKNOWN, /* 34 386 Mov drX,r32 */ 0, 2, 12, 0, FALSE, UNKNOWN, /* 35 386 Mov r32,trX */ 1, 2, 12, 0, FALSE, UNKNOWN, /* 36 386 Mov trX,r32 */ 1, 1, 1, 0, TRUE, BYTE_PTR, /* 37 386 Movsx, Movzx */ 1, 1, 1, 0, TRUE, WORD_PTR, /* 38 386 Movsx, Movzx */ 4, 1, 9, 0, TRUE, UNKNOWN, /* 39 386 Shld,Shrd mem/reg,reg,imm byte */ 4, 1, 13, 0, TRUE, UNKNOWN, /* 40 386 Shld,Shrd mem/reg,reg,cl */ }; extern int pub_compare(); extern int ext_compare(); int over_seg = -1; /* -1 = No overiding segment yet */ static int over_opsize = FALSE; /* FALSE = No overiding operand size yet */ static int over_adrsize = FALSE; /* FALSE = No overiding address size yet */ static int size_large = FALSE; /* Within a WORD address size */ static int addr_large = FALSE; /* Special Addressing Modes */ static int size_bytes = 2; /* Default WORD size */ static int addr_bytes = 2; /* Default WORD size */ static char fp_opcode[8] = {0}; /* 7 + \0 */ static char fp_wait = FALSE; /* No preceeding "wait" */ void inst_init() { over_seg = -1; /* Setup for the next instruction */ over_opsize = FALSE; over_adrsize = FALSE; if ( segment_mode == 386 ) { size_large = TRUE; addr_large = TRUE; } else { size_large = FALSE; addr_large = FALSE; } size_bytes = segment_bytes; addr_bytes = segment_bytes; } char *reg_format( reg, output_string, base_text, base_fmt, extra_fmt ) int reg; char *output_string; char *base_text; char *base_fmt; char *extra_fmt; { if ( reg == NREG ) { sprintf( output_string, base_fmt, base_text ); } else { if ( reg < 9 ) { /* Byte sized half register */ sprintf( output_string, extra_fmt, base_text, regs[0][reg-1] ); } else { if ( reg < 17 ) { /* Word/DWord sized general register */ if ( size_large ) { sprintf( output_string, extra_fmt, base_text, regs[2][reg-9] ); } else { sprintf( output_string, extra_fmt, base_text, regs[1][reg-9] ); } } else { sprintf( output_string, extra_fmt, base_text, sregs[reg-17] ); } } } return( output_string ); } int check_getc( result ) dword *result; { int ch; ch = buff_getc(); if ( ch == EOF ) { return( TRUE ); } else { *result = (dword)ch; return( FALSE ); } } int check_getw( result ) dword *result; { int ch; ch = buff_getc(); if ( ch == EOF ) { return( TRUE ); } *result = ch; ch = buff_getc(); if ( ch == EOF ) { return( TRUE ); } *result += ch << 8; return( FALSE ); } int check_getl( result ) dword *result; { int ch; ch = buff_getc(); if ( ch == EOF ) { return( TRUE ); } *result = (dword)((uchar)ch); ch = buff_getc(); if ( ch == EOF ) { return( TRUE ); } *result += (dword)((uchar)ch) << 8; ch = buff_getc(); if ( ch == EOF ) { return( TRUE ); } *result += (dword)((uchar)ch) << 16; ch = buff_getc(); if ( ch == EOF ) { return( TRUE ); } *result += (dword)((uchar)ch) << 24; return( FALSE ); } int check_getv( value, flag ) dword *value; int flag; { int result; if ( flag ) { result = check_getl( value ); } else { result = check_getw( value ); } return( result ); } int get_checkc( offset ) dword *offset; { int dcheck; dcheck = data_check( 1 ); if ( dcheck != NORMAL && dcheck != BAD ) { return( TRUE ); /* Must be no labels or fixups */ } return( check_getc( offset ) ); } void instr_opcode( text ) char *text; { if ( pass == 3 ) { out_opcode( text ); } tab_offset = 0; inst_init(); /* Reset for next instruction */ } void instr_operand( text ) char *text; { if ( pass == 3 ) { out_operand( text ); } } int stub( byte, text, class ) uchar byte; char *text; int class; { byte = byte; /* Prevent unused variable warnings */ text = text; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ return( 0 ); } int in_out( byte, text, class ) uchar byte; char *text; int class; { int dir; int size; char temp[50]; dir = ( byte & 0x02 ) >> 1; size = ( byte & 0x01 ); if ( size == 1 && size_large ) { size = 2; } instr_opcode(text); if ( dir == 0 ) { sprintf( temp, "%s,dx", regs[size][0] ); } else { sprintf( temp, "dx,%s", regs[size][0] ); } instr_operand(temp); class = class; /* Prevent unused variable warnings */ return(1); } int string_byte( byte, text, class ) uchar byte; char *text; int class; { char opcode[10]; char operand[50]; int size; int mode; char *format; class = class; /* Prevent unused variable warnings */ size = byte & 0x01; if ( size == 1 && size_large ) { size = 2; } switch( byte ) { case 0xA4: case 0xA5: format = "%ses:[di],%s%s[si]"; mode = 0; break; case 0xA6: case 0xA7: format = "%ses:[di],%s%s[si]"; mode = 0; break; case 0xAA: case 0xAB: mode = 2; break; case 0xAC: case 0xAD: format = "%s%s[si]"; mode = 1; break; case 0xAE: case 0xAF: mode = 2; break; } strcpy( opcode, text ); if ( over_seg == -1 ) { switch( size ) { case 0: strcat(opcode,"b"); break; case 1: strcat(opcode,"w"); break; case 2: strcat(opcode,"d"); break; } instr_opcode(opcode); return(1); } else { switch( mode ) { case 0: sprintf( operand, format, sz_text[size], sz_text[size], sregsc[over_seg] ); break; case 1: sprintf( operand, format, sz_text[size], sregsc[over_seg] ); break; case 2: /* No segment overide on */ return( 0 ); /* SCAS instructions */ } instr_opcode( opcode ); instr_operand( operand ); return(1); } } int one_byte( byte, text, class ) uchar byte; char *text; int class; { char line[50]; if ( strlen(text) == 0 ) { return(0); } reg_format( class, line, text, "%s", "%s\t%s" ); instr_opcode(line); byte = byte; /* Prevent unused variable warnings */ return(1); } int two_byte( byte, text, class ) uchar byte; char *text; int class; { dword offset; char line[50]; char temp[50]; if ( get_checkc( &offset ) ) { return( 0 ); } reg_format( class, line, text, "%s\t", "%s\t%s," ); out_hexize( offset, temp, 1 ); /* Convert 1 byte into hex */ strcat( line, temp ); instr_opcode( line ); byte = byte; /* Prevent unused varaible warnings */ return(2); } int two_ubyte( byte, text, class ) uchar byte; char *text; int class; { dword offset; char line[50]; if ( get_checkc( &offset ) ) { return( 0 ); } if ( offset <= 127L ) { sprintf( line, "%s+0%02lXh", text, offset ); } else { sprintf( line, "%s-0%02lXh", text, 0x0100L - offset ); } instr_opcode( line ); byte = byte; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ return(2); } int three_byte( byte, text, class ) uchar byte; char *text; int class; { dword offset; int dummy; int data_size; int size_tmp; char line[50]; char temp[50]; if ( over_seg != -1 ) { /* No segment overide allowed */ return( 0 ); } reg_format( class, line, text, "%s\t", "%s\t%s," ); size_tmp = size_large; if ( byte == 0xC2 || byte == 0xCA ) { /* / */ size_tmp = FALSE; size_bytes = 2; } data_size = size_bytes; switch( data_check( 1 ) ) { case BAD: return( 0 ); case LABEL: return( 0 ); case FIXUP: if ( fix_rec->word_sized && fix_rec->extended == size_tmp ) { get_fix( temp, 2, TRUE, size_bytes, NEAR, FALSE, &dummy, DS ); strcat( line, temp ); adjust_assumes(); instr_opcode( line ); return( data_size+1 ); } else { return( 0 ); } case NORMAL: if ( check_getv( &offset, size_tmp ) ) { return( 0 ); } out_hexize( offset, temp, size_bytes ); strcat( line, temp ); instr_opcode(line); return( data_size+1 ); } return(0); } int five_byte( byte, text, class ) uchar byte; char *text; int class; { dword offset; dword segment; int size_known; char line[50]; char temp[50]; int data_size; if ( over_seg != -1 ) { /* No segment overide allowed */ return(0); } data_size = size_bytes; switch( data_check( 1 ) ) { case BAD: return( 0 ); case LABEL: return( 0 ); case FIXUP: if ( fix_rec->form != POINTER || fix_rec->extended != size_large ) { return( 0 ); } get_fix( temp, 1, FALSE, size_bytes, NEAR, TRUE, &size_known, CS ); sprintf( line, "far ptr %s", temp ); break; case NORMAL: if ( check_getv( &offset, size_large ) ) { return( 0 ); } if ( data_check(3) != NORMAL ) { return( 0 ); } if ( check_getw( &segment ) ) { return( 0 ); } out_hexize( segment, temp, 2 ); /* Convert 2 byte into hex */ strcpy( line, temp ); strcat( line, ":" ); out_hexize( offset, temp, size_bytes ); /* Convert 2/4 bytes into hex */ strcat( line, temp ); break; } adjust_assumes(); instr_opcode( text ); instr_operand( line ); byte = byte; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ return( 3 + data_size ); } int one_a( byte, text, class ) uchar byte; char *text; int class; { char line[50]; if ( size_large ) { reg_format( class, line, text, "%s\teax", "%s\teax,%s" ); } else { reg_format( class, line, text, "%s\tax", "%s\tax,%s" ); } instr_opcode(line); byte = byte; /* Prevent unused varaible warnings */ return(1); } int two_a( byte, text, class ) uchar byte; char *text; int class; { int size; dword offset; char line[50]; size = (byte & 0x01); if ( check_getc( &offset ) ) { return( 0 ); } if ( size == 0 ) { sprintf( line, "0%02lXh,al", offset ); } else { sprintf( line, "0%02lXh,ax", offset ); } instr_opcode( text ); instr_operand(line); byte = byte; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ return(2); } int three_a( byte, text, class ) uchar byte; char *text; int class; { int dir; int reg_size; int data_size; int type; int size_known; dword offset; char line[50]; char seg_text[10]; char temp[50]; char temp2[15]; int ref_seg; data_size = addr_bytes; dir = ( byte & 0x02 ) >> 1; reg_size = ( byte & 0x01 ); if ( reg_size == 1 && size_large ) { reg_size = 2; } type = reg_size_to_type(reg_size); if ( over_seg != -1 ) { /* Any previous segment overide? */ ref_seg = 17+over_seg; strcpy( seg_text, sregsc[over_seg] ); } else { ref_seg = DS; seg_text[0] = '\0'; } switch( data_check( 1 ) ) { case BAD: return( 0 ); case LABEL: return( 0 ); case FIXUP: if ( fix_rec->word_sized && fix_rec->extended == addr_large ) { get_fix( temp, 0, FALSE, size_bytes, type, TRUE, &size_known, ref_seg ); } else { return( 0 ); } strcpy( line, "" ); if ( compatibility == 2 ) { strcat( line, seg_text ); strcat( line, " " ); if ( !size_known ) { strcat( line, type_to_text(type) ); } } else { if ( !size_known ) { strcat( line, type_to_text(type) ); } strcat( line, seg_text ); } strcat( line, temp ); break; case NORMAL: if ( check_getv( &offset, addr_large ) ) { return( 0 ); } out_hexize( offset, temp2, addr_bytes ); /* Make hex digits */ if ( compatibility == 2 ) { sprintf( line, "%s .%s", seg_text, temp2 ); } else { if ( over_seg == -1 ) { sprintf( line, "ds:[%s]", temp2 ); } else { sprintf( line, "%s[%s]", seg_text, temp2 ); } } break; } if ( dir == 0 ) { sprintf( temp, "%s,%s", regs[reg_size][0], line ); } else { sprintf( temp, "%s,%s", line, regs[reg_size][0] ); } adjust_assumes(); instr_opcode( text ); instr_operand( temp ); class = class; /* Prevent unused variable warnings */ return( 1 + data_size ); } int enter( byte, text, class ) uchar byte; char *text; int class; { dword num_bytes; dword nest_level; char line[50]; switch( data_check(1) ) { case BAD: case LABEL: case FIXUP: return( 0 ); case NORMAL: if ( check_getw(&num_bytes) ) { return( 0 ); } if ( check_getc(&nest_level) ) { return( 0 ); } sprintf( line, "0%04lXh,0%02lXh", num_bytes, nest_level ); break; } instr_opcode( text ); instr_operand( line ); byte = byte; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ return(4); } int check_forward( inst_table, method ) INST_T inst_table[]; int method; { dword byte; int data; int valid; extern char *buff_cur; char *buff_save; buff_save = buff_cur; /* ** The next byte must be a valid instruction */ if ( get_checkc( &byte ) ) { return( 0 ); } data = (uchar)byte; #ifdef DEBUG printf("Checking forward with byte [%02X]\n", data ); #endif inst_offset += 1; if ( method == 0 && pass == 3 ) { pass = 2; /* Temporarily disable output */ valid = (*inst_table[data].rtn) ((uchar)data, inst_table[data].text, inst_table[data].special ); pass = 3; } else { valid = (*inst_table[data].rtn) ((uchar)data, inst_table[data].text, inst_table[data].special ); } inst_offset -= 1; if ( method == 0 || valid == 0 ) { buff_cur = buff_save; } return( valid ); } int seg_over( byte, text, class ) uchar byte; char *text; int class; { int valid; int save_seg; text = text; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ save_seg = over_seg; if ( byte >= 0x64 ) { over_seg = 4 + byte - 0x64; } else { over_seg = (byte & 0x18) >> 3; } valid = check_forward( instr, 1 ); if ( valid ) { return( valid+1 ); } else { over_seg = save_seg; return( 0 ); } } int opsize_over( byte, text, class ) uchar byte; char *text; int class; { int valid; int save_large; int save_bytes; byte = byte; /* Prevent unused variable warnings */ text = text; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ save_large = size_large; save_bytes = size_bytes; size_large = !size_large; /* Toggle size */ if ( size_bytes == 2 ) { size_bytes = 4; } else { size_bytes = 2; } over_opsize = TRUE; valid = check_forward( instr, 1 ); if ( valid ) { return( valid+1 ); } else { size_large = save_large; size_bytes = save_bytes; return( 0 ); } } int adrsize_over( byte, text, class ) uchar byte; char *text; int class; { int valid; int save_large; int save_bytes; byte = byte; /* Prevent unused variable warnings */ text = text; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ save_large = addr_large; save_bytes = addr_bytes; addr_large = !addr_large; /* Toggle address */ if ( addr_bytes == 2 ) { addr_bytes = 4; } else { addr_bytes = 2; } over_adrsize = TRUE; valid = check_forward( instr, 1 ); if ( valid ) { return( valid+1 ); } else { addr_large = save_large; addr_bytes = save_bytes; return( 0 ); } } int wait( byte, text, class ) uchar byte; char *text; int class; { int valid; byte = byte; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ fp_opcode[0] = '\0'; valid = check_forward( instr, 0 ); if ( !valid ) { instr_opcode(text); return(1); } else { if ( strlen(fp_opcode) == 0 ) { instr_opcode( text ); return( 1 ); } else { fp_wait = TRUE; /* Will be reset by "esc" routine */ hex_finish = FALSE; return( 1 ); } } } int prefix( byte, text, class ) uchar byte; char *text; int class; { int valid; valid = check_forward( instr, 0 ); byte = byte; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ if ( valid ) { instr_opcode( text ); tab_offset = 1; hex_finish = FALSE; return( 1 ); } else { return( 0 ); } } int extra( byte, text, class ) uchar byte; char *text; int class; { int result; result = check_forward( ex_instr, 1 ); byte = byte; /* Prevent unused variable warnings */ text = text; class = class; if ( result == 0 ) { return( 0 ); } else { return( result+1 ); } } int disp8( byte, text, class ) uchar byte; char *text; int class; { int dcheck; char line[60]; char *short_text; dword offset; dword dest; PUB_T *pub_rec; if ( over_seg != -1 ) { /* No segment overide allowed */ return( 0 ); } dcheck = data_check(1); if ( dcheck != NORMAL && dcheck != BAD ) { return( 0 ); } if ( check_getc( &offset ) ) { return( 0 ); } if ( offset == 0xFFFFFFFFL ) { /* This would generate a label in */ return( 0 ); /* the middle of the jmp statement */ } /* thereby preventing it anyway */ /* ** Special 'SHORT' for small jumps */ short_text = ""; if ( compatibility != 2 ) { if ( byte == 0xEB || segment_mode == 386 ) { short_text = "short "; } } dest = inst_offset + (signed char)offset + 2; pub_rec = check_public( 1, segment, dest, 'L' ); if ( pub_rec ) { pub_rec->type = NEAR; offset = dest - pub_rec->offset; if ( offset > 0 ) { sprintf( line, "%s%s + 0%04lXh", short_text, pub_rec->name, offset ); } else { sprintf( line, "%s%s", short_text, pub_rec->name ); } } else { if ( compatibility == 2 ) { sprintf( text, " %s.0%04lXh", short_text, offset ); } else { sprintf( text, "%s[%s:0%04lXh]", short_text, cseg_name, offset ); } } instr_opcode( text ); instr_operand( line ); byte = byte; /* Prevent unused variable warnings */ class = class; return( 2 ); } int disp16( byte, text, class ) uchar byte; char *text; int class; { dword offset; dword dest; int dummy; #if 0 int bad_size; #endif char line[60]; char *short_text; PUB_T *pub_rec; int data_size; byte = byte; /* Prevent unused variable warnings */ class=class; /* Prevent unused variable warnings */ data_size = addr_bytes; if ( over_seg != -1 ) /* No segment overide allowed */ return( 0 ); switch( data_check( 1 ) ) { case BAD: return( 0 ); case LABEL: return( 0 ); case FIXUP: if ( fix_rec->word_sized && fix_rec->extended == addr_large && fix_rec->relate == 0 ) { get_fix( line, 1, FALSE, size_bytes, NEAR, TRUE, &dummy, CS ); adjust_assumes(); instr_opcode( text ); instr_operand( line ); return( 1 + data_size ); } else { return( 0 ); } case NORMAL: if ( check_getv( &offset, addr_large ) ) { return( 0 ); } /* note: the next if actually checks for 1 byte too far in ** the negative direction. but this is ok, because it would be ** replaced by a short jmp */ if ( offset < 0x80 || offset >= 0xFFFFFF7FL ) { #if 0 bad_size = TRUE; #endif short_text = "near ptr "; } else { short_text = ""; #if 0 bad_size = FALSE; #endif } dest = inst_offset + offset + addr_bytes + 1; pub_rec = check_public( 1, segment, dest, 'L' ); if ( pub_rec ) { offset = dest - pub_rec->offset; if ( offset > 0L ) { if ( addr_large ) { sprintf( line, "%s%s + 0%08lXh", short_text, pub_rec->name, offset ); } else { sprintf( line, "%s%s + 0%04lXh", short_text, pub_rec->name, offset ); } } else { sprintf( line, "%s%s", short_text, pub_rec->name ); } } else { if ( compatibility == 2 ) { if ( addr_large ) { sprintf( line, "%s .0%08lXh", short_text, offset ); } else { sprintf( line, "%s .0%04lXh", short_text, offset ); } } else { if ( addr_large ) { sprintf( line, "%s[%s:0%08lXh]", short_text, cseg_name, offset ); } else { sprintf( line, "%s[%s:0%04lXh]", short_text, cseg_name, offset ); } } } instr_opcode( text ); instr_operand( line ); #if 0 if ( bad_size ) { if ( pass == 3 ) { out_comment( "Large jump translated into short" ); } instr_opcode( "nop" ); if ( pass == 3 ) { out_comment( "Nop inserted to retain proper length" ); } } #endif return( 1 + data_size ); } return(0); } int two_bcd( byte, text, class ) uchar byte; char *text; int class; { dword offset; byte = byte; /* Prevent unused variable warnings */ class = class; /* Prevent unused variable warnings */ if ( check_getc( &offset ) ) { return( 0 ); } if ( offset == 0x0AL ) { instr_opcode( text ); return(2); } else { return( 0 ); } } int mod0( ref_mode, sib_offset,sib_text, text, r_m, type, size_known ) int ref_mode; int sib_offset; char *sib_text; char *text; uchar r_m; int type; int *size_known; { dword offset; int result; PUB_T *pub_rec; STRUC_T *pub_struct; int ref_seg; char this_member[NAMESIZE+1+1]; /* 1 for ., 1 for \0 */ char temp[16]; this_member[0] = '\0'; if ( (r_m == 6 && !addr_large) || (r_m == 5 && addr_large) ) { if ( over_seg != -1 ) { ref_seg = 17+over_seg; } else { ref_seg = DS; } switch( data_check( 2+sib_offset ) ) { case BAD: result = -1; return( result ); case LABEL: result = -1; return( result ); case FIXUP: if ( fix_rec->word_sized && fix_rec->extended == addr_large ) { get_fix( text, 0, FALSE, size_bytes, type, TRUE, size_known, ref_seg ); if ( strlen(sib_text) ) { strcat( text, "[" ); strcat( text, sib_text ); strcat( text, "]" ); } result = addr_bytes+sib_offset; } else { result = -1; } return( result ); case NORMAL: if ( check_getv( &offset, size_large ) ) { result = -1; return( result ); } if ( add_labels ) { pub_rec = check_public( 0, 0, offset, 'S' ); } else { pub_rec = NULL; } if ( pub_rec ) { offset -= pub_rec->offset; pub_struct = pub_rec->structure; if ( pub_struct ) { find_member( this_member, pub_struct, &offset ); } if ( offset != 0 ) { out_hexize( offset, temp, size_bytes ); sprintf( text, "%s%s + %s", pub_rec->name, this_member, temp ); } else { sprintf( text, "%s%s", pub_rec->name, this_member ); } } else { out_hexize( offset, temp, size_bytes ); if ( compatibility == 2 ) { sprintf( text, " .%s", temp ); } else { if ( over_seg == -1 ) { sprintf( text, "ds:[%s", temp ); } else { sprintf( text, "[%s", temp ); } if ( strlen( sib_text ) ) { strcat( text, "+" ); strcat( text, sib_text ); } strcat( text, "]" ); } } result = addr_bytes+sib_offset; *size_known = FALSE; return( result ); } } if ( addr_large ) { strcpy( text, addr_386m[r_m] ); } else { strcpy( text, addr_mode[r_m] ); } if ( strlen( sib_text ) ) { strcat( text, "+" ); strcat( text, sib_text ); } strcat( text, "]" ); result = sib_offset; *size_known = TRUE; return( result ); } int mod1( ref_mode, sib_offset, sib_text, text, r_m ) int ref_mode; int sib_offset; char *sib_text; char *text; uchar r_m; { char temp[50]; dword offset; int result; uchar zero_rm; if ( check_getc( &offset ) ) { result = -1; } else { if ( addr_large ) { /* ** Assembler should have optimized */ zero_rm = 5; strcpy( text, addr_386m[r_m] ); } else { /* ** Assembler should have optimized */ zero_rm = 6; strcpy( text, addr_mode[r_m] ); } if ( strlen(sib_text) ) { strcat( text, "+" ); strcat( text, sib_text ); } if ( offset >= 0x80 ) { sprintf( temp, "-0%02lXh", 0x0100L-offset ); } else { if ( offset == 0L ) { if ( r_m == zero_rm ) { temp[0] = '\0'; } else { return( -1 ); } } else { sprintf( temp, "+0%02lXh", offset ); } } if ( compatibility == 2 ) { strcat( text, "]" ); strcat( text, temp ); } else { strcat( text, temp ); strcat( text, "]" ); } result = 1 + sib_offset; } return( result ); } int mod2( ref_mode, sib_offset, sib_text, text, r_m, type, size_known ) int ref_mode; int sib_offset; char *sib_text; char *text; uchar r_m; int type; int *size_known; { dword offset; int result; char temp[50]; char *sign; int ref_seg; if ( r_m == 2 || r_m == 3 ) { ref_seg = SS; /* SS relative addressing modes */ } else { ref_seg = DS; } if ( over_seg != -1 ) { ref_seg = 17+over_seg; } switch( data_check(2+sib_offset) ) { case BAD: result = -1; break; case LABEL: result = -1; break; case FIXUP: if ( fix_rec->word_sized && fix_rec->extended == addr_large ) { get_fix( temp, 0, FALSE, addr_bytes, type, TRUE, size_known, ref_seg ); if ( compatibility == 2 ) { strcpy( text, temp ); strcat( text, addr_mode[r_m] ); } else { if ( addr_large ) { strcpy( text, addr_386m[r_m] ); } else { strcpy( text, addr_mode[r_m] ); } if ( strlen(sib_text) ) { strcat( text, "+" ); strcat( text, sib_text ); } strcat( text, "+" ); strcat( text, temp ); } strcat( text, "]" ); result = addr_bytes+sib_offset; } else { result = -1; } break; case NORMAL: if ( check_getv( &offset, addr_large ) ) { result = -1; } else { if ( offset == 0x00000000L ) { return( -1 ); /* Assembler should have optimized */ } if ( addr_large ) { strcpy( text, addr_386m[r_m] ); } else { strcpy( text, addr_mode[r_m] ); } if ( strlen(sib_text) ) { strcat( text, "+" ); strcat( text, sib_text ); } if ( offset > 0x80000000L ) { offset = -offset; sign = "-"; } else { sign = "+"; } out_hexize( offset, temp, addr_bytes ); if ( compatibility == 2 ) { strcat( text, "]" ); strcat( text, sign ); strcat( text, temp ); } else { strcat( text, sign ); strcat( text, temp ); strcat( text, "]" ); } result = addr_bytes+sib_offset; *size_known = TRUE; } break; } return( result ); } int mod3( ref_mode, text, r_m, type, type_known ) int ref_mode; char *text; uchar r_m; int type; int *type_known; { switch( type ) { case BYTE_PTR: strcpy( text, regs[0][r_m] ); break; case WORD_PTR: strcpy( text, regs[1][r_m] ); break; case DWORD_PTR: strcpy( text, regs[2][r_m] ); break; default: strcpy( text, regs[3][r_m] ); break; } *type_known = TRUE; return( 0 ); } int do_sib( text, base ) char *text; uchar *base; { dword sib_byte; int ss; int idx; switch( data_check(2) ) { case LABEL: return( -1 ); case FIXUP: return( -1 ); case BAD: case NORMAL: if ( check_getc( &sib_byte ) ) { return( -1 ); } ss = (int)(sib_byte & 0xC0L) >> 6; idx = (int)(sib_byte & 0x38L) >> 3; *base = (uchar)(sib_byte & 0x07L); if ( idx == 0x04 ) { /* For special [ESP] */ if ( ss != 0 ) { return( -1 ); } else { strcpy( text, "" ); } } else { strcat( text, regs[2][idx] ); strcat( text, sib_scale[ss] ); } return( 1 ); } } int do_mod_rm( line, mod, r_m, type, size_needed, ref_mode ) char *line; uchar mod; uchar r_m; int type; int size_needed; int ref_mode; { int size_known; int sib_offset; char sib_text[50]; char temp2[50]; int result; size_known = FALSE; strcpy( sib_text, "" ); sib_offset = 0; if ( addr_large ) { if ( r_m == 4 && mod != 3 ) { /* Get result and new r_m */ sib_offset = do_sib( sib_text, &r_m ); if ( sib_offset == -1 ) { return( -1 ); } } } switch( mod ) { case 0: result = mod0( ref_mode, sib_offset, sib_text, temp2, r_m, type, &size_known ); break; case 1: result = mod1( ref_mode, sib_offset, sib_text, temp2, r_m ); size_known = TRUE; break; case 2: result = mod2( ref_mode, sib_offset, sib_text, temp2, r_m, type, &size_known ); break; case 3: result = mod3( ref_mode, temp2, r_m, type, &size_known ); size_needed = FALSE; break; } if ( result != -1 ) { if ( compatibility == 2 ) { if ( over_seg != -1 ) { /* Any previous segment overide? */ strcat( line, sregsc[over_seg] ); strcat( line, " " ); } if ( !size_known || size_needed ) { strcat( line, type_to_text(type) ); } } else { /* ** If the size is already known, then we don't need 'xxxx' ptr text */ if ( !size_known || size_needed ) { strcat( line, type_to_text(type) ); } if ( over_seg != -1 ) { /* Any previous segment overide? */ strcat( line, sregsc[over_seg] ); } } strcat( line, temp2 ); } return( result ); } int byte_immed( text, additional, sign ) char *text; int additional; int sign; { int dcheck; dword offset; dcheck = data_check( additional+2 ); switch( dcheck ) { case LABEL: return( 1 ); case FIXUP: return( 1 ); case BAD: case NORMAL: if ( check_getc( &offset ) ) { return( 1 ); } if ( sign ) { if ( offset <= 0x7F ) { sprintf( text, "+0%02lXh", offset ); } else { sprintf( text, "-0%02lXh", 0x0100L-offset ); } } else { out_hexize( offset, text, 1 ); } break; } return( 0 ); } int word_immed( text, additional ) char *text; int additional; { int dcheck; int dummy; dword offset; dcheck = data_check( additional+2 ); switch( dcheck ) { case LABEL: return( 1 ); case FIXUP: if ( fix_rec->word_sized && fix_rec->extended == addr_large ) { get_fix( text, 2, TRUE, size_bytes, NEAR, FALSE, &dummy, DS ); } else { return( 1 ); } break; case BAD: return( 1 ); case NORMAL: if ( check_getv( &offset, size_large ) ) { return( 1 ); } if ( size_large ) { out_hexize( offset, text, 4 ); } else { out_hexize( offset, text, 2 ); } break; } return( 0 ); } int mod_reg( byte, text, class ) uchar byte; char *text; int class; { char *opcode; int dir; int size; int reg_size; int size_needed; int type; int method; int group; dword mod_reg; uchar mod; uchar r_m; uchar reg; int additional; char mem_text[50]; char reg_text[50]; char third_text[50]; char operands[80]; int ref_mode; if ( get_checkc( &mod_reg ) ) { return( 0 ); } mod = (uchar)(((int)mod_reg & 0xC0) >> 6); reg = (uchar)(((int)mod_reg & 0x38) >> 3); r_m = (uchar)(((int)mod_reg & 0x07) >> 0); dir = modrm_class[class].dir; size = modrm_class[class].size; method = modrm_class[class].method; group = modrm_class[class].group; size_needed = modrm_class[class].size_needed; type = modrm_class[class].type; ref_mode = 0; if ( group == 6 ) { if ( mod == 3 ) { if ( reg == 0 || reg == 1 || reg == 2 || reg == 3 ) { addr_large = FALSE; /* 286 only instructions */ size_large = FALSE; } } type = WORD_PTR; size_needed = TRUE; } reg_size = size; /* 386 Segments effect register names */ if ( reg_size == 1 && size_large ) { reg_size = 2; } if ( type == UNKNOWN ) { type = reg_size_to_type( reg_size ); } if ( group == 0 ) { opcode = text; } else { opcode = op_grp[group-1][reg]; } if ( strlen(opcode) == 0 ) { return( 0 ); } if ( class == 26 ) { reg_size = 1; } if ( byte == 0x62 || byte == 0x63 ) { if ( byte == 0x63 ) { type = WORD_PTR; } size_needed = TRUE; /* LAR, LSL, BOUND, ARPL need size */ } if ( byte == 0xC4 || byte == 0xC5 || byte == 0xB2 || byte == 0xB4 || byte == 0xB5 ) { size_needed = TRUE; } if ( group == 3 ) { /* Group 3 has a test instruction which */ if ( reg == 0 ) { /* needs an immediate value (byte or word) */ method = 5 + size; /* the rest of group 3 needs nothing */ dir = 0; size_needed = TRUE; } else { method = 0; if ( reg == 2 || reg == 3 ) { size_needed = FALSE; } else { size_needed = TRUE; } } } if ( group == 5 ) { /* Group 5 has call far and jump far */ if ( reg == 3 || reg == 5 ) { /* instructions which are dwords */ /* the rest of group 5 is words */ if ( size_large ) { type = FWORD_PTR; } else { type = DWORD_PTR; } size_needed = TRUE; } else { if ( reg == 0 || reg == 1 ) { size_needed = TRUE; } } } if ( group == 7 ) { /* Group 7 has mostly FWORD sized */ if ( reg == 4 || reg == 6 ) { /* EAs, but these two are WORD sized */ type = WORD_PTR; } } if ( dir == 0 && mod == 3 && (class == 0 || class == 1) ) { /* ** Ambiguous! eg. OBJ 20F8 = and al,bh => MASM 22C7 ** This is because MASM prefers the first operand to be ** a register and the other operand to be a mem/reg. */ return(0); } mem_text[0] = '\0'; additional = do_mod_rm( mem_text, mod, r_m, type, size_needed, ref_mode ); if ( additional == -1 ) { return( 0 ); } switch( method ) { case 0: /* No additional information (one operand only) */ break; case 1: /* Other operand is a register */ /* LES,LDS,BOUND,LSS,LFS,LGS, ARPL instructions */ /* revert to Word */ if ( byte == 0xC4 || byte == 0xC5 || byte == 0x62 || byte == 0x63 || byte == 0xB2 || byte == 0xB4 || byte == 0xB5 ) { reg_size = 1; } strcpy( reg_text, regs[reg_size][reg] ); break; case 2: /* Other operand is a segment register */ if ( reg > 5 ) { /* 0=ES,1=CS,2=SS,3=DS,4=FS,5=GS */ return( 0 ); } strcpy( reg_text, sregs[reg] ); break; case 3: /* Other operand is 1 */ strcpy( reg_text, "1" ); break; case 4: /* Other operand is register CL */ strcpy( reg_text, "cl" ); break; case 5: /* Immediate byte value */ if ( byte_immed(reg_text, additional,FALSE) ) { return( 0 ); } additional++; break; case 6: /* Immediate word value (could be fixupp) */ if ( word_immed(reg_text, additional) ) { return( 0 ); } if ( size_large ) { additional += 4; } else { additional += 2; } break; case 7: /* Immediate signed byte value */ if ( byte_immed(reg_text,additional,TRUE) ) { return( 0 ); } additional++; break; case 8: /* IMUL (3 operands [reg,mem,immed word]) */ strcpy( reg_text, regs[reg_size][reg] ); if ( word_immed(third_text, additional) ) { return(0); } additional += 2; break; case 9: /* IMUL (3 operands [reg,mem,immed signed byte]) */ strcpy( reg_text, regs[reg_size][reg] ); if ( byte_immed(third_text, additional,TRUE) ) { return(0); } additional += 1; break; case 10: /* Control Register (CR0...) */ strcpy( reg_text, cr_regs[reg] ); break; case 11: /* Debug Register (DR0...) */ strcpy( reg_text, dr_regs[reg] ); break; case 12: strcpy( reg_text, tr_regs[reg] ); break; case 13: strcpy( reg_text, regs[reg_size][reg] ); strcpy( third_text, "cl" ); break; } if ( dir & 0x01 ) { sprintf( operands, dir_fmt[dir], reg_text, mem_text, third_text ); } else { sprintf( operands, dir_fmt[dir], mem_text, reg_text, third_text ); } adjust_assumes(); instr_opcode( opcode ); instr_operand( operands ); return( 2+additional ); } int esc( byte, text, class ) uchar byte; char *text; int class; { int size; int dir; int type; dword mod_reg; uchar mod; uchar r_m; uchar lll; /* Described as LLL in 80386 Prog Ref Man. */ uchar ttt; /* Described as TTT in 80386 Prog Ref Man. */ uchar esc_byte; uchar mf; int additional; int size_needed; char line[50]; char temp[50]; int ref_mode; text = text; /* Prevent unused variable warnings */ class=class; /* Prevent unused variable warnings */ size = (byte & 0x01); dir = (byte & 0x02) >> 1; ref_mode = 0; if ( get_checkc( &mod_reg ) ) { return( 0 ); } mod = (uchar)(((int)mod_reg & 0xC0) >> 6); lll = (uchar)(((int)mod_reg & 0x38) >> 3); r_m = (uchar)(((int)mod_reg & 0x07) >> 0); ttt = (uchar)(byte & 0x07); esc_byte = (uchar)(ttt << 3) + lll; if ( mod == 0x03 ) { esc_special( temp, line, esc_byte, r_m ); additional = 0; } else { mf = (uchar)((esc_byte & 0x30) >> 4); size_needed = TRUE; switch( mf ) { case 0: type = DWORD_PTR; break; case 1: type = DWORD_PTR; break; case 2: type = QWORD_PTR; break; case 3: type = WORD_PTR; break; } /* ** There are always exceptions to the rule (Thank you, Intel) */ if ( esc_byte == 0x1D || esc_byte == 0x1F || esc_byte == 0x3C || esc_byte == 0x3E ) { type = TBYTE_PTR; } if ( esc_byte == 0x2F ) { type = WORD_PTR; } if ( esc_byte == 0x3D || esc_byte == 0x3F ) { type = DWORD_PTR; } if ( (esc_byte >= 0x0C && esc_byte <= 0x0F) || (esc_byte >= 0x2C && esc_byte <= 0x2E) ) { size_needed = FALSE; } sprintf( line, "" ); additional = do_mod_rm( line, mod, r_m, type, size_needed, ref_mode ); if ( additional == -1 ) { /* Error in 'do_mod_rm' */ return( 0 ); } strcpy( temp, esc_inst[esc_byte] ); } if ( strlen( temp ) ) { if ( temp[1] == 'n' && fp_wait ) { fp_opcode[0] = temp[0]; /* Remove the 'n' */ strcpy( &fp_opcode[1], &temp[2] ); } else { strcpy( fp_opcode, temp ); } instr_opcode( fp_opcode ); instr_operand( line ); } else { sprintf( line, "0%02Xh,%s", esc_byte, regs[1][r_m] ); instr_opcode( "esc" ); instr_operand( line ); } fp_wait = FALSE; /* Reset always */ dir = dir; size = size; return( 2+additional ); }