/*
    io.c
    I/O controller chip emulation
*/

#include "shared.h"

port_t port[3];
uint8 io_reg[0x10];


void io_reset(void)
{
    /* I/O register default settings */
    uint8 io_def[0x10] =
    {
        0xA0,
        0x7F, 0x7F, 0x7F,
        0x00, 0x00, 0x00, 
        0xFF, 0x00, 0x00,
        0xFF, 0x00, 0x00,  
        0xFB, 0x00, 0x00,  
    };

    /* Initialize I/O registers */
    memcpy(io_reg, io_def, 0x10);

    /*
        Port A : 3B pad
        Port B : Unused
        Port C : Unused
    */
    port[0].data_w = device_3b_w;   
    port[0].data_r = device_3b_r;
    port[1].data_w = NULL;
    port[1].data_r = NULL;
    port[2].data_w = NULL;
    port[2].data_r = NULL;
}

/*--------------------------------------------------------------------------*/
/* I/O chip functions                                                       */
/*--------------------------------------------------------------------------*/

void gen_io_w(int offset, int value)
{
    switch(offset)
    {
        case 0x01: /* Port A Data */
            value = ((value & 0x80) | (value & io_reg[offset+3]));
            io_reg[offset] = value;
            if(port[0].data_w) port[0].data_w(value);
            return;

        case 0x02: /* Port B Data */
            value = ((value & 0x80) | (value & io_reg[offset+3]));
            io_reg[offset] = value;
            if(port[1].data_w) port[1].data_w(value);
            return;

        case 0x03: /* Port C Data */
            value = ((value & 0x80) | (value & io_reg[offset+3]));
            io_reg[offset] = value;
            if(port[2].data_w) port[2].data_w(value);
            return;  

        case 0x04: /* Port A Ctrl */
        case 0x05: /* Port B Ctrl */
        case 0x06: /* Port C Ctrl */
            io_reg[offset] = value & 0xFF;
            break;

        case 0x07: /* Port A TxData */
        case 0x0A: /* Port B TxData */
        case 0x0D: /* Port C TxData */
            io_reg[offset] = value;
            break;

        case 0x09: /* Port A S-Ctrl */
        case 0x0C: /* Port B S-Ctrl */
        case 0x0F: /* Port C S-Ctrl */
            io_reg[offset] = (value & 0xF8);
            break;
    }
}

int gen_io_r(int offset)
{
    uint8 temp;
    uint8 has_scd = 0x20; /* No Sega CD unit attached */
    uint8 gen_ver = 0x00; /* Version 0 hardware */

    switch(offset)
    {
        case 0x00: /* Version */
            switch(rom_version())
            {
                case 'J':
                    temp = 0x00;
                    break;
                case 'U':
                    temp = 0x80;
                    break;
                case 'E':
                    temp = 0xC0;
                    break;
                case 'A':
                    temp = 0xC0;
                    break;
                case 'B':
                    temp = 0xC0;
                    break;
                case '4':
                    temp = 0x80;
                    break;
                default:
                    temp = 0x80;
                    break;
            }
            return (temp | has_scd | gen_ver);
            break;

        case 0x01: /* Port A Data */
            if(port[0].data_r) return ((io_reg[offset] & 0x80) | port[0].data_r());
            return (io_reg[offset] | ((~io_reg[offset+3]) & 0x7F));

        case 0x02: /* Port B Data */
            if(port[1].data_r) return ((io_reg[offset] & 0x80) | port[1].data_r());
            return (io_reg[offset] | ((~io_reg[offset+3]) & 0x7F));

        case 0x03: /* Port C Data */
            if(port[2].data_r) return ((io_reg[offset] & 0x80) | port[2].data_r());
            return (io_reg[offset] | ((~io_reg[offset+3]) & 0x7F));
    }

    return (io_reg[offset]);
}

/*--------------------------------------------------------------------------*/
/* Input callbacks                                                          */
/*--------------------------------------------------------------------------*/

uint8 pad_2b_r(void)
{
    uint8 temp = 0x3F;
    if(input.pad[0] & INPUT_UP)    temp &= ~0x01;
    if(input.pad[0] & INPUT_DOWN)  temp &= ~0x02;
    if(input.pad[0] & INPUT_LEFT)  temp &= ~0x04;
    if(input.pad[0] & INPUT_RIGHT) temp &= ~0x08;
    if(input.pad[0] & INPUT_B)     temp &= ~0x10;
    if(input.pad[0] & INPUT_C)     temp &= ~0x20;
    return (temp);
}

static int th = 0;

uint8 device_3b_r(void)
{
    uint8 temp = 0x3F;

    if(th)
    {
        temp = 0x3f;
        if(input.pad[0] & INPUT_UP)    temp &= ~0x01;
        if(input.pad[0] & INPUT_DOWN)  temp &= ~0x02;
        if(input.pad[0] & INPUT_LEFT)  temp &= ~0x04;
        if(input.pad[0] & INPUT_RIGHT) temp &= ~0x08;
        if(input.pad[0] & INPUT_B)     temp &= ~0x10;
        if(input.pad[0] & INPUT_C)     temp &= ~0x20;
        return (temp | 0x40);
    }
    else
    {
        temp = 0x33;
        if(input.pad[0] & INPUT_UP)    temp &= ~0x01;
        if(input.pad[0] & INPUT_DOWN)  temp &= ~0x02;
        if(input.pad[0] & INPUT_A)     temp &= ~0x10;
        if(input.pad[0] & INPUT_START) temp &= ~0x20;
        return (temp);
    }
}

void device_3b_w(uint8 data)
{
    th = (data & 0x40);
}
