/*
 * Sega Saturn cartridge flash tool
 * by Anders Montonen, 2012
 *
 * Original software by ExCyber
 * Graphics routines by Charles MacDonald
 *
 * Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
 */
#include <stdint.h>

#include "vdp2.h"
#include "smpc.h"

#include "vdp.h"
#include "conio.h"

#include "flash.h"

extern const uint8_t  RomImage;
extern const uint32_t RomLen;

#define PAD_START   (1<<11)
#define PAD_A       (1<<10)
#define PAD_C       (1<<9)
#define PAD_B       (1<<8)

static int InitPad(void)
{
    int pad_connected = 3;
    unsigned char temp;
    int count;

    DDR1    = 0x60; /* TH,TR out, TL,D3-D0 in */
    DDR2    = 0x60; /* TH,TR out, TL,D3-D0 in */
    IOSEL   = IOSEL1 | IOSEL2; /* Manual mode for both ports */
    EXLE    = 0x00;

    /** Test pad1 **/
    PDR1 = 0x60;
    for(count=0; count < 16; count++)
    {
        PDR1 = 0x60;
    }
    temp = PDR1;

    if((temp&0x1) != 0x00)
    { /* Pad1 is disconnected. */
        pad_connected -= 1;
    }


    /** Test pad2 **/
    PDR2 = 0x60;
    for(count=0; count < 16; count++)
    {
        PDR2 = 0x60;
    }
    temp = PDR2;

    if((temp&0x1) != 0x00)
    { /* Pad2 is disconnected. */
        pad_connected -= 2;
    }

    return pad_connected;
}

static uint16_t ReadPad(int pad_connected)
{
    uint16_t bits;

    if(pad_connected & 0x1)
    { /* Read input from pad1 */
        PDR1 = 0x60;
        bits = (PDR1 & 0x8) << 9;
        PDR1 = 0x40;
        bits |= (PDR1 & 0xf) << 8;
        PDR1 = 0x20;
        bits |= (PDR1 & 0xf) << 4;
        PDR1 = 0x00;
        bits |= (PDR1 & 0xf);
    }
    else
    { /* Read input from pad2 */
        PDR2 = 0x60;
        bits = (PDR2 & 0x8) << 9;
        PDR2 = 0x40;
        bits |= (PDR2 & 0xf) << 8;
        PDR2 = 0x20;
        bits |= (PDR2 & 0xf) << 4;
        PDR2 = 0x00;
        bits |= (PDR2 & 0xf);
    }

    return bits ^ 0x1FFF;
}

static void ExitToMultiplayer(void)
{
    /* Exit to multiplayer. */
    (**(volatile void(**)(void))0x600026C)();
}


int main(void)
{
    uint16_t        vendorID;
    uint16_t        deviceID;
    uint32_t        page;
    FlashDevice_t   device;
    const uint16_t *pRom = (const uint16_t*)&RomImage;
    uint16_t        padData;
    int             pad_connected;

    vdp_init();
    conio_init();
    pad_connected = InitPad();

    TVMD = 0x8000;

    _printf(2, 2, 0xF0, "Saturn Cartridge Flasher [USB dev cart]");
    _printf(2, 3, 0xF0, "Revision 12");
    _printf(2, 4, 0xF0, "Press Start to begin");

    while (1)
    {
        padData = ReadPad(pad_connected);
        if (padData & PAD_START)
            break;
        WaitForVBLANKIn();
        WaitForVBLANKOut();
    }

    _printf(2, 6, 0xF0, "Flash Vendor ID:");
    WaitForVBLANKIn();
    WaitForVBLANKOut();

    vendorID = FlashGetVendorID();
    _printhexl(19, 6, 0xF0, vendorID);
    WaitForVBLANKIn();
    WaitForVBLANKOut();

    _printf(2, 7, 0xF0, "Flash Device ID:");
    WaitForVBLANKIn();
    WaitForVBLANKOut();

    deviceID = FlashGetDeviceID();
    _printhexl(19, 7, 0xF0, deviceID);
    WaitForVBLANKIn();
    WaitForVBLANKOut();

    FillFlashDeviceInfo(vendorID, deviceID, &device);
    _printf(2, 8, 0xF0, device.pVendorString);
    _printf(2, 9, 0xF0, device.pDeviceString);
    WaitForVBLANKIn();
    WaitForVBLANKOut();

    if (device.RomSize == 0 || device.PageSize == 0)
    {
        while(1)
        {
            WaitForVBLANKIn();
            WaitForVBLANKOut();
        }
    }

    _printf(2, 11, 0xF0, "Press A+B+C+Start to initiate write...");
    while (1)
    {
        padData = ReadPad(pad_connected);
        if ((padData & (PAD_START|PAD_A|PAD_C|PAD_B)) == (PAD_START|PAD_A|PAD_C|PAD_B))
            break;
        WaitForVBLANKIn();
        WaitForVBLANKOut();
    }

    _printf(2, 13, 0xF0, "Writing...");

    device.EraseFlash(&device);

    _printf(2, 14, 0xF0, "Page          of          ");

    _printhexl(23, 14, 0xF0, RomLen / device.PageSize / 2);

    for (page = 0; page < RomLen / device.PageSize / 2; page++)
    {
        _printhexl(7, 14, 0xF0, page + 1);
        device.WriteFlashPage(&device, page,
                              (uint16_t*)(pRom + (page * device.PageSize)));
    }

    _printf(2, 16, 0xF0, "Done!");

    _printf(2, 18, 0xF0, "Press Start to exit");
    while (1)
    {
        padData = ReadPad(pad_connected);
        if (padData & PAD_START)
            break;
        WaitForVBLANKIn();
        WaitForVBLANKOut();
    }
    ExitToMultiplayer();
}
