|
- #include <string.h>
-
- #include "ppu.h"
-
- //#define DEBUG "PPU"
- #include "log.h"
-
-
- static const uint8_t mirror_schemes[][4] = {
- [nes_Mirror_Horizontal] = {0, 0, 1, 1},
- [nes_Mirror_Vertical] = {0, 1, 0, 1},
- [nes_Mirror_Only_0] = {0, 0, 0, 0},
- [nes_Mirror_Only_1] = {1, 1, 1, 1},
- [nes_Mirror_Four] = {0, 1, 2, 3},
- };
-
-
- void nes_ppu_set_mirroring(nes_PPU_Memory* mem,
- nes_Nametable_Mirroring mirror) {
- for (int i = 0; i < 4; ++i) {
- mem->bank[nes_PPU_Nametable_Bank_Index + i] =
- vram_page(mem, (int)mirror_schemes[mirror][i]);
- }
- }
-
- void nes_ppu_find_hit_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
- int sprite_bank = !!(mem->ctrl & ppu_Control_Sprite_Bank);
- oam_sprite sprite = *(oam_sprite*)mem->oam;
- int sprite_height = (mem->ctrl & ppu_Control_Sprite_Size) ?
- 16 : 8;
- int stride = 1;
- int y_off = 0;
- if (sprite.attr & oam_Attr_Flip_Y) {
- stride = -1;
- y_off = sprite_height - 1;
- sprite_bank = (sprite.index & 1);
- sprite.index &= 0xFEU;
- }
-
- sprite_bank = (sprite_bank << 2) | (sprite.index >> 6);
-
- const int data_off = ((sprite.index & 0x3FU) << 4) +
- ((y_off & 8) << 1) + (y_off & 7);
- uint8_t* data = mem->bank[sprite_bank] + data_off;
-
- ppu->hit_line = -1;
-
- if (sprite.y + 1 <= nes_ppu_render_h && sprite.y > 0) {
- for (int line = 0; line < sprite_height; ++line) {
- if (data[0] | data[8]) {
- ppu->hit_line = sprite.y + 1 + line + nes_ppu_visible_line;
- break;
- }
- data += stride;
- }
- }
- }
-
-
- void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
- uint8_t* scanline_ptr = ppu->screen_data +
- ( nes_ppu_render_w *
- (ppu->scanline - nes_ppu_visible_line));
- uint8_t* ptr = scanline_ptr;
-
- if (!(mem->mask & ppu_Mask_Back)) {
- memset(ptr, 0xFFU, nes_ppu_render_w);
- /*
- // Emulate the happy part of the backdrop override quirk
- int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ?
- (mem->addr & 0x1FU) : 0;
- // Don't use the rendering palette (masked transparency)
- memset(ptr, mem->pal_bank[0x300 + pal_idx],
- nes_ppu_render_w);
- */
- } else {
- int back_bank = nes_PPU_Nametable_Bank_Index +
- ((mem->addr >> 10) & 3);
-
- int y_coarse = (mem->addr >> 5) & 0x1F;
- int y_fine = mem->addr >> 12;
- int x_coarse = mem->addr & 0x1FU;
-
- const int bank_off = !!(mem->ctrl & ppu_Control_Back_Bank) << 2;
-
- const uint8_t* nametable = mem->bank[back_bank] + (y_coarse * 32) + x_coarse;
- const uint8_t* attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3);
-
- // Partial left column
- ptr += 8 - mem->x;
-
- // Omit if left column is masked
- if (mem->mask & ppu_Mask_Left_Back) {
- const uint8_t* pal = mem->palette +
- (((attrs[x_coarse >> 2] >>
- ( (x_coarse & 2) +
- ((y_coarse & 2) << 1))) & 3) << 2);
- const int ch = *nametable;
- const int bank = (ch >> 6) + bank_off;
- const int addr_off = ((ch & 0x3F) << 4) + y_fine;
- const uint8_t* data = mem->bank[bank] + addr_off;
- const uint8_t pl0 = data[0];
- const uint8_t pl1 = data[8];
- const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
- const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
- switch (mem->x) {
- case 0: ptr[-8] = pal[(pat1 >> 6) & 3];
- case 1: ptr[-7] = pal[(pat0 >> 6) & 3];
- case 2: ptr[-6] = pal[(pat1 >> 4) & 3];
- case 3: ptr[-5] = pal[(pat0 >> 4) & 3];
- case 4: ptr[-4] = pal[(pat1 >> 2) & 3];
- case 5: ptr[-3] = pal[(pat0 >> 2) & 3];
- case 6: ptr[-2] = pal[(pat1 >> 0) & 3];
- case 7: ptr[-1] = pal[(pat0 >> 0) & 3];
- }
- }
-
- // TODO: Mapper ppu_bus
-
- ++x_coarse;
- ++nametable;
-
- void __attribute__((always_inline)) inline render_bg(void) {
- const uint8_t* pal = mem->palette +
- (((attrs[x_coarse >> 2] >>
- ( (x_coarse & 2) +
- ((y_coarse & 2) << 1))) & 3) << 2);
- const int ch = *nametable;
- const int bank = (ch >> 6) + bank_off;
- const int addr_off = ((ch & 0x3F) << 4) + y_fine;
- const uint8_t* data = mem->bank[bank] + addr_off;
- const uint8_t pl0 = data[0];
- const uint8_t pl1 = data[8];
- const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
- const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
-
- ptr[0] = pal[(pat1 >> 6) & 3];
- ptr[1] = pal[(pat0 >> 6) & 3];
- ptr[2] = pal[(pat1 >> 4) & 3];
- ptr[3] = pal[(pat0 >> 4) & 3];
- ptr[4] = pal[(pat1 >> 2) & 3];
- ptr[5] = pal[(pat0 >> 2) & 3];
- ptr[6] = pal[(pat1 >> 0) & 3];
- ptr[7] = pal[(pat0 >> 0) & 3];
- ptr += 8;
- }
-
- // Left Nametable
- for (; x_coarse < 32; ++x_coarse) {
- render_bg();
- // TODO: Mapper ppu_bus
- ++nametable;
- }
-
- // Right Nametable
- back_bank ^= 0b01;
- nametable = mem->bank[back_bank] + (y_coarse * 32);
- attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3);
-
- for (x_coarse = 0; x_coarse < (mem->addr & 0x1FU); ++x_coarse) {
- render_bg();
- // TODO: Mapper ppu_bus
- ++nametable;
- }
-
- // Partial right column
- {
- const uint8_t* pal = mem->palette +
- (((attrs[x_coarse >> 2] >>
- ( (x_coarse & 2) +
- ((y_coarse & 2) << 1))) & 3) << 2);
- const int ch = *nametable;
- const int bank = (ch >> 6) + bank_off;
- const int addr_off = ((ch & 0x3F) << 4) + y_fine;
- const uint8_t* data = mem->bank[bank] + addr_off;
- const uint8_t pl0 = data[0];
- const uint8_t pl1 = data[8];
- const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
- const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
- switch (mem->x) {
- case 8: ptr[7] = pal[(pat0 >> 0) & 3];
- case 7: ptr[6] = pal[(pat1 >> 0) & 3];
- case 6: ptr[5] = pal[(pat0 >> 2) & 3];
- case 5: ptr[4] = pal[(pat1 >> 2) & 3];
- case 4: ptr[3] = pal[(pat0 >> 4) & 3];
- case 3: ptr[2] = pal[(pat1 >> 4) & 3];
- case 2: ptr[1] = pal[(pat0 >> 6) & 3];
- case 1: ptr[0] = pal[(pat1 >> 6) & 3];
- }
- }
- }
-
- // TODO: Mapper ppu_bus
-
- if (!(mem->mask & ppu_Mask_Left_Back)) {
- memset(scanline_ptr, 0xFFU, 8);
- }
-
- // TODO: Mapper VROM switch
- /*
- if (mem->mask & ppu_Mask_Sprite) {
- // This time, we do need overflow protection
- // uint8_t sprite_data[nes_ppu_render_w + 8] = {0};
-
- const int sprite_height = (mem->ctrl & ppu_Control_Sprite_Size) ?
- 16 : 8;
- const int scanline = ppu->scanline - nes_ppu_visible_line;
-
- int bank = !!(mem->ctrl & ppu_Control_Sprite_Bank) << 2;
-
- int n_sprites = 0;
- const oam_sprite* sprites = (oam_sprite*)mem->oam;
- const oam_sprite* sprite = sprites +
- NES_PPU_SPRITE_COUNT - 1;
- for ( ; sprite >= sprites && n_sprites < 8; --sprite) {
- int y = sprite->y + 1;
- if (y > scanline || y + sprite_height <= scanline) {
- continue;
- }
-
- int y_off = scanline - y;
- if (sprite->attr & oam_Attr_Flip_Y) y_off = sprite_height - y_off - 1;
-
- int ch = sprite->index;
- if (mem->ctrl & ppu_Control_Sprite_Size) {
- bank = (ch & 1) << 2;
- ch &= 0xFEU;
- }
-
- bank += (ch >> 6);
- const int addr_off = ((ch & 0x3fU) << 4) + ((y_off & 8) << 1) + (y_off & 7);
- const uint8_t* data = mem->bank[bank] + addr_off;
- const uint8_t pl0 = data[0];
- const uint8_t pl1 = data[8];
- const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
- const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
-
- const uint8_t* pal = &mem->palette[0x10 + ((sprite->attr & oam_Attr_Pal_Mask) << 2)];
- uint8_t* dst = scanline_ptr + sprite->x;
-
- const int over_x = ((int)sprite->x + 8) - nes_ppu_render_w;
-
- // TODO: X Flip
- switch (over_x) {
- default:
- { int pal_idx = (pat1 << 0) & 3;
- if ( ( !(sprite->attr & oam_Attr_Background) ||
- dst[7] == 0xFFU)
- && pal_idx) {
- dst[7] = pal[pal_idx];
- }
- }
- case 1:
- { int pal_idx = (pat0 << 0) & 3;
- if ( ( !(sprite->attr & oam_Attr_Background) ||
- dst[6] == 0xFFU)
- && pal_idx) {
- dst[6] = pal[pal_idx];
- }
- }
- case 2:
- { int pal_idx = (pat1 << 2) & 3;
- if ( ( !(sprite->attr & oam_Attr_Background) ||
- dst[5] == 0xFFU)
- && pal_idx) {
- dst[5] = pal[pal_idx];
- }
- }
- case 3:
- { int pal_idx = (pat0 << 2) & 3;
- if ( ( !(sprite->attr & oam_Attr_Background) ||
- dst[4] == 0xFFU)
- && pal_idx) {
- dst[4] = pal[pal_idx];
- }
- }
- case 4:
- { int pal_idx = (pat1 << 4) & 3;
- if ( ( !(sprite->attr & oam_Attr_Background) ||
- dst[3] == 0xFFU)
- && pal_idx) {
- dst[3] = pal[pal_idx];
- }
- }
- case 5:
- { int pal_idx = (pat0 << 4) & 3;
- if ( ( !(sprite->attr & oam_Attr_Background) ||
- dst[2] == 0xFFU)
- && pal_idx) {
- dst[2] = pal[pal_idx];
- }
- }
- case 6:
- { int pal_idx = (pat1 << 6) & 3;
- if ( ( !(sprite->attr & oam_Attr_Background) ||
- dst[1] == 0xFFU)
- && pal_idx) {
- dst[1] = pal[pal_idx];
- }
- }
- case 7:
- { int pal_idx = (pat0 << 6) & 3;
- if ( ( !(sprite->attr & oam_Attr_Background) ||
- dst[0] == 0xFFU)
- && pal_idx) {
- dst[0] = pal[pal_idx];
- }
- }
- }
-
- ++n_sprites;
- }
-
- if (n_sprites >= 8) {
- mem->status |= ppu_Status_Overflow;
- } else {
- mem->status &= ~ppu_Status_Overflow;
- }
- }
- */
-
- // Increment internal registers
- if (mem->mask & (ppu_Mask_Sprite | ppu_Mask_Back)) {
- uint16_t mask = 0b10000011111;
- mem->addr = (mem->addr & ~mask) | (mem->t & mask);
-
- int y_scroll = (mem->addr >> 12) |
- ((mem->addr >> 2) & 0xF8U);
- if (nes_ppu_render_h - 1 == y_scroll) {
- y_scroll = 0;
- mem->addr ^= 0x800;
- } else if (0xFFU == y_scroll) {
- y_scroll = 0;
- } else {
- ++y_scroll;
- }
- mem->addr = (mem->addr & ~0b111001111100000) |
- ((y_scroll & 7) << 12) |
- ((y_scroll & 0xF8U) << 2);
- }
- }
|