#include "gpulocal.h"

#define SUPPORT_ALPHA
//#define USE_TCACHE
//#define USE_CCACHE

#define   CMASK0    (0x1f)
#define   CMASK1    (0x1f<<5)
#define   CMASK2    (0x1f<<10)
#define   CMASKL0   (0x3f)
#define   CMASKL1   (0x3f<<11)
#define   CMASKL2   (0x3e<<21)

#define   CMASK     (~((1<<15)|(1<<10)|(1<<5)))
#define   CMASKL    (~((1<<31)|(1<<10)|(1<<21)))
#define   COLPACK(c)     ((c)&CMASK1)|(((c)>>16)&(CMASK0|CMASK2))
#define   RGB2COL(r,g,b) (((r)<<23)|((b)<<13)|((g)<<2))
/* 0rrrrrxxxx0bbbbbxxxxx0gggggxxxxx -> 0rrrrrgggggbbbbb */
/* |       |       |       |       | */
/* r=1bit¸­‚Č‚¢ */
#define   COLUNPACK(c)   ((c)&CMASK1)|(((c)&(CMASK0|CMASK2))<<16)
/* 0rrrrrgggggbbbbb -> 0rrrrr00000bbbbb000000ggggg00000 */

#define   FIX_BIT   10
#define   FIX_1     (1<<FIX_BIT)
#define   I2F(a)    ((a)<<FIX_BIT)
#define   F2I(a)    ((a)>>FIX_BIT)
#define   F4I(a)    (((a)>>FIX_BIT) & 0xFF)

static EDGE edge[512*2];

static int masktbl[2048];

#define   VRAMADR(x,y)   vram + (x) + (y)*FRAME_W

static void EdgeF(POINT_F *p0,POINT_F *p1,EDGE *edge)
{
     int dy;
     INT32 x,dx;

     dy = p1->y - p0->y;
     if (dy==0) return;
     if (dy<0) {
          /* right */
          void *t;
          t = p0; p0=p1; p1=t;
          dy = -dy;
     } else {
          /* left */
          edge++;
     }

     edge+=p0->y*2;
     x = I2F(p0->x);
     dx = I2F(p1->x - p0->x)/dy;

     /* clip */
     if (p1->y<YMIN || p0->y>=YMAX) return;
     if (p0->y<YMIN) {
          int ddy = YMIN - p0->y;
          x += dx*ddy;
          edge +=ddy*2;
          if ((dy-=ddy) <= 0) return;
     }
     if (p1->y>=YMAX) {
          dy += YMAX - p1->y;
     }

     do {
          edge->x = x; x+=dx;
          edge+=2;
     } while(--dy);
}

static void EdgeFT(POINT_FT *p0,POINT_FT *p1,EDGE *edge)
{
     int dy;
     INT32 x,u,v,dx,du,dv;

     dy = p1->y - p0->y;
     if (dy==0) return;
     if (dy<0) {
          /* right */
          void *t;
          t = p0; p0=p1; p1=t;
          dy = -dy;
     } else {
          /* left */
          edge++;
     }

     edge+=p0->y*2;
     x = I2F(p0->x);
     u = I2F(p0->u);
     v = I2F(p0->v);
     dx = I2F(p1->x - p0->x)/dy;
     du = I2F(p1->u - p0->u)/dy;
     dv = I2F(p1->v - p0->v)/dy;

     /* clip */
     if (p1->y<YMIN || p0->y>=YMAX) return;
     if (p0->y<YMIN) {
          int ddy = YMIN - p0->y;
          x += dx*ddy;
          u += du*ddy;
          v += dv*ddy;
          edge +=ddy*2;
          if ((dy-=ddy) <= 0) return;
     }
     if (p1->y>=YMAX) {
          dy += YMAX - p1->y;
     }

     do {
          edge->x = x; x+=dx;
          edge->u = u; u+=du;
          edge->v = v; v+=dv;
          edge+=2;
     } while(--dy);
}

#define   ADDCOL(c,dc)            c.r+=dc.r; c.g+=dc.g; c.b+=dc.b
#define   ADDMULCOL(c,dc,m)   c.r+=dc.r*m; c.g+=dc.g*m; c.b+=dc.b*m
#define   CALCDC(dc,c0,c1,d)  dc.r=(c1.r-c0.r)/d;dc.g=(c1.g-c0.g)/d;dc.b=(c1.b-c0.b)/d
#define   COLOR2LCOLOR(lc,c)  lc.r=I2F(c.r);lc.g=I2F(c.g);lc.b=I2F(c.b)

#define   COLOR2PIXEL(c) ((overtbl[c.r>>2]<<10)|(overtbl[c.g>>2]<<5)|(overtbl[c.b>>2]))
#define   LCOL2PIXEL(c)  ((overtbl[F2I(c.r)>>2]<<10)|(overtbl[F2I(c.g)>>2]<<5)|(overtbl[F2I(c.b)>>2]))

#define   MULCOL(c,c0) (\
     (overtbl[(((c>>10)&31)*c0.b)>>(FIX_BIT+7)]<< 10) | \
     (overtbl[(((c>> 5)&31)*c0.g)>>(FIX_BIT+7)]<< 5) | \
     (overtbl[(((c&31)    )*c0.r)>>(FIX_BIT+7)]) )

#define   MULCOL2(c,c0) (\
     (((((c>>10)&31)*c0.b)>>(FIX_BIT+8)) << 10) | \
     (((((c>> 5)&31)*c0.g)>>(FIX_BIT+8)) << 5)  | \
     (((((c&31)    )*c0.r)>>(FIX_BIT+8))) )

/*
static unsigned int overtbl[32*2] = {
      0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
     16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
     31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,
     31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
};
*/
static unsigned int overtbl[32*2] = {
      0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
      8, 8, 9, 9,10,10,11,11,12,12,13,13,14,14,15,15,
     16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,
     24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31
};

static void EdgeG(POINT_G *p0,POINT_G *p1,EDGE *edge)
{
     int  dy;
     INT32  x,dx;
     LCOLOR c0,c1,dc;

     dy = p1->y - p0->y;
     if (dy==0) return;
     if (dy<0) {
          /* right */
          void *t;
          t = p0; p0=p1; p1=t;
          dy = -dy;
     } else {
          /* left */
          edge++;
     }

     edge+=p0->y*2;
     x = I2F(p0->x);
     dx = I2F(p1->x - p0->x)/dy;
     COLOR2LCOLOR(c0,p0->c);
     COLOR2LCOLOR(c1,p1->c);
     CALCDC(dc,c0,c1,dy);

     /* clip */
     if (p1->y<YMIN || p0->y>=YMAX) return;
     if (p0->y<YMIN) {
          int ddy = YMIN - p0->y;
          x += dx*ddy;
          ADDMULCOL(c0,dc,ddy);
          edge +=ddy*2;
          if ((dy-=ddy) <= 0) return;
     }
     if (p1->y>=YMAX) {
          dy += YMAX - p1->y;
     }

     do {
          edge->x = x; x+=dx;
          edge->c = c0; ADDCOL(c0,dc);
          edge+=2;
     } while(--dy);
}

static void EdgeGT(POINT_GT *p0,POINT_GT *p1,EDGE *edge)
{
     int  dy;
     INT32  x,u,v,dx,du,dv;
     LCOLOR c0,c1,dc;

     dy = p1->y - p0->y;
     if (dy==0) return;
     if (dy<0) {
          /* right */
          void *t;
          t = p0; p0=p1; p1=t;
          dy = -dy;
     } else {
          /* left */
          edge++;
     }

     edge+=p0->y*2;
     x = I2F(p0->x);
     u = I2F(p0->u);
     v = I2F(p0->v);
     dx = I2F(p1->x - p0->x)/dy;
     du = I2F(p1->u - p0->u)/dy;
     dv = I2F(p1->v - p0->v)/dy;

     COLOR2LCOLOR(c0,p0->c);
     COLOR2LCOLOR(c1,p1->c);
     CALCDC(dc,c0,c1,dy);

     /* clip */
     if (p1->y<YMIN || p0->y>=YMAX) return;
     if (p0->y<YMIN) {
          int ddy = YMIN - p0->y;
          x += dx*ddy;
          u += du*ddy;
          v += dv*ddy;
          ADDMULCOL(c0,dc,ddy);
          edge +=ddy*2;
          if ((dy-=ddy) <= 0) return;
     }
     if (p1->y>=YMAX) {
          dy += YMAX - p1->y;
     }

     do {
          edge->x = x; x+=dx;
          edge->u = u; u+=du;
          edge->v = v; v+=dv;
          edge->c = c0; ADDCOL(c0,dc);
          edge+=2;
     } while(--dy);
}

#define   ALIGN4(adr)    (((UINT32)(adr)&3)==0)

static void _hline(INT16 *p,int dx,int c)
{
     if (!ALIGN4(p)) { *p++=c; dx--; }
     for(;dx>=2;dx-=2) { *((long*)p)++= c; }
     if (dx) *p++=c;
}

void DrawInit(void)
{
    int i;

    for(i=0;i<2048;i++) {
        int mask = 0;
        if (i&1      ) mask |= 31;
        if (i&(1<<5) ) mask |= 31<<5;
        if (i&(1<<10)) mask |= 31<<10;
        masktbl[i] = mask;
    }
}

#define   BITMODE   (3*4)
#define   ABRFLG    (2*16)
#define   BIT4 0
#define   BIT8 (1*4)
#define   BIT15     (2*4)
#define   T50      0|ABRFLG
#define   T100 1|ABRFLG
#define   T100m     2|ABRFLG
#define   T25      3|ABRFLG
#define   NOSHADE   (1*16)

#define   TEXEL15(tex,u,v)    tex[F4I(u)+F4I(v)*256];

#define   ALPHA50(cc,c2) cc = ((cc&CMASK)+(c2&CMASK))/2
#define   ALPHA100(cc,c2)     { \
     cc = (cc&CMASK) + (c2&CMASK); \
     cc = (cc&CMASK) | masktbl[cc>>5]; }
#define   ALPHA100m(cc,c2) { \
     cc = (c2|~CMASK) - (cc&CMASK); \
     cc = (cc&CMASK) & ~masktbl[cc>>5]; }
#define   ALPHA25(cc,c2) { \
     /* 1100011000111 */ \
     cc = ((cc>>2)&0x18c7) + (c2&CMASK); \
     cc = (cc&CMASK) | masktbl[cc>>5]; }

void DrawF(EDGE *edge,int ymin,int ymax,unsigned int c)
{
     int dx,dy;
     INT16 *p,*p0;

     if ((dy = ymax-ymin) <= 0) return;
     p0 = VRAMADR(0,ymin);
     edge += ymin*2;
     c |= (c<<16);
     do {
          dx = F2I(edge[1].x)-F2I(edge[0].x);
          if (dx) {
               p = p0+F2I(edge[0].x);
               if (F2I(edge[0].x)<XMIN) {
                    int d = XMIN-F2I(edge[0].x);
                    p+=d;
                    dx-=d;
               }
               if (F2I(edge[1].x)>XMAX) {
                    dx -= F2I(edge[1].x)-XMAX;
               }
               if (dx>0) {
                    if (!ALIGN4(p)) *p++=c;
                    for(;dx>=2;dx-=2) { *((long*)p)++= c; }
                    if (dx) *p++=c;
               }
          }
          edge+=2;
          p0+=FRAME_W;
     } while(--dy);
    toUpdate = 1;
}

#define   G_HLINE_A(ALPHA) \
                    do { \
                         int c2 = MULCOL(*p,c0); \
                         ALPHA(c2,*p); \
                         *p=c2; \
                         p++; \
                         ADDCOL(c0,dc); \
                    } while(--dx)

void DrawG(EDGE *edge,int ymin,int ymax,int code)
{
     int dx,dy,mode;
     INT16 *p,*p0;
     LCOLOR c0,dc;

     if ((dy = ymax-ymin) <= 0) return;
     p0 = VRAMADR(0,ymin);
     edge += ymin*2;
     mode = ((gpu.tpage>>5)&3)|((code&2)*16);
     do {
          dx = F2I(edge[1].x)-F2I(edge[0].x);
          if (dx > 0) {
               CALCDC(dc,edge[0].c,edge[1].c,dx);
               c0 = edge[0].c;
               p = p0+F2I(edge[0].x);
               if (F2I(edge[0].x)<XMIN) {
                    int d = XMIN-F2I(edge[0].x);
                    c0.r+=dc.r*d;
                    c0.g+=dc.g*d;
                    c0.b+=dc.b*d;
                    p+=d;
                    dx-=d;
               }
               if (F2I(edge[1].x)>XMAX) {
                    dx -= F2I(edge[1].x)-XMAX;
               }
               if (dx>0)
                    switch (mode) {
                    case T50:
                         G_HLINE_A(ALPHA50);
                         break;
                    case T100:
                         G_HLINE_A(ALPHA100);
                         break;
                    case T100m:
                         G_HLINE_A(ALPHA100m);
                         break;
                    case T25:
                         G_HLINE_A(ALPHA25);
                         break;
                    default:
                         do {
                              *p++= LCOL2PIXEL(c0);
                              ADDCOL(c0,dc);
                         } while(--dx);
                         break;
                    }
          }
          edge+=2;
          p0+=FRAME_W;
     } while(--dy);
    toUpdate = 1;
}

#define   FT_HLINE(TEXEL) \
                    do { \
                         int c = TEXEL(tex,u,v); \
                         if (c) *p=c; \
                         p++; u +=du; v+=dv; \
                    } while(--dx)

#define   FT_HLINE_A(TEXEL,ALPHA) \
                    do { \
                         int c = TEXEL(tex,u,v); \
                         if (c) { \
                              if (c&0x8000) ALPHA(c,*p); \
                              *p = c; \
                         } \
                         p++; u += du; v+=dv; \
                    } while(--dx)

#define   FT_HLINE_A15(TEXEL,ALPHA) \
                    do { \
                         int c = TEXEL(tex,u,v); \
                         if (c) { \
                              int c2 = MULCOL(c,c0); \
                              if (c&0x8000) ALPHA(c2,*p); \
                              *p = c2; \
                         } \
                         p++; u +=du; v+=dv; \
                    } while(--dx)


#define   GT_HLINE(TEXEL) \
                    do { \
                         int c = TEXEL(tex,u,v); \
                         if (c) *p=MULCOL(c,c0); \
                         p++; u +=du; v+=dv; \
                         ADDCOL(c0,dc); \
                    } while(--dx)

#define   GT_HLINE_A(TEXEL,ALPHA) \
                    do { \
                         int c = TEXEL(tex,u,v); \
                         if (c) { \
                              int c2 = MULCOL(c,c0); \
                              if (c&0x8000) ALPHA(c2,*p); \
                                                *p=c2; \
                         } \
                         p++; u +=du; v+=dv; \
                         ADDCOL(c0,dc); \
                    } while(--dx)

void DrawFT(EDGE *edge,int ymin,int ymax,int tpage,int clut,COLOR c0code)
{
     int dx,dy,mode;
     INT16 *p0;
     INT16 *tex;
     LCOLOR c0;

        if (ymin > YMAX) return;
        if (ymax < YMIN) return;
        if (ymin < YMIN) ymin = YMIN;
        if (ymax > YMAX) ymax = YMAX;

     if ((dy = ymax-ymin) <= 0) return;

     if (c0code.r==0x80 && c0code.g==0x80 && c0code.b==0x80)
          c0code.code|=1; /* NOSHADE */

     p0 = VRAMADR(0,ymin);
     edge += ymin*2;
     tex = get_texture(tpage,clut);
     mode = ((tpage>>5)&3)|((c0code.code&3)*16);

     if (!(mode&NOSHADE))
          COLOR2LCOLOR(c0,c0code);

     do {
          dx = F2I(edge[1].x)-F2I(edge[0].x);
          if (dx > 0) {
               INT32 u,v,du,dv;
               INT16 *p;

               u = edge[0].u;
               v = edge[0].v;
               du = (edge[1].u-edge[0].u)/dx;
               dv = (edge[1].v-edge[0].v)/dx;
               p = p0+F2I(edge[0].x);
               if (F2I(edge[0].x)<XMIN) {
                    int d = XMIN-F2I(edge[0].x);
                    p+=d;
                    u = u+du*d;
                    v = v+dv*d;
                    dx-=d;
               }
               if (F2I(edge[1].x)>XMAX) {
                    dx -= F2I(edge[1].x)-XMAX;
               }
               if (dx>0)
               switch(mode) {
               case T50|NOSHADE:
                    FT_HLINE_A(TEXEL15,ALPHA50);
                    break;
               case T100|NOSHADE:
                    FT_HLINE_A(TEXEL15,ALPHA100);
                    break;
               case T100m|NOSHADE:
                    FT_HLINE_A(TEXEL15,ALPHA100m);
                    break;
               case T25|NOSHADE:
                    FT_HLINE_A(TEXEL15,ALPHA25);
                    break;

               case T50:
                    FT_HLINE_A15(TEXEL15,ALPHA50);
                    break;
               case T100:
                    FT_HLINE_A15(TEXEL15,ALPHA100);
                    break;
               case T100m:
                    FT_HLINE_A15(TEXEL15,ALPHA100m);
                    break;
               case T25:
                    FT_HLINE_A15(TEXEL15,ALPHA25);
                    break;
               default:
                    if (mode & NOSHADE) FT_HLINE(TEXEL15);
                    else
                    do {
                         int c = TEXEL15(tex,u,v);
                         if (c&0x7FFF) *p=MULCOL(c,c0);
                         p++; u +=du; v+=dv;
                    } while(--dx);

                    break;
               }
          }
          edge+=2;
          p0+=FRAME_W;
     } while(--dy);
    toUpdate = 1;
}

void DrawGT(EDGE *edge,int ymin,int ymax,int tpage,int clut,int code)
{
     int dx,dy,mode;
     INT32 u,v,du,dv;
     INT16 *p,*p0;
     INT16 *tex;
     LCOLOR c0,dc;

        if (ymin > YMAX) return;
        if (ymax < YMIN) return;
        if (ymin < YMIN) ymin = YMIN;
        if (ymax > YMAX) ymax = YMAX;

     if ((dy = ymax-ymin) <= 0) return;
     p0 = vram+ymin*FRAME_W;
     edge += ymin*2;

     tex = get_texture(tpage,clut);
     mode = ((tpage>>5)&3)|((code&3)*16);

     do {
          dx = F2I(edge[1].x)-F2I(edge[0].x);
          if (dx > 0) {
               u = edge[0].u;
               v = edge[0].v;
               du = (edge[1].u-edge[0].u)/dx;
               dv = (edge[1].v-edge[0].v)/dx;
               p = p0+F2I(edge[0].x);
               CALCDC(dc,edge[0].c,edge[1].c,dx);
               c0 = edge[0].c;
               if (F2I(edge[0].x)<XMIN) {
                    int d = XMIN-F2I(edge[0].x);
                    p+=d;
                    u+=du*d;
                    v+=dv*d;
                    c0.r+=dc.r*d;
                    c0.g+=dc.g*d;
                    c0.b+=dc.b*d;
                    dx-=d;
               }
               if (F2I(edge[1].x)>XMAX) {
                    dx -= F2I(edge[1].x)-XMAX;
               }
               if (dx>0)
               switch(mode) {
               case T50:
                    GT_HLINE_A(TEXEL15,ALPHA50);
                    break;
               case T100:
                    GT_HLINE_A(TEXEL15,ALPHA100);
                    break;
               case T100m:
                    GT_HLINE_A(TEXEL15,ALPHA100m);
                    break;
               case T25:
                    GT_HLINE_A(TEXEL15,ALPHA25);
                    break;
               default:
                    GT_HLINE(TEXEL15);
                                break;
               }
          }
          edge+=2;
          p0+=FRAME_W;
     } while(--dy);
    toUpdate = 1;
}

#define   ABS(x)  (((x)>0)?(x):-(x))

#define FX(yy)   (yy*dx/dy - x)
#define FY(xx)   (xx*dy/dx - y)

void DrawLineF(POINT_F *p0,POINT_F *p1,int c)
{
     int x,y,dx,dy;
     INT16 *p;

// printf("old: %d %d %d %d\n",p0->x,p0->y,p1->x,p1->y);
     dx = p1->x - p0->x;
        y=1;
        if (dx<0) y = -1;
        dx += y;

     dy = p1->y - p0->y;
        y=1;
        if (dy<0) y = -1;
        dy += y;

// CLIPPING
        x = p0->y*dx/dy - p0->x;
        y = p0->x*dy/dx - p0->y;

        if (p0->y < YMIN) {     // p0
            p0->x = FX(YMIN);
            p0->y = YMIN;
        } else
        if (p0->y > YMAX) {
            p0->x = FX(YMAX);
            p0->y = YMAX;
        }
        if (p0->x < XMIN) {
            p0->y = FY(XMIN);
            p0->x = XMIN;
        } else
        if (p0->x > XMAX) {
            p0->y = FY(XMAX);
            p0->x = XMAX;
        }
        if (p1->y < YMIN) {     // p1
            p1->x = FX(YMIN);
            p1->y = YMIN;
        } else
        if (p1->y > YMAX) {
            p1->x = FX(YMAX);
            p1->y = YMAX;
        }
        if (p1->x < XMIN) {
            p1->y = FY(XMIN);
            p1->x = XMIN;
        } else
        if (p1->x > XMAX) {
            p1->y = FY(XMAX);
            p1->x = XMAX;
        }
// printf("new: %d %d %d %d\n",p0->x,p0->y,p1->x,p1->y);
        if (p0->x > XMAX || p0->x < XMIN) return;
        if (p1->x > XMAX || p1->x < XMIN) return;
        if (p0->y > YMAX || p0->y < YMIN) return;
        if (p1->y > YMAX || p1->y < YMIN) return;

// Recalculate variations
     dx = p1->x - p0->x;
     dy = p1->y - p0->y;

// Drawing
     if (ABS(dx)<=ABS(dy)) {
          if (dy<0) {
               void *t;
               t=p0;p0=p1;p1=t;
               dy = -dy;
                        dx = -dx;
          }
                dy++;
          dx = I2F(dx)/dy;
          x = I2F(p0->x);
          y = p0->y;

          p = vram+y*FRAME_W;
          do {
               p[F2I(x)] = c; x+=dx;
               p+=FRAME_W;
          } while(--dy);
     } else {
          if (dx<0) {
               void *t;
               t=p0;p0=p1;p1=t;
               dx = -dx;
                        dy = -dy;
          }
                dx++;
          dy = I2F(dy)/dx;

          x = p0->x;
          y = I2F(p0->y);

          p = vram+x;
          do {
               p[F2I(y)*FRAME_W] = c; y+=dy;
               p++;
          } while(--dx);
     }
    toUpdate = 1;
}

//int ct=0;
void DrawLineG(POINT_G *p0,POINT_G *p1)
{
     int x,y,dx,dy;
     INT16 *p;
     LCOLOR    c0,c1,dc;

     dx = p1->x - p0->x;
        y=1;
        if (dx<0) y = -1;
        dx += y;

     dy = p1->y - p0->y;
        y=1;
        if (dy<0) y = -1;
        dy += y;

// CLIPPING
        x = p0->y*dx/dy - p0->x;
        y = p0->x*dy/dx - p0->y;

//printf("%d) old: %d %d %d %d\n",ct,p0->x,p0->y,p1->x,p1->y);
        if (p0->y < YMIN) {     // p0
            p0->x = FX(YMIN);
            p0->y = YMIN;
        } else
        if (p0->y > YMAX) {
            p0->x = FX(YMAX);
            p0->y = YMAX;
        }
        if (p0->x < XMIN) {
            p0->y = FY(XMIN);
            p0->x = XMIN;
        } else
        if (p0->x > XMAX) {
            p0->y = FY(XMAX);
            p0->x = XMAX;
        }
        if (p1->y < YMIN) {     // p1
            p1->x = FX(YMIN);
            p1->y = YMIN;
        } else
        if (p1->y > YMAX) {
            p1->x = FX(YMAX);
            p1->y = YMAX;
        }
        if (p1->x < XMIN) {
            p1->y = FY(XMIN);
            p1->x = XMIN;
        } else
        if (p1->x > XMAX) {
            p1->y = FY(XMAX);
            p1->x = XMAX;
        }
// printf("%d) new: %d %d %d %d\n",ct,p0->x,p0->y,p1->x,p1->y);
//ct++;

        if (p0->x > XMAX || p0->x < XMIN) return;
        if (p1->x > XMAX || p1->x < XMIN) return;
        if (p0->y > YMAX || p0->y < YMIN) return;
        if (p1->y > YMAX || p1->y < YMIN) return;

// Recalculate variations
     dx = p1->x - p0->x;
     dy = p1->y - p0->y;

     if (ABS(dx)<=ABS(dy)) {
          if (dy<0) {
               void *t;
               t=p0;p0=p1;p1=t;
               dy = -dy;
                        dx = -dx;
          }
          dy++;
          dx = I2F(dx)/dy;

          COLOR2LCOLOR(c0,p0->c);
          COLOR2LCOLOR(c1,p1->c);
          CALCDC(dc,c0,c1,dy);

          x = I2F(p0->x);
          y = p0->y;

          p = vram + y*FRAME_W;
          do {
               p[F2I(x)] = LCOL2PIXEL(c0);
               x+=dx;
               ADDCOL(c0,dc);
               p+=FRAME_W;
          } while(--dy);

     } else {
          int y;
          if (dx<0) {
               void *t;
               t=p0;p0=p1;p1=t;
               dx = -dx;
                        dy = -dy;
          }
          dx++;
          dy = I2F(dy)/dx;

          COLOR2LCOLOR(c0,p0->c);
          COLOR2LCOLOR(c1,p1->c);
          CALCDC(dc,c0,c1,dx);

          x = p0->x;
          y = I2F(p0->y);

          p = vram+x;
          do {
               p[F2I(y)*FRAME_W] = LCOL2PIXEL(c0);
               y+=dy;
               ADDCOL(c0,dc);
               p++;
          } while(--dx);
     }
    toUpdate = 1;
}

#define   FT_SHLINE() \
               do { \
                    int c = *p2++; \
                    if (c) *p=c; \
                    p++; \
               } while(--dx)

#define   FT_SHLINE_A(ALPHA) \
               do { \
                    int c = *p2++; \
                    if (c) { \
                         if (c&0x8000) ALPHA(c,*p); \
                         *p = c; \
                    } \
                    p++; \
               } while(--dx)

#define   FT_SHLINE_A15(ALPHA) \
               do { \
                    int c = *p2++; \
                    if (c) { \
                         int c2 = MULCOL(c,c0); \
                         if (c&0x8000) ALPHA(c2,*p); \
                         *p = c2; \
                    } \
                    p++; \
               } while(--dx)

void DrawSprite(Sprt *prim,int w,int h)
{
    INT16 *p0;
    INT16 *tex;
    int mode;
    int x,y,u,v;
    int tpage,clut;
    COLOR c0code;
    LCOLOR c0;

    x = prim->v.x + OFFX;
    y = prim->v.y + OFFY;
    u = prim->v.u;
    v = prim->v.v;
    clut = prim->v.pad;
    tpage = gpu.tpage;
    c0code = prim->c;

    if (x>XMAX || x+w<XMIN ||
            y>YMAX || y+h<YMIN ||
            w<=0       || h<=0) return;
    if (x<XMIN)
            {
                int dx=XMIN-x;
                if ((w-=dx) <= 0) return;
                u+=dx;
                x=XMIN;
            }
    else if (x+w>XMAX) {
            if ((w=XMAX-x) <=0) return;
        }
    if (y<YMIN)
            {
                int dy=YMIN-y;
                if ((h-=dy) <= 0) return;
                v+=dy;
                y=YMIN;
            }
    else if (y+h>YMAX) {
            if ((h=YMAX-y) <= 0) return;
        }

    p0 = vram+y*FRAME_W+x;

    tex = get_texture(tpage,clut);
    mode = ((tpage>>5)&3)|((c0code.code&3)*16);
        vramchange(x,y,x+w,y+h);

        if (!(mode&NOSHADE))
            COLOR2LCOLOR(c0,c0code);

    do {
        INT16 *p = p0;
        INT16 *p2 = &tex[u+v*256];
        int dx = w;

        switch(mode) {
        case 0:
            do {
                int c = *p2++;
                if (c) *p=MULCOL2(c,c0);
                p++;
            } while(--dx);
                break;
        case T50|NOSHADE:
            FT_SHLINE_A(ALPHA50);
            break;
        case T100|NOSHADE:
            FT_SHLINE_A(ALPHA100);
            break;
        case T100m|NOSHADE:
            FT_SHLINE_A(ALPHA100m);
            break;
        case T25|NOSHADE:
            FT_SHLINE_A(ALPHA25);
            break;

        case T50:
            FT_SHLINE_A15(ALPHA50);
            break;
        case T100:
            FT_SHLINE_A15(ALPHA100);
            break;
        case T100m:
            FT_SHLINE_A15(ALPHA100m);
            break;
        case T25:
            FT_SHLINE_A15(ALPHA25);
            break;
        default:
            FT_SHLINE();
            break;
        }
        v++;
        p0+=FRAME_W;
    } while(--h);
}

void DrawPixel(int x,int y,int c)
{
    x+=OFFX;
    y+=OFFY;
    if (x>=XMIN && x<=XMAX && y>=YMIN && y<=YMAX)
    {
        vram[y*FRAME_W+x]=c;
        vramchange(x,y,x+1,y+1);
    }
}

void DrawBoxNoClip(int x,int y,int w,int h,int c)
{
    INT16 *p0;

    if (x>1023 || y>511) return;
    if (x<0) {
        w += x; x = 0;
    }
    if (y<0) {
        h += y; y = 0;
    }

    if (w<=0 || h<=0) return;
    if (x+w > 1023) w = 1023-x;
    if (y+h > 511) h = 511-y;
    vramchange(x,y,x+w,y+h);

    p0 = &vram[y*FRAME_W+x];
    c = (c<<16)|c;
    do {
        _hline(p0,w,c);
        p0+=FRAME_W;
    } while(--h);
}

void DrawBoxShaded(int x,int y,int w,int h, COLOR c)
{
    INT16 *p0,*p;
     LCOLOR c0;
     int    dx;

    if (x>1023 || y>511) return;
    if (x<0) {
        w += x; x = 0;
    }
    if (y<0) {
        h += y; y = 0;
    }

    if (w<=0 || h<=0) return;
    if (x+w > 1023) w = 1023-x;
    if (y+h > 511) h = 511-y;

    vramchange(x,y,x+w,y+h);
    p0 = &vram[y*FRAME_W+x];
    c.r = ~c.r; c.g = ~c.g; c.b = ~c.b; 
    COLOR2LCOLOR(c0, c);
     
    do{
          p = p0;
          dx = w;
          do {
               *p=MULCOL2(*p,c0);
               p++;
          } while(--dx);

        p0+=FRAME_W;
    } while(--h);
}

void DrawTile(Tile *prim,int w,int h)
{
    int x,y;

    x = prim->v.x + OFFX;
    y = prim->v.y + OFFY;

    if (x>XMAX || x+w<XMIN ||
        y>YMAX || y+h<YMIN ||
        w<=0   || h<=0) return;
    if (x<XMIN) x=XMIN;
    else if (x+w>XMAX) w=XMAX-x;
    if (y<YMIN) y=YMIN;
    else if (y+h>YMAX) h=YMAX-y;

     if ((prim->c.code & 2) == 0)
          DrawBoxNoClip(x,y,w,h,COLOR2PIXEL(prim->c));
     else
          DrawBoxShaded(x,y,w,h,prim->c);
}


void move(int x0,int y0,int x1,int y1,int w,int h)
{
     INT16 *from = VRAMADR(x0,y0);
     INT16 *to   = VRAMADR(x1,y1);

// printf("x0=%d y0=%d x1=%d y1=%d w=%d h=%d\n",x0,y0,x1,y1,w,h);
        if (w<=0 || h<=0) return;
        if (x0+w > 1023) w = 1023 - x0;
        if (x1+w > 1023) w = 1023 - x1;
        if (y0+h > 511) h = 511 - y0;
        if (y1+h > 511) h = 511 - y1;
    vramchange(x1,y1,x1+w,y1+h);
     if (from>to) {
          do {
               memcpy(to,from,w*2);
               from += FRAME_W;
               to += FRAME_W;
          } while(--h);
     } else {
          from += FRAME_W*h;
          to += FRAME_W*h;
          do {
               from -= FRAME_W;
               to -= FRAME_W;
               memmove(to,from,w*2);
          } while(--h);
     }
}

#define   MINMAX(N,nn) \
     {int xmin,xmax,i; \
     xmin = xmax = v[0].x; \
     ymin = ymax = v[0].y; \
     for (i=1;i<N;i++) { \
          if      (xmin>v[i].x) xmin = v[i].x; \
          else if (xmax<v[i].x) xmax = v[i].x; \
          if      (ymin>v[i].y) ymin = v[i].y; \
          else if (ymax<v[i].y) ymax = v[i].y; \
     } \
     if (xmax==xmin || xmin>=XMAX || xmax<XMIN) return nn; }\
     if (ymax==ymin || ymin>=YMAX || ymax<YMIN) return nn; \
     if (ymin<YMIN) ymin=YMIN; \
     if (ymax>YMAX) ymax=YMAX;
#define   MINMAX3(nn)    MINMAX(3,nn)
#define   MINMAX4(nn)    MINMAX(4,nn)

//--------------------------------------------------------------------------

int gpu_polyF3(PolyF3 *prim)
{
    int ymin,ymax;
    POINT_F v[3];

    memcpy(v,prim->v,sizeof(v));
    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;
    v[2].x += OFFX;
    v[2].y += OFFY;

#if SYSLOGOUT==1
    syslog("F3 code=%08x (%d,%d)-(%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->c, v[0].x, v[0].y, v[1].x, v[1].y,
        v[2].x, v[2].y);
#endif

    if (v[0].x*(v[1].y-v[2].y) + v[1].x*(v[2].y-v[0].y) + v[2].x*(v[0].y-v[1].y)<0) {
        POINT_F tmp;
        tmp = v[1]; v[1]=v[2]; v[2]=tmp;
    }

    MINMAX3(NbF3);
    EdgeF(&v[0],&v[1],edge);
    EdgeF(&v[1],&v[2],edge);
    EdgeF(&v[2],&v[0],edge);
    DrawF(edge,ymin,ymax,COLOR2PIXEL(prim->c));

    return NbF3;
}

int gpu_polyF4(PolyF4 *prim)
{
    int ymin,ymax;
    POINT_F v[4];

    memcpy(v,prim->v,sizeof(v));
    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;
    v[2].x += OFFX;
    v[2].y += OFFY;
    v[3].x += OFFX;
    v[3].y += OFFY;

#if SYSLOGOUT==1
    syslog("F4 code=%08x (%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->c, v[0].x, v[0].y, v[1].x, v[1].y,
        v[2].x, v[2].y, v[3].x, v[3].y);
#endif

    MINMAX4(NbF4);
    EdgeF(&v[0],&v[1],edge);
    EdgeF(&v[1],&v[3],edge);
    EdgeF(&v[3],&v[2],edge);
    EdgeF(&v[2],&v[0],edge);
    DrawF(edge,ymin,ymax,COLOR2PIXEL(prim->c));

    return NbF4;
}

int gpu_polyG3(PolyG3 *prim)
{
    int ymin,ymax;
    POINT_G v[3];

    memcpy(v,prim->v,sizeof(v));

    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;
    v[2].x += OFFX;
    v[2].y += OFFY;

#if SYSLOGOUT==1
    syslog("G3 code=%08x (%d,%d)-(%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->v[0].c, v[0].x, v[0].y, v[1].x, v[1].y,
        v[2].x, v[2].y);
#endif

    if (v[0].x*(v[1].y-v[2].y) + v[1].x*(v[2].y-v[0].y) + v[2].x*(v[0].y-v[1].y)<0) {
        POINT_G tmp;
        tmp = v[1]; v[1]=v[2]; v[2]=tmp;
    }
    MINMAX3(NbG3);
    EdgeG(&v[0],&v[1],edge);
    EdgeG(&v[1],&v[2],edge);
    EdgeG(&v[2],&v[0],edge);
    DrawG(edge,ymin,ymax,prim->v[0].c.code);

    return NbG3;
}

int gpu_polyG4(PolyG4 *prim)
{
    int ymin,ymax;
    POINT_G v[4];

    memcpy(v,prim->v,sizeof(v));

    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;
    v[2].x += OFFX;
    v[2].y += OFFY;
    v[3].x += OFFX;
    v[3].y += OFFY;

#if SYSLOGOUT==1
    syslog("G4 code=%08x (%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->v[0].c, v[0].x, v[0].y, v[1].x, v[1].y,
        v[2].x, v[2].y, v[3].x, v[3].y);
#endif

    if (v[0].x*(v[1].y-v[2].y) + v[1].x*(v[2].y-v[0].y) + v[2].x*(v[0].y-v[1].y)<0) {
        POINT_G tmp;
        tmp = v[1]; v[1]=v[2]; v[2]=tmp;
    }
    MINMAX4(NbG4);
    EdgeG(&v[0],&v[1],edge);
    EdgeG(&v[1],&v[3],edge);
    EdgeG(&v[3],&v[2],edge);
    EdgeG(&v[2],&v[0],edge);
    DrawG(edge,ymin,ymax,prim->v[0].c.code);

    return NbG4;
}

int gpu_polyFT3(PolyFT3 *prim)
{
    int ymin,ymax;
    POINT_FT v[3];

    memcpy(v,prim->v,sizeof(v));

    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;
    v[2].x += OFFX;
    v[2].y += OFFY;

#if SYSLOGOUT==1
    syslog("FT3 code=%08x (%d,%d)-(%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->c, v[0].x, v[0].y, v[1].x, v[1].y,
        v[2].x, v[2].y);
#endif

    if (v[0].x*(v[1].y-v[2].y) + v[1].x*(v[2].y-v[0].y) + v[2].x*(v[0].y-v[1].y)<0) {
        POINT_FT tmp;
        tmp = v[1]; v[1]=v[2]; v[2]=tmp;
    }
    MINMAX3(NbFT3);
    EdgeFT(&v[0],&v[1],edge);
    EdgeFT(&v[1],&v[2],edge);
    EdgeFT(&v[2],&v[0],edge);
    DrawFT(edge,ymin,ymax,prim->v[1].pad,prim->v[0].pad,prim->c);

    return NbFT3;
}

int gpu_polyFT4(PolyFT4 *prim)
{
    int ymin,ymax;
    POINT_FT v[4];

    memcpy(v,prim->v,sizeof(v));

    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;
    v[2].x += OFFX;
    v[2].y += OFFY;
    v[3].x += OFFX;
    v[3].y += OFFY;

#if SYSLOGOUT==1
    syslog("FT4 code=%08x (%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->c, v[0].x, v[0].y, v[1].x, v[1].y,
        v[2].x, v[2].y, v[3].x, v[3].y);
#endif

    if (v[0].x*(v[1].y-v[2].y) + v[1].x*(v[2].y-v[0].y) + v[2].x*(v[0].y-v[1].y)<0) {
        POINT_FT tmp;
        tmp = v[1]; v[1]=v[2]; v[2]=tmp;
    }
    MINMAX4(NbFT4);
    EdgeFT(&v[0],&v[1],edge);
    EdgeFT(&v[1],&v[3],edge);
    EdgeFT(&v[3],&v[2],edge);
    EdgeFT(&v[2],&v[0],edge);
    DrawFT(edge,ymin,ymax,prim->v[1].pad,prim->v[0].pad,prim->c);

    return NbFT4;
}

int gpu_polyGT3(PolyGT3 *prim)
{
    int ymin,ymax;
    POINT_GT v[3];

    memcpy(v,prim->v,sizeof(v));

    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;
    v[2].x += OFFX;
    v[2].y += OFFY;

#if SYSLOGOUT==1
    syslog("GT3 code=%08x (%d,%d)-(%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->v[0].c, v[0].x, v[0].y, v[1].x, v[1].y,
        v[2].x, v[2].y);
#endif

    if (v[0].x*(v[1].y-v[2].y) + v[1].x*(v[2].y-v[0].y) + v[2].x*(v[0].y-v[1].y)<0) {
        POINT_GT tmp;
        tmp = v[1]; v[1]=v[2]; v[2]=tmp;
    }
    MINMAX3(NbGT3);
    EdgeGT(&v[0],&v[1],edge);
    EdgeGT(&v[1],&v[2],edge);
    EdgeGT(&v[2],&v[0],edge);
    DrawGT(edge,ymin,ymax,prim->v[1].pad,prim->v[0].pad,prim->v[0].c.code);

    return NbGT3;
}

int gpu_polyGT4(PolyGT4 *prim)
{
    int ymin,ymax;
    POINT_GT v[4];

    memcpy(v,prim->v,sizeof(v));

    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;
    v[2].x += OFFX;
    v[2].y += OFFY;
    v[3].x += OFFX;
    v[3].y += OFFY;

#if SYSLOGOUT==1
    syslog("GT4 code=%08x (%d,%d)-(%d,%d)-(%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->v[0].c, v[0].x, v[0].y, v[1].x, v[1].y,
        v[2].x, v[2].y, v[3].x, v[3].y);
#endif

    if (v[0].x*(v[1].y-v[2].y) + v[1].x*(v[2].y-v[0].y) + v[2].x*(v[0].y-v[1].y)<0) {
        POINT_GT tmp;
        tmp = v[1]; v[1]=v[2]; v[2]=tmp;
    }
    MINMAX4(NbGT4);
    EdgeGT(&v[0],&v[1],edge);
    EdgeGT(&v[1],&v[3],edge);
    EdgeGT(&v[3],&v[2],edge);
    EdgeGT(&v[2],&v[0],edge);
    DrawGT(edge,ymin,ymax,prim->v[1].pad,prim->v[0].pad,prim->v[0].c.code);
//    DrawFT(edge,ymin,ymax,prim->v[1].pad,prim->v[0].pad,prim->v[0].c);

    return NbGT4;
}

int gpu_lineF2(LineF2 *prim)
{
    POINT_F v[2];

    memcpy(v,prim->v,sizeof(v));

    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;

#if SYSLOGOUT==1
    syslog("LineG code=%08x (%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->c, v[0].x, v[0].y, v[1].x, v[1].y);
#endif

    DrawLineF(&v[0],&v[1],COLOR2PIXEL(prim->c));

    return NbF2;
}

int gpu_lineF(LineF3 *prim)
{
return 5;
/*
    POINT_F v[2],*vv;
    int     n=3;

    v[0] = prim->v[0];
    v[0].x += OFFX;
    v[0].y += OFFY;
    vv = prim->v + 1;
    while (*(UINT32 *)(vv) != 0x55555555)
    {
        v[1] = *vv;
        v[1].x += OFFX;
        v[1].y += OFFY;

#if SYSLOGOUT==1
        syslog("LineG code=%08x (%d,%d)-(%d,%d)",
            *(UINT32 *)&prim->c, v[0].x, v[0].y, v[1].x, v[1].y);
#endif

        DrawLineF(&v[0],&v[1],COLOR2PIXEL(prim->c));
        v[0] = v[1];
        vv++;
        n++;
    }

    return n;
*/
}

int gpu_lineG2(LineG2 *prim)
{
    POINT_G v[2];

    memcpy(v,prim->v,sizeof(v));

    v[0].x += OFFX;
    v[0].y += OFFY;
    v[1].x += OFFX;
    v[1].y += OFFY;

#if SYSLOGOUT==1
    syslog("LineG code=%08x (%d,%d)-(%d,%d)",
        *(UINT32 *)&prim->c, v[0].x, v[0].y, v[1].x, v[1].y);
#endif

    DrawLineG(&v[0],&v[1]);

    return NbG2;
}

int gpu_lineG(LineG3 *prim)
{
return 7;
/*
    POINT_G v[2],*vv;
    int     n=4;

    v[0] = prim->v[0];
    v[0].x += OFFX;
    v[0].y += OFFY;
    vv = prim->v + 1;
    while (*(UINT32 *)(vv) != 0x55555555)
    {
        v[1] = *vv;
        v[1].x += OFFX;
        v[1].y += OFFY;

#if SYSLOGOUT==1
        syslog("LineG code=%08x (%d,%d)-(%d,%d)",
            *(UINT32 *)&prim->c, v[0].x, v[0].y, v[1].x, v[1].y);
#endif

        DrawLineG(&v[0],&v[1]);
        v[0] = v[1];
        vv++;
        n+=2;
    }

    return n;
*/
}

int gpu_fill(Fill *prim)
{
#if SYSLOGOUT==1
    syslog("FILL code=%08x (%d,%d) w=%d h=%d",
        *(UINT32 *)&prim->c,prim->v.x,prim->v.y,prim->w,prim->h);
#endif

    DrawBoxNoClip(prim->v.x,prim->v.y,prim->w,prim->h,COLOR2PIXEL(prim->c));

    return NbFILL;
}

int gpu_tile(Tile *prim)
{
#if SYSLOGOUT==1
    syslog("TILE code=%08x (%d,%d) w=%d h=%d",
        *(UINT32 *)&prim->c,prim->v.x,prim->v.y,prim->w,prim->h);
#endif

    DrawTile(prim,prim->w,prim->h);

    return NbFILL;
}

int gpu_tile8(Tile8 *prim)
{
#if SYSLOGOUT==1
    syslog("TILE8 code=%08x (%d,%d)",
        *(UINT32 *)&prim->c,prim->v.x,prim->v.y);
#endif

    DrawTile((Tile *)prim,8,8);

    return NbTILEx;
}

int gpu_tile16(Tile16 *prim)
{
#if SYSLOGOUT==1
    syslog("TILE16 code=%08x (%d,%d)",
        *(UINT32 *)&prim->c,prim->v.x,prim->v.y);
#endif

    DrawTile((Tile *)prim,16,16);

    return NbTILEx;
}

int gpu_tile1(Tile1 *prim)
{
#if SYSLOGOUT==1
    syslog("TILE1 code=%08x (%d,%d)",
        *(UINT32 *)&prim->c,prim->v.x,prim->v.y);
#endif

    DrawPixel(prim->v.x,prim->v.y,COLOR2PIXEL(prim->c));

    return NbTILEx;
}


int gpu_sprt(Sprt *prim)
{
#if SYSLOGOUT==1
    syslog("Sprite code=%08x (%d,%d) w=%d h=%d",
        *(UINT32 *)&prim->c,prim->v.x,prim->v.y,prim->w,prim->h);
#endif

    DrawSprite(prim,prim->w,prim->h);

    return NbSPRT;
}

int gpu_sprt8(Sprt *prim)
{
#if SYSLOGOUT==1
    syslog("Sprite8 code=%08x (%d,%d)",
        *(UINT32 *)&prim->c,prim->v.x,prim->v.y);
#endif

    DrawSprite(prim,8,8);

    return NbSPRTx;
}

int gpu_sprt16(Sprt *prim)
{
#if SYSLOGOUT==1
    syslog("Sprite16 code=%08x (%d,%d)",
        *(UINT32 *)&prim->c,prim->v.x,prim->v.y);
#endif

    DrawSprite(prim,16,16);

    return NbSPRTx;
}