Plan 9 from Bell Labs’s /usr/web/sources/contrib/aiju/nes/ppu.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#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);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.