/***************************************************************************
**
**  File name : ASM86.C
**
**  Description : main module and parser of assembler
**
**  Status:  REVIEWED
**
**  $Log:   S:/tbird/mt2_186/dadamd/asm86.c_v  $
** 
**    Rev 1.0   26 Jun 1997 15:25:52   cjchen
** Initial revision.
** 
**    Rev 1.1   03 Apr 1997 15:06:22   Judy
** No change.
** 
**    Rev 1.0   16 Dec 1996 16:43:28   Judy
** Initial revision.
** 
**    Rev 1.12   21 Oct 1994 18:21:46   steve
** when opAddrSize is AUTO, get the default size from address descriptor.
** 
**    Rev 1.11   21 Oct 1994 11:49:30   steve
** Assembler supports address and operand overrides in 32-bit mode.
** 
**    Rev 1.10   20 Oct 1994 13:41:36   steve
** fixed a DB bug
** 
**    Rev 1.9   19 Oct 1994 10:38:42   steve
** Under 32-bit mode, it sometimes got not enough bytes of immediate value.
** The modification forces assembler always to get 8 bytes for each immediate
** value.
** 
**    Rev 1.8   18 Oct 1994 15:04:28   steve
** fixed a bug which made users couldn't assemble instructions in PROT32 format.
** 
**    Rev 1.7   17 Oct 1994 11:56:56   steve
** fixed a bug of the [EBP][index]disp case
** 
**    Rev 1.6   17 Oct 1994 08:31:14   steve
** Fixed a SIB bug
** 
**    Rev 1.5   14 Oct 1994 18:00:42   steve
** defined ESP
** 
**    Rev 1.4   14 Oct 1994 17:56:44   steve
** Modified ParseMemory() to handle SIB format correctly
** 
**    Rev 1.3   13 Oct 1994 14:14:14   steve
** Fixed a bug which was generated when Jcc was a two-byte opcode.
**
**  $Header:   S:/tbird/mt2_186/dadamd/asm86.c_v   1.0   26 Jun 1997 15:25:52   cjchen  $
**
**  Copyright (C) 1994 Microtek International Inc.   All Rights Reserved
**
****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#ifndef __STRING_H
#include <string.h>
#endif

#ifndef __STDIO_H
#include <stdio.h>
#endif

#ifndef __STDLIB_H
#include <stdlib.h>
#endif

#ifndef __CTYPE_H
#include <ctype.h>
#endif

#ifndef _ASMDEF86_
#include "asmdef86.h"
#endif

#ifndef _DASM_
#include "dasm.h"
#endif

#ifndef _DADDEF86_
#include "daddef86.h"
#endif

#ifndef _ASMTBL86_
#include "asmtbl86.h"
#endif

#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef _HEAP_
#include "heap.h"
#endif

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/
#define COMMENT_CHAR ';'
#define XX  99
#define ESP  5
#define EBP  6
#define ENDOFSTR(x) ((x) == '\0' || (x) == '\r' || (x) == COMMENT_CHAR)

PRIVATE CHAR* SegTable[6] = {
    "ES", "CS", "SS", "DS", "FS", "GS"
};
PRIVATE CHAR* Reg8Table[8] = {
    "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH"
};
PRIVATE CHAR* Reg16Table[8] = {
    "AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI"
};
PRIVATE CHAR* Reg32Table[8] = {
    "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI"
};
/* The order in PtrTable must match order in enum OPERAND_TYPE (ASMDEF.H) */
PRIVATE CHAR* PtrTable[] = {
    "BYTE", "WORD", "DWORD", "QWORD", "TBYTE", "NEAR", "FAR", "FWORD"
};
#define FWORD     7   // should be consist with the order in PtrTable

PRIVATE CHAR* BaseIndexRegTable[4] = {
    "DI", "SI", "BP", "BX",
};
PRIVATE CHAR* SpecialRegTable[11] = {
   "CR0","CR2","CR3", "DR0","DR1","DR2","DR3","DR6","DR7", "TR6","TR7"
};
PRIVATE U8 SpecialRegMap[11] = {
   0, 2, 3,  0, 1, 2, 3, 6, 7,  6, 7
};
/* use BX-BP-SI-DI 4 bits as index to get r/m bits */
PRIVATE U8 BaseIndexTable[16] = {
    6, 5, 4, XX,   6, 3, 2, XX,   7, 1, 0, XX,   XX, XX, XX, XX
};

PRIVATE BOOLEAN fOper32, fAddr32, fSIB, fTwobyte, fReg16, fData16;
PRIVATE ADDR_OP_SIZE opAddrSize;
PRIVATE U8      SegOverride;
PRIVATE U32     asmIP;
PRIVATE U16     MaxDefSize;   /* max size when processing DB and DW */
PRIVATE CHAR*   LookAhead;
PRIVATE CHAR    Mnemonic[80], Token[80];
PRIVATE U8      ExtraCodes[5], ExtraLen, SIB;

                        /****************************
                         *                          *
                         *     LOCAL PROTOTYPES     *
                         *                          *
                         ****************************/
PRIVATE RETCODE IdentifyMnemonic(CHAR* Mnemonic, U16* ID);
PRIVATE RETCODE IdentifyFormat(U16 MnemonicID, OPERANDSTRUCT *Operands,
                               TYPEROUTINE *Type);
PRIVATE RETCODE GetType(U16 MnemonicID, U8 *Format, TYPEROUTINE *Type);
PRIVATE RETCODE GetCode(U16 MnemonicID, TYPEROUTINE Type, LPSTR Codes);
PRIVATE RETCODE GetToken(VOID);
PRIVATE RETCODE GetWholeToken(VOID);
PRIVATE RETCODE PseudoDS(LPWORD Length);
PRIVATE RETCODE PseudoDBDW(S16 Size, LPSTR Codes, LPWORD Length);
PRIVATE RETCODE GetOperands(U16 *pMnemonic, OPERANDSTRUCT *OperandBuffer);
PRIVATE RETCODE checkAddr32(VOID);
PRIVATE RETCODE ParseST(OPERANDSTRUCT *Operand);
PRIVATE RETCODE ParseData(CHAR* Prefix, OPERANDSTRUCT *Operand);
PRIVATE RETCODE ParseNumber(CHAR* Prefix, S32 *Number);
PRIVATE RETCODE ParseMemory(OPERANDSTRUCT *Operand);
PRIVATE RETCODE SearchTable(CHAR* Table[], U16 TableLen, CHAR* Key, U16* ID);
PRIVATE BOOLEAN AreXdigits(CHAR* s);

                        /****************************
                         *                          *
                         *      EXECUTABLE CODE     *
                         *                          *
                         ****************************/
/***************************************************************************
**
**  Name: AsmInst(id, Instruction, Codes, Length, OpCode)
**
**  Description:
**     assemble one line of instruction into object codes
**
**  Input:  address, instruction
**
**  Output: code data, code length
**
**  Return: GOOD if success
**
****************************************************************************/
RETCODE AsmInst(DESCRIPTOR id, LPSTR Instruction, LPSTR *outCodes,
                LPWORD Length, LPSTR OpCode)
{
U16           SegID, MnemonicID, i;
S32           offset;
OPERANDSTRUCT Operands[MAXOPERAND];
TYPEROUTINE   Type;
LPSTR         StrPtr;
U8            Codes[MAX_CODE_LENGTH];
DESCRIPTOR    addr;
PROBE_TYPE    Processor;
RETCODE       Result;


   if ((DAD_STATE FAR *)id == NULL)
      return(ER_DAD_INVALID_ID_DESCRIPTOR);
   addr = ((DAD_STATE FAR *)id)->startAddr;

   if ((StrPtr = *outCodes = (LPSTR)TMalloc(strlen(Instruction) +
         MAX_LINE_PER_INST*MAX_LINE_LENGTH)) == NULL)
      return ER_OUT_OF_MEMORY;

   SegOverride = SIB = 0;
   fOper32 = fAddr32 = fSIB = fTwobyte = fReg16 = fData16 = FALSE;
   if ( (MaxDefSize = *Length) == 0 )  MaxDefSize = 0xFFFF;
   if ((Result = AdrGetAddrOffset(addr, &asmIP)) != GOOD)
      return(Result);
   if ((Result = DadGetDasmOpAddrSize(id, &opAddrSize)) != GOOD)
      return(Result);
   if (opAddrSize == ADDR_USE_AUTO) {
      if ((Result = AdrGetDefaultSize(addr, &opAddrSize)) != GOOD)
         return(Result);
   }
   *Length = 0;

   LookAhead = Instruction;
   GetToken();
   strcpy(Mnemonic, Token);

/*** Process pseudo instructions ***/
   if (stricmp(Mnemonic,"DS") == 0 && *LookAhead != ':') {
      PseudoDS(Length);
      goto DONE_ASM;
   }
   if (stricmp(Mnemonic,"DB") == 0) {
      PseudoDBDW(1, Codes, Length);
      goto DONE_ASM;
   }

/*** Process prefix in instruction ***/
   ExtraLen = 0;
   if (stricmp(Mnemonic,"LOCK") == 0)
      ExtraCodes[ExtraLen++] = 0xF0;
   else if (stricmp(Mnemonic,"REP") == 0 || stricmp(Mnemonic,"REPE") == 0 ||
            stricmp(Mnemonic,"REPZ") == 0)
      ExtraCodes[ExtraLen++] = 0xF3;
   else if (stricmp(Mnemonic,"REPNE") == 0 || stricmp(Mnemonic,"REPNZ") == 0)
      ExtraCodes[ExtraLen++] = 0xF2;
   else if (SearchTable(SegTable, COUNT(SegTable), Mnemonic, &SegID) == GOOD) {
      if (SegID < 4)  /* ES: CS: SS: DS: */
         SegOverride = 0x26 + (SegID << 3);
      else   /* FS: GS: */
         SegOverride = 0x60 + SegID;

      GetToken();          /* read ':' */
      if (*Token != ':')   /* ES: CS: SS: DS: FS: GS: */
         return (ER_DAD_ASM_ERROR);
   }
   if (ExtraLen > 0) {                       /* has prefix */
      if (*LookAhead == ':') GetToken();     /* get rid of ':' */
      memcpy(Codes, ExtraCodes, ExtraLen);   /* preset Codes and Length to */
      *Length = ExtraLen;                    /*   avoid having prefix only */
      GetToken();
      strcpy(Mnemonic, Token);
   }

/*** Check instruction format and obtain op-codes ***/
   if (*Mnemonic == '\0')   /* null instruction */
      return (GOOD);
   // some operand-size override cases
   if (stricmp(Mnemonic, "CWDE") == 0  || stricmp(Mnemonic, "CDQ") == 0 ||
       stricmp(Mnemonic, "IRETD") == 0 || stricmp(Mnemonic, "JECXZ") == 0 ||
       stricmp(Mnemonic, "CMPSD") == 0 || stricmp(Mnemonic, "INSD") == 0 ||
       stricmp(Mnemonic, "LODSD") == 0 || stricmp(Mnemonic, "MOVSD") == 0 ||
       stricmp(Mnemonic, "OUTSD") == 0 || stricmp(Mnemonic, "SCASD") == 0 ||
       stricmp(Mnemonic, "POPAD") == 0 || stricmp(Mnemonic, "POPFD") == 0 ||
       stricmp(Mnemonic, "PUSHAD") ==0 || stricmp(Mnemonic, "PUSHFD") == 0 ||
       stricmp(Mnemonic, "STOSD") == 0
       ) {
      fOper32 = TRUE;
      if (stricmp(Mnemonic, "JECXZ") == 0)
        asmIP++; // due to one byte prefix, shift one byte rightward
   }

   if (IdentifyMnemonic(Mnemonic, &MnemonicID) != GOOD)
      return (ER_DAD_ASM_ERROR);
   if ((Result = GetOperands(&MnemonicID, Operands)) != GOOD)
      return (Result);
   if ((Result = IdentifyFormat(MnemonicID, Operands,  &Type)) != GOOD)
      return (Result);
   // calculate the exact offset of address
   if (Type == Atype_Xs1_disp || Type == Atype_X_disp) {
      offset = Operands[0].Number - (asmIP + 2);
      if (offset >= -0x80 && offset <= 0x7F)
         Type = Atype_X_disp;
      else
         Type = Atype_Xs1_disp;
   }

   if ((Result = GetCode(MnemonicID, Type, Codes)) != GOOD)
      return (Result);

   // if this is a two-byte opcode the offset should be less 1.
   if (fTwobyte && (Type == Atype_Xs1_disp || Type == Atype_X_disp))
      Operands[0].Number--;

#pragma warn -pro
/*** Call type processing routine to generate object codes ***/
   if (Type == Atype_Xs1_disp || Type == Atype_X_disp ||
       Type == Atype_X_ldisp_hdisp) {
      Result = (*Type)(asmIP, Operands, Codes, Length);
   }
   else {
      Result = (*Type)(Operands, Codes, Length);
   }
#pragma warn .pro

   if (Result != GOOD) return(Result);

   if (fSIB) {
      if (*Length == 2)
         Codes[(*Length)++] = SIB;
      else {
         memmove(Codes+3, &(Codes[2]), (*Length)-2);
         (*Length)++;
         Codes[2] = SIB;
      }
   }

   if (opAddrSize == ADDR_USE_32) {
      if (fAddr32 || fOper32)
         fAddr32 = fOper32 = FALSE;
      else {
         if (fReg16)
            fAddr32 = TRUE;
         if (fData16)
            fOper32 = TRUE;
      }
   }

   ProcReturnSpecificProcessor(&Processor);
   if (Processor == I80C186_MP) {
      if (fAddr32 || fOper32 || fTwobyte ||  // only for 32-bit mode
          SegOverride >= 0x64)               // FS or GS
         return(ER_DAD_ASM_ERROR);
   }
   if (fAddr32 == TRUE)
      ExtraCodes[ExtraLen++] = 0x67;
   if (fOper32 == TRUE)
      ExtraCodes[ExtraLen++] = 0x66;
   if (SegOverride > 0)
      ExtraCodes[ExtraLen++] = SegOverride;
   if (fTwobyte == TRUE)
      ExtraCodes[ExtraLen++] = 0x0F;
   if (ExtraLen > 0) {
      memmove(Codes+ExtraLen, Codes, *Length);
      memcpy(Codes, ExtraCodes, ExtraLen);
      *Length += ExtraLen;
   }

DONE_ASM:
   if ((Result = AdrConvAddressToText(addr, *outCodes)) != GOOD)
      return(Result);
   StrPtr += strlen(*outCodes);
   for (i=0; i<*Length; i++) {
      StrPtr += sprintf(StrPtr, " %02X", Codes[i]);
      OpCode[i] = Codes[i]; 
   }
   StrPtr += sprintf(StrPtr, "   ");
   sprintf(StrPtr, "%s", Instruction);

   return (Result);
}  /* end of Assemble() */

/***************************************************************************
**
**  Name: IdentifyMnemonic(Mnemonic, ID)
**
**  Description:
**     identify if mnemonic symbol is correct
**
**  Input:  mnemonic symbol
**
**  Return: index of matched record in assemble table if success,-1 otherwise
**
****************************************************************************/
PRIVATE RETCODE IdentifyMnemonic(CHAR* Mnemonic, U16 *ID)
{
int MnemonicCmp(CHAR*, ASMSTRUCT*);
int (*fcmp)();
CHAR* ptr;

   fcmp = MnemonicCmp;
   ptr = bsearch(Mnemonic, AsmTable, AsmTableLen, sizeof(AsmTable[0]), fcmp);
   if (ptr == NULL)  return (!GOOD);
   *ID = ((U16)ptr-(U16)AsmTable) / sizeof(AsmTable[0]);
   return (GOOD);
}  /* end of IdentifyMnemonic() */

/***************************************************************************/
PRIVATE int MnemonicCmp(CHAR* Mnemonic, ASMSTRUCT *AsmRecord)
{
   return (stricmp(Mnemonic, AsmRecord->Mnemonic));
}

/***************************************************************************
**
**  Name: IdentifyFormat(MnemonicID, Operands, Type)
**
**  Description:
**     identify if there exists suitable format of operands
**
**  Input:  ID of mnemonic symbol, buffer of operands information
**
**  Output: code-type routine pointer
**
**  Return: error code
**
****************************************************************************/
PRIVATE RETCODE IdentifyFormat(U16 MnemonicID, OPERANDSTRUCT *Operands,
                               TYPEROUTINE *Type)
{
U8 AllFormats[MAXOPERAND][4];
U16 FormatCounts[MAXOPERAND];
U8 OperandType, MemberID, Format[MAXOPERAND];
U16 i, j, k, l;

/*** Find possible operand types of operand ***/
   for (i = 0; i < MAXOPERAND; i++) {
      FormatCounts[i] = 0;
      OperandType = Operands[i].OperandType;
      MemberID    = Operands[i].MemberID;
   /* check special operand */
      if (OperandType == REG8) {
         if (MemberID == 0)
            AllFormats[i][FormatCounts[i]++] = AL;
         else if (MemberID == 1)
            AllFormats[i][FormatCounts[i]++] = CL;
      }
      else if (OperandType == REG16) {
         if (MemberID == 0)
            AllFormats[i][FormatCounts[i]++] = AX;
         else if (MemberID == 2)
            AllFormats[i][FormatCounts[i]++] = DX;
      }
      else if (MemberID == 6 && !fAddr32) {   /* direct address */
         if (OperandType == MEM)
            AllFormats[i][FormatCounts[i]++] = MEMDIRECT;
         else if (OperandType == MEMBYTE)
            AllFormats[i][FormatCounts[i]++] = MEMDIRECTBYTE;
         else if (OperandType == MEMWORD)
            AllFormats[i][FormatCounts[i]++] = MEMDIRECTWORD;
         else if (OperandType == MEMDWORD)
            AllFormats[i][FormatCounts[i]++] = MEMDIRECTDWORD;
      }
      else if (MemberID == 5 && fAddr32) {   /* 32-bit direct address */
         if (OperandType == MEM || OperandType == MEMDWORD)
            AllFormats[i][FormatCounts[i]++] = MEMDIRECTDWORD;
         else  //case of (OperandType == MEMBYTE || OperandType == MEMWORD)
            return (ER_DAD_ASM_ERROR);
      }
      else if (OperandType == DATA) {
         if (Operands[i].Number == 1)
            AllFormats[i][FormatCounts[i]++] = DATAONE;
         if (Operands[i].Number > -0x100L && Operands[i].Number < 0x100L)
            AllFormats[i][FormatCounts[i]++] = DATA8;
      }
      AllFormats[i][FormatCounts[i]++] = OperandType;
   }  /* end of for */

/*** try all possible combinations of operand formats ***/
   for (i = 0; i < FormatCounts[0]; i++) {
      Format[0] = AllFormats[0][i];
      for (j = 0; j < FormatCounts[1]; j++) {
         Format[1] = AllFormats[1][j];
         for (k = 0; k < FormatCounts[2]; k++) {
            Format[2] = AllFormats[2][k];
            if (GetType(MnemonicID, Format, Type) == GOOD) {
               for (l=0; l<MAXOPERAND; l++) {
                  Operands[l].OperandType = Format[l];
                  if (opAddrSize == ADDR_USE_32 && Format[l] == DATA8)
                     fData16 = FALSE;
               }
               return (GOOD);
            }
         }
      }
   }
   return (ER_DAD_ASM_ERROR);
}  /* end of IdentifyFormat() */

/***************************************************************************
**
**  Name: GetType(MnemonicID, Format,  Type)
**
**  Description:
**     search matched format in format table to obtain code type
**
**  Input:  ID of mnemonic symbol, format of operand
**
**  Output: code-type routine
**
**  Return: error code
**
****************************************************************************/
PRIVATE RETCODE GetType(U16 MnemonicID, U8 *Format,  TYPEROUTINE *Type)
{
FORMATSTRUCT *FormatTable;
U16 TotalFormat, i;

   FormatTable = AsmTable[MnemonicID].FormatTable;
   TotalFormat = AsmTable[MnemonicID].TotalFormat;
   for (i = 0; i < TotalFormat; i++)
      if (memcmp(Format, FormatTable[i].Format, MAXOPERAND) == 0) {
         *Type = FormatTable[i].Type;
         return (GOOD);
      }
   return (ER_DAD_ASM_ERROR);
}  /* end of GetType() */

/***************************************************************************
**
**  Name: GetCode(MnemonicID, Type,  Codes)
**
**  Description:
**     search matched type in code table to obtain op-codes
**
**  Input:  ID of mnemonic symbol, code-type routine
**
**  Output: op-codes
**
**  Return: error code
**
****************************************************************************/
PRIVATE RETCODE GetCode(U16 MnemonicID, TYPEROUTINE Type, LPSTR Codes)
{
CODESTRUCT *CodeTable;
U16 TotalCode, i;

   CodeTable = AsmTable[MnemonicID].CodeTable;
   TotalCode = AsmTable[MnemonicID].TotalCode;
   for (i = 0; i < TotalCode; i++)
      if (Type == CodeTable[i].Type) {
         if (CodeTable[i].Codes[0] == 0x0F) { // two-byte Opcode
            memcpy(Codes, &(CodeTable[i].Codes[1]), MAXOPCODE-1);
            Codes[MAXOPCODE-1] = 0;
            fTwobyte = TRUE;
         }
         else
            memcpy(Codes, CodeTable[i].Codes, MAXOPCODE);
         return (GOOD);
      }
   return (ER_DAD_ASM_ERROR);
}  /* end of GetCode() */

/***************************************************************************
**                       Procedures of parsing operands
****************************************************************************/

/***************************************************************************
**
**  Name: GetToken()
**
**  Description:
**     get an token from string variable LookAhead
**
**  Notes:  LookAhead always points to next available token
**
****************************************************************************/
PRIVATE RETCODE GetToken()
{
   CHAR* TokenPtr;

   TokenPtr = Token;

/*** Skip leading white spaces ***/
   while (isspace(*LookAhead))
      LookAhead++;

/*** Extract token ***/
   if (isalnum(*LookAhead) || *LookAhead == '_')
      while (isalnum(*LookAhead) || *LookAhead == '_')
         if (TokenPtr-Token >= sizeof(Token)-1)  LookAhead++;
         else  *(TokenPtr++) = *(LookAhead++);
   else if (!ENDOFSTR(*LookAhead))
      *(TokenPtr++) = *(LookAhead++);
   *TokenPtr = '\0';

/*** Skip tailing white spaces ***/
   while (!ENDOFSTR(*LookAhead) && isspace(*LookAhead))  // skip to next char
      LookAhead++;

   return(GOOD);
}  /* end of GetToken() */

/***************************************************************************
**
**  Name: GetWholeToken()
**
**  Description:
**     get the whole token from the rest of string variable LookAhead
**
****************************************************************************/
PRIVATE RETCODE GetWholeToken()
{
   CHAR *TokenPtr, *LookAheadPtr;

   TokenPtr = Token;
   LookAheadPtr = LookAhead;

/*** Skip leading white spaces ***/
   while (!ENDOFSTR(*LookAhead)) {
      if (!isspace(*LookAhead))
         *(TokenPtr++) = *(LookAhead++);
      else
         LookAhead++;
   }
   *TokenPtr = '\0';

   LookAhead = LookAheadPtr;
   return(GOOD);
}  /* end of GetWholeToken() */

/***************************************************************************
**
**  Name: PseudoDS(Length)
**
**  Description:
**     process the pseudo instruction: DS (define storage)
**
**  Output: storage size
**
**  Return: error code
**
*****************************************************************************/
PRIVATE RETCODE PseudoDS(LPWORD Length)
{
S32 Number;
RETCODE Result;

   if (ENDOFSTR(*LookAhead))  return (GOOD);
   GetToken();
   if (*Token != '+' && !isxdigit(*Token))  return (ER_DAD_ASM_ERROR);
   if ((Result = ParseNumber(Token,  &Number)) != GOOD)
      return (Result);
   *Length = (U16)Number&0xFFFF;
   return (ER_DAD_ASM_ERROR);
}  /* end of PseudoDS() */

/***************************************************************************
**
**  Name: PseudoDBDW(Size,  Codes, Length)
**
**  Description:
**     process the pseudo instruction: DB, DW
**
**  Input:  size = 1 for DB, 2 for DW
**
**  Output: codes, code length
**
**  Return: error code
**
****************************************************************************/
PRIVATE RETCODE PseudoDBDW(S16 Size, LPSTR Codes, LPWORD Length)
{
CHAR Quote;
S32 Number;
U16  SaveLen, DupSize, i;
RETCODE Result;
BOOLEAN HasData = FALSE;

   while (TRUE) {
      if (ENDOFSTR(*LookAhead)) break;
      if (*LookAhead == ')')   /* end of DUP ? */
         if (HasData)  break;
         else  return (ER_DAD_ASM_ERROR);

   /*** Define string ***/
      if (*LookAhead == '"' || *LookAhead == '\'') {
         Quote = *(LookAhead++);
         if (*LookAhead == Quote)  return (ER_DAD_ASM_ERROR);
         while (*LookAhead != Quote) {
            if (*LookAhead != COMMENT_CHAR && ENDOFSTR(*LookAhead))
               return (ER_DAD_ASM_ERROR);
            if ((U16)(*Length) >= MaxDefSize)  return (ER_DAD_ASM_ERROR);
            Codes[(*Length)++] = *(LookAhead++);
         }
         GetToken();   /* read ' or " */
      }

   /*** Define data ***/
      else {
         GetToken();
         if (*Token != '+' && *Token != '-' && !isxdigit(*Token))
            return (ER_DAD_ASM_ERROR);
         if ((Result = ParseNumber(Token,  &Number)) != GOOD)
            return (Result);

         if (strnicmp(LookAhead, "DUP", 3) == 0) {
            GetToken();
            if (stricmp(Token, "DUP") != 0)  return (ER_DAD_ASM_ERROR);
            if (Number <= 0)  return (ER_DAD_ASM_ERROR);
            GetToken();
            if (*Token != '(')  return (ER_DAD_ASM_ERROR);
            SaveLen = *Length;
            if ((Result=PseudoDBDW(Size, Codes, Length)) != GOOD)
               return (Result);
            GetToken();
            if (*Token != ')')  return (ER_DAD_ASM_ERROR);
            DupSize = *Length - SaveLen;
            /* check definition size */
            if (((U32)(*Length) + (Number-1) * (U32)DupSize) > MaxDefSize)
               return (ER_DAD_ASM_ERROR);
            for (i = 1; i < Number; i++)
               memcpy(&Codes[SaveLen + DupSize * i],
                      &Codes[SaveLen], DupSize);
            *Length += ((U16)Number - 1) * DupSize;
         }
         else if (Size == 2) {   /* DW */
            if ((U16)(*Length) >= MaxDefSize-1)  return (ER_DAD_ASM_ERROR);
            Codes[(*Length)++] = Number & 0x00FF;
            Codes[(*Length)++] = (Number & 0xFF00) >> 8;
         }
         else if (Size == 1) {   /* DB */
            if (Number <= -0x100 || Number >= 0x100)  return(ER_DAD_ASM_ERROR);
            if ((U16)(*Length) >= MaxDefSize)  return (ER_DAD_ASM_ERROR);
            Codes[(*Length)++] = Number & 0x00FF;
         }
      }
      HasData = TRUE;
      if (*LookAhead == ',')  GetToken();
   }  /* end of while */
   return (GOOD);
}  /* end of PseudoDBDW() */

/***************************************************************************
**
**  Name: GetOperands(OperandBuffer)
**
**  Description:
**     parse the operands
**
**  Output: operand information
**
**  Return: error code
**
****************************************************************************/
PRIVATE RETCODE GetOperands(U16 *pMnemonicID, OPERANDSTRUCT *OperandBuffer)
{
OPERANDSTRUCT *Operand;
RETCODE Result;
S32 Number;
U16 i, ID;

   for (i = 0; i < MAXOPERAND; i++)
      OperandBuffer[i].OperandType = NONE;

   if (ENDOFSTR(*LookAhead))  return (GOOD);

   /* special cases */
   // case 1: OUTS DX, ....
   if (stricmp(AsmTable[*pMnemonicID].Mnemonic, "OUTS") == 0) {
      GetToken();
      if (stricmp(Token, "DX") == 0 && *LookAhead == ',') {
         GetToken();
         GetToken();
         if (stricmp(Token, "DS") == 0) {
            if (*LookAhead != ':')
               return (ER_DAD_ASM_ERROR);
            GetToken();
         }
         GetToken();
         if (stricmp(Token, "[") == 0) {
            GetToken();
            if (stricmp(Token, "SI") == 0 || stricmp(Token, "ESI") == 0) {
               GetToken();
               if (stricmp(Token, "]") == 0 && ENDOFSTR(*LookAhead)) {
                  *pMnemonicID += 2;  // default OUTSW
                  return(GOOD);
               }
            }
         }
         return (ER_DAD_ASM_ERROR);
      }
   }

   // case 2: XLAT ....
   if (stricmp(AsmTable[*pMnemonicID].Mnemonic, "XLAT") == 0) {
      GetToken();
      if (SearchTable(SegTable, COUNT(SegTable), Token, &ID) == GOOD) {
         GetToken();  // get rid of ':'
         GetToken();  // get rid of '['
      }
      GetWholeToken();
      if (stricmp(Token, "EBX]") == 0)   // XLAT [EBX]
         ExtraCodes[ExtraLen++] = 0x67;   // prefix of addr size
      else if (*Token!='\0' && stricmp(Token, "BX]") != 0)
         return (ER_DAD_ASM_ERROR);   // not XLAT, XLAT [BX], XLAT [EBX]

      return(GOOD);
   }

   for (i = 0; i < MAXOPERAND; i++) {
      Operand = &OperandBuffer[i];
      Operand->MemberID = Operand->Number = Operand->Extra = 0;
      GetToken();

   /*** Check segment operand ***/
      if (*LookAhead != ':' && (SearchTable(SegTable, COUNT(SegTable), Token,
            (U16*)&Operand->MemberID)) == GOOD)
         Operand->OperandType = SEG;

   /*** Check register operand ***/
      else if (SearchTable(Reg8Table, COUNT(Reg8Table), Token,
                 (U16*)&Operand->MemberID) == GOOD)
         Operand->OperandType = REG8;
      else if (SearchTable(Reg16Table, COUNT(Reg16Table), Token,
                 (U16*)&Operand->MemberID) == GOOD) {
         if (opAddrSize == ADDR_USE_32 && !fAddr32)
            fReg16 = TRUE;
         Operand->OperandType = REG16;
      }
      else if (SearchTable(Reg32Table, COUNT(Reg32Table), Token,
                 (U16*)&Operand->MemberID) == GOOD) {
         fOper32 = TRUE;
         Operand->OperandType = REG16;
      }
      else if (SearchTable(SpecialRegTable, COUNT(SpecialRegTable), Token, &ID) == GOOD) {
         if (ID < 3) // CR0/CR2/CR3
            Operand->OperandType = CR;
         else if (ID < 9) // DR0/DR1/DR2/DR3/DR6/DR7
            Operand->OperandType = DR;
         else  // TR6/TR7
            Operand->OperandType = TR;

         Operand->MemberID = SpecialRegMap[ID];
      }

   /*** Check ST or ST(i) operand ***/
      else if (stricmp(Token, "ST") == 0) {
         if ((Result = ParseST(Operand)) != GOOD)
            return (Result);
      }

   /*** Check data operand ***/
      else if (stricmp(Token, "SHORT") == 0) {
         GetToken();
         if (*Token == '+' || *Token == '-' ||
                  *Token == '%' || isdigit(*Token) || AreXdigits(Token)) {
            if ((Result = ParseData(Token,  Operand)) != GOOD)
               return (Result);
         }
         else
            return (ER_DAD_ASM_ERROR);
      }

   /*** Check data operand ***/
      else if (*Token == '+' || *Token == '-' ||
               *Token == '%' || isdigit(*Token) || AreXdigits(Token)) {
         if ((Result = ParseData(Token,  Operand)) != GOOD)
            return (Result);
         if (fOper32 && Operand->Extra == 0)
            Operand->Extra = 0xFFFF0000L; //DWORD type if virtual address mode
      }

   /*** Check character operand ***/
      else if (*Token == '\'') {
         if (*LookAhead != COMMENT_CHAR && ENDOFSTR(*LookAhead))
            return (ER_DAD_ASM_ERROR);
         Operand->Number = (S32) ((unsigned CHAR) *(LookAhead++));
         Operand->OperandType = DATA;
         if (*LookAhead != '\'')  return (ER_DAD_ASM_ERROR);
         GetToken();   /* read ' */
      }

   /*** Check $ operand ***/
      else if (*Token == '$') {
         Operand->Number = (S32)(U16)asmIP;
         Operand->OperandType = DATA;
         if (!ENDOFSTR(*LookAhead)) {
            GetToken();
            if (*Token != '+' && *Token != '-')  return (ER_DAD_ASM_ERROR);
            if ((Result = ParseNumber(Token,  &Number)) != GOOD)
               return (Result);
            Operand->Number = (S32)(U16)((Operand->Number + Number)
                                          & 0xFFFFL);
         }
      }

   /*** Check memory operand ***/
      else {
         /* check xxx PTR */
         if (SearchTable(PtrTable, COUNT(PtrTable), Token, &ID) == GOOD) {

            if (ID == FWORD) {
               ID = 2; // DWORD
            }

            // check the mnemonic if it is in the following cases
            // If true, change ID of mnemonic to xxxB, xxxW, or xxxD.
            if ((stricmp(AsmTable[*pMnemonicID].Mnemonic, "CMPS") == 0) ||
                (stricmp(AsmTable[*pMnemonicID].Mnemonic, "INS" ) == 0) ||
                (stricmp(AsmTable[*pMnemonicID].Mnemonic, "LODS") == 0) ||
                (stricmp(AsmTable[*pMnemonicID].Mnemonic, "MOVS") == 0) ||
                (stricmp(AsmTable[*pMnemonicID].Mnemonic, "SCAS") == 0) ||
                (stricmp(AsmTable[*pMnemonicID].Mnemonic, "STOS") == 0)) {
               if (ID > 3)
                  return (ER_DAD_ASM_ERROR);
               switch (ID) {
                  case 0: // BYTE
                     *pMnemonicID = *pMnemonicID + 1;
                     break;
                  case 1: // WORD
                     *pMnemonicID = *pMnemonicID + 3;
                     break;
                  case 2: // DWORD
                     *pMnemonicID = *pMnemonicID + 2;
                     fOper32 = TRUE;
                     break;
               }
               if (checkAddr32() == GOOD)
                  fAddr32 = TRUE;
               return (GOOD);
            } // end IF of CMPS/INS/LODS/MOVS/SCAS/STOS

            Operand->OperandType = MEMBYTE + ID;
            if (Operand->OperandType == MEMDWORD)
                fOper32 = TRUE;

            GetToken();
            if (stricmp(Token, "DWORD")==0 || stricmp(Token, "WORD")==0) {
               if (Operand->OperandType == MEMFAR) {
                  if (stricmp(Token, "WORD") == 0)
                     fOper32 = TRUE;
                  GetToken();
               }
               else if (Operand->OperandType == MEMNEAR) {
                  if (stricmp(Token, "DWORD") == 0)
                     fOper32 = TRUE;
                  GetToken();
               }
               else
                  return(ER_DAD_ASM_ERROR);
            }
            if (stricmp(Token, "PTR") == 0)
               GetToken();
            if (ENDOFSTR(*Token)) return (ER_DAD_ASM_ERROR);
         }

         /* check segment override */
         if (SearchTable(SegTable, COUNT(SegTable), Token, &ID) == GOOD) {
            GetToken();                            /* read ':' */
            if (*Token != ':')                     /* ES: CS: SS: DS: */
               return (ER_DAD_ASM_ERROR);
            if (ID < 4)  /* ES: CS: SS: DS: */
               SegOverride = 0x26 + (ID << 3);
            else   /* FS: GS: */
               SegOverride = 0x60 + ID;
            GetToken();
         }
         if (*Token != '[') {
            if ((Result = ParseData(Token, Operand)) != GOOD)
               return (Result);
            if (fOper32 && Operand->Extra == 0)
               Operand->Extra = 0xFFFF0000L; //DWORD if virtual address mode
         }
         else if ((Result = ParseMemory(Operand)) != GOOD)   /* [...] */
            return (Result);
      }

      if (ENDOFSTR(*LookAhead))  break;
      if (*LookAhead == ',')
         GetToken();
      else
         return (ER_DAD_ASM_ERROR);
   }  /* end of for */

   return (GOOD);
}  /* end of GetOperands() */

/***************************************************************************
**
**  Name: checkAddr32()
**
**  Description:
**     parse the operand to determine whether it is address-size override
**
**  Return: TRUE/FALSE
**
****************************************************************************/
PRIVATE RETCODE checkAddr32() {
   U16 dummy;
   while (!ENDOFSTR(*LookAhead)) {
      GetToken();
      if (SearchTable(Reg32Table, COUNT(Reg32Table), Token, &dummy) == GOOD)
         return(GOOD);
   }
   return(!GOOD);
}


/***************************************************************************
**
**  Name: ParseST(Operand)
**
**  Description:
**     parse ST or ST(i)
**
**  Output: binary number
**
**  Return: error code
**
****************************************************************************/
PRIVATE RETCODE ParseST(OPERANDSTRUCT *Operand)
{
RETCODE Result;
S32 Number;

   Operand->OperandType = ST;
   if (*LookAhead == '(') {
      GetToken();   /* read '(' */
      GetToken();
      if (ENDOFSTR(*Token))  return (ER_DAD_ASM_ERROR);
      if (*Token != '+' && *Token != '-' && !isxdigit(*Token))
         return (ER_DAD_ASM_ERROR);
      if ((Result = ParseNumber(Token,  &Number)) != GOOD)
         return (Result);
      /* Only ST(0) ... ST(7) are allowed */
      if (Number < 0 || Number > 7)  return (ER_DAD_ASM_ERROR);
      Operand->OperandType = STI;
      Operand->MemberID = (U8)Number;
      GetToken();
      if (*Token != ')')  return (ER_DAD_ASM_ERROR);
   }
   return (GOOD);
}  /* end of ParseST() */

#pragma warn -cln
/***************************************************************************
**
**  Name: ParseData(Prefix,  Operand)
**
**  Description:
**     parse data: xxx, +xxx, -xxx, xxxx:xxxx, %symbol, +%symbol, -%symbol
**
**  Input: string -- "+", "-", "%", or hex-number
**
**  Output: operand information
**
**  Return: error code
**
****************************************************************************/
PRIVATE RETCODE ParseData(CHAR* Prefix,  OPERANDSTRUCT *Operand)
{
RETCODE Result;
BOOLEAN IsSymbol;
S32 Number;

   IsSymbol = (*Prefix == '%' || *LookAhead == '%');
   if ((Result = ParseNumber(Prefix,  &Number)) != GOOD)
      return (Result);

/*** Check offset[...] or %symbol[...] ***/
   if (*LookAhead == '[') {
      GetToken();   /* read '[' */
      Operand->Number = (S32)(U16)(Number & 0x0000FFFF);
      if (Operand->Number != Number) {
         Operand->Number = Number;
         Operand->Extra = 0xFFFF0000; // 4-byte expression
         fAddr32 = TRUE;
      }
      return (ParseMemory(Operand));
   }
/*** Check segment:offset ***/
   else if (*LookAhead == ':') {
      GetToken();   /* read ':' */
      Operand->Number = Number & 0x0000FFFF; // XXXX:XXXX or XXXX:XXXXXXXX
      if (Operand->Number != Number)
         return(ER_DAD_ASM_ERROR);
      Operand->OperandType = SEGOFFSET;
      GetToken();
      if (ENDOFSTR(*Token))  return (ER_DAD_ASM_ERROR);
      if ((Result = ParseNumber(Token,  &Number)) != GOOD)
         return (Result);

      if (Number != (Number&0x0000FFFF) || opAddrSize == ADDR_USE_32) {
         Operand->Extra  = Operand->Number|0xFFFF0000; //XXXX:xxxxxxxx
         Operand->Number = Number;          //xxxx:XXXXXXXX
         fOper32 = TRUE;
      }
      else
         Operand->Number = ((Operand->Number)<<16) + (Number & 0x0000FFFF);
         // XXXX:XXXX
   }
/*** Check JMP/CALL %symbol ***/
   else if (IsSymbol &&
            (stricmp(Mnemonic, "JMP") == 0 ||
             stricmp(Mnemonic, "CALL") == 0)) {
      Operand->OperandType = SEGOFFSET;
      Operand->Number = Number;
   }
/*** Until now, it is actually immediate data operand ***/
   else {
      Operand->OperandType = DATA;
      if ((opAddrSize==ADDR_USE_32 && (fOper32||fAddr32))
          || (Number & 0xFFFF0000)) {
         Operand->Extra = 0xFFFF0000; // 4-byte expression
         Operand->Number = Number;
         fOper32 = TRUE;
         fData16 = FALSE;
      }
      else {
         Operand->Number = (S32)(U16)(Number & 0x0000FFFF);
         if (opAddrSize == ADDR_USE_32)
            fData16 = TRUE;
      }
   }
   return (GOOD);
}  /* end of ParseData() */

/***************************************************************************
**
**  Name: ParseNumber(Prefix,  Number)
**
**  Description:
**     parse number: xxx, +xxx, -xxx, %symbol, +%symbol, -%symbol
**
**  Input: string -- "+", "-", "%", or hex-num
**
**  Output: binary number
**
**  Return: error code
**
****************************************************************************/
PRIVATE RETCODE ParseNumber(CHAR* Prefix,  S32 *Number)
{
CHAR *Ptr;
CHAR Sign, Xdigit;
S32 Value;

   Sign = *Prefix;
   if (Sign == '+' || Sign == '-')  GetToken();
   else  strcpy(Token, Prefix);
   *Number = 0;
   Ptr = Token;

   if (*Ptr == '%') {   /* symbol */
      GetToken();
      if (ENDOFSTR(*Token))  return (ER_DAD_ASM_ERROR);
//!!      if (asmSym2Addr(Token,  (U32 *)Number) == FALSE)
//!!         return (ER_DAD_ASM_ERROR);
   }
   else                 /* hex number */
      if (!ENDOFSTR(*Ptr))
         while (*Ptr) {
            Xdigit = toupper(*Ptr);
            Ptr++;
            if (!isxdigit(Xdigit))  return (ER_DAD_ASM_ERROR);
            if (isdigit(Xdigit))
               Value = Xdigit - '0';
            else
               Value = 10 + Xdigit - 'A';
            *Number = (*Number << 4) + Value;
    //        if (*Number >= 0x10000L)  return (ER_DAD_ASM_ERROR);
         }
   else  return (ER_DAD_ASM_ERROR);
   if (Sign == '-')  *Number = -(*Number);
   return (GOOD);
}  /* end of ParseNumber() */

/***************************************************************************
**
**  Name: ParseMemory(Operand)
**
**  Description:
**     parse memory operand: [BX+SI],[BX+DI], ...
**
**  Output: operand information
**
**  Return: error code
**
****************************************************************************/
PRIVATE RETCODE ParseMemory(OPERANDSTRUCT *Operand)
{
U8 BaseIndex, mod;  /* BaseIndex -- bit 3:BX, bit 2:BP, bit 1:SI, bit 0:DI */
U8 BaseIndex32, RegIndex32, numReg;
S32 Number;
RETCODE Result;
U16 ID;
BOOLEAN fDisp32 = FALSE, fDone32 = FALSE;

   if (Operand->OperandType == NONE)   /* not set xxx PTR yet */
      Operand->OperandType = MEM;
   BaseIndex = BaseIndex32 = RegIndex32 = numReg = 0;
   GetToken();

/*** Parsing [...][...]... ***/
   while (1) {
      if (AreXdigits(Token) || *Token == '%' ||
          (*Token == '+' || *Token == '-') &&
          (AreXdigits(LookAhead) || *LookAhead == '%')) {
      /* offset, +offset, -offset, %symbol, +%symbol, -%symbol */
         if ((Result = ParseNumber(Token,  &Number)) != GOOD)
            return (Result);
         if (Number >= (S16)-0x8000 && Number <= 0x7FFF)
            Operand->Number += (S32)(U16)(Number & 0x0000FFFFL);
         else {
            fAddr32 = fDisp32 = TRUE;
            Operand->Extra = 0xFFFF0000; // 4-byte expression
            Operand->Number += Number;
         }
      }
      else {
         if (*Token == '+') {
            GetToken();
            if ((Result = ParseNumber(Token,  &Number)) == GOOD) {
               if (Number >= (S16)-0x8000 && Number <= 0x7FFF)
                  Operand->Number += (S32)(U16)(Number & 0x0000FFFFL);
               else {
                  fAddr32 = fDisp32 = TRUE;
                  Operand->Extra = 0xFFFF0000; // 4-byte expression
                  Operand->Number += Number;
               }
            }
         }

         /* BX/BP/SI/DI */
         if (SearchTable(BaseIndexRegTable, COUNT(BaseIndexRegTable), Token,
               &ID) == GOOD) {
            if (opAddrSize == ADDR_USE_32 && !fAddr32)
               fReg16 = TRUE;
            if (BaseIndex & (1 << ID))   /* already set ? */
               return (ER_DAD_ASM_ERROR);
            else
               BaseIndex |= (1 << ID);
         }
         // EAX/ECX/EDX/EBX/ESP/ /ESI/EDI
         else if (SearchTable(Reg32Table, COUNT(Reg32Table), Token, &ID)
                   == GOOD) {
            numReg++;
            fAddr32 = fDisp32 = TRUE;
            Operand->Extra = 0xFFFF0000; // 4-byte expression
            RegIndex32 = ID + 1;  // make BaseIndex be always greater zero
            if (BaseIndex32 > 0) {
               if (fSIB) {
                  if (BaseIndex32 != ESP)
                     BaseIndex32 = RegIndex32;
               }
               else {
                  fSIB = TRUE;
                  if (*LookAhead == '+')
                     RegIndex32 ^= BaseIndex32 ^= RegIndex32 ^= BaseIndex32;
                  SIB |= ((RegIndex32-1) << 3);
               }
            }
            else
               BaseIndex32 = RegIndex32;

            if (RegIndex32 == ESP) { // BaseIndex is ESP
               if (fSIB == FALSE) {
                  fSIB = TRUE;
                  BaseIndex32 = RegIndex32;
               }
            }
            fAddr32 = TRUE;
            if (*LookAhead == '*') {
               GetToken(); // get '*'
               GetToken(); // get number
               if (strlen(Token) == 1) {
                  switch (*Token) { // set SS (bit 7,6)
                     case '1':
                        SIB = 0;
                        break;
                     case '2':
                        SIB = 1 << 6;
                        break;
                     case '4':
                        SIB = 2 << 6;
                        break;
                     case '8':
                        SIB = 3 << 6;
                        break;
                     default:
                        return(ER_DAD_ASM_ERROR);
                  }
                  fSIB = TRUE;
                  SIB |= ((RegIndex32-1) << 3);

               }
            }
         }
         else
            return (ER_DAD_ASM_ERROR);
      }
      GetToken();
      if (*Token == ']') {
         if (*LookAhead == '[') {
            GetToken();   /* read '[' */
            GetToken();
         }
         else break;
      }
   }  /* end of while */

/*** Check whether offset is after ']' ***/
   if (!ENDOFSTR(*LookAhead) && *LookAhead != ',') {
      GetToken();
      if ((Result = ParseNumber(Token,  &Number)) != GOOD)
         return (Result);
      if (Number >= (S16)-0x8000 && Number <= 0x7FFF)
         Operand->Number += (S32)(U16)(Number & 0x0000FFFFL);
      else {
         fAddr32 = fDisp32 = TRUE;
         Operand->Extra = 0xFFFF0000; // 4-byte expression
         Operand->Number += Number;
      }
   }

/*** Determine mod and r/m bits, which are stored in Operand->MemberID ***/
/*!!*/
   if (BaseIndex32 > 0 && BaseIndex > 0)
      return(ER_DAD_ASM_ERROR);

   if (fDisp32 || BaseIndex32 > 0) {
      if (BaseIndex32 == ESP && numReg == 1)
         SIB |= ((RegIndex32-1) << 3);

      if (BaseIndex32 == 0) {
         mod = 0;           /* direct address */
         BaseIndex32 = 5 + 1;   // it will be decreased by 1 later
      }
      else if (fSIB && RegIndex32 != ESP) {
         if (numReg == 1) {
            mod = 0;
            fDisp32 = TRUE;
            Operand->Extra = ((Operand->Number&0xFFFF0000)>>16)|0xFFFF0000;
            //put a mark indicating disp32
            Operand->Number = (Operand->Number&0x0000FFFF) | 0xFFFF0000;
            fDone32 = TRUE;
            SIB |= ((RegIndex32-1) << 3);
            BaseIndex32 = 5 + 1;   // Base Index is 5
         }
         else {
            if (Operand->Number == 0L && BaseIndex32 != EBP) 
               mod = 0;
            else if (Operand->Number >= -0x80 && Operand->Number <= 0x7F)
               mod = 1;  /* 1 offset byte, sign extended */
            else
               mod = 2;  /* 4 offset bytes */
         }
      }
      else {
         if (Operand->Number == 0L)    /* really no offset ? */
            mod = 0;
         else if (Operand->Number >= -0x80 && Operand->Number <= 0x7F)
            mod = 1;  /* 1 offset byte, sign extended */
         else
            mod = 2;  /* 4 offset bytes */
      }

      if (Operand->Number != 0) {
         //store the high word of the disp
         if (!fDone32)
            Operand->Extra = ((Operand->Number&0xFFFF0000)>>16)|0xFFFF0000;
         //put a mark indicating disp32
         Operand->Number = (Operand->Number&0x0000FFFF) | 0xFFFF0000;
      }
      if (BaseIndex32 > 0) {
         Operand->MemberID = BaseIndex32 - 1; // recover the original ID
         if (fSIB) {
            SIB |= Operand->MemberID;
            Operand->MemberID &= 0xF8; // get rid of bit 0,1,2
            Operand->MemberID |= 0x04; // SIB: bitwise XXXXX100
         }
      }
   }
   else {
      if ((Operand->MemberID = BaseIndexTable[BaseIndex]) == XX)
         /* r/m bits,  XX stands for failure */
         return (ER_DAD_ASM_ERROR);
      if (BaseIndex == 0)  mod = 0;           /* direct address */
      else
         if (Operand->Number == 0L) {          /* really no offset ? */
            mod = 0;
//            if (BaseIndex == 0x04)
//               mod = 1;  //!!??? is [BP]; set one offset byte
//            else
//               mod = 0;  // no offset
         }
         else {
            if ((Operand->Number&0xFFFF0000) == 0)
               Operand->Number = (S32)(S16)Operand->Number;
            if (Operand->Number >= -0x80 && Operand->Number <= 0x7F)
               mod = 1;  /* 1 offset byte, sign extended */
            else
               mod = 2;  /* 2 offset bytes */
         }
   }

   Operand->MemberID |= (mod << 6);        /* set mod bits */
   return (GOOD);
}  /* end of ParseMemory() */

/***************************************************************************
**
**  Name: SearchTable(Table, TableLen, Key, ID)
**
**  Description:
**     search a string in a table
**
**  Input:  table, table length, key string
**
**  Return: GOOD if success
**
****************************************************************************/
PRIVATE RETCODE SearchTable(CHAR* Table[], U16 TableLen, CHAR* Key, U16* ID)
{
U16 i;

   for (i = 0; i < TableLen; i++)
      if (stricmp(Table[i], Key) == 0) {
         *ID = i;
         return(GOOD);
      }

   return (!GOOD);
}  /* end of SearchTable() */

/***************************************************************************
**
**  Name: AreXdigits(s)
**
**  Description:
**     check if characters in a string are all hex-digits
**
**  Input:  string
**
**  Return: TRUE or FALSE
**
****************************************************************************/
PRIVATE BOOLEAN AreXdigits(CHAR* s)
{
   if ( !isxdigit(*(s++)) )  return (FALSE);
   while (isalnum(*s) || *s == '_')
      if ( !isxdigit(*(s++)) )  return (FALSE);
   return (TRUE);
}  /* end of AreXdigits() */

/******************************** E O F ***********************************/
