📄 nes_ppu.cpp
字号:
tile_addr += 0x1000;
if(y < 8) tile_addr -= 16;
} else {
if(y >= 8) tile_addr += 16;
}
} else {
tile_addr += spr_pattern_table_addr;
}
// read 16bits = 2bits x 8pixels
uint8 *t = TILE(tile_addr) + TILE_OFFSET(line);
uint32 pattern = ((uint32)*t << 8) | *(t + 1);
uint32 attrib_bits = (spr[2] & 0x03) << 2;
for(x = start_x; x != end_x; x += inc_x)
{
//uint8 col = 0x00;
uint32 col;
// if a sprite has drawn on this pixel, don't draw anything
if(!((*solid) & SPR_WRITTEN_FLAG))
{
//col = *(TILE(tile_addr) + TILE_OFFSET(line) + ((x & 0x7) >> 2));
col = pattern >> ((7 - (x & 7)) * 2);
col &= 0x03;
if (col) {
col |= attrib_bits;
// set sprite 0 hit flag
if(!s)
{
if((*solid) & BG_WRITTEN_FLAG)
{
LowRegs[2] |= 0x40;
}
}
(*solid) |= SPR_WRITTEN_FLAG;
if(priority)
{
//(*solid) |= SPR_WRITTEN_FLAG;
if(!((*solid) & BG_WRITTEN_FLAG))
{
*p = NES::NES_COLOR_BASE + spr_pal[col];
}
}
else
{
//if(!((*solid) & SPR_WRITTEN_FLAG))
//{
*p = NES::NES_COLOR_BASE + spr_pal[col];
//(*solid) |= SPR_WRITTEN_FLAG;
//}
}
}
}
p++;
solid++;
}
}
if(num_sprites >= 8)
{
LowRegs[2] |= 0x20;
}
else
{
LowRegs[2] &= 0xDF;
}
}
#undef NEW_BG_PIXEL
#undef VRAM
#define VRAM(addr) \
lazy_VRAM_banks[(addr) >> 10][(addr) & 0x3FF]
// fast sprite 0 hit test, no real rendering
void NES_PPU::fast_test_sprite0_hit()
{
uint8 *buf = dummy_buffer;
uint8 *p;
uint32 i;
uint32 col;
uint32 tile_x; // pixel coords within nametable
uint32 tile_y;
uint32 name_addr;
uint32 pattern_addr;
tile_x = (loopy_v & 0x001F);
tile_y = (loopy_v & 0x03E0) >> 5;
name_addr = 0x2000 + (loopy_v & 0x0FFF);
p = buf + (SIDE_MARGIN - loopy_x);
uint32 line = (loopy_v & 0x7000) >> 12;
// draw 33 tiles
for(i = 33; i; i--)
{
uint32 this_tile = VRAM(name_addr);
pattern_addr = bg_pattern_table_addr + ((int32)this_tile << 4) + line;
//CHECK_MMC2(pattern_addr);
uint8 *data = TILE(pattern_addr) + TILE_OFFSET(line);
col = *(uint16 *)data;
p[0] = (col >> 6) & 0x03;
p[1] = (col >> 4) & 0x03;
p[2] = (col >> 2) & 0x03;
p[3] = col & 0x03;
p[4] = col >> 14;
p[5] = (col >> 12) & 0x03;
p[6] = (col >> 10) & 0x03;
p[7] = (col >> 8) & 0x03;
p += 8;
tile_x++;
name_addr++;
// are we crossing a dual-tile boundary?
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
name_addr -= 0x0020;
tile_x -= 0x0020;
}
}
}
}
if(bg_clip_left8())
{
// clip left 8 pixels
memset(buf + SIDE_MARGIN, 0, 8);
}
int32 spr_x; // sprite coordinates
uint32 spr_y;
uint8* spr = spr_ram; // pointer to sprite 0
int32 inc_x; // drawing vars
int32 start_x, end_x;
int32 x,y; // in-sprite coords
uint32 spr_height = sprites_8x16() ? 16 : 8;
// get y coord
spr_y = spr[0]+1;
// 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) return;
start_x += (8 - spr_x);
}
y = current_frame_line - spr_y;
//CHECK_MMC2(spr[1] << 4);
// calc offsets into buffers
p = &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;
uint32 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 += spr_pattern_table_addr;
}
// read 16bits = 2bits x 8pixels
uint8 *t = TILE(tile_addr) + TILE_OFFSET(line);
uint32 pattern = ((uint32)*t << 8) | *(t + 1);
for(x = start_x; x != end_x; x += inc_x)
{
//uint8 col = 0x00;
uint32 col;
col = pattern >> ((7 - (x & 7)) * 2);
col &= 0x03;
if (col && *p) {
// set sprite 0 hit flag
LowRegs[2] |= 0x40;
return;
}
p++;
}
}
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#undef TILE
#define TILE(addr) \
(lazy_tile_banks[(addr) >> 10] + ((addr) & 0x3FF) / 16 * BYTES_PER_MERGED_TILE)
void NES_PPU::lazy_update_bg()
{
LOOPY_SCANLINE_START(lazy_v, lazy_t);
uint8 *p;
uint32 i;
uint32 tile_x; // pixel coords within nametable
uint32 tile_y;
uint32 name_addr;
uint32 pattern_addr;
uint32 attrib_addr;
uint32 attrib_bits;
uint8 * buf_line_start = ppu_buf + lazy_bg_start_line * ppu_ypitch;
while (lazy_bg_start_line < current_frame_line) {
name_addr = 0x2000 + (lazy_v & 0x0FFF);
tile_y = (lazy_v & 0x03E0) >> 5;
tile_x = (lazy_v & 0x001F);
attrib_addr = 0x2000 + (lazy_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;
}
uint8 * fast_pal = &lazy_bg_pal[attrib_bits];
uint8 * buf = buf_line_start + (SIDE_MARGIN - lazy_x);
uint32 line = (lazy_v & 0x7000) >> 12;
uint32 lines_to_draw = MIN(8 - line, current_frame_line - lazy_bg_start_line);
// draw 33 tiles
for(i = 33; i; i--) {
uint32 this_tile = VRAM(name_addr);
//pattern_addr = bg_pattern_table_addr + ((int32)this_tile << 4) + line;
pattern_addr = lazy_bg_pattern_table_addr + ((int32)this_tile << 4) + line;
uint8 *data;
uint8 **tile_banks;
if (mapper_num == 5) {
if(uint8 MMC5_pal = parent_NES->mapper->PPU_Latch_RenderScreen(1,name_addr & 0x03FF))
{
attrib_bits = MMC5_pal & 0x0C;
fast_pal = &lazy_bg_pal[attrib_bits];
//memcpy(lazy_tile_banks, PPU_tile_banks, sizeof(lazy_tile_banks));
}
tile_banks = PPU_tile_banks;
} else {
/*
if(((name_addr) & 0x0FC0) == 0x0FC0) {
if((((name_addr) & 0x0FF0) == 0x0FD0) || (((name_addr) & 0x0FF0) == 0x0FE0)) {
parent_NES->mapper->PPU_Latch_FDFE(name_addr);
}
}
memcpy(lazy_tile_banks, PPU_tile_banks, sizeof(lazy_tile_banks));
*/
tile_banks = lazy_tile_banks;
}
data = (tile_banks[pattern_addr >> 10] + (pattern_addr & 0x3FF) / 16 * BYTES_PER_MERGED_TILE) + (line) * (BYTES_PER_MERGED_TILE / 8);
p = buf;
//uint8 *data = TILE(pattern_addr) + TILE_OFFSET(line);
uint32 col;
for (int j = lines_to_draw; j > 0; j--) {
col = *(uint16 *)data;
data += 2;
// TODO: for little endian CPU only
/*
*p++ = NES::NES_COLOR_BASE + fast_pal[(col >> 6) & 0x03];
*p++ = NES::NES_COLOR_BASE + fast_pal[(col >> 4) & 0x03];
*p++ = NES::NES_COLOR_BASE + fast_pal[(col >> 2) & 0x03];
*p++ = NES::NES_COLOR_BASE + fast_pal[col & 0x03];
*p++ = NES::NES_COLOR_BASE + fast_pal[col >> 14];
*p++ = NES::NES_COLOR_BASE + fast_pal[(col >> 12) & 0x03];
*p++ = NES::NES_COLOR_BASE + fast_pal[(col >> 10) & 0x03];
*p++ = NES::NES_COLOR_BASE + fast_pal[(col >> 8) & 0x03];
p += ppu_ypitch - 8;
*/
p[0] = NES::NES_COLOR_BASE + fast_pal[(col >> 6) & 0x03];
p[1] = NES::NES_COLOR_BASE + fast_pal[(col >> 4) & 0x03];
p[2] = NES::NES_COLOR_BASE + fast_pal[(col >> 2) & 0x03];
p[3] = NES::NES_COLOR_BASE + fast_pal[col & 0x03];
p[4] = NES::NES_COLOR_BASE + fast_pal[col >> 14];
p[5] = NES::NES_COLOR_BASE + fast_pal[(col >> 12) & 0x03];
p[6] = NES::NES_COLOR_BASE + fast_pal[(col >> 10) & 0x03];
p[7] = NES::NES_COLOR_BASE + fast_pal[(col >> 8) & 0x03];
p += ppu_ypitch;
}
buf += 8; // next tile
tile_x++;
name_addr++;
// are we crossing a dual-tile boundary?
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;
}
fast_pal = &lazy_bg_pal[attrib_bits];
}
}
lazy_bg_start_line += lines_to_draw;
while (lines_to_draw) {
if(bg_clip_left8()) {
// clip left 8 pixels
memset(buf_line_start + SIDE_MARGIN, NES::NES_COLOR_BASE + bg_pal[0], 8);
}
buf_line_start += ppu_ypitch;
LOOPY_NEXT_LINE(lazy_v);
lines_to_draw--;
}
}
}
static uint32 spr_written[NES_PPU::NES_SCREEN_HEIGHT * NES_PPU::NES_SCREEN_WIDTH / 32];
#undef SPR_WRITTEN
#undef SET_SPR_WRITTEN
#define SPR_WRITTEN(ptr) (spr_written[(ptr) / 32] & (1 << (ptr % 32)))
#define SET_SPR_WRITTEN(ptr) spr_written[(ptr) / 32] |= (1 << (ptr % 32))
void NES_PPU::lazy_update_spr()
{
int32 s; // sprite #
int32 spr_x; // sprite coordinates
uint32 spr_y;
uint8* spr; // pointer to sprite RAM entry
uint8* p; // draw pointer
//uint8 *solid;
uint32 solid_ptr;
uint32 priority;
int32 inc_x, inc_y; // drawing vars
int32 start_x, end_x;
int32 x,y; // in-sprite coords
uint32 spr_height;
spr_height = sprites_8x16() ? 16 : 8;
memset(&spr_written[NES_SCREEN_WIDTH / 32 * lazy_spr_start_line], 0,
NES_SCREEN_WIDTH / 8 * (current_frame_line - lazy_spr_start_line));
// for MMC5 VROM switch
if (mapper_num == 5) {
parent_NES->mapper->PPU_Latch_RenderScreen(0,0);
memcpy(lazy_tile_banks, PPU_tile_banks, sizeof(lazy_tile_banks));
}
for(s = 0, spr = spr_ram; s < 64; s++, spr+=4)
{
//spr = &spr_ram[s<<2];
// get y coord
spr_y = spr[0]+1;
// on current scanline region?
if((spr_y > current_frame_line) || ((spr_y+(spr_height)) <= lazy_spr_start_line))
continue;
// 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);
}
/*
int name_addr = spr[1] << 4;
if(((name_addr) & 0x0FC0) == 0x0FC0) {
if((((name_addr) & 0x0FF0) == 0x0FD0) || (((name_addr) & 0x0FF0) == 0x0FE0)) {
parent_NES->mapper->PPU_Latch_FDFE(name_addr);
}
}
memcpy(lazy_tile_banks, PPU_tile_banks, sizeof(lazy_tile_banks));
*/
// clip top
if (spr_y < lazy_spr_start_line) {
y = lazy_spr_start_line - spr_y;
} else {
y = 0;
}
int lines_to_draw = MIN(spr_height - y, current_frame_line - spr_y);
// calc offsets into buffers
p = ppu_buf + ppu_ypitch * (spr_y + y) + SIDE_MARGIN + spr_x + start_x;
//solid = spr_written + NES_BACKBUF_WIDTH * (spr_y + y) + SIDE_MARGIN + spr_x + start_x;
solid_ptr = NES_SCREEN_WIDTH * (spr_y + y) + spr_x + start_x;
// flip horizontally?
int x_len = end_x - start_x;;
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;
inc_y = -1;
}
else
{
inc_y = 1;
}
// get priority bit
priority = spr[2] & 0x20;
uint8 * fast_pal = &lazy_spr_pal[(spr[2] & 0x03) << 2];
while (lines_to_draw) {
int line = y & 7;
uint32 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 += lazy_spr_pattern_table_addr;
}
// read 16bits = 2bits x 8pixels
uint8 *t = TILE(tile_addr) + TILE_OFFSET(line);
uint32 pattern = ((uint32)*t << 8) | *(t + 1);
if (pattern) {
for(x = start_x; x != end_x; x += inc_x)
{
//uint8 col = 0x00;
uint32 col;
// if a sprite has drawn on this pixel, don't draw anything
//if(!(*solid))
if(!SPR_WRITTEN(solid_ptr))
{
//col = *(TILE(tile_addr) + TILE_OFFSET(line) + ((x & 0x7) >> 2));
col = pattern >> ((7 - (x & 7)) * 2);
col &= 0x03;
if (col) {
//col |= attrib_bits;
//*solid = 1;
SET_SPR_WRITTEN(solid_ptr);
if(priority)
{
if(*p == NES::NES_COLOR_BASE + fast_pal[0]) // BG color
{
*p = NES::NES_COLOR_BASE + fast_pal[col];
}
}
else
{
*p = NES::NES_COLOR_BASE + fast_pal[col];
}
}
}
p++;
//solid++;
solid_ptr++;
}
p += ppu_ypitch - x_len;
//solid += NES_BACKBUF_WIDTH - x_len;
solid_ptr += NES_SCREEN_WIDTH - x_len;
} else {
p += ppu_ypitch;
solid_ptr += NES_SCREEN_WIDTH;
}
lines_to_draw--;
y += inc_y;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -