]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/games/gba/ppu.c
demote libemu to common code
[plan9front.git] / sys / src / games / gba / ppu.c
index c25b96e01a8e54f9421f5ab6f38f469f3f8e42bd..b70aba8fbaaef9a77067dfcc7cd92309f12f18a7 100644 (file)
@@ -1,32 +1,38 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
+#include "../eui.h"
 #include "dat.h"
 #include "fns.h"
 
-int ppux, ppuy;
-uchar pic[240*160*2*3*3];
+int hblank, ppuy;
 u8int bldy, blda, bldb;
+u32int hblclock;
+int ppux0;
+u32int pixcol[480];
+u8int pixpri[480];
+u8int pixwin[240];
+int objalpha;
 
 typedef struct bg bg;
 struct bg {
        uchar n;
-       uchar depth;
-
-       s32int rpx0, rpy0, rpx, rpy;
-       s32int sx, sy;
-       
+       s32int rpx0, rpy0, rpx1, rpy1, rpx, rpy;
        u16int tx, ty;
        u8int tnx, tny;
-       u16int t;
-       u8int *chr;
-       u16int *pal;
+       
+       u8int mosaic, mctr, lasti;
+       u32int curc;
+       u8int curpri;
 };
-static u8int mode=-1;
 static bg bgst[4] = {{.n = 0}, {.n = 1}, {.n = 2}, {.n = 3}};
-static u32int pixeldat[2], pixelpri[2];
-static u16int bgmask;
-static u8int objwin, objtrans;
+
+Var ppuvars[] = {
+       VAR(hblank), VAR(ppuy), VAR(hblclock),
+       VAR(bldy), VAR(blda), VAR(bldb), VAR(objalpha),
+       VAR(bgst[2].rpx0), VAR(bgst[2].rpy0), VAR(bgst[3].rpx0), VAR(bgst[3].rpy0),
+       {nil, 0, 0},
+};
 
 typedef struct sprite sprite;
 struct sprite {
@@ -44,140 +50,296 @@ struct sprite {
        
        s32int rx, ry;
        s16int dx, dy;
+       
+       u8int mctr, mcol;
 };
 static sprite sprt[128], *sp = sprt;
 enum {
        SPRROT = 1<<8,
        SPRDIS = 1<<9,
        SPRDOUB = 1<<9,
+       SPRMOSA = 1<<12,
        SPR8 = 1<<13,
        SPRWIDE = 1<<14,
        SPRTALL = 1<<15,
        SPRHFLIP = 1<<28,
        SPRVFLIP = 1<<29,
        SPRSIZE0 = 1<<30,
-       SPRSIZE1 = 1<<31
+       SPRSIZE1 = 1<<31,
+
+       NOWIN = 0,
+       OBJWIN = 1,
+       WIN2 = 2,
+       WIN1 = 4,
+       
+       OBJALPHA = 1<<16,
+       SRCOBJ = 4<<17,
+       SRCBACK = 5<<17,
+       
+       VACANT = 0x10,
+       BACKDROP = 8,
 };
+#define SRCBG(n) ((n)<<17)
 
 void
-pixeldraw(int x, int y, u16int v)
+sprinit(void)
 {
-       uchar *p;
-       u16int *q;
-       union { u16int w; u8int b[2]; } u;
+       u16int *p, *pp;
+       u16int cnt, t1;
+       u32int t0;
+       int budg;
+       uchar ws, h, hb, d, dy, s;
+       static uchar wss[16] = {3, 4, 5, 6, 4, 5, 5, 6, 3, 3, 4, 5};
+       static uchar hss[16] = {3, 4, 5, 6, 3, 3, 4, 5, 4, 5, 5, 6};
 
-       if(scale == 1){
-               p = pic + (x + y * 240) * 2;
-               p[0] = v;
-               p[1] = v >> 8;
-               return;
-       }
-       u.b[0] = v;
-       u.b[1] = v >> 8;
-       if(scale == 2){
-               q = (u16int*)pic + (x + y * 240) * 2;
-               q[0] = u.w;
-               q[1] = u.w;
-       }else{
-               q = (u16int*)pic + (x + y * 240) * 3;
-               q[0] = u.w;
-               q[1] = u.w;
-               q[2] = u.w;
+       sp = sprt;
+       cnt = reg[DISPCNT];
+       budg = (cnt & HBLFREE) != 0 ? 954 : 1210;
+       for(p = oam; p < oam + 512; p += 4){
+               t0 = p[0];
+               if((t0 & (SPRROT|SPRDIS)) == SPRDIS)
+                       continue;
+               t0 |= p[1] << 16;
+               s = t0 >> 30 & 3 | t0 >> 12 & 12;
+               hb = h = 1 << hss[s];
+               dy = ppuy - (u8int) t0;
+               if((t0 & (SPRROT|SPRDOUB)) == (SPRROT|SPRDOUB))
+                       hb <<= 1;
+               if(dy >= hb || (u8int)t0 + hb > 256 && ppuy + 256 - (u8int)t0 >= hb)
+                       continue;
+               if((t0 & SPRMOSA) != 0){
+                       dy = dy - dy % ((reg[MOSAIC] >> 12 & 15) + 1);
+                       sp->mctr = 0;
+               }
+               sp->x = (s32int)(t0 << 7) >> 23;
+               sp->t0 = t0;
+               ws = wss[s];
+               sp->wb = sp->w = 1<<ws;
+               sp->h = h;
+               sp->t1 = t1 = p[2];
+               sp->base = vram + 0x10000 + ((t1 & 0x3ff) << 5);
+               d = (t0 & SPR8) != 0;
+               sp->ysh = (cnt & OBJNOMAT) != 0 ? 2 + d + ws : 10;
+               if((t0 & SPRROT) != 0){
+                       if((t0 & SPRDOUB) != 0)
+                               sp->wb <<= 1;
+                       budg -= 10 + sp->w*2;
+                       pp = oam + 3 + (t0 >> 21 & 0x1f0);
+                       sp->dx = pp[0];
+                       sp->dy = pp[8];
+                       sp->rx = (dy - hb/2) * (s16int) pp[4] + (sp->w << 7) - sp->dx * sp->wb/2;
+                       sp->ry = (dy - hb/2) * (s16int)pp[12] + (sp->h << 7) - sp->dy * sp->wb/2;
+                       if(sp->x < 0){
+                               sp->rx -= sp->x * sp->dx;
+                               sp->ry -= sp->x * sp->dy;
+                       }
+               }else{
+                       budg -= sp->w;
+                       if((t0 & SPRVFLIP) != 0)
+                               dy = h - 1 - dy;
+                       sp->base += (dy & 7) << 2 + d;
+                       sp->base += dy >> 3 << sp->ysh;
+                       if((t0 & SPRHFLIP) != 0)
+                               sp->base += sp->w - 7 << 2 + d;
+                       sp->inc = (1 << 5 + d) - (1 << 2 + d);
+                       if(sp->x < 0)
+                               if((t0 & SPRHFLIP) != 0){
+                                       sp->base -= ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
+                                       if((t0 & SPR8) == 0 && (sp->x & 1) != 0)
+                                               sp->base--;
+                               }else
+                                       sp->base += ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
+               }
+               if((t0 & SPR8) != 0)
+                       sp->pal = pram + 0x100;
+               else
+                       sp->pal = pram + 0x100 + (t1 >> 8 & 0xf0);
+               if(budg < 0)
+                       break;
+               sp++;
        }
 }
 
 void
-pixel(u16int c, int n, int p)
+spr(int x1)
 {
-       if(p < pixelpri[0]){
-               pixeldat[1] = pixeldat[0];
-               pixelpri[1] = pixelpri[0];
-               pixelpri[0] = p;
-               pixeldat[0] = c | n << 16;
-       }else if(p < pixelpri[1]){
-               pixelpri[1] = p;
-               pixeldat[1] = c | n << 16;
+       int x0, i, dx, sx0, sx1;
+       u8int pri, v, d, *b;
+       u16int x, y;
+       u32int c, t0;
+       sprite *s;
+       
+       x0 = ppux0;
+       for(s = sprt; s < sp; s++){
+               if(s->x >= x1 || s->x + s->wb <= x0)
+                       continue;
+               t0 = s->t0;
+               pri = s->t1 >> 10 & 3;
+               sx0 = s->x >= x0 ? s->x : x0;
+               sx1 = s->x + s->wb;
+               if(x1 < sx1)
+                       sx1 = x1;
+               dx = sx0 - s->x;
+               for(i = sx0; i < sx1; i++, dx++){
+                       if((t0 & SPRROT) != 0){
+                               d = (t0 & SPR8) != 0;
+                               x = s->rx >> 8;
+                               y = s->ry >> 8;
+                               s->rx += s->dx;
+                               s->ry += s->dy;
+                               if(x < s->w && y < s->h){
+                                       b = s->base;
+                                       b += (y & 7) << 2 + d;
+                                       b += y >> 3 << s->ysh;
+                                       b += (x & 7) >> 1 - d;
+                                       b += x >> 3 << 5 + d;
+                                       v = *b;
+                                       if(!d)
+                                               if((x & 1) != 0)
+                                                       v >>= 4;
+                                               else
+                                                       v &= 0xf;
+                               }else
+                                       v = 0;
+                       }else if((t0 & SPRHFLIP) != 0){
+                               if((t0 & SPR8) != 0)
+                                       v = *--s->base;
+                               else if((dx & 1) != 0)
+                                       v = *s->base & 0x0f;
+                               else
+                                       v = *--s->base >> 4;
+                               if((dx & 7) == 7)
+                                       s->base -= s->inc;
+                       }else{
+                               v = *s->base;
+                               if((t0 & SPR8) != 0)
+                                       s->base++;
+                               else if((dx & 1) != 0){
+                                       v >>= 4;
+                                       s->base++;
+                               }else
+                                       v &= 0xf;
+                               if((dx & 7) == 7)
+                                       s->base += s->inc;
+                       }
+                       if((t0 & SPRMOSA) != 0)
+                               if(s->mctr == 0){
+                                       s->mctr = reg[MOSAIC] >> 8 & 15;
+                                       s->mcol = v;
+                               }else{
+                                       --s->mctr;
+                                       v = s->mcol;
+                               }
+                       if(v != 0){
+                               c = s->pal[v] | SRCOBJ;
+                               switch(t0 >> 10 & 3){
+                               case 1:
+                                       c |= OBJALPHA;
+                                       objalpha++;
+                               case 0:
+                                       if(pri < pixpri[i]){
+                                               pixcol[i] = c;
+                                               pixpri[i] = pri;
+                                       }
+                                       break;
+                               case 2:
+                                       if((reg[DISPCNT] & 1<<15) != 0)
+                                               pixwin[i] |= OBJWIN;
+                                       break;
+                               }
+                       }
+               }
        }
 }
 
 void
-tile(bg *b)
+bgpixel(bg *b, int i, u32int c, int pri)
 {
-       u16int bgcnt, ta, tx, ty, y, t;
-       u8int d;
-       u8int *chr;
-       
-       bgcnt = reg[BG0CNT + b->n];
-       d = bgcnt >> 7 & 1;
-       tx = b->tx;
-       ty = b->ty;
-       ta = (bgcnt << 3 & 0xf800) + ((tx & 0x1f) << 1) + ((ty & 0x1f) << 6);
-       switch(bgcnt >> 14){
-       case 1: ta += tx << 6 & 0x800; break;
-       case 2: ta += ty << 6 & 0x800; break;
-       case 3: ta += tx << 6 & 0x800 | ty << 7 & 0x1000; break;
+       u8int *p;
+       u32int *q;
+       int j;
+
+       if(b != nil){
+               c |= SRCBG(b->n);
+               if(b->mosaic){
+                       for(j = (u8int)(b->lasti+1); j <= i; j++){
+                               if(b->mctr == 0){
+                                       if(j == i){
+                                               b->curc = c;
+                                               b->curpri = pri;
+                                       }else
+                                               b->curpri = VACANT;
+                                       b->mctr = reg[MOSAIC] & 15;
+                               }else
+                                       b->mctr--;
+                               if(b->curpri != VACANT && (pixwin[j] & 1<<b->n) == 0)
+                                       bgpixel(nil, j, b->curc, b->curpri);
+                       }
+                       b->lasti = i;
+                       return;
+               }
+       }
+       p = pixpri + i;
+       q = pixcol + i;
+       if(pri < p[0]){
+               p[240] = p[0];
+               p[0] = pri;
+               q[240] = q[0];
+               q[0] = c;
+       }else if(pri < p[240]){
+               p[240] = pri;
+               q[240] = c;
        }
-       t = vram[ta] | vram[ta+1] << 8;
-       b->t = t;
-       chr = vram + (bgcnt << 12 & 0xc000) + ((t & 0x3ff) << 5+d);
-       y = b->tny;
-       if((t & 1<<11) != 0)
-               y ^= 7;
-       chr = chr + (y << 2+d);
-       b->chr = chr;
-       if(d != 0)
-               b->pal = pram;
-       else
-               b->pal = pram + (t >> 8 & 0xf0);
 }
 
+
+
 void
-bginit(bg *b, int scal, int bit)
+bginit(bg *b, int scal, int)
 {
-       u16int cnt, x, y;
+       u16int x, y;
        u16int *rr;
-       
-       cnt = reg[DISPCNT];
+       int msz;
+
+       b->mosaic = (reg[BG0CNT + b->n] & BGMOSAIC) != 0;
+       if(b->mosaic){
+               b->mctr = 0;
+               b->lasti = -1;
+       }
        if(scal){
                rr = reg + (b->n - 2 << 3);
                if(ppuy == 0){
                        b->rpx0 = (s32int)(rr[BG2XL] | rr[BG2XH] << 16) << 4 >> 4;
                        b->rpy0 = (s32int)(rr[BG2YL] | rr[BG2YH] << 16) << 4 >> 4;
                }
-               b->rpx = b->rpx0;
-               b->rpy = b->rpy0;
+               if(!b->mosaic || ppuy % ((reg[MOSAIC] >> 4 & 15) + 1) == 0){
+                       b->rpx1 = b->rpx0;
+                       b->rpy1 = b->rpy0;
+               }
+               b->rpx = b->rpx1;
+               b->rpy = b->rpy1;
                b->rpx0 += (s16int)rr[BG2PB];
                b->rpy0 += (s16int)rr[BG2PD];
-               switch(cnt & 7){
-               case 3:
-               case 4:
-                       b->sx = 240 << 8;
-                       b->sy = 160 << 8;
-                       b->depth = (cnt & 7) == 3;
-                       break;
-               case 5:
-                       b->sx = 160 << 8;
-                       b->sy = 128 << 8;
-                       b->depth = 1;
-                       break;
-               }
        }else{
                rr = reg + (b->n << 1);
                x = rr[BG0HOFS] & 0x1ff;
-               y = (rr[BG0VOFS] & 0x1ff) + ppuy;
+               y = ppuy;
+               if(b->mosaic){
+                       msz = (reg[MOSAIC] >> 4 & 15) + 1;
+                       y = y - y % msz;
+               }
+               y += (rr[BG0VOFS] & 0x1ff);
                b->tx = x >> 3;
                b->ty = y >> 3;
                b->tnx = x & 7;
                b->tny = y & 7;
-               tile(b);
        }
 }
 
 void
 bgsinit(void)
 {
-       mode = reg[DISPCNT] & 7;
-       switch(mode){
+       switch(reg[DISPCNT] & 7){
        case 0:
                bginit(&bgst[0], 0, 0);
                bginit(&bgst[1], 0, 0);
@@ -202,289 +364,200 @@ bgsinit(void)
 }
 
 void
-bitbg(bg *b)
+bitbg(bg *b, int x1)
 {
-       u16int cnt;
-       int v;
-       uchar *p;
-       u16int *rr;
-       uchar *base;
+       u8int *base, *p, pri, d;
+       u16int cnt, *rr, sx, sy;
+       int i, v;
        
        cnt = reg[DISPCNT];
-       rr = reg - 8 + (b->n << 3);
-       if((bgmask & 1<<b->n) == 0)
-               goto next;
-       if(b->rpx >= 0 && b->rpy >= 0 && b->rpx <= b->sx && b->rpy <= b->sy){
-               base = vram;
-               if((cnt & FRAME) != 0 && (cnt & 7) != 3)
-                       base += 0xa000;
-               if(b->depth){
-                       p = base + 2 * (b->rpx >> 8) + 480 * (b->rpy >> 8);
-                       v = p[0] | p[1] << 8;
-               }else{
-                       v = base[(b->rpx >> 8) + 240 * (b->rpy >> 8)];
-                       if(v != 0)
-                               v = pram[v];
-                       else
-                               v = -1;
+       if((cnt & 1<<8 + b->n) == 0)
+               return;
+       rr = reg + (b->n - 2 << 3);
+       if((cnt & 7) != 5){
+               sx = 240 << 8;
+               sy = 160 << 8;
+               d = (cnt & 7) == 3;
+       }else{
+               sx = 160 << 8;
+               sy = 128 << 8;
+               d = 1;
+       }
+       base = vram;
+       if((cnt & FRAME) != 0 && (cnt & 7) != 3)
+               base += 0xa000;
+       pri = reg[BG0CNT + b->n] & 3;
+       for(i = ppux0; i < x1; i++){
+               if(((pixwin[i] & 1<<b->n) == 0 || b->mosaic) && (u32int)b->rpx < sx && (u32int)b->rpy < sy){
+                       if(d){
+                               p = base + 2 * (b->rpx >> 8) + 480 * (b->rpy >> 8);
+                               v = p[0] | p[1] << 8;
+                       }else{
+                               v = base[(b->rpx >> 8) + 240 * (b->rpy >> 8)];
+                               if(v != 0)
+                                       v = pram[v];
+                               else
+                                       v = -1;
+                       }
+                       if(v >= 0)
+                               bgpixel(b, i, v, pri);
+       
                }
-       }else
-               v = -1;
-       if(v >= 0)
-               pixel(v, b->n, reg[BG0CNT + b->n] & 3);
-next:
-       b->rpx += (s16int) rr[BG2PA];
-       b->rpy += (s16int) rr[BG2PC];
-}
-
-void
-rotbg(bg *b)
-{
-       u16int *rr, ta;
-       u16int bgcnt;
-       int row, sz, x, y;
-       uchar *p, v;
-
-       rr = reg - 8 + (b->n << 3);
-       if((bgmask & 1<<b->n) == 0)
-               goto next;
-       bgcnt = reg[BG0CNT + b->n];
-       row = (bgcnt >> 14) + 4;
-       sz = 1 << 3 + row;
-       x = b->rpx >> 8;
-       y = b->rpy >> 8;
-       if((bgcnt & DISPWRAP) != 0){
-               x &= sz - 1;
-               y &= sz - 1;
-       }else if((uint)x >= sz || (uint)y >= sz)
-               goto next;
-       ta = (bgcnt << 3 & 0xf800) + ((y >> 3) << row) + (x >> 3);
-       p = vram + (bgcnt << 12 & 0xc000) + (vram[ta] << 6);
-       p += (x & 7) + ((y & 7) << 3);
-       if((v = *p) != 0)
-               pixel(pram[v], b->n, bgcnt & 3);
-next:
-       b->rpx += (s16int) rr[BG2PA];
-       b->rpy += (s16int) rr[BG2PC];
+               b->rpx += (s16int) rr[BG2PA];
+               b->rpy += (s16int) rr[BG2PC];
+       }
 }
 
 void
-txtbg(bg *b)
+txtbg(bg *b, int x1)
 {
-       u16int bgcnt;
-       u8int x, v;
-
+       u8int y, v, d, *cp;
+       u16int bgcnt, ta0, ta, tx, ty, t, *pal;
+       u32int ca;
+       int i, x, mx;
+       
+       if((reg[DISPCNT] & 1<<8 + b->n) == 0)
+               return;
        bgcnt = reg[BG0CNT + b->n];
-       if((bgmask & 1<<b->n) == 0)
-               goto next;
-       x = b->tnx;
-       if((b->t & 1<<10) != 0)
-               x ^= 7;
-       if((bgcnt & BG8) != 0)
-               v = b->chr[x];
-       else{
-               v = b->chr[x>>1];
-               if((x & 1) != 0)
-                       v >>= 4;
-               else
-                       v &= 0xf;
-       }
-       if(v != 0)
-               pixel(b->pal[v], b->n, bgcnt & 3);
-next:
-       if(++b->tnx == 8){
-               b->tnx = 0;
-               b->tx++;
-               tile(b);
+       d = bgcnt >> 7 & 1;
+       tx = b->tx;
+       ty = b->ty;
+       ta0 = (bgcnt << 3 & 0xf800) + ((ty & 0x1f) << 6);
+       switch(bgcnt >> 14){
+       case 2: ta0 += ty << 6 & 0x800; break;
+       case 3: ta0 += ty << 7 & 0x1000; break;
        }
-}
-
-void
-bgs(void)
-{
-       switch(mode){
-       case 0:
-               txtbg(&bgst[0]);
-               txtbg(&bgst[1]);
-               txtbg(&bgst[2]);
-               txtbg(&bgst[3]);
-               break;
-       case 1:
-               txtbg(&bgst[0]);
-               txtbg(&bgst[1]);
-               rotbg(&bgst[2]);
-               break;
-       case 2:
-               rotbg(&bgst[2]);
-               rotbg(&bgst[3]);
-               break;
-       case 3:
-       case 4:
-       case 5:
-               bitbg(&bgst[2]);
-               break;
+       x = ppux0;
+       i = b->tnx;
+       for(; x < x1; tx++, i = 0){
+               ta = ta0 + ((tx & 0x1f) << 1);
+               if((bgcnt & 1<<14) != 0)
+                       ta += tx << 6 & 0x800;
+               t = vram[ta] | vram[ta+1] << 8;
+               if(d)
+                       pal = pram;
+               else
+                       pal = pram + (t >> 8 & 0xf0);
+               ca = (bgcnt << 12 & 0xc000) + ((t & 0x3ff) << 5+d);
+               if(ca >= 0x10000)
+                       continue;
+               y = b->tny;
+               if((t & 1<<11) != 0)
+                       y ^= 7;
+               ca += y << 2+d;
+               cp = vram + ca;
+               for(; i < 8; i++, x++){
+                       if(x >= x1)
+                               goto out;
+                       if((pixwin[x] & 1<<b->n) != 0 && !b->mosaic)
+                               continue;
+                       mx = i;
+                       if((t & 1<<10) != 0)
+                               mx ^= 7;
+                       v = cp[mx >> 1-d];
+                       if(!d)
+                               if((mx & 1) != 0)
+                                       v >>= 4;
+                               else
+                                       v &= 0xf;
+                       if(v != 0)
+                               bgpixel(b, x, pal[v], bgcnt & 3);
+               }
        }
+out:
+       b->tx = tx;
+       b->tnx = i;
 }
 
 void
-sprinit(void)
+rotbg(bg *b, int x1)
 {
-       u16int *p, *pp;
-       u16int cnt, t1;
-       u32int t0;
-       int budg;
-       uchar ws, h, hb, d, dy, s;
-       static uchar wss[16] = {3, 4, 5, 6, 4, 5, 5, 6, 3, 3, 4, 5};
-       static uchar hss[16] = {3, 4, 5, 6, 3, 3, 4, 5, 4, 5, 5, 6};
+       uchar *p, v;
+       u16int bgcnt, *rr, ta;
+       int i, row, sz, x, y;
 
-       sp = sprt;
-       cnt = reg[DISPCNT];
-       budg = (cnt & HBLFREE) != 0 ? 954 : 1210;
-       for(p = oam; p < oam + 512; p += 4){
-               t0 = p[0];
-               if((t0 & (SPRROT|SPRDIS)) == SPRDIS)
-                       continue;
-               t0 |= p[1] << 16;
-               s = t0 >> 30 & 3 | t0 >> 12 & 12;
-               hb = h = 1 << hss[s];
-               dy = ppuy - (u8int) t0;
-               if((t0 & (SPRROT|SPRDOUB)) == (SPRROT|SPRDOUB))
-                       hb <<= 1;
-               if(dy >= hb || (u8int)t0 + hb > 256 && ppuy + 256 - (u8int)t0 >= hb)
+       rr = reg + (b->n - 2 << 3);
+       if((reg[DISPCNT] & 1<<8 + b->n) == 0)
+               return;
+       bgcnt = reg[BG0CNT + b->n];
+       row = (bgcnt >> 14) + 4;
+       sz = 1 << 3 + row;
+       for(i = ppux0; i < x1; i++){
+               x = b->rpx >> 8;
+               y = b->rpy >> 8;
+               b->rpx += (s16int) rr[BG2PA];
+               b->rpy += (s16int) rr[BG2PC];
+               if((pixwin[i] & 1<<b->n) != 0 && !b->mosaic)
                        continue;
-               sp->x = (s32int)(t0 << 7) >> 23;
-               sp->t0 = t0;
-               ws = wss[s];
-               sp->wb = sp->w = 1<<ws;
-               sp->h = h;
-               sp->t1 = t1 = p[2];
-               sp->base = vram + 0x10000 + ((t1 & 0x3ff) << 5);
-               d = (t0 & SPR8) != 0;
-               sp->ysh = (cnt & OBJNOMAT) != 0 ? 2 + d + ws : 10;
-               if((t0 & SPRROT) != 0){
-                       if((t0 & SPRDOUB) != 0)
-                               sp->wb <<= 1;
-                       budg -= 10 + sp->w*2;
-                       pp = oam + 3 + (t0 >> 21 & 0x1f0);
-                       sp->dx = pp[0];
-                       sp->dy = pp[8];
-                       sp->rx = (dy - hb/2) * (s16int) pp[4] + (sp->w << 7) - sp->dx * sp->wb/2;
-                       sp->ry = (dy - hb/2) * (s16int)pp[12] + (sp->h << 7) - sp->dy * sp->wb/2;
-                       if(sp->x < 0){
-                               sp->rx -= sp->x * sp->dx;
-                               sp->ry -= sp->x * sp->dy;
-                       }
-               }else{
-                       budg -= sp->w;
-                       if((t0 & SPRVFLIP) != 0)
-                               dy = h - 1 - dy;
-                       sp->base += (dy & 7) << 2 + d;
-                       sp->base += dy >> 3 << sp->ysh;
-                       if((t0 & SPRHFLIP) != 0)
-                               sp->base += sp->w - 7 << 2 + d;
-                       sp->inc = (1 << 5 + d) - (1 << 2 + d);
-                       if(sp->x < 0)
-                               if((t0 & SPRHFLIP) != 0){
-                                       sp->base -= ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
-                                       if((t0 & SPR8) == 0 && (sp->x & 1) != 0)
-                                               sp->base--;
-                               }else
-                                       sp->base += ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
-               }
-               if((t0 & SPR8) != 0)
-                       sp->pal = pram + 0x100;
-               else
-                       sp->pal = pram + 0x100 + (t1 >> 8 & 0xf0);
-               if(budg < 0)
-                       break;
-               sp++;
+               if((bgcnt & DISPWRAP) != 0){
+                       x &= sz - 1;
+                       y &= sz - 1;
+               }else if((uint)x >= sz || (uint)y >= sz)
+                        continue;
+               ta = (bgcnt << 3 & 0xf800) + ((y >> 3) << row) + (x >> 3);
+               p = vram + (bgcnt << 12 & 0xc000) + (vram[ta] << 6);
+               p += (x & 7) + ((y & 7) << 3);
+               if((v = *p) != 0)
+                       bgpixel(b, i, pram[v], bgcnt & 3);
+               
        }
 }
 
 void
-spr(void)
+windows(int x1)
 {
-       sprite *s;
-       ushort dx;
-       u32int t0;
-       uchar v;
-       ushort x, y;
-       u16int c;
-       int pv, ppri, pri;
-       uchar d;
-       uchar *b;
+       static u8int wintab[8] = {2, 3, 1, 1, 0, 0, 0, 0};
+       int i, sx0, sx1;
+       u16int v, h;
+       u16int cnt;
        
-       pv = -1;
-       ppri = 6;;
-       for(s = sprt; s < sp; s++){
-               dx = ppux - s->x;
-               if(dx >= s->wb)
-                       continue;
-               t0 = s->t0;
-               if((t0 & SPRROT) != 0){
-                       x = s->rx >> 8;
-                       y = s->ry >> 8;
-                       if(x < s->w && y < s->h){
-                               b = s->base;
-                               d = (t0 & SPR8) != 0;
-                               b += (y & 7) << 2 + d;
-                               b += y >> 3 << s->ysh;
-                               b += (x & 7) >> 1 - d;
-                               b += x >> 3 << 5 + d;
-                               v = *b;
-                               if(!d)
-                                       if((x & 1) != 0)
-                                               v >>= 4;
-                                       else
-                                               v &= 0xf;
-                       }else
-                               v = 0;
-                       s->rx += s->dx;
-                       s->ry += s->dy;
-               }else if((t0 & SPRHFLIP) != 0){
-                       if((t0 & SPR8) != 0)
-                               v = *--s->base;
-                       else if((dx & 1) != 0)
-                               v = *s->base & 0x0f;
-                       else
-                               v = *--s->base >> 4;
-                       if((dx & 7) == 7)
-                               s->base -= s->inc;
-               }else{
-                       v = *s->base;
-                       if((t0 & SPR8) != 0)
-                               s->base++;
-                       else if((dx & 1) != 0){
-                               v >>= 4;
-                               s->base++;
-                       }else
-                               v &= 0xf;
-                       if((dx & 7) == 7)
-                               s->base += s->inc;
+       cnt = reg[DISPCNT];
+       if((cnt >> 13) != 0){
+               if((cnt & 1<<13) != 0){
+                       v = reg[WIN0V];
+                       h = reg[WIN0H];
+                       if(ppuy < (u8int)v && ppuy >= v >> 8){
+                               sx1 = (u8int)h;
+                               sx0 = h >> 8;
+                               if(sx0 < ppux0)
+                                       sx0 = ppux0;
+                               if(sx1 > x1)
+                                       sx1 = x1;
+                               for(i = sx0; i < sx1; i++)
+                                       pixwin[i] |= WIN1;
+                       }
                }
-               if(v != 0){
-                       pri = s->t1 >> 10 & 3;
-                       c = s->pal[v];
-                       switch(s->t0 >> 10 & 3){
-                       case 1:
-                               c |= 1<<16;
-                       case 0:
-                               if(ppri > pri){
-                                       pv = c;
-                                       ppri = pri;
-                               }
-                               break;
-                       case 2:
-                               objwin = 1;
-                               break;
+               if((cnt & 1<<14) != 0){
+                       v = reg[WIN1V];
+                       h = reg[WIN1H];
+                       if(ppuy < (u8int)v && ppuy >= v >> 8){
+                               sx1 = (u8int)h;
+                               sx0 = h >> 8;
+                               if(sx0 < ppux0)
+                                       sx0 = ppux0;
+                               if(sx1 > x1)
+                                       sx1 = x1;
+                               for(i = sx0; i < sx1; i++)
+                                       pixwin[i] |= WIN2;
                        }
                }
+               for(i = ppux0; i < x1; i++){
+                       v = wintab[pixwin[i]];
+                       h = reg[WININ + (v & 2) / 2];
+                       if((v & 1) != 0)
+                               h >>= 8;
+                       pixwin[i] = ~h;
+               }
        }
-       if(pv >= 0){
-               pixel(pv, 4, ppri);
-               if(pv >> 16 != 0)
-                       objtrans = 1;
-       }
+       for(i = ppux0; i < x1; i++)
+               if(pixpri[i] == VACANT || (pixwin[i] & 1<<4) != 0){
+                       pixcol[i] = pram[0] | SRCBACK;
+                       pixpri[i] = BACKDROP;
+               }else{
+                       pixcol[i+240] = pram[0] | SRCBACK;
+                       pixpri[i+240] = BACKDROP;
+               }
+       objalpha = 0;
 }
 
 u16int
@@ -537,117 +610,189 @@ darken(u16int c1)
 }
 
 void
-windows(void)
+colormath(int x1)
 {
-       u16int dispcnt;
-       u16int v, h;
-
-       dispcnt = reg[DISPCNT];
-       bgmask = dispcnt >> 8 | 1<<5;
-       if((dispcnt >> 13) != 0){
-               if((dispcnt & 1<<13) != 0){
-                       v = reg[WIN0V];
-                       h = reg[WIN0H];
-                       if(ppuy < (u8int)v && ppuy >= v >> 8 &&
-                               ppux < (u8int)h && ppux >= h >> 8){
-                               bgmask &= reg[WININ];
-                               goto windone;
-                       }
-               }
-               if((dispcnt & 1<<14) != 0){
-                       v = reg[WIN1V];
-                       h = reg[WIN1H];
-                       if(ppuy < (u8int)v && ppuy >= v >> 8 &&
-                               ppux < (u8int)h && ppux >= h >> 8){
-                               bgmask &= reg[WININ] >> 8;
-                               goto windone;
-                       }
-               }
-               if((dispcnt & 1<<15) != 0 && objwin != 0){
-                       bgmask &= reg[WINOUT] >> 8;
-                       goto windone;
+       u16int bldcnt;
+       u32int *p;
+       int i;
+       
+       bldcnt = reg[BLDCNT];
+       if((bldcnt & 3<<6) == 0 && objalpha == 0)
+               return;
+       p = pixcol + ppux0;
+       for(i = ppux0; i < x1; i++, p++){
+               if((*p & OBJALPHA) != 0)
+                       goto alpha;
+               if((pixwin[i] & 1<<5) != 0 || (bldcnt & 1<<(*p >> 17)) == 0)
+                       continue;
+               switch(bldcnt >> 6 & 3){
+               case 1:
+               alpha:
+                       if((bldcnt & 1<<8+(p[240] >> 17)) == 0)
+                               continue;
+                       *p = mix(*p, p[240]);
+                       break;
+               case 2:
+                       *p = brighten(*p);
+                       break;
+               case 3:
+                       *p = darken(*p);
+                       break;
                }
-               bgmask &= reg[WINOUT];
-       }
-windone:
-       if(pixelpri[0] != 8 && (bgmask & 1<<4) == 0){
-               pixelpri[0] = 8;
-               pixeldat[0] = pram[0] | 5 << 16;
        }
 }
 
 void
-colormath(void)
+syncppu(int x1)
 {
-       u8int src0;
-       u16int bldcnt;
-       
-       if((bgmask & 1<<5) == 0)
+       int i;
+       u16int cnt;
+
+       if(hblank || ppuy >= 160)
                return;
-       bldcnt = reg[BLDCNT];
-       src0 = pixeldat[0] >> 16;
-       if(objtrans && src0 == 4)
-               goto alpha;
-       if((bldcnt & 3<<6) == 0 || (bldcnt & 1<<src0) == 0)
+       if(x1 >= 240)
+               x1 = 240;
+       else if(x1 <= ppux0)
+               return;
+       cnt = reg[DISPCNT];
+       if((cnt & FBLANK) != 0){
+               for(i = ppux0; i < x1; i++)
+                       pixcol[i] = 0xffff;
+               ppux0 = x1;
                return;
-       switch(bldcnt >> 6 & 3){
+       }
+
+       if((cnt & 1<<12) != 0)
+               spr(x1);
+       windows(x1);
+       switch(cnt & 7){
+       case 0:
+               txtbg(&bgst[0], x1);
+               txtbg(&bgst[1], x1);
+               txtbg(&bgst[2], x1);
+               txtbg(&bgst[3], x1);
+               break;
        case 1:
-       alpha:
-               if((bldcnt & 1<<8+(pixeldat[1]>>16)) == 0)
-                       return;
-               pixeldat[0] = mix(pixeldat[0], pixeldat[1]);
+               txtbg(&bgst[0], x1);
+               txtbg(&bgst[1], x1);
+               rotbg(&bgst[2], x1);
                break;
        case 2:
-               pixeldat[0] = brighten(pixeldat[0]);
+               rotbg(&bgst[2], x1);
+               rotbg(&bgst[3], x1);
                break;
        case 3:
-               pixeldat[0] = darken(pixeldat[0]);
-               break;
+       case 4:
+       case 5:
+               bitbg(&bgst[2], x1);
        }
+       colormath(x1);
+       ppux0 = x1;
 }
 
 void
-ppustep(void)
+linecopy(u32int *p, int y)
 {
+       uchar *q;
+       u16int *r;
+       u16int v;
+       union { u16int w; u8int b[2]; } u;
+       int n;
+
+       q = pic + y * 240 * 2 * scale;
+       r = (u16int*)q;
+       n = 240;
+       while(n--){
+               v = *p++;
+               u.b[0] = v;
+               u.b[1] = v >> 8;
+               switch(scale){
+               case 16: *r++ = u.w;
+               case 15: *r++ = u.w;
+               case 14: *r++ = u.w;
+               case 13: *r++ = u.w;
+               case 12: *r++ = u.w;
+               case 11: *r++ = u.w;
+               case 10: *r++ = u.w;
+               case 9: *r++ = u.w;
+               case 8: *r++ = u.w;
+               case 7: *r++ = u.w;
+               case 6: *r++ = u.w;
+               case 5: *r++ = u.w;
+               case 4: *r++ = u.w;
+               case 3: *r++ = u.w;
+               case 2: *r++ = u.w;
+               default: *r++ = u.w;
+               }
+       }
+}
+
+void
+hblanktick(void *)
+{
+       extern Event evhblank;
        u16int stat;
-       u16int cnt;
-       
+
        stat = reg[DISPSTAT];
-       cnt = reg[DISPCNT];
-       if(ppuy < 160 && ppux < 240)
-               if((cnt & FBLANK) == 0){
-                       objwin = 0;
-                       objtrans = 0;
-                       pixelpri[0] = 8;
-                       pixeldat[0] = pram[0] | 5 << 16;
-                       if((cnt & 1<<12) != 0)
-                               spr();
-                       windows();
-                       bgs();
-                       colormath();
-                       pixeldraw(ppux, ppuy, pixeldat[0]);
-               }else
-                       pixeldraw(ppux, ppuy, 0xffff);
-       if(ppux == 240 && ppuy < 160){
-               if((stat & IRQHBLEN) != 0)
-                       setif(IRQHBL);
-               dmastart(DMAHBL);
-       }
-       if(++ppux >= 308){
-               ppux = 0;
+       if(hblank){
+               hblclock = clock + evhblank.time;
+               addevent(&evhblank, 240*4);
+               hblank = 0;
+               ppux0 = 0;
+               memset(pixpri, VACANT, sizeof(pixpri));
+               memset(pixwin, 0, 240);
                if(++ppuy >= 228){
                        ppuy = 0;
                        flush();
                }
-               if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8)
-                       setif(IRQVCTR);
                if(ppuy < 160){
-                       bgsinit();
                        sprinit();
+                       bgsinit();
                }else if(ppuy == 160){
+                       dmastart(DMAVBL);
                        if((stat & IRQVBLEN) != 0)
                                setif(IRQVBL);
-                       dmastart(DMAVBL);
                }
+               if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8)
+                       setif(IRQVCTR);
+       }else{
+               syncppu(240);
+               if(ppuy < 160)
+                       linecopy(pixcol, ppuy);
+               addevent(&evhblank, 68*4);
+               hblank = 1;
+               if((stat & IRQHBLEN) != 0)
+                       setif(IRQHBL);
+               if(ppuy < 160)
+                       dmastart(DMAHBL);
+       }
+}
+
+void
+ppuwrite(u16int a, u16int v)
+{
+       syncppu((clock - hblclock) / 4);
+       switch(a){
+       case BLDALPHA*2:
+               blda = v & 0x1f;
+               if(blda > 16)
+                       blda = 16;
+               bldb = v >> 8 & 0x1f;
+               if(bldb > 16)
+                       bldb = 16;
+               break;
+       case BLDY*2:
+               bldy = v & 0x1f;
+               if(bldy > 16)
+                       bldy = 16;
+               break;
+       case BG2XL*2: bgst[2].rpx0 = bgst[2].rpx0 & 0xffff0000 | v; break;
+       case BG2XH*2: bgst[2].rpx0 = bgst[2].rpx0 & 0xffff | (s32int)(v << 20) >> 4; break;
+       case BG2YL*2: bgst[2].rpy0 = bgst[2].rpy0 & 0xffff0000 | v; break;
+       case BG2YH*2: bgst[2].rpy0 = bgst[2].rpy0 & 0xffff | (s32int)(v << 20) >> 4; break;
+       case BG3XL*2: bgst[3].rpx0 = bgst[3].rpx0 & 0xffff0000 | v; break;
+       case BG3XH*2: bgst[3].rpx0 = bgst[3].rpx0 & 0xffff | (s32int)(v << 20) >> 4; break;
+       case BG3YL*2: bgst[3].rpy0 = bgst[3].rpy0 & 0xffff0000 | v; break;
+       case BG3YH*2: bgst[3].rpy0 = bgst[3].rpy0 & 0xffff | (s32int)(v << 20) >> 4; break;
        }
 }