#include "gpulocal.h"

UINT8 primsizeT[256] =
{
     0,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,   /* 00 */
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   /* 10 */
     4,4,4,4,7,7,7,7,5,5,5,5,9,9,9,9,   /* 20 */
     6,6,6,6,9,9,9,9,8,8,8,8,12,12,12,12,/* 30 */
     3,3,3,3,0,0,0,0,5,5,5,5,6,6,6,6,   /* 40 */
     4,4,4,4,0,0,0,0,7,7,7,7,9,9,9,9,   /* 50 */
     3,3,3,3,4,4,4,4,2,2,2,2,0,0,0,0,   /* 60 */
     2,2,2,2,3,3,3,3,2,2,2,2,3,3,3,3,   /* 70 */
     4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   /* 80 */
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   /* 90 */
     3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   /* a0 */
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   /* b0 */
     3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   /* c0 */
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   /* d0 */
     0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,   /* e0 */
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   /* f0 */
};

static int gpu_unknown(UINT32 *prim);
static int gpu_01(UINT32 *prim);
static int gpu_LoadImage(UINT16 *sp);
static int gpu_MoveImage(UINT16 *sp);
static int gpu_StoreImage(UINT16 *sp);
static int gpu_e1(UINT32 *prim);
static int gpu_e2(UINT32 *prim);
static int gpu_e3(UINT32 *prim);
static int gpu_e4(UINT32 *prim);
static int gpu_e5(UINT32 *prim);
static int gpu_e6(UINT32 *prim);

int (*primfunc[256])() = {
     /* 00 */
     gpu_unknown,gpu_01,gpu_fill,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* 10 */
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* 20 */
     gpu_polyF3,gpu_polyF3,gpu_polyF3,gpu_polyF3,
     gpu_polyFT3,gpu_polyFT3,gpu_polyFT3,gpu_polyFT3,
     gpu_polyF4,gpu_polyF4,gpu_polyF4,gpu_polyF4,
     gpu_polyFT4,gpu_polyFT4,gpu_polyFT4,gpu_polyFT4,
     /* 30 */
     gpu_polyG3,gpu_polyG3,gpu_polyG3,gpu_polyG3,
     gpu_polyGT3,gpu_polyGT3,gpu_polyGT3,gpu_polyGT3,
     gpu_polyG4,gpu_polyG4,gpu_polyG4,gpu_polyG4,
     gpu_polyGT4,gpu_polyGT4,gpu_polyGT4,gpu_polyGT4,
     /* 40 */
     gpu_lineF2,gpu_lineF2,gpu_lineF2,gpu_lineF2,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_lineF,gpu_lineF,gpu_lineF,gpu_lineF,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* 50 */
     gpu_lineG2,gpu_lineG2,gpu_lineG2,gpu_lineG2,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_lineG,gpu_lineG,gpu_lineG,gpu_lineG,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* 60 */
     gpu_tile,gpu_tile,gpu_tile,gpu_tile,
     gpu_sprt,gpu_sprt,gpu_sprt,gpu_sprt,
     gpu_tile1,gpu_tile1,gpu_tile1,gpu_tile1,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* 70 */
     gpu_tile8,gpu_tile8,gpu_tile8,gpu_tile8,
     gpu_sprt8,gpu_sprt8,gpu_sprt8,gpu_sprt8,
     gpu_tile16,gpu_tile16,gpu_tile16,gpu_tile16,
     gpu_sprt16,gpu_sprt16,gpu_sprt16,gpu_sprt16,
     /* 80 */
     gpu_MoveImage,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* 90 */
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* a0 */
     gpu_LoadImage,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* b0 */
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* c0 */
     gpu_StoreImage,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* d0 */
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* e0 */
     gpu_unknown,gpu_e1,gpu_e2,gpu_e3,
     gpu_e4,gpu_e5,gpu_e6,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     /* f0 */
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
     gpu_unknown,gpu_unknown,gpu_unknown,gpu_unknown,
};

static short clutflg[0x8000/16];
static long tpageflg;

GPU_Type  gpu;
INT16    *vram;
/*
     gpu status
    0 - 10 : draw mode (e0 prim )
    11,12: mask mode
    12-15: ??
    16 - 22: disp mode
     23: Display (1=off)
    24-25: ??
     26: GPU ready (1=ready)
     27: storeimage ready (1=ready)
     28: command ready (1=ready)
     29-30: DMA mode
    31: in interlace mode, 1:odd 0:even
*/

void GPU_Update(void)
{
/* interlace odd/even */
    gpu.status^=0x80000000;

/* Update Image */
    if (toUpdate)
    {
        screen_update();
        toUpdate = 0;
    }
}

UINT32 GP1_Read(void)
{
     return (gpu.status | 0x1c000000) & ~0x00480000;
     // isinterとheight=480 をoff これがあるとinterlase時にwaitするので
     /* GPU_Idle | GPU_cmd_ready | img ready */
      //|0x800;
     /* nazo */
}

UINT32 GP0_Read(void)
{
     if (inTrans==VRAM2MEM) {
          int tmp;
          tmp = pvram[px];
          if (++px>=x_end) {
               px = x_start;
               pvram += 1024;
               if (++py>=y_end) inTrans=0;
          }
          tmp |= pvram[px]<<16;
          if (++px>=x_end) {
               px = x_start;
               pvram +=1024;
               if (++py>=y_end) inTrans=0;
          }
          gpu.retval = tmp;
     }
     return gpu.retval;
}

void GP0_Write(UINT32 data)
{
     if (inTrans==MEM2VRAM) {
          pvram[px]=data;
          if (++px>=x_end) {
               px = x_start;
               pvram += 1024;
               if (++py>=y_end) inTrans=0;
          }
          if (inTrans) {
               pvram[px]=data>>16;
               if (++px>=x_end) {
                    px = x_start;
                    pvram += 1024;
                    if (++py>=y_end) inTrans=0;
               }
          }
          if (inTrans==0) {
               vramchange(x_start,y_start,x_end,y_end);
          }
          return;
     }
     if (gpu.primptr) {
          gpu.primdata[gpu.primptr++] = data;
          if (gpu.primptr == gpu.primsize) {
                        gpu.primdata[gpu.primptr] = 0x55555555;
               gpu.primptr = gpu.primsize = 0;
               do_prim(gpu.primdata);
          }
     } else {
          int primsize=primsizeT[data>>24];
          gpu.primdata[0] = data;
          switch(primsize) {
          case 0:
               printf("unknown prim:%08x\n",(int)data);
               break;
          case 1:
               do_prim(gpu.primdata);
               break;
          default:
               gpu.primsize = primsize;
               gpu.primptr = 1;
               break;
          }
     }
}

void GP1_Write(UINT32 code)
{
     switch(code>>24) {
     case 0x00:     /* All Reset */
          gpu.status = 0x14802000;
          /* com ready | GPU ready | display off 
          256x240, NTSC, 15bit, non-interlace,
          tpage = 0, dither off, can't draw to display area, mask off
          */
          gpu.disp.w = gpu.screen.w = 256;
                gpu.disp.h = gpu.screen.h = 240;
                win_resize(256,240);
          break;
     case 0x01:     /* Command Reset */
          gpu.primptr = gpu.primsize = 0;
          break;
     case 0x02:     /* IRQ Reset */
          break;
     case 0x03:     /* Display Mask */
          /* bit 1 to status bit 23 */
          gpu.status = (gpu.status & ~(1<<23)) | ((code&1)<<23);
          break;
     case 0x04:     /* DMA mode */
          /* bit 0,1 to status bit 29,30 */
          gpu.status = (gpu.status & ~(3<<29)) | ((code&3)<<29);

                switch (code) {
                case 0:
                    inTrans = 0;
                    break;
                case 2:
                    inTrans = MEM2VRAM;
                    break;
                case 3:
                    inTrans = VRAM2MEM;
                    break;
                }

          break;
     case 0x05:     /* disp offset */
          gpu.disp.x = code & 0x3ff;
          gpu.disp.y = (code>>10) & 0x3ff;
          break;
     case 0x06: {   /* H start/end */
          int s,e;
          s = code&0xfff;
          e = (code>>12)&0xfff;
          gpu.screen.x = (s - 608)/10;
          gpu.screen.w = (e-s)/10;
          }
          break;
     case 0x07: {   /* V start/end */
          int s,e;
          s = code&0x3ff;
          e = (code>>10)&0x3ff;
          gpu.screen.y = s - PAL?19:16;
          gpu.screen.h = e-s;
          }
          break;
     case 0x08:     /* display mode */
          /* bit 0..5 to status bit 17..22  */
          /* bit    6 to status bit 16      */
          gpu.status = (gpu.status &~0x7f0000) | ((code&0x3f)<<17) | ((code&0x40)<<(16-6));
          { static int dispw[] = {256,320,512,640};
          if (code&0x40) gpu.disp.w = 368;
          else gpu.disp.w = dispw[code&3];
          }
          gpu.disp.h = (code&4)?480:240;
                win_resize(gpu.disp.w,gpu.disp.h);
                toUpdate = 1;
          break;
     case 0x10:     /* GPU Info */
          switch(code&0xffffff) {
          case 3:
          case 4:
          case 5:
               gpu.retval = gpu.info[code&0xffffff];
               break;
          case 7:
               gpu.retval = 2; /* version */
               break;
          }
          break;
     default:
          printf("unknown %x\n",(int)code);
          break;
     }
}

/*
     return true if tpage changed
     and clear changed flag

     check 1page if  4bit texture
     check 2page if  8bit texture
     check 4page if 15bit texture
*/
int tpage_changed(int tpage)
{
     int changed,mask;
     static int mask0[] = {1,3,15};
     /* mode = 0:1page 1:2page 3:4page */

     mask = mask0[(tpage>>7)&3];
     tpage&=31;
     changed = tpageflg &   (mask<<tpage);
     if (changed)  tpageflg &= ~(mask<<tpage);
     return changed;
}

/*
     return true if clut changed
     and clear changed flag
*/
int clut_changed(int clut)
{
     int changed;
     changed = clutflg[clut/16] &   (1<<(clut&16));
     if (changed)  clutflg[clut/16] &= ~(1<<(clut&16));
     return changed;
}

void vramchange(int x0,int y0,int x1,int y1)
{
     int x,y;
     for(y=y0 ;y<y1; y++) {
          for(x=x0/16; x<=(x1-1)/16; x++)
          //   clutflg[x+y*64]=1;
               clutflg[(x+y*64)/16] |= 1<<(x&16);
     }
     for(y=y0/256; y<=(y1-1)/256; y++) {
          for(x=x0/64; x<=(x1-1)/64; x++)
          //   tpageflg[x+y*16]=1;
               tpageflg |= 1<<(x+y*16);
     }
    toUpdate = 1;
}

static int gpu_unknown(UINT32 *prim)
{
    return 1;
}

static int gpu_01(UINT32 *prim)
{
    return 1;
}

static int gpu_LoadImage(UINT16 *sp)
{
     /* LoadImage */
     inTrans = MEM2VRAM;
#ifdef MSB_FIRST
     px = x_start = sp[3];
     py = y_start = sp[2];
     x_end = x_start + sp[5];
     y_end = y_start + sp[4];
#else
     px = x_start = sp[2];
     py = y_start = sp[3];
     x_end = x_start + sp[4];
     y_end = y_start + sp[5];
#endif
     pvram = &vram[py*1024];

    return 3;
}

static int gpu_MoveImage(UINT16 *sp)
{
     int x0,y0,x1,y1,w,h;
#ifdef MSB_FIRST
     x0 = sp[3]&1023;
     y0 = sp[2]& 511;
     x1 = sp[5]&1023;
     y1 = sp[4]& 511;
     w  = sp[7]&1023;
     h  = sp[6]& 511;
#else
     x0 = sp[2]&1023;
     y0 = sp[3]& 511;
     x1 = sp[4]&1023;
     y1 = sp[5]& 511;
     w  = sp[6]&1023;
     h  = sp[7]& 511;
#endif
     move(x0,y0,x1,y1,w,h);
     vramchange(x1,y1,x1+w,y1+h);

    return 4;
}

static int gpu_StoreImage(UINT16 *sp)
{
/* StoreImage */
    inTrans = VRAM2MEM;
#ifdef MSB_FIRST
    px = x_start = sp[3];
    py = y_start = sp[2];
    x_end = x_start + sp[5];
    y_end = y_start + sp[4];
#else
    px = x_start = sp[2];
    py = y_start = sp[3];
    x_end = x_start + sp[4];
    y_end = y_start + sp[5];
#endif
    pvram = &vram[py*1024];

    return 3;
}

static int gpu_e1(UINT32 *prim)
{
    UINT32 code = prim[0];
/* bit 0..10 to status bit 0..10 */

    gpu.status = ( gpu.status &~0x7ff) | (code&0x7ff);
    gpu.tpage = code & 0x7ff;

    return 1;
}

static int gpu_e2(UINT32 *prim)
{
    UINT32 code = prim[0];

    gpu.texwindow.w = ((code&31))*8;
    gpu.texwindow.h = ((code>>5)&31)*8;
    gpu.texwindow.x = 256-((code>>10)&31)*8;
    gpu.texwindow.y = 256-((code>>15)&31)*8;

    return 1;
}

static int gpu_e3(UINT32 *prim)
{
    UINT32 code = prim[0];

    gpu.info[3] = code&0xffffff;
    gpu.clip_min.x = code&0x3ff;
    gpu.clip_min.y = (code>>10)&0x1ff;

    return 1;
}

static int gpu_e4(UINT32 *prim)
{
    UINT32 code = prim[0];

    gpu.info[4] = code&0xffffff;
    gpu.clip_max.x = code&0x3ff;
    gpu.clip_max.y = (code>>10)&0x1ff;

    return 1;
}

static int gpu_e5(UINT32 *prim)
{
    UINT32 code = prim[0];

    gpu.info[5] = code&0xffffff;
    gpu.drawoffset.x = ((int)code<<(32-11))>>(32-11);
    gpu.drawoffset.y = ((int)code<<(32-22))>>(32-11);

    return 1;
}

static int gpu_e6(UINT32 *prim)
{
    UINT32 code = prim[0];
/* bit 0,1 to status bit 11,12 */
    gpu.status = (gpu.status & ~(3<<11)) | ((code&3)<<11);

    return 1;
}