#include <stdio.h>
#include <string.h>
#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 ) {       /* <RET jjkk>/<RETF jjkk> */
        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 );
}
