Files
player/Project/Src/NES/nes_ppu.c

973 lines
30 KiB
C
Raw Normal View History

2025-07-10 11:30:57 +08:00
//////////////////////////////////////////////////////////////////////////////////
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ye781205<30><35>NESģ<53><C4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// ALIENTEK STM32F407<30><37><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// NES PPU <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20><><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD>@ALIENTEK
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̳:www.openedv.com
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:2014/7/1
// <20><EFBFBD><E6B1BE>V1.0
//////////////////////////////////////////////////////////////////////////////////
2025-06-27 00:32:57 +08:00
#include "nes_ppu.h"
2025-07-10 11:30:57 +08:00
#include "mymem.h"
#include "stm32f4xx.h"
2025-06-27 00:32:57 +08:00
#include "string.h"
2025-07-10 11:30:57 +08:00
#include "lcd_rgb.h"
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
uint8_t *PPU_patterntables; // 8192 VROM<4F><4D>ʼ<EFBFBD><CABC>ַ ͼ<><CDBC><EFBFBD><EFBFBD>
void set_tile_banks(uint8_t *bank0, uint8_t *bank1, uint8_t *bank2, uint8_t *bank3,
uint8_t *bank4, uint8_t *bank5, uint8_t *bank6, uint8_t *bank7) {
ppu->PPU_tile_banks[0] = bank0;
ppu->PPU_tile_banks[1] = bank1;
ppu->PPU_tile_banks[2] = bank2;
ppu->PPU_tile_banks[3] = bank3;
ppu->PPU_tile_banks[4] = bank4;
ppu->PPU_tile_banks[5] = bank5;
ppu->PPU_tile_banks[6] = bank6;
ppu->PPU_tile_banks[7] = bank7;
2025-06-27 00:32:57 +08:00
}
//*******************************************************************
2025-07-10 11:30:57 +08:00
void set_tile_bank(int i, uint8_t *bank) { ppu->PPU_tile_banks[i] = bank; }
void set_name_table(uint8_t bank, int bank_num) {
ppu->PPU_VRAM_banks[bank] = ppu->PPU_nametables + ((bank_num & 0x03) << 10);
}
// 0x2000
uint32_t NMI_enabled(void) { return ppu->LowRegs[0] & 0x80; }
uint32_t sprites_8x16() { return ppu->LowRegs[0] & 0x20; }
// 0x2001
uint32_t spr_enabled(void) { return ppu->LowRegs[1] & 0x10; }
uint32_t bg_enabled(void) { return ppu->LowRegs[1] & 0x08; }
uint32_t spr_clip_left8() { return !(ppu->LowRegs[1] & 0x04); }
uint32_t bg_clip_left8() { return !(ppu->LowRegs[1] & 0x02); }
uint32_t rgb_pal() { return ppu->LowRegs[1] & 0xE0; }
// uint8_t rgb_bak;
// 0x2002
uint32_t sprite0_hit() { return ppu->LowRegs[2] & 0x40; }
uint32_t more_than_8_sprites_on_cur_line() { return ppu->LowRegs[2] & 0x20; }
uint32_t VRAM_accessible() { return ppu->LowRegs[2] & 0x10; }
// by rinao
// uint8_t* get_patt() { return PPU_patterntables; }
// uint8_t* get_namt() { return ppu->PPU_nametables; }
// uint8_t get_pattype(uint8_t bank) { return PPU_patterntype[bank]; }
// void set_pattype(uint8_t bank, uint8_t data) { PPU_patterntype[bank] = data; }
//*****************************************************************
// ROM_banks = (uint8_t*)malloc(header.num_16k_rom_banks * (16*1024));
// VROM_banks = (uint8_t*)malloc(header.num_8k_vrom_banks * (8*1024));
// VROM_tiles = (uint8_t *)malloc(RomHeader->num_8k_vrom_banks * 8 * 1024 / 16 *
// BYTES_PER_COMPILED_TILE); compile(header.num_8k_vrom_banks * 8 * 1024 / 16,
// VROM_banks, VROM_tiles);
// uint8_t* get_ROM_banks() { return ROM_banks; }
// uint8_t* get_VROM_banks() { return VROM_banks; }
// uint8_t* get_VROM_tiles() { return VROM_tiles; }
// #define MASK_BANK(bank,mask) (bank) = ((bank) & (mask))
2025-06-27 00:32:57 +08:00
// #define VALIDATE_VROM_BANK(bank) \
// MASK_BANK(bank,VROM_mask); \
2025-07-10 11:30:57 +08:00
// if((bank) >= (RomHeader->num_8k_vrom_banks * 8)) return;
2025-06-27 00:32:57 +08:00
//****************************************************************************************************
2025-07-10 11:30:57 +08:00
#define EXTRACT_4_PIXELS() \
col = 0; \
if (pattern_lo & pattern_mask) \
col |= (0x01 << 6); \
if (pattern_hi & pattern_mask) \
col |= (0x02 << 6); \
pattern_mask >>= 1; \
if (pattern_lo & pattern_mask) \
col |= (0x01 << 4); \
if (pattern_hi & pattern_mask) \
col |= (0x02 << 4); \
pattern_mask >>= 1; \
if (pattern_lo & pattern_mask) \
col |= (0x01 << 2); \
if (pattern_hi & pattern_mask) \
col |= (0x02 << 2); \
pattern_mask >>= 1; \
if (pattern_lo & pattern_mask) \
col |= (0x01 << 0); \
if (pattern_hi & pattern_mask) \
col |= (0x02 << 0); \
*p++ = col;
void compile1(int count, uint8_t *src, uint8_t *dest) {
uint8_t *p = dest;
uint8_t col;
uint8_t pattern_lo;
uint8_t pattern_hi;
int i, line, pattern_mask;
for (i = 0; i < count; i++) {
for (line = 0; line < 8; line++) {
pattern_lo = *src;
pattern_hi = *(src + 8);
pattern_mask = 0x80;
EXTRACT_4_PIXELS();
pattern_mask >>= 1;
EXTRACT_4_PIXELS();
src++;
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
src += 8;
}
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
void compile(int count, uint8_t *src, uint8_t *dest) {
u8 destemp[16];
u8 *p = destemp;
u8 col;
u8 pattern_lo;
u8 pattern_hi;
int i, line, pattern_mask;
u8 j;
for (i = 0; i < count; i++) {
for (line = 0; line < 8; line++) {
pattern_lo = *src;
pattern_hi = *(src + 8);
pattern_mask = 0x80;
EXTRACT_4_PIXELS();
pattern_mask >>= 1;
EXTRACT_4_PIXELS();
src++;
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
p = destemp; // <20><><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>׵<EFBFBD>ַ
for (j = 0; j < 16; j++)
*dest++ = destemp[j];
src += 8;
}
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
#define UPDATE_PIXEL() \
if (data & pattern_mask) \
*p |= bit; \
2025-06-27 00:32:57 +08:00
p++;
2025-07-10 11:30:57 +08:00
#define VRAM(addr) ppu->PPU_VRAM_banks[(addr) >> 10][(addr) & 0x3FF]
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
#define TILE(addr) \
2025-06-27 00:32:57 +08:00
(ppu->PPU_tile_banks[(addr) >> 10] + ((addr) & 0x3FF) / 16 * 16)
2025-07-10 11:30:57 +08:00
#define TILE_OFFSET(line) ((line) * 2)
2025-06-27 00:32:57 +08:00
/*
scanline start (if background or sprites are enabled):
2025-07-10 11:30:57 +08:00
v:0000010000011111=t:0000010000011111
2025-06-27 00:32:57 +08:00
*/
2025-07-10 11:30:57 +08:00
#define LOOPY_SCANLINE_START(v, t) \
{ v = (v & 0xFBE0) | (t & 0x041F); }
#define LOOPY_NEXT_LINE(v) \
{ \
if ((v & 0x7000) == 0x7000) /* is subtile y offset == 7? */ \
{ \
v &= 0x8FFF; /* subtile y offset = 0 */ \
if ((v & 0x03E0) == 0x03A0) /* name_tab line == 29? */ \
{ \
v ^= 0x0800; /* switch nametables (bit 11) */ \
v &= 0xFC1F; /* name_tab line = 0 */ \
} else { \
if ((v & 0x03E0) == 0x03E0) /* line == 31? */ \
{ \
v &= 0xFC1F; /* name_tab line = 0 */ \
} else { \
v += 0x0020; \
} \
} \
} else { \
v += 0x1000; /* next subtile y offset */ \
} \
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
#define LOOPY_NEXT_TILE(v) \
{ \
if ((v & 0x001F) == 0x001F) { \
v ^= 0x0400; /* switch nametables (bit 10) */ \
v &= 0xFFE0; /* tile x = 0 */ \
} else { \
v++; /* next tile */ \
} \
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
#define LOOPY_NEXT_PIXEL(v, x) \
{ \
if (x == 0x07) { \
LOOPY_NEXT_TILE(v); \
x = 0x00; \
} else { \
x++; \
} \
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
void PPU_Latch_FDFE(uint32_t addr) {}
#define CHECK_MMC2(addr) \
if (((addr) & 0x0FC0) == 0x0FC0) { \
if ((((addr) & 0x0FF0) == 0x0FD0) || (((addr) & 0x0FF0) == 0x0FE0)) { \
PPU_Latch_FDFE(addr); \
} \
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC>
static u16 *LCD_ADDR = 0;
void PPU_reset(void *lcd_addr) {
LCD_ADDR = lcd_addr;
if (LCD_ADDR == 0)
return;
// static u8 first=0;
// if (first==0)
// {
// first=1;
// for (int i=0;i<64;i++)
// {
// NES_Palette[i]=COLOR565TO888(NES_Palette[i]);
// }
// }
// <20><><EFBFBD><EFBFBD>
// for (int i=0;i<480*272;i++)
// {
// LCD_ADDR[i]=0;
// }
2025-06-27 00:32:57 +08:00
// set up PPU memory space table
2025-07-10 11:30:57 +08:00
PPU_patterntables = VROM_banks; // <20><>ȷ<EFBFBD><C8B7><EFBFBD><EFBFBD>ȷ
ppu->PPU_VRAM_banks[0x00] = PPU_patterntables + (0 * 0x400);
ppu->PPU_VRAM_banks[0x01] = PPU_patterntables + (1 * 0x400);
ppu->PPU_VRAM_banks[0x02] = PPU_patterntables + (2 * 0x400);
ppu->PPU_VRAM_banks[0x03] = PPU_patterntables + (3 * 0x400);
ppu->PPU_VRAM_banks[0x04] = PPU_patterntables + (4 * 0x400);
ppu->PPU_VRAM_banks[0x05] = PPU_patterntables + (5 * 0x400);
ppu->PPU_VRAM_banks[0x06] = PPU_patterntables + (6 * 0x400);
ppu->PPU_VRAM_banks[0x07] = PPU_patterntables + (7 * 0x400);
2025-06-27 00:32:57 +08:00
// point nametables at internal name table 0
ppu->PPU_VRAM_banks[0x08] = ppu->PPU_nametables;
ppu->PPU_VRAM_banks[0x09] = ppu->PPU_nametables;
ppu->PPU_VRAM_banks[0x0A] = ppu->PPU_nametables;
ppu->PPU_VRAM_banks[0x0B] = ppu->PPU_nametables;
2025-07-10 11:30:57 +08:00
ppu->PPU_tile_tables = VROM_tiles; // <20><>ȷ<EFBFBD><C8B7><EFBFBD><EFBFBD>ȷ
// BYTES_PER_COMPILED_TILE=16
ppu->PPU_tile_banks[0x00] = ppu->PPU_tile_tables + (0 * 0x400);
ppu->PPU_tile_banks[0x01] = ppu->PPU_tile_tables + (1 * 0x400);
ppu->PPU_tile_banks[0x02] = ppu->PPU_tile_tables + (2 * 0x400);
ppu->PPU_tile_banks[0x03] = ppu->PPU_tile_tables + (3 * 0x400);
ppu->PPU_tile_banks[0x04] = ppu->PPU_tile_tables + (4 * 0x400);
ppu->PPU_tile_banks[0x05] = ppu->PPU_tile_tables + (5 * 0x400);
ppu->PPU_tile_banks[0x06] = ppu->PPU_tile_tables + (6 * 0x400);
ppu->PPU_tile_banks[0x07] = ppu->PPU_tile_tables + (7 * 0x400);
2025-06-27 00:32:57 +08:00
ppu->read_2007_buffer = 0x00;
ppu->in_vblank = 0;
ppu->bg_pattern_table_addr = 0;
ppu->spr_pattern_table_addr = 0;
ppu->ppu_addr_inc = 0;
ppu->loopy_v = 0;
ppu->loopy_t = 0;
ppu->loopy_x = 0;
ppu->toggle_2005_2006 = 0;
ppu->spr_ram_rw_ptr = 0;
ppu->read_2007_buffer = 0;
ppu->current_frame_line = 0;
2025-07-10 11:30:57 +08:00
if (RomHeader->flags_1 & 0x01)
set_mirroring(0, 1, 0, 1); // <20><>ֱ<EFBFBD><D6B1><EFBFBD><EFBFBD> // PPU_set_mirroring();
// <20><><EFBFBD>þ<EFBFBD><C3BE><EFBFBD>
else
set_mirroring(0, 0, 1, 1); // ˮƽ<CBAE><C6BD><EFBFBD><EFBFBD>
2025-06-27 00:32:57 +08:00
}
//***********************************************************************************************
2025-07-10 11:30:57 +08:00
void set_mirroring(uint32_t nt0, uint32_t nt1, uint32_t nt2,
uint32_t nt3) // <20><><EFBFBD>ô<EFBFBD>ֱˮƽ<CBAE><C6BD><EFBFBD><EFBFBD>
2025-06-27 00:32:57 +08:00
{
ppu->PPU_VRAM_banks[0x08] = ppu->PPU_nametables + (nt0 << 10); // * 0x0400
ppu->PPU_VRAM_banks[0x09] = ppu->PPU_nametables + (nt1 << 10);
ppu->PPU_VRAM_banks[0x0A] = ppu->PPU_nametables + (nt2 << 10);
ppu->PPU_VRAM_banks[0x0B] = ppu->PPU_nametables + (nt3 << 10);
2025-07-10 11:30:57 +08:00
// name_table_switched = TRUE;
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
void PPU_start_frame(void) {
2025-06-27 00:32:57 +08:00
ppu->current_frame_line = 0;
2025-07-10 11:30:57 +08:00
if (spr_enabled() || bg_enabled()) {
2025-06-27 00:32:57 +08:00
ppu->loopy_v = ppu->loopy_t;
}
}
2025-07-10 11:30:57 +08:00
uint8_t getBGColor() { return ppu->bg_pal[0]; }
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
void do_scanline_and_draw(uint8_t *buf) {
uint16_t i;
if (!bg_enabled()) {
2025-07-05 19:47:28 +08:00
// set to background color<6F><72><EFBFBD>ñ<EFBFBD><C3B1><EFBFBD><EFBFBD><EFBFBD>ɫ
2025-07-10 11:30:57 +08:00
// memset(buf, bg_pal[0],
// NES_BACKBUF_WIDTH);//NES_BACKBUF_WIDTH=256+(2*8)
for (i = 0; i < (256 + 16); i++) {
buf[i] = ppu->bg_pal[0];
}
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
if (spr_enabled() || bg_enabled()) {
2025-06-27 00:32:57 +08:00
LOOPY_SCANLINE_START(ppu->loopy_v, ppu->loopy_t);
2025-07-10 11:30:57 +08:00
if (bg_enabled()) {
2025-07-05 19:47:28 +08:00
// draw background<6E><64><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
2025-06-27 00:32:57 +08:00
render_bg(buf);
2025-07-10 11:30:57 +08:00
} else {
2025-07-05 19:47:28 +08:00
// clear out solid buffer<65><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E5BBBA><EFBFBD><EFBFBD>
2025-07-10 11:30:57 +08:00
memset(ppu->solid_buf, 0x00, sizeof(ppu->solid_buf));
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
if (spr_enabled()) {
2025-07-05 19:47:28 +08:00
// draw sprites<65><73><EFBFBD>ƾ<EFBFBD><C6BE><EFBFBD>
2025-06-27 00:32:57 +08:00
render_spr(buf);
}
LOOPY_NEXT_LINE(ppu->loopy_v);
}
2025-07-10 11:30:57 +08:00
ppu->current_frame_line++;
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
extern u8 nes_xoff; // <20><>ʾ<EFBFBD><CABE>x<EFBFBD><EFBFBD><E1B7BD><EFBFBD><EFBFBD>ƫ<EFBFBD><C6AB><EFBFBD><EFBFBD><><CAB5><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD>=256-2*nes_xoff)
2025-06-27 00:32:57 +08:00
extern u8 nes_yoff;
2025-07-10 11:30:57 +08:00
void scanline_draw(int LineNo) {
uint16_t i;
u16 sx, ex;
do_scanline_and_draw(ppu->dummy_buffer);
sx = 0 + 8;
ex = 256 + 8 - 0;
{
for (i = sx; i < ex; i++) {
// LCD_ADDR[(480*272-1)-((NES_scanline+nes_yoff)*480+(i-8+nes_xoff))]=NES_Palette[ppu->dummy_buffer[i]];
LCD_ADDR[((NES_scanline + nes_yoff) * 480 + (i - 8 + nes_xoff))] =
NES_Palette[ppu->dummy_buffer[i]];
}
}
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
void do_scanline_and_dont_draw(int LineNo) {
// uint16_t i;
// mmc2 / punchout -- we must simulate the ppu for every line
// if(parent_NES->ROM->get_mapper_num() == 9)
// {
// do_scanline_and_draw(ppu->dummy_buffer);
// }
// else
{
// if sprite 0 flag not set and sprite 0 on current line
if ((!sprite0_hit()) &&
(ppu->current_frame_line >= ((uint32_t)(spr_ram[0] + 1))) &&
(ppu->current_frame_line <
((uint32_t)(spr_ram[0] + 1 + (sprites_8x16() ? 16 : 8))))) {
// render line to dummy buffer
do_scanline_and_draw(ppu->dummy_buffer);
} else {
if (spr_enabled() || bg_enabled()) {
LOOPY_SCANLINE_START(ppu->loopy_v, ppu->loopy_t);
LOOPY_NEXT_LINE(ppu->loopy_v);
}
ppu->current_frame_line++;
}
}
}
void start_vblank() {
2025-06-27 00:32:57 +08:00
ppu->in_vblank = 1;
// set vblank register flag
ppu->LowRegs[2] |= 0x80;
}
2025-07-10 11:30:57 +08:00
void end_vblank() {
2025-06-27 00:32:57 +08:00
ppu->in_vblank = 0;
// reset vblank register flag and sprite0 hit flag1
ppu->LowRegs[2] &= 0x3F;
}
// these functions read from/write to VRAM using loopy_v
2025-07-10 11:30:57 +08:00
uint8_t read_2007() {
uint16_t addr;
uint8_t temp;
2025-06-27 00:32:57 +08:00
addr = ppu->loopy_v;
ppu->loopy_v += ppu->ppu_addr_inc;
addr &= 0x3FFF;
2025-07-10 11:30:57 +08:00
if (addr >= 0x3000) {
2025-06-27 00:32:57 +08:00
// is it a palette entry?
2025-07-10 11:30:57 +08:00
if (addr >= 0x3F00) {
2025-06-27 00:32:57 +08:00
// palette
// handle palette mirroring
2025-07-10 11:30:57 +08:00
if (0x0000 == (addr & 0x0010)) {
2025-06-27 00:32:57 +08:00
// background palette
return ppu->bg_pal[addr & 0x000F];
2025-07-10 11:30:57 +08:00
} else {
2025-06-27 00:32:57 +08:00
// sprite palette
return ppu->spr_pal[addr & 0x000F];
}
}
// handle mirroring
addr &= 0xEFFF;
}
temp = ppu->read_2007_buffer;
ppu->read_2007_buffer = VRAM(addr);
return temp;
}
2025-07-10 11:30:57 +08:00
void write_2007(uint8_t data) {
uint16_t addr;
2025-06-27 00:32:57 +08:00
addr = ppu->loopy_v;
ppu->loopy_v += ppu->ppu_addr_inc;
addr &= 0x3FFF;
2025-07-10 11:30:57 +08:00
if (addr >= 0x3000) {
2025-06-27 00:32:57 +08:00
// is it a palette entry?
2025-07-10 11:30:57 +08:00
if (addr >= 0x3F00) {
2025-06-27 00:32:57 +08:00
// palette
data &= 0x3F;
2025-07-10 11:30:57 +08:00
if (0x0000 == (addr & 0x000F)) // is it THE 0 entry?0<><30><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF>
2025-06-27 00:32:57 +08:00
{
ppu->bg_pal[0] = ppu->spr_pal[0] = data;
2025-07-10 11:30:57 +08:00
} else if (0x0000 == (addr & 0x0010)) {
2025-06-27 00:32:57 +08:00
// background palette
ppu->bg_pal[addr & 0x000F] = data;
2025-07-10 11:30:57 +08:00
} else {
2025-06-27 00:32:57 +08:00
// sprite palette
ppu->spr_pal[addr & 0x000F] = data;
}
return;
}
2025-07-05 19:47:28 +08:00
// handle mirroring<6E><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
2025-06-27 00:32:57 +08:00
addr &= 0xEFFF;
}
VRAM(addr) = data;
if (addr < 0x2000) {
2025-07-10 11:30:57 +08:00
#if NES_RAM_SPEED == 1 // 1:<3A>ڴ<EFBFBD>ռ<EFBFBD><D5BC>С 0:<3A>ٶȿ<D9B6>
*(TILE(addr) + (addr & 0xf)) = data; //************************
#else
update_tile(addr, data);
#endif
2025-06-27 00:32:57 +08:00
}
}
2025-07-10 11:30:57 +08:00
#define UPDATE_4_PIXELS() \
col = 0; \
if (data & pattern_mask) \
col |= (bit << 6); \
pattern_mask >>= 1; \
if (data & pattern_mask) \
col |= (bit << 4); \
pattern_mask >>= 1; \
if (data & pattern_mask) \
col |= (bit << 2); \
pattern_mask >>= 1; \
if (data & pattern_mask) \
col |= (bit << 0); \
*p++ |= col;
void update_tile(int byte_offset, uint8_t data) {
// int tileNum = byte_offset >> 4;
int line = byte_offset & 0xf;
uint8_t *p = TILE(byte_offset) + TILE_OFFSET(line & 0x7);
uint8_t bit;
uint8_t pattern_mask;
int col;
if (line < 8) {
// low pattern
bit = 0x01;
*(uint16_t *)p &= 0xaaaa;
} else {
// high pattern
bit = 0x02;
*(uint16_t *)p &= 0x5555;
}
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
pattern_mask = 0x80;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
UPDATE_4_PIXELS();
pattern_mask >>= 1;
UPDATE_4_PIXELS();
}
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
uint8_t PPU_ReadFromPort(uint16_t addr) {
uint8_t temp;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// switch(addr)
switch (addr & 0x7) {
// case 0x2002:
case 0x2:
// clear toggle
ppu->toggle_2005_2006 = 0;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
temp = ppu->LowRegs[2];
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// clear v-blank flag
ppu->LowRegs[2] &= 0x7F;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
return temp;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// break;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// case 0x2007:
case 0x7:
return read_2007();
// break;
}
return ppu->LowRegs[addr & 0x0007];
}
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
void PPU_WriteToPort(uint8_t data, uint16_t addr) {
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// uint32_t t;
ppu->LowRegs[addr & 0x0007] = data;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// switch(addr)
switch (addr & 0x7) {
// case 0x2000:
case 0: {
ppu->bg_pattern_table_addr = (data & 0x10) ? 0x1000 : 0x0000;
ppu->spr_pattern_table_addr = (data & 0x08) ? 0x1000 : 0x0000;
ppu->ppu_addr_inc = (data & 0x04) ? 32 : 1;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// t:0000110000000000=d:00000011
ppu->loopy_t = (ppu->loopy_t & 0xF3FF) | (((uint16_t)(data & 0x03)) << 10);
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
break;
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
// Rick, lazy updating stuff
// case 0x2001:
// case 1:
// bg_enabled();
//
// spr_enabled();
// break;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// case 0x2003:
case 3:
ppu->spr_ram_rw_ptr = data;
break;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// case 0x2004:
case 4:
spr_ram[ppu->spr_ram_rw_ptr++] = data;
break;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// case 0x2005:
case 5:
ppu->toggle_2005_2006 = !ppu->toggle_2005_2006;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
if (ppu->toggle_2005_2006) {
// first write
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// t:0000000000011111=d:11111000
ppu->loopy_t = (ppu->loopy_t & 0xFFE0) | (((uint16_t)(data & 0xF8)) >> 3);
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// x=d:00000111
ppu->loopy_x = data & 0x07;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
} else {
// second write
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// t:0000001111100000=d:11111000
ppu->loopy_t = (ppu->loopy_t & 0xFC1F) | (((uint16_t)(data & 0xF8)) << 2);
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// t:0111000000000000=d:00000111
ppu->loopy_t = (ppu->loopy_t & 0x8FFF) | (((uint16_t)(data & 0x07)) << 12);
}
break;
// case 0x2006:
case 6:
ppu->toggle_2005_2006 = !ppu->toggle_2005_2006;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
if (ppu->toggle_2005_2006) {
// first write
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// t:0011111100000000=d:00111111
// t:1100000000000000=0
ppu->loopy_t = (ppu->loopy_t & 0x00FF) | (((uint16_t)(data & 0x3F)) << 8);
} else {
// second write
// t:0000000011111111=d:11111111
ppu->loopy_t = (ppu->loopy_t & 0xFF00) | ((uint16_t)data);
// v=t
ppu->loopy_v = ppu->loopy_t;
}
break;
// case 0x2007:
case 7:
write_2007(data);
break;
2025-06-27 00:32:57 +08:00
}
}
//*****************************************************************************
2025-07-10 11:30:57 +08:00
#define DRAW_BG_PIXEL() \
col = attrib_bits; \
\
if (pattern_lo & pattern_mask) \
col |= 0x01; \
if (pattern_hi & pattern_mask) \
col |= 0x02; \
\
if (col & 0x03) { \
*p = ppu->bg_pal[col]; \
/* set solid flag */ \
*solid = BG_WRITTEN_FLAG; \
} else { \
*p = ppu->bg_pal[0]; \
/* set solid flag */ \
*solid = 0; \
} \
solid++; \
p++; \
2025-06-27 00:32:57 +08:00
//***************************************************************************************
2025-07-10 11:30:57 +08:00
#define NEW_BG_PIXEL() \
if (col) { \
col |= attrib_bits; \
*solid = BG_WRITTEN_FLAG; \
} else { \
col = 0; \
*solid = 0; \
} \
*p = ppu->bg_pal[col]; \
solid++; \
p++;
void render_bg(uint8_t *buf) {
uint8_t *p;
uint32_t i;
uint8_t *solid;
uint8_t *data;
uint32_t col;
uint32_t tile_x; // pixel coords within nametable<6C><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>nametable
uint32_t tile_y;
uint32_t name_addr;
uint32_t line;
uint32_t pattern_addr;
uint32_t attrib_addr;
uint32_t attrib_bits;
#if NES_RAM_SPEED == 1 // 1:<3A>ڴ<EFBFBD>ռ<EFBFBD><D5BC>С 0:<3A>ٶȿ<D9B6>
uint8_t pattern_lo;
uint8_t pattern_hi;
uint8_t pattern_mask;
#else
int col2;
#endif
2025-06-27 00:32:57 +08:00
tile_x = (ppu->loopy_v & 0x001F);
tile_y = (ppu->loopy_v & 0x03E0) >> 5;
name_addr = 0x2000 + (ppu->loopy_v & 0x0FFF);
2025-07-10 11:30:57 +08:00
attrib_addr = 0x2000 + (ppu->loopy_v & 0x0C00) + 0x03C0 +
((tile_y & 0xFFFC) << 1) + (tile_x >> 2);
if (0x0000 == (tile_y & 0x0002))
if (0x0000 == (tile_x & 0x0002))
2025-06-27 00:32:57 +08:00
attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
else
attrib_bits = (VRAM(attrib_addr) & 0x0C);
2025-07-10 11:30:57 +08:00
else if (0x0000 == (tile_x & 0x0002))
attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
2025-06-27 00:32:57 +08:00
else
2025-07-10 11:30:57 +08:00
attrib_bits = (VRAM(attrib_addr) & 0xC0) >> 4;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
p = buf + (SIDE_MARGIN - ppu->loopy_x);
solid =
ppu->solid_buf + (SIDE_MARGIN - ppu->loopy_x); // set "solid" buffer ptr
2025-06-27 00:32:57 +08:00
line = (ppu->loopy_v & 0x7000) >> 12;
// draw 33 tiles
2025-07-10 11:30:57 +08:00
for (i = 33; i; i--) {
pattern_addr =
ppu->bg_pattern_table_addr + ((int32_t)VRAM(name_addr) << 4) + line;
// CHECK_MMC2(pattern_addr);//*******************************************************
#if NES_RAM_SPEED == 1 // 1:<3A>ڴ<EFBFBD>ռ<EFBFBD><D5BC>С 0:<3A>ٶȿ<D9B6>
data = TILE(pattern_addr) + line;
pattern_lo = *data;
pattern_hi = *(data + 8);
pattern_mask = 0x80;
DRAW_BG_PIXEL();
pattern_mask >>= 1;
DRAW_BG_PIXEL();
pattern_mask >>= 1;
DRAW_BG_PIXEL();
pattern_mask >>= 1;
DRAW_BG_PIXEL();
pattern_mask >>= 1;
DRAW_BG_PIXEL();
pattern_mask >>= 1;
DRAW_BG_PIXEL();
pattern_mask >>= 1;
DRAW_BG_PIXEL();
pattern_mask >>= 1;
DRAW_BG_PIXEL();
#else
data = TILE(pattern_addr) + TILE_OFFSET(line);
col2 = *data++;
col = col2 >> 6;
NEW_BG_PIXEL();
col = (col2 >> 4) & 0x03;
NEW_BG_PIXEL();
col = (col2 >> 2) & 0x03;
NEW_BG_PIXEL();
col = col2 & 0x03;
NEW_BG_PIXEL();
col2 = *data++;
col = col2 >> 6;
NEW_BG_PIXEL();
col = (col2 >> 4) & 0x03;
NEW_BG_PIXEL();
col = (col2 >> 2) & 0x03;
NEW_BG_PIXEL();
col = col2 & 0x03;
NEW_BG_PIXEL();
#endif
2025-06-27 00:32:57 +08:00
tile_x++;
name_addr++;
2025-07-05 19:47:28 +08:00
// are we crossing a dual-tile boundary?<3F><><EFBFBD>Ǵ<EFBFBD>Խdual-tile<6C>߽<EFBFBD><DFBD><EFBFBD>?
2025-07-10 11:30:57 +08:00
if (0x0000 == (tile_x & 0x0001)) {
2025-06-27 00:32:57 +08:00
// are we crossing a quad-tile boundary?
2025-07-10 11:30:57 +08:00
if (0x0000 == (tile_x & 0x0003)) {
2025-06-27 00:32:57 +08:00
// are we crossing a name table boundary?
2025-07-10 11:30:57 +08:00
if (0x0000 == (tile_x & 0x001F)) {
2025-06-27 00:32:57 +08:00
name_addr ^= 0x0400; // switch name tables
attrib_addr ^= 0x0400;
name_addr -= 0x0020;
attrib_addr -= 0x0008;
tile_x -= 0x0020;
}
attrib_addr++;
}
2025-07-10 11:30:57 +08:00
if (0x0000 == (tile_y & 0x0002))
if (0x0000 == (tile_x & 0x0002))
2025-06-27 00:32:57 +08:00
attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
else
attrib_bits = (VRAM(attrib_addr) & 0x0C);
2025-07-10 11:30:57 +08:00
else if (0x0000 == (tile_x & 0x0002))
attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
2025-06-27 00:32:57 +08:00
else
2025-07-10 11:30:57 +08:00
attrib_bits = (VRAM(attrib_addr) & 0xC0) >> 4;
2025-06-27 00:32:57 +08:00
}
}
2025-07-10 11:30:57 +08:00
// if(bg_clip_left8())
// {
// // clip left 8 pixels<6C><73><EFBFBD><EFBFBD>8<EFBFBD><38><EFBFBD><EFBFBD><EFBFBD><EFBFBD>SIDE_MARGIN = 8,
// memset(buf + SIDE_MARGIN, bg_pal[0], 8);
// memset(solid + SIDE_MARGIN, 0, sizeof(solid[0])*8);
// }
2025-06-27 00:32:57 +08:00
}
2025-07-10 11:30:57 +08:00
void render_spr(uint8_t *buf) {
int32_t s; // sprite #
int32_t spr_x; // sprite coordinates
uint32_t spr_y;
uint8_t *spr; // pointer to sprite RAM entry
uint8_t *p; // draw pointer
int line;
// uint32_t *solid;
uint8_t *solid;
uint32_t priority;
uint32_t tile_addr;
int32_t inc_x; // drawing vars
int32_t start_x, end_x;
int32_t x, y; // in-sprite coords
uint32_t col;
uint32_t num_sprites = 0;
uint8_t *t;
uint32_t spr_height;
#if NES_RAM_SPEED == 1 // 1:<3A>ڴ<EFBFBD>ռ<EFBFBD><D5BC>С 0:<3A>ٶȿ<D9B6>
uint8_t pattern_lo;
uint8_t pattern_hi;
#else
uint32_t pattern;
#endif
spr_height = sprites_8x16() ? 16 : 8;
// for(s = 0; s < 64; s++)
for (s = 0, spr = spr_ram; s < 64; s++, spr += 4) {
spr_y = spr[0] + 1;
// on current scanline<6E>Ե<EFBFBD>ǰɨ<C7B0><C9A8><EFBFBD><EFBFBD>?
if ((spr_y > ppu->current_frame_line) ||
((spr_y + (spr_height)) <= ppu->current_frame_line))
continue;
num_sprites++;
if (num_sprites > 8) {
/*if(!show_more_than_8_sprites)*/
break; //**************************************
}
// get x coord
spr_x = spr[3];
start_x = 0;
end_x = 8;
// clip right
if ((spr_x + 7) > 255) {
end_x -= ((spr_x + 7) - 255);
}
// clip left
if ((spr_x < 8) && (spr_clip_left8())) {
if (0 == spr_x)
continue;
start_x += (8 - spr_x);
}
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
y = ppu->current_frame_line - spr_y;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// CHECK_MMC2(spr[1] <<
//4);//**************************************************************************
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// calc offsets into buffers
p = &buf[SIDE_MARGIN + spr_x + start_x];
solid = &ppu->solid_buf[SIDE_MARGIN + spr_x + start_x];
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// flip horizontally?
if (spr[2] & 0x40) // yes
{
inc_x = -1;
start_x = (8 - 1) - start_x;
end_x = (8 - 1) - end_x;
} else {
inc_x = 1;
}
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
// flip vertically?
if (spr[2] & 0x80) // yes
{
y = (spr_height - 1) - y;
}
line = y & 7;
// get priority bit
priority = spr[2] & 0x20;
tile_addr = spr[1] << 4;
if (sprites_8x16()) {
if (spr[1] & 0x01) {
tile_addr += 0x1000;
if (y < 8)
tile_addr -= 16;
} else {
if (y >= 8)
tile_addr += 16;
}
} else {
tile_addr += ppu->spr_pattern_table_addr;
}
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
#if NES_RAM_SPEED == 1 // 1:<3A>ڴ<EFBFBD>ռ<EFBFBD><D5BC>С 0:<3A>ٶȿ<D9B6>
t = TILE(tile_addr) + line;
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
pattern_lo = *t;
pattern_hi = *(t + 8);
2025-06-27 00:32:57 +08:00
2025-07-10 11:30:57 +08:00
#else
// read 16bits = 2bits x 8pixels
t = TILE(tile_addr) + TILE_OFFSET(line);
pattern = ((uint32_t)*t << 8) | *(t + 1);
#endif
for (x = start_x; x != end_x; x += inc_x) {
// uint8_t col = 0x00;
// if a sprite has drawn on this pixel, don't draw
// anything<6E><67><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>ѩ<EFBFBD><D1A9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD><EFBFBD><EFBFBD>κζ<CEBA><CEB6><EFBFBD>
if (!((*solid) & SPR_WRITTEN_FLAG)) {
#if NES_RAM_SPEED == 1 // 1:<3A>ڴ<EFBFBD>ռ<EFBFBD><D5BC>С 0:<3A>ٶȿ<D9B6>
col =
((pattern_hi >> (7 - x) << 1) & 2) | ((pattern_lo >> (7 - x)) & 1);
#else
col = pattern >> ((7 - (x & 7)) * 2);
#endif
col &= 0x03;
if (col) {
col |= (spr[2] & 0x03) << 2;
// set sprite 0 hit flag
if (!s) {
if ((*solid) & BG_WRITTEN_FLAG) {
ppu->LowRegs[2] |= 0x40;
}
}
(*solid) |= SPR_WRITTEN_FLAG;
if (priority) {
//(*solid) |= SPR_WRITTEN_FLAG;
if (!((*solid) & BG_WRITTEN_FLAG)) {
*p = ppu->spr_pal[col];
}
} else {
*p = ppu->spr_pal[col];
}
}
}
p++;
solid++;
}
}
if (num_sprites >= 8) {
ppu->LowRegs[2] |= 0x20;
} else {
ppu->LowRegs[2] &= 0xDF;
}
}