#ifndef _X86_H_
#define _X86_H_

#define EAX cpu_state.regs[0].l
#define ECX cpu_state.regs[1].l
#define EDX cpu_state.regs[2].l
#define EBX cpu_state.regs[3].l
#define ESP cpu_state.regs[4].l
#define EBP cpu_state.regs[5].l
#define ESI cpu_state.regs[6].l
#define EDI cpu_state.regs[7].l
#define AX cpu_state.regs[0].w
#define CX cpu_state.regs[1].w
#define DX cpu_state.regs[2].w
#define BX cpu_state.regs[3].w
#define SP cpu_state.regs[4].w
#define BP cpu_state.regs[5].w
#define SI cpu_state.regs[6].w
#define DI cpu_state.regs[7].w
#define AL cpu_state.regs[0].b.l
#define AH cpu_state.regs[0].b.h
#define CL cpu_state.regs[1].b.l
#define CH cpu_state.regs[1].b.h
#define DL cpu_state.regs[2].b.l
#define DH cpu_state.regs[2].b.h
#define BL cpu_state.regs[3].b.l
#define BH cpu_state.regs[3].b.h

typedef union
{
        uint32_t l;
        uint16_t w;
        struct
        {
                uint8_t l,h;
        } b;
} x86reg;

typedef struct
{
        uint32_t base;
        uint32_t limit, limit_raw;
        uint8_t access, access2;
        uint16_t seg;
        uint32_t limit_low, limit_high;
        int checked; /*Non-zero if selector is known to be valid*/
} x86seg;

typedef union MMX_REG
{
        uint64_t q;
        int64_t  sq;
        uint32_t l[2];
        int32_t  sl[2];
        uint16_t w[4];
        int16_t  sw[4];
        uint8_t  b[8];
        int8_t   sb[8];
        float    f[2];
} MMX_REG;

typedef struct
{
        x86reg regs[8];

        uint8_t tag[8];

        x86seg *ea_seg;
        uint32_t eaaddr;

        int flags_op;
        uint32_t flags_res;
        uint32_t flags_op1, flags_op2;

        uint32_t pc;
        uint32_t oldpc;
        uint32_t op32;

        int TOP;

        union
        {
                struct
                {
                        int8_t rm, mod, reg;
                } rm_mod_reg;
                uint32_t rm_mod_reg_data;
        } rm_data;

        int8_t ssegs;
        int8_t ismmx;
        int8_t abrt;
        int8_t smi_pending;

        int _cycles;
        int cpu_recomp_ins;

        uint16_t npxs, npxc;

        double ST[8];

        uint16_t MM_w4[8];

        MMX_REG MM[8];

        uint32_t old_fp_control, new_fp_control;
#if defined i386 || defined __i386 || defined __i386__ || defined _X86_
        uint16_t old_fp_control2, new_fp_control2;
#endif
#if defined i386 || defined __i386 || defined __i386__ || defined _X86_ || defined __amd64__
        uint32_t trunc_fp_control;
#endif
        x86seg seg_cs,seg_ds,seg_es,seg_ss,seg_fs,seg_gs;

        union
        {
                uint32_t l;
                uint16_t w;
        } CR0;

        uint16_t flags, eflags;

        uint32_t smbase;
} CPU_STATE;

extern CPU_STATE cpu_state;

#define cpu_state_offset(MEMBER) ((uintptr_t)&cpu_state.MEMBER - (uintptr_t)&cpu_state - 128)

#define cycles cpu_state._cycles
extern int cycles_main;

#define cr0 cpu_state.CR0.l
#define msw cpu_state.CR0.w

/*Segments -
  _cs,_ds,_es,_ss are the segment structures
  CS,DS,ES,SS is the 16-bit data
  cs,ds,es,ss are defines to the bases*/
#define CS cpu_state.seg_cs.seg
#define DS cpu_state.seg_ds.seg
#define ES cpu_state.seg_es.seg
#define SS cpu_state.seg_ss.seg
#define FS cpu_state.seg_fs.seg
#define GS cpu_state.seg_gs.seg
#define cs cpu_state.seg_cs.base
#define ds cpu_state.seg_ds.base
#define es cpu_state.seg_es.base
#define ss cpu_state.seg_ss.base
//#define seg_fs _fs.base
#define gs cpu_state.seg_gs.base

#define CPL ((cpu_state.seg_cs.access>>5)&3)

#define C_FLAG  0x0001
#define P_FLAG  0x0004
#define A_FLAG  0x0010
#define Z_FLAG  0x0040
#define N_FLAG  0x0080
#define T_FLAG  0x0100
#define I_FLAG  0x0200
#define D_FLAG  0x0400
#define V_FLAG  0x0800
#define NT_FLAG 0x4000
#define VM_FLAG 0x0002 /*In EFLAGS*/
#define VIF_FLAG 0x0008 /*In EFLAGS*/
#define VIP_FLAG 0x0010 /*In EFLAGS*/

#define WP_FLAG 0x10000 /*In CR0*/

#define CR4_VME (1 << 0)
#define CR4_PVI (1 << 1)
#define CR4_PSE (1 << 4)

#define IOPL ((cpu_state.flags >> 12) & 3)

#define IOPLp ((!(msw&1)) || (CPL<=IOPL))

extern x86seg gdt, ldt, idt, tr;

extern uint32_t cr2, cr3, cr4;
extern uint32_t dr[8];

extern uint16_t sysenter_cs;
extern uint32_t sysenter_eip, sysenter_esp;


extern uint16_t cpu_cur_status;

/*The flags below must match in both cpu_cur_status and block->status for a block
  to be valid*/
#define CPU_STATUS_USE32   (1 << 0)
#define CPU_STATUS_STACK32 (1 << 1)
#define CPU_STATUS_PMODE   (1 << 2)
#define CPU_STATUS_V86     (1 << 3)
#define CPU_STATUS_SMM     (1 << 4)
#define CPU_STATUS_FLAGS 0xff

/*If the flags below are set in cpu_cur_status, they must be set in block->status.
  Otherwise they are ignored*/
#define CPU_STATUS_NOTFLATDS  (1 << 8)
#define CPU_STATUS_NOTFLATSS  (1 << 9)
#define CPU_STATUS_MASK 0xff00


extern uint32_t rmdat;
#define fetchdat rmdat

extern uint32_t easeg;
extern uint32_t *eal_r, *eal_w;

extern int oldcpl;
extern uint32_t oldss;

extern int nmi_enable;

extern int trap;

extern uint32_t use32;
extern int stack32;

extern uint8_t znptable8[256];
extern uint16_t *mod1add[2][8];
extern uint32_t *mod1seg[8];

extern int cgate16, cgate32;
extern int cpl_override;

extern int x86_was_reset;

extern int insc;
extern int fpucount;
extern float mips,flops;

#define setznp168 setznp16

#define getr8(r)   ((r&4)?cpu_state.regs[r&3].b.h:cpu_state.regs[r&3].b.l)
#define getr16(r)  cpu_state.regs[r].w
#define getr32(r)  cpu_state.regs[r].l

#define setr8(r,v) if (r&4) cpu_state.regs[r&3].b.h=v; \
                   else     cpu_state.regs[r&3].b.l=v;
#define setr16(r,v) cpu_state.regs[r].w=v
#define setr32(r,v) cpu_state.regs[r].l=v

#define fetchea()   { rmdat=readmemb(cs+pc); pc++;  \
                    reg=(rmdat>>3)&7;               \
                    mod=(rmdat>>6)&3;               \
                    rm=rmdat&7;                   \
                    if (mod!=3) fetcheal(); }


extern int optype;
#define JMP 1
#define CALL 2
#define IRET 3
#define OPTYPE_INT 4


enum
{
        ABRT_NONE = 0,
        ABRT_GEN,
        ABRT_TS  = 0xA,
        ABRT_NP  = 0xB,
        ABRT_SS  = 0xC,
        ABRT_GPF = 0xD,
        ABRT_PF  = 0xE
};

#define ABRT_MASK 0x7f
/*An 'expected' exception is one that would be expected to occur on every execution
  of this code path; eg a GPF due to being in v86 mode. An 'unexpected' exception is
  one that would be unlikely to occur on the next exception, eg a page fault may be
  fixed up by the exception handler and the next execution would not hit it.
  
  This distinction is used by the dynarec; a block that hits an 'expected' exception
  would be compiled, a block that hits an 'unexpected' exception would be rejected so
  that we don't end up with an unnecessarily short block*/
#define ABRT_EXPECTED  0x80
extern uint32_t abrt_error;

void x86_doabrt(int x86_abrt);

extern int codegen_flat_ds;
extern int codegen_flat_ss;

extern uint32_t pccache;
extern uint8_t *pccache2;

void x86illegal();

void x86seg_reset();
void x86gpf(const char *s, uint16_t error);
void x86gpf_expected(const char *s, uint16_t error);

void resetx86();
void softresetx86();
void refreshread();
void dumpregs();

void execx86(int cycs);
void exec386(int cycs);
void exec386_dynarec(int cycs);

void pmodeint(int num, int soft);
int loadseg(uint16_t seg, x86seg *s);
void loadcs(uint16_t seg);
void loadcscall(uint16_t seg, uint32_t old_pc);
void loadcsjmp(uint16_t seg, uint32_t old_pc);
void pmoderetf(int is32, uint16_t off);
void pmodeiret(int is32);

void x86_int(int num);
void x86_int_sw(int num);
int x86_int_sw_rm(int num);

void sysenter(void);
void sysexit(void);

int divl(uint32_t val);
int idivl(int32_t val);

extern int cpu_end_block_after_ins;

void x86_smi_trigger(void);
void x86_smi_enter(void);
void x86_smi_leave(void);

void cyrix_load_seg_descriptor(uint32_t addr, x86seg *seg);
void cyrix_write_seg_descriptor(uint32_t addr, x86seg *seg);

#define SMHR_VALID (1 << 0)
#define SMHR_ADDR_MASK (0xfffffffc)

struct cyrixs
{
        struct
        {
                uint32_t base;
                uint64_t size;
        } arr[8];
        uint32_t smhr;
};
extern struct cyrixs cyrix;
#endif
