#include "types.h"

#include "vdp.h"

#include "vdp_dma.h"
#include "vdp_tile.h"
#include "vdp_pal.h"
#include "vdp_spr.h"

#include "font.h"


static u8 regValues[0x13];


void VDP_init()
{
    volatile u16 *pw;
    u16 i;

    regValues[0x00] = 0x14; /* reg. 0 - Enable HBL */
    regValues[0x01] = 0x74; /* reg. 1 - Enable display, VBL, DMA + VCell size */
    regValues[0x02] = 0x30; /* reg. 2 - Plane A =$30*$400=$C000 */
    regValues[0x03] = 0x2C; /* reg. 3 - Window  =$2C*$400=$B000 */
    regValues[0x04] = 0x07; /* reg. 4 - Plane B =$7*$2000=$E000 */
    regValues[0x05] = 0x5E; /* reg. 5 - sprite table begins at $BC00=$5E*$200 */
    regValues[0x06] = 0x00; /* reg. 6 - not used */
    regValues[0x07] = 0x00; /* reg. 7 - Background Color number*/
    regValues[0x08] = 0x00; /* reg. 8 - not used */
    regValues[0x09] = 0x00; /* reg. 9 - not used */
    regValues[0x0A] = 0x01; /* reg 10 - HInterrupt timing */
    regValues[0x0B] = 0x00; /* reg 11 - $0000abcd a=extr.int b=vscr cd=hscr */
    regValues[0x0C] = 0x81; /* reg 12 - hcell mode + shadow/highight + interlaced mode (40 cell, no shadow, no interlace)*/
    regValues[0x0D] = 0x2E; /* reg 13 - HScroll Table = $B800 */
    regValues[0x0E] = 0x00; /* reg 14 - not used */
    regValues[0x0F] = 0x02; /* reg 15 - auto increment data */
    regValues[0x10] = 0x11; /* reg 16 - scrl screen v&h size (64x64) */
    regValues[0x11] = 0x00; /* reg 17 - window hpos */
    regValues[0x12] = 0xFF; /* reg 18 - window vpos */

    /* set registers */
    pw = (u16 *) GFX_CTRL_PORT;
    for (i = 0x00; i < 0x13; i++) *pw = 0x8000 | (i << 8) | regValues[i];

    /* reset video memory */
    VDP_DoVRamDMAFill(0, 0xFFFF, 0);
    /* wait for DMA completion */
    VDP_waitDMACompletion();

    /* load default font */
    VDP_loadFont((u32*) font_base, 1);

    /* load defaults palettes */
    VDP_setPalette(0, (u16*) palette_grey);
    VDP_setPalette(1, (u16*) palette_red);
    VDP_setPalette(2, (u16*) palette_green);
    VDP_setPalette(3, (u16*) palette_blue);

    /* reset sprite struct */
    VDP_resetSprites();
}


u8 VDP_getReg(u16 reg)
{
    if (reg < 0x13) return regValues[reg];
    else return 0;
}

void VDP_setReg(u16 reg, u8 value)
{
    volatile u16 *pw;

    if (reg < 0x13) regValues[reg] = value;

    pw = (u16 *) GFX_CTRL_PORT;
    *pw = 0x8000 | (reg << 8) | value;
}


u16 VDP_getHeight()
{
    return 224;
}

u16 VDP_getWidth()
{
    if (regValues[0x0C] & 0x81) return 320;
    else return 256;
}

void VDP_setWidth256()
{
    volatile u16 *pw;

    regValues[0x0C] &= ~0x81;

    pw = (u16 *) GFX_CTRL_PORT;
    *pw = 0x8C00 | regValues[0x0C];
}

void VDP_setWidth320()
{
    volatile u16 *pw;

    regValues[0x0C] |= 0x81;

    pw = (u16 *) GFX_CTRL_PORT;
    *pw = 0x8C00 | regValues[0x0C];
}


u16 VDP_getPlanWidth()
{
    return ((regValues[0x10] & 0xF) + 1) << 5;
}

u16 VDP_getPlanHeight()
{
    return ((regValues[0x10] >> 4) + 1) << 5;
}

void VDP_setPlanSize(u16 w, u16 h)
{
    volatile u16 *pw;

    regValues[0x10] = (((h >> 5) - 1) << 4) | (((w >> 5) - 1) << 0);

    pw = (u16 *) GFX_CTRL_PORT;
    *pw = 0x9000 | regValues[0x10];
}


u16 VDP_getAutoInc()
{
    return regValues[0x0F];
}

void VDP_setAutoInc(u8 value)
{
    volatile u16 *pw;

    regValues[0x0F] = value;

    pw = (u16 *) GFX_CTRL_PORT;
    *pw = 0x8F00 | value;
}


void VDP_setHInterrupt(u8 value)
{
    volatile u16 *pw;

    if (value) regValues[0x00] |= 0x10;
    else regValues[0x00] &= ~0x10;

    pw = (u16 *) GFX_CTRL_PORT;
    *pw = 0x8000 | regValues[0x00];
}

void VDP_setHilightShadow(u8 value)
{
    volatile u16 *pw;

    if (value) regValues[0x0C] |= 0x08;
    else regValues[0x0C] &= ~0x08;

    pw = (u16 *) GFX_CTRL_PORT;
    *pw = 0x8C00 | regValues[0x0C];
}


void VDP_waitDMACompletion()
{
    while(GETVDPSTATUS(VDP_DMABUSY_FLAG));
}

void VDP_waitFIFOEmpty()
{
    while(!GETVDPSTATUS(VDP_FIFOEMPTY_FLAG));
}


void VDP_waitVSync()
{
    volatile u16 *pw;
    u16 vdp_state;

    vdp_state = VDP_VBLANK_FLAG;
    pw = (u16 *) GFX_CTRL_PORT;

    while (vdp_state & VDP_VBLANK_FLAG) vdp_state = *pw;
    while (!(vdp_state & VDP_VBLANK_FLAG)) vdp_state = *pw;
}


void VDP_blit()
{
    // do all update operation
    VDP_updateSprites();
}
