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