#include "ibm.h"
#include "cpu.h"
#include "model.h"
#include "io.h"
#include "x86_ops.h"
#include "mem.h"
#include "pci.h"
#include "codegen.h"
#include "x87_timings.h"

int fpu_type;
uint32_t cpu_features;

static int cpu_turbo_speed, cpu_nonturbo_speed;
static int cpu_turbo = 1;

int isa_cycles;
int has_vlb;
static uint8_t ccr0, ccr1, ccr2, ccr3, ccr4, ccr5, ccr6;

OpFn *x86_dynarec_opcodes;
OpFn *x86_dynarec_opcodes_0f;
OpFn *x86_dynarec_opcodes_d8_a16;
OpFn *x86_dynarec_opcodes_d8_a32;
OpFn *x86_dynarec_opcodes_d9_a16;
OpFn *x86_dynarec_opcodes_d9_a32;
OpFn *x86_dynarec_opcodes_da_a16;
OpFn *x86_dynarec_opcodes_da_a32;
OpFn *x86_dynarec_opcodes_db_a16;
OpFn *x86_dynarec_opcodes_db_a32;
OpFn *x86_dynarec_opcodes_dc_a16;
OpFn *x86_dynarec_opcodes_dc_a32;
OpFn *x86_dynarec_opcodes_dd_a16;
OpFn *x86_dynarec_opcodes_dd_a32;
OpFn *x86_dynarec_opcodes_de_a16;
OpFn *x86_dynarec_opcodes_de_a32;
OpFn *x86_dynarec_opcodes_df_a16;
OpFn *x86_dynarec_opcodes_df_a32;
OpFn *x86_dynarec_opcodes_REPE;
OpFn *x86_dynarec_opcodes_REPNE;
OpFn *x86_dynarec_opcodes_3DNOW;

OpFn *x86_opcodes;
OpFn *x86_opcodes_0f;
OpFn *x86_opcodes_d8_a16;
OpFn *x86_opcodes_d8_a32;
OpFn *x86_opcodes_d9_a16;
OpFn *x86_opcodes_d9_a32;
OpFn *x86_opcodes_da_a16;
OpFn *x86_opcodes_da_a32;
OpFn *x86_opcodes_db_a16;
OpFn *x86_opcodes_db_a32;
OpFn *x86_opcodes_dc_a16;
OpFn *x86_opcodes_dc_a32;
OpFn *x86_opcodes_dd_a16;
OpFn *x86_opcodes_dd_a32;
OpFn *x86_opcodes_de_a16;
OpFn *x86_opcodes_de_a32;
OpFn *x86_opcodes_df_a16;
OpFn *x86_opcodes_df_a32;
OpFn *x86_opcodes_REPE;
OpFn *x86_opcodes_REPNE;
OpFn *x86_opcodes_3DNOW;

enum {
	CPUID_FPU = (1 << 0),
	CPUID_VME = (1 << 1),
	CPUID_PSE = (1 << 3),
	CPUID_TSC = (1 << 4),
	CPUID_MSR = (1 << 5),
	CPUID_CMPXCHG8B = (1 << 8),
	CPUID_SEP = (1 << 11),
	CPUID_CMOV = (1 << 15),
	CPUID_MMX = (1 << 23)
};

/*Addition flags returned by CPUID function 0x80000001*/
enum {
	CPUID_3DNOW = (1 << 31)
};

int cpu = 3, cpu_manufacturer = 0;
CPU *cpu_s;
int cpu_multi;
int cpu_iscyrix;
int cpu_16bitbus;
int cpu_busspeed;
int cpu_use_dynarec;
int cpu_cyrix_alignment;

uint64_t cpu_CR4_mask;

int cpu_cycles_read, cpu_cycles_read_l, cpu_cycles_write, cpu_cycles_write_l;
int cpu_prefetch_cycles, cpu_prefetch_width, cpu_mem_prefetch_cycles, cpu_rom_prefetch_cycles;
int cpu_waitstates;
int cpu_cache_int_enabled, cpu_cache_ext_enabled;

int is386;
int is486;
int CPUID;

uint64_t tsc = 0;

int timing_rr;
int timing_mr, timing_mrl;
int timing_rm, timing_rml;
int timing_mm, timing_mml;
int timing_bt, timing_bnt;
int timing_int, timing_int_rm, timing_int_v86, timing_int_pm, timing_int_pm_outer;
int timing_iret_rm, timing_iret_v86, timing_iret_pm, timing_iret_pm_outer;
int timing_call_rm, timing_call_pm, timing_call_pm_gate, timing_call_pm_gate_inner;
int timing_retf_rm, timing_retf_pm, timing_retf_pm_outer;
int timing_jmp_rm, timing_jmp_pm, timing_jmp_pm_gate;
int timing_misaligned;

static struct {
	uint32_t tr1, tr12;
	uint32_t cesr;
	uint32_t fcr;
	uint64_t fcr2, fcr3;
} msr;

void cpu_set_edx() {
	EDX = models[model]->cpu[cpu_manufacturer].cpus[cpu].edx_reset;
}

int fpu_get_type(int model, int manu, int cpu, const char *internal_name) {
	CPU *cpu_s = &models[model]->cpu[manu].cpus[cpu];
	const FPU *fpus = cpu_s->fpus;
	int fpu_type = fpus[0].type;
	int c = 0;

	while (fpus[c].internal_name) {
		if (!strcmp(internal_name, fpus[c].internal_name))
			fpu_type = fpus[c].type;
		c++;
	}

	return fpu_type;
}

const char *fpu_get_internal_name(int model, int manu, int cpu, int type) {
	CPU *cpu_s = &models[model]->cpu[manu].cpus[cpu];
	const FPU *fpus = cpu_s->fpus;
	int c = 0;

	while (fpus[c].internal_name) {
		if (fpus[c].type == type)
			return fpus[c].internal_name;
		c++;
	}

	return fpus[0].internal_name;
}

const char *fpu_get_name_from_index(int model, int manu, int cpu, int c) {
	CPU *cpu_s = &models[model]->cpu[manu].cpus[cpu];
	const FPU *fpus = cpu_s->fpus;

	return fpus[c].name;
}

int fpu_get_type_from_index(int model, int manu, int cpu, int c) {
	CPU *cpu_s = &models[model]->cpu[manu].cpus[cpu];
	const FPU *fpus = cpu_s->fpus;

	return fpus[c].type;
}

CPU *cpu_s;

void cpu_set() {
	if (!models[model]->cpu[cpu_manufacturer].cpus) {
		/*CPU is invalid, set to default*/
		cpu_manufacturer = 0;
		cpu = 0;
	}

	cpu_s = &models[model]->cpu[cpu_manufacturer].cpus[cpu];

	CPUID = cpu_s->cpuid_model;
	cpuspeed = cpu_s->speed;
	is8086 = (cpu_s->cpu_type > CPU_8088);
	is386 = (cpu_s->cpu_type >= CPU_386SX);
	is486 = (cpu_s->cpu_type >= CPU_i486SX) || (cpu_s->cpu_type == CPU_486SLC || cpu_s->cpu_type == CPU_486DLC);
	hasfpu = (fpu_type != FPU_NONE);

	cpu_iscyrix = (cpu_s->cpu_type == CPU_486SLC || cpu_s->cpu_type == CPU_486DLC || cpu_s->cpu_type == CPU_Cx486S || cpu_s->cpu_type == CPU_Cx486DX || cpu_s->cpu_type == CPU_Cx5x86
		|| cpu_s->cpu_type == CPU_Cx6x86 || cpu_s->cpu_type == CPU_Cx6x86MX || cpu_s->cpu_type == CPU_Cx6x86L || cpu_s->cpu_type == CPU_CxGX1);
	cpu_16bitbus = (cpu_s->cpu_type == CPU_286 || cpu_s->cpu_type == CPU_386SX || cpu_s->cpu_type == CPU_486SLC);
	if (cpu_s->multi)
		cpu_busspeed = cpu_s->rspeed / cpu_s->multi;
	cpu_multi = cpu_s->multi;
	ccr0 = ccr1 = ccr2 = ccr3 = ccr4 = ccr5 = ccr6 = 0;
	has_vlb = (cpu_s->cpu_type >= CPU_i486SX) && (cpu_s->cpu_type <= CPU_Cx5x86);

	cpu_turbo_speed = cpu_s->rspeed;
	if (cpu_s->cpu_type < CPU_286)
		cpu_nonturbo_speed = 4772728;
	else if (cpu_s->rspeed < 8000000)
		cpu_nonturbo_speed = cpu_s->rspeed;
	else
		cpu_nonturbo_speed = 8000000;
	cpu_turbo = 1;

	cpu_update_waitstates();

	isa_cycles = cpu_s->atclk_div;

	if (cpu_s->rspeed <= 8000000)
		cpu_rom_prefetch_cycles = cpu_mem_prefetch_cycles;
	else
		cpu_rom_prefetch_cycles = cpu_s->rspeed / 1000000;

	if (cpu_s->pci_speed) {
		pci_nonburst_time = 4 * cpu_s->rspeed / cpu_s->pci_speed;
		pci_burst_time = cpu_s->rspeed / cpu_s->pci_speed;
	} else {
		pci_nonburst_time = 4;
		pci_burst_time = 1;
	}
	pclog("PCI burst=%i nonburst=%i\n", pci_burst_time, pci_nonburst_time);

	if (cpu_iscyrix)
		io_sethandler(0x0022, 0x0002, cyrix_read, NULL, NULL, cyrix_write, NULL, NULL, NULL);
	else
		io_removehandler(0x0022, 0x0002, cyrix_read, NULL, NULL, cyrix_write, NULL, NULL, NULL);

	pclog("hasfpu - %i\n", hasfpu);
	pclog("is486 - %i  %i\n", is486, cpu_s->cpu_type);

	x86_setopcodes(ops_386, ops_386_0f, dynarec_ops_386, dynarec_ops_386_0f);
	x86_opcodes_REPE = ops_REPE;
	x86_opcodes_REPNE = ops_REPNE;
	x86_opcodes_3DNOW = ops_3DNOW;
	x86_dynarec_opcodes_REPE = dynarec_ops_REPE;
	x86_dynarec_opcodes_REPNE = dynarec_ops_REPNE;
	x86_dynarec_opcodes_3DNOW = dynarec_ops_3DNOW;

	if (hasfpu) {
		x86_dynarec_opcodes_d8_a16 = dynarec_ops_fpu_d8_a16;
		x86_dynarec_opcodes_d8_a32 = dynarec_ops_fpu_d8_a32;
		x86_dynarec_opcodes_d9_a16 = dynarec_ops_fpu_d9_a16;
		x86_dynarec_opcodes_d9_a32 = dynarec_ops_fpu_d9_a32;
		x86_dynarec_opcodes_da_a16 = dynarec_ops_fpu_da_a16;
		x86_dynarec_opcodes_da_a32 = dynarec_ops_fpu_da_a32;
		x86_dynarec_opcodes_db_a16 = dynarec_ops_fpu_db_a16;
		x86_dynarec_opcodes_db_a32 = dynarec_ops_fpu_db_a32;
		x86_dynarec_opcodes_dc_a16 = dynarec_ops_fpu_dc_a16;
		x86_dynarec_opcodes_dc_a32 = dynarec_ops_fpu_dc_a32;
		x86_dynarec_opcodes_dd_a16 = dynarec_ops_fpu_dd_a16;
		x86_dynarec_opcodes_dd_a32 = dynarec_ops_fpu_dd_a32;
		x86_dynarec_opcodes_de_a16 = dynarec_ops_fpu_de_a16;
		x86_dynarec_opcodes_de_a32 = dynarec_ops_fpu_de_a32;
		x86_dynarec_opcodes_df_a16 = dynarec_ops_fpu_df_a16;
		x86_dynarec_opcodes_df_a32 = dynarec_ops_fpu_df_a32;
	} else {
		x86_dynarec_opcodes_d8_a16 = dynarec_ops_nofpu_a16;
		x86_dynarec_opcodes_d8_a32 = dynarec_ops_nofpu_a32;
		x86_dynarec_opcodes_d9_a16 = dynarec_ops_nofpu_a16;
		x86_dynarec_opcodes_d9_a32 = dynarec_ops_nofpu_a32;
		x86_dynarec_opcodes_da_a16 = dynarec_ops_nofpu_a16;
		x86_dynarec_opcodes_da_a32 = dynarec_ops_nofpu_a32;
		x86_dynarec_opcodes_db_a16 = dynarec_ops_nofpu_a16;
		x86_dynarec_opcodes_db_a32 = dynarec_ops_nofpu_a32;
		x86_dynarec_opcodes_dc_a16 = dynarec_ops_nofpu_a16;
		x86_dynarec_opcodes_dc_a32 = dynarec_ops_nofpu_a32;
		x86_dynarec_opcodes_dd_a16 = dynarec_ops_nofpu_a16;
		x86_dynarec_opcodes_dd_a32 = dynarec_ops_nofpu_a32;
		x86_dynarec_opcodes_de_a16 = dynarec_ops_nofpu_a16;
		x86_dynarec_opcodes_de_a32 = dynarec_ops_nofpu_a32;
		x86_dynarec_opcodes_df_a16 = dynarec_ops_nofpu_a16;
		x86_dynarec_opcodes_df_a32 = dynarec_ops_nofpu_a32;
	}
	codegen_timing_set(&codegen_timing_486);

	if (hasfpu) {
		x86_opcodes_d8_a16 = ops_fpu_d8_a16;
		x86_opcodes_d8_a32 = ops_fpu_d8_a32;
		x86_opcodes_d9_a16 = ops_fpu_d9_a16;
		x86_opcodes_d9_a32 = ops_fpu_d9_a32;
		x86_opcodes_da_a16 = ops_fpu_da_a16;
		x86_opcodes_da_a32 = ops_fpu_da_a32;
		x86_opcodes_db_a16 = ops_fpu_db_a16;
		x86_opcodes_db_a32 = ops_fpu_db_a32;
		x86_opcodes_dc_a16 = ops_fpu_dc_a16;
		x86_opcodes_dc_a32 = ops_fpu_dc_a32;
		x86_opcodes_dd_a16 = ops_fpu_dd_a16;
		x86_opcodes_dd_a32 = ops_fpu_dd_a32;
		x86_opcodes_de_a16 = ops_fpu_de_a16;
		x86_opcodes_de_a32 = ops_fpu_de_a32;
		x86_opcodes_df_a16 = ops_fpu_df_a16;
		x86_opcodes_df_a32 = ops_fpu_df_a32;
	} else {
		x86_opcodes_d8_a16 = ops_nofpu_a16;
		x86_opcodes_d8_a32 = ops_nofpu_a32;
		x86_opcodes_d9_a16 = ops_nofpu_a16;
		x86_opcodes_d9_a32 = ops_nofpu_a32;
		x86_opcodes_da_a16 = ops_nofpu_a16;
		x86_opcodes_da_a32 = ops_nofpu_a32;
		x86_opcodes_db_a16 = ops_nofpu_a16;
		x86_opcodes_db_a32 = ops_nofpu_a32;
		x86_opcodes_dc_a16 = ops_nofpu_a16;
		x86_opcodes_dc_a32 = ops_nofpu_a32;
		x86_opcodes_dd_a16 = ops_nofpu_a16;
		x86_opcodes_dd_a32 = ops_nofpu_a32;
		x86_opcodes_de_a16 = ops_nofpu_a16;
		x86_opcodes_de_a32 = ops_nofpu_a32;
		x86_opcodes_df_a16 = ops_nofpu_a16;
		x86_opcodes_df_a32 = ops_nofpu_a32;
	}

	memset(&msr, 0, sizeof(msr));

	timing_misaligned = 0;
	cpu_cyrix_alignment = 0;
	cpu_CR4_mask = 0;

	switch (cpu_s->cpu_type) {
	case CPU_8088:
	case CPU_8086:break;

	case CPU_286:x86_setopcodes(ops_286, ops_286_0f, dynarec_ops_286, dynarec_ops_286_0f);
		timing_rr = 2;   /*register dest - register src*/
		timing_rm = 7;   /*register dest - memory src*/
		timing_mr = 7;   /*memory dest   - register src*/
		timing_mm = 7;   /*memory dest   - memory src*/
		timing_rml = 9;   /*register dest - memory src long*/
		timing_mrl = 11;  /*memory dest   - register src long*/
		timing_mml = 11;  /*memory dest   - memory src*/
		timing_bt = 7 - 3; /*branch taken*/
		timing_bnt = 3;   /*branch not taken*/
		timing_int = 0;
		timing_int_rm = 23;
		timing_int_v86 = 0;
		timing_int_pm = 40;
		timing_int_pm_outer = 78;
		timing_iret_rm = 17;
		timing_iret_v86 = 0;
		timing_iret_pm = 31;
		timing_iret_pm_outer = 55;
		timing_call_rm = 13;
		timing_call_pm = 26;
		timing_call_pm_gate = 52;
		timing_call_pm_gate_inner = 82;
		timing_retf_rm = 15;
		timing_retf_pm = 25;
		timing_retf_pm_outer = 55;
		timing_jmp_rm = 11;
		timing_jmp_pm = 23;
		timing_jmp_pm_gate = 38;
		break;

	case CPU_386SX:timing_rr = 2;   /*register dest - register src*/
		timing_rm = 6;   /*register dest - memory src*/
		timing_mr = 7;   /*memory dest   - register src*/
		timing_mm = 6;   /*memory dest   - memory src*/
		timing_rml = 8;   /*register dest - memory src long*/
		timing_mrl = 11;  /*memory dest   - register src long*/
		timing_mml = 10;  /*memory dest   - memory src*/
		timing_bt = 7 - 3; /*branch taken*/
		timing_bnt = 3;   /*branch not taken*/
		timing_int = 0;
		timing_int_rm = 37;
		timing_int_v86 = 59;
		timing_int_pm = 99;
		timing_int_pm_outer = 119;
		timing_iret_rm = 22;
		timing_iret_v86 = 60;
		timing_iret_pm = 38;
		timing_iret_pm_outer = 82;
		timing_call_rm = 17;
		timing_call_pm = 34;
		timing_call_pm_gate = 52;
		timing_call_pm_gate_inner = 86;
		timing_retf_rm = 18;
		timing_retf_pm = 32;
		timing_retf_pm_outer = 68;
		timing_jmp_rm = 12;
		timing_jmp_pm = 27;
		timing_jmp_pm_gate = 45;
		break;

	case CPU_386DX:timing_rr = 2; /*register dest - register src*/
		timing_rm = 6; /*register dest - memory src*/
		timing_mr = 7; /*memory dest   - register src*/
		timing_mm = 6; /*memory dest   - memory src*/
		timing_rml = 6; /*register dest - memory src long*/
		timing_mrl = 7; /*memory dest   - register src long*/
		timing_mml = 6; /*memory dest   - memory src*/
		timing_bt = 7 - 3; /*branch taken*/
		timing_bnt = 3; /*branch not taken*/
		timing_int = 0;
		timing_int_rm = 37;
		timing_int_v86 = 59;
		timing_int_pm = 99;
		timing_int_pm_outer = 119;
		timing_iret_rm = 22;
		timing_iret_v86 = 60;
		timing_iret_pm = 38;
		timing_iret_pm_outer = 82;
		timing_call_rm = 17;
		timing_call_pm = 34;
		timing_call_pm_gate = 52;
		timing_call_pm_gate_inner = 86;
		timing_retf_rm = 18;
		timing_retf_pm = 32;
		timing_retf_pm_outer = 68;
		timing_jmp_rm = 12;
		timing_jmp_pm = 27;
		timing_jmp_pm_gate = 45;
		break;

	case CPU_486SLC:timing_rr = 1; /*register dest - register src*/
		timing_rm = 3; /*register dest - memory src*/
		timing_mr = 5; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 5; /*register dest - memory src long*/
		timing_mrl = 7; /*memory dest   - register src long*/
		timing_mml = 7;
		timing_bt = 6 - 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		/*unknown*/
		timing_int = 4;
		timing_int_rm = 14;
		timing_int_v86 = 82;
		timing_int_pm = 49;
		timing_int_pm_outer = 77;
		timing_iret_rm = 14;
		timing_iret_v86 = 66;
		timing_iret_pm = 31;
		timing_iret_pm_outer = 66;
		timing_call_rm = 12;
		timing_call_pm = 30;
		timing_call_pm_gate = 41;
		timing_call_pm_gate_inner = 83;
		timing_retf_rm = 13;
		timing_retf_pm = 26;
		timing_retf_pm_outer = 61;
		timing_jmp_rm = 9;
		timing_jmp_pm = 26;
		timing_jmp_pm_gate = 37;
		timing_misaligned = 3;
		break;

	case CPU_486DLC:timing_rr = 1; /*register dest - register src*/
		timing_rm = 3; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 3; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 6 - 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		/*unknown*/
		timing_int = 4;
		timing_int_rm = 14;
		timing_int_v86 = 82;
		timing_int_pm = 49;
		timing_int_pm_outer = 77;
		timing_iret_rm = 14;
		timing_iret_v86 = 66;
		timing_iret_pm = 31;
		timing_iret_pm_outer = 66;
		timing_call_rm = 12;
		timing_call_pm = 30;
		timing_call_pm_gate = 41;
		timing_call_pm_gate_inner = 83;
		timing_retf_rm = 13;
		timing_retf_pm = 26;
		timing_retf_pm_outer = 61;
		timing_jmp_rm = 9;
		timing_jmp_pm = 26;
		timing_jmp_pm_gate = 37;
		timing_misaligned = 3;
		break;

	case CPU_iDX4:cpu_features = CPU_FEATURE_CR4 | CPU_FEATURE_VME;
		cpu_CR4_mask = CR4_VME | CR4_PVI | CR4_VME;
	case CPU_i486SX:
	case CPU_i486DX:timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 3 - 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		timing_int = 4;
		timing_int_rm = 26;
		timing_int_v86 = 82;
		timing_int_pm = 44;
		timing_int_pm_outer = 71;
		timing_iret_rm = 15;
		timing_iret_v86 = 36; /*unknown*/
		timing_iret_pm = 20;
		timing_iret_pm_outer = 36;
		timing_call_rm = 18;
		timing_call_pm = 20;
		timing_call_pm_gate = 35;
		timing_call_pm_gate_inner = 69;
		timing_retf_rm = 13;
		timing_retf_pm = 17;
		timing_retf_pm_outer = 35;
		timing_jmp_rm = 17;
		timing_jmp_pm = 19;
		timing_jmp_pm_gate = 32;
		timing_misaligned = 3;
		break;

	case CPU_Am486SX:
	case CPU_Am486DX:
		/*AMD timing identical to Intel*/
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 3 - 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		timing_int = 4;
		timing_int_rm = 26;
		timing_int_v86 = 82;
		timing_int_pm = 44;
		timing_int_pm_outer = 71;
		timing_iret_rm = 15;
		timing_iret_v86 = 36; /*unknown*/
		timing_iret_pm = 20;
		timing_iret_pm_outer = 36;
		timing_call_rm = 18;
		timing_call_pm = 20;
		timing_call_pm_gate = 35;
		timing_call_pm_gate_inner = 69;
		timing_retf_rm = 13;
		timing_retf_pm = 17;
		timing_retf_pm_outer = 35;
		timing_jmp_rm = 17;
		timing_jmp_pm = 19;
		timing_jmp_pm_gate = 32;
		timing_misaligned = 3;
		break;

	case CPU_Cx486S:
	case CPU_Cx486DX:timing_rr = 1; /*register dest - register src*/
		timing_rm = 3; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 3; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 4 - 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		timing_int = 4;
		timing_int_rm = 14;
		timing_int_v86 = 82;
		timing_int_pm = 49;
		timing_int_pm_outer = 77;
		timing_iret_rm = 14;
		timing_iret_v86 = 66; /*unknown*/
		timing_iret_pm = 31;
		timing_iret_pm_outer = 66;
		timing_call_rm = 12;
		timing_call_pm = 30;
		timing_call_pm_gate = 41;
		timing_call_pm_gate_inner = 83;
		timing_retf_rm = 13;
		timing_retf_pm = 26;
		timing_retf_pm_outer = 61;
		timing_jmp_rm = 9;
		timing_jmp_pm = 26;
		timing_jmp_pm_gate = 37;
		timing_misaligned = 3;
		break;

	case CPU_Cx5x86:timing_rr = 1; /*register dest - register src*/
		timing_rm = 1; /*register dest - memory src*/
		timing_mr = 2; /*memory dest   - register src*/
		timing_mm = 2;
		timing_rml = 1; /*register dest - memory src long*/
		timing_mrl = 2; /*memory dest   - register src long*/
		timing_mml = 2;
		timing_bt = 5 - 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		timing_int = 0;
		timing_int_rm = 9;
		timing_int_v86 = 82; /*unknown*/
		timing_int_pm = 21;
		timing_int_pm_outer = 32;
		timing_iret_rm = 7;
		timing_iret_v86 = 26; /*unknown*/
		timing_iret_pm = 10;
		timing_iret_pm_outer = 26;
		timing_call_rm = 4;
		timing_call_pm = 15;
		timing_call_pm_gate = 26;
		timing_call_pm_gate_inner = 35;
		timing_retf_rm = 4;
		timing_retf_pm = 7;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 5;
		timing_jmp_pm = 7;
		timing_jmp_pm_gate = 17;
		timing_misaligned = 2;
		cpu_cyrix_alignment = 1;
		break;

	case CPU_WINCHIP:x86_setopcodes(ops_386, ops_winchip_0f, dynarec_ops_386, dynarec_ops_winchip_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 2; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 2; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 3 - 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MMX | CPU_FEATURE_MSR | CPU_FEATURE_CR4;
		msr.fcr = (1 << 8) | (1 << 16);
		cpu_CR4_mask = CR4_TSD | CR4_DE | CR4_MCE | CR4_PCE;
		/*unknown*/
		timing_int_rm = 26;
		timing_int_v86 = 82;
		timing_int_pm = 44;
		timing_int_pm_outer = 71;
		timing_iret_rm = 7;
		timing_iret_v86 = 26;
		timing_iret_pm = 10;
		timing_iret_pm_outer = 26;
		timing_call_rm = 4;
		timing_call_pm = 15;
		timing_call_pm_gate = 26;
		timing_call_pm_gate_inner = 35;
		timing_retf_rm = 4;
		timing_retf_pm = 7;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 5;
		timing_jmp_pm = 7;
		timing_jmp_pm_gate = 17;
		timing_misaligned = 2;
		cpu_cyrix_alignment = 1;
		codegen_timing_set(&codegen_timing_winchip);
		break;

	case CPU_WINCHIP2:x86_setopcodes(ops_386, ops_winchip2_0f, dynarec_ops_386, dynarec_ops_winchip2_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 2; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 2; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 3 - 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MMX | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_3DNOW;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21);
		cpu_CR4_mask = CR4_TSD | CR4_DE | CR4_MCE | CR4_PCE;
		/*unknown*/
		timing_int_rm = 26;
		timing_int_v86 = 82;
		timing_int_pm = 44;
		timing_int_pm_outer = 71;
		timing_iret_rm = 7;
		timing_iret_v86 = 26;
		timing_iret_pm = 10;
		timing_iret_pm_outer = 26;
		timing_call_rm = 4;
		timing_call_pm = 15;
		timing_call_pm_gate = 26;
		timing_call_pm_gate_inner = 35;
		timing_retf_rm = 4;
		timing_retf_pm = 7;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 5;
		timing_jmp_pm = 7;
		timing_jmp_pm_gate = 17;
		timing_misaligned = 2;
		cpu_cyrix_alignment = 1;
		codegen_timing_set(&codegen_timing_winchip2);
		break;

	case CPU_PENTIUM:x86_setopcodes(ops_386, ops_pentium_0f, dynarec_ops_386, dynarec_ops_pentium_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 0; /*branch taken*/
		timing_bnt = 2; /*branch not taken*/
		timing_int = 6;
		timing_int_rm = 11;
		timing_int_v86 = 54;
		timing_int_pm = 25;
		timing_int_pm_outer = 42;
		timing_iret_rm = 7;
		timing_iret_v86 = 27; /*unknown*/
		timing_iret_pm = 10;
		timing_iret_pm_outer = 27;
		timing_call_rm = 4;
		timing_call_pm = 4;
		timing_call_pm_gate = 22;
		timing_call_pm_gate_inner = 44;
		timing_retf_rm = 4;
		timing_retf_pm = 4;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 3;
		timing_jmp_pm = 3;
		timing_jmp_pm_gate = 18;
		timing_misaligned = 3;
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_VME;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		cpu_CR4_mask = CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | CR4_MCE | CR4_PCE;
		codegen_timing_set(&codegen_timing_pentium);
		break;

	case CPU_PENTIUMMMX:x86_setopcodes(ops_386, ops_pentiummmx_0f, dynarec_ops_386, dynarec_ops_pentiummmx_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 0; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		timing_int = 6;
		timing_int_rm = 11;
		timing_int_v86 = 54;
		timing_int_pm = 25;
		timing_int_pm_outer = 42;
		timing_iret_rm = 7;
		timing_iret_v86 = 27; /*unknown*/
		timing_iret_pm = 10;
		timing_iret_pm_outer = 27;
		timing_call_rm = 4;
		timing_call_pm = 4;
		timing_call_pm_gate = 22;
		timing_call_pm_gate_inner = 44;
		timing_retf_rm = 4;
		timing_retf_pm = 4;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 3;
		timing_jmp_pm = 3;
		timing_jmp_pm_gate = 18;
		timing_misaligned = 3;
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_VME | CPU_FEATURE_MMX;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		cpu_CR4_mask = CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | CR4_MCE | CR4_PCE;
		codegen_timing_set(&codegen_timing_pentium);
		break;

	case CPU_Cx6x86:x86_setopcodes(ops_386, ops_c6x86_0f, dynarec_ops_386, dynarec_ops_c6x86_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 1; /*register dest - memory src*/
		timing_mr = 2; /*memory dest   - register src*/
		timing_mm = 2;
		timing_rml = 1; /*register dest - memory src long*/
		timing_mrl = 2; /*memory dest   - register src long*/
		timing_mml = 2;
		timing_bt = 0; /*branch taken*/
		timing_bnt = 2; /*branch not taken*/
		timing_int_rm = 9;
		timing_int_v86 = 46;
		timing_int_pm = 21;
		timing_int_pm_outer = 32;
		timing_iret_rm = 7;
		timing_iret_v86 = 26;
		timing_iret_pm = 10;
		timing_iret_pm_outer = 26;
		timing_call_rm = 3;
		timing_call_pm = 4;
		timing_call_pm_gate = 15;
		timing_call_pm_gate_inner = 26;
		timing_retf_rm = 4;
		timing_retf_pm = 4;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 1;
		timing_jmp_pm = 4;
		timing_jmp_pm_gate = 14;
		timing_misaligned = 2;
		cpu_cyrix_alignment = 1;
		cpu_features = CPU_FEATURE_RDTSC;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		codegen_timing_set(&codegen_timing_686);
		CPUID = 0; /*Disabled on powerup by default*/
		break;

	case CPU_Cx6x86L:x86_setopcodes(ops_386, ops_pentium_0f, dynarec_ops_386, dynarec_ops_pentium_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 1; /*register dest - memory src*/
		timing_mr = 2; /*memory dest   - register src*/
		timing_mm = 2;
		timing_rml = 1; /*register dest - memory src long*/
		timing_mrl = 2; /*memory dest   - register src long*/
		timing_mml = 2;
		timing_bt = 0; /*branch taken*/
		timing_bnt = 2; /*branch not taken*/
		timing_int_rm = 9;
		timing_int_v86 = 46;
		timing_int_pm = 21;
		timing_int_pm_outer = 32;
		timing_iret_rm = 7;
		timing_iret_v86 = 26;
		timing_iret_pm = 10;
		timing_iret_pm_outer = 26;
		timing_call_rm = 3;
		timing_call_pm = 4;
		timing_call_pm_gate = 15;
		timing_call_pm_gate_inner = 26;
		timing_retf_rm = 4;
		timing_retf_pm = 4;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 1;
		timing_jmp_pm = 4;
		timing_jmp_pm_gate = 14;
		timing_misaligned = 2;
		cpu_cyrix_alignment = 1;
		cpu_features = CPU_FEATURE_RDTSC;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		codegen_timing_set(&codegen_timing_686);
		ccr4 = 0x80;
		break;

	case CPU_CxGX1:x86_setopcodes(ops_386, ops_pentium_0f, dynarec_ops_386, dynarec_ops_pentium_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 1; /*register dest - memory src*/
		timing_mr = 2; /*memory dest   - register src*/
		timing_mm = 2;
		timing_rml = 1; /*register dest - memory src long*/
		timing_mrl = 2; /*memory dest   - register src long*/
		timing_mml = 2;
		timing_bt = 5 - 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		cpu_CR4_mask = CR4_TSD | CR4_DE | CR4_PCE;
		codegen_timing_set(&codegen_timing_686);
		break;

	case CPU_Cx6x86MX:x86_setopcodes(ops_386, ops_c6x86mx_0f, dynarec_ops_386, dynarec_ops_c6x86mx_0f);
		x86_dynarec_opcodes_da_a16 = dynarec_ops_fpu_686_da_a16;
		x86_dynarec_opcodes_da_a32 = dynarec_ops_fpu_686_da_a32;
		x86_dynarec_opcodes_db_a16 = dynarec_ops_fpu_686_db_a16;
		x86_dynarec_opcodes_db_a32 = dynarec_ops_fpu_686_db_a32;
		x86_dynarec_opcodes_df_a16 = dynarec_ops_fpu_686_df_a16;
		x86_dynarec_opcodes_df_a32 = dynarec_ops_fpu_686_df_a32;
		x86_opcodes_da_a16 = ops_fpu_686_da_a16;
		x86_opcodes_da_a32 = ops_fpu_686_da_a32;
		x86_opcodes_db_a16 = ops_fpu_686_db_a16;
		x86_opcodes_db_a32 = ops_fpu_686_db_a32;
		x86_opcodes_df_a16 = ops_fpu_686_df_a16;
		x86_opcodes_df_a32 = ops_fpu_686_df_a32;
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 1; /*register dest - memory src*/
		timing_mr = 2; /*memory dest   - register src*/
		timing_mm = 2;
		timing_rml = 1; /*register dest - memory src long*/
		timing_mrl = 2; /*memory dest   - register src long*/
		timing_mml = 2;
		timing_bt = 0; /*branch taken*/
		timing_bnt = 2; /*branch not taken*/
		timing_int_rm = 9;
		timing_int_v86 = 46;
		timing_int_pm = 21;
		timing_int_pm_outer = 32;
		timing_iret_rm = 7;
		timing_iret_v86 = 26;
		timing_iret_pm = 10;
		timing_iret_pm_outer = 26;
		timing_call_rm = 3;
		timing_call_pm = 4;
		timing_call_pm_gate = 15;
		timing_call_pm_gate_inner = 26;
		timing_retf_rm = 4;
		timing_retf_pm = 4;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 1;
		timing_jmp_pm = 4;
		timing_jmp_pm_gate = 14;
		timing_misaligned = 2;
		cpu_cyrix_alignment = 1;
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_MMX;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		cpu_CR4_mask = CR4_TSD | CR4_DE | CR4_PCE;
		codegen_timing_set(&codegen_timing_686);
		ccr4 = 0x80;
		break;

	case CPU_K6:x86_setopcodes(ops_386, ops_pentiummmx_0f, dynarec_ops_386, dynarec_ops_pentiummmx_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 0; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		timing_int = 6;
		timing_int_rm = 11;
		timing_int_v86 = 54;
		timing_int_pm = 25;
		timing_int_pm_outer = 42;
		timing_iret_rm = 7;
		timing_iret_v86 = 27; /*unknown*/
		timing_iret_pm = 10;
		timing_iret_pm_outer = 27;
		timing_call_rm = 4;
		timing_call_pm = 4;
		timing_call_pm_gate = 22;
		timing_call_pm_gate_inner = 44;
		timing_retf_rm = 4;
		timing_retf_pm = 4;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 3;
		timing_jmp_pm = 3;
		timing_jmp_pm_gate = 18;
		timing_misaligned = 3;
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_VME | CPU_FEATURE_MMX;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		cpu_CR4_mask = CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | CR4_MCE;
		codegen_timing_set(&codegen_timing_k6);
		break;

	case CPU_K6_2:
	case CPU_K6_3:
	case CPU_K6_2P:
	case CPU_K6_3P:x86_setopcodes(ops_386, ops_winchip2_0f, dynarec_ops_386, dynarec_ops_winchip2_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 0; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		timing_int = 6;
		timing_int_rm = 11;
		timing_int_v86 = 54;
		timing_int_pm = 25;
		timing_int_pm_outer = 42;
		timing_iret_rm = 7;
		timing_iret_v86 = 27; /*unknown*/
		timing_iret_pm = 10;
		timing_iret_pm_outer = 27;
		timing_call_rm = 4;
		timing_call_pm = 4;
		timing_call_pm_gate = 22;
		timing_call_pm_gate_inner = 44;
		timing_retf_rm = 4;
		timing_retf_pm = 4;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 3;
		timing_jmp_pm = 3;
		timing_jmp_pm_gate = 18;
		timing_misaligned = 3;
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_VME | CPU_FEATURE_MMX | CPU_FEATURE_3DNOW;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		cpu_CR4_mask = CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | CR4_MCE;
		codegen_timing_set(&codegen_timing_k6);
		break;

	case CPU_PENTIUMPRO:x86_setopcodes(ops_386, ops_pentiumpro_0f, dynarec_ops_386, dynarec_ops_pentiumpro_0f);
		x86_dynarec_opcodes_da_a16 = dynarec_ops_fpu_686_da_a16;
		x86_dynarec_opcodes_da_a32 = dynarec_ops_fpu_686_da_a32;
		x86_dynarec_opcodes_db_a16 = dynarec_ops_fpu_686_db_a16;
		x86_dynarec_opcodes_db_a32 = dynarec_ops_fpu_686_db_a32;
		x86_dynarec_opcodes_df_a16 = dynarec_ops_fpu_686_df_a16;
		x86_dynarec_opcodes_df_a32 = dynarec_ops_fpu_686_df_a32;
		x86_opcodes_da_a16 = ops_fpu_686_da_a16;
		x86_opcodes_da_a32 = ops_fpu_686_da_a32;
		x86_opcodes_db_a16 = ops_fpu_686_db_a16;
		x86_opcodes_db_a32 = ops_fpu_686_db_a32;
		x86_opcodes_df_a16 = ops_fpu_686_df_a16;
		x86_opcodes_df_a32 = ops_fpu_686_df_a32;
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 0; /*branch taken*/
		timing_bnt = 2; /*branch not taken*/
		timing_int = 6;
		timing_int_rm = 11;
		timing_int_v86 = 54;
		timing_int_pm = 25;
		timing_int_pm_outer = 42;
		timing_iret_rm = 7;
		timing_iret_v86 = 27; /*unknown*/
		timing_iret_pm = 10;
		timing_iret_pm_outer = 27;
		timing_call_rm = 4;
		timing_call_pm = 4;
		timing_call_pm_gate = 22;
		timing_call_pm_gate_inner = 44;
		timing_retf_rm = 4;
		timing_retf_pm = 4;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 3;
		timing_jmp_pm = 3;
		timing_jmp_pm_gate = 18;
		timing_misaligned = 3;
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_VME | CPU_FEATURE_CX8 | CPU_FEATURE_SYSCALL;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		cpu_CR4_mask = CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | CR4_MCE | CR4_PCE;
		codegen_timing_set(&codegen_timing_p6);
		break;

	case CPU_PENTIUM_2:
	case CPU_CELERON:
	case CPU_CELERON_A:x86_setopcodes(ops_386, ops_pentium2_0f, dynarec_ops_386, dynarec_ops_pentium2_0f);
		x86_dynarec_opcodes_da_a16 = dynarec_ops_fpu_686_da_a16;
		x86_dynarec_opcodes_da_a32 = dynarec_ops_fpu_686_da_a32;
		x86_dynarec_opcodes_db_a16 = dynarec_ops_fpu_686_db_a16;
		x86_dynarec_opcodes_db_a32 = dynarec_ops_fpu_686_db_a32;
		x86_dynarec_opcodes_df_a16 = dynarec_ops_fpu_686_df_a16;
		x86_dynarec_opcodes_df_a32 = dynarec_ops_fpu_686_df_a32;
		x86_opcodes_da_a16 = ops_fpu_686_da_a16;
		x86_opcodes_da_a32 = ops_fpu_686_da_a32;
		x86_opcodes_db_a16 = ops_fpu_686_db_a16;
		x86_opcodes_db_a32 = ops_fpu_686_db_a32;
		x86_opcodes_df_a16 = ops_fpu_686_df_a16;
		x86_opcodes_df_a32 = ops_fpu_686_df_a32;
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 2; /*register dest - memory src*/
		timing_mr = 3; /*memory dest   - register src*/
		timing_mm = 3;
		timing_rml = 2; /*register dest - memory src long*/
		timing_mrl = 3; /*memory dest   - register src long*/
		timing_mml = 3;
		timing_bt = 0; /*branch taken*/
		timing_bnt = 2; /*branch not taken*/
		timing_int = 6;
		timing_int_rm = 11;
		timing_int_v86 = 54;
		timing_int_pm = 25;
		timing_int_pm_outer = 42;
		timing_iret_rm = 7;
		timing_iret_v86 = 27; /*unknown*/
		timing_iret_pm = 10;
		timing_iret_pm_outer = 27;
		timing_call_rm = 4;
		timing_call_pm = 4;
		timing_call_pm_gate = 22;
		timing_call_pm_gate_inner = 44;
		timing_retf_rm = 4;
		timing_retf_pm = 4;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 3;
		timing_jmp_pm = 3;
		timing_jmp_pm_gate = 18;
		timing_misaligned = 3;
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_VME | CPU_FEATURE_CX8 | CPU_FEATURE_MMX | CPU_FEATURE_SYSCALL;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21);
		cpu_CR4_mask = CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | CR4_MCE | CR4_PCE;
		codegen_timing_set(&codegen_timing_p6);
		break;

	case CPU_CYRIX_III:x86_setopcodes(ops_386, ops_winchip2_0f, dynarec_ops_386, dynarec_ops_winchip2_0f);
		timing_rr = 1; /*register dest - register src*/
		timing_rm = 1; /*register dest - memory src*/
		timing_mr = 1; /*memory dest   - register src*/
		timing_mm = 1;
		timing_rml = 1; /*register dest - memory src long*/
		timing_mrl = 1; /*memory dest   - register src long*/
		timing_mml = 1;
		timing_bt = 1; /*branch taken*/
		timing_bnt = 1; /*branch not taken*/
		cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MMX | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_3DNOW;
		msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21);
		cpu_CR4_mask = CR4_TSD | CR4_DE | CR4_MCE | CR4_PCE;
		/*unknown*/
		timing_int_rm = 26;
		timing_int_v86 = 82;
		timing_int_pm = 44;
		timing_int_pm_outer = 71;
		timing_iret_rm = 7;
		timing_iret_v86 = 26;
		timing_iret_pm = 10;
		timing_iret_pm_outer = 26;
		timing_call_rm = 4;
		timing_call_pm = 15;
		timing_call_pm_gate = 26;
		timing_call_pm_gate_inner = 35;
		timing_retf_rm = 4;
		timing_retf_pm = 7;
		timing_retf_pm_outer = 23;
		timing_jmp_rm = 5;
		timing_jmp_pm = 7;
		timing_jmp_pm_gate = 17;
		timing_misaligned = 2;
		cpu_cyrix_alignment = 1;
		codegen_timing_set(&codegen_timing_cyrixiii);
		break;

	default:fatal("cpu_set : unknown CPU type %i\n", cpu_s->cpu_type);
	}

	switch (fpu_type) {
	case FPU_NONE:break;

	case FPU_8087:x87_timings = x87_timings_8087;
		break;

	case FPU_287:x87_timings = x87_timings_287;
		break;

	case FPU_287XL:
	case FPU_387:x87_timings = x87_timings_387;
		break;

	default:x87_timings = x87_timings_486;
		break;
	}
}

void cpu_CPUID() {
	switch (models[model]->cpu[cpu_manufacturer].cpus[cpu].cpu_type) {
	case CPU_i486DX:
		if (!EAX) {
			EAX = 0x00000001;
			EBX = 0x756e6547;
			EDX = 0x49656e69;
			ECX = 0x6c65746e;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU; /*FPU*/
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_iDX4:
		if (!EAX) {
			EAX = 0x00000001;
			EBX = 0x756e6547;
			EDX = 0x49656e69;
			ECX = 0x6c65746e;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_VME;
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_Am486SX:
		if (!EAX) {
			EAX = 1;
			EBX = 0x68747541;
			ECX = 0x444D4163;
			EDX = 0x69746E65;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = EDX = 0; /*No FPU*/
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_Am486DX:
		if (!EAX) {
			EAX = 1;
			EBX = 0x68747541;
			ECX = 0x444D4163;
			EDX = 0x69746E65;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU; /*FPU*/
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_WINCHIP:
		if (!EAX) {
			EAX = 1;
			if (msr.fcr2 & (1 << 14)) {
				EBX = msr.fcr3 >> 32;
				ECX = msr.fcr3 & 0xffffffff;
				EDX = msr.fcr2 >> 32;
			} else {
				EBX = 0x746e6543; /*CentaurHauls*/
				ECX = 0x736c7561;
				EDX = 0x48727561;
			}
		} else if (EAX == 1) {
			EAX = 0x540;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_TSC | CPUID_MSR;
			if (cpu_has_feature(CPU_FEATURE_CX8))
				EDX |= CPUID_CMPXCHG8B;
			if (msr.fcr & (1 << 9))
				EDX |= CPUID_MMX;
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_WINCHIP2:
		switch (EAX) {
		case 0:EAX = 1;
			if (msr.fcr2 & (1 << 14)) {
				EBX = msr.fcr3 >> 32;
				ECX = msr.fcr3 & 0xffffffff;
				EDX = msr.fcr2 >> 32;
			} else {
				EBX = 0x746e6543; /*CentaurHauls*/
				ECX = 0x736c7561;
				EDX = 0x48727561;
			}
			break;
		case 1:EAX = 0x580;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_TSC | CPUID_MSR;
			if (cpu_has_feature(CPU_FEATURE_CX8))
				EDX |= CPUID_CMPXCHG8B;
			if (msr.fcr & (1 << 9))
				EDX |= CPUID_MMX;
			break;
		case 0x80000000:EAX = 0x80000005;
			break;
		case 0x80000001:EAX = 0x580;
			EDX = CPUID_FPU | CPUID_TSC | CPUID_MSR;
			if (cpu_has_feature(CPU_FEATURE_CX8))
				EDX |= CPUID_CMPXCHG8B;
			if (msr.fcr & (1 << 9))
				EDX |= CPUID_MMX;
			if (cpu_has_feature(CPU_FEATURE_3DNOW))
				EDX |= CPUID_3DNOW;
			break;

		case 0x80000002: /*Processor name string*/
			EAX = 0x20544449; /*IDT WinChip 2-3D*/
			EBX = 0x436e6957;
			ECX = 0x20706968;
			EDX = 0x44332d32;
			break;

		case 0x80000005: /*Cache information*/
			EBX = 0x08800880; /*TLBs*/
			ECX = 0x20040120; /*L1 data cache*/
			EDX = 0x20020120; /*L1 instruction cache*/
			break;

		default:EAX = EBX = ECX = EDX = 0;
			break;
		}
		break;

	case CPU_PENTIUM:
		if (!EAX) {
			EAX = 0x00000001;
			EBX = 0x756e6547;
			EDX = 0x49656e69;
			ECX = 0x6c65746e;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B;
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_PENTIUMMMX:
		if (!EAX) {
			EAX = 0x00000001;
			EBX = 0x756e6547;
			EDX = 0x49656e69;
			ECX = 0x6c65746e;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_MMX;
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_Cx6x86:
		if (!EAX) {
			EAX = 0x00000001;
			EBX = 0x69727943;
			EDX = 0x736e4978;
			ECX = 0x64616574;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU;
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_Cx6x86L:
		if (!EAX) {
			EAX = 0x00000001;
			EBX = 0x69727943;
			EDX = 0x736e4978;
			ECX = 0x64616574;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_CMPXCHG8B;
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_CxGX1:
		if (!EAX) {
			EAX = 0x00000001;
			EBX = 0x69727943;
			EDX = 0x736e4978;
			ECX = 0x64616574;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B;
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_Cx6x86MX:
		if (!EAX) {
			EAX = 0x00000001;
			EBX = 0x69727943;
			EDX = 0x736e4978;
			ECX = 0x64616574;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_CMOV | CPUID_MMX;
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_K6:
		switch (EAX) {
		case 0:EAX = 1;
			EBX = 0x68747541; /*AuthenticAMD*/
			ECX = 0x444d4163;
			EDX = 0x69746e65;
			break;
		case 1:EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_MMX;
			break;
		case 0x80000000:EAX = 0x80000005;
			break;
		case 0x80000001:EAX = CPUID + 0x100;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_MMX;
			break;

		case 0x80000002: /*Processor name string*/
			EAX = 0x2d444d41; /*AMD-K6tm w/ mult*/
			EBX = 0x6d74364b;
			ECX = 0x202f7720;
			EDX = 0x746c756d;
			break;

		case 0x80000003: /*Processor name string*/
			EAX = 0x64656d69; /*imedia extension*/
			EBX = 0x65206169;
			ECX = 0x6e657478;
			EDX = 0x6e6f6973;
			break;

		case 0x80000004: /*Processor name string*/
			EAX = 0x00000073; /*s*/
			EBX = 0x00000000;
			ECX = 0x00000000;
			EDX = 0x00000000;
			break;

		case 0x80000005: /*Cache information*/
			EBX = 0x02800140; /*TLBs*/
			ECX = 0x20020220; /*L1 data cache*/
			EDX = 0x20020220; /*L1 instruction cache*/
			break;

		default:EAX = EBX = ECX = EDX = 0;
			break;
		}
		break;

	case CPU_K6_2:
		switch (EAX) {
		case 0:EAX = 1;
			EBX = 0x68747541; /*AuthenticAMD*/
			ECX = 0x444d4163;
			EDX = 0x69746e65;
			break;
		case 1:EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_MMX;
			break;
		case 0x80000000:EAX = 0x80000005;
			break;
		case 0x80000001:EAX = CPUID + 0x100;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_MMX | CPUID_3DNOW;
			break;

		case 0x80000002: /*Processor name string*/
			EAX = 0x2d444d41; /*AMD-K6(tm) 3D pr*/
			EBX = 0x7428364b;
			ECX = 0x3320296d;
			EDX = 0x72702044;
			break;

		case 0x80000003: /*Processor name string*/
			EAX = 0x7365636f; /*ocessor*/
			EBX = 0x00726f73;
			ECX = 0x00000000;
			EDX = 0x00000000;
			break;

		case 0x80000005: /*Cache information*/
			EBX = 0x02800140; /*TLBs*/
			ECX = 0x20020220; /*L1 data cache*/
			EDX = 0x20020220; /*L1 instruction cache*/
			break;

		default:EAX = EBX = ECX = EDX = 0;
			break;
		}
		break;

	case CPU_K6_3:
		switch (EAX) {
		case 0:EAX = 1;
			EBX = 0x68747541; /*AuthenticAMD*/
			ECX = 0x444d4163;
			EDX = 0x69746e65;
			break;
		case 1:EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_MMX;
			break;
		case 0x80000000:EAX = 0x80000006;
			break;
		case 0x80000001:EAX = CPUID + 0x100;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_MMX | CPUID_3DNOW;
			break;

		case 0x80000002: /*Processor name string*/
			EAX = 0x2d444d41; /*AMD-K6(tm) 3D+ P*/
			EBX = 0x7428364b;
			ECX = 0x3320296d;
			EDX = 0x50202b44;
			break;

		case 0x80000003: /*Processor name string*/
			EAX = 0x65636f72; /*rocessor*/
			EBX = 0x726f7373;
			ECX = 0x00000000;
			EDX = 0x00000000;
			break;

		case 0x80000005: /*Cache information*/
			EBX = 0x02800140; /*TLBs*/
			ECX = 0x20020220; /*L1 data cache*/
			EDX = 0x20020220; /*L1 instruction cache*/
			break;

		case 0x80000006: /*L2 Cache information*/
			ECX = 0x01004220;
			break;

		default:EAX = EBX = ECX = EDX = 0;
			break;
		}
		break;

	case CPU_K6_2P:
	case CPU_K6_3P:
		switch (EAX) {
		case 0:EAX = 1;
			EBX = 0x68747541; /*AuthenticAMD*/
			ECX = 0x444d4163;
			EDX = 0x69746e65;
			break;
		case 1:EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_MMX;
			break;
		case 0x80000000:EAX = 0x80000007;
			break;
		case 0x80000001:EAX = CPUID + 0x100;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_MMX | CPUID_3DNOW;
			break;

		case 0x80000002: /*Processor name string*/
			EAX = 0x2d444d41; /*AMD-K6(tm)-III P*/
			EBX = 0x7428364b;
			ECX = 0x492d296d;
			EDX = 0x50204949;
			break;

		case 0x80000003: /*Processor name string*/
			EAX = 0x65636f72; /*rocessor*/
			EBX = 0x726f7373;
			ECX = 0x00000000;
			EDX = 0x00000000;
			break;

		case 0x80000005: /*Cache information*/
			EBX = 0x02800140; /*TLBs*/
			ECX = 0x20020220; /*L1 data cache*/
			EDX = 0x20020220; /*L1 instruction cache*/
			break;

		case 0x80000006: /*L2 Cache information*/
			if (models[model]->cpu[cpu_manufacturer].cpus[cpu].cpu_type == CPU_K6_3P)
				ECX = 0x01004220;
			else
				ECX = 0x00804220;
			break;

		case 0x80000007: /*PowerNow information*/
			EDX = 7;
			break;

		default:EAX = EBX = ECX = EDX = 0;
			break;
		}
		break;

	case CPU_PENTIUMPRO:
		if (!EAX) {
			EAX = 0x00000002;
			EBX = 0x756e6547;
			EDX = 0x49656e69;
			ECX = 0x6c65746e;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_CMOV | CPUID_SEP;
		} else if (EAX == 2) {
			EAX = 0x03020101;
			EBX = 0;
			ECX = 0;
			EDX = 0x06040a42;
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_PENTIUM_2:
	case CPU_CELERON:
	case CPU_CELERON_A:
		if (!EAX) {
			EAX = 0x00000002;
			EBX = 0x756e6547;
			EDX = 0x49656e69;
			ECX = 0x6c65746e;
		} else if (EAX == 1) {
			EAX = CPUID;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_CMPXCHG8B | CPUID_CMOV | CPUID_MMX | CPUID_SEP;
		} else if (EAX == 2) {
			EAX = 0x03020101;
			EBX = 0;
			ECX = 0;
			if (models[model]->cpu[cpu_manufacturer].cpus[cpu].cpu_type == CPU_PENTIUM_2)
				EDX = 0x08040c43; /*512kb L2 cache*/
			else if (models[model]->cpu[cpu_manufacturer].cpus[cpu].cpu_type == CPU_CELERON)
				EDX = 0x08040c40; /*No L2 cache*/
			else
				EDX = 0x08040c41; /*128kb L2 cache*/
		} else
			EAX = EBX = ECX = EDX = 0;
		break;

	case CPU_CYRIX_III:
		switch (EAX) {
		case 0:EAX = 1;
			EBX = 0x746e6543; /*CentaurHauls*/
			ECX = 0x736c7561;
			EDX = 0x48727561;
			break;
		case 1:EAX = models[model]->cpu[cpu_manufacturer].cpus[cpu].cpuid_model;
			EBX = ECX = 0;
			EDX = CPUID_FPU | CPUID_TSC | CPUID_MSR;
			if (cpu_has_feature(CPU_FEATURE_CX8))
				EDX |= CPUID_CMPXCHG8B;
			if (msr.fcr & (1 << 9))
				EDX |= CPUID_MMX;
			break;
		case 0x80000000:EAX = 0x80000006;
			break;
		case 0x80000001:EAX = models[model]->cpu[cpu_manufacturer].cpus[cpu].edx_reset;
			EDX = CPUID_FPU | CPUID_TSC | CPUID_MSR;
			if (cpu_has_feature(CPU_FEATURE_CX8))
				EDX |= CPUID_CMPXCHG8B;
			if (msr.fcr & (1 << 9))
				EDX |= CPUID_MMX;
			if (cpu_has_feature(CPU_FEATURE_3DNOW))
				EDX |= CPUID_3DNOW;
			break;

		case 0x80000002: /*Processor name string*/
			if (models[model]->cpu[cpu_manufacturer].cpus[cpu].cpuid_model >= 0x670) {
				EAX = 0x20414956; /*VIA Samuel 2*/
				EBX = 0x756d6153;
				ECX = 0x32206c65;
				EDX = 0x00000000;
			} else {
				EAX = 0x20414956; /*VIA SamuelM*/
				EBX = 0x756d6153;
				ECX = 0x004d6c65;
				EDX = 0x00000000;
			}
			break;

		case 0x80000005: /*Cache information*/
			EBX = 0x08800880; /*TLBs*/
			ECX = 0x40040120; /*L1 data cache*/
			EDX = 0x40020120; /*L1 instruction cache*/
			break;

		case 0x80000006: /*L2 Cache information*/
			if (models[model]->cpu[cpu_manufacturer].cpus[cpu].cpuid_model >= 0x670)
				ECX = 0x40040120; /*L2 cache*/
			else
				ECX = 0;
			EAX = EBX = EDX = 0;
			break;

		default:EAX = EBX = ECX = EDX = 0;
			break;
		}
		break;
	}
}

void cpu_RDMSR() {
	switch (models[model]->cpu[cpu_manufacturer].cpus[cpu].cpu_type) {
	case CPU_WINCHIP:
	case CPU_WINCHIP2:
	case CPU_CYRIX_III:EAX = EDX = 0;
		switch (ECX) {
		case 0x02:EAX = msr.tr1;
			break;
		case 0x0e:EAX = msr.tr12;
			break;
		case 0x10:EAX = tsc & 0xffffffff;
			EDX = tsc >> 32;
			break;
		case 0x11:EAX = msr.cesr;
			break;
		case 0x107:EAX = msr.fcr;
			break;
		case 0x108:EAX = msr.fcr2 & 0xffffffff;
			EDX = msr.fcr2 >> 32;
			break;
		case 0x10a:EAX = cpu_multi & 3;
			break;
		}
		break;

	case CPU_PENTIUM:
	case CPU_PENTIUMMMX:EAX = EDX = 0;
		switch (ECX) {
		case 0x10:EAX = tsc & 0xffffffff;
			EDX = tsc >> 32;
			break;
		}
		break;
	case CPU_Cx6x86:
	case CPU_Cx6x86L:
	case CPU_CxGX1:
	case CPU_Cx6x86MX:
		switch (ECX) {
		case 0x10:EAX = tsc & 0xffffffff;
			EDX = tsc >> 32;
			break;
		}
		break;
	case CPU_K6:
	case CPU_K6_2:
	case CPU_K6_3:
	case CPU_K6_2P:
	case CPU_K6_3P:EAX = EDX = 0;
		switch (ECX) {
		case 0x10:EAX = tsc & 0xffffffff;
			EDX = tsc >> 32;
			break;
		}
		break;
	case CPU_PENTIUMPRO:
	case CPU_PENTIUM_2:
	case CPU_CELERON:
	case CPU_CELERON_A:EAX = EDX = 0;
		switch (ECX) {
		case 0x10:EAX = tsc & 0xffffffff;
			EDX = tsc >> 32;
			break;

		case 0x174:EAX = sysenter_cs;
			EDX = 0;
			break;
		case 0x175:EAX = sysenter_esp;
			EDX = 0;
			break;
		case 0x176:EAX = sysenter_eip;
			EDX = 0;
			break;
		}
		break;
	}
}

void cpu_WRMSR() {
	switch (models[model]->cpu[cpu_manufacturer].cpus[cpu].cpu_type) {
	case CPU_WINCHIP:
	case CPU_WINCHIP2:
	case CPU_CYRIX_III:
		switch (ECX) {
		case 0x02:msr.tr1 = EAX & 2;
			break;
		case 0x0e:msr.tr12 = EAX & 0x228;
			break;
		case 0x10:tsc = EAX | ((uint64_t)EDX << 32);
			break;
		case 0x11:msr.cesr = EAX & 0xff00ff;
			break;
		case 0x107:msr.fcr = EAX;
			if (EAX & (1 << 9))
				cpu_features |= CPU_FEATURE_MMX;
			else
				cpu_features &= ~CPU_FEATURE_MMX;
			if (EAX & (1 << 1))
				cpu_features |= CPU_FEATURE_CX8;
			else
				cpu_features &= ~CPU_FEATURE_CX8;
			if ((EAX & (1 << 20)) && models[model]->cpu[cpu_manufacturer].cpus[cpu].cpu_type >= CPU_WINCHIP2)
				cpu_features |= CPU_FEATURE_3DNOW;
			else
				cpu_features &= ~CPU_FEATURE_3DNOW;
			if (EAX & (1 << 29))
				CPUID = 0;
			else
				CPUID = models[model]->cpu[cpu_manufacturer].cpus[cpu].cpuid_model;
			break;
		case 0x108:msr.fcr2 = EAX | ((uint64_t)EDX << 32);
			break;
		case 0x109:msr.fcr3 = EAX | ((uint64_t)EDX << 32);
			break;
		}
		break;

	case CPU_PENTIUM:
	case CPU_PENTIUMMMX:
		switch (ECX) {
		case 0x10:tsc = EAX | ((uint64_t)EDX << 32);
			break;
		}
		break;
	case CPU_Cx6x86:
	case CPU_Cx6x86L:
	case CPU_CxGX1:
	case CPU_Cx6x86MX:
		switch (ECX) {
		case 0x10:tsc = EAX | ((uint64_t)EDX << 32);
			break;
		}
		break;
	case CPU_K6:
	case CPU_K6_2:
	case CPU_K6_3:
	case CPU_K6_2P:
	case CPU_K6_3P:
		switch (ECX) {
		case 0x10:tsc = EAX | ((uint64_t)EDX << 32);
			break;
		}
		break;
	case CPU_PENTIUMPRO:
	case CPU_PENTIUM_2:
	case CPU_CELERON:
	case CPU_CELERON_A:
		switch (ECX) {
		case 0x10:tsc = EAX | ((uint64_t)EDX << 32);
			break;

		case 0x174:sysenter_cs = EAX & 0xffff;
			break;
		case 0x175:sysenter_esp = EAX;
			break;
		case 0x176:sysenter_eip = EAX;
			break;
		}
		break;
	}
}

static int cyrix_addr;

#define CCR1_USE_SMI  (1 << 1)
#define CCR1_SMAC     (1 << 2)
#define CCR1_SM3      (1 << 7)

#define CCR3_SMI_LOCK (1 << 0)
#define CCR3_NMI_EN   (1 << 1)

void cyrix_write(uint16_t addr, uint8_t val, void *priv) {
	if (!(addr & 1))
		cyrix_addr = val;
	else
		switch (cyrix_addr) {
		case 0xc0: /*CCR0*/
			ccr0 = val;
			break;
		case 0xc1: /*CCR1*/
			if ((ccr3 & CCR3_SMI_LOCK) && !(cpu_cur_status & CPU_STATUS_SMM))
				val = (val & ~(CCR1_USE_SMI | CCR1_SMAC | CCR1_SM3)) | (ccr1 & (CCR1_USE_SMI | CCR1_SMAC | CCR1_SM3));
			ccr1 = val;
			break;
		case 0xc2: /*CCR2*/
			ccr2 = val;
			break;
		case 0xc3: /*CCR3*/
			if ((ccr3 & CCR3_SMI_LOCK) && !(cpu_cur_status & CPU_STATUS_SMM))
				val = (val & ~(CCR3_NMI_EN)) | (ccr3 & CCR3_NMI_EN) | CCR3_SMI_LOCK;
			ccr3 = val;
			break;
		case 0xcd:
			if (!(ccr3 & CCR3_SMI_LOCK) || (cpu_cur_status & CPU_STATUS_SMM)) {
				cyrix.arr[3].base = (cyrix.arr[3].base & ~0xff000000) | (val << 24);
				cyrix.smhr &= ~SMHR_VALID;
			}
			break;
		case 0xce:
			if (!(ccr3 & CCR3_SMI_LOCK) || (cpu_cur_status & CPU_STATUS_SMM)) {
				cyrix.arr[3].base = (cyrix.arr[3].base & ~0x00ff0000) | (val << 16);
				cyrix.smhr &= ~SMHR_VALID;
			}
			break;
		case 0xcf:
			if (!(ccr3 & CCR3_SMI_LOCK) || (cpu_cur_status & CPU_STATUS_SMM)) {
				cyrix.arr[3].base = (cyrix.arr[3].base & ~0x0000f000) | ((val & 0xf0) << 8);
				if ((val & 0xf) == 0xf)
					cyrix.arr[3].size = 1ull << 32; /*4 GB*/
				else if (val & 0xf)
					cyrix.arr[3].size = 2048 << (val & 0xf);
				else
					cyrix.arr[3].size = 0; /*Disabled*/
				cyrix.smhr &= ~SMHR_VALID;
			}
			break;

		case 0xe8: /*CCR4*/
			if ((ccr3 & 0xf0) == 0x10) {
				ccr4 = val;
				if (models[model]->cpu[cpu_manufacturer].cpus[cpu].cpu_type >= CPU_Cx6x86) {
					if (val & 0x80)
						CPUID = models[model]->cpu[cpu_manufacturer].cpus[cpu].cpuid_model;
					else
						CPUID = 0;
				}
			}
			break;
		case 0xe9: /*CCR5*/
			if ((ccr3 & 0xf0) == 0x10)
				ccr5 = val;
			break;
		case 0xea: /*CCR6*/
			if ((ccr3 & 0xf0) == 0x10)
				ccr6 = val;
			break;
		}
}

uint8_t cyrix_read(uint16_t addr, void *priv) {
	if (addr & 1) {
		switch (cyrix_addr) {
		case 0xc0:return ccr0;
		case 0xc1:return ccr1;
		case 0xc2:return ccr2;
		case 0xc3:return ccr3;
		case 0xe8:return ((ccr3 & 0xf0) == 0x10) ? ccr4 : 0xff;
		case 0xe9:return ((ccr3 & 0xf0) == 0x10) ? ccr5 : 0xff;
		case 0xea:return ((ccr3 & 0xf0) == 0x10) ? ccr6 : 0xff;
		case 0xfe:return models[model]->cpu[cpu_manufacturer].cpus[cpu].cyrix_id & 0xff;
		case 0xff:return models[model]->cpu[cpu_manufacturer].cpus[cpu].cyrix_id >> 8;
		}
		if (cyrix_addr == 0x20 && models[model]->cpu[cpu_manufacturer].cpus[cpu].cpu_type == CPU_Cx5x86)
			return 0xff;
	}
	return 0xff;
}

void x86_setopcodes(OpFn *opcodes, OpFn *opcodes_0f, OpFn *dynarec_opcodes, OpFn *dynarec_opcodes_0f) {
	x86_opcodes = opcodes;
	x86_opcodes_0f = opcodes_0f;
	x86_dynarec_opcodes = dynarec_opcodes;
	x86_dynarec_opcodes_0f = dynarec_opcodes_0f;
}

void cpu_update_waitstates() {
	cpu_s = &models[model]->cpu[cpu_manufacturer].cpus[cpu];

	if (is486)
		cpu_prefetch_width = 16;
	else
		cpu_prefetch_width = cpu_16bitbus ? 2 : 4;

	if (cpu_cache_int_enabled) {
		/* Disable prefetch emulation */
		cpu_prefetch_cycles = 0;
	} else if (cpu_waitstates && (cpu_s->cpu_type >= CPU_286 && cpu_s->cpu_type <= CPU_386DX)) {
		/* Waitstates override */
		cpu_prefetch_cycles = cpu_waitstates + 1;
		cpu_cycles_read = cpu_waitstates + 1;
		cpu_cycles_read_l = (cpu_16bitbus ? 2 : 1) * (cpu_waitstates + 1);
		cpu_cycles_write = cpu_waitstates + 1;
		cpu_cycles_write_l = (cpu_16bitbus ? 2 : 1) * (cpu_waitstates + 1);
	} else if (cpu_cache_ext_enabled) {
		/* Use cache timings */
		cpu_prefetch_cycles = cpu_s->cache_read_cycles;
		cpu_cycles_read = cpu_s->cache_read_cycles;
		cpu_cycles_read_l = (cpu_16bitbus ? 2 : 1) * cpu_s->cache_read_cycles;
		cpu_cycles_write = cpu_s->cache_write_cycles;
		cpu_cycles_write_l = (cpu_16bitbus ? 2 : 1) * cpu_s->cache_write_cycles;
	} else {
		/* Use memory timings */
		cpu_prefetch_cycles = cpu_s->mem_read_cycles;
		cpu_cycles_read = cpu_s->mem_read_cycles;
		cpu_cycles_read_l = (cpu_16bitbus ? 2 : 1) * cpu_s->mem_read_cycles;
		cpu_cycles_write = cpu_s->mem_write_cycles;
		cpu_cycles_write_l = (cpu_16bitbus ? 2 : 1) * cpu_s->mem_write_cycles;
	}
	if (is486)
		cpu_prefetch_cycles = (cpu_prefetch_cycles * 11) / 16;
	cpu_mem_prefetch_cycles = cpu_prefetch_cycles;
	if (cpu_s->rspeed <= 8000000)
		cpu_rom_prefetch_cycles = cpu_mem_prefetch_cycles;
}

void cpu_set_turbo(int turbo) {
	if (cpu_turbo != turbo) {
		cpu_turbo = turbo;

		cpu_s = &models[model]->cpu[cpu_manufacturer].cpus[cpu];
		if (cpu_s->cpu_type >= CPU_286) {
			if (cpu_turbo)
				setpitclock(cpu_turbo_speed);
			else
				setpitclock(cpu_nonturbo_speed);
		} else
			setpitclock(14318184.0);
	}
}

int cpu_get_turbo() {
	return cpu_turbo;
}

int cpu_get_speed() {
	if (cpu_turbo)
		return cpu_turbo_speed;
	return cpu_nonturbo_speed;
}

void cpu_set_nonturbo_divider(int divider) {
	if (divider < 2)
		cpu_set_turbo(1);
	else {
		cpu_nonturbo_speed = cpu_turbo_speed / divider;
		cpu_set_turbo(0);
	}
}
