emu_nes/ppu.c

157 lines
2.9 KiB
C

#include "ppu.h"
struct ppu ppu = {0};
static void
vram_addr_inc(void)
{
ppu.regs.v += (ppu.ctrl.inc_mode) ? 32 : 1;
}
static uint16_t
vram_addr_mirror(uint16_t addr)
{
uint8_t nametable;
addr &= 0x2fff;
addr -= 0x2000;
nametable = addr / 0x400;
switch (ppu.rom->mirror) {
case M_HORIZONTAL:
if (nametable == 1 || nametable == 2)
return addr - 0x400;
else if (nametable == 3)
return addr - 0x800;
break;
case M_VERTICAL:
if (nametable == 2 || nametable == 3)
return addr - 0x800;
default:
break;
};
return addr;
}
void
ppu_tick(void)
{
ppu.cycles++;
if (ppu.cycles > 256 && ppu.cycles <= 320)
ppu.oam_addr = 0;
if (ppu.cycles >= 341) {
ppu.cycles -= 341;
ppu.scanlines++;
}
if (ppu.scanlines >= 262)
ppu.scanlines -= 262;
}
uint8_t
ppu_read(uint16_t addr)
{
uint8_t tmp;
switch (addr) {
case 0x2002:
tmp = (ppu.status.vblank << 7)
| (ppu.status.sprite0_hit << 6)
| (ppu.status.sprite_overflow << 5);
ppu.status.vblank = 0;
ppu.regs.write_toggle = 0;
return tmp;
case 0x2004:
return ppu.oam[ppu.oam_addr];
case 0x2007:
tmp = ppu.last_read;
if (addr <= 0x1fff)
ppu.last_read = ppu.rom->chr_rom[ppu.regs.v];
else if (addr <= 0x2fff)
ppu.last_read = ppu.vram[vram_addr_mirror(addr)];
vram_addr_inc();
return tmp;
default:
fprintf(stderr, "Invalid PPU read at address $%04\n", addr);
return 0;
}
}
void
ppu_write(uint16_t addr, uint8_t byte)
{
switch (addr) {
case 0x2000:
ppu.ctrl.nametable_base = byte & 3;
ppu.ctrl.inc_mode = byte & (1 << 2);
ppu.ctrl.sprite_tile_sel = byte & (1 << 3);
ppu.ctrl.bg_tile_sel = byte & (1 << 4);
ppu.ctrl.sprite_height = byte & (1 << 5);
ppu.ctrl.master_slave = byte & (1 << 6);
ppu.ctrl.nmi_enable = byte & (1 << 7);
if (ppu.ctrl.nametable_base & 1)
ppu.regs.scroll_x += 256;
if (ppu.ctrl.nametable_base & 2)
ppu.regs.scroll_y += 240;
/*
* TODO: send NMI if vblank flag still set and nmi_enable
* changes from 0 to 1
*/
break;
case 0x2001:
ppu.mask.grayscale = byte & 1;
ppu.mask.bg_left_enable = byte & (1 << 1);
ppu.mask.sprite_left_enable = byte & (1 << 2);
ppu.mask.bg_enable = byte & (1 << 3);
ppu.mask.sprite_enable = byte & (1 << 4);
ppu.mask.colour_emphasis = byte & (1 << 5);
break;
case 0x2003:
ppu.oam_addr = byte;
break;
case 0x2004:
ppu.oam[ppu.oam_addr++] = byte;
break;
case 0x2005:
if (ppu.regs.write_toggle == 0)
ppu.regs.scroll_x = byte;
else
ppu.regs.scroll_y = byte;
ppu.regs.write_toggle = !ppu.regs.write_toggle;
break;
case 0x2006:
if (ppu.regs.write_toggle == 0)
ppu.regs.v = (byte << 8);
else {
ppu.regs.v |= byte;
ppu.regs.v &= 0x3fff;
}
ppu.regs.write_toggle = !ppu.regs.write_toggle;
break;
case 0x2007:
ppu.vram[ppu.regs.v] = byte;
vram_addr_inc();
break;
case 0x4014:
break;
default:
fprintf(stderr, "Invalid PPU write at address $%04X\n", addr);
return;
}
}