|
- #include <stdio.h>
-
- #include "ppu.h"
-
-
- // TODO: Retain open bus bits?
-
-
- #define ppu_reg_ctrl (0x2000U)
- #define ppu_reg_mask (0x2001U)
- #define ppu_reg_status (0x2002U)
- #define oam_reg_addr (0x2003U)
- #define oam_reg_data (0x2004U)
- #define ppu_reg_scroll (0x2005U)
- #define ppu_reg_addr (0x2006U)
- #define ppu_reg_data (0x2007U)
- #define oam_reg_dma (0x4014U)
-
-
- uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) {
- uint8_t val = 0;
-
- if (ppu_reg_status == addr) {
- val = ppu->status;
- ppu->latch = 0;
-
- } else if (oam_reg_data == addr) {
- val = ppu->oam[ppu->oam_addr];
-
- } else if (ppu_reg_data == addr) {
- val = ppu->data;
-
- if (ppu->addr < nes_ppu_mem_vram_start) {
- ppu->data = ppu->chr_mem[ppu->addr];
- } else if (ppu->addr < nes_ppu_mem_vram_start +
- nes_ppu_mem_vram_size) {
- ppu->data = ppu->vram[ppu->addr -
- nes_ppu_mem_vram_start];
- } else if (ppu->addr < nes_ppu_mem_pal_start) {
- ppu->data = ppu->chr_mem[ppu->addr];
- } else if (ppu->addr < nes_ppu_mem_size) {
- uint8_t pal_addr =
- (ppu->addr - nes_ppu_mem_pal_start) &
- (nes_ppu_mem_pal_size - 1);
- ppu->data = ppu->palette[pal_addr];
- }
-
- ppu->addr += (ppu->status & ppu_Control_VRAM_Inc) ?
- 32 : 1;
- }
-
- // fprintf(stdout, "PPU: <-R $%04x %02x\n", addr, val);
-
- return val;
- }
-
- void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) {
- // fprintf(stdout, "PPU: W-> $%04x %02x\n", addr, val);
-
- if (ppu_reg_ctrl == addr) {
- ppu->control = val;
- // TODO: Trigger NMI if it's enabled during VBlank?
-
- } else if (oam_reg_addr == addr) {
- ppu->oam_addr = val;
-
- } else if (oam_reg_data == addr) {
- ppu->oam[ppu->oam_addr++] = val;
-
- } else if (ppu_reg_mask == addr) {
- ppu->mask = val;
-
- } else if (ppu_reg_scroll == addr) {
- if (ppu->latch) {
- ppu->scroll &= 0xFF00U;
- ppu->scroll |= val;
- } else {
- ppu->scroll &= 0x00FFU;
- ppu->scroll |= (uint16_t)val << 8;
- }
- ppu->latch = !ppu->latch;
-
- } else if (ppu_reg_addr == addr) {
- if (ppu->latch) {
- ppu->addr &= 0x3F00U;
- ppu->addr |= val;
- // printf("PPU: VRAM ADDR %04x\n", ppu->addr);
-
- } else {
- ppu->addr &= 0x00FFU;
- ppu->addr |= (uint16_t)val << 8;
- }
- ppu->latch = !ppu->latch;
-
- } else if (ppu_reg_data == addr) {
- if (ppu->addr >= nes_ppu_mem_size) {
- printf("!!! PPU: MEM OOB: %04x", ppu->addr);
-
- } else if (ppu->addr >= nes_ppu_mem_pal_start) {
- uint8_t pal_addr =
- (ppu->addr - nes_ppu_mem_pal_start) &
- (nes_ppu_mem_pal_size - 1);
- // fprintf(stderr, "PPU PAL %02x < %02x\n", pal_addr, val);
- ppu->palette[pal_addr] = val;
-
- } else if (ppu->addr >= nes_ppu_mem_vram_start) {
- uint16_t vram_addr = ppu->addr - nes_ppu_mem_vram_start;
- if (vram_addr >= nes_ppu_mem_vram_size) {
- printf("!!! PPU: VRAM OOB: %04x", vram_addr);
- vram_addr &= (nes_ppu_mem_vram_size - 1);
- }
- ppu->vram[vram_addr] = val;
- }
-
- ppu->addr += (ppu->status & ppu_Control_VRAM_Inc) ?
- 32 : 1;
- }
- }
-
- void nes_ppu_reset(nes_ppu* ppu) {
- ppu->control = 0;
- ppu->mask = 0;
- ppu->latch = 0;
- ppu->scroll = 0;
- ppu->data = 0;
- ppu->frame = 0;
- ppu->scanline = 0;
- ppu->cycle = 0;
- }
-
- void nes_ppu_init(nes_ppu* ppu, uint8_t* chr_mem) {
- ppu->chr_mem = chr_mem;
- ppu->status = 0;
- ppu->oam_addr = 0;
- ppu->addr = 0;
- nes_ppu_reset(ppu);
- }
-
- int nes_ppu_run(nes_ppu* ppu, int cycles) {
- int vblank = 0;
-
- ppu->cycle += cycles;
-
- if ( 0 != ppu->hit_line &&
- ppu->scanline > ppu->hit_line &&
- ppu->cycle > ppu->hit_dot) {
- ppu->status |= ppu_Status_Hit;
- }
-
- while (ppu->cycle >= nes_ppu_dots) {
- ppu->cycle -= nes_ppu_dots;
- if ( ppu->scanline <= nes_ppu_prerender &&
- (ppu->frame & 1)) {
- // Prerender line is one dot shorter in odd frames
- // Fake it by incrementing the cycle in this case
- ppu->cycle++;
- }
- ppu->scanline++;
- if (ppu->scanline >= nes_ppu_prerender +
- nes_ppu_height +
- nes_ppu_postrender +
- nes_ppu_vblank) {
- ppu->status &= ~(ppu_Status_VBlank | ppu_Status_Hit);
- ppu->hit_line = 0;
- ppu->hit_dot = 0;
- ppu->scanline = 0;
- ppu->frame++;
- // TODO: Render callback if vblank was previously set
- } else if (ppu->scanline >= nes_ppu_prerender +
- nes_ppu_height +
- nes_ppu_postrender) {
- ppu->status |= ppu_Status_VBlank;
- if (ppu->control & ppu_Control_VBlank) {
- vblank = 1;
- }
- }
- }
-
- return vblank;
- }
-
-
- int nes_ppu_cycles_til_vblank(nes_ppu* ppu) {
- int cycles_til_vblank = nes_ppu_active_cycles - (
- ppu->cycle + (ppu->scanline * nes_ppu_dots));
- return (cycles_til_vblank > 0 ? cycles_til_vblank :
- nes_ppu_vblank_cycles + cycles_til_vblank);
- }
|