|
- #include <string.h>
-
- #include "ppu.h"
-
- //#define NESE_DEBUG "PPU"
- #include "log.h"
-
- #include "serdes.h"
-
-
- void nes_ppu_init(nes_PPU* ppu, nes_PPU_Memory* mem) {
- uint8_t* pal = mem->palette;
- pal[0] = pal[4] = pal[8] = pal[12] =
- pal[16] = pal[20] = pal[24] =
- pal[28] = 0xFFU;
-
- ppu->hit_line = -1;
- }
-
-
- 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;
- }
- }
- }
-
- static const uint8_t sprite_rev[256] = {
- 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
- 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
- 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
- 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
- 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
- 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
- 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
- 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
- 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
- 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
- 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
- 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
- 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
- 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
- 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
- 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
- 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
- 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
- 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
- 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
- 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
- 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
- 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
- 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
- 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
- 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
- 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
- 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
- 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
- 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
- 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
- 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF,
- };
-
- 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);
-
- } 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) {
- const int sprite_height = (mem->ctrl & ppu_Control_Sprite_Size) ?
- 16 : 8;
- const int scanline = ppu->scanline - nes_ppu_visible_line;
-
- int sprite_bank = !!(mem->ctrl & ppu_Control_Sprite_Bank) << 2;
-
- uint8_t foreground[nes_ppu_render_w];
- memset(foreground, 0xFFU, nes_ppu_render_w);
-
- 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 bank = sprite_bank;
- 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];
- int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
- int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
-
- const uint8_t* pal = &mem->palette[0x10 + ((sprite->attr & oam_Attr_Pal_Mask) << 2)];
- const uint8_t* back =
- (sprite->attr & oam_Attr_Background) ?
- (scanline_ptr + sprite->x) : NULL;
- uint8_t* dst = foreground + sprite->x;
-
- const int over_x = ((int)sprite->x + 8) - nes_ppu_render_w;
-
- if (sprite->attr & oam_Attr_Flip_X) {
- uint8_t tmp = pat0;
- pat0 = sprite_rev[pat1];
- pat1 = sprite_rev[tmp];
- }
-
- #define do_sprite(idx, pi, shift) \
- if (back && back[idx] != 0xFFU) { \
- dst[idx] = back[idx]; \
- } else { \
- int pal_idx = (pat##pi >> shift) & 3; \
- if (pal_idx) dst[idx] = pal[pal_idx]; \
- }
-
- switch (over_x) {
- default:
- do_sprite(7, 0, 0);
- case 1:
- do_sprite(6, 1, 0);
- case 2:
- do_sprite(5, 0, 2);
- case 3:
- do_sprite(4, 1, 2);
- case 4:
- do_sprite(3, 0, 4);
- case 5:
- do_sprite(2, 1, 4);
- case 6:
- do_sprite(1, 0, 6);
- case 7:
- do_sprite(0, 1, 6);
- }
-
- ++n_sprites;
- }
-
- if (n_sprites >= 8) {
- mem->status |= ppu_Status_Overflow;
- } else {
- mem->status &= ~ppu_Status_Overflow;
- }
-
- // Mask column 0?
- if (!(mem->mask & ppu_Mask_Left_Sprite)) {
- memset(foreground, 0xFFU, 8);
- }
-
- // Overlay the sprites
- const uint8_t* fg = foreground;
- uint8_t* dst = scanline_ptr;
- for (int i = 0; i < nes_ppu_render_w; ++i) {
- if (*fg != 0xFFU) *dst = *fg;
- ++fg;
- ++dst;
- }
- }
-
- // 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);
- }
- }
-
- /*
- static int nes_ppu_chr_ram_size(const void* _ppu, const void*) {
- const nes_PPU_Memory* ppu = (const nes_PPU_Memory*)_ppu;
- return (ppu->chr_ram ?
- ppu->n_chr_banks * NES_CHR_ROM_PAGE_SIZE :
- 0);
- }
-
- static int nes_ppu_read_chr_ram(void* dst, const void* src,
- int avail, const void*) {
- int size = 0;
- nes_PPU_Memory* ppu = (nes_PPU_Memory*)dst;
- if (ppu->chr_ram) {
- size = ppu->n_chr_banks * NES_CHR_ROM_PAGE_SIZE;
- if (size > avail) size = avail;
- memcpy(ppu->chr_ram, src, size);
- }
- return size;
- }
-
- static int nes_ppu_write_chr_ram(void* dst, const void* src,
- int avail, const void*) {
- int size = 0;
- const nes_PPU_Memory* ppu = (const nes_PPU_Memory*)src;
- if (ppu->chr_ram) {
- size = ppu->n_chr_banks * NES_CHR_ROM_PAGE_SIZE;
- if (size > avail) size = avail;
- memcpy(dst, ppu->chr_ram, size);
- }
- return size;
- }
-
- static const Serdes_IO ppu_serdes_io = {
- .read = nes_ppu_read_chr_ram,
- .write = nes_ppu_write_chr_ram,
- .in_size = nes_ppu_chr_ram_size,
- .out_size = nes_ppu_chr_ram_size,
- };
- */
-
- static void* ppu_chr_ram_ptr(const void* _ppu) {
- return ((nes_PPU_Memory*)_ppu)->chr_ram;
- }
-
- static size_t ppu_chr_ram_size(const void* _ppu) {
- return ((nes_PPU_Memory*)_ppu)->n_chr_banks * NES_CHR_ROM_PAGE_SIZE;
- }
-
- static Serdes_Ptr_Ref ppu_chr_ram_ref = {
- .ptr = ppu_chr_ram_ptr,
- .size = ppu_chr_ram_size,
- };
-
- const Serdes_Item nes_ppu_memory_serdes[] = {
- {"CRAM", 0, &serdes_mem_ptr, &ppu_chr_ram_ref},
- {"VRAM", offsetof(nes_PPU_Memory, ctrl), &serdes_mem, (void*)(sizeof(nes_PPU_Memory) - offsetof(nes_PPU_Memory, ctrl))},
- {0}
- };
|