/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  Parser items
*
****************************************************************************/

#ifndef PARSER_H
#define PARSER_H

#include "operands.h"
#include "symbols.h"
#include "token.h"

/* define tokens for SpecialTable (registers, operators, ... ) */
enum special_token {
    T_NULL,
#define  res(token, string, type, value, bytval, flags, cpu, sflags) T_ ## token ,
#include "special.h"
#undef res
/* define tokens for SpecialTable (directives) */
#define  res(token, string, value, bytval, flags, cpu, sflags) T_ ## token ,
#include "directve.h"
#undef res
SPECIAL_LAST
};

/* define tokens for instruction table (InstrTable[] in reswords.c) */

enum instr_token {
    INS_FIRST_1 = SPECIAL_LAST - 1, /* to ensure tokens are unique */
#define  ins(token, string, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix ) T_ ## token ,
#define insx(token, string, opcls, byte1_info,op_dir,rm_info,opcode,rm_byte,cpu,prefix,flgs ) T_ ## token ,
#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
#if AVXSUPP
#define VEX_START  T_VBROADCASTSS  /* first VEX encoded item */
#define avxins(token, string, cpu, flgs ) T_V ## token ,
#include "instravx.h"
#undef avxins
#endif
};

/*---------------------------------------------------------------------------*/

/* queue of symbols */
struct symbol_queue {
    struct dsym *head;
    struct dsym *tail;
};

enum queue_type {
    TAB_UNDEF = 0,
    TAB_EXT,      /* externals (EXTERNDEF, EXTERN, COMM, PROTO ) */
    TAB_SEG,      /* SEGMENT items */
    TAB_GRP,      /* GROUP items */
    TAB_PROC,     /* PROC items */
    TAB_ALIAS,    /* ALIAS items */
    TAB_LAST,
};

/* several lists, see enum queue_type above */
extern struct symbol_queue SymTables[];

/*
 values for <rm_info> (3 bits)
 000            -> has rm_byte with w-, d- and/or s-bit in opcode
 001( no_RM   ) -> no rm_byte - may have w-bit
 010( no_WDS  ) -> has rm_byte, but w-bit, d-bit, s-bit of opcode are absent
 011( R_in_OP ) -> no rm_byte, reg field (if any) is included in opcode
 */
enum rm_info {
    no_RM   = 0x1,
    no_WDS  = 0x2,
    R_in_OP = 0x3,
};

/* values for <allowed_prefix> (3 bits) */
enum allowed_prefix {
    // AP_NO_PREFIX= 0x00, /* value 0 means "normal" */
    AP_LOCK     = 0x01,
    AP_REP      = 0x02,
    AP_REPxx    = 0x03,
    AP_FWAIT    = 0x04,
    AP_NO_FWAIT = 0x05
};

/* values for field type in special_item.
 * it should match order of T_REGISTER - T_RES_ID in token.h
 */

enum special_type {
    RWT_REG = 2,  /* same value as for T_REG */
    RWT_DIRECTIVE,
    RWT_UNARY_OP,
    RWT_BINARY_OP,
    RWT_STYPE,
    RWT_RES_ID
};

// values for sflags if register
enum op1_flags {
    SFR_SIZMSK  = 0x1F, /* size in bits 0-4 */
    SFR_IREG    = 0x20,
    SFR_SSBASED = 0x40, /* v2.11: added */
};

#if AMD64_SUPPORT
enum rex_bits {
    REX_B = 1,  /* regno 0-7 <-> 8-15 of ModR/M or SIB base */
    REX_X = 2,  /* regno 0-7 <-> 8-15 of SIB index */
    REX_R = 4,  /* regno 0-7 <-> 8-15 of ModR/M REG */
    REX_W = 8   /* wide 32 <-> 64 */
};
#endif

/* operand classes. this table is defined in reswords.c.
 * index into this array is member opclsidx in instr_item.
 * v2.06: data removed from struct instr_item.
 */
struct opnd_class {
    enum operand_type opnd_type[2];  /* operands 1 + 2 */
    unsigned char opnd_type_3rd;     /* operand 3 */
};

/* instr_item is the structure used to store instructions
 * in InstrTable (instruct.h).
 * Most compilers will use unsigned type for enums, just OW
 * allows to use the smallest size possible.
 */

struct instr_item {
    //enum operand_type opnd_type[2];  /* operands 1 + 2 */
    //unsigned char opnd_type_3rd;     /* operand 3 */
    unsigned char opclsidx;     /* v2.06: index for opnd_clstab */
    unsigned char byte1_info;   /* flags for 1st byte */
    unsigned char
        allowed_prefix  : 3,    /* allowed prefix */
        first           : 1,    /* 1=opcode's first entry */
        rm_info         : 3,    /* info on r/m byte */
        opnd_dir        : 1;    /* operand direction */
    unsigned char reserved;     /* not used yet */
#ifdef __WATCOMC__
    enum cpu_info   cpu;        /* CPU type */
#else
    unsigned short  cpu;
#endif
    unsigned char   opcode;     /* opcode byte */
    unsigned char   rm_byte;    /* mod_rm_byte */
};

/* special_item is the structure used to store directives and
 * other reserved words in SpecialTable (special.h).
 */
struct special_item {
    unsigned     value;
    unsigned     sflags;     /* contains size in bits 0-4 (SFR_SIZMSK) */
#ifdef __WATCOMC__
    enum cpu_info   cpu;     /* CPU type */
#else
    uint_16         cpu;     /* CPU type */
#endif
    uint_8          bytval;
#ifdef __WATCOMC__
    enum special_type type;
#else
    uint_8            type;
#endif
};

#define GetRegNo( x )    SpecialTable[x].bytval
#define GetSflagsSp( x ) SpecialTable[x].sflags
#define GetValueSp( x )  SpecialTable[x].value
#define GetMemtypeSp( x ) SpecialTable[x].bytval
#define GetCpuSp( x )    SpecialTable[x].cpu

/* values for <value> if type == RWT_DIRECTIVE */
enum directive_flags {
    DF_CEXPR    = 0x01, /* avoid '<' being used as string delimiter (.IF, ...) */
    DF_STRPARM  = 0x02, /* directive expects string param(s) (IFB, IFDIF, ...) */
                        /* enclose strings in <> in macro expansion step */
    DF_NOEXPAND = 0x04, /* don't expand params for directive (PURGE, FOR, IFDEF, ...) */
    DF_LABEL    = 0x08, /* directive requires a label */
    DF_NOSTRUC  = 0x10, /* directive not allowed inside structs/unions */
    DF_NOCONCAT = 0x20, /* don't concat line */
    DF_PROC     = 0x40, /* directive triggers prologue generation */
    DF_STORE    = 0x80, /* FASTPASS: directive triggers line store */
    DF_CGEN     = 0x100 /* directive generates lines */
};

/* values for <bytval> if type == RWT_DIRECTIVE */
#define  res(token, function) DRT_ ## token ,
enum directive_type {
#include "dirtype.h"
};
#undef  res

#define MAX_OPND 3

struct opnd_item {
    enum operand_type type;
    union {
        struct {
            int_32    data32l;
            int_32    data32h; /* needed for OP_I48 and OP_I64 */
        };
        uint_64       data64;
    };
    struct fixup      *InsFixup;
};

/* code_info describes the current instruction. It's the communication
 * structure between parser and code generator.
 */
struct code_info {
    struct {
        enum instr_token ins;          /* prefix before instruction, e.g. lock, rep, repnz */
        enum assume_segreg RegOverride;/* segment override (0=ES,1=CS,2=SS,3=DS,...) */
#if AMD64_SUPPORT
        unsigned char   rex;
#endif
        unsigned char   adrsiz:1;      /* address size prefix 0x67 is to be emitted */
        unsigned char   opsiz:1;       /* operand size prefix 0x66 is to be emitted */
    } prefix;
    const struct instr_item *pinstr;   /* current pointer into InstrTable (=InstrTable[IndexFromToken(token)] */
    enum instr_token token;
    enum memtype    mem_type;          /* byte / word / etc. NOT near/far */
    struct opnd_item opnd[MAX_OPND];
    unsigned char   rm_byte;           /* rm byte set by set_rm_sib() in parser.c */
    unsigned char   sib;               /* sib byte set by set_rm_sib() in parser.c */
    unsigned char   Ofssize;           /* current offset size */
    unsigned char   opc_or;            /* bit 0 & 2 or'ed with opcode */
#if AVXSUPP
    unsigned char   vexregop;          /* in based-1 format (0=empty) */
#endif
    union {
        unsigned char flags;
        struct {
            unsigned char   iswide:1;       /* 0=byte, 1=word/dword/qword */
            unsigned char   isdirect:1;     /* 1=direct addressing mode */
            unsigned char   isfar:1;        /* CALL/JMP far */
            unsigned char   const_size_fixed:1; /* v2.01 */
#if AMD64_SUPPORT
            unsigned char   x86hi_used:1;   /* AH,BH,CH,DH used */
            unsigned char   x64lo_used:1;   /* SPL,BPL,SIL,DIL used */
#endif
            unsigned char   undef_sym:1;    /* v2.06b: struct member is forward ref */
        };
    };
};

#define OPND1 0
#define OPND2 1
#define OPND3 2

/* branch instructions are still sorted:
 * CALL, JMP, Jcc, J[e|r]CXZ, LOOP, LOOPcc
 */

#define IS_CALL( inst )       ( inst == T_CALL )
#define IS_JMPCALL( inst )    ( inst == T_CALL || inst == T_JMP    )
#define IS_JMP( inst )        ( inst >= T_JMP  && inst < T_LOOP  )
#define IS_JCC( inst )        ( inst >  T_JMP  && inst < T_JCXZ  )
#define IS_BRANCH( inst )     ( inst >= T_CALL && inst < T_LOOP  )
#define IS_ANY_BRANCH( inst ) ( inst >= T_CALL && inst <= T_LOOPNZW )
#define IS_XCX_BRANCH( inst ) ( inst >= T_JCXZ && inst <= T_LOOPNZW )

#define IS_OPER_32( s )   ( s->Ofssize ? ( s->prefix.opsiz == FALSE ) : ( s->prefix.opsiz == TRUE ))

/* globals */
//extern struct asym           WordSize;
//#define CurrWordSize WordSize.value
extern const struct instr_item   InstrTable[];   /* instruction table */
extern const struct special_item SpecialTable[]; /* rest of res words */
extern uint_16                   optable_idx[];  /* helper, access thru IndexFromToken() only */

#define IndexFromToken( tok )  optable_idx[ ( tok ) - SPECIAL_LAST ]

extern int        SizeFromMemtype( enum memtype, int, struct asym * );
extern ret_code   MemtypeFromSize( int, enum memtype * );
extern int        SizeFromRegister( int );
extern ret_code   GetLangType( int *, struct asm_tok[], enum lang_type * );

extern void       sym_add_table( struct symbol_queue *, struct dsym * );
extern void       sym_remove_table( struct symbol_queue *, struct dsym * );
extern void       sym_ext2int( struct asym * );

extern int        OperandSize( enum operand_type, const struct code_info * );
extern void       set_frame( const struct asym *sym );
extern void       set_frame2( const struct asym *sym );
extern ret_code   ParseLine( struct asm_tok[] );
extern void       ProcessFile( struct asm_tok[] );

extern void       WritePreprocessedLine( const char * );

#endif
