#include <u.h>
#include <libc.h>
#include "6502.h"
#include "ppu.h"
byte ppuctrl = 0,
ppumask = 0,
ppustatus = 0x80,
ppuscrollx,
ppuscrolly,
ppuscrolli,
ppuaddrmsb,
ppumem[0x4000],
oamaddr,
ppureadbuf
;
word ppuaddr;
int curx = 0, cury = 0;
#define maxx 256
#define maxy 262
byte oam[256];
byte sprites[65];
void oamfill(byte b) {
int i, j = 0;
word p = b << 8;
for(i=0;i<64;i++) {
*(int*)(oam + 4 * i) = *(int*)(mem + p + 4 * i);
if(oam[i] < 0xEF)
sprites[j++] = i;
}
if(j < 64) sprites[j] = 255;
}
byte ppuread(word addr) {
switch(addr) {
case 0x2002: {
byte x = ppustatus;
ppustatus &= 0x7f;
ppuscrolli = 0;
return x;
}
case 0x2007: {
byte x = ppureadbuf;
ppureadbuf = ppumem[ppuaddr++];
return x;
}
default: sysfatal("read from ppu register %x either not permitted or not implemented", addr);
}
return 0;
}
void ppuwrite(word addr, byte val) {
switch(addr) {
case 0x2000: ppuctrl = val; break;
case 0x2001: ppumask = val; break;
case 0x2003: oamaddr = val; break;
// case 0x2004: oammem[oamaddr++] = val; break;
case 0x2005: switch(ppuscrolli) {
case 0: ppuscrollx = val;
ppuscrolli = 1;
break;
case 1: ppuscrolly = val;
ppuscrolli = 0;
break;
}; break;
case 0x2006: switch(ppuscrolli) {
case 0: ppuaddrmsb = val;
ppuscrolli = 1;
break;
case 1: ppuaddr = (ppuaddrmsb << 8) | val;
ppuscrolli = 0;
break;
}; break;
case 0x2007:
ppumem[(ppuaddr) % 0x4000] = val;
if(ppuctrl & 4)
ppuaddr += 32;
else
ppuaddr++;
break;
default: sysfatal("write to ppu register %x either not permitted or not implemented", addr);
}
}
const int colortable[] = {
0x788084, 0x0000FC, 0x0000C4, 0x4028C4, 0x94008C, 0xAC0028,
0xAC1000, 0x8C1800, 0x503000, 0x007800, 0x006800, 0x005800,
0x004058, 0x000000, 0x000000, 0x000008, 0xBCC0C4, 0x0078FC,
0x0088FC, 0x6848FC, 0xDC00D4, 0xE40060, 0xFC3800, 0xE46018,
0xAC8000, 0x00B800, 0x00A800, 0x00A848, 0x008894, 0x2C2C2C,
0x000000, 0x000000, 0xFCF8FC, 0x38C0FC, 0x6888FC, 0x9C78FC,
0xFC78FC, 0xFC589C, 0xFC7858, 0xFCA048, 0xFCB800, 0xBCF818,
0x58D858, 0x58F89C, 0x00E8E4, 0x606060, 0x000000, 0x000000,
0xFCF8FC, 0xA4E8FC, 0xBCB8FC, 0xDCB8FC, 0xFCB8FC, 0xF4C0E0,
0xF4D0B4, 0xFCE0B4, 0xFCD884, 0xDCF878, 0xB8F878, 0xB0F0D8,
0x00F8FC, 0xC8C0C0, 0x000000, 0x000000
};
int drawbg(void) {
int x = curx, y = cury - 22;
int nx = x / 8, ny = y / 8, rx = 7 - (x % 8), ry = y % 8;
int nametable = 0x2000;
byte nte = ppumem[nametable + nx + ny * 32];
byte ate = ppumem[nametable + 0x3C0 + nx/4 + (ny/4) * 8];
byte subtilenum = ((nx & 2) >> 1) | (ny & 2);
byte paln = (ate >> (subtilenum << 1)) & 3;
byte* pal = ppumem + 0x3F00 + (paln << 2);
byte* pt = ppumem + 16 * nte;
if(ppuctrl & (1<<4)) pt += 0x1000;
byte coli = ((pt[ry] >> rx) & 1) | (((pt[ry+8] >> rx) & 1) << 1);
if(coli > 0) {
drawpixel(x, y, colortable[pal[coli] & 0x3F]);
return 1;
}
else {
drawpixel(x, y, colortable[ppumem[0x3F00] & 0x3F]);
return 0;
}
}
void drawspr(int opbg) {
int x = curx, y = cury - 22;
register int i;
int h16 = (ppuctrl & 32) ? 1 : 0;
for(i=0;sprites[i] != 0xFF;i++) {
byte spy = oam[sprites[i]];
byte spi = oam[sprites[i]+1];
byte spa = oam[sprites[i]+2];
byte spx = oam[sprites[i]+3];
if(x < spx ||
x >= spx + 8 ||
y < spy + 1 ||
y >= spy + 9 + h16 * 8) continue;
int cx = x - spx, cy = y - spy - 1;
if(!(spa & (1<<6))) cx = 7 - cx;
if(spa & (1<<7)) cy = (h16 ? 15 : 7) - cy;
byte* pal = ppumem + 0x3F00 + ((4 + (spa & 3)) << 2);
byte coli;
if(h16) {
byte* pt = ppumem + 16 * (spi & ~1);
if(spi & 1) pt += 0x1000;
if(cy >= 8) cy += 8;
coli = ((pt[cy] >> cx) & 1) | (((pt[cy+8] >> cx) & 1) << 1);
} else {
byte* pt = ppumem + 16 * spi;
if(ppuctrl & (1<<3)) pt += 0x1000;
coli = ((pt[cy] >> cx) & 1) | (((pt[cy+8] >> cx) & 1) << 1);
}
if(coli > 0) {
if(i == 0) {
if(opbg)
ppustatus |= 0x40;
else
ppustatus &= ~0x40;
}
if(!(spa & (1<<5)) || !opbg)
drawpixel(x, y, colortable[pal[coli] & 0x3F]);
}
else if(i == 0) ppustatus &= ~0x40;
}
}
void ppuadvance() {
curx++;
if(curx >= maxx) {
curx = 0;
cury++;
if(cury >= maxy) {
cury = 0;
updatedisplay();
ppustatus |= 0x80;
if(ppuctrl & 0x80) nmi();
}
}
int opbg = 0;
if(cury >= 22 && (ppumask & (1<<3)))
opbg = drawbg();
if(cury >= 22 && (ppumask & (1<<4)))
drawspr(opbg);
}
|