#include "ibm.h"
#include "acc2036.h"
#include "io.h"
#include "mem.h"

static struct {
        int reg_idx;
        uint8_t regs[256];
} acc2036;

static void acc2036_shadow_recalc() {
        if (acc2036.regs[0x2c] & 8) {
                switch (acc2036.regs[0x22] & 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;
                }
        } else
                mem_set_mem_state(0xf0000, 0x10000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL);

        if (acc2036.regs[0x2c] & 4) {
                switch (acc2036.regs[0x22] & 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;
                }
        } else
                mem_set_mem_state(0xe0000, 0x10000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL);
}

static void acc2036_write(uint16_t addr, uint8_t val, void *p) {
        if (!(addr & 1))
                acc2036.reg_idx = val;
        else {
                acc2036.regs[acc2036.reg_idx] = val;
                //                pclog("Write ACC2036 R%02x %02x %04x:%04x %i\n", acc2036.reg_idx, val, CS,cpu_state.pc, ins);

                switch (acc2036.reg_idx) {
                case 0x22:
                case 0x2c:
                        acc2036_shadow_recalc();
                        break;

                case 0x23:
                        mem_mapping_disable(&ram_remapped_mapping);
                        if (val & 0x10) {
                                if (acc2036.regs[0x2c] & 0xc)
                                        mem_remap_top_256k();
                                else
                                        mem_remap_top_384k();
                        }
                        break;
                }
        }
}

static uint8_t acc2036_read(uint16_t addr, void *p) {
        if (!(addr & 1))
                return acc2036.reg_idx;

        if (acc2036.reg_idx >= 0xbc)
                return 0xff;

        return acc2036.regs[acc2036.reg_idx];
}

void acc2036_init() {
        memset(&acc2036, 0, sizeof(acc2036));
        io_sethandler(0x00f2, 0x0002, acc2036_read, NULL, NULL, acc2036_write, NULL, NULL, NULL);
}
