#include <stdlib.h>
#include "ibm.h"
#include "io.h"
#include "device.h"
#include "tandy_eeprom.h"
#include "nvr.h"

typedef struct
{
        int state;
        int count;
        int addr;
        int clock;
        uint16_t data;
        uint16_t store[64];
        
        int romset;
} tandy_eeprom_t;

enum
{
        EEPROM_IDLE,
        EEPROM_GET_OPERATION,
        EEPROM_READ,
        EEPROM_WRITE
};

static int eeprom_data_out;

void tandy_eeprom_write(uint16_t addr, uint8_t val, void *p)
{
        tandy_eeprom_t *eeprom = (tandy_eeprom_t *)p;
        
        if ((val & 4) && !eeprom->clock)
        {
//                pclog("eeprom_write %02x %i %i\n", val, eeprom->state, eeprom->count);
                switch (eeprom->state)
                {
                        case EEPROM_IDLE:
                        switch (eeprom->count)
                        {
                                case 0:
                                if (!(val & 3))
                                        eeprom->count = 1;
                                else
                                        eeprom->count = 0;
                                break;
                                case 1:
                                if ((val & 3) == 2)
                                        eeprom->count = 2;
                                else
                                        eeprom->count = 0;
                                break;
                                case 2:
                                if ((val & 3) == 3)
                                        eeprom->state = EEPROM_GET_OPERATION;
                                eeprom->count = 0;
                                break;
                        }
                        break;
                        case EEPROM_GET_OPERATION:
                        eeprom->data = (eeprom->data << 1) | (val & 1);
                        eeprom->count++;
                        if (eeprom->count == 8)
                        {
                                eeprom->count = 0;
//                                pclog("EEPROM get operation %02x\n", eeprom->data);
                                eeprom->addr = eeprom->data & 0x3f;
                                switch (eeprom->data & 0xc0)
                                {
                                        case 0x40:
                                        eeprom->state = EEPROM_WRITE;
                                        break;
                                        case 0x80:
                                        eeprom->state = EEPROM_READ;
                                        eeprom->data = eeprom->store[eeprom->addr];
//                                        pclog("EEPROM read data %02x %04x\n", eeprom->addr, eeprom->data);
                                        break;
                                        default:
                                        eeprom->state = EEPROM_IDLE;
                                        break;
                                }
                        }
                        break;
                        
                        case EEPROM_READ:
                        eeprom_data_out = eeprom->data & 0x8000;
                        eeprom->data <<= 1;
                        eeprom->count++;
                        if (eeprom->count == 16)
                        {
                                eeprom->count = 0;
                                eeprom->state = EEPROM_IDLE;
                        }
                        break;
                        case EEPROM_WRITE:
                        eeprom->data = (eeprom->data << 1) | (val & 1);
                        eeprom->count++;
                        if (eeprom->count == 16)
                        {
                                eeprom->count = 0;
                                eeprom->state = EEPROM_IDLE;
//                                pclog("EEPROM write %04x to %02x\n", eeprom->data, eeprom->addr);
                                eeprom->store[eeprom->addr] = eeprom->data;
                        }
                        break;
                }
        }
        
        eeprom->clock = val & 4;
}

int tandy_eeprom_read()
{
//        pclog("tandy_eeprom_read: data_out=%x\n", eeprom_data_out);
        return eeprom_data_out;
}

void *tandy_eeprom_init()
{
        tandy_eeprom_t *eeprom = malloc(sizeof(tandy_eeprom_t));
        FILE *f = NULL;

        memset(eeprom, 0, sizeof(tandy_eeprom_t));
        
        eeprom->romset = romset;
        switch (romset)
        {
                case ROM_TANDY1000HX:
                f = nvrfopen("tandy1000hx.bin" ,"rb");
                break;
                case ROM_TANDY1000SL2:
                f = nvrfopen("tandy1000sl2.bin" ,"rb");
                break;
        }
        if (f)
        {
                fread(eeprom->store, 128, 1, f);
                fclose(f);
        }
        else
                memset(eeprom->store, 0, 128);

        io_sethandler(0x037c, 0x0001, NULL, NULL, NULL, tandy_eeprom_write, NULL, NULL, eeprom);
        
        return eeprom;
}

void tandy_eeprom_close(void *p)
{
        tandy_eeprom_t *eeprom = (tandy_eeprom_t *)p;
        FILE *f = NULL;
                
        switch (eeprom->romset)
        {
                case ROM_TANDY1000HX:
                f = nvrfopen("tandy1000hx.bin" ,"wb");
                break;
                case ROM_TANDY1000SL2:
                f = nvrfopen("tandy1000sl2.bin" ,"wb");
                break;
        }
        fwrite(eeprom->store, 128, 1, f);
        fclose(f);

        free(eeprom);
}

device_t tandy_eeprom_device =
{
        "Tandy EEPROM",
        0,
        tandy_eeprom_init,
        tandy_eeprom_close,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL
};
