#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 cpu_state_t {
        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_t;

extern cpu_state_t 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 ((int8_t)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(char *s, uint16_t error);
void x86gpf_expected(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)

typedef struct cyrix_t {
        struct {
                uint32_t base;
                uint64_t size;
        } arr[8];
        uint32_t smhr;
} cyrix_t;

extern cyrix_t cyrix;

#endif /* _X86_H_ */
