/**
 *  Sega Saturn VBT emulator test program
 *  by cafe-alpha.
 *  WWW: http://ppcenter.free.fr/
 *
 *  Based on Sega Saturn VDP1 example program
 *  by Charles MacDonald
 *  WWW: http://cgfm2.emuviews.com
**/

#include "shared.h"

/* Texture map size */
#define TX_W    128
#define TX_H    128

/* Create a 5:5:5 RGB color with the MSB set */
#define COLOR(r,g,b)    (((r)&0x1F)|((g)&0x1F)<<5|((b)&0x1F)<<10|0x8000)



unsigned short list_index = 1;  /* Offset into the command list */
unsigned short mesh_flag = 0;   /* Global mesh enable flag */



/* Clear command list */
void list_clear(void)
{
    unsigned short *p = (unsigned short *)VDP1_VRAM;
    int i;
    for(i=0;i<16;i++)
        p[i] = 0xFFFF;
    p[CMDCTRL] = 0x4000;
    list_index = 1;
}

/* Add end marker to command list */
void list_end(void)
{
    unsigned short *p = (unsigned short *)(VDP1_VRAM + (list_index << 5));
    p[CMDCTRL] = 0x8000;
    list_index++;
}

/* Add system clip to command list */
void list_set_system_clip(short x, short y)
{
    unsigned short *p = (unsigned short *)(VDP1_VRAM + (list_index << 5));

    p[CMDCTRL] = 0x0009; /* Set system clip */
    p[CMDLINK] = 0x0000; /* Jump address */
    p[CMDXC]   = x;
    p[CMDYC]   = y;
    list_index++;
}


/* Add user clip to command list */
void list_set_user_clip(short x1, short y1, short x2, short y2)
{
    unsigned short *p = (unsigned short *)(VDP1_VRAM + (list_index << 5));

    p[CMDCTRL] = 0x0008; /* Set user clip */
    p[CMDLINK] = 0x0000; /* Jump address */
    p[CMDXA]   = x1;
    p[CMDYA]   = y1;
    p[CMDXC]   = x2;
    p[CMDYC]   = y2;
    list_index++;
}

/* Add local coords to command list */
void list_set_local_coords(short x, short y)
{
    unsigned short *p = (unsigned short *)(VDP1_VRAM + (list_index << 5));

    p[CMDCTRL] = 0x000A; /* Set local coords */
    p[CMDLINK] = 0x0000; /* Jump address */
    p[CMDXA]   = x;
    p[CMDYA]   = y;
    list_index++;
}

/* Add local coords to command list */
void list_polyline(short x1, short y1, short x2, short y2, short x3, short y3, short x4, short y4, unsigned short color)
{
    unsigned short *p = (unsigned short *)(VDP1_VRAM + (list_index << 5));

    p[CMDCTRL] = 0x0005; /* Polyline */
    p[CMDLINK] = 0x0000; /* Jump address */
    p[CMDPMOD] = 0x00C0 | mesh_flag;
    p[CMDCOLR] = color;
    p[CMDXA]   = x1;
    p[CMDYA]   = y1;
    p[CMDXB]   = x2;
    p[CMDYB]   = y2;
    p[CMDXC]   = x3;
    p[CMDYC]   = y3;
    p[CMDXD]   = x4;
    p[CMDYD]   = y4;
    list_index++;
}

/* Add local coords to command list */
void list_polygon(short x1, short y1, short x2, short y2, short x3, short y3, short x4, short y4, unsigned short color, unsigned long grda)
{
    unsigned short *p = (unsigned short *)(VDP1_VRAM + (list_index << 5));

    p[CMDCTRL] = 0x0004; /* Polygon */
    p[CMDLINK] = 0x0000; /* Jump address */
    p[CMDPMOD] = 0x00C0 | mesh_flag | 4;
    p[CMDCOLR] = color;
    p[CMDXA]   = x1;
    p[CMDYA]   = y1;
    p[CMDXB]   = x2;
    p[CMDYB]   = y2;
    p[CMDXC]   = x3;
    p[CMDYC]   = y3;
    p[CMDXD]   = x4;
    p[CMDYD]   = y4;
    p[CMDGRDA] = grda>>3;
    list_index++;
}

/* Add local coords to command list */
void list_distorted_sprite(short x1, short y1, short x2, short y2, short x3, short y3, short x4, short y4, unsigned long source, int w, int h)
{
    unsigned short *p = (unsigned short *)(VDP1_VRAM + (list_index << 5));

    p[CMDCTRL] = 0x1002; /* Distorted sprite, RGB mode */
    p[CMDLINK] = 0x0000; /* Jump address */
    p[CMDPMOD] = 0x00A8 | mesh_flag;
    p[CMDCOLR] = 0;
    p[CMDSRCA] = source;
    p[CMDSIZE] = (((w>>3)&0x3F)<<8)|(h&0xFF);
    p[CMDXA]   = x1;
    p[CMDYA]   = y1;
    p[CMDXB]   = x2;
    p[CMDYB]   = y2;
    p[CMDXC]   = x3;
    p[CMDYC]   = y3;
    p[CMDXD]   = x4;
    p[CMDYD]   = y4;
    list_index++;
}




/* Define line filled with spaces in order to erase a line. */
char* empty_line = "                                     ";




/* CDC test. */
void cdrom_test(void);


typedef struct fread_callback_t
{
    unsigned char row; /* Row where to write progress. */
} fread_callback_t;
fread_callback_t _fread_callback_data;
void fread_callback(unsigned long data_transfer, unsigned long data_total, void* callback_data)
{
    char txt[256];
    fread_callback_t* dat = (fread_callback_t*)callback_data;

    sprintf(txt, "Loading data = %3d / 100 ...    ", (int)((100*data_transfer)/data_total));
    scd_logout("loading = >%s<", txt); scd_logflush();
    _printf(2, dat->row, COLOR_SILVER, txt);
}



void vbt_reset(void)
{
    scd_logout("Set-up VDP2 STT", 0);
    TVMD = 0x0000; //OFF
#include "cd/log_00000384_reg.txt"
(*(volatile unsigned short *)(0x25F80000)) = 0x8001; // TVMD
    scd_logout("Set-up VDP2 END", 0);
}



#if 0
#include "data/output.h"
#include "data/output.c"
void vbt_load(void)
{
    unsigned long len;
    scd_logout("Set-up VDP2 VRAM STT", 0);
    len = decomp_exec((unsigned char*)D0000384_CMP_dat, D0000384_CMP_SIZ, (unsigned char*)VDP2_D1_STT);
    scd_logout("Set-up VDP2 VRAM END (%d bytes)", len);

    scd_logout("Set-up VDP2 CRAM STT", 0);
    len = decomp_exec((unsigned char*)D1000384_CMP_dat, D1000384_CMP_SIZ, (unsigned char*)VDP2_D2_STT);
    scd_logout("Set-up VDP2 CRAM END (%d bytes)", len);
}
#else
void vbt_load(void)
{
    sc_file_t* fh = NULL;
    unsigned long len;

    /* VRAM */
    scd_logout("Set-up VDP2 VRAM STT", 0);
    fh = sc_fopen(SC_FILE_CDROM, VDP2_D1_FIL);
    len = sc_fread(fh, 0/*offset*/, VDP2_D1_LEN/*size*/, (void*)VDP2_D1_STT/*buff*/);
    sc_fclose(fh);
    scd_logout("Set-up VDP2 VRAM END (%d bytes)", len);

    /* CRAM */
    scd_logout("Set-up VDP2 CRAM STT", 0);
    fh = sc_fopen(SC_FILE_CDROM, VDP2_D2_FIL);
    len = sc_fread(fh, 0/*offset*/, VDP2_D2_LEN/*size*/, (void*)VDP2_D2_STT/*buff*/);
    sc_fclose(fh);
    scd_logout("Set-up VDP2 CRAM END (%d bytes)", len);
(*(volatile unsigned short *)(0x25F80000)) = 0x8001; // TVMD
}
#endif


unsigned long int strtoul (const char *nptr, char **endptr,int base);
void vbt_command_parse(char* data, int datalen, int* argc, char** argv)
{
    int i, j;

    /* If needed, append first line to argv. */
    if((data[0] > ' ') && (data[0] != '#'))
    {
        argv[(*argc)] = data; (*argc)++;
    }
    for(i=0; i<(datalen-2); i++)
    {
        /* If found, append command. */
        if((data[i  ] < ' ')
        && (data[i+1] > ' '))
        {
            /* Ignore comments */
            if(data[i+1] != '#')
            {
                argv[(*argc)] = data + i+1; (*argc)++;
            }
        }
        /* Remove non displayable characters. */
        if(data[i] < ' ') data[i] = '\0';
    }
    /* Remove non displayable characters. */
    j=i;
    for(i=j; i<datalen; i++)
    {
        if(data[i] < ' ') data[i] = '\0';
    }
}




void _main(void)
{
    pad_t pad;
    /* SatCom: initialization. */
    sc_init();


    vdp_init();
    conio_init();
    pad_init();

    /* Set up VDP1 to appear under VDP2 graphics (0=hide, 1-7=visible) */
    PRISA = 0x0101;
    PRISB = 0x0101;
    PRISC = 0x0101;
    PRISD = 0x0101;

    /* Set up VDP1 registers */
    TVMR = 0x0000;
    FBCR = 0x0000;
    PTMR = 0x0002;
    EWDR = COLOR(7,7,7);    /* Erase color: gray */
    EWLR = 0x0000;          /* Erase range: (0,0) -> (352,240) */
    EWRR = 0x50DF;



    /* Print message */
    int row=1, datarow;

    _printf(2, row++, COLOR_TEAL, "=/      VBT EMU DEBUG PROGRAM     \\=");
    _printf(2, row++, COLOR_YELLOW, "= By cafe-alpha (ppcenter.free.fr) =");
    _printf(2, row++, COLOR_YELLOW, "Build on " RELEASE_DATE);
    row++;
    _printf(2, row++, COLOR_SILVER, "From VDP1EX by Charles MacDonald");
    _printf(2, row++, COLOR_SILVER, "WWW: http://cgfm2.emuviews.com");
    row++;

    /* Help screen end. */
    row++;
    datarow=row;


    /* Screen on */
    TVMD = 0x8000;
    TVMR = 0x0000;
    FBCR = 0x0001;
    EWRR = 0x50DF;


    /* Initialize display list */
    list_clear();
    list_end();


    _printf(2, row++, COLOR_SILVER, "Type A then B to test result  ");
    _printf(2, row++, COLOR_SILVER, "Other button:interactive debug");
    do
    {
        sc_check();
        pad_read(&pad);
    } while(pad.new == 0);


    /* Display stuff before and after. */
    if(pad.new & PAD_A)
    {
        /* Before. */
        vbt_reset();
        vbt_load();

        /* Wait for key. */
        do
        {
            sc_check();
            pad_read(&pad);
        } while((pad.new & PAD_B) == 0);

        /* After. */
        (*(volatile unsigned short *)(0x25F80018)) = 0x5302; // CYCB0L (0xF2FF -> 0x5302)

        /* Wait for key. */
        sc_usleep(3 * 1000 * 1000);
        do
        {
            sc_check();
            pad_read(&pad);
        } while(pad.new == 0);
    }




    /*------------------------------------------------------------------------*/
    /* VBT emu tayst STT */

    /* Set-up VDP2. */
    vbt_reset();

    /* Load VRAM and CRAM to VDP2 memory. */
    vbt_load();


    int open_type = SC_FILE_CDROM;
    while(1)
    {
        /* Parse command file. */
        sc_file_t* fh = NULL;
        unsigned long len;
        int i;

        char cmd_file[4096];
        int argc;
        char* argv[2048];

        scd_logout("Read command file STT", 0); scd_logflush();
        memset((void*)cmd_file, 0, sizeof(cmd_file));
        fh = sc_fopen(open_type, "COMMANDS.TXT");
        len = sc_fread(fh, 0/*offset*/, sizeof(cmd_file)-1/*size*/, (void*)cmd_file/*buff*/);
        sc_fclose(fh);
        cmd_file[sizeof(cmd_file)-1] = '\0';
        scd_logout("Read command file END (%d bytes)", len); scd_logflush();

        /* Parse commands file. */
        argc = 1;
        argv[0] = "";
        vbt_command_parse(cmd_file, len, &argc, argv);
        for(i=1; i<argc; i++)
        {
            scd_logout("Command[%d] = *%s*", i, argv[i]); scd_logflush();

            if(!strcmp(argv[i], "reset"))
            {
                scd_logout("Perform VDP2 reset ...", 0); scd_logflush();
                vbt_reset();
            }
            else if(!strcmp(argv[i], "load"))
            {
                scd_logout("Load data to VDP2 ...", 0); scd_logflush();
                vbt_load();
            }
            else if(!strcmp(argv[i], "wait"))
            {
                int w = strtoul(argv[i+1], NULL, 0);
                scd_logout("Wait %d msec ...", w); scd_logflush();
                sc_usleep(w * 1000);
                i+=1;
            }
            else if(!strcmp(argv[i], "set"))
            {
                volatile unsigned short * adr = (volatile unsigned short *)strtoul(argv[i+1], NULL, 0);
                unsigned long  val = strtoul(argv[i+2], NULL, 0);
                scd_logout("Set 0x%04X to address 0x%08X ...", val, adr); scd_logflush();
                *adr = val;
                i+=2;
            }
            else
            {
                scd_logout("Error: unknown command: %s", argv[i]); scd_logflush();
            }
        }

        while(1)
        {
            char buff1[16];
            char buff2[16];
            int i, j;

            /* Prompt user. */
            char* ret = scd_prompt("Type e to exit.");

            if(ret[0] == 'e')
            { /* Exit to multiplayer. */
                int (*pFun)(void);
                pFun = (int(*)(void))0x600026C;
                (*pFun)();
            }
            if(ret[0] == 'r')
            {
                scd_logout("Perform VDP2 reset ...", 0); scd_logflush();
                vbt_reset();
            }
            else if(ret[0] == 'l')
            {
                scd_logout("Load data to VDP2 ...", 0); scd_logflush();
                vbt_load();
            }
            else if(ret[0] == 'w')
            {
                i = 0; j = 1;
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = '\0';
                int w = strtoul(buff1, NULL, 0);
                scd_logout("Wait %d msec ...", w); scd_logflush();
                sc_usleep(w * 1000);
            }
            else if(ret[0] == 's')
            {
                i = 0; j = 1;
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = '\0';

                i = 0;
                buff2[i++] = ret[j++];
                buff2[i++] = ret[j++];
                buff2[i++] = ret[j++];
                buff2[i++] = ret[j++];
                buff2[i++] = ret[j++];
                buff2[i++] = ret[j++];
                buff2[i++] = '\0';

                volatile unsigned short * adr = (volatile unsigned short *)strtoul(buff1, NULL, 0);
                unsigned long  val = strtoul(buff2, NULL, 0);
                scd_logout("Set 0x%04X to address 0x%08X ...", val, adr); scd_logflush();
                *adr = val;
            }
            else if(ret[0] == 'S')
            {
                i = 0; j = 1;
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = ret[j++];
                buff1[i++] = '\0';

                volatile unsigned short * adr = (volatile unsigned short *)strtoul(buff1, NULL, 0);
                unsigned long val;
                do
                {
                    val = rand()&0xFFFF;
                    scd_logout("Set 0x%04X to address 0x%08X ...", val, adr); scd_logflush();
                    *adr = val;

                    sc_check();

                    pad_read(&pad);
                } while(pad.new == 0);
            }
            else
            {
                scd_logout("Error: unknown command: %s", argv[i]); scd_logflush();
            }
        }
        open_type = SC_FILE_PC;
    }
    /* VBT emu tayst END */
    /*------------------------------------------------------------------------*/



    vdp_shutdown();
    pad_shutdown();
}

