|
- #include <stdio.h>
-
- #include "ppu.h"
- #include "mapper.h"
- #include "cart.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) {
- OAM_LOG("PPU: OAM READ %02x > %02x\n", ppu->oam_addr, val);
- val = ((uint8_t*)ppu->oam)[ppu->oam_addr];
-
- } else if (ppu_reg_data == addr) {
- if (ppu->addr >= nes_ppu_mem_pal_start) {
- PPU_LOG("PPU: PAL READ %04x > %02x\n", ppu->addr, val);
- uint8_t pal_addr =
- (ppu->addr - nes_ppu_mem_pal_start) &
- (nes_ppu_mem_pal_size - 1);
- val = ppu->palette[pal_addr];
-
- } else {
- val = ppu->data;
-
- if (ppu->addr < nes_ppu_mem_vram_start) {
- PPU_LOG("PPU: CHR MEM READ %04x > %02x\n", ppu->addr, val);
- ppu->data = nes_chr_read(ppu->mapper,
- ppu->map_data,
- ppu->addr);
- } else if (ppu->addr < nes_ppu_mem_vram_start +
- nes_ppu_mem_vram_size) {
- VRAM_LOG("PPU: VRAM READ %04x > %02x\n", ppu->addr, val);
- ppu->data = nes_vram_read(
- ppu->mapper, ppu->map_data,
- ppu->addr - nes_ppu_mem_vram_start
- );
- } else if (ppu->addr < nes_ppu_mem_pal_start) {
- PPU_LOG("PPU: BLANK READ %04x > %02x\n", ppu->addr, val);
- /*
- ppu->data = *(nes_map_chr_addr(ppu->mapper,
- ppu->addr));
- */
- }
- }
-
- ppu->addr += (ppu->control & ppu_Control_VRAM_Inc) ?
- 32 : 1;
- }
-
- PPU_LOG("PPU: <-R $%04x %02x\n", addr, val);
-
- return val;
- }
-
- // Copy t to v (decoded into scroll_x, control)
- static inline void nes_ppu_internal_copy_x(nes_ppu* ppu) {
- ppu->control &= ~(1U);
- ppu->control |= ((ppu->t >> 10) & 1U);
-
- ppu->scroll_x = ((ppu->t & 0b11111U) << 3) |
- (ppu->x & 0b111U);
- }
-
- // Copy t to v (decoded into scroll_y, control)
- static inline void nes_ppu_internal_copy_y(nes_ppu* ppu) {
- ppu->control &= ~(0b10U);
- ppu->control |= ((ppu->t >> 10) & 0b10U);
-
- ppu->scroll_y = ((ppu->t & 0x03E0U) >> 2) |
- ((ppu->t & 0x7000U) >> 12);
- }
-
- void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) {
- PPU_LOG("PPU: W-> $%04x %02x\n", addr, val);
-
- if (ppu_reg_ctrl == addr) {
- PPU_LOG("PPU: CTRL %02x\n", val);
- ppu->control &= ppu_Control_Nametable_Mask;
- ppu->control |= (val & ~ppu_Control_Nametable_Mask);
- ppu->t &= ~(0xC00U);
- ppu->t |= (uint16_t)(val & ppu_Control_Nametable_Mask) << 10;
-
- } else if (oam_reg_addr == addr) {
- OAM_LOG("PPU: OAM ADDR %02x\n", val);
- ppu->oam_addr = val;
-
- } else if (oam_reg_data == addr) {
- OAM_LOG("PPU: OAM %02x < %02x\n", ppu->oam_addr, val);
- ((uint8_t*)ppu->oam)[ppu->oam_addr++] = val;
-
- } else if (ppu_reg_mask == addr) {
- PPU_LOG("PPU: Mask %02x\n", val);
- ppu->mask = val;
-
- } else if (ppu_reg_scroll == addr) {
- if (ppu->latch) {
- PPU_LOG("PPU: Scroll Y %02x\n", val);
- ppu->t &= 0b0000110000011111U;
- ppu->t |= (uint16_t)(val & 0b00000111U) << 12;
- ppu->t |= (uint16_t)(val & 0b11111000U) << 2;
-
- } else {
- PPU_LOG("PPU: Scroll X %02x\n", val);
- ppu->t &= ~(0b11111U);
- ppu->t |= (val & 0b11111000U) >> 3;
- ppu->x = (val & 0b111U);
- }
-
- ppu->latch = !ppu->latch;
-
- } else if (ppu_reg_addr == addr) {
- VRAM_LOG("PPU: ADDR %02x\n", val);
-
- if (ppu->latch) {
- ppu->t &= 0xFF00U;
- ppu->t |= val;
-
- ppu->addr = (ppu->t & 0x3FFFU);
- VRAM_LOG("PPU: VRAM ADDR %04x\n", ppu->addr);
-
- nes_ppu_internal_copy_x(ppu);
- nes_ppu_internal_copy_y(ppu);
-
- PPU_LOG("PPU: Scroll N, X, Y = %d, %d, %d\n",
- ppu->control & ppu_Control_Nametable_Mask,
- ppu->scroll_x, ppu->scroll_y);
-
- } else {
- ppu->t &= 0x00FFU;
- ppu->t |= (uint16_t)(0x3FU & val) << 8;
- }
-
- ppu->latch = !ppu->latch;
-
- } else if (ppu_reg_data == addr) {
- if (ppu->addr >= nes_ppu_mem_size) {
- printf("!!! PPU: MEM OOB: %04x\n", 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);
- PPU_LOG("PPU: PAL %02x < %02x\n", pal_addr, val);
- ppu->palette[pal_addr] = val;
- if ((pal_addr & 0b11) == 0) {
- ppu->palette[pal_addr & 0xF] = 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\n", vram_addr);
- vram_addr &= (nes_ppu_mem_vram_size - 1);
- }
- #ifdef DEBUG_VRAM
- {
- int page = vram_addr >> 10;
- int loc = vram_addr & 0x3FFU;
- if (loc < 960) {
- int y = loc / 32;
- int x = loc % 32;
- printf("PPU: VRAM Page %d @ %2d,%2d : %02x\n",
- page, y, x, val);
- } else {
- printf("PPU: VRAM Attr %2d : %02x\n",
- loc - 960, val);
- }
- }
- #endif // DEBUG_VRAM
- // printf("PPU: VRAM %04x < %02x\n", vram_addr, val);
- nes_vram_write(ppu->mapper, ppu->map_data,
- vram_addr, val);
-
- } else {
- // PPU_LOG("PPU: CHR MEM WRITE %04x > %02x\n", ppu->addr, val);
- nes_chr_write(ppu->mapper, ppu->map_data,
- ppu->addr, val);
- }
-
- ppu->addr += (ppu->control & ppu_Control_VRAM_Inc) ?
- 32 : 1;
- }
- }
-
- void nes_ppu_reset(nes_ppu* ppu) {
- ppu->control = 0;
- ppu->mask = 0;
- ppu->latch = 0;
- ppu->scroll_x = 0;
- ppu->scroll_y = 0;
- ppu->data = 0;
- ppu->frame = 0;
- ppu->scanline = 0;
- ppu->cycle = 0;
- }
-
- int nes_ppu_init(nes_ppu* ppu, const nes_cart* cart) {
- ppu->mapper = cart->mapper;
- ppu->map_data = cart->map_data;
- ppu->status = 0;
- ppu->oam_addr = 0;
- ppu->addr = 0;
- ppu->t = 0;
- nes_ppu_reset(ppu);
- return 0;
- }
-
- nes_ppu_Result nes_ppu_run(nes_ppu* ppu, int cycles) {
- nes_ppu_Result result = ppu_Result_Running;
-
- int next_cycle = ppu->cycle + cycles;
-
- if ( ppu->scanline < nes_ppu_render &&
- ppu->cycle < 257 && next_cycle >= 257) {
- nes_ppu_internal_copy_x(ppu);
- if (ppu->scanline == 0) {
- nes_ppu_internal_copy_y(ppu);
- }
- }
-
- if ( NULL != ppu->mapper->scanline &&
- ppu->scanline < nes_ppu_render &&
- (ppu->mask & (ppu_Mask_Back | ppu_Mask_Sprite)) &&
- ppu->cycle < 260 && next_cycle >= 260) {
- ppu->mapper->scanline(ppu->map_data);
- }
-
- ppu->cycle = next_cycle;
-
- 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
- // TODO: Only if actually rendering
- ppu->cycle++;
- }
-
- ppu->scanline++;
-
- if (ppu->scanline >= nes_ppu_frame) {
- ppu->status &= ~(ppu_Status_VBlank | ppu_Status_Hit);
- ppu->hit_line = 0;
- ppu->hit_dot = 0;
- ppu->scanline = 0;
- ppu->frame++;
- result = ppu_Result_VBlank_Off;
-
- } else if (ppu->scanline >= nes_ppu_active) {
- ppu->status |= ppu_Status_VBlank;
- if (ppu->control & ppu_Control_VBlank) {
- result = ppu_Result_VBlank_On;
- }
-
- } else {
- if ( ppu->scanline > nes_ppu_prerender &&
- ppu->scanline < nes_ppu_render) {
- ppu->scroll_y++;
- if (ppu->scroll_y >= nes_ppu_render_h) {
- ppu->scroll_y -= nes_ppu_render_h;
- ppu->control ^= 0b10;
- }
- }
-
- result = ppu_Result_Ready;
- }
- }
-
- if ( ppu->hit_line > 0 &&
- ppu->scanline > ppu->hit_line &&
- ppu->cycle >= ppu->hit_dot) {
- if (!(ppu->status & ppu_Status_Hit)) {
- PPU_LOG("PPU: Hit @ %d, %d\n", ppu->hit_line + 1, ppu->hit_dot);
- PPU_LOG("(Currently @ %d, %d)\n", ppu->scanline, ppu->cycle);
- }
- ppu->status |= ppu_Status_Hit;
- }
-
- return result;
- }
|