
#include "uae.h"
#include "ibm.h"
#include "pit.h"
#include "pic.h"
#include "cpu.h"
#include "model.h"
#include "x86.h"
#include "x86_ops.h"
#include "codegen.h"
#include "timer.h"
#include "sound.h"
#include "rom.h"
#include "thread.h"

#ifdef _WIN32
#include <windows.h>
#include "midi.h"
#endif

#include "pcemglue.h"

#include "sysconfig.h"
#include "sysdeps.h"
#include "threaddep/thread.h"
#include "machdep/maccess.h"
#include "gfxboard.h"
#include "uae/time.h"

#include "video.h"
#include "vid_svga.h"

CPU_STATE cpu_state;

PIT pit, pit2;
PIC pic, pic2;
dma_t dma[8];
int AT;

int ppispeakon;
int gated, speakval, speakon;

PPI ppi;

int codegen_flags_changed;
uint32_t codegen_endpc;
int codegen_in_recompile;
int codegen_flat_ds, codegen_flat_ss;
uint32_t recomp_page;
uint16_t *codeblock_hash;
codeblock_t *codeblock;

void codegen_reset(void)
{
}
void codegen_block_init(unsigned int)
{
}
void codegen_block_remove()
{
}
void codegen_block_start_recompile(struct codeblock_t *)
{
}
void codegen_block_end_recompile(struct codeblock_t *)
{
}
void codegen_block_end()
{
}
void codegen_generate_call(uint8_t opcode, OpFn op, uint32_t fetchdat, uint32_t new_pc, uint32_t old_pc)
{
}
void codegen_check_flush(struct page_t *page, uint64_t mask, uint32_t phys_addr)
{
}
void codegen_flush(void)
{
}
void codegen_init(void)
{
}

void pclog(char const *format, ...)
{
	va_list parms;
	va_start(parms, format);
	char buf[256];
	vsprintf(buf, format, parms);
	write_log("%s", buf);
	va_end(parms);
}

extern void activate_debugger(void);
void fatal(char const *format, ...)
{
	va_list parms;
	va_start(parms, format);
	char buf[256];
	vsprintf(buf, format, parms);
	write_log("PCEMFATAL: %s", buf);
	va_end(parms);
	activate_debugger();
}

void video_updatetiming(void)
{
	write_log(_T("video_updatetiming\n"));
}

void device_speed_changed(void)
{
	write_log(_T("device_speed_changed\n"));
}

int model;
int cpuspeed;
int insc;
int hasfpu;
int romset;
int nmi_mask;
uint32_t rammask;
uintptr_t *readlookup2;
uintptr_t *writelookup2;
uint16_t flags, eflags;
int writelookup[256], writelookupp[256];
int readlookup[256], readlookupp[256];
int readlnext;
int writelnext;
uint32_t olddslimit, oldsslimit, olddslimitw, oldsslimitw;
int pci_burst_time, pci_nonburst_time;
uint32_t oxpc;
char logs_path[512];
uint32_t ealimit, ealimitw;
int MCA;
struct svga_t *mb_vga;

uint32_t x87_pc_off, x87_op_off;
uint16_t x87_pc_seg, x87_op_seg;

int xi8088_bios_128kb(void)
{
	return 0;
}

uint8_t portin(uint16_t portnum);
uint8_t inb(uint16_t port)
{
	return portin(port);
}

void portout(uint16_t, uint8_t);
void outb(uint16_t port, uint8_t v)
{
	portout(port, v);
}
uint16_t portin16(uint16_t portnum);
uint16_t inw(uint16_t port)
{
	return portin16(port);
}
void portout16(uint16_t portnum, uint16_t value);
void outw(uint16_t port, uint16_t val)
{
	portout16(port, val);
}
uint32_t portin32(uint16_t portnum);
uint32_t inl(uint16_t port)
{
	return portin32(port);
}
void portout32(uint16_t portnum, uint32_t value);
void outl(uint16_t port, uint32_t val)
{
	portout32(port, val);
}


static void model_init(void)
{

}

int AMSTRAD;
int TANDY;
int keybsenddelay;
int mouse_buttons;
int mouse_type;
uint8_t pcem_key[272];

int mouse_get_type(int mouse_type)
{
	return 0;
}

void t3100e_notify_set(unsigned char v)
{

}
void t3100e_display_set(unsigned char v)
{

}
void t3100e_turbo_set(unsigned char v)
{

}
void t3100e_mono_set(unsigned char v)
{

}
uint8_t t3100e_display_get()
{
	return 0;
}
uint8_t t3100e_config_get()
{
	return 0;
}
uint8_t t3100e_mono_get()
{
	return 0;
}
void xi8088_turbo_set(unsigned char v)
{

}
uint8_t xi8088_turbo_get()
{
	return 0;
}

void ps2_cache_clean(void)
{

}

int video_is_mda()
{
	return 0;
}

static FPU fpus_none[] =
{
	{ "None", "none", FPU_NONE },
	{ NULL, NULL, 0 }
};
static FPU fpus_8088[] =
{
	{ "None", "none", FPU_NONE },
	{ "8087", "8087", FPU_8087 },
	{ NULL, NULL, 0 }
};
static FPU fpus_80286[] =
{
	{ "None", "none", FPU_NONE },
	{ "287", "287", FPU_287 },
	{ "287XL", "287xl", FPU_287XL },
	{ NULL, NULL, 0 }
};
static FPU fpus_80386[] =
{
	{ "None", "none", FPU_NONE },
	{ "387", "387", FPU_387 },
	{ NULL, NULL, 0 }
};
static FPU fpus_builtin[] =
{
	{ "Built-in", "builtin", FPU_BUILTIN },
	{ NULL, NULL, 0 }
};

static CPU cpus_8088[] =
{
	/*8088 standard*/
	{ "8088/4.77", CPU_8088, fpus_8088, 0, 4772728, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
	{ "8088/7.16", CPU_8088, fpus_8088, 1, 14318184 / 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
	{ "8088/8", CPU_8088, fpus_8088, 1, 8000000, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
	{ "8088/10", CPU_8088, fpus_8088, 2, 10000000, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
	{ "8088/12", CPU_8088, fpus_8088, 3, 12000000, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
	{ "8088/16", CPU_8088, fpus_8088, 4, 16000000, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
	{ "", -1, 0, 0, 0, 0 }
};

static CPU cpus_286[] =
{
	/*286*/
	{ "286/6", CPU_286, fpus_80286, 0, 6000000, 1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1 },
	{ "286/8", CPU_286, fpus_80286, 1, 8000000, 1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1 },
	{ "286/10", CPU_286, fpus_80286, 2, 10000000, 1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1 },
	{ "286/12", CPU_286, fpus_80286, 3, 12000000, 1, 0, 0, 0, 0, 0, 3, 3, 3, 3, 2 },
	{ "286/16", CPU_286, fpus_80286, 4, 16000000, 1, 0, 0, 0, 0, 0, 3, 3, 3, 3, 2 },
	{ "286/20", CPU_286, fpus_80286, 5, 20000000, 1, 0, 0, 0, 0, 0, 4, 4, 4, 4, 3 },
	{ "286/25", CPU_286, fpus_80286, 6, 25000000, 1, 0, 0, 0, 0, 0, 4, 4, 4, 4, 3 },
	{ "", -1, 0, 0, 0, 0 }
};

static CPU cpus_i386SX[] =
{
	/*i386SX*/
	{ "i386SX/16", CPU_386SX, fpus_80386, 0, 16000000, 1, 0, 0x2308, 0, 0, 0, 3, 3, 3, 3, 2 },
	{ "i386SX/20", CPU_386SX, fpus_80386, 1, 20000000, 1, 0, 0x2308, 0, 0, 0, 4, 4, 3, 3, 3 },
	{ "i386SX/25", CPU_386SX, fpus_80386, 2, 25000000, 1, 0, 0x2308, 0, 0, 0, 4, 4, 3, 3, 3 },
	{ "i386SX/33", CPU_386SX, fpus_80386, 3, 33333333, 1, 0, 0x2308, 0, 0, 0, 6, 6, 3, 3, 4 },
	{ "", -1, 0, 0, 0 }
};

static CPU cpus_i386DX[] =
{
	/*i386DX*/
	{ "i386DX/16", CPU_386DX, fpus_80386, 0, 16000000, 1, 0, 0x0308, 0, 0, 0, 3, 3, 3, 3, 2 },
	{ "i386DX/20", CPU_386DX, fpus_80386, 1, 20000000, 1, 0, 0x0308, 0, 0, 0, 4, 4, 3, 3, 3 },
	{ "i386DX/25", CPU_386DX, fpus_80386, 2, 25000000, 1, 0, 0x0308, 0, 0, 0, 4, 4, 3, 3, 3 },
	{ "i386DX/33", CPU_386DX, fpus_80386, 3, 33333333, 1, 0, 0x0308, 0, 0, 0, 6, 6, 3, 3, 4 },
	{ "", -1, 0, 0, 0 }
};

static CPU cpus_i486[] =
{
	/*i486*/
	{ "i486SX/16", CPU_i486SX, fpus_none, 0, 16000000, 1, 16000000, 0x42a, 0, 0, CPU_SUPPORTS_DYNAREC, 3, 3, 3, 3, 2 },
	{ "i486SX/20", CPU_i486SX, fpus_none, 1, 20000000, 1, 20000000, 0x42a, 0, 0, CPU_SUPPORTS_DYNAREC, 4, 4, 3, 3, 3 },
	{ "i486SX/25", CPU_i486SX, fpus_none, 2, 25000000, 1, 25000000, 0x42a, 0, 0, CPU_SUPPORTS_DYNAREC, 4, 4, 3, 3, 3 },
	{ "i486SX/33", CPU_i486SX, fpus_none, 3, 33333333, 1, 33333333, 0x42a, 0, 0, CPU_SUPPORTS_DYNAREC, 6, 6, 3, 3, 4 },
	{ "i486SX2/50", CPU_i486SX, fpus_none, 5, 50000000, 2, 25000000, 0x45b, 0, 0, CPU_SUPPORTS_DYNAREC, 8, 8, 6, 6, 2 * 3 },
	{ "i486DX/25", CPU_i486DX, fpus_builtin, 2, 25000000, 1, 25000000, 0x404, 0, 0, CPU_SUPPORTS_DYNAREC, 4, 4, 3, 3, 3 },
	{ "i486DX/33", CPU_i486DX, fpus_builtin, 3, 33333333, 1, 33333333, 0x404, 0, 0, CPU_SUPPORTS_DYNAREC, 6, 6, 3, 3, 4 },
	{ "i486DX/50", CPU_i486DX, fpus_builtin, 5, 50000000, 1, 25000000, 0x404, 0, 0, CPU_SUPPORTS_DYNAREC, 8, 8, 4, 4, 6 },
	{ "i486DX2/40", CPU_i486DX, fpus_builtin, 4, 40000000, 2, 20000000, 0x430, 0, 0, CPU_SUPPORTS_DYNAREC, 8, 8, 6, 6, 2 * 3 },
	{ "i486DX2/50", CPU_i486DX, fpus_builtin, 5, 50000000, 2, 25000000, 0x430, 0, 0, CPU_SUPPORTS_DYNAREC, 8, 8, 6, 6, 2 * 3 },
	{ "i486DX2/66", CPU_i486DX, fpus_builtin, 6, 66666666, 2, 33333333, 0x430, 0, 0, CPU_SUPPORTS_DYNAREC, 12, 12, 6, 6, 2 * 4 },
	{ "", -1, 0, 0, 0 }
};


MODEL models[] =
{
	{ "[8088] Generic XT clone", ROM_GENXT, "genxt", { { "", cpus_8088 }, { "", NULL }, { "", NULL } }, MODEL_GFX_NONE, 32, 704, 16, model_init, NULL },
	{ "[286] AMI 286 clone", ROM_AMI286, "ami286", { { "", cpus_286 }, { "", NULL }, { "", NULL } }, MODEL_GFX_NONE | MODEL_AT | MODEL_HAS_IDE, 512, 16384, 128, model_init, NULL },
	{ "[386SX] AMI 386SX clone", ROM_AMI386SX, "ami386", { { "386SX", cpus_i386SX }, { "386DX", cpus_i386DX }, { "486", cpus_i486 } }, MODEL_GFX_NONE | MODEL_AT | MODEL_HAS_IDE, 512, 16384, 128, model_init, NULL },
};

int rom_present(const char *s)
{
	return 0;
}
static int midi_open;

void midi_write(uint8_t v)
{
	if (!midi_open) {
		midi_open = Midi_Open();
	}
	Midi_Parse(midi_output, &v);
}

void pcem_close(void)
{
	if (midi_open)
		Midi_Close();
	midi_open = 0;
}

#ifdef CPU_i386
void codegen_set_rounding_mode(int mode)
{
	/*SSE*/
	cpu_state.new_fp_control = (cpu_state.old_fp_control & ~0x6000) | (mode << 13);
	/*x87 - used for double -> i64 conversions*/
	cpu_state.new_fp_control2 = (cpu_state.old_fp_control2 & ~0x0c00) | (mode << 10);
}
#else
void codegen_set_rounding_mode(int mode)
{
	/*SSE*/
	cpu_state.new_fp_control = (cpu_state.old_fp_control & ~0x6000) | (mode << 13);
}
#endif

// VIDEO STUFF

int video_timing_read_b;
int video_timing_read_w;
int video_timing_read_l;

int video_timing_write_b;
int video_timing_write_w;
int video_timing_write_l;

int cycles_lost;

uint8_t fontdat[2048][8];
uint8_t fontdatm[2048][16];
uint8_t fontdatw[512][32];	/* Wyse700 font */
uint8_t fontdat8x12[256][16];	/* MDSI Genius font */
uint8_t fontdat12x18[256][36];	/* IM1024 font */
uint8_t fontdatksc5601[16384][32]; /* Korean KSC-5601 font */
uint8_t fontdatksc5601_user[192][32]; /* Korean KSC-5601 user defined font */

int PCI;
int readflash;
int egareads;
int egawrites;
int changeframecount;
PCBITMAP *buffer32;
static int buffer32size;
static uae_u8 *tempbuf;
int vid_resize;
int xsize, ysize;
uint64_t timer_freq;

uint8_t edatlookup[4][4];
uint8_t rotatevga[8][256];
uint32_t *video_15to32, *video_16to32;

static void *gfxboard_priv;

static mem_mapping_t *pcem_mapping_linear;
void *pcem_mapping_linear_priv;
uae_u32 pcem_mapping_linear_offset;
uint8_t(*pcem_linear_read_b)(uint32_t addr, void *priv);
uint16_t(*pcem_linear_read_w)(uint32_t addr, void *priv);
uint32_t(*pcem_linear_read_l)(uint32_t addr, void *priv);
void (*pcem_linear_write_b)(uint32_t addr, uint8_t  val, void *priv);
void (*pcem_linear_write_w)(uint32_t addr, uint16_t val, void *priv);
void (*pcem_linear_write_l)(uint32_t addr, uint32_t val, void *priv);

#define MAX_PCEMMAPPINGS 10
static mem_mapping_t *pcemmappings[MAX_PCEMMAPPINGS];
#define PCEMMAPBLOCKSIZE 0x10000
#define MAX_PCEMMAPBLOCKS (0x80000000 / PCEMMAPBLOCKSIZE)
mem_mapping_t **pcemmap;

static uint8_t dummy_bread(uint32_t addr, void *p)
{
	return 0;
}
static uint16_t dummy_wread(uint32_t addr, void *p)
{
	return 0;
}
static uint32_t dummy_lread(uint32_t addr, void *p)
{
	return 0;
}
static void dummy_bwrite(uint32_t addr, uint8_t v, void *p)
{

}
static void dummy_wwrite(uint32_t addr, uint16_t v, void *p)
{

}
static void dummy_lwrite(uint32_t addr, uint32_t v, void *p)
{

}

uint64_t timer_read(void)
{
	return read_processor_time();
}

static pc_timer_t *timer_head = NULL;

void timer_enablex(pc_timer_t *timer)
{
	pc_timer_t *timer_node = timer_head;
	if (timer->enabled)
		timer_disablex(timer);
	timer->enabled = 1;
	if (!timer_head)
	{
		timer_head = timer;
		timer->next = timer->prev = NULL;
		return;
	}
	timer_node = timer_head;
}
void timer_disablex(pc_timer_t *timer)
{
	if (!timer->enabled)
		return;
	timer->enabled = 0;
	if (timer->prev)
		timer->prev->next = timer->next;
	else
		timer_head = timer->next;
	if (timer->next)
		timer->next->prev = timer->prev;
	timer->prev = timer->next = NULL;
}
void timer_addx(pc_timer_t *timer, void (*callback)(void* p), void *p, int start_timer)
{
	memset(timer, 0, sizeof(pc_timer_t));

	timer->callback = callback;
	timer->p = p;
	timer->enabled = 0;
	timer->prev = timer->next = NULL;
}
void timer_set_delay_u64x(pc_timer_t *timer, uint64_t delay)
{
	timer_enablex(timer);
}
static void timer_remove_headx(void)
{
	if (timer_head)
	{
		pc_timer_t *timer = timer_head;
		timer_head = timer->next;
		if (timer_head) {
			timer_head->prev = NULL;
		}
		timer->next = timer->prev = NULL;
		timer->enabled = 0;
	}
}

void pcemglue_hsync(void)
{
	while (timer_head) {
		timer_head->callback(timer_head->p);
		timer_remove_headx();
	}
}

void initpcemvideo(void *p, bool swapped)
{
	int c, d, e;

	int pcemblocks = MAX_PCEMMAPBLOCKS;
	if (!pcemmap) {
		pcemmap = xcalloc(mem_mapping_t *, pcemblocks);
		if (!pcemmap) {
			gui_message(_T("out of memory for PCI mappin table\n"));
			abort();
		}
	}
	for (int i = 0; i < pcemblocks; i++) {
		pcemmap[i] = NULL;
	}
	for (int i = 0; i < MAX_PCEMMAPPINGS; i++) {
		pcemmappings[i] = NULL;
	}

	gfxboard_priv = p;
	has_vlb = 0;
	timer_freq = syncbase;

	for (c = 0; c < 256; c++)
	{
		e = c;
		for (d = 0; d < 8; d++)
		{
			rotatevga[d][c] = e;
			e = (e >> 1) | ((e & 1) ? 0x80 : 0);
		}
	}
	for (c = 0; c < 4; c++)
	{
		for (d = 0; d < 4; d++)
		{
			edatlookup[c][d] = 0;
			if (c & 1) edatlookup[c][d] |= 1;
			if (d & 1) edatlookup[c][d] |= 2;
			if (c & 2) edatlookup[c][d] |= 0x10;
			if (d & 2) edatlookup[c][d] |= 0x20;
			//                        printf("Edat %i,%i now %02X\n",c,d,edatlookup[c][d]);
		}
	}

	if (!video_15to32)
		video_15to32 = (uint32_t*)malloc(4 * 65536);
	for (c = 0; c < 65536; c++) {
		if (swapped) {
			video_15to32[c] = (((c >> 10) & 31) << 3) | (((c >> 5) & 31) << 11) | (((c >> 0) & 31) << 19);
		} else {
			video_15to32[c] = ((c & 31) << 3) | (((c >> 5) & 31) << 11) | (((c >> 10) & 31) << 19);
		}
	}

	if (!video_16to32)
		video_16to32 = (uint32_t*)malloc(4 * 65536);
	for (c = 0; c < 65536; c++) {
		if (swapped) {
			video_16to32[c] = (((c >> 11) & 31) << 3) | (((c >> 5) & 63) << 10) | (((c >> 0) & 31) << 19);
		} else {
			video_16to32[c] = ((c & 31) << 3) | (((c >> 5) & 63) << 10) | (((c >> 11) & 31) << 19);
		}
	}

	if (!buffer32) {
		buffer32 = (PCBITMAP *)calloc(sizeof(PCBITMAP) + sizeof(uint8_t *) * 4096, 1);
		buffer32->w = 2048;
		buffer32->h = 4096;
		buffer32->dat = xcalloc(uae_u8, buffer32->w * buffer32->h * 4);
		for (int i = 0; i < buffer32->h; i++) {
			buffer32->line[i] = buffer32->dat + buffer32->w * 4 * i;
		}
	}

	pcem_linear_read_b = dummy_bread;
	pcem_linear_read_w = dummy_wread;
	pcem_linear_read_l = dummy_lread;
	pcem_linear_write_b = dummy_bwrite;
	pcem_linear_write_w = dummy_wwrite;
	pcem_linear_write_l = dummy_lwrite;
	pcem_mapping_linear = NULL;
	pcem_mapping_linear_offset = 0;
	timer_head = NULL;
}

extern void *svga_get_object(void);

uae_u8 pcem_read_io(int port, int index)
{
	svga_t *svga = (svga_t*)svga_get_object();
	if (port == 0x3d5) {
		return svga->crtc[index];
	} else {
		write_log("unknown port %04x!\n", port);
		abort();
	}
}

#if 0
void update_pcembuffer32(void *buf, int w, int h, int bytesperline)
{
	if (!buf) {
		w = 2048;
		h = 2048;
		if (!tempbuf) {
			tempbuf = xmalloc(uae_u8, w * 4);
		}
		buf = tempbuf;
		bytesperline = 0;
	}
	if (!buffer32 || h > buffer32->h) {
		free(buffer32);
		buffer32 = (PCBITMAP *)calloc(sizeof(PCBITMAP) + sizeof(uint8_t *) * h, 1);
	}
	if (buffer32->w == w && buffer32->h == h && buffer32->line[0] == buf)
		return;
	buffer32->w = w;
	buffer32->h = h;
	uae_u8 *b = (uae_u8*)buf;
	for (int i = 0; i < h; i++) {
		buffer32->line[i] = b;
		b += bytesperline;
	}
}
#endif

uae_u8 *getpcembuffer32(int x, int y, int yy)
{
	return buffer32->line[y + yy] + x * 4;
}


void video_wait_for_buffer(void)
{
	//write_log(_T("video_wait_for_buffer\n"));
}
void updatewindowsize(int x, int mx, int y, int my)
{
	x *= mx;
	gfxboard_resize(x, y, mx, my, gfxboard_priv);
}

static void (*pci_card_write)(int func, int addr, uint8_t val, void *priv);
static uint8_t(*pci_card_read)(int func, int addr, void *priv);
static void *pci_card_priv;

void put_pci_pcem(uaecptr addr, uae_u8 v)
{
	if (pci_card_write) {
		pci_card_write(0, addr, v, pci_card_priv);
	}
}
uae_u8 get_pci_pcem(uaecptr addr)
{
	uae_u8 v = 0;
	if (pci_card_read) {
		v = pci_card_read(0, addr, pci_card_priv);
	}
	return v;
}

int pci_add(uint8_t(*read)(int func, int addr, void *priv), void (*write)(int func, int addr, uint8_t val, void *priv), void *priv)
{
	pci_card_read = read;
	pci_card_write = write;
	pci_card_priv = priv;
	return 0;
}
extern void gfxboard_intreq(void *, int, bool);
void pci_set_irq_routing(int card, int irq)
{
	//write_log(_T("pci_set_irq_routing %d %d\n"), card, irq);
}
void pci_set_irq(int card, int pci_int)
{
	//write_log(_T("pci_set_irq %d %d\n"), card, pci_int);
	gfxboard_intreq(gfxboard_priv, 1, true);
}
void pci_clear_irq(int card, int pci_int)
{
	//write_log(_T("pci_clear_irq %d %d\n"), card, pci_int);
	gfxboard_intreq(gfxboard_priv, 0, true);
}
int rom_init(rom_t *rom, char *fn, uint32_t address, int size, int mask, int file_offset, uint32_t flags)
{
	return 0;
}

void thread_sleep(int n)
{
	sleep_millis(n);
}

thread_t *thread_create(void (*thread_rout)(void *param), void *param)
{
	uae_thread_id tid;
	uae_start_thread(_T("PCem helper"), thread_rout, param, &tid);
	return tid;
}
void thread_kill(thread_t *handle)
{
	uae_end_thread((uae_thread_id*)&handle);
}
event_t *thread_create_event(void)
{
	uae_sem_t sem = { 0 };
	uae_sem_init(&sem, 1, 0);
	return sem;
}
void thread_set_event(event_t *event)
{
	uae_sem_post((uae_sem_t*)&event);
}
void thread_reset_event(event_t *_event)
{
	uae_sem_init((uae_sem_t*)&_event, 1, 0);
}
int thread_wait_event(event_t *event, int timeout)
{
	uae_sem_trywait_delay((uae_sem_t*)&event, timeout < 0 ? INFINITE : timeout);
	return 0;
}
void thread_destroy_event(event_t *_event)
{
	uae_sem_destroy((uae_sem_t*)&_event);
}

typedef struct win_mutex_t
{
	HANDLE handle;
} win_mutex_t;

mutex_t* thread_create_mutex(void)
{
	win_mutex_t *mutex = xcalloc(win_mutex_t,1);
	mutex->handle = CreateSemaphore(NULL, 1, 1, NULL);
	return mutex;
}

void thread_lock_mutex(mutex_t *_mutex)
{
	win_mutex_t *mutex = (win_mutex_t*)_mutex;
	WaitForSingleObject(mutex->handle, INFINITE);
}

void thread_unlock_mutex(mutex_t *_mutex)
{
	win_mutex_t *mutex = (win_mutex_t*)_mutex;
	ReleaseSemaphore(mutex->handle, 1, NULL);
}

void thread_destroy_mutex(mutex_t *_mutex)
{
	win_mutex_t *mutex = (win_mutex_t*)_mutex;
	CloseHandle(mutex->handle);
	xfree(mutex);
}

static mem_mapping_t *getmm(uaecptr *addrp)
{
	uaecptr addr = *addrp;
	addr &= 0x7fffffff;
	int index = addr / PCEMMAPBLOCKSIZE;
	mem_mapping_t *m = pcemmap[index];
	if (!m) {
		write_log(_T("%08x no mapping\n"), addr);
	}
	return m;
}

void put_mem_pcem(uaecptr addr, uae_u32 v, int size)
{
#if 0
	write_log("put_mem_pcem %08x %08x %d\n", addr, v, size);
#endif
	mem_mapping_t *m = getmm(&addr);
	if (m) {
		if (size == 0) {
			m->write_b(addr, v, m->p);
		} else if (size == 1) {
			m->write_w(addr, v, m->p);
		} else if (size == 2) {
			m->write_l(addr, v, m->p);
		}
	}
}
uae_u32 get_mem_pcem(uaecptr addr, int size)
{
	uae_u32 v = 0;
	mem_mapping_t *m = getmm(&addr);
	if (m) {
		if (size == 0) {
			v = m->read_b(addr, m->p);
		} else if (size == 1) {
			v = m->read_w(addr, m->p);
		} else if (size == 2) {
			v = m->read_l(addr, m->p);
		}
	}
#if 0
	write_log("get_mem_pcem %08x %08x %d\n", addr, v, size);
#endif
	return v;
}

static void mapping_recalc(mem_mapping_t *mapping)
{
	if (mapping->read_b == NULL)
		mapping->read_b = dummy_bread;
	if (mapping->read_w == NULL)
		mapping->read_w = dummy_wread;
	if (mapping->read_l == NULL)
		mapping->read_l = dummy_lread;
	if (mapping->write_b == NULL)
		mapping->write_b = dummy_bwrite;
	if (mapping->write_w == NULL)
		mapping->write_w = dummy_wwrite;
	if (mapping->write_l == NULL)
		mapping->write_l = dummy_lwrite;


	if (mapping == pcem_mapping_linear || (!pcem_mapping_linear && mapping->size >= 1024 * 1024)) {
		pcem_mapping_linear = mapping;
		pcem_mapping_linear_priv = mapping->p;
		pcem_mapping_linear_offset = mapping->base;
		if (!mapping->enable) {
			pcem_linear_read_b = dummy_bread;
			pcem_linear_read_w = dummy_wread;
			pcem_linear_read_l = dummy_lread;
			pcem_linear_write_b = dummy_bwrite;
			pcem_linear_write_w = dummy_wwrite;
			pcem_linear_write_l = dummy_lwrite;
		} else {
			pcem_linear_read_b = mapping->read_b;
			pcem_linear_read_w = mapping->read_w;
			pcem_linear_read_l = mapping->read_l;
			pcem_linear_write_b = mapping->write_b;
			pcem_linear_write_w = mapping->write_w;
			pcem_linear_write_l = mapping->write_l;
		}
	}

	for (uae_u32 i = 0; i < mapping->size; i += PCEMMAPBLOCKSIZE) {
		uae_u32 addr = i + mapping->base;
		addr &= 0x7fffffff;
		int offset = addr / PCEMMAPBLOCKSIZE;
		if (offset >= 0 && offset < MAX_PCEMMAPBLOCKS) {
			if (mapping->enable) {
				pcemmap[offset] = mapping;
			} else {
				pcemmap[offset] = NULL;
			}
		}
	}
}

void mem_mapping_addx(mem_mapping_t *mapping,
	uint32_t base,
	uint32_t size,
	uint8_t(*read_b)(uint32_t addr, void *p),
	uint16_t(*read_w)(uint32_t addr, void *p),
	uint32_t(*read_l)(uint32_t addr, void *p),
	void (*write_b)(uint32_t addr, uint8_t  val, void *p),
	void (*write_w)(uint32_t addr, uint16_t val, void *p),
	void (*write_l)(uint32_t addr, uint32_t val, void *p),
	uint8_t *exec,
	uint32_t flags,
	void *p)
{
	mapping->enable = 0;
	for (int i = 0; i < MAX_PCEMMAPPINGS; i++) {
		if (pcemmappings[i] == mapping)
			return;
	}
	for (int i = 0; i < MAX_PCEMMAPPINGS; i++) {
		if (pcemmappings[i] == NULL) {
			pcemmappings[i] = mapping;
			mapping->base = base;
			mapping->size = size;
			mapping->read_b = read_b;
			mapping->read_w = read_w;
			mapping->read_l = read_l;
			mapping->write_b = write_b;
			mapping->write_w = write_w;
			mapping->write_l = write_l;
			mapping->exec = exec;
			mapping->flags = flags;
			mapping->p = p;
			mapping->next = NULL;
			mapping_recalc(mapping);
			return;
		}
	}
}

void mem_mapping_set_handlerx(mem_mapping_t *mapping,
	uint8_t(*read_b)(uint32_t addr, void *p),
	uint16_t(*read_w)(uint32_t addr, void *p),
	uint32_t(*read_l)(uint32_t addr, void *p),
	void (*write_b)(uint32_t addr, uint8_t  val, void *p),
	void (*write_w)(uint32_t addr, uint16_t val, void *p),
	void (*write_l)(uint32_t addr, uint32_t val, void *p))
{
	mapping->read_b = read_b;
	mapping->read_w = read_w;
	mapping->read_l = read_l;
	mapping->write_b = write_b;
	mapping->write_w = write_w;
	mapping->write_l = write_l;
	mapping_recalc(mapping);
}

void mem_mapping_set_px(mem_mapping_t *mapping, void *p)
{
	mapping->p = p;
}

void pci_add_specific(int card, uint8_t(*read)(int func, int addr, void *priv), void (*write)(int func, int addr, uint8_t val, void *priv), void *priv)
{
}

void mca_add(uint8_t(*read)(int addr, void *priv), void (*write)(int addr, uint8_t val, void *priv), void (*reset)(void *priv), void *priv)
{
}

#define MAX_IO_PORT 0x10000
static uint8_t(*port_inb[MAX_IO_PORT])(uint16_t addr, void *priv);
static uint16_t(*port_inw[MAX_IO_PORT])(uint16_t addr, void *priv);
static uint32_t(*port_inl[MAX_IO_PORT])(uint16_t addr, void *priv);
static void(*port_outb[MAX_IO_PORT])(uint16_t addr, uint8_t  val, void *priv);
static void(*port_outw[MAX_IO_PORT])(uint16_t addr, uint16_t val, void *priv);
static void(*port_outl[MAX_IO_PORT])(uint16_t addr, uint32_t val, void *priv);
static void *port_priv[MAX_IO_PORT];

void put_io_pcem(uaecptr addr, uae_u32 v, int size)
{
#if 0
	write_log(_T("put_io_pcem(%08x,%08x,%d)\n"), addr, v, size);
#endif

	addr &= MAX_IO_PORT - 1;
	if (size == 0) {
		if (port_outb[addr])
			port_outb[addr](addr, v, port_priv[addr]);
	} else if (size == 1) {
		if (port_outw[addr]) {
			port_outw[addr](addr, v, port_priv[addr]);
		} else {
			put_io_pcem(addr + 0, v >> 0, 0);
			put_io_pcem(addr + 1, v >> 8, 0);
		}
	} else if (size == 2) {
		if (port_outl[addr]) {
			port_outl[addr](addr, v, port_priv[addr]);
		} else {
			put_io_pcem(addr + 0, v >> 16, 1);
			put_io_pcem(addr + 2, v >> 0, 1);
		}
	}
}
uae_u32 get_io_pcem(uaecptr addr, int size)
{
#if 0
	write_log(_T("get_io_pcem(%08x,%d)\n"), addr, size);
#endif

	uae_u32 v = 0;
	addr &= MAX_IO_PORT - 1;
	if (size == 0) {
		if (port_inb[addr])
			v = port_inb[addr](addr, port_priv[addr]);
	} else if (size == 1) {
		if (port_inw[addr]) {
			v = port_inw[addr](addr, port_priv[addr]);
		} else {
			v = get_io_pcem(addr + 0, 0) << 0;
			v |= get_io_pcem(addr + 1, 0) << 8;
		}
	} else if (size == 2) {
		if (port_inl[addr]) {
			v = port_inl[addr](addr, port_priv[addr]);
		} else {
			v = get_io_pcem(addr + 0, 1) << 16;
			v |= get_io_pcem(addr + 2, 1) << 0;
		}
	}
	return v;
}

void io_sethandlerx(uint16_t base, int size,
	uint8_t(*inb)(uint16_t addr, void *priv),
	uint16_t(*inw)(uint16_t addr, void *priv),
	uint32_t(*inl)(uint16_t addr, void *priv),
	void (*outb)(uint16_t addr, uint8_t  val, void *priv),
	void (*outw)(uint16_t addr, uint16_t val, void *priv),
	void (*outl)(uint16_t addr, uint32_t val, void *priv),
	void *priv)
{
	if (base + size > MAX_IO_PORT)
		return;

	for (int i = 0; i < size; i++) {
		int io = base + i;
		port_inb[io] = inb;
		port_inw[io] = inw;
		port_inl[io] = inl;
		port_outb[io] = outb;
		port_outw[io] = outw;
		port_outl[io] = outl;
		port_priv[io] = priv;
	}
}

void io_removehandlerx(uint16_t base, int size,
	uint8_t(*inb)(uint16_t addr, void *priv),
	uint16_t(*inw)(uint16_t addr, void *priv),
	uint32_t(*inl)(uint16_t addr, void *priv),
	void (*outb)(uint16_t addr, uint8_t  val, void *priv),
	void (*outw)(uint16_t addr, uint16_t val, void *priv),
	void (*outl)(uint16_t addr, uint32_t val, void *priv),
	void *priv)
{
	if (base + size > MAX_IO_PORT)
		return;

	for (int i = 0; i < size; i++) {
		int io = base + i;
		port_inb[io] = NULL;
		port_inw[io] = NULL;
		port_inl[io] = NULL;
		port_outb[io] = NULL;
		port_outw[io] = NULL;
		port_outl[io] = NULL;
	}
}

void mem_mapping_set_addrx(mem_mapping_t *mapping, uint32_t base, uint32_t size)
{
	mapping->enable = 0;
	mapping_recalc(mapping);
	mapping->enable = 1;
	mapping->base = base;
	mapping->size = size;
	mapping_recalc(mapping);
}

void mem_mapping_disablex(mem_mapping_t *mapping)
{
	mapping->enable = 0;
	mapping_recalc(mapping);
}

void mem_mapping_enablex(mem_mapping_t *mapping)
{
	mapping->enable = 1;
	mapping_recalc(mapping);
}

void pcem_linear_mark(int offset)
{
	if (!pcem_mapping_linear)
		return;
	uae_u16 w = pcem_linear_read_w(offset, pcem_mapping_linear_priv);
	pcem_linear_write_w(offset, w, pcem_mapping_linear_priv);
}

int model_get_config_int(const char *s)
{
	return 0;
}
char *model_get_config_string(const char *s)
{
	return NULL;
}
void upc_set_mouse(void (*mouse_write)(uint8_t, void*), void *p)
{
}
