#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;

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;

#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(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)

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

#endif /* _X86_H_ */
