#include "ibm.h"
#include "device.h"
#include "io.h"
#include "cpu.h"
#include "lpt.h"
#include "mem.h"
#include "serial.h"
#include "vid_oti067.h"

#include "acer386sx.h"

static int acer_index = 0;
static uint8_t acer_regs[256];
static uint8_t acer_last_write;
static int acer_locked;
static void *acer_oti067;

void acer386sx_set_oti067(void *oti067) {
	acer_oti067 = oti067;
}

void acer386sx_write(uint16_t addr, uint8_t val, void *priv) {
	if (addr & 1) {
		if (acer_locked) {
			if (acer_last_write == 0x14 && val == 0x09)
				acer_locked = 0;

			acer_last_write = val;
			return;
		}

		if (acer_index == 0xff && val == 0xff)
			acer_locked = 1;
		else {
			pclog("Write ACER reg %02x %02x %08x\n", acer_index, val, cs);
			acer_regs[acer_index] = val;

			switch (acer_index) {
			case 0xa:
				switch ((val >> 4) & 3) {
				case 0:mem_set_mem_state(0xe0000, 0x10000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL);
					break;
				case 1:mem_set_mem_state(0xe0000, 0x10000, MEM_READ_INTERNAL | MEM_WRITE_EXTERNAL);
					break;
				case 2:mem_set_mem_state(0xe0000, 0x10000, MEM_READ_EXTERNAL | MEM_WRITE_INTERNAL);
					break;
				case 3:mem_set_mem_state(0xe0000, 0x10000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
					break;
				}
				break;
			case 0xb:
				switch ((val >> 4) & 3) {
				case 0:mem_set_mem_state(0xf0000, 0x10000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL);
					break;
				case 1:mem_set_mem_state(0xf0000, 0x10000, MEM_READ_INTERNAL | MEM_WRITE_EXTERNAL);
					break;
				case 2:mem_set_mem_state(0xf0000, 0x10000, MEM_READ_EXTERNAL | MEM_WRITE_INTERNAL);
					break;
				case 3:mem_set_mem_state(0xf0000, 0x10000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
					break;
				}
				break;
			}
		}
	} else
		acer_index = val;
}

uint8_t acer386sx_read(uint16_t addr, void *priv) {
	if (acer_locked)
		return 0xff;
	if (addr & 1) {
		if ((acer_index >= 0xc0 || acer_index == 0x20) && cpu_iscyrix)
			return 0xff; /*Don't conflict with Cyrix config registers*/
		return acer_regs[acer_index];
	} else
		return acer_index;
}

static uint8_t acer386sx_37f = 0, acer386sx_io = 0;
void acer386sx_io_write(uint16_t addr, uint8_t val, void *priv) {
	switch (addr) {
	case 0x37f:acer386sx_37f = val;
		if (val & 4)
			oti067_enable_disable(acer_oti067, 0);
		else
			oti067_enable_disable(acer_oti067, 1);
		break;

	case 0x3f3:acer386sx_io = val;
		serial1_remove();
		serial2_remove();
		lpt1_remove();
		lpt2_remove();

		switch (val & 3) {
		case 0: lpt1_init(0x378);
			break;
		case 1: lpt1_init(0x3bc);
			break;
		case 2: lpt1_init(0x278);
			break;
		}
		if (!(val & 0x04))
			serial1_set(0x3f8, 4);
		if (!(val & 0x08))
			serial2_set(0x2f8, 3);
		break;
	}
}

uint8_t acer386sx_io_read(uint16_t addr, void *priv) {
	switch (addr) {
	case 0x37f:return acer386sx_37f;

	case 0x3f3:return acer386sx_io;
	}

	return 0xff;
}

void acer386sx_init() {
	io_sethandler(0x0022, 0x0002, acer386sx_read, NULL, NULL, acer386sx_write, NULL, NULL, NULL);
	io_sethandler(0x037f, 0x0001, acer386sx_io_read, NULL, NULL, acer386sx_io_write, NULL, NULL, NULL);
	io_sethandler(0x03f3, 0x0001, acer386sx_io_read, NULL, NULL, acer386sx_io_write, NULL, NULL, NULL);

	acer386sx_io = 0;
	acer386sx_37f = 0;
}
