/*Arculator 2.1 by Sarah Walker*/

/*Computer Concepts / Wild Vision Colourcard

  IOC Address map :
  0000-1fff : ROM (read)
  0000 - (write)
        bit 0 - FPGA serial data
        bit 1 - FPGA serial clock
  2000 - ROM bank register (write)
  2000 - IRQ status (read)
        bit 0 - IRQ (cleared on read?)
  3000 - Control register
        bit 2 - !VIDC passthrough (?)
        bit 6 - IRQ enable (?)
        others - unknown
        Set to 41 when VIDC driving display, cc when G332 driving

  MEMC Address map :
  0000-0fff : Inmos G332/5 register write
  2000 - High byte latch (write before writing to 0000-0fff

  VIDC data capture - starts 32 clocks (at 24 MHz) after hsync ends, terminates
    at start of next hsync
  only captures when !vsync
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "arc.h"
#include "arm.h"
#include "colourcard.h"
#include "config.h"
#include "g332.h"
#include "podules.h"
#include "podule_api.h"

static const podule_callbacks_t *podule_callbacks;

typedef struct colourcard_t
{
        uint8_t rom[0x20000];
        uint8_t ram[0x80000];

        uint32_t wp;
        int vsync_state;

        unsigned int rom_bank;

        uint8_t high_byte;
        uint8_t irq_status;
        uint8_t control;
        uint8_t fpga_control;

        g332_t g332;
        int64_t g332_time;

        podule_t *podule;
} colourcard_t;

static void colourcard_data_callback(uint8_t *data, int pixels, int hsync_length, int resolution, void *p);
static void colourcard_vsync_callback(void *p, int state);
static void colourcard_irq_callback(void *p, int state);

static int colourcard_init(struct podule_t *podule)
{
        FILE *f;
        char fn[512];

        colourcard_t *colourcard = malloc(sizeof(colourcard_t));
        memset(colourcard, 0, sizeof(colourcard_t));

        append_filename(fn, exname, "roms/podules/colourcard/cc.bin", 511);
        f = fopen(fn, "rb");
        if (f)
        {
                fread(colourcard->rom, 0x20000, 1, f);
                fclose(f);
        }
        else
        {
                rpclog("colourcard_init failed\n");
                free(colourcard);
                return -1;
        }

        podule->p = colourcard;
        colourcard->podule = podule;

        g332_init(&colourcard->g332, colourcard->ram, INMOS_G335, colourcard_irq_callback, colourcard);
        vidc_attach(colourcard_data_callback, colourcard_vsync_callback, colourcard);

        return 0;
}

static void colourcard_close(struct podule_t *podule)
{
        colourcard_t *colourcard = podule->p;

        g332_close(&colourcard->g332);

        free(colourcard);
}

static void colourcard_data_callback(uint8_t *data, int pixels, int hsync_length, int resolution, void *p)
{
        colourcard_t *colourcard = (colourcard_t *)p;
        int x;

        if (!colourcard->vsync_state)
        {
                /*ColourCard horizontal offset is 32 clocks @ 24 MHz after hsync end*/
                int h_offset = hsync_length + (resolution ? 16 : 16*2) - 1;

                for (x = h_offset; x < (pixels-1); x += 2)
                {
                        colourcard->ram[colourcard->wp & 0x7ffff] = data[x] | (data[x+1] << 4);
                        colourcard->wp++;
                }
        }
}

static void colourcard_vsync_callback(void *p, int state)
{
        colourcard_t *colourcard = (colourcard_t *)p;

        colourcard->vsync_state = state;

        if (state)
                colourcard->wp = 0;
}

static void colourcard_irq_callback(void *p, int state)
{
        colourcard_t *colourcard = (colourcard_t *)p;

        if (state)
        {
                colourcard->irq_status |= 1;
                if (colourcard->control & 0x40)
                        podule_callbacks->set_irq(colourcard->podule, 1);
        }
}

static uint8_t colourcard_read_b(struct podule_t *podule, podule_io_type type, uint32_t addr)
{
        colourcard_t *colourcard = podule->p;
        uint8_t temp = 0xff;

        if (type != PODULE_IO_TYPE_IOC)
        {
//                rpclog("Read Colourcard MEMC %07X %07X  %08x\n", addr, PC, armregs[2]);
                return 0xff; /*Only IOC accesses supported*/
        }
        switch (addr & 0x3000)
        {
                case 0x0000: case 0x1000:
                temp = colourcard->rom[((addr >> 2) & 0x7ff) + ((colourcard->rom_bank << 11) & 0x1f800)];
                break;

                case 0x2000:
                temp = colourcard->irq_status;
                colourcard->irq_status &= ~1;
                podule_callbacks->set_irq(colourcard->podule, 0);
                break;
        }

//        if (addr & 0x2000) rpclog("Read Colourcard %07X %02x %07X  %08x\n", addr, temp, PC, armregs[2]);
        return temp;
}

static uint16_t colourcard_read_w(struct podule_t *podule, podule_io_type type, uint32_t addr)
{
        uint16_t temp = 0xffff;

        if (type != PODULE_IO_TYPE_IOC)
        {
//                rpclog("Readw Colourcard MEMC %07X %07X  %08x\n", addr, PC, armregs[2]);
                return 0xffff; /*Only IOC accesses supported*/
        }
        switch (addr & 0x3000)
        {
                default:
                temp = colourcard_read_b(podule, type, addr);
                break;
        }

//        rpclog("Readw Colourcard %07X %02x %07X  %08x\n", addr, temp, PC, armregs[2]);
        return temp;
}

static void colourcard_write_b(struct podule_t *podule, podule_io_type type, uint32_t addr, uint8_t val)
{
        colourcard_t *colourcard = podule->p;

        if (type != PODULE_IO_TYPE_IOC)
        {
//                rpclog("Write Colourcard MEMC %07X %02X %07X\n",addr,val,PC);
                return; /*Only IOC accesses supported*/
        }

//        /*if ((addr & 0x3fff) != 0x2000) */rpclog("Write Colourcard %07X %02X %07X  %08x %08x\n",addr,val,PC, armregs[0], armregs[1]);
        switch (addr & 0x3000)
        {
                case 0x0000:
                colourcard->fpga_control = val;
                break;

                case 0x2000:
                colourcard->rom_bank = val;
                break;

                case 0x3000:
                colourcard->control = val;
//                rpclog("CC control=%02x\n", val);
                vidc_output_enable(!(val & 4));
                g332_output_enable(&colourcard->g332, val & 4);
                break;
        }
}

static void colourcard_write_w(struct podule_t *podule, podule_io_type type, uint32_t addr, uint16_t val)
{
        colourcard_t *colourcard = podule->p;

        if (type == PODULE_IO_TYPE_MEMC)
        {
//                rpclog("Writew Colourcard MEMC %07X %04X %07X\n",addr,val,PC);

                switch (addr & 0x3000)
                {
                        case 0x0000:
//                        rpclog(" IMS write %03x %06x\n", (addr & 0xfff) >> 2, val | (colourcard->high_byte << 16));
                        g332_write(&colourcard->g332, (addr & 0xfff) >> 2, val | (colourcard->high_byte << 16));
                        break;

                        case 0x2000:
                        colourcard->high_byte = val;
                        break;
                }
        }
        else
        {
//                rpclog("Writew Colourcard %07X %04X %07X\n",addr,val,PC);

                switch (addr & 0x3000)
                {
                        default:
                        colourcard_write_b(podule, type, addr, val & 0xff);
                        break;
                }
        }
}

static int colourcard_run(struct podule_t *podule, int timeslice_us)
{
        colourcard_t *colourcard = podule->p;

        colourcard->g332_time -= (uint64_t)timeslice_us << 32;

        while (colourcard->g332_time < 0)
                colourcard->g332_time += g332_poll(&colourcard->g332);

        return (colourcard->g332_time >> 32) + 1;
}

static const podule_header_t colourcard_podule_header =
{
        .version = PODULE_API_VERSION,
        .flags = PODULE_FLAGS_UNIQUE,
        .short_name = "colourcard",
        .name = "Computer Concepts Colour Card",
        .functions =
        {
                .init = colourcard_init,
                .close = colourcard_close,
                .read_b = colourcard_read_b,
                .read_w = colourcard_read_w,
                .write_b = colourcard_write_b,
                .write_w = colourcard_write_w,
                .run = colourcard_run
        }
};

const podule_header_t *colourcard_probe(const podule_callbacks_t *callbacks, char *path)
{
        FILE *f;
        char fn[512];

        podule_callbacks = callbacks;

        append_filename(fn, exname, "roms/podules/colourcard/cc.bin", 511);
        f = fopen(fn, "rb");
        if (!f)
                return NULL;
        fclose(f);
        return &colourcard_podule_header;
}
