/****************************************************************************
*
*  This code is Public Domain.
*
*  ========================================================================
*
* Description:  reserved word handling, including hash table access
*
****************************************************************************/

#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "reswords.h"
#include "expreval.h"
#include "condasm.h"
#include "codegen.h"
#ifdef __I86__
#include "i86.h"
#endif

//#define HASH_TABITEMS 211
#if AVXSUPP
#define HASH_TABITEMS 811
#else
#define HASH_TABITEMS 599
#endif

#if 0 // def __I86__
/* optionally, for JWASMR, use a void based pointer for the name field.
 * However, this requires to deactivate the RENAMEKEYWORD option!
 */
#define GetPtr( x, y ) seg:>x->y
#define BASEPTR
#else
#define GetPtr( x, y ) x->y
#endif

/* reserved words hash table */
static uint_16 resw_table[ HASH_TABITEMS ];

/* define unary operand (LOW, HIGH, OFFSET, ...) type flags */
enum unary_operand_types {
#define res( value, func ) UOT_ ## value,
#include "unaryop.h"
#undef res
};

/* v2.06: the following operand combinations are used
 * inside InstrTable[] only, they don't need to be known
 * by the parser.
 */
enum operand_sets {
    OP_R_MS      = ( OP_R | OP_MS ),
    OP_R8_M08    = ( OP_R8 | OP_M08 ),
    OP_RGT8_MS   = ( OP_RGT8 | OP_MS ),
    OP_RGT8_MGT8 = ( OP_RGT8 | OP_MGT8 ),
    OP_RMGT16    = ( OP_RGT16 | OP_MGT16 ),
    OP_RGT16_M08 = ( OP_RGT16 | OP_M08 ),
    OP_R16_R32   = ( OP_R16 | OP_R32 ),
    OP_R16_M16   = ( OP_R16 | OP_M16 ),
    OP_R32_M08   = ( OP_R32 | OP_M08 ),
    OP_R32_M16   = ( OP_R32 | OP_M16 ),
    OP_R32_M32   = ( OP_R32 | OP_M32 ),
#if AMD64_SUPPORT
    OP_R16_R64   = ( OP_R16 | OP_R64 ),
    OP_R64_M64   = ( OP_R64 | OP_M64 ),
    OP_M16_M64   = ( OP_M16 | OP_M64 ),
#endif
    OP_M16_M32   = ( OP_M16 | OP_M32 ),
    OP_MMX_M64   = ( OP_MMX | OP_M64 ),
    OP_XMM_M16   = ( OP_XMM | OP_M16 ),
    OP_XMM_M32   = ( OP_XMM | OP_M32 ),
    OP_XMM_M64   = ( OP_XMM | OP_M64 ),
    OP_XMM_M128  = ( OP_XMM | OP_M128 ),
#if MASM_SSE_MEMX
/* extended Masm syntax: sometimes Masm accepts 2 mem types
 * for the memory operand, although the mem access will always
 * be QWORD/OWORD.
 */
    OP_MMX_M64_08  = ( OP_MMX | OP_M64  | OP_M08 ),
    OP_MMX_M64_16  = ( OP_MMX | OP_M64  | OP_M16 ),
    OP_MMX_M64_32  = ( OP_MMX | OP_M64  | OP_M32 ),

    OP_XMM_M128_08 = ( OP_XMM | OP_M128 | OP_M08 ),
    OP_XMM_M128_16 = ( OP_XMM | OP_M128 | OP_M16 ),
    OP_XMM_M128_32 = ( OP_XMM | OP_M128 | OP_M32 ),
    OP_XMM_M128_64 = ( OP_XMM | OP_M128 | OP_M64 ),
#else
/* see macro OpCls() below */
#define OPC_MMXMMX_M64_08NONE  OPC_MMXMMX_M64NONE
#define OPC_MMXMMX_M64_16NONE  OPC_MMXMMX_M64NONE
#define OPC_MMXMMX_M64_32NONE  OPC_MMXMMX_M64NONE

#define OPC_XMMXMM_M128_08NONE OPC_XMMXMM_M128NONE
#define OPC_XMMXMM_M128_16NONE OPC_XMMXMM_M128NONE
#define OPC_XMMXMM_M128_32NONE OPC_XMMXMM_M128NONE
#define OPC_XMMXMM_M128_64NONE OPC_XMMXMM_M128NONE
#endif
#if AVXSUPP
    OP_YMM_M256  = ( OP_YMM | OP_M256 ),
#endif
};

/* v2.06: operand types have been removed from InstrTable[], they
 * are stored now in their own table, opnd_clstab[], below.
 * This will allow to add a 4th operand ( AVX ) more effectively.
 */
enum opnd_variants {
#define OpCls( op1, op2, op3 ) OPC_ ## op1 ## op2 ## op3,
#include "opndcls.h"
#undef OpCls
};

/* the tables to handle "reserved words" are now generated:
 * 1. InstrTable: contains info for instructions.
 *    instructions may need multiple rows!
 * 2. SpecialTable: contains info for reserved words which are
 *    NOT instructions. One row each.
 * 3. optable_idx: array of indices for InstrTable.
 * 4. resw_strings: strings of reserved words. No terminating x'00'!
 * 5. ResWordTable: array of reserved words (name, name length, flags).
 *
 * Each reserved word has a "token" value assigned, which is a short integer.
 * This integer can be used as index for:
 * - SpecialTable
 * - optable_idx ( needs adjustment, better use macro IndexFromToken() )
 * - ResWordTable
 */

/* create InstrTable. */

#define OpCls( op1, op2, op3 ) OPC_ ## op1 ## op2 ## op3

const struct instr_item InstrTable[] = {
#define ins(tok, string, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix) \
    { opcls, byte1_info, prefix, 1, rm_info, op_dir, 0, cpu, opcode, rm_byte },
#define insx(tok,string, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix,flgs) \
    { opcls, byte1_info, prefix, 1, rm_info, op_dir, 0, cpu, opcode, rm_byte },
#define insn(tok,suffix,     opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix) \
    { opcls, byte1_info, prefix, 0, rm_info, op_dir, 0, cpu, opcode, rm_byte },
#define insm(tok,suffix,     opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix) \
    { opcls, byte1_info, prefix, 1, rm_info, op_dir, 0, cpu, opcode, rm_byte },
#include "instruct.h"
#include "instr64.h"
ins (NULL,0,OpCls(NONE,NONE,NONE),0,0,0,0,0,0,0) /* last entry - needed for its ".first" (=1) field */
#undef insm
#undef insn
#undef insx
#undef ins
};
#undef OpCls

/* create SpecialTable. */

const struct special_item SpecialTable[] = {
    { 0, 0, 0, 0, 0 }, /* dummy entry for T_NULL */
#define res(tok, string, type, value, bytval, flags, cpu, sflags ) \
    { value, sflags, cpu, bytval, type },
#include "special.h"
#undef res
#define res(tok, string, value, bytval, flags, cpu, sflags ) \
    { value, sflags, cpu, bytval, RWT_DIRECTIVE },
#include "directve.h"
#undef res
};

/* define symbolic indices for InstrTable[] */

enum res_idx {
#define  ins(tok, string, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix) T_ ## tok ## _I,
#define insx(tok, string, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix,flgs) T_ ## tok ## _I,
#define insn(tok, suffix, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix) T_ ## tok ## _ ## suffix,
#define insm(tok, suffix, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix) T_ ## tok ## _ ## suffix,
#include "instruct.h"
#undef insm
#undef insn
#undef ins

#define  ins(tok, string, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix) T_ ## tok ## _I64,
#define insn(tok, suffix, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix) T_ ## tok ## _ ## suffix ## _I64,
#define insm(tok, suffix, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix) T_ ## tok ## _ ## suffix ## _I64,
#include "instr64.h"
#undef insm
#undef insn
#undef insx
#undef ins
//T_NULL_I /* v2.06: removed */
};

/* create optable_idx, the index array for InstrTable.
 * This is needed because instructions often need more than
 * one entry in InstrTable.
 */

uint_16 optable_idx[] = {

#define  ins( tok, string, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix) T_ ## tok ## _I,
#define insx( tok, string, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix,flgs) T_ ## tok ## _I,
#define insn( tok, suffix, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix)
#define insm( tok, suffix, opcls, byte1_info, op_dir, rm_info, opcode, rm_byte, cpu, prefix)
#include "instruct.h"
#undef insm
#undef insn
#undef insx
#undef ins

    /* v2.06: this was superfluous, because the additional entries for
     * 64-bit are only needed in InstrTable[]. For optable_idx[], a
     * patch is done (see patchtabr[])
     */
//#define  ins(tok,string, op1,op2,op3, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix) T_ ## tok ## _I64,
//#define insn(tok,suffix, op1,op2,op3, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix) T_ ## tok ## _ ## suffix ## _I64,
//#define insm(tok,suffix, op1,op2,op3, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix) T_ ## tok ## _ ## suffix ## _I64,
//#include "instr64.h"
//#undef insm
//#undef insn
//#undef ins

#if AVXSUPP
#define avxins( tok, string, cpu, flgs ) T_ ## tok ## _I,
#include "instravx.h"
#undef avxins
#endif
    //T_NULL_I /* v2.06: also not needed */
};

/* table of instruction operand classes
 */
const struct opnd_class opnd_clstab[] = {
#define OpCls( op1, op2, op3 ) { { OP_ ## op1, OP_ ## op2 }, OP3_ ## op3 },
#include "opndcls.h"
#undef OpCls
};

/* create the strings for all reserved words */

static const char resw_strings[] = {
#define res(tok, string, type, value, bytval, flags, cpu, sflags) \
 # string
#include "special.h"
#undef res
#define res(tok, string, value, bytval, flags, cpu, sflags) \
 # string
#include "directve.h"
#undef res

#define ins(tok,string, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix) \
 # string
#define insn(tok,suffix, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix)
#define insm(tok,suffix, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix)
#define insx(tok,string, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix,flgs) \
 # string
#include "instruct.h"
#if AVXSUPP
#define avxins( tok, string, cpu, flgs ) # string
#include "instravx.h"
#undef avxins
#endif
    "syscall_" /* replacement for "syscall" language type in 64-bit */
};
#define strSyscall_ &resw_strings[sizeof(resw_strings)-9]
#undef insx
#undef insm
#undef insn
#undef ins

/* create the 'reserved words' table (ResWordTable).
 * this table's entries will be used to create the instruction hash table.
 * v2.11: RWF_SPECIAL flag removed:
 * { 0, sizeof(#string)-1, RWF_SPECIAL | flags, NULL },
 */
struct ReservedWord ResWordTable[] = {
    { 0, 0, 0, NULL }, /* dummy entry for T_NULL */
#define res(tok, string, type, value, bytval, flags, cpu, sflags) \
    { 0, sizeof(#string)-1, flags, NULL },
#include "special.h"
#undef res
#define res(tok, string, value, bytval, flags, cpu, sflags) \
    { 0, sizeof(#string)-1, flags, NULL },
#include "directve.h"
#undef res

#define ins(tok,string,  opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix) \
    { 0, sizeof(#string)-1, 0, NULL },
#define insn(tok,suffix, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix)
#define insm(tok,suffix, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix)
#define insx(tok,string, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix,flags) \
    { 0, sizeof(#string)-1, flags, NULL },
#include "instruct.h"
#undef insx
#undef insm
#undef insn
#undef ins
#if AVXSUPP
#define avxins( tok, string, cpu, flgs ) \
    { 0, sizeof(#string)-1, RWF_VEX, NULL },
#include "instravx.h"
#undef avxins
#endif
};

#if AVXSUPP
/* these is a special 1-byte array for vex-encoded instructions.
 * it could probably be moved to InstrTable[] (there is an unused byte),
 * but in fact it's the wrong place, since the content of vex_flags[]
 * are associated with opcodes, not with instruction variants.
 */
const uint_8 vex_flags[] = {
    /* flags for the AVX instructions in instruct.h. The order must
     * be equal to the one in instruct.h! ( this is to be improved.)
     * For a description of the VX_ flags see codegen.h
     */
    VX_NND,      /* VBROADCASTSS   */
    VX_NND,      /* VBROADCASTSD   */
    VX_NND,      /* VBROADCASTF128 */
    VX_L,        /* VBLENDVPD      */
    VX_L,        /* VBLENDVPS      */
    0,           /* VINSERTF128    */
    VX_NND,      /* VEXTRACTF128   */
    VX_L,        /* VMASKMOVPS     */
    VX_L,        /* VMASKMOVPD     */
    0,           /* VPBLENDVB      */
    VX_L|VX_IMM, /* VPERMILPD      */
    VX_L|VX_IMM, /* VPERMILPS      */
    /* VPERMIL2xx has been dropped */
    //VX_L,      /* VPERMIL2PD     */
    //VX_L,      /* VPERMIL2PS     */
    0,           /* VPERM2F128     */
    VX_L|VX_NND, /* VTESTPS        */
    VX_L|VX_NND, /* VTESTPD        */
    VX_L,        /* VZEROALL       */
    0,           /* VZEROUPPER     */
    VX_NND,      /* VCVTPD2DQ      */
    VX_NND,      /* VCVTTPD2DQ     */
    VX_NND,      /* VCVTPD2PS      */
    VX_NND,      /* VMOVDDUP       */
    VX_L|VX_NND, /* VMOVMSKPD      */ /* v2.11 */
    VX_L|VX_NND, /* VMOVMSKPS      */ /* v2.11 */
#define avxins( tok, string, cpu, flgs ) flgs,
#include "instravx.h"
#undef avxins
};
#endif

#if AMD64_SUPPORT

/* keywords to be added for 64-bit */
static const enum instr_token patchtab64[] = {
    T_SPL,             /* add x64 register part of special.h */
    T_FRAME,           /* add x64 reserved word part of special.h */
    T_DOT_ALLOCSTACK,  /* add x64 directive part of directve.h (win64) */
    T_JRCXZ,           /* branch instructions must be grouped together */
    T_CDQE,            /* add x64 part of instruct.h */
#if AVXSUPP
    T_VPEXTRQ,         /* add x64 part of instravx.h */
#endif
};

/* keywords to be removed for 64-bit */
static const enum instr_token patchtab32[] = {
    T_TR3,          /* registers invalid for IA32+              */
    T_DOT_SAFESEH,  /* directives invalid for IA32+             */
    T_AAA,          /* instructions invalid for IA32+           */
    T_JCXZ,         /* 1. branch instructions invalid for IA32+ */
    T_LOOPW         /* 2. branch instructions invalid for IA32+ */
};

struct replace_ins {
    uint_16       tok; /* is an optable_idx[] index */
    enum res_idx  idx32;
    enum res_idx  idx64;
};

/* keyword entries to be changed for 64-bit (see instr64.h) */
static const struct replace_ins patchtabr[] = {
    { T_LGDT - SPECIAL_LAST, T_LGDT_I, T_LGDT_I64 },
    { T_LIDT - SPECIAL_LAST, T_LIDT_I, T_LIDT_I64 },
    { T_CALL - SPECIAL_LAST, T_CALL_I, T_CALL_I64 },
    { T_JMP  - SPECIAL_LAST, T_JMP_I,  T_JMP_I64  },
    { T_POP  - SPECIAL_LAST, T_POP_I,  T_POP_I64  }, /* v2.06: added */
    { T_PUSH - SPECIAL_LAST, T_PUSH_I, T_PUSH_I64 }, /* v2.06: added */
#if 1
    /* with Masm, in 16/32-bit SLDT|SMSW|STR accept a WORD argument only -
     * in 64-bit (ML64), 32- and 64-bit registers are also accepted!
     */
    { T_SLDT - SPECIAL_LAST, T_SLDT_I, T_SLDT_I64 },
    { T_SMSW - SPECIAL_LAST, T_SMSW_I, T_SMSW_I64 },
    { T_STR  - SPECIAL_LAST, T_STR_I,  T_STR_I64  },
#endif
#if VMXSUPP /* v2.09: added */
    { T_VMREAD  - SPECIAL_LAST, T_VMREAD_I,   T_VMREAD_I64  },
    { T_VMWRITE - SPECIAL_LAST, T_VMWRITE_I,  T_VMWRITE_I64 },
#endif
};

#endif

#if RENAMEKEY
static struct qdesc renamed_keys = { NULL, NULL };
#endif

/* global queue of "disabled" reserved words.
 * just indices of ResWordTable[] are used.
 */
static struct {
    uint_16 Head;
    uint_16 Tail;
} Removed = { 0, 0 };

#if AMD64_SUPPORT
static bool  b64bit = FALSE; /* resw tables in 64bit mode? */
#endif

static unsigned get_hash( const char *s, unsigned char size )
/***********************************************************/
{
    uint_32 h;
    uint_32 g;

    for( h = 0; size; size-- ) {
        /* ( h & ~0x0fff ) == 0 is always true here */
        h = (h << 3) + (*s++ | ' ');
        g = h & ~0x1fff;
        h ^= g;
        h ^= g >> 13;
    }
    return( h % HASH_TABITEMS );
}

unsigned FindResWord( const char *name, unsigned char size )
/**********************************************************/
/* search reserved word in hash table */
{
    struct ReservedWord *inst;
    unsigned i;
#ifdef BASEPTR
    __segment seg = FP_SEG( resw_strings );
#endif

    for( i = resw_table[ get_hash( name, size ) ]; i != 0; i = inst->next ) {
        inst = &ResWordTable[i];
        /* check if the name matches the entry for this inst in AsmChars */
        //if( name[ inst->len ] == NULLC && _strnicmp( name, inst->name, inst->len ) == 0) {
        if( inst->len == size && _memicmp( name, GetPtr( inst, name ), inst->len ) == 0 ) {
            return( i );
        }
    }
    return( 0 );
}

/* add reserved word to hash table */

static void AddResWord( int token )
/*********************************/
{
    int i;
    int old;
    int curr;
#ifdef BASEPTR
    __segment seg = FP_SEG( resw_strings );
#endif

    i = get_hash( ResWordTable[token].name, ResWordTable[token].len );

    /* sort the items of a line by length! */

    for( curr = resw_table[i], old = 0; curr != 0 && ResWordTable[curr].len <= ResWordTable[token].len; old = curr, curr = ResWordTable[curr].next );

    if ( old == 0 ) {
        ResWordTable[token].next = resw_table[i];
        resw_table[i] = token;
    } else {
        ResWordTable[token].next = ResWordTable[old].next;
        ResWordTable[old].next = token;
    }

    return;
}

/* remove a reserved word from the hash table. */

static int RemoveResWord( int token )
/***********************************/
{
    int i;
    int old;
    int curr;
#ifdef BASEPTR
    __segment seg = FP_SEG( resw_strings );
#endif

    i = get_hash( ResWordTable[token].name, ResWordTable[token].len );

    for( curr = resw_table[i], old = 0 ; curr != 0 ; old = curr, curr = ResWordTable[curr].next )  {
        if( curr == token ) {
            if ( old != 0 )
                ResWordTable[old].next = ResWordTable[curr].next;
            else
                resw_table[i] = ResWordTable[curr].next;
            return( TRUE );
        }
    }
    return( FALSE );
}

#if RENAMEKEY

struct rename_node {
    struct rename_node *next;
    const char *name; /* the original name in resw_strings[] */
    uint_16 token; /* is either enum instr_token or enum special_token */
    uint_8 length;
};

/* Rename a keyword - used by OPTION RENAMEKEYWORD.
 * - token: keyword to rename
 * - newname: new name of keyword
 * - length: length of new name
 */

void RenameKeyword( unsigned token, const char *newname, uint_8 length )
/**********************************************************************/
{
    struct rename_node *curr;
    struct rename_node *prev;

    /* v2.11: do nothing if new name matches current name */
    if ( ResWordTable[token].len == length && !_memicmp( newname, ResWordTable[token].name, length ) )
        return;

    RemoveResWord( token );
    /* if it is the first rename action for this keyword,
     * the original name must be saved.
     */
    if ( ResWordTable[token].name >= resw_strings &&
        ResWordTable[token].name < ( resw_strings + sizeof( resw_strings ) ) ) {
        curr = LclAlloc( sizeof( struct rename_node ) );
        curr->next = NULL;
        curr->name = ResWordTable[token].name;
        curr->token = token;
        curr->length = ResWordTable[token].len;
        if ( renamed_keys.head == NULL ) {
            renamed_keys.head = renamed_keys.tail = curr;
        } else {
            ((struct rename_node *)renamed_keys.tail)->next = curr;
            renamed_keys.tail = curr;
        }
    } else {
        LclFree( (void *)ResWordTable[token].name );
#if 1
        /* v2.11: search the original name. if the "new" names matches
         * the original name, restore the name pointer
         * v2.17: fixed infinite loop ( curr wasn't changed )
         */
        //for ( curr = renamed_keys.head, prev = NULL; curr; prev = curr ) {
        for ( curr = renamed_keys.head, prev = NULL; curr; prev = curr, curr = curr->next ) {
            if ( curr->token == token ) {
                if ( curr->length == length && !memcmp( newname, curr->name, length ) ) {
                    if ( prev )
                        prev->next = curr->next;
                    else
                        renamed_keys.head = curr->next;
                    if ( renamed_keys.tail == curr )
                        renamed_keys.tail = prev;
                    ResWordTable[token].name = curr->name;
                    ResWordTable[token].len = curr->length;
                    AddResWord( token );
                    return;
                }
                break;
            }
        }
#endif
    }
    ResWordTable[token].name = LclAlloc( length );
    /* convert to lowercase? */
    memcpy( (void *)ResWordTable[token].name, newname, length );
    ResWordTable[token].len = length;
    AddResWord( token );
}

#endif

#if AMD64_SUPPORT

/* depending on 64bit on or off, some instructions must be added,
 * some removed. Currently this is a bit hackish.
 */
void Set64Bit( bool newmode )
/***************************/
{
    static const char *syscallname;   /* "true" syscall name stored here */
    int token;
    int i;

    if ( newmode != b64bit ) {
        DebugMsg1(("Set64Bit(%u): mode is to change\n", newmode ));
        if ( newmode != FALSE ) {
            optable_idx[ T_INC - SPECIAL_LAST ]++;   /* skip the one-byte register INC */
            optable_idx[ T_DEC - SPECIAL_LAST ]++;   /* skip the one-byte register DEC */
            /*
             * change SYSCALL to SYSCALL_ language in long mode.
             * one cannot just change the name, since the hash value
             * will differ!
             */
            RemoveResWord( T_SYSCALL );
            syscallname = ResWordTable[T_SYSCALL].name; /* save the "true" name */
            ResWordTable[T_SYSCALL].name = strSyscall_;
            ResWordTable[T_SYSCALL].len++;
            AddResWord( T_SYSCALL );

            for ( i = 0; i < sizeof( patchtab64 ) / sizeof( patchtab64[0] ); i++ )
                for( token = patchtab64[i]; ResWordTable[token].flags & RWF_X64; token++ )
                    if ( !( ResWordTable[token].flags & RWF_DISABLED ) )
                        AddResWord( token );
            for ( i = 0; i < sizeof( patchtab32 ) / sizeof( patchtab32[0] ); i++ )
                for( token = patchtab32[i]; ResWordTable[token].flags & RWF_IA32; token++ )
                    if ( !( ResWordTable[token].flags & RWF_DISABLED ) )
                        RemoveResWord( token );
            for ( i = 0; i < sizeof( patchtabr) / sizeof( patchtabr[0] ); i++ ) {
                optable_idx[ patchtabr[i].tok] = patchtabr[i].idx64;
            }
        } else  {
            optable_idx[T_INC - SPECIAL_LAST]--;   /* restore the one-byte register INC */
            optable_idx[T_DEC - SPECIAL_LAST]--;   /* restore the one-byte register DEC */

            for ( i = 0; i < sizeof( patchtab64 ) / sizeof( patchtab64[0] ); i++ )
                for( token = patchtab64[i]; ResWordTable[token].flags & RWF_X64; token++ )
                    if ( !( ResWordTable[token].flags & RWF_DISABLED ) )
                        RemoveResWord( token );
            for ( i = 0; i < sizeof( patchtab32 ) / sizeof( patchtab32[0] ); i++ )
                for( token = patchtab32[i]; ResWordTable[token].flags & RWF_IA32; token++ )
                    if ( !( ResWordTable[token].flags & RWF_DISABLED ) )
                        AddResWord( token );
            for ( i = 0; i < sizeof( patchtabr) / sizeof( patchtabr[0] ); i++ ) {
                optable_idx[patchtabr[i].tok] = patchtabr[i].idx32;
            }

            /* change calling convention syscall_ back to syscall */
            RemoveResWord( T_SYSCALL );
            ResWordTable[T_SYSCALL].name = syscallname; /* restore "true" name */
            ResWordTable[T_SYSCALL].len--;
            AddResWord( T_SYSCALL );
        }
        b64bit = newmode;
    }
}
#endif

void DisableKeyword( unsigned token )
/***********************************/
{
    if ( !( ResWordTable[token].flags & RWF_DISABLED ) ) {
        RemoveResWord( token );
        ResWordTable[token].next = 0;
        ResWordTable[token].flags |= RWF_DISABLED;
        if ( Removed.Head == 0 )
            Removed.Head = Removed.Tail = token;
        else {
            ResWordTable[Removed.Tail].next = token;
            Removed.Tail = token;
        }
    }
}

/* check if a keyword is in the list of disabled words.
 */

bool IsKeywordDisabled( const char *name, int len )
/*************************************************/
{
    unsigned token;
    for ( token = Removed.Head; token != 0; token = ResWordTable[token].next )
        if( ResWordTable[token].name[ len ] == NULLC && _memicmp( name, ResWordTable[token].name, len ) == 0 )
            return( TRUE );
    return( FALSE );
}

/* get current name of a reserved word.
 * max size is 255.
 */

char *GetResWName( unsigned resword, char *buff )
/***********************************************/
{
#ifdef __I86__
    static char intbuff[32];
#else
    static char intbuff[256];
#endif
    if ( !buff )
        buff = intbuff;
    memcpy( buff, ResWordTable[resword].name, ResWordTable[resword].len );
    buff[ResWordTable[resword].len] = NULLC;
    return( buff );
}

/* ResWordsInit() initializes the reserved words hash array ( resw_table[] )
 * and also the reserved words string pointers ( ResWordTable[].name + ResWordTable[].len )
 */

void ResWordsInit( void )
/***********************/
{
    int i;
    const char *p = resw_strings;

    /* exit immediately if table is already initialized */
    if ( ResWordTable[1].name )
        return;

    DebugMsg(("ResWordsInit() enter\n"));

    /* clear hash table */
    memset( &resw_table, 0, sizeof( resw_table ) );

#if AVXSUPP && AMD64_SUPPORT
    /* currently these flags must be set manually, since the
     * RWF_ flags aren't contained in instravx.h */
    ResWordTable[T_VPEXTRQ].flags |= RWF_X64;
    ResWordTable[T_VPINSRQ].flags |= RWF_X64;
#endif

    /* initialize ResWordTable[].name and .len.
     * add keyword to hash table ( unless it is 64-bit only ).
     * v2.09: start with index = 1, since index 0 is now T_NULL
     */
    for( i = 1; i < sizeof( ResWordTable ) / sizeof( ResWordTable[0] ); i++ ) {
        ResWordTable[i].name = p;
        p += ResWordTable[i].len;
#if AMD64_SUPPORT /* don't add the words specific to x64 */
        if ( !(ResWordTable[i].flags & RWF_X64 ) )
#endif
            AddResWord( i );
    }
    DebugMsg(("ResWordsInit() exit\n"));
    return;
}

/*
 * v2.17: up to v2.16, ResWordsFini() was called once per module.
 *        now it has argument bAll: 0=called after each pass
 *                                  1=called when module is done
 * restoring the resword table.
 * bAll = 0: restore the renamed keywords only
 *      = 1: restore the keywords disabled by option nokeyword
 */

void ResWordsFini( bool bNoKeyWord )
/**********************************/
{
    int i;
    int next;
#if RENAMEKEY
    struct rename_node  *rencurr;
#endif

    DebugMsg(("ResWordsFini(%u) enter\n", bNoKeyWord ));
#if RENAMEKEY
    if ( !bNoKeyWord ) { /* v2.17: just restore the renamed keywords */
        /* restore renamed keywords.
         * the keyword has to be removed ( and readded ) from the hash table,
         * since its position most likely will change.
         */
        for ( rencurr = renamed_keys.head; rencurr; ) {
            struct rename_node *tmp = rencurr->next;
            RemoveResWord( rencurr->token );
            /* v2.06: this is the correct name to free */
            LclFree( (void *)ResWordTable[rencurr->token].name );
            ResWordTable[rencurr->token].name = rencurr->name;
            ResWordTable[rencurr->token].len = rencurr->length;
            AddResWord( rencurr->token );
            DebugMsg(("ResWordsFini(): %s restored\n", GetResWName( rencurr->token, NULL ) ));
            //LclFree( (void *)rencurr->name ); /* v2.06: this was the wrong one */
            LclFree( rencurr );
            rencurr = tmp;
        }
        renamed_keys.head = NULL;
        return;
    }
#endif

    /* reenter disabled keywords */
    for( i = Removed.Head; i != 0; i = next ) {
        next = ResWordTable[i].next;
        ResWordTable[i].flags &= ~RWF_DISABLED;
#if AMD64_SUPPORT /* don't add the words specific to x64 */
        if ( !(ResWordTable[i].flags & RWF_X64 ) )
#endif
            AddResWord( i );
        DebugMsg(("ResWordsInit(): %s reenabled\n", GetResWName( i, NULL ) ));
    }
    Removed.Head = Removed.Tail = 0;

    return;
}

#ifdef DEBUG_OUT

void DumpResWords( void )
/***********************/
{
    int i;

    printf("SpecialTable\n");
    printf("   # keyword             value   sflags  cpu val8 type flg len\n");
    printf("--------------------------------------------------------------\n");
    /* start with index 1 ( index 0 is T_NULL ) */
    for ( i = 1; i < sizeof( SpecialTable ) / sizeof( SpecialTable[0] ); i++ ) {
        printf("%4u %-16s %8X %8X %4X %4X  %2X  %2X %3u\n", i, GetResWName( i, NULL ),
               SpecialTable[i].value, SpecialTable[i].sflags,
               SpecialTable[i].cpu, SpecialTable[i].bytval,
               SpecialTable[i].type, ResWordTable[i].flags, ResWordTable[i].len );
    }
    printf("--------------------------------------------------------------\n");

    printf("\nitems in InstrTable[]: %u\n", sizeof( InstrTable ) / sizeof( InstrTable[0] ) );
    printf("items in optable_idx[]: %u, used by ResWordTable items %u-%u\n",
           sizeof( optable_idx ) / sizeof( optable_idx[0] ), INS_FIRST_1 + 1, sizeof( ResWordTable ) / sizeof( ResWordTable[0] ) - 1 );

    printf("\nInstructionTable\n");
    printf("   # keyword          cls cpu opc rmb b1 rmi pfx fst idx flg len\n");
    printf("----------------------------------------------------------------\n");
    for ( i = INS_FIRST_1 + 1; i < sizeof( ResWordTable ) / sizeof( ResWordTable[0] ); i++ ) {
        const struct instr_item *ins = &InstrTable[IndexFromToken( i )];
        printf("%4u %-16s %02X %4X  %02X  %02X %2u %X   %X   %u  %4u %3X %3u\n", i, GetResWName( i, NULL ),
               ins->opclsidx,
               ins->cpu, ins->opcode, ins->rm_byte, ins->byte1_info,
               ins->rm_info, ins->allowed_prefix, ins->first,
               IndexFromToken( i ), ResWordTable[i].flags, ResWordTable[i].len );
    }
    printf("----------------------------------------------------------------\n");
}

void DumpInstrStats( void )
/*************************/
{
    unsigned            i;
    int                 inst;
    unsigned            count = 0;
    unsigned            max = 0;
    unsigned            curr = 0;
    unsigned            num[8] = {0,0,0,0,0,0,0,0};

    if ( Options.dump_reswords_hash ) {
        printf("\nReserved Word Hash Table\n");
        printf("Idx keywords\n");
        printf("---------------------------\n");
    }

    for( i = 0; i < HASH_TABITEMS; i++ ) {
        if ( Options.dump_reswords_hash )
            printf("%3u ", i );
        for( inst = resw_table[i], curr = 0; inst != 0; inst = ResWordTable[inst].next ) {
            if ( Options.dump_reswords_hash )
                printf(" %-8s", GetResWName( inst, NULL ) );
            curr++;
        }
        if ( Options.dump_reswords_hash )
            printf("\n" );
        count += curr;
        if ( curr <= 7 )
            num[curr]++;
        if (max < curr)
            max = curr;
    }
    if ( Options.dump_reswords_hash )
        printf("---------------------------\n");
    if ( Options.quiet == FALSE ) {
        printf( "reserved words hash table: %u items, max items/line=%u ", count, max );
        printf( "[0=%u 1=%u %u %u %u %u %u %u]\n", num[0], num[1], num[2], num[3], num[4], num[5], num[6], num[7] );
    }
}
#endif
