ref: c8cf0cee47914699e3e7c10a7b7501af94b53b4e
dir: /sys/src/games/gba/ppu.c/
#include <u.h> #include <libc.h> #include <thread.h> #include "dat.h" #include "fns.h" int ppux, ppuy; uchar pic[240*160*2*3*3]; u8int bldy, blda, bldb; typedef struct bg bg; struct bg { uchar n; uchar depth; s32int rpx0, rpy0, rpx, rpy; s32int sx, sy; u16int tx, ty; u8int tnx, tny; u16int t; u8int *chr; u16int *pal; }; 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; 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; }; static sprite sprt[128], *sp = sprt; enum { SPRROT = 1<<8, SPRDIS = 1<<9, SPRDOUB = 1<<9, SPR8 = 1<<13, SPRWIDE = 1<<14, SPRTALL = 1<<15, SPRHFLIP = 1<<28, SPRVFLIP = 1<<29, SPRSIZE0 = 1<<30, SPRSIZE1 = 1<<31 }; void pixeldraw(int x, int y, u16int v) { uchar *p; u16int *q; union { u16int w; u8int b[2]; } u; 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; } } void pixel(u16int c, int n, int p) { 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; } } void tile(bg *b) { 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; } 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) { u16int cnt, x, y; u16int *rr; cnt = reg[DISPCNT]; 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; 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; 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){ 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) { u16int cnt; int v; uchar *p; u16int *rr; uchar *base; 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; } }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]; } void txtbg(bg *b) { u16int bgcnt; u8int x, v; 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); } } 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; } } 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; 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(void) { sprite *s; ushort dx; u32int t0; uchar v; ushort x, y; u16int c; int pv, ppri, pri; uchar d; uchar *b; 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; } 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(pv >= 0){ pixel(pv, 4, ppri); if(pv >> 16 != 0) objtrans = 1; } } 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 windows(void) { 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; } bgmask &= reg[WINOUT]; } windone: if(pixelpri[0] != 8 && (bgmask & 1<<4) == 0){ pixelpri[0] = 8; pixeldat[0] = pram[0] | 5 << 16; } } void colormath(void) { u8int src0; u16int bldcnt; if((bgmask & 1<<5) == 0) return; bldcnt = reg[BLDCNT]; src0 = pixeldat[0] >> 16; if(objtrans && src0 == 4) goto alpha; if((bldcnt & 3<<6) == 0 || (bldcnt & 1<<src0) == 0) return; switch(bldcnt >> 6 & 3){ case 1: alpha: if((bldcnt & 1<<8+(pixeldat[1]>>16)) == 0) return; pixeldat[0] = mix(pixeldat[0], pixeldat[1]); break; case 2: pixeldat[0] = brighten(pixeldat[0]); break; case 3: pixeldat[0] = darken(pixeldat[0]); break; } } void ppustep(void) { 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(++ppuy >= 228){ ppuy = 0; flush(); } if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8) setif(IRQVCTR); if(ppuy < 160){ bgsinit(); sprinit(); }else if(ppuy == 160){ if((stat & IRQVBLEN) != 0) setif(IRQVBL); dmastart(DMAVBL); } } }