ref: 1216c06c3bdf3f3fb953ec96de99e026ef318360
dir: /sys/src/games/nes/ppu.c/
#include <u.h> #include <libc.h> #include <thread.h> #include <draw.h> #include <mouse.h> #include "dat.h" #include "fns.h" int ppuy, ppux, odd; uchar pic[256*240*4*9]; static void pixel(int x, int y, int val, int back) { int Y; union { u8int c[4]; u32int l; } u; u32int *p, l; static u8int palred[64] = { 0x7C, 0x00, 0x00, 0x44, 0x94, 0xA8, 0xA8, 0x88, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x68, 0xD8, 0xE4, 0xF8, 0xE4, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3C, 0x68, 0x98, 0xF8, 0xF8, 0xF8, 0xFC, 0xF8, 0xB8, 0x58, 0x58, 0x00, 0x78, 0x00, 0x00, 0xFC, 0xA4, 0xB8, 0xD8, 0xF8, 0xF8, 0xF0, 0xFC, 0xF8, 0xD8, 0xB8, 0xB8, 0x00, 0xF8, 0x00, 0x00, }; static u8int palgreen[64] = { 0x7C, 0x00, 0x00, 0x28, 0x00, 0x00, 0x10, 0x14, 0x30, 0x78, 0x68, 0x58, 0x40, 0x00, 0x00, 0x00, 0xBC, 0x78, 0x58, 0x44, 0x00, 0x00, 0x38, 0x5C, 0x7C, 0xB8, 0xA8, 0xA8, 0x88, 0x00, 0x00, 0x00, 0xF8, 0xBC, 0x88, 0x78, 0x78, 0x58, 0x78, 0xA0, 0xB8, 0xF8, 0xD8, 0xF8, 0xE8, 0x78, 0x00, 0x00, 0xFC, 0xE4, 0xB8, 0xB8, 0xB8, 0xA4, 0xD0, 0xE0, 0xD8, 0xF8, 0xF8, 0xF8, 0xFC, 0xD8, 0x00, 0x00, }; static u8int palblue[64] = { 0x7C, 0xFC, 0xBC, 0xBC, 0x84, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xBC, 0xF8, 0xF8, 0xFC, 0xCC, 0x58, 0x00, 0x10, 0x00, 0x00, 0x00, 0x44, 0x88, 0x00, 0x00, 0x00, 0xF8, 0xFC, 0xFC, 0xF8, 0xF8, 0x98, 0x58, 0x44, 0x00, 0x18, 0x54, 0x98, 0xD8, 0x78, 0x00, 0x00, 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xC0, 0xB0, 0xA8, 0x78, 0x78, 0xB8, 0xD8, 0xFC, 0xF8, 0x00, 0x00, }; u.c[0] = palblue[val]; u.c[1] = palgreen[val]; u.c[2] = palred[val]; u.c[3] = back ? 0 : 0xFF; l = u.l; if(scale == 3){ p = ((u32int*)pic) + y * 3 * 3 * 256 + 3 * x; for(Y = 0; Y < 3; Y++){ *p++ = l; *p++ = l; *p = l; p += 3 * 256 - 2; } }else if(scale == 2){ p = ((u32int*)pic) + y * 2 * 2 * 256 + 2 * x; *p++ = l; *p = l; p += 2 * 256 - 1; *p++ = l; *p = l; }else{ p = ((u32int*)pic) + y * 256 + x; *p = l; } } static int iscolor(int x, int y) { return pic[y * scale * scale * 256 * 4 + x * scale * 4 + 3] != 0; } static int pal(int c, int a, int spr) { if(c == 0) return ppuread(0x3F00); return ppuread(0x3F00 | ((a&3)<<2) | (c & 3) | (spr << 4)); } static void incppuy(void) { int y; if((ppuv & 0x7000) != 0x7000){ ppuv += 0x1000; return; } y = (ppuv >> 5) & 31; if(y++ == 29){ y = 0; ppuv ^= 0x800; } ppuv = (ppuv & 0x0C1F) | ((y & 31) << 5); } static void drawbg(void) { static int t; u8int c, a; static u8int nr1, nr2, na; static u16int r1, r2, a1, a2; if(ppux >= 2 && ppux <= 257 || ppux >= 322 && ppux <= 337){ c = (r1 >> (15-ppusx)) & 1 | (r2 >> (14-ppusx)) & 2; if(ppuy < 240 && ppux <= 257){ a = (a1 >> (15-ppusx)) & 1 | (a2 >> (14-ppusx)) & 2; pixel(ppux-2, ppuy, pal(c, a, 0), c == 0); } r1 <<= 1; r2 <<= 1; a1 <<= 1; a2 <<= 1; } if(ppux >= 256 && ppux <= 320){ if(ppux == 256) incppuy(); if(ppux == 257) ppuv = (ppuv & 0x7BE0) | (pput & 0x041F); return; } switch(ppux & 7){ case 0: if(ppux != 0){ if((ppuv & 0x1f) == 0x1f){ ppuv &= ~0x1f; ppuv ^= 0x400; }else ppuv++; } break; case 1: t = ppuread(0x2000 | ppuv & 0x0FFF); if(ppux != 1){ r1 |= nr1; r2 |= nr2; if(na & 1) a1 |= 0xff; if(na & 2) a2 |= 0xff; } break; case 3: na = ppuread(0x23C0 | ppuv & 0x0C00 | ((ppuv & 0x0380) >> 4) | ((ppuv & 0x001C) >> 2)); if((ppuv & 0x0002) != 0) na >>= 2; if((ppuv & 0x0040) != 0) na >>= 4; break; case 5: nr1 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12); break; case 7: nr2 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12 | 8); break; } } static void drawsprites(int show) { uchar *p; int big, dx, dy, i, x, cc, pri; u8int r1, r2, c; static int n, m, nz, s0, t0; static struct { u8int x, a; u16int t; } s[8], *sp; static struct { u8int x, a, r1, r2; } t[8]; big = (mem[PPUCTRL] & BIGSPRITE) != 0; if(ppux == 65){ s0 = 0; for(p = oam, sp = s, n = 0; p < oam + sizeof(oam); p += 4){ if((dy = p[0]) >= 0xEF) continue; dy = ppuy - dy; if(dy < 0 || dy >= (big ? 16 : 8)) continue; if(p == oam) s0 = 1; sp->t = p[1]; sp->a = p[2]; sp->x = p[3]; if((sp->a & (1<<7)) != 0) dy = (big ? 15 : 7) - dy; if(big){ sp->t |= (sp->t & 1) << 8; if(dy >= 8){ sp->t |= 1; dy -= 8; }else sp->t &= 0x1fe; }else sp->t |= (mem[PPUCTRL] & SPRTABLE) << 5; sp->t = sp->t << 4 | dy; sp++; if(++n == 8) break; } } if(ppux >= 2 && ppux <= 257 && m > 0){ x = ppux - 2; dx = x - t[0].x; if(t0 && dx >= 0 && dx < 8 && ppux != 257){ if((nz & 1) != 0 && iscolor(x, ppuy) && show) mem[PPUSTATUS] |= SPRITE0HIT; nz >>= 1; } cc = -1; pri = 0; for(i = m - 1; i >= 0; i--){ dx = x - t[i].x; if(dx < 0 || dx > 7) continue; c = (t[i].r1 & 1) | (t[i].r2 & 1) << 1; if(c != 0){ cc = pal(c, t[i].a & 3, 1); pri = (t[i].a & (1<<5)) == 0; } t[i].r1 >>= 1; t[i].r2 >>= 1; } if(cc != -1 && show && (pri || !iscolor(x, ppuy))) pixel(x, ppuy, cc, 0); } if(ppux == 257){ for(i = 0; i < n; i++){ r1 = ppuread(s[i].t); r2 = ppuread(s[i].t | 8); if((s[i].a & (1<<6)) == 0){ r1 = ((r1 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32; r2 = ((r2 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32; } t[i].x = s[i].x; t[i].a = s[i].a; t[i].r1 = r1; t[i].r2 = r2; } m = n; nz = t[0].r1 | t[0].r2; t0 = s0; } } static void flush(void) { extern Rectangle picr; extern Image *tmp, *bg; extern Mousectl *mc; static vlong old, delta; vlong new, diff; Mouse m; Point p; int h; h = 240; if(oflag) h -= 16; while(nbrecv(mc->c, &m) > 0) ; if(nbrecvul(mc->resizec) > 0){ if(getwindow(display, Refnone) < 0) sysfatal("resize failed: %r"); p = divpt(addpt(screen->r.min, screen->r.max), 2); picr = (Rectangle){subpt(p, Pt(scale * 128, scale * h/2)), addpt(p, Pt(scale * 128, scale * h/2))}; if(bg->chan != screen->chan){ freeimage(bg); bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF); } draw(screen, screen->r, bg, nil, ZP); } if(screen->chan != tmp->chan || !rectinrect(picr, screen->r)){ loadimage(tmp, tmp->r, pic + oflag*8*256*4*scale*scale, 256*h*4*scale*scale); draw(screen, picr, tmp, nil, ZP); }else loadimage(screen, picr, pic + oflag*8*256*4*scale*scale, 256*h*4*scale*scale); flushimage(display, 1); memset(pic, sizeof pic, 0); if(audioout() < 0){ new = nsec(); diff = 0; if(old != 0){ diff = BILLION/60 - (new - old) - delta; if(diff >= MILLION) sleep(diff/MILLION); } old = nsec(); if(diff != 0){ diff = (old - new) - (diff / MILLION) * MILLION; delta += (diff - delta) / 100; } } } void ppustep(void) { extern int nmi; int mask; if(ppuy < 240 || ppuy == 261){ mask = mem[PPUMASK]; if((mask & BGDISP) != 0) drawbg(); if((((mask & BGDISP) == 0 && ppux <= 257 || ppux < 10 && (mask & BG8DISP) == 0) && ppux >= 2) && ppuy != 261) pixel(ppux - 2, ppuy, ppuread(0x3F00), 1); if((mask & SPRITEDISP) != 0 && ppuy != 261) drawsprites(ppux >= 10 || (mask & SPRITE8DISP) != 0); if(ppux == 240 && (mask & SPRITEDISP) != 0) mapper[map](SCAN, 0); if(ppuy == 261){ if(ppux == 1) mem[PPUSTATUS] &= ~(PPUVBLANK|SPRITE0HIT); else if(ppux >= 280 && ppux <= 304 && (mask & BGDISP) != 0) ppuv = (pput & 0x7BE0) | (ppuv & 0x041F); } }else if(ppuy == 241){ if(ppux == 1){ mem[PPUSTATUS] |= PPUVBLANK; if((mem[PPUCTRL] & PPUNMI) != 0) nmi = 2; flush(); } } ppux++; if(ppux > 340){ ppux = 0; ppuy++; if(ppuy > 261){ ppuy = 0; if(odd && (mem[PPUMASK] & (BGDISP | SPRITEDISP)) != 0) ppux++; odd ^= 1; } } }