
#include "shared.h"



/* Global data */
vuint16 *vdp1_vram = (vuint16 *)VDP1_VRAM;
vuint16 *vdp1_fb   = (vuint16 *)VDP1_FRAMEBUFFER;
vuint16 *vdp2_vram = (vuint16 *)VDP2_VRAM;
vuint16 *vdp2_cram = (vuint16 *)VDP2_CRAM;
vuint16 *vdp2_reg  = (vuint16 *)VDP2_REGISTER_BASE;


/*
 * Global variables stuff.
 * Warning : globals are not directly usable from outside.
 */
uint16 _list_index = 0; /* Offset into the command list */
uint16 _mesh_flag  = 0; /* Global mesh enable flag */
void vdp_init(void)
{
    //void vdp2_init(void)
    int i;
    TVMD = 0x0000;

    *((unsigned short *) 0x25f8000e) = *((unsigned short *) 0x25f8000e) & (~0x3000);

    // Map Offset Register: Bitmap screen will be located at VRAM offset 0
    /*0x25f8003c*/MPOFN = 0;

    // Character Control Register: 256 colors, enable NBG0 as a bitmap
    // |8 for 1024x256 bitmap
    /*0x25f80028*/CHCTLA = 0x0012|8;

    // Screen Scroll Value Registers: No scroll
    /*0x25f80070*/SCXIN0 = 0;
    /*0x25f80072*/SCXDN0 = 0;
    /*0x25f80074*/SCYIN0 = 0;

    // Screen Display Enable Register: Invalidate the transparency code for
    // NBG0 and display NBG0
    /*0x25f80020*/BGON = 0x0001;

    /* Clear VRAM */
    for(i = 0; i < 0x40000; i++)
        vdp2_vram[i] = 0x0000;

    /* Clear CRAM */
    for(i = 0; i < 0x0800; i++)
        vdp2_cram[i] = 0x0000;



    //void vdp1_init(void)
    /* 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 = 0x0003;       /* VPD1 refresh : "Manual mode (change)"          */
    PTMR = 0x0002;       /* Starts drawing automatically with frame change */
    EWDR = COLOR(0,0,0); /* Erase color: black                             */
    EWLR = 0x0000;       /* Erase range: (0,0) -> (352,240)                */
    EWRR = 0x50DF;

    /* Initialize display list */
    _list_index = 0;
    _mesh_flag  = 0;
    vdp1_start(1/*single buff*/, 500);

}

void vdp_shutdown(void)
{
    int i;

    TVMD = 0x0000;

    for(i = 0; i < 0x100; i++)
        vdp2_reg[i] = 0x0000;

    for(i = 0; i < 0x40000; i++)
        vdp2_vram[i] = 0x0000;

    for(i = 0; i < 0x0800; i++)
        vdp2_cram[i] = 0x0000;
}

void wait_hblank_in(void)
{
    while((TVSTAT & 4) == 0);
}

void wait_hblank_out(void)
{
    while((TVSTAT & 4) == 4);
}

void wait_vblank_in(void)
{
    while((TVSTAT & 8) == 0);
}

void wait_vblank_out(void)
{
    while((TVSTAT & 8) == 8);
}

void wait_vblank(void)
{
    while((TVSTAT & 8) == 0);
    while((TVSTAT & 8) == 8);
}

void wait_hblank(void)
{
    while((TVSTAT & 4) == 0);
    while((TVSTAT & 4) == 4);
}




/**********************************************************************
 * VDP1 Commands stuff                                               *
**********************************************************************/


/* Macros */
/* Get the address of a command in the VDP1 VRAM */
#define CMD_ADDRESS(index) ((uint16 *)(VDP1_VRAM + ((index) << 5)))




/* Global variables "get" functions */
uint16 vdp1_list_get_index(void)
{
    return _list_index;
}
void vdp1_list_set_index(uint16 i)
{
    _list_index = i;
}
/* Set or clear the global mesh processing flag */
void vdp1_mesh(int n)
{
    _mesh_flag=(n?0x0100:0x0000);
}



/*
 * Set buffer count (1 or 2).
 * commands_count is the number of commands available
 * (i.e. depends of your memory management).
 * Note: command wrote until calling vdp1_start are considered
 *       as buffer common, and will not be overwritten after calling
 *       vdp1_flip (usefull for setting local coords, etc once for all).
 */
uint16 _buffer_count    = 1;      // buffer count (user set)
uint16 _buffer_id       = 0;      // current buffer ID (0/1)
uint16 _jmp_cmd_id      = 0;      // jump command ID (used in double buffering)
uint16 _buffer_start[2] = {0, 0}; // buffer start address
/*
 * Example of double buffering configuration.
 * C C C C J b b b b b b E _ _ _ _ B B B B B B B E
 * | C    : common commands.
 * | J    : jump command in order to flip buffers.
 * | b(B) : Buffer 1(2) commands, start from _buffer_start[0(1)].
 * | E    : end commands.
 * | _    : unsused commands.
 */
void vdp1_start(int buffer_count, int commands_count)
{
    uint16 *p;
    
    _buffer_count = (buffer_count == 1 ? 1 : 2);

    if(_buffer_count == 1)
    { /* Single buffering case. */

        _buffer_start[0] = _list_index;
        _buffer_start[1] = _buffer_start[0];

    }
    else if(_buffer_count == 2)
    { /* Double buffering case. */

        /* Next command: jump used for flipping. */
        _jmp_cmd_id = _list_index;
        /* Since no buffer are defined yet, don't jump. */
        p = CMD_ADDRESS(_jmp_cmd_id);
        p[CMDCTRL] = 0x8000; // END

        /* Set buffers start address. */
        _buffer_start[0] = _list_index+1;
        _buffer_start[1] = _buffer_start[0] + (commands_count - _buffer_start[0])/2;

    }

    /* Set list index to next command. */
    _buffer_id = 0;
    _list_index = _buffer_start[_buffer_id];
}

/*
 * End the current command list, and prepares the second buffer for writing.
 * (in single buffering, return to the begining of the current list, so that 
 *  next commands will overwrite the commands written so far)
 * Return polygons count.
 */
unsigned long vdp1_flip(void)
{
    unsigned long ret;
    uint16 *p;

    /* Set polygon count. */
    ret = _list_index - _buffer_start[_buffer_id];

    /* Terminate the current command list. */
    p = CMD_ADDRESS(_list_index);
    p[CMDCTRL] = 0x8000; // END

    /* Double buffering stuff. */
    if(_buffer_count == 2)
    {
        /* Jump. */
        p = CMD_ADDRESS(_jmp_cmd_id);
        // VDP1 "skip assign" command, ST-013-R3-061694.pdf, page72
        p[CMDCTRL] = 0x5000; // Skip assign
        p[CMDLINK] = (_buffer_start[_buffer_id]) << 2;

        /* Flip. */
        _buffer_id = 1-_buffer_id;
    }

    /* Reset list index. */
    _list_index = _buffer_start[_buffer_id];

    return ret;
}



/* Add system clip to command list */
void vdp1_add_system_clip(int16 x, int16 y)
{
    uint16 *p;

    p = CMD_ADDRESS(_list_index);

    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 vdp1_add_user_clip(int16 x1, int16 y1, int16 x2, int16 y2)
{
    uint16 *p;

    p = CMD_ADDRESS(_list_index);

    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 vdp1_add_local_coords(int16 x, int16 y)
{
    uint16 *p;

    p = CMD_ADDRESS(_list_index);

    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 vdp1_add_polyline(int16 x1, int16 y1, int16 x2, int16 y2, int16 x3, int16 y3, int16 x4, int16 y4, uint16 color)
{
    uint16 *p;

    p = CMD_ADDRESS(_list_index);

    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 vdp1_add_polygon(int16 x1, int16 y1, int16 x2, int16 y2, int16 x3, int16 y3, int16 x4, int16 y4, uint16 color, uint32 grda)
{
    uint16 *p;

    p = CMD_ADDRESS(_list_index);

    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 vdp1_add_distorted_sprite(int16 x1, int16 y1, int16 x2, int16 y2, int16 x3, int16 y3, int16 x4, int16 y4, uint32 source, int w, int h)
{
    uint16 *p;

    p = CMD_ADDRESS(_list_index);

    p[CMDCTRL] = 0x0002; /* Distorted sprite, RGB mode */
    p[CMDLINK] = 0x0000; /* Jump address */
    //p[CMDPMOD] = 0x00A8 | _mesh_flag; // see below
    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;


    /* Should set HSS ? */
    long d1 = 0;
    long d2 = (w*w + h*h)<<1;
    long tmp;
    int i;
    for(i=0; i<8; i+=2)
    {
        // D1 += (x'-x)^2
        tmp = p[CMDXA + ((i+2 + 0/*X*/)&0x7)] - p[CMDXA + ((i+0 + 0/*X*/)&0x7)];
        d1 += tmp * tmp;
        // D1 += (y'-y)^2
        tmp = p[CMDXA + ((i+2 + 1/*Y*/)&0x7)] - p[CMDXA + ((i+0 + 1/*Y*/)&0x7)];
        d1 += tmp * tmp;
    }

    // If d1 < d2* a/b (b>a)
    // Note: in order to avoid overflow, respect 0 < _A < _B < 100
#define _A 1
#define _B 4
    if((d1 * _B) < (d2 * _A))
    { /* HSS ON */
        p[CMDPMOD] = 0x00A8 | _mesh_flag | 1<<12/*HSS*/;
        //p[CMDCTRL] = 0x4002; /* Skip next. */
    }
    else
    { /* HSS OFF */
        p[CMDPMOD] = 0x00A8 | _mesh_flag;
    }
#undef _A
#undef _B


    _list_index++;
}



