ref: 7ab86494986671bf38464435f57fb6f925ce8632
dir: /sys/src/games/gba/ppu.c/
#include <u.h> #include <libc.h> #include <thread.h> #include "dat.h" #include "fns.h" int hblank, ppuy; u8int bldy, blda, bldb; u32int hblclock; int ppux0; u32int pixcol[480]; u8int pixpri[480]; u8int pixwin[240]; uchar pic[240*160*3*2]; int objalpha; typedef struct bg bg; struct bg { uchar n; s32int rpx0, rpy0, rpx1, rpy1, rpx, rpy; u16int tx, ty; u8int tnx, tny; u8int mosaic, mctr, lasti; u32int curc; u8int curpri; }; static bg bgst[4] = {{.n = 0}, {.n = 1}, {.n = 2}, {.n = 3}}; 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 { uchar w, wb, h; s16int x; uchar ysh; uchar *base; u16int *pal; u16int inc; u32int t0; u16int t1; uchar depth; 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, 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 sprinit(void) { 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}; 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 spr(int x1) { 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 bgpixel(bg *b, int i, u32int c, int pri) { 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; } } void bginit(bg *b, int scal, int) { u16int x, y; u16int *rr; 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; } 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]; }else{ rr = reg + (b->n << 1); x = rr[BG0HOFS] & 0x1ff; 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; } } void bgsinit(void) { switch(reg[DISPCNT] & 7){ case 0: bginit(&bgst[0], 0, 0); bginit(&bgst[1], 0, 0); bginit(&bgst[2], 0, 0); bginit(&bgst[3], 0, 0); break; case 1: bginit(&bgst[0], 0, 0); bginit(&bgst[1], 0, 0); bginit(&bgst[2], 1, 0); break; case 2: bginit(&bgst[2], 1, 0); bginit(&bgst[3], 1, 0); break; case 3: case 4: case 5: bginit(&bgst[2], 1, 1); break; } } void bitbg(bg *b, int x1) { u8int *base, *p, pri, d; u16int cnt, *rr, sx, sy; int i, v; cnt = reg[DISPCNT]; 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); } b->rpx += (s16int) rr[BG2PA]; b->rpy += (s16int) rr[BG2PC]; } } void txtbg(bg *b, int x1) { 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]; 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; } 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 rotbg(bg *b, int x1) { uchar *p, v; u16int bgcnt, *rr, ta; int i, row, sz, x, y; 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; 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 windows(int x1) { static u8int wintab[8] = {2, 3, 1, 1, 0, 0, 0, 0}; int i, sx0, sx1; u16int v, h; u16int cnt; 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((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; } } 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 mix(u16int c1, u16int c2) { u16int r, g, b, eva, evb; eva = blda; evb = bldb; b = ((c1 & 0x7c00) * eva + (c2 & 0x7c00) * evb) >> 4; g = ((c1 & 0x03e0) * eva + (c2 & 0x03e0) * evb) >> 4; r = ((c1 & 0x001f) * eva + (c2 & 0x001f) * evb) >> 4; if(b > 0x7c00) b = 0x7c00; if(g > 0x03e0) g = 0x03e0; if(r > 0x001f) r = 0x001f; return b & 0x7c00 | g & 0x03e0 | r; } u16int brighten(u16int c1) { u16int r, g, b, y; y = bldy; b = c1 & 0x7c00; b = b + (0x7c00 - b) * y / 16; g = c1 & 0x03e0; g = g + (0x03e0 - g) * y / 16; r = c1 & 0x001f; r = r + (0x001f - r) * y / 16; if(b > 0x7c00) b = 0x7c00; if(g > 0x03e0) g = 0x03e0; if(r > 0x001f) r = 0x001f; return b & 0x7c00 | g & 0x03e0 | r; } u16int darken(u16int c1) { u16int r, g, b, y; y = 16 - bldy; b = c1 & 0x7c00; b = b * y / 16; g = c1 & 0x03e0; g = g * y / 16; r = c1 & 0x001f; r = r * y / 16; return b & 0x7c00 | g & 0x03e0 | r; } void colormath(int x1) { 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; } } } void linecopy(void) { u32int *p; uchar *q; u16int *r; u16int v; union { u16int w; u8int b[2]; } u; int n; p = pixcol; q = pic + ppuy * 240 * 2 * scale; r = (u16int*)q; n = 240; while(n--){ v = *p++; if(scale == 1){ *q++ = v; *q++ = v >> 8; continue; } u.b[0] = v; u.b[1] = v >> 8; if(scale == 2){ *r++ = u.w; *r++ = u.w; }else{ *r++ = u.w; *r++ = u.w; *r++ = u.w; } } } void syncppu(int x1) { int i; u16int cnt; if(hblank || ppuy >= 160) return; 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; } 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: txtbg(&bgst[0], x1); txtbg(&bgst[1], x1); rotbg(&bgst[2], x1); break; case 2: rotbg(&bgst[2], x1); rotbg(&bgst[3], x1); break; case 3: case 4: case 5: bitbg(&bgst[2], x1); } colormath(x1); ppux0 = x1; } void hblanktick(void *) { extern Event evhblank; u16int stat; stat = reg[DISPSTAT]; 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(ppuy < 160){ sprinit(); bgsinit(); }else if(ppuy == 160){ dmastart(DMAVBL); if((stat & IRQVBLEN) != 0) setif(IRQVBL); } if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8) setif(IRQVCTR); }else{ syncppu(240); if(ppuy < 160) linecopy(); 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; } }