/*static char *SCCSID = "@(#)debdasm.c  6.1 90/11/17";*/
/*static char *SCCSID = "@(#)debdasm.c  13.19 90/03/23";*/

/*
 *  DEBDASM.C
 *
 *
 *  Intel 80386 Disassembler
 *
 *  Copyright (C) Microsoft Corp 1986
 *
 *  Author:     Pete Stewart
 *  Date:       November 11, 1986
 *
 */

#include "debdef.h"

#define MAXINST         16
#define MAXOP           3
#define MAXOPLEN        10

#define OPCOLUMN        30
#define BPCOLUMN        55
#define MAXCOLUMN       79      /* Can't use the 80 col on most terms */

#define OP0F            0x0F
#define OPESCLO         0xD8
#define OPESCHI         0xDF

#define verifytype(x,y)
#define modrm_mod(x)    (((x) >> 6) & 3)
#define modrm_reg(x)    (((x) >> 3) & 7)
#define modrm_rm(x)     ((x) & 7)
#define sib_base(x)     ((x) & 7)
#define sib_index(x)    (((x) >> 3) & 7)
#define sib_scale(x)    (((x) >> 6) & 3)
#define fpregi(op,modrm) ((((op) - OPESCLO) << 3) + modrm_reg(modrm))

#define REG_AX          0
#define REG_CX          1
#define REG_DX          2
#define REG_BX          3
#define REG_SP          4
#define REG_BP          5
#define REG_SI          6
#define REG_DI          7

#define SIB_BASE_BP     REG_BP
#define SIB_BASE_SP     REG_SP
#define SIB_NOINDEX     REG_SP

#define D0RM_BASE_BXSI  0
#define D0RM_BASE_BXDI  1
#define D0RM_BASE_BPSI  2
#define D0RM_BASE_BPDI  3
#define D0RM_BASE_SI    4
#define D0RM_BASE_DI    5
#define D0RM_BASE_BP    6
#define D0RM_BASE_BX    7
#define D1RM_BASE_BP    REG_BP
#define D1RM_SIB        REG_SP

#define MOD_NODISP      0
#define MOD_BYTEDISP    1
#define MOD_WORDDISP    2
#define MOD_REG         3

#define PFX_SEGES       0x26            /* ES override */
#define PFX_SEGCS       0x2E            /* CS override */
#define PFX_SEGSS       0x36            /* SS override */
#define PFX_SEGDS       0x3E            /* DS override */
#define PFX_SEGFS       0x64            /* FS override */
#define PFX_SEGGS       0x65            /* GS override */

#define SEGES           0               /* Segment num/index into segreg[]*/
#define SEGCS           1
#define SEGSS           2
#define SEGDS           3
#define SEGFS           4
#define SEGGS           5
#define SEGDEF          8               /* Default segment number */
#define NOSEG           0xffff          /* No segment value */

#define OPSIZE          0x66            /* Operand size prefix */
#define OPADDR          0x67            /* Operand address prefix */
#define LOCK            0xF0            /* Bus lock prefix */
#define REPNE           0xF2            /* Repeat prefix */
#define REPE            0xF3            /* Repeat prefix */

/* Operand size/type definitions */

#define NOSIZEOP        0x0000          /* no size (for LEA) */
#define BYTEOP          0x0001          /* 8 bits,  1 byte */
#define WORDOP          0x0002          /* 16 bits, 2 bytes */
#define DWORDOP         0x0004          /* 32 bits, 4 bytes */
#define FWORDOP         0x0006          /* 48 bits, 6 bytes */
#define QWORDOP         0x0008          /* 64 bits, 8 bytes */
#define TBYTEOP         0x000A          /* 80 bits, 10 bytes */
#define OPSIZEMASK      0x000F          /* Opcode size mask */

#define OPER32          0x0010          /* 32-bit operands */
#define ADDR32          0x0020          /* 32-bit addresses */
#define VPTR            0x0040          /* virtual pointer, 32 or 48 bits */
#define VINT            0x0080          /* virtual int, 16 or 32 bits */
#define VINTREG         0x4000          /* virtual int if register operand */
#define POPESP          0x8000          /* Could be "POP [ESP+disp]" */

#define VSIZE           2               /* size increase for virtual guys */

typedef struct opinfo {
        byte            op_code;        /* Opcode table index */
        byte            op_fmt;         /* Instr format table index */
} OPINFO;

extern int getsymbuf(char *, AddrS *, int);/* Get symbol from pointer */
extern word getdbit(AddrS *);
extern int checkbp(AddrS *);               /* Do breakpoint check */
extern dword regval(word);                 /* Return register value */
extern word segregval(word);               /* Return register value */
extern int getmemc(AddrS *, byte *, int, int *); /* Get user memory */
extern int getbuffc(AddrS *, byte *, int);       /* Get user memory */
extern char *strcpy(char *, const char *);
extern char *strcpylc(char *, const char *);
extern unsigned int strlen(const char *);
extern int sprintf(char *, const char *, ...);
extern int sprintflc(char *, const char *, ...);
extern void lowercase(char *);
extern char *geterrmessage(int);        /* Return error message string */
extern word GetCurrTask(void);

int disasm(char *, AddrS *);
void decodeop(char *);
static void addtxt(char *, char *, int);
static int decodeinst(char *, byte *, AddrS *);
static void decodereg(char *, byte);
static void decodememop(char *, word, word);
static byte *decodemodrm(char *, byte *, byte, word);
static char *decodetypeseg(char *, word);
static byte *decodesib(char *, byte *, byte, byte);
static void decodesignedbyte(char *, char);
static void decodeimmed(char *, dword, word);
static void decodedisp(char *, word, long, word);
static void decodeaddr(char *, byte, word, word, dword, bool, word);
void fmtaddr(char *, AddrS *, word);
static int d0base(char *, word);
static int d1base(char *, word);
static byte map0Fop(byte);

extern byte DefType;                    /* Current client mode (lowbyte only) */
extern struct DebugInit DebData;        /* Debugger configuration struct */

static int opcnt = 0;                   /* Operand count */
static AddrS opaddrs[MAXOP];            /* Operand address */
static word optyps[MAXOP];              /* Operand type flags */
static word opsegs[MAXOP];              /* Operand segment number */

static long opdisp;                     /* Operand displacement */
static dword opoff;                     /* Operand base offset */
static word opseg;                      /* Memory operand segment prefix */
static word optyp;                      /* Operand type flag */
static byte commentchar = 0;            /* Comment character (0 = none) */

static char *opcodes[] =
{
        NULL,           /* 0 */
        "ADD",
        "PUSH    ES",
        "POP     ES",
        "OR",
        "PUSH    CS",
        "ADC",
        "PUSH    SS",
        "POP     SS",
        "SBB",

        "PUSH    DS",   /* 10 */
        "POP     DS",
        "AND",
        "LLDT",
        "DAA",
        "SUB",
        "LTR",
        "DAS",
        "XOR",
        "VERR",

        "AAA",          /* 20 */
        "CMP",
        "VERW",
        "AAS",
        "INC",
        "DEC",
        "PUSH",
        "POP",
        "*PUSHA\0PUSHAD",
        "*POPA\0POPAD",

        "BOUND",        /* 30 */
        "ARPL",
        "SGDT",
        "SIDT",
        "LGDT",
        "LIDT",
        "IMUL",
        "INS",
        "LSS",
        "OUTS",

        "LFS",          /* 40 */
        "JO",
        "JNO",
        "JC",
        "JNC",
        "JZ",
        "JNZ",
        "JBE",
        "JA",
        "JS",

        "JNS",          /* 50 */
        "JP",
        "JNP",
        "JL",
        "JGE",
        "JLE",
        "JG",
        "TEST",
        "XCHG",
        "MOV",

        "LEA",          /* 60 */
        "NOP",
        "*CBW\0CWDE",
        "*CWD\0CDQ",
        "CALL",
        "WAIT",
        "*PUSHF\0PUSHFD",
        "*POPF\0POPFD",
        "SAHF",
        "LAHF",

        "MOVS",         /* 70 */
        "LGS",
        "CMPS",
        "MOVZX",
        "STOS",
        "BSF",
        "LODS",
        "BSR",
        "SCAS",
        "LOADALL",

        "*RET\0RETD",   /* 80 */
        "LES",
        "LDS",
        "ENTER",
        "LEAVE",
        "*RETF\0RETFD",
        "INT     3",
        "INT",
        "INTO",
        "*IRET\0IRETD",

        "AAM",          /* 90 */
        "AAD",
        "SLDT",
        "XLAT",
        "LOOPNZ",
        "LOOPZ",
        "LOOP",
        "+JCXZ\0JECXZ",
        "IN",
        "OUT",

        "JMP",          /* 100 */
        "BT",
        "BTS",
        "BTR",
        "BTC",
        "MOVSX",
        "STR",
        "SMSW",
        "LMSW",
        "HLT",

        "CMC",          /* 110 */
        "CLC",
        "STC",
        "CLI",
        "STI",
        "CLD",
        "STD",
        "ROL",
        "ROR",
        "RCL",

        "RCR",          /* 120 */
        "SHL",
        "SHR",
        "SAR",
        "NOT",
        "NEG",
        "MUL",
        "DIV",
        "IDIV",
        "LAR",

        "LSL",          /* 130 */
        "CLTS",
        "SETO",
        "SETNO",
        "SETB",
        "SETAE",
        "SETE",
        "SETNE",
        "SETBE",
        "SETA",

        "SETS",         /* 140 */
        "SETNS",
        "SETP",
        "SETNP",
        "SETL",
        "SETGE",
        "SETLE",
        "SETG",
        "PUSH    FS",
        "POP     FS",

        "SHLD",         /* 150 */
        "PUSH    GS",
        "POP     GS",
        "SHRD",
        "FBC     AL",
        "FADD",
        "FMUL",
        "FCOM",
        "FCOMP",
        "FSUB",

        "FSUBR",        /* 160 */
        "FDIV",
        "FDIVR",
        "FLD",
        "FST",
        "FSTP",
        "FLDENV",
        "FLDCW",
        "FSTENV",
        "FSTCW",

        "FIADD",        /* 170 */
        "FIMUL",
        "FICOM",
        "FICOMP",
        "FISUB",
        "FISUBR",
        "FIDIV",
        "FIDIVR",
        "FILD",
        "FIST",

        "FISTP",        /* 180 */
        "FRSTOR",
        "FSAVE",
        "FSTSW",
        "FBLD",
        "FBSTP",
        "FXCH",
        "FFREE",
        "FADDP",
        "FMULP",

        "FSUBP",        /* 190 */
        "FSUBRP",
        "FDIVP",
        "FDIVRP",
        "FFREEP",
        "FNOP",
        "FCHS",
        "FABS",
        "FTST",
        "FXAM",

        "FLD1",         /* 200 */
        "FLDL2T",
        "FLDL2E",
        "FLDPI",
        "FLDLG2",
        "FLDLN2",
        "FLDZ",
        "F2XM1",
        "FYL2X",
        "FPTAN",

        "FPATAN",       /* 210 */
        "FXTRACT",
        "FDECSTP",
        "FINCSTP",
        "FPREM",
        "FYL2XP1",
        "FSQRT",
        "FRNDINT",
        "FSCALE",
        "FENI",

        "FDISI",        /* 220 */
        "FCLEX",
        "FINIT",
        "FSETPM",
        "FCOMPP",
        "FSTSW   AX",
        "SVC",          /* 226 */
};



/*
 *  The following table contains the instruction dope vectors used
 *  to decode the operands of an instruction once the opcode has
 *  been identified.  The format used is the same as the format used
 *  to describe the instructions in Appendix A of the Intel document,
 *  "The 80386 Programmer's Reference Manual."  Briefly, each operand
 *  is represented by a two-character code.  The first character
 *  specifies how the operand is found; the second specifies the type
 *  of the operand.  The code letters are summarized below:
 *
 *  A - Operand is an address immediately following the opcode.
 *  C - Operand is a control register specified by the REG field
 *      of the Mod-REG-R/M byte.
 *  D - Operand is a debug register specified by the REG field
 *      of the Mod-REG-R/M byte.
 *  E - Operand is specified by the Mod and R/M fields of the
 *      Mod-REG-R/M byte.
 *  F - Operand is a floating point register specified by the
 *      R/M field of the Mod-REG-R/M byte.
 *  G - Operand is the general register selected by the REG field
 *      of the Mod-REG-R/M byte.
 *  I - Operand is immediate.
 *  J - Operand is an (E)IP-relative offset following the opcode.
 *  M - Like E, except operand must be in memory.
 *  O - Operand is at the location specified by an immediate offset.
 *  P - Operand is specified by the Mod and R/M fields of the
 *      Mod-REG-R/M byte.  The base register is ESP and the instruction
 *      is POP.  ESP is incremented by four *BEFORE* the operand address
 *      is computed.  This is a *feature* (ha!), not a bug.  It requires
 *      special casing.
 *  R - Like E, except operand must be a general register.
 *  S - Operand is the segment register selected by the REG field
 *      of the Mod-REG-R/M byte.
 *  T - Operand is a TLB register specified by the REG field
 *      of the Mod-REG-R/M byte.
 *  X - Memory operand nominally addressed by DS:(E)SI, but
 *      the segment may be overriden.
 *  Y - Memory operand addressed by ES:(E)DI.
 *  Z - Memory operand nominally addressed by DS:(E)BX, but
 *      the segment may be overriden.
 *  0 - AL, AX, or EAX depending upon the type specifier.
 *  1 - CL, CX, or ECX depending upon the type specifier.
 *  2 - DL, DX, or EDX depending upon the type specifier.
 *  3 - BL, BX, or EBX depending upon the type specifier.
 *  4 - AH, SP, or ESP depending upon the type specifier.
 *  5 - CH, BP, or EBP depending upon the type specifier.
 *  6 - DH, SI, or ESI depending upon the type specifier.
 *  7 - BH, DI, or EDI depending upon the type specifier.
 *  # - Operand is implicit.  The type specifier is an index
 *      into the table of implicit operand strings.
 *
 *  These are the type specifiers:
 *
 *  a - Operand is an unsigned byte.
 *  b - Operand is an unsigned byte - comment character is printed.
 *  c - Operand is a signed byte - comment character is printed.
 *  d - Operand is a DWORD.
 *  f - Operand is a floating point register.
 *  p - Operand is a pointer.  The size of the offset portion depends
 *      upon the current operand size attribute.
 *  q - Operand is a QWORD.
 *  s - Operand is six contiguous bytes of memory.
 *  t - Operand is a 10-byte temp. real
 *  v - Operand is a WORD or a DWORD depending upon the current
 *      operand size attribute.
 *  w - Operand is a WORD.
 *  x - There is no size operand (for LEA).
 *  y - If the operand is a register, then the operand is a WORD or a
 *      DWORD depending upon the current operand size attribute.  If the
 *      operand is a memory location, then it is a WORD.  This type
 *      should be used only with the "mov op,segreg" instructions.
 */

static char *opfmts[] =
{
        NULL,           /* 0 */
        "EbGb",         /* 1 */
        "EvGv",         /* 2 */
        "GbEb",         /* 3 */
        "GvEv",         /* 4 */
        "0bIb",         /* 5 */
        "0vIv",         /* 6 */
        "0v",           /* 7 */
        "1v",           /* 8 */
        "2v",           /* 9 */

        "3v",           /* 10 */
        "4v",
        "5v",
        "6v",
        "7v",
        "GvMv",         /* BOUND instruction */
        "EwGw",
        "Iw",
        "GvEvIv",
        "Ia",           /* 19, INT */

        "GvEvIb",       /* 20 */
        "Jb",           /* 21 */
        "EySw",         /* 22 */
        "GvMx",         /* 23 */
        "SwEw",
        "Ev",           /* 25 */
        "0v1v",
        "0v2v",
        "0v3v",
        "0v4v",

        "0v5v",         /* 30 */
        "0v6v",
        "0v7v",
        "Ap",
        "0bOb",
        "0vOv",
        "Ob0b",
        "Ov0v",
        "1bIb",
        "2bIb",

        "3bIb",         /* 40 */
        "4bIb",
        "5bIb",
        "6bIb",
        "7bIb",
        "1vIv",
        "2vIv",
        "3vIv",
        "4vIv",
        "5vIv",

        "6vIv",         /* 50 */
        "7vIv",
        "GvMp",
        "EbIb",
        "EvIv",
        "IwIa",         /* 55, ENTER 0000,00 */
        "0vIa",         /* 56, IN [E]AX,00  */
        "Ia0b",         /* 57, OUT 00,AL */
        "Ia0v",         /* 58, OUT 00,[E]AX */
        "Ms",

        "Jv",           /* 60 */
        "EvIb",
        "Eb#\001",
        "Ev#\001",
        "Eb1b",
        "Ev1b",
        "Eb",
        "Mp",
        "GvEw",
        "CdRd",

        "DdRd",         /* 70 */
        "RdCd",
        "RdDd",
        "TdRd",
        "RdTd",
        "0b2w",
        "0v2w",
        "2w0b",
        "2w0v",
        "EvGvIb",

        "EvGv1b",       /* 80 */
        "GvEb",         /* 81 */
        "Ew",           /* 82 */
        "EvIc",         /* 83 */
        "Yb",           /* 84 386 LOADALL */
        "Yb2w",         /* 85 */
        "Yv2w",         /* 86 */
        "2wXb",         /* 87 */
        "2wXv",         /* 88 */
        "YbXb",         /* 89 */

        "YvXv",         /* 90 */
        "XbYb",         /* 91 */
        "XvYv",         /* 92 */
        "Yb0b",         /* 93 */
        "Yv0v",         /* 94 */
        "0bXb",         /* 95 */
        "0vXv",         /* 96 */
        "0bYb",         /* 97 */
        "0vYv",         /* 98 */
        "Zb",

        "Ic",           /* 100 */
        "Md",
        "Mw",
        "Mb",
        "Mq",
        "Mt",
        "#\002Ff",
        "Ff",
        "Ff#\002",
        "Iv",

        "0bIa",         /* 110, IN AL,00 */
        "Pv"            /* 111, POP [ESP+displacement] */
};


static char *implicitops[] =
{
        NULL,           /* 0 */
        "1",
        "ST"
};


/*
 *  The  following  table,  optab,  maps  a one-byte opcode to a
 *  struct  containing  indices  into  the opcodes table and the
 *  opfmts  table.  If the opcodes index is non-zero, the opcode
 *  is  fully  specified  by  the  first  byte.  However, if the
 *  opcodes index is zero, then the REG field of the Mod-REG-R/M
 *  byte is used to contain three additional bits of opcode.  In
 *  this  case,  the  opfmts  index  is  actually  an index into
 *  extoptab, and the REG field selects an entry in the table to
 *  which  the  entry  in  extoptab  points.   An entry in these
 *  extended  opcode  tables  has the same format as an entry in
 *  optab.   A  zero  opfmts index with a non-zero opcodes index
 *  indicates an instruction having no explicit operands.
 */

static OPINFO optab[] =
{
        { 1,    1 },    /* 0 */
        { 1,    2 },
        { 1,    3 },
        { 1,    4 },
        { 1,    5 },
        { 1,    6 },
        { 2,    0 },
        { 3,    0 },
        { 4,    1 },
        { 4,    2 },
        { 4,    3 },    /* 10 */
        { 4,    4 },
        { 4,    5 },
        { 4,    6 },
        { 5,    0 },
        { 0,    0 },
        { 6,    1 },
        { 6,    2 },
        { 6,    3 },
        { 6,    4 },
        { 6,    5 },    /* 20 */
        { 6,    6 },
        { 7,    0 },
        { 8,    0 },
        { 9,    1 },
        { 9,    2 },
        { 9,    3 },
        { 9,    4 },
        { 9,    5 },
        { 9,    6 },
        { 10,   0 },    /* 30 */
        { 11,   0 },
        { 12,   1 },
        { 12,   2 },
        { 12,   3 },
        { 12,   4 },
        { 12,   5 },
        { 12,   6 },
        { 0,    0 },
        { 14,   0 },
        { 15,   1 },    /* 40 */
        { 15,   2 },
        { 15,   3 },
        { 15,   4 },
        { 15,   5 },
        { 15,   6 },
        { 0,    0 },
        { 17,   0 },
        { 18,   1 },
        { 18,   2 },
        { 18,   3 },    /* 50 */
        { 18,   4 },
        { 18,   5 },
        { 18,   6 },
        { 0,    0 },
        { 20,   0 },
        { 21,   1 },
        { 21,   2 },
        { 21,   3 },
        { 21,   4 },
        { 21,   5 },    /* 60 */
        { 21,   6 },
        { 0,    0 },
        { 23,   0 },
        { 24,   7 },
        { 24,   8 },
        { 24,   9 },
        { 24,   10 },
        { 24,   11 },
        { 24,   12 },
        { 24,   13 },   /* 70 */
        { 24,   14 },
        { 25,   7 },
        { 25,   8 },
        { 25,   9 },
        { 25,   10 },
        { 25,   11 },
        { 25,   12 },
        { 25,   13 },
        { 25,   14 },
        { 26,   7 },    /* 80 */
        { 26,   8 },
        { 26,   9 },
        { 26,   10 },
        { 26,   11 },
        { 26,   12 },
        { 26,   13 },
        { 26,   14 },
        { 27,   7 },
        { 27,   8 },
        { 27,   9 },    /* 90 */
        { 27,   10 },
        { 27,   11 },
        { 27,   12 },
        { 27,   13 },
        { 27,   14 },
        { 28,   0 },
        { 29,   0 },
        { 30,   15 },
        { 31,   16 },
        { 0,    0 },    /* 100 */
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 26,   109 },
        { 36,   18 },
        { 26,   100 },
        { 36,   20 },
        { 37,   85 },
        { 37,   86 },
        { 39,   87 },   /* 110 */
        { 39,   88 },
        { 41,   21 },
        { 42,   21 },
        { 43,   21 },
        { 44,   21 },
        { 45,   21 },
        { 46,   21 },
        { 47,   21 },
        { 48,   21 },
        { 49,   21 },   /* 120 */
        { 50,   21 },
        { 51,   21 },
        { 52,   21 },
        { 53,   21 },
        { 54,   21 },
        { 55,   21 },
        { 56,   21 },
        { 0,    1 },
        { 0,    2 },
        { 0,    1 },    /* 130 */
        { 0,    3 },
        { 57,   1 },
        { 57,   2 },
        { 58,   1 },
        { 58,   2 },
        { 59,   1 },
        { 59,   2 },
        { 59,   3 },
        { 59,   4 },
        { 59,   22 },   /* 140 */
        { 60,   23 },
        { 59,   24 },
        { 27,   111 },  /* POP into memory */
        { 61,   0 },
        { 58,   26 },
        { 58,   27 },
        { 58,   28 },
        { 58,   29 },
        { 58,   30 },
        { 58,   31 },   /* 150 */
        { 58,   32 },
        { 62,   0 },
        { 63,   0 },
        { 64,   33 },
        { 65,   0 },
        { 66,   0 },
        { 67,   0 },
        { 68,   0 },
        { 69,   0 },
        { 59,   34 },   /* 160 */
        { 59,   35 },
        { 59,   36 },
        { 59,   37 },
        { 70,   89 },
        { 70,   90 },
        { 72,   91 },
        { 72,   92 },
        { 57,   5 },
        { 57,   6 },
        { 74,   93 },   /* 170 */
        { 74,   94 },
        { 76,   95 },
        { 76,   96 },
        { 78,   97 },
        { 78,   98 },
        { 59,   5 },
        { 59,   38 },
        { 59,   39 },
        { 59,   40 },
        { 59,   41 },   /* 180 */
        { 59,   42 },
        { 59,   43 },
        { 59,   44 },
        { 59,   6 },
        { 59,   45 },
        { 59,   46 },
        { 59,   47 },
        { 59,   48 },
        { 59,   49 },
        { 59,   50 },   /* 190 */
        { 59,   51 },
        { 0,    4 },
        { 0,    5 },
        { 80,   17 },
        { 80,   0 },
        { 81,   52 },
        { 82,   52 },
        { 59,   53 },
        { 59,   54 },
        { 83,   55 },   /* 200 */
        { 84,   0 },
        { 85,   17 },
        { 85,   0 },
        { 86,   0 },
        { 87,   19 },
        { 88,   0 },
        { 89,   0 },
        { 0,    6 },
        { 0,    7 },
        { 0,    8 },    /* 210 */
        { 0,    9 },
        { 90,   0 },
        { 91,   0 },
        { 154,  0 },
        { 93,   99 },
        { 0,    10 },
        { 0,    11 },
        { 0,    12 },
        { 0,    13 },
        { 0,    14 },   /* 220 */
        { 0,    15 },
        { 0,    16 },
        { 0,    17 },
        { 94,   21 },
        { 95,   21 },
        { 96,   21 },
        { 97,   21 },
        { 98,   110 },
        { 98,   56 },
        { 99,   57 },   /* 230 */
        { 99,   58 },
        { 64,   60 },
        { 100,  60 },
        { 100,  33 },
        { 100,  21 },
        { 98,   75 },
        { 98,   76 },
        { 99,   77 },
        { 99,   78 },
        { 0,    0 },    /* 240 */
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 109,  0 },
        { 110,  0 },
        { 0,    18 },
        { 0,    19 },
        { 111,  0 },
        { 112,  0 },
        { 113,  0 },    /* 250 */
        { 114,  0 },
        { 115,  0 },
        { 116,  0 },
        { 0,    20 },
        { 0,    21 }    /* 255 */
};



/*
 *  The  following  table,  op0Ftab,  is  very  much like optab.
 *  However, since the table of two-byte opcodes is very sparse,
 *  this  table  is not indexed directly by the second byte of a
 *  two-byte  opcode.   Rather,  a  binary search for the second
 *  byte  is  done  in  the  table  op0Fmap.   If  the search is
 *  successful,  then  the index into op0Fmap serves as an index
 *  into  this  table.   Once  an index into this table has been
 *  found,  then  the  decoding  of  a  two-byte-op  instruction
 *  proceeds  in  exactly  the  same  manner  as  a  one-byte-op
 *  instruction.
 */

static OPINFO op0Ftab[] =
{
        { 0,    0 },
        { 0,    22 },   /* 0 */
        { 0,    23 },   /* 1 */
        { 129,  68 },   /* 2 */
        { 130,  68 },   /* 3 */
        { 79,   0 },    /* 4 */
        { 131,  0 },    /* 5 */
        { 79,   84 },   /* 6 */
        { 59,   71 },   /* 7 */
        { 59,   72 },   /* 8 */
        { 59,   69 },   /* 9 */
        { 59,   70 },   /* 10 */
        { 59,   74 },   /* 11 */
        { 59,   73 },   /* 12 */
        { 226,  19 },   /* 13 */
        { 41,   60 },   /* 14 */
        { 42,   60 },
        { 43,   60 },
        { 44,   60 },
        { 45,   60 },
        { 46,   60 },
        { 47,   60 },
        { 48,   60 },
        { 49,   60 },
        { 50,   60 },
        { 51,   60 },
        { 52,   60 },
        { 53,   60 },
        { 54,   60 },
        { 55,   60 },
        { 56,   60 },
        { 132,  66 },
        { 133,  66 },
        { 134,  66 },
        { 135,  66 },
        { 136,  66 },
        { 137,  66 },
        { 138,  66 },
        { 139,  66 },
        { 140,  66 },
        { 141,  66 },
        { 142,  66 },
        { 143,  66 },
        { 144,  66 },
        { 145,  66 },
        { 146,  66 },
        { 147,  66 },
        { 148,  0 },
        { 149,  0 },
        { 101,  2 },
        { 150,  79 },
        { 150,  80 },
        { 151,  0 },
        { 152,  0 },
        { 102,  2 },
        { 153,  79 },
        { 153,  80 },
        { 36,   4 },
        { 38,   52 },
        { 103,  2 },
        { 40,   52 },
        { 71,   52 },
        { 73,   81 },
        { 73,   68 },
        { 0,    24 },
        { 104,  2 },
        { 75,   4 },
        { 77,   4 },
        { 105,  81 },
        { 105,  68 }    /* 0FBF */
};



/*
 *  Here are the extended opcode tables.  They are indexed by
 *  the REG field of the Mod-REG-R/M byte.
 */

static OPINFO op80tab[] =
{
        { 1,    53 },
        { 4,    53 },
        { 6,    53 },
        { 9,    53 },
        { 12,   53 },
        { 15,   53 },
        { 18,   53 },
        { 21,   53 }    /* 80/82, mod-7-R/M */
};


static OPINFO op81tab[] =
{
        { 1,    54 },
        { 4,    54 },
        { 6,    54 },
        { 9,    54 },
        { 12,   54 },
        { 15,   54 },
        { 18,   54 },
        { 21,   54 }    /* 81, mod-7-R/M */
};


static OPINFO op83tab[] =
{
        { 1,    83 },
        { 4,    83 },
        { 6,    83 },
        { 9,    83 },
        { 12,   83 },
        { 15,   83 },
        { 18,   83 },
        { 21,   83 }    /* 83, mod-7-R/M */
};


static OPINFO opC0tab[] =
{
        { 117,  53 },
        { 118,  53 },
        { 119,  53 },
        { 120,  53 },
        { 121,  53 },
        { 122,  53 },
        { 121,  53 },
        { 123,  53 }    /* C0, mod-7-R/M */
};


static OPINFO opC1tab[] =
{
        { 117,  61 },
        { 118,  61 },
        { 119,  61 },
        { 120,  61 },
        { 121,  61 },
        { 122,  61 },
        { 121,  61 },
        { 123,  61 }    /* C1, mod-7-R/M */
};


static OPINFO opD0tab[] =
{
        { 117,  62 },
        { 118,  62 },
        { 119,  62 },
        { 120,  62 },
        { 121,  62 },
        { 122,  62 },
        { 121,  62 },
        { 123,  62 }    /* D0, mod-7-R/M */
};


static OPINFO opD1tab[] =
{
        { 117,  63 },
        { 118,  63 },
        { 119,  63 },
        { 120,  63 },
        { 121,  63 },
        { 122,  63 },
        { 121,  63 },
        { 123,  63 }    /* D1, mod-7-R/M */
};


static OPINFO opD2tab[] =
{
        { 117,  64 },
        { 118,  64 },
        { 119,  64 },
        { 120,  64 },
        { 121,  64 },
        { 122,  64 },
        { 121,  64 },
        { 123,  64 }    /* D2, mod-7-R/M */
};


static OPINFO opD3tab[] =
{
        { 117,  65 },
        { 118,  65 },
        { 119,  65 },
        { 120,  65 },
        { 121,  65 },
        { 122,  65 },
        { 121,  65 },
        { 123,  65 }    /* D3, mod-7-R/M */
};


static OPINFO opD8tab[] =
{
        { 155,  101 },
        { 156,  101 },
        { 157,  101 },
        { 158,  101 },
        { 159,  101 },
        { 160,  101 },
        { 161,  101 },
        { 162,  101 }   /* D8, mod-7-R/M */
};


static OPINFO opD9tab[] =
{
        { 163,  101 },
        { 0,    0 },
        { 164,  101 },
        { 165,  101 },
        { 166,  103 },
        { 167,  102 },
        { 168,  103 },
        { 169,  102 }   /* D9, mod-7-R/M */
};


static OPINFO opDAtab[] =
{
        { 170,  101 },
        { 171,  101 },
        { 172,  101 },
        { 173,  101 },
        { 174,  101 },
        { 175,  101 },
        { 176,  101 },
        { 177,  101 }   /* DA, mod-7-R/M */
};


static OPINFO opDBtab[] =
{
        { 178,  101 },
        { 0,    0 },
        { 179,  101 },
        { 180,  101 },
        { 0,    0 },
        { 163,  105 },
        { 0,    0 },
        { 165,  105 }   /* DB, mod-7-R/M */
};


static OPINFO opDCtab[] =
{
        { 155,  104 },
        { 156,  104 },
        { 157,  104 },
        { 158,  104 },
        { 159,  104 },
        { 160,  104 },
        { 161,  104 },
        { 162,  104 }   /* DC, mod-7-R/M */
};


static OPINFO opDDtab[] =
{
        { 163,  104 },
        { 0,    0 },
        { 164,  104 },
        { 165,  104 },
        { 181,  103 },
        { 0,    0 },
        { 182,  103 },
        { 183,  102 }   /* DD, mod-7-R/M */
};


static OPINFO opDEtab[] =
{
        { 170,  102 },
        { 171,  102 },
        { 172,  102 },
        { 173,  102 },
        { 174,  102 },
        { 175,  102 },
        { 176,  102 },
        { 177,  102 }   /* DE, mod-7-R/M */
};


static OPINFO opDFtab[] =
{
        { 178,  102 },
        { 0,    0 },
        { 179,  102 },
        { 180,  102 },
        { 184,  105 },
        { 178,  104 },
        { 185,  105 },
        { 180,  104 }   /* DF, mod-7-R/M */
};


static OPINFO opF6tab[] =
{
        { 57,   53 },
        { 0,    0 },
        { 124,  66 },
        { 125,  66 },
        { 126,  66 },
        { 36,   66 },
        { 127,  66 },
        { 128,  66 }    /* F6, mod-7-R/M */
};


static OPINFO opF7tab[] =
{
        { 57,   54 },
        { 0,    0 },
        { 124,  25 },
        { 125,  25 },
        { 126,  25 },
        { 36,   25 },
        { 127,  25 },
        { 128,  25 }    /* F7, mod-7-R/M */
};


static OPINFO opFEtab[] =
{
        { 24,   66 },
        { 25,   66 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 }     /* FE, mod-7-R/M */
};


static OPINFO opFFtab[] =
{
        { 24,   25 },
        { 25,   25 },
        { 64,   25 },
        { 64,   67 },
        { 100,  25 },
        { 100,  67 },
        { 26,   25 },
        { 0,    0 }     /* FF, mod-7-R/M */
};


static OPINFO op0F00tab[] =
{
        { 92,   82 },
        { 106,  82 },
        { 13,   82 },
        { 16,   82 },
        { 19,   82 },
        { 22,   82 },
        { 0,    0 },
        { 0,    0 }     /* 0F00, mod-7-R/M */
};


static OPINFO op0F01tab[] =
{
        { 32,   59 },
        { 33,   59 },
        { 34,   59 },
        { 35,   59 },
        { 107,  82 },
        { 0,    0 },
        { 108,  82 },
        { 0,    0 }     /* 0F01, mod-7-R/M */
};


static OPINFO op0FBAtab[] =
{
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 101,  61 },
        { 102,  61 },
        { 103,  61 },
        { 104,  61 }    /* 0FBA, mod-7-R/M */
};



static OPINFO *extoptab[] =
{
        NULL,
        op80tab,
        op81tab,
        op83tab,
        opC0tab,
        opC1tab,
        opD0tab,
        opD1tab,
        opD2tab,
        opD3tab,
        opD8tab,
        opD9tab,
        opDAtab,
        opDBtab,
        opDCtab,
        opDDtab,
        opDEtab,
        opDFtab,
        opF6tab,
        opF7tab,
        opFEtab,
        opFFtab,
        op0F00tab,
        op0F01tab,
        op0FBAtab
};


static byte op0Fmap[] =
{
        68,                     /* No. of entries in table */

        /* Sorted list of legal second bytes */

        0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x07,
        0x20, 0x21, 0x22, 0x23, 0x24, 0x26, 0x7f,
        0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
        0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
        0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
        0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
        0xA0, 0xA1, 0xA3, 0xA4, 0xA5,
        0xA8, 0xA9, 0xAB, 0xAC, 0xAD, 0xAF,
        0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
        0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF
};


/*
 *  This table is used to decode floating point instructions
 *  that manipulate floating point registers on the coprocessor
 *  register stack.  The index is formed by concatenating the
 *  low order three bits of the opcode with the REG field of
 *  the MOD-R/M byte to form a 6-bit index.
 */

static OPINFO opFPtab[] =
{
        { 155,  106 },
        { 156,  106 },
        { 157,  107 },
        { 158,  107 },
        { 159,  106 },
        { 160,  106 },
        { 161,  106 },
        { 162,  106 },
        { 163,  107 },
        { 186,  107 },
        { 0,    1 },
        { 165,  107 },
        { 0,    2 },
        { 0,    3 },
        { 0,    4 },
        { 0,    5 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    6 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 155,  108 },
        { 156,  108 },
        { 157,  107 },
        { 158,  107 },
        { 159,  108 },
        { 160,  108 },
        { 161,  108 },
        { 162,  108 },
        { 187,  107 },
        { 186,  107 },
        { 164,  107 },
        { 165,  107 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 188,  108 },
        { 189,  108 },
        { 158,  107 },
        { 0,    7 },
        { 190,  108 },
        { 191,  108 },
        { 192,  108 },
        { 193,  108 },
        { 194,  108 },
        { 186,  108 },
        { 165,  108 },
        { 165,  108 },
        { 0,    8 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },    /* DF, 3-7-R/M */
};


/*
 *  Extended opcode tables for FP instructions with no
 *  or implicit operands.  These tables are indexed by
 *  the R/M field of the Mod-REG-R/M byte.
 */

static OPINFO opFP1tab[] =
{
        { 195,  0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 }     /* D9, 3-2-7 */
};


static OPINFO opFP2tab[] =
{
        { 196,  0 },
        { 197,  0 },
        { 0,    0 },
        { 0,    0 },
        { 198,  0 },
        { 199,  0 },
        { 0,    0 },
        { 0,    0 }     /* D9, 3-4-7 */
};


static OPINFO opFP3tab[] =
{
        { 200,  0 },
        { 201,  0 },
        { 202,  0 },
        { 203,  0 },
        { 204,  0 },
        { 205,  0 },
        { 206,  0 },
        { 0,    0 }     /* D9, 3-5-7 */
};


static OPINFO opFP4tab[] =
{
        { 207,  0 },
        { 208,  0 },
        { 209,  0 },
        { 210,  0 },
        { 211,  0 },
        { 0,    0 },
        { 212,  0 },
        { 213,  0 }     /* D9, 3-6-7 */
};


static OPINFO opFP5tab[] =
{
        { 214,  0 },
        { 215,  0 },
        { 216,  0 },
        { 0,    0 },
        { 217,  0 },
        { 218,  0 },
        { 0,    0 },
        { 0,    0 }     /* D9, 3-7-7 */
};


static OPINFO opFP6tab[] =
{
        { 219,  0 },
        { 220,  0 },
        { 221,  0 },
        { 222,  0 },
        { 223,  0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 }     /* DB, 3-4-7 */
};


static OPINFO opFP7tab[] =
{
        { 0,    0 },
        { 224,  0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 }     /* DE, 3-3-7 */
};


static OPINFO opFP8tab[] =
{
        { 225,  0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 },
        { 0,    0 }     /* DF, 3-4-7 */
};


static OPINFO *opFPexttab[] =
{
        NULL,
        opFP1tab,
        opFP2tab,
        opFP3tab,
        opFP4tab,
        opFP5tab,
        opFP6tab,
        opFP7tab,
        opFP8tab
};


/*
 *  Table of formats for addressing modes for 16-bit
 *  addresses when the Mod field is either 0, 1, or 2.
 */

static char *d0rmmem[] =
{
        "BX+SI",        /* R/M = 0 */
        "BX+DI",        /* R/M = 1 */
        "BP+SI",        /* R/M = 2 */
        "BP+DI",        /* R/M = 3 */
        "SI",           /* R/M = 4 */
        "DI",           /* R/M = 5 */
        "BP",           /* R/M = 6 */
        "BX",           /* R/M = 7 */
};


/*
 *  Table of byte registers.
 */

static char *regbyte[] =
{
        "AL",           /* REG = 0, or Mod = 3, R/M = 0 */
        "CL",           /* REG = 1, or Mod = 3, R/M = 1 */
        "DL",           /* REG = 2, or Mod = 3, R/M = 2 */
        "BL",           /* REG = 3, or Mod = 3, R/M = 3 */
        "AH",           /* REG = 4, or Mod = 3, R/M = 4 */
        "CH",           /* REG = 5, or Mod = 3, R/M = 5 */
        "DH",           /* REG = 6, or Mod = 3, R/M = 6 */
        "BH"            /* REG = 7, or Mod = 3, R/M = 7 */
};


/*
 *  Table of "word" registers.
 */

static char *regword[] =
{
        "AX",           /* REG = 0, or Mod = 3, R/M = 0 */
        "CX",           /* REG = 1, or Mod = 3, R/M = 1 */
        "DX",           /* REG = 2, or Mod = 3, R/M = 2 */
        "BX",           /* REG = 3, or Mod = 3, R/M = 3 */
        "SP",           /* REG = 4, or Mod = 3, R/M = 4 */
        "BP",           /* REG = 5, or Mod = 3, R/M = 5 */
        "SI",           /* REG = 6, or Mod = 3, R/M = 6 */
        "DI"            /* REG = 7, or Mod = 3, R/M = 7 */
};


/*
 *  Table of segment registers.
 */

static char *segreg[] =
{
        "ES",           /* REG =   0 */
        "CS",           /* REG =   1 */
        "SS",           /* REG =   2 */
        "DS",           /* REG =   3 */
        "FS",           /* REG =   4 */
        "GS",           /* REG =   5 */
        "",             /* Unused  6 */
        "",             /* Unused  7 */
        "DS",           /* Default 8 */
};






int disasm( char *buffer,            /* Buffer for formatted output */
            AddrS *address )         /* Pointer to address structure */
{
    int cb;                         /* Number of bytes decoded */
    int i;                          /* Counter */
    int bpnum;                      /* Breakpoint number */
    byte *cp;                       /* Updated stream pointer */
    byte stream[MAXINST];           /* Instruction buffer */
    char txtbuf[OPBUFLEN];          /* Instruction text buffer */
    register char *pch = buffer;    /* Pointer to buffer */

    commentchar = 0;                /* No comment character yet */

    /* Format instruction address */
    fmtaddr( pch,
             address,
             DebData.InitFlag & INIT_ENV386
                 ? FMT_ADDR_32ENV | FMT_ADDR_SEG
                 : FMT_ADDR_SEG );
    lowercase(pch);

    /* Fill the local buffer */
    if ( ( i = getmemc( address, stream, MAXINST, &cb ) ) != 0 )
    {
        addtxt( buffer, geterrmessage(i), OPCOLUMN );
        return(0);
    }
    if (cb == 0)
    {
        return(0);
    }

    /* Decode opcodes */
    if ( ( cb = decodeinst( txtbuf, stream, address ) ) == 0 )
    {
        return(0);
    }

    pch += strlen(pch);       /* Skip over address */
    *pch++ = ' ';             /* Add a space */
    cp = stream;              /* Point at buffer */

    /* Decode the raw bytes in hex */
    for( i = 0; i < cb; ++i )
    {
        pch += sprintflc( pch, "%02.2x", *cp++ );
    }

    /* Put decoded instruction into buffer */
    addtxt( buffer, txtbuf, OPCOLUMN );

    /* Clear out the text buffer */
    *txtbuf = '\0';

    /* if there is a breakpoint at this address */
    if( ( bpnum = checkbp( address ) ) != -1 )
    {
        sprintflc( txtbuf, ";BR%c", bpnum + '0' );
    }
    else if (    ( commentchar >= ' ' )
              && ( commentchar <= '~' ) )
    {
        sprintf( txtbuf, ";'%c'", commentchar );
    }

    /* Put BP or commentchar into buffer */
    addtxt( buffer, txtbuf, BPCOLUMN );

    /* Return number of bytes consumed */
    return(cb);
}





void decodeop( register char *buffer )  /* Buffer for formatted output */
{
    register word oplen;            /* Operand length */
    register word optype;           /* Operand type */
    byte opbuf[MAXOPLEN];           /* Operand buffer */
    char txtbuf[OPBUFLEN];          /* Text buffer */
    char *pch = txtbuf;             /* Pointer to text buffer */
    char *fmt;                      /* Output format string for offst */
    int opi;                        /* Index */

    /* Clear out the text buffer */
    *txtbuf = '\0';

    /* While operands remain */
    for( opi = 0; opi < opcnt; ++opi )
    {

        /* Get flags */
        optype = optyps[opi];

        /* Get operand length */
        oplen = optype & OPSIZEMASK;

        /* Don't format if no size operand (LEA) */
        if(oplen == NOSIZEOP)
        {
            continue;
        }

        /* Check if virtual int or ptr */
        if ( optype & (VINT | VPTR) )
        {

            /* 32 bit int or 48 bit pointer */
            if (optype & OPER32)
            {
                oplen += VSIZE;
            }
        }

        /* If not the first operand, put a space for the second */
        if (opi != 0)
        {
            *pch++ = ' ';
        }

        /* If 32 bit offset */
        if (optype & ADDR32)
        {
            fmt = "%s:%08.8lx=";
        }
        else
        {
            fmt = "%s:%04.4lx=";
        }

        /* Decode segment and offset */
        pch += sprintflc( pch,
                          fmt,
                          segreg[opsegs[opi]],
                          opaddrs[opi].AddrOff );

        /* If operand valid */
        if ( getbuffc( &opaddrs[opi], opbuf, oplen ) == 0 )
        {

            /* If "ptr" operand */
            if (optype & VPTR)
            {

                /* Else if 48-bit pointer */
                if(optype & OPER32)
                {
                    pch += sprintflc( pch,
                                      "%04x:%08lx",
                                      *((word *)(opbuf + 4)),
                                      *((dword *)opbuf) );
                }
                /* Else 32-bit pointer */
                else
                {
                    pch += sprintflc( pch,
                                      "%04x:%04x",
                                      *((word *)(opbuf + 2)),
                                      *((word *)opbuf) );
                }
            }
            /* else "scalar" operand */
            else
            {
                /* Decode bytes */
                while( oplen-- != 0 )
                {
                    pch += sprintflc( pch,
                                      "%02.2x",
                                      opbuf[oplen] );
                }
            }
        }
        else
        {
            /* Else invalid operand */
            pch += sprintflc( pch, "INVALID" );
        }
    }

    /* Put into buffer, right justify */
    addtxt( buffer, txtbuf, -1 );

    return;
}





static void addtxt( char *buffer,     /* Buffer to add newtxt */
                    char *newtxt,     /* Text to be added at column */
                    int column )      /* Column to put newtxt at */
{
    int newtxtlen;                  /* New text length */
    register int i;                 /* Temporary variable */
    register char *pbuf;            /* Pointer to buffer */
    char tmpbuf[OPBUFLEN];          /* Temp copy of buffer */

    /* Get new text length, return if empty */
    newtxtlen = strlen(newtxt);
    if(newtxtlen == 0)
    {
            return;
    }

    /* Check for right justify */
    if(column == -1)
    {
            column = MAXCOLUMN - newtxtlen;
    }
    else
    {
            column--;
    }

    /* Start buffer after last LF in buffer if one exists */
    pbuf = buffer;
    while(*pbuf != '\0')
    {
        if(*pbuf++ == '\n')
        {
                buffer = pbuf;
        }
    }

restart:
    /* Get length and pbuf is the end of the buffer */
    i = pbuf - buffer;

    /* Save the original buffer */
    strcpy(tmpbuf, buffer);

    /* Pad to "column"; put at least one space */
    i = column - i;
    do
    {
        *pbuf++ = ' ';
    } while(--i > 0);

    /* Copy new text into buffer */
    strcpy(pbuf, newtxt);

    /* Get length and end of buffer */
    pbuf = (i = strlen(buffer)) + buffer;

    /* If the new text can't fit in the buffer */
    if ( (i -= MAXCOLUMN) > 0 )
    {

        /* While spaces need to be removed */
        while(i--)
        {

            /* Look for two spaces in a row */
            while ( *pbuf != ' ' || *(pbuf - 1) != ' ' )
            {

                /* If at begining of buffer */
                if (pbuf <= buffer)
                {

                    /* If original buffer empty, return */
                    if (*tmpbuf == '\0')
                    {
                        return;
                    }

                    /* Restore buffer */
                    strcpy( buffer, tmpbuf );

                    /* Get end of buffer */
                    pbuf = strlen(buffer) + buffer;

                    /* Put CR/LF in buffer */
                    *pbuf++ = '\r';
                    *pbuf++ = '\n';

                    /* Setup to restart addtxt */
                    *pbuf = '\0';
                    buffer = pbuf;
                    goto restart;
                }
                pbuf--;
            }
            /* Remove the space from buffer */
            strcpy( pbuf, pbuf + 1 );
            pbuf--;
        }
    }
}


static int decodeinst( char *buffer,     /* Buffer in which to format */
                       byte *start,      /* Start of instruction stream */
                       AddrS *address )  /* Pointer to address structure */
{
    register byte *stream;      /* Instruction stream */
    register byte *newstream;   /* Updated IP */
    char *opend;                /* Pointer to end of opcode in buffer */
    char sizechar;              /* Character to put at end of opcode */
    char *fmt;                  /* Format string */
    dword eip;                  /* EIP value */
    word flags;                 /* Flags */
    word reg;                   /* General register number */
    word seg;                   /* Segment prefix */
    word tseg;                  /* Temp segment prefix */
    byte rep;                   /* Repeat prefix */
    byte opcode;                /* Opcode */
    byte opfmt;                 /* Format table index */
    bool lock = FALSE;          /* Bus lock prefix flag */
    bool seenop = FALSE;        /* Seen operand prefix */
    bool seenaddr = FALSE;      /* Seen address prefix */
    bool prefix;                /* Prefix flag */

    /* Set defaults */
    stream = start;             /* Initialize to start of stream */

    /* Check the descriptor's "D" bit */
    flags = getdbit(address) ? OPER32 | ADDR32 : 0;

    rep = 0;                        /* Assume no rep prefixes */
    seg = SEGDEF;                   /* Default segment register */
    newstream = stream;             /* Save pointer */
    eip = address->AddrOff;         /* Initialize EIP */
    prefix = TRUE;                  /* Assume there are prefixes */

    /* Loop to get prefixes */
    while (prefix)
    {
        opcode = *stream++;     /* Get opcode or prefix */
        switch (opcode)         /* Switch on code */
        {

            case PFX_SEGES:     /* ES override prefix */
                seg = SEGES;    /* Save table index */
                break;

            case PFX_SEGCS:     /* CS override prefix */
                seg = SEGCS;    /* Save table index */
                break;

            case PFX_SEGSS:     /* SS override prefix */
                seg = SEGSS;    /* Save table index */
                break;

            case PFX_SEGDS:     /* DS override prefix */
                seg = SEGDS;    /* Save table index */
                break;

            case PFX_SEGFS:     /* FS override prefix */
                seg = SEGFS;    /* Save table index */
                break;

            case PFX_SEGGS:     /* GS override prefix */
                seg = SEGGS;    /* Save table index */
                break;

            case OPSIZE:        /* Operand size prefix */
                if (!seenop++)
                {
                    /* Toggle flag once */
                    flags ^= OPER32;
                }
                break;

            case OPADDR:            /* Operand address prefix */
                if (!seenaddr++)
                {
                    /* Toggle flag once */
                    flags ^= ADDR32;
                }
                break;

            case LOCK:          /* Bus lock prefix */
                lock = 1;       /* Set flag */
                break;

            case REPNE:         /* Repeat prefix */
                rep = REPNE;    /* Save prefix */
                break;

            case REPE:          /* Repeat prefix */
                rep = REPE;     /* Save prefix */
                break;

        default:                /* Not a prefix */
                prefix = FALSE;
                break;
        }
    }

    /* If floating pt. instruction */
    if ( opcode >= OPESCLO && opcode <= OPESCHI )
    {

        /* If no memory operand */
        if ( modrm_mod(*stream) == MOD_REG )
        {

            /* Get table index */
            opcode = (byte)fpregi( opcode, *stream );

            /* Get format index */
            opfmt = opFPtab[opcode].op_fmt;

            /* If entry found */
            if ( (opcode = opFPtab[opcode].op_code) == 0 )
            {

                /* If possible indirection */
                if (opfmt != 0)
                {

                    /* Get opcode index */
                    opcode = opFPexttab[opfmt][modrm_rm(*stream)].op_code;

                    /* Get format index */
                    opfmt = opFPexttab[opfmt][modrm_rm(*stream)].op_fmt;

                    ++stream;       /* Skip Mod-R/M byte */
                }

                /* Cause Mod-R/M byte to be skipped */
                if (opcode == 0)
                    opfmt = 1;
            }
            goto decode_loop;       /* Ack!  Unstructured! */
        }

    }

    /* 1st byte of 2-byte opcode? */
    if (opcode == OP0F)
    {
        opcode = map0Fop(*stream++);    /* Get table index */
        opfmt = op0Ftab[opcode].op_fmt; /* Get format table index */

        /* Get opcode table index */
        opcode = op0Ftab[opcode].op_code;
    }
    else /* Else 1-byte opcode */
    {
        opfmt = optab[opcode].op_fmt;   /* Get format table index */
        opcode = optab[opcode].op_code; /* Get opcode table index */
    }

    /* If extended opcode */
    if (opcode == 0 && opfmt != 0)
    {

        /* get extend opcode */
        if ( (opcode = extoptab[opfmt][modrm_reg(*stream)].op_code) != 0 )
        {
                /* Get format if opcode valid */
                opfmt = extoptab[opfmt][modrm_reg(*stream)].op_fmt;
        }
    }

decode_loop:

    opcnt = 0;                              /* Initialize */
    if (opcode == 0)                        /* If invalid opcode */
    {
        if (opfmt != 0)
            ++stream;                       /* Fail in Mod-REG-R/M byte */
        strcpylc( buffer, "DB      " );     /* List bytes */
        buffer += 8;                        /* Skip DB */
        while (newstream < stream)          /* Loop to decode bytes */
        {
            /* Decode the byte */
            buffer += sprintflc( buffer, "%02.2x", *newstream++ );
            if (newstream < stream)         /* If there are more bytes */
            {
                    *buffer++ = ',';        /* Add a comma */
                    *buffer++ = ' ';        /* Add a space */
            }
        }
        return(stream - start);             /* Ret num of bytes decoded */
    }
    if (lock)                               /* If bus lock prefix */
    {
        strcpylc( buffer, "LOCK " );        /* Put prefix in buffer */
        buffer += 5;                        /* Skip over it */
    }
    if (rep == REPNE)                       /* If REPNE */
    {
        strcpylc( buffer, "REPNE " );       /* Put prefix in buffer */
        buffer += 6;                        /* Skip over it */
    }
    else if (rep == REPE)                   /* Else if REPE */
    {
        strcpylc( buffer, "REPE " );       /* Put prefix in buffer */
        buffer += 5;                       /* Skip over it */
    }
    eip += stream - newstream;              /* Account for opcode bytes */
    newstream = stream;                     /* Remember updated stream */

    fmt = opcodes[opcode];                  /* Get opcode string */
    if (*fmt == '*')                        /* If affected by oper size */
    {
        fmt++;                              /* Skip asterisk */
        if (flags & OPER32)                 /* Skip 16-bit form */
        {
            fmt += strlen(fmt) + 1;
        }
    }
    if (*fmt == '+')                        /* If affected by addr size */
    {
        fmt++;                              /* Skip asterisk */
        if (flags & ADDR32)                 /* Skip 16-bit form */
        {
            fmt += strlen(fmt) + 1;
        }
    }
    if (opfmt != 0)                         /* If instr has operands */
    {

        /* Setup pointer to end of the opcode (for movsb, etc.) */
        opend = buffer + strlen(fmt);

        /* Put opcode in buffer */
        buffer += sprintflc( buffer, "%-8s", fmt );

        /* Loop to interpret operands */
        for ( fmt = opfmts[opfmt]; *fmt != '\0'; fmt += 2 )
        {
            opseg = NOSEG;          /* Assume not a mem operand */
            optyp = flags;          /* Set default flags */
            switch (*fmt)           /* Switch on oper format char */
            {
                case 'A':           /* Direct address */

                    /* Internal consistency check */
                    verifytype( fmt[1], "p" );

                    /* If offset is 32 bits */
                    if(optyp & OPER32)
                    {
                        /* Get the target offset */
                        eip = *((dword *) newstream);

                        /* Adjust pointer */
                        newstream += 4;
                    }
                    /* Else offset is 16 bits */
                    else
                    {
                        /* Get the target offset */
                        eip = (dword) *((word *) newstream);

                        /* Adjust pointer */
                        newstream += 2;
                    }

                    /* Decode the pointer */
                    decodeaddr( buffer,
                                DefType,
                                address->AddrTask,
                                *((word *) newstream),
                                eip,
                                optyp & OPER32,
                                FMT_ADDR_SEG );

                    /* Skip over the selector */
                    newstream += 2;
                    break;

                case 'C':       /* Operand is control register */
                case 'D':       /* Operand is debug register */
                case 'T':       /* Operand is TLB register */

                    /* Internal consistency check */
                    verifytype( fmt[1], "d" );

                    /* Decode the register */
                    sprintflc( buffer,
                               "%cR%x",
                               fmt[0],
                               modrm_reg(*stream) );
                    break;

                case 'F':       /* Operand is FP register */

                    /* Internal consistency check */
                    verifytype( fmt[1], "f" );

                    /* Decode the register */
                    sprintflc( buffer,
                               "ST(%x)",
                               modrm_rm(*stream) );
                    ++newstream;    /* Skip Mod-R/M byte */
                    break;

                case 'P':       /* Operand from Mod-R/M byte (POP) */
                    optyp |= POPESP; /* Need to special case "POP [ESP+...]"

                    /* Drop through */

                case 'E':       /* Operand from Mod-R/M byte */
                case 'M':       /* Memory operand from Mod-R/M byte */
                case 'R':       /* Register from Mod-R/M byte */

                    /* Internal consistency check */
                    verifytype( fmt[1], "bdpqstvwxy" );

                    /* Switch on operand type */
                    switch(fmt[1])
                    {
                        case 'x':       /* no operand size (LEA) */
                            optyp |= NOSIZEOP;
                            break;
                        case 'b':       /* Byte operand */
                            optyp |= BYTEOP;
                            break;
                        case 'v':       /* int operand (16 or 32) */
                            optyp |= (VINT | WORDOP);
                            break;
                        case 'y':       /* "mov op,segreg" */
                            optyp |= (VINTREG | WORDOP);
                            break;
                        case 'w':       /* 16-bit operand */
                            optyp |= WORDOP;
                            break;
                        case 'd':       /* 32-bit operand */
                            optyp |= DWORDOP;
                            break;
                        case 'p':       /* Pointer operand (32 or 48) */
                            optyp |= (VPTR | DWORDOP);
                            break;
                        case 's':       /* Six-byte operand */
                            optyp |= FWORDOP;
                            break;
                        case 'q':       /* 64-bit operand */
                            optyp |= QWORDOP;
                            break;
                        case 't':       /* Ten-byte operand */
                            optyp |= TBYTEOP;
                            break;
                    }

                    /* Decode operand */
                    newstream = decodemodrm( buffer,
                                             stream + 1,
                                             *stream,
                                             seg );
                    break;

                case 'G':               /* Operand from REG field */

                    /* Internal consistency check */
                    verifytype( fmt[1], "bvw" );

                    if ( fmt[1] == 'b' )

                        /* Set byte operand type */
                        optyp |= BYTEOP;

                    else if ( fmt[1] == 'w' )

                        /* Else set 16-bit operand type */
                        optyp |= WORDOP;

                    else

                        /* Else 16- or 32-bit operand */
                        optyp |= (VINT | WORDOP);

                    /* Decode the REG field */
                    decodereg( buffer, *stream );
                    break;

                case 'I':               /* Immediate operand */

                    /* Internal consistency check */
                    verifytype( fmt[1], "abcvw" );

                    switch(fmt[1])  /* Switch on type specifier */
                    {
                        case 'b':       /* Unsigned byte */

                            commentchar = *newstream;
                            /* Fall thru */

                        case 'a':       /* Unsigned byte */

                            /* Decode unsigned immediate byte */
                            sprintflc( buffer, "%02.2x", *newstream++ );
                            break;

                        case 'c':       /* Signed byte */

                            /* Decode signed immediate byte */
                            commentchar = *newstream++;
                            decodesignedbyte( buffer, (char) commentchar );
                            break;

                        case 'v':       /* "Word" */

                            /* If 32-bit "word" */
                            if (optyp & OPER32)
                            {
                                /* Decode immed word or dword */
                                decodeimmed( buffer,
                                             *((dword *) newstream ),
                                             FMT_ADDR_32ENV );

                                /* Skip word */
                                newstream += 4;
                                break;
                            }

                            /* Drop through */

                        case 'w':       /* 16-bit word */

                            /* Decode immed word */
                            decodeimmed( buffer,
                                         (dword) *((word *) newstream),
                                         0 );

                            /* Skip word */
                            newstream += 2;
                            break;
                    }
                    break;

                case 'J':               /* Relative offset */

                    /* Internal consistency check */
                    verifytype( fmt[1], "bv" );

                    /* Calculate byte displacement */
                    if (fmt[1] == 'b')
                    {
                        eip += 1 + (char) *newstream++;
                    }
                    else if(optyp & OPER32)
                    {
                        /* Calculate 32-bit displacement */
                        eip += 4 + *((dword *) newstream);
                        newstream += 4; /* Update pointer */
                    }
                    else
                    {
                        /* Calculate 16-bit displacement */
                        eip += 2 + *((word *) newstream);
                        newstream += 2; /* Update pointer */
                    }

                    /* Decode target offset */
                    decodeaddr( buffer,
                                address->AddrType,
                                address->AddrTask,
                                address->AddrSeg,
                                eip,
                                optyp & OPER32,
                                0 );
                    break;

                case 'O':               /* Immediate offset */

                    /* No base offset */
                    opoff = 0L;

                    /* Set segment used */
                    opseg = seg;

                    /* Internal consistency check */
                    verifytype( fmt[1], "bv" );
                    if (fmt[1] == 'b')
                        /* Byte operand */
                        optyp |= BYTEOP;
                    else
                        /* 16 or 32-bit operand */
                        optyp |= (VINT | WORDOP);

                    /* Operand type and segment prefix */
                    buffer = decodetypeseg( buffer, seg );
                    *buffer++ = '[';      /* Open square brackets */
                    if (optyp & ADDR32)
                    {
                        /* Get 32-bit displacement */
                        opdisp = *((long *) newstream);
                        newstream += 4; /* Skip word */
                    }
                    else
                    {
                        /* Get 16-bit displacement */
                        opdisp = (long) *((short *) newstream) & 0xFFFFL;
                        newstream += 2; /* Skip two bytes */
                    }

                    /* Decode displacement */
                    decodedisp( buffer, opseg, opdisp, optyp );

                    buffer += strlen(buffer);
                    *buffer++ = ']';  /* Close square brackets */
                    *buffer = '\0';   /* Null-terminate */
                    break;

                case 'S':       /* REG field specifies segment reg */

                    /* Internal consistency check */
                    verifytype( fmt[1], "w" );

                    /* Decode segment register */
                    strcpylc( buffer, segreg[modrm_reg(*stream)] );
                    break;

                case 'X':       /* Implicit DS:(E)SI operand */
                case 'Y':       /* Implicit ES:(E)DI operand */
                case 'Z':       /* Implicit DS:(E)BX operand */

                    /* Internal consistency check */
                    verifytype( fmt[1], "bv" );

                    /* If byte operand */
                    if (fmt[1] == 'b')
                    {
                        /* Set flag for byte operand */
                        optyp |= BYTEOP;
                        sizechar = 'B';
                    }
                    else
                    {
                        /* Else 16- or 32-bit operand */
                        optyp |= (VINT | WORDOP);
                        if (optyp & OPER32)
                        {
                            sizechar = 'D';
                        }
                        else
                        {
                            sizechar = 'W';
                        }
                    }
                    /* Use DS if no explicit prefix */
                    if (seg == SEGDEF)
                    {
                        seg = SEGDS;
                    }

                    /* If no overrides, use short form */
                    if (seg == SEGDS && !seenaddr )
                    {
                        sprintflc( opend, "%c", sizechar );
                    }

                    /* Select general register */
                    tseg = seg;
                    switch(fmt[0])
                    {
                        case 'X':
                            reg = REG_SI;
                            break;

                        case 'Y':
                            tseg = SEGES;
                            reg = REG_DI;
                            break;

                        case 'Z':
                            reg = REG_BX;
                            break;
                    }

                    /* Decode the operand */
                    decodememop( buffer, tseg, reg );
                    break;

                case '0':       /* Implicit register operand */
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':

                    /* Internal consistency check */
                    verifytype( fmt[1], "bvw" );
                    if (fmt[1] == 'b')
                    {
                        /* Decode name of byte register */
                        strcpylc( buffer, regbyte[*fmt - '0'] );
                    }
                    else if ( fmt[1] == 'w' || !(optyp & OPER32) )
                    {
                        /* Decode name of 16-bit register */
                        strcpylc(buffer,regword[*fmt - '0']);
                    }
                    else
                    {
                        /* Decode name of 32-bit register */
                        sprintflc( buffer, "E%s", regword[*fmt - '0'] );
                    }
                    break;

                case '#':       /* Implicit operand */
                    /* Get immediate operand */
                    strcpylc( buffer, implicitops[ (byte)fmt[1] ] );
                    break;

                default:        /* ERROR */
                    return(0);          /* Ret error to caller */
            }

            buffer += strlen(buffer);   /* Skip to end of field */
            if (fmt[2] != '\0')         /* If not last field */
            {
                *buffer++ = ',';    /* Add comma */
                *buffer = '\0';     /* Null terminate */
            }

            /* If there is a memory operand */
            if (opseg != NOSEG)
            {
                /* Save operand type */
                optyps[opcnt] = optyp;

                /* Save segment number */
                opsegs[opcnt] = opseg;

                /* Save operand address */
                opaddrs[opcnt].AddrOff = opdisp + opoff;
                if (!(optyp & ADDR32))
                    opaddrs[opcnt].AddrOff &= 0xFFFFL;
                opaddrs[opcnt].AddrSeg = segregval(opseg);
                opaddrs[opcnt].AddrType = address->AddrType;
                opaddrs[opcnt].AddrTask = address->AddrTask;
                opcnt++;
            }
        }
    }
    else          /* Else no explicit operands */
    {
        strcpylc( buffer, fmt );  /* Put opcode in buffer */
    }
    return(newstream - start);      /* Return number of bytes decoded */
}



static void decodereg( char *buffer,     /* Formatted output buffer */
                       byte modrm )      /* Mod-R/M byte */
{
    byte reg;                         /* REG field of Mod-R/M byte */

    reg = (byte)modrm_reg(modrm);     /* Extract REG field */
    if (!(optyp & BYTEOP))            /* If word operand */
    {
        if(!(optyp & OPER32))
        {
                /* Word size is 16 bits */
            strcpylc( buffer, regword[reg] );
        }
        else
        {
            /* Else word size is 32 bits */
            sprintflc( buffer, "E%s", regword[reg] );
        }
    }
    else
    {
        /* Else decode byte register name */
        strcpylc( buffer, regbyte[reg] );
    }
}



static void decodememop( char *buffer,  /* Decode buffer */
                         word seg,      /* Segment prefix number */
                         word reg )     /* Register number */
{
    buffer = decodetypeseg( buffer, seg ); /* Operand type and segment prefix */
    opoff = regval(reg);                   /* Get base offset */
    if(!(optyp & ADDR32))                  /* Mask off high word if 16 bit seg */
    {
        opoff &= 0xffff;
    }
    opdisp = 0L;                    /* No displacement */
    opseg = seg;                    /* Set segment register */

    /* Base/index register */
    sprintflc( buffer,
               "[%s%s]",
               ( (optyp & ADDR32) ? "E" : "" ),
               regword[reg] );
}



static byte * decodemodrm( register char *buffer,  /* Formatted output buffer */
                           register byte *stream,  /* Instruction stream */
                           byte modrm,             /* Mod-R/M byte */
                           word seg )              /* Segment override prefix */
{
    byte rm;                        /* R/M field of Mod-R/M byte */
    byte sib;                       /* SIB byte */

    rm = (byte)modrm_rm(modrm);           /* Extract R/M field */

    /* If register operand */
    if (modrm_mod(modrm) == MOD_REG)
    {
        /* Switch on operand type */
        switch (optyp & OPSIZEMASK)
        {
            case BYTEOP:                    /* Byte operand */

                /* Decode byte register name */
                strcpylc( buffer, regbyte[rm] );
                break;

            case WORDOP:                    /* Word or int operand */

                /* is this a virtual int operand */
                if (optyp & (VINT | VINTREG))
                {
                    /* Check int operand size */
                    if (optyp & OPER32)
                    {
                            /* E-register */
                            sprintflc( buffer, "E%s", regword[rm] );
                            break;
                    }
                }

                /* Decode word register name */
                strcpylc( buffer, regword[rm] );
                break;

            case DWORDOP:                   /* Dword operand */

                /* E-register */
                sprintflc( buffer, "E%s", regword[rm] );
                break;
        }
        return(stream);                  /* Done */

    }
    /* Save override */
    opseg = seg;

    /* Get operand type and seg prefix */
    buffer = decodetypeseg( buffer, seg );

    *buffer++ = '[';                /* Open braces */

    if (!(optyp & ADDR32))          /* If 16-bit addresses */
    {
        switch (modrm_mod(modrm)) /* Switch on mod field */
        {
            case MOD_NODISP:        /* No displacement */

                /* If 16-bit disp, no bas reg */
                if (rm == D0RM_BASE_BP)
                {
                    opoff = 0L;             /* No base register */

                    /* Save displacement value */
                    opdisp = (long) *((short *) stream) & 0xFFFFL;
                    stream += 2;            /* Two bytes used */

                    /* Format in buffer */
                    decodedisp( buffer, opseg, opdisp, optyp );
                }
                else
                {
                    opdisp = 0L;            /* No displacement */
                    d0base( buffer, rm );      /* Decode into buffer */
                }
                break;

            case MOD_BYTEDISP:              /* 8-bit displacement */

                /* Decode registers */
                buffer += d0base( buffer, rm );

                /* Save displacement */
                opdisp = (long) *((char *) stream++);

                /* Decode displacement */
                if (opdisp != 0)
                {
                    decodesignedbyte( buffer, (char) opdisp );
                }
                break;

            case MOD_WORDDISP:              /* 16-bit displacement */

                /* Decode registers */
                buffer += d0base( buffer, rm );

                /* Save displacement */
                opdisp = (long) *((short *) stream) & 0xFFFFL;
                stream += 2;            /* Skip displacement word */
                *buffer++ = '+';        /* Add separator */

                /* Format in buffer */
                decodedisp( buffer, opseg, opdisp, optyp );
                break;
        }
    }

    else /* Else if 32-bit addresses */
    {
        sib = *stream;                  /* Get SIB byte if any */
        switch (modrm_mod(modrm))       /* Switch on mod field */
        {
            case MOD_NODISP:            /* No displacement */

                opdisp = 0L;            /* Assume no displacement */
                switch (rm)             /* Switch on R/M field */
                {
                    case D1RM_BASE_BP:      /* 32-bit disp., no base reg */

                        opoff = 0L;     /* No base */

                        /* Get displacement */
                        opdisp = *((long *) stream);
                        stream += 4;    /* Four bytes used */

                        /* Format in buffer */
                        decodedisp( buffer, opseg, opdisp, optyp );
                        break;

                    case D1RM_SIB:          /* No disp., SIB byte present */

                        /* Decode SIB byte into buffer */
                        stream = decodesib( buffer, ++stream, modrm, sib );
                        break;

                    default:                /* Base with no displacement */
                        d1base( buffer, rm ); /* Decode base into buffer */
                        break;
                }
                break;

            case MOD_BYTEDISP:              /* 8-bit displacement */

                ++stream;               /* Skip SIB/disp. byte */
                if (rm == D1RM_SIB)     /* If SIB byte present */
                {
                    /* Decode SIB byte into buffer */
                    decodesib( buffer, (byte *) 0, modrm, sib );

                    /* Skip decoded SIB stuff */
                    buffer += strlen(buffer);

                    /* Get displacement */
                    sib = *stream++;
                }
                else
                {
                    /* Else decode base into buffer */
                    buffer += d1base( buffer, rm );
                }

                /* Save displacement */
                opdisp = (char) sib;

                /* Decode displacement */
                if (opdisp != 0)
                {
                    decodesignedbyte( buffer, (char) opdisp );
                }
                break;

            case MOD_WORDDISP:              /* 32-bit displacement */

                if (rm == D1RM_SIB)      /* If SIB byte present */
                {
                    ++stream;       /* Skip SIB byte */

                    /* Decode SIB byte into buffer */
                    decodesib( buffer, (byte *) 0, modrm, sib );

                    /* Skip decoded SIB stuff */
                    buffer += strlen(buffer);
                }
                else
                {
                    /* Else decode base into buffer */
                    buffer += d1base(buffer,rm);
                }

                /* Save displacement */
                opdisp = *((long *) stream);
                stream += 4;            /* Four bytes used */
                *buffer++ = '+';        /* Add separator */
                decodedisp( buffer, opseg, opdisp, optyp );
                break;

        }
    }
    buffer += strlen(buffer);               /* Skip to end of string */
    *buffer++ = ']';                        /* Close braces */
    *buffer = '\0';                         /* Null terminate */
    return(stream);                         /* Return updated pointer */
}




static char * decodetypeseg( char *buffer,        /* Buffer */
                             word  seg )          /* Segment prefix number */
{
    register short oplen;           /* Operand length */
    static char *typenames[] =      /* Type name array */
    {
        "",             /* 0 NOSIZEOP */
        "BYTE PTR ",    /* 1 BYTEOP */
        "WORD PTR ",    /* 2 WORDOP */
        "",
        "DWORD PTR ",   /* 4 DWORDOP */
        "",
        "FWORD PTR ",   /* 6 FWORDOP */
        "",
        "QWORD PTR ",   /* 8 QWORDOP */
        "",
        "TBYTE PTR ",   /* A TBYTEOP */
    };

    /* Get operand length */
    oplen = optyp & OPSIZEMASK;

    /* If virtual int or ptr */
    if (optyp & (VINT | VPTR))
    {

        /* 32 bit int or 48 bit pointer */
        if(optyp & OPER32)
        {
            oplen += VSIZE;
        }
    }

    /* Put type name in buffer */
    strcpylc( buffer, typenames[oplen] );

    /* Skip over the name */
    buffer += strlen(buffer);

    /* Decode segment override prefix */
    if (seg != SEGDEF)
        buffer += sprintflc( buffer, "%s:", segreg[seg] );

    return(buffer);                 /* Return updated pointer */
}




static byte * decodesib( register char *buffer,  /* Formatted output buffer */
                         register byte *stream,  /* Instruction stream */
                         byte           modrm,   /* Mod-R/M byte */
                         byte           sib )    /* SIB byte */
{
    byte sibi;                      /* Index field value */
    bool base;                      /* Base register flag */
    byte sibs;                      /* Scale factor */

    sibi = (byte)sib_index(sib);          /* Get index field value */

    /* If base register used */
    if ( base = (    sib_base(sib) != SIB_BASE_BP
                  || modrm_mod(modrm) != MOD_NODISP ) )
    {
        /* Put base in buffer */
        buffer += d1base( buffer, sib_base(sib) );

        /* Add plus sign if needed */
        if (sibi != SIB_NOINDEX)
            *buffer++ = '+';
    }
    else
    {
        opoff = 0L;             /* Else no base register offset */
    }

    if (sibi != SIB_NOINDEX)        /* If there is an index register */
    {
        sibs = (byte)sib_scale(sib);

        /* Add index register portion */
        opoff += regval(sibi) << sibs;

        /* Decode index in buffer */
        if(sibs == 0 )
        {
            buffer += sprintflc( buffer, "E%s", regword[sibi] );
        }
        else
        {
            buffer += sprintflc( buffer, "E%s*%x", regword[sibi],1<<sibs );
        }
    }

    if (!base)                       /* If displacement instead of base */
    {
        /* Add plus sign if needed */
        if (sibi != SIB_NOINDEX)
            *buffer++ = '+';
        opdisp = *((long *) stream);      /* Save displacement */
        stream += 4;                      /* Update stream pointer */
        decodedisp( buffer, opseg, opdisp, optyp );
    }

    return(stream);                 /* Return (updated) stream pointer */
}




static void decodesignedbyte( char *buffer,  /* Buffer */
                              char sbyte )   /* Signed byte */
{
    char sign = '+';                /* Assume positive displacement */

    if (sbyte < 0)                  /* If negative value */
    {
        sbyte = (byte)-sbyte;       /* Change sign */
        sign = '-';                 /* Change sign character */
    }
    sprintflc( buffer, "%c%02.2x", sign, (byte)sbyte );
}




static void decodeimmed( char  *buffer,   /* Output buffer */
                         dword  immed,    /* Immediate value */
                         word   flags )   /* format flags */
{
    decodeaddr( buffer, EXPR_TYPE_DATA, 0, 0, immed, 0, flags );
}




static void decodedisp( char *buffer,    /* Output buffer */
                        word  seg,       /* Segment Number */
                        long  disp,      /* Displacement */
                        word  optype )   /* Operand type */
{
    decodeaddr( buffer, DefType, 0, segregval(seg), disp, optype & ADDR32, 0 );
}




static void decodeaddr( char *buffer,         /* Output buffer */
                        byte atype,           /* Address type */
                        word atask,           /* Address task number */
                        word aseg,            /* Address segment number */
                        dword aoff,           /* Address offset */
                        bool sizeflag,        /* If TRUE, 32 bit offset size */
                        register word flags ) /* Format flags */
{
    AddrS address;                  /* Address structure */
    char addrbuf[MAXADDRSTR];       /* Holds the max address string */
    word symflags;

    address.AddrOff = aoff;         /* Fill in address struct */
    address.AddrSeg = aseg;
    address.AddrType = atype;
    address.AddrTask = atask;
    address.AddrSize = 4;

    /* If size is TRUE, 32 bit size */
    if (sizeflag)
    {
        flags |= FMT_ADDR_32ENV;
    }

    if (!(flags & FMT_ADDR_32ENV))
    {

        /* If address type is seg or sel */
        if (address.AddrType & EXPR_TYPE_SEGSEL)
        {
            /* Mask off high word */
            address.AddrOff &= 0xffff;
        }
    }

    /* Put address into buffer */
    fmtaddr( addrbuf, &address, flags );

    /* If segment/selector field formatted, get group name */
    symflags = flags & FMT_ADDR_SEG ? MAP_GETGROUP : 0;

    /* Get symbol for the address */
    if ( getsymbuf( buffer, &address, symflags ) )
    {
        buffer += strlen(buffer);
        sprintflc( buffer, " (%s)", addrbuf );
    }
    else
    {
        /* Get symbol for a 32 bit linear offset */
        if (    (address.AddrType == EXPR_TYPE_DATA)
             && (flags & FMT_ADDR_32ENV) )
        {
            address.AddrType = EXPR_TYPE_LIN;
            strcpylc( buffer, "OFFSET " );
            if ( getsymbuf( buffer + strlen(buffer),
                            &address,
                            symflags ) )
            {
                buffer += strlen(buffer);
                sprintflc( buffer, " (%s)", addrbuf );
                return;
            }
        }
        strcpylc( buffer, addrbuf );
    }
}




void fmtaddr( char           *buffer,      /* Output buffer */
              register AddrS *address,     /* Pointer to address structure */
              register word   flags )      /* Format flags */
{
    char *typestr = "";             /* Address type string */
    char *fmtstr;                   /* Address output format string */
    byte  type;                     /* Address type code */

    /* Get default address type */
    type = address->AddrType & EXPR_TYPE_DEF ? DefType : address->AddrType;
    switch (type)
    {
        case EXPR_TYPE_DATA:
            /* don't format segment/selector field */
            flags &= ~FMT_ADDR_SEG;
            break;

        case EXPR_TYPE_SEG:
            /* if seg should be formated, format type char */
            if (flags & FMT_ADDR_SEG)
            {
                typestr = "&";
            }
            break;

        case EXPR_TYPE_SEL:
            /* if sel should be formated, format type char */
            if (flags & FMT_ADDR_SEG)
            {
                typestr = "#";

                /* If selector is LDT and not current */
                if (    (address->AddrSeg & TABLE_MASK)
                     && (address->AddrTask != GetCurrTask()) )
                {
                    flags |= FMT_ADDR_TASK;
                }
            }
            break;

        case EXPR_TYPE_LIN:
            typestr = "%";
            flags |= FMT_ADDR_LINPHY;

            /* don't format segment/selector field */
            flags &= ~FMT_ADDR_SEG;
            break;

        case EXPR_TYPE_PHY:
            typestr = "%%";
            flags |= FMT_ADDR_LINPHY;

            /* don't format segment/selector field */
            flags &= ~FMT_ADDR_SEG;
            break;

        default:
            /* format seg/sel and task number fields */
            flags |= FMT_ADDR_SEG | FMT_ADDR_TASK;
            break;
    }

    /* If type == current address type, don't format type string */
    if (type == DefType)
    {
        typestr = "";
    }

    /* format address type string */
    buffer += sprintf( buffer, "%s", typestr );

    /* format task number */
    if (flags & FMT_ADDR_TASK)
    {
        buffer += sprintf( buffer, "%04x|", address->AddrTask );
    }

    /* format segment/selector value */
    if (flags & FMT_ADDR_SEG)
    {
        buffer += sprintf( buffer, "%04x:", address->AddrSeg );
    }

    /* if 386 environment, always format offset as 32 bits */
    if (flags & FMT_ADDR_32ENV)
    {
        fmtstr = "%08.8lx";
    }
    else
    {
        /* if 286 environment, format as 24 bits for phy/lin */
        if (flags & FMT_ADDR_LINPHY)
        {
            fmtstr = "%06.6lx";
        }
        else
        {
            fmtstr = "%04.4lx";
        }
    }

    /* format offset */
    sprintf( buffer, fmtstr, address->AddrOff );
}




static int d0base( char *buffer,   /* Output buffer */
                   word  rm )      /* R/M field value */
{
    register word off;              /* Base offset value */

    off = 0;                        /* Initialize */
    switch (rm)                     /* Switch on R/M value */
    {

        case D0RM_BASE_BXSI:            /* [BX+SI] */
            /* Get BX value */
            off = (word) regval(REG_BX);

            /* Drop through */

        case D0RM_BASE_SI:              /* [SI] */
            /* Get/add SI value */
            off += (word) regval(REG_SI);
            break;

        case D0RM_BASE_BXDI:            /* [BX+DI] */
            /* Get DI value */
            off = (word) regval(REG_DI);

            /* Drop through */

        case D0RM_BASE_BX:              /* [BX] */
            /* Get/add BX value */
            off += (word) regval(REG_BX);
            break;

        case D0RM_BASE_BPSI:            /* [BP+SI] */
            /* Get SI value */
            off = (word) regval(REG_SI);

            /* Drop through */

        case D0RM_BASE_BP:              /* [BP] */
            /* Get/add BP value */
            off += (word) regval(REG_BP);

            /* SS-relative by default */
            if (opseg == SEGDEF)
            {
                opseg = SEGSS;
            }
            break;

        case D0RM_BASE_BPDI:            /* [BP+DI] */
            /* Get BP value */
            off = (word) regval(REG_BP);

            /* SS-relative by default */
            if (opseg == SEGDEF)
            {
                opseg = SEGSS;
            }

            /* Drop through */

        case D0RM_BASE_DI:              /* [DI] */
            /* Get/add DI value */
            off += (word) regval(REG_DI);
            break;
    }

    opoff = (dword) off;            /* Save offset */

    /* Decode into buffer */
    return( sprintflc( buffer, "%s", d0rmmem[rm] ) );
}




static int d1base( char *buffer,   /* Output buffer */
                   word  rm )      /* R/M (or SIB base) field value */
{
    /*
     *  NOTE: REG_SP as the value of the R/M field of
     *  a Mod-REG-R/M byte indicates that an SIB byte
     *  follows.  It is assumed that this function will
     *  not be called to process an R/M field without
     *  first checking for the value REG_SP.
     */

    /* Check for SS-relative default */
    if (rm == D1RM_BASE_BP || rm == SIB_BASE_SP )
    {
        /* SS-relative by default */
        if (opseg == SEGDEF)
        {
            opseg = SEGSS;
        }
    }

    /* Set the base */
    opoff = regval(rm);

    /* If ESP is base register for POP */
    if ( rm == SIB_BASE_SP && (optyp & POPESP) )
    {
        opoff += 2;             /* Add two */
        if (optyp & OPER32)
            opoff += 2;         /* Add two more if 32-bit operand */
    }

    /* Decode into buffer */
    return( sprintflc( buffer, "E%s", regword[rm] ) );
}





static byte map0Fop( byte op2 )   /* 2nd byte of two-byte opcode */
{
    byte u;                         /* Upper limit */
    byte l;                         /* Lower limit */
    byte p;                         /* Pivot */

    l = 1;                          /* Initialize */
    u = (byte)( op0Fmap[0] + 1 );   /* Initialize */
    while (l <= u)                  /* While not done */
    {
        p = (byte)( (l + u) >> 1 ); /* Calculate pivot */
        if (op2 == op0Fmap[p])
            return(p);              /* Return index if match found */
        else if (op2 < op0Fmap[p])
            u = (byte)( p - 1 );    /* Else search lower half */
        else
            l = (byte)( p + 1 );    /* Else search upper half */
    }

    return(0);                      /* Search failed */

}
