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