Files
player/Project/Src/NES/nes_ppu.c
2025-07-10 11:30:57 +08:00

973 lines
30 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//////////////////////////////////////////////////////////////////////////////////
// 本程序移植自网友ye781205的NES模拟器工程
// ALIENTEK STM32F407开发板
// NES PPU 驱动代码
// 正点原子@ALIENTEK
// 技术论坛:www.openedv.com
// 创建日期:2014/7/1
// 版本V1.0
//////////////////////////////////////////////////////////////////////////////////
#include "nes_ppu.h"
#include "mymem.h"
#include "stm32f4xx.h"
#include "string.h"
#include "lcd_rgb.h"
uint8_t *PPU_patterntables; // 8192 VROM开始地址 图案表
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;
}
//*******************************************************************
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))
// #define VALIDATE_VROM_BANK(bank) \
// MASK_BANK(bank,VROM_mask); \
// if((bank) >= (RomHeader->num_8k_vrom_banks * 8)) return;
//****************************************************************************************************
#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++;
}
src += 8;
}
}
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++;
}
p = destemp; // 重新指向数组首地址
for (j = 0; j < 16; j++)
*dest++ = destemp[j];
src += 8;
}
}
#define UPDATE_PIXEL() \
if (data & pattern_mask) \
*p |= bit; \
p++;
#define VRAM(addr) ppu->PPU_VRAM_banks[(addr) >> 10][(addr) & 0x3FF]
#define TILE(addr) \
(ppu->PPU_tile_banks[(addr) >> 10] + ((addr) & 0x3FF) / 16 * 16)
#define TILE_OFFSET(line) ((line) * 2)
/*
scanline start (if background or sprites are enabled):
v:0000010000011111=t:0000010000011111
*/
#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 */ \
} \
}
#define LOOPY_NEXT_TILE(v) \
{ \
if ((v & 0x001F) == 0x001F) { \
v ^= 0x0400; /* switch nametables (bit 10) */ \
v &= 0xFFE0; /* tile x = 0 */ \
} else { \
v++; /* next tile */ \
} \
}
#define LOOPY_NEXT_PIXEL(v, x) \
{ \
if (x == 0x07) { \
LOOPY_NEXT_TILE(v); \
x = 0x00; \
} else { \
x++; \
} \
}
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); \
} \
}
// 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、初始化
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]);
// }
// }
// 清屏
// for (int i=0;i<480*272;i++)
// {
// LCD_ADDR[i]=0;
// }
// set up PPU memory space table
PPU_patterntables = VROM_banks; // 不确定正确
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);
// 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;
ppu->PPU_tile_tables = VROM_tiles; // 不确定正确
// 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);
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;
if (RomHeader->flags_1 & 0x01)
set_mirroring(0, 1, 0, 1); // 垂直镜像 // PPU_set_mirroring();
// 设置镜像
else
set_mirroring(0, 0, 1, 1); // 水平镜像
}
//***********************************************************************************************
void set_mirroring(uint32_t nt0, uint32_t nt1, uint32_t nt2,
uint32_t nt3) // 设置垂直水平镜像
{
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);
// name_table_switched = TRUE;
}
void PPU_start_frame(void) {
ppu->current_frame_line = 0;
if (spr_enabled() || bg_enabled()) {
ppu->loopy_v = ppu->loopy_t;
}
}
uint8_t getBGColor() { return ppu->bg_pal[0]; }
void do_scanline_and_draw(uint8_t *buf) {
uint16_t i;
if (!bg_enabled()) {
// set to background color设置背景颜色
// 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];
}
}
if (spr_enabled() || bg_enabled()) {
LOOPY_SCANLINE_START(ppu->loopy_v, ppu->loopy_t);
if (bg_enabled()) {
// draw background画背景
render_bg(buf);
} else {
// clear out solid buffer清除固体缓冲区
memset(ppu->solid_buf, 0x00, sizeof(ppu->solid_buf));
}
if (spr_enabled()) {
// draw sprites绘制精灵
render_spr(buf);
}
LOOPY_NEXT_LINE(ppu->loopy_v);
}
ppu->current_frame_line++;
}
extern u8 nes_xoff; // 显示在x轴方向的偏移量(实际显示宽度=256-2*nes_xoff)
extern u8 nes_yoff;
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]];
}
}
}
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() {
ppu->in_vblank = 1;
// set vblank register flag
ppu->LowRegs[2] |= 0x80;
}
void end_vblank() {
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
uint8_t read_2007() {
uint16_t addr;
uint8_t temp;
addr = ppu->loopy_v;
ppu->loopy_v += ppu->ppu_addr_inc;
addr &= 0x3FFF;
if (addr >= 0x3000) {
// is it a palette entry?
if (addr >= 0x3F00) {
// palette
// handle palette mirroring
if (0x0000 == (addr & 0x0010)) {
// background palette
return ppu->bg_pal[addr & 0x000F];
} else {
// 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;
}
void write_2007(uint8_t data) {
uint16_t addr;
addr = ppu->loopy_v;
ppu->loopy_v += ppu->ppu_addr_inc;
addr &= 0x3FFF;
if (addr >= 0x3000) {
// is it a palette entry?
if (addr >= 0x3F00) {
// palette
data &= 0x3F;
if (0x0000 == (addr & 0x000F)) // is it THE 0 entry?0的条目吗
{
ppu->bg_pal[0] = ppu->spr_pal[0] = data;
} else if (0x0000 == (addr & 0x0010)) {
// background palette
ppu->bg_pal[addr & 0x000F] = data;
} else {
// sprite palette
ppu->spr_pal[addr & 0x000F] = data;
}
return;
}
// handle mirroring处理镜像
addr &= 0xEFFF;
}
VRAM(addr) = data;
if (addr < 0x2000) {
#if NES_RAM_SPEED == 1 // 1:内存占用小 0:速度快
*(TILE(addr) + (addr & 0xf)) = data; //************************
#else
update_tile(addr, data);
#endif
}
}
#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;
}
pattern_mask = 0x80;
UPDATE_4_PIXELS();
pattern_mask >>= 1;
UPDATE_4_PIXELS();
}
uint8_t PPU_ReadFromPort(uint16_t addr) {
uint8_t temp;
// switch(addr)
switch (addr & 0x7) {
// case 0x2002:
case 0x2:
// clear toggle
ppu->toggle_2005_2006 = 0;
temp = ppu->LowRegs[2];
// clear v-blank flag
ppu->LowRegs[2] &= 0x7F;
return temp;
// break;
// case 0x2007:
case 0x7:
return read_2007();
// break;
}
return ppu->LowRegs[addr & 0x0007];
}
void PPU_WriteToPort(uint8_t data, uint16_t addr) {
// uint32_t t;
ppu->LowRegs[addr & 0x0007] = data;
// 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;
// t:0000110000000000=d:00000011
ppu->loopy_t = (ppu->loopy_t & 0xF3FF) | (((uint16_t)(data & 0x03)) << 10);
break;
}
// Rick, lazy updating stuff
// case 0x2001:
// case 1:
// bg_enabled();
//
// spr_enabled();
// break;
// case 0x2003:
case 3:
ppu->spr_ram_rw_ptr = data;
break;
// case 0x2004:
case 4:
spr_ram[ppu->spr_ram_rw_ptr++] = data;
break;
// case 0x2005:
case 5:
ppu->toggle_2005_2006 = !ppu->toggle_2005_2006;
if (ppu->toggle_2005_2006) {
// first write
// t:0000000000011111=d:11111000
ppu->loopy_t = (ppu->loopy_t & 0xFFE0) | (((uint16_t)(data & 0xF8)) >> 3);
// x=d:00000111
ppu->loopy_x = data & 0x07;
} else {
// second write
// t:0000001111100000=d:11111000
ppu->loopy_t = (ppu->loopy_t & 0xFC1F) | (((uint16_t)(data & 0xF8)) << 2);
// 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;
if (ppu->toggle_2005_2006) {
// first write
// 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;
}
}
//*****************************************************************************
#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++; \
//***************************************************************************************
#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像素坐标内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:内存占用小 0:速度快
uint8_t pattern_lo;
uint8_t pattern_hi;
uint8_t pattern_mask;
#else
int col2;
#endif
tile_x = (ppu->loopy_v & 0x001F);
tile_y = (ppu->loopy_v & 0x03E0) >> 5;
name_addr = 0x2000 + (ppu->loopy_v & 0x0FFF);
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))
attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
else
attrib_bits = (VRAM(attrib_addr) & 0x0C);
else if (0x0000 == (tile_x & 0x0002))
attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
else
attrib_bits = (VRAM(attrib_addr) & 0xC0) >> 4;
p = buf + (SIDE_MARGIN - ppu->loopy_x);
solid =
ppu->solid_buf + (SIDE_MARGIN - ppu->loopy_x); // set "solid" buffer ptr
line = (ppu->loopy_v & 0x7000) >> 12;
// draw 33 tiles
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:内存占用小 0:速度快
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
tile_x++;
name_addr++;
// are we crossing a dual-tile boundary?我们穿越dual-tile边界吗?
if (0x0000 == (tile_x & 0x0001)) {
// are we crossing a quad-tile boundary?
if (0x0000 == (tile_x & 0x0003)) {
// are we crossing a name table boundary?
if (0x0000 == (tile_x & 0x001F)) {
name_addr ^= 0x0400; // switch name tables
attrib_addr ^= 0x0400;
name_addr -= 0x0020;
attrib_addr -= 0x0008;
tile_x -= 0x0020;
}
attrib_addr++;
}
if (0x0000 == (tile_y & 0x0002))
if (0x0000 == (tile_x & 0x0002))
attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
else
attrib_bits = (VRAM(attrib_addr) & 0x0C);
else if (0x0000 == (tile_x & 0x0002))
attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
else
attrib_bits = (VRAM(attrib_addr) & 0xC0) >> 4;
}
}
// if(bg_clip_left8())
// {
// // clip left 8 pixels夹了8个像素SIDE_MARGIN = 8,
// memset(buf + SIDE_MARGIN, bg_pal[0], 8);
// memset(solid + SIDE_MARGIN, 0, sizeof(solid[0])*8);
// }
}
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:内存占用小 0:速度快
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对当前扫描线?
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);
}
y = ppu->current_frame_line - spr_y;
// CHECK_MMC2(spr[1] <<
//4);//**************************************************************************
// calc offsets into buffers
p = &buf[SIDE_MARGIN + spr_x + start_x];
solid = &ppu->solid_buf[SIDE_MARGIN + spr_x + start_x];
// 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;
}
// 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;
}
#if NES_RAM_SPEED == 1 // 1:内存占用小 0:速度快
t = TILE(tile_addr) + line;
pattern_lo = *t;
pattern_hi = *(t + 8);
#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如果一个雪碧吸引了这个像素,不画任何东西
if (!((*solid) & SPR_WRITTEN_FLAG)) {
#if NES_RAM_SPEED == 1 // 1:内存占用小 0:速度快
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;
}
}