ref: c3ebdd79471e75b51ab8cd18cb7aba112aada527
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;
}
}
}