📄 nes_ppu.cpp
字号:
}
void NES_PPU::start_vblank()
{
in_vblank = 1;
// set vblank register flag
LowRegs[2] |= 0x80;
}
void NES_PPU::end_vblank()
{
in_vblank = 0;
// reset vblank register flag and sprite0 hit flag1
LowRegs[2] &= 0x3F;
}
// these functions read from/write to VRAM using loopy_v
uint8 NES_PPU::read_2007()
{
uint16 addr;
uint8 temp;
addr = loopy_v;
loopy_v += ppu_addr_inc;
ASSERT(addr < 0x4000);
addr &= 0x3FFF;
if(addr >= 0x3000)
{
// is it a palette entry?
if(addr >= 0x3F00)
{
// palette
// handle palette mirroring
if(0x0000 == (addr & 0x0010))
{
// background palette
return bg_pal[addr & 0x000F];
}
else
{
// sprite palette
return spr_pal[addr & 0x000F];
}
}
// handle mirroring
addr &= 0xEFFF;
}
temp = read_2007_buffer;
read_2007_buffer = VRAM(addr);
return temp;
}
void NES_PPU::write_2007(uint8 data)
{
uint16 addr;
addr = loopy_v;
loopy_v += ppu_addr_inc;
addr &= 0x3FFF;
// LOG("PPU 2007 WRITE: " << HEX(addr,4) << " " << HEX(data,2) << endl);
if(addr >= 0x3000)
{
// is it a palette entry?
if(addr >= 0x3F00)
{
// palette
data &= 0x3F;
if(0x0000 == (addr & 0x000F)) // is it THE 0 entry?
{
bg_pal[0] = spr_pal[0] = data;
lazy_bg_need_update = lazy_spr_need_update = TRUE;
}
else if(0x0000 == (addr & 0x0010))
{
// background palette
if (bg_pal[addr & 0x000F] != data) {
bg_pal[addr & 0x000F] = data;
lazy_bg_need_update = TRUE;
}
}
else
{
// sprite palette
if (spr_pal[addr & 0x000F] != data) {
spr_pal[addr & 0x000F] = data;
lazy_spr_need_update = TRUE;
}
}
return;
}
// handle mirroring
addr &= 0xEFFF;
}
if(!(vram_write_protect && addr < 0x2000))
{
VRAM(addr) = data;
}
if (!vram_write_protect && addr < 0x2000) {
//tiles.updateTile(addr, data);
update_tile(addr, data);
}
}
#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 NES_PPU::update_tile(int byte_offset, uint8 data)
{
//int tileNum = byte_offset >> 4;
int line = byte_offset & 0xf;
uint8 *p = TILE(byte_offset) + TILE_OFFSET(line & 0x7);
uint8 bit;
if (line < 8) {
// low pattern
bit = 0x01;
*(uint16 *)p &= 0xaaaa;
} else {
// high pattern
bit = 0x02;
*(uint16 *)p &= 0x5555;
}
uint8 pattern_mask = 0x80;
int col;
UPDATE_4_PIXELS();
pattern_mask >>= 1;
UPDATE_4_PIXELS();
}
uint8 NES_PPU::ReadLowRegs(uint32 addr)
{
ASSERT((addr >= 0x2000) && (addr < 0x2008));
// LOG("PPU Read " << HEX(addr,4) << endl);
//switch(addr)
switch(addr & 0x7)
{
//case 0x2002:
case 0x2:
{
uint8 temp;
// clear toggle
toggle_2005_2006 = 0;
temp = LowRegs[2];
// clear v-blank flag
LowRegs[2] &= 0x7F;
return temp;
}
break;
//case 0x2007:
case 0x7:
return read_2007();
break;
}
return LowRegs[addr & 0x0007];
}
void NES_PPU::WriteLowRegs(uint32 addr, uint8 data)
{
ASSERT((addr >= 0x2000) && (addr < 0x2008));
// LOG("PPU Write " << HEX(addr,4) << " = " << HEX(data,2) << endl);
LowRegs[addr & 0x0007] = data;
//switch(addr)
switch(addr & 0x7)
{
//case 0x2000:
case 0:
{
bg_pattern_table_addr = (data & 0x10) ? 0x1000 : 0x0000;
spr_pattern_table_addr = (data & 0x08) ? 0x1000 : 0x0000;
ppu_addr_inc = (data & 0x04) ? 32 : 1;
uint32 t = loopy_t;
// t:0000110000000000=d:00000011
loopy_t = (loopy_t & 0xF3FF) | (((uint16)(data & 0x03)) << 10);
//if (ppu_buf && t != loopy_t)
if (bg_pattern_table_addr != lazy_bg_pattern_table_addr || t != loopy_t) {
lazy_bg_need_update = TRUE;
}
if (spr_pattern_table_addr != lazy_spr_pattern_table_addr) {
lazy_spr_need_update = TRUE;
}
break;
}
// Rick, lazy updating stuff
//case 0x2001:
case 1:
/* if (ppu_buf) {
{
int bgenabled = bg_enabled();
if (bgenabled && !lazy_bg_enabled) {
lazy_bg_start_line = current_frame_line;
lazy_bg_enabled = TRUE;
} else if (!bgenabled && lazy_bg_enabled) {
lazy_bg_need_update = TRUE;
}
int sprenabled = spr_enabled();
if (sprenabled && !lazy_spr_enabled) {
lazy_spr_start_line = current_frame_line;
lazy_spr_enabled = TRUE;
} else if (!sprenabled && lazy_spr_enabled) {
lazy_spr_need_update = TRUE;
}
} */
if (bg_enabled() != lazy_bg_enabled) {
lazy_bg_need_update = TRUE;
}
if (spr_enabled() != lazy_spr_enabled) {
lazy_spr_need_update = TRUE;
}
break;
//case 0x2003:
case 3:
spr_ram_rw_ptr = data;
break;
//case 0x2004:
case 4:
spr_ram[spr_ram_rw_ptr++] = data;
break;
//case 0x2005:
case 5:
toggle_2005_2006 = !toggle_2005_2006;
if(toggle_2005_2006)
{
uint16 t = loopy_t;
uint8 x = loopy_x;
// first write
// t:0000000000011111=d:11111000
loopy_t = (loopy_t & 0xFFE0) | (((uint16)(data & 0xF8)) >> 3);
// x=d:00000111
loopy_x = data & 0x07;
//if (ppu_buf && (t != loopy_t || x != loopy_x))
if (t != loopy_t || x != loopy_x)
lazy_bg_need_update = TRUE;
}
else
{
uint16 t = loopy_t;
// second write
// t:0000001111100000=d:11111000
loopy_t = (loopy_t & 0xFC1F) | (((uint16)(data & 0xF8)) << 2);
// t:0111000000000000=d:00000111
loopy_t = (loopy_t & 0x8FFF) | (((uint16)(data & 0x07)) << 12);
//if (ppu_buf && t != loopy_t)
if (t != loopy_t)
lazy_bg_need_update = TRUE;
}
break;
//case 0x2006:
case 6:
toggle_2005_2006 = !toggle_2005_2006;
if(toggle_2005_2006)
{
// first write
// t:0011111100000000=d:00111111
// t:1100000000000000=0
loopy_t = (loopy_t & 0x00FF) | (((uint16)(data & 0x3F)) << 8);
}
else
{
// second write
// t:0000000011111111=d:11111111
loopy_t = (loopy_t & 0xFF00) | ((uint16)data);
// v=t
loopy_v = loopy_t;
}
//if (ppu_buf)
lazy_bg_need_update = 1;
break;
//case 0x2007:
case 7:
write_2007(data);
break;
}
}
uint8 NES_PPU::Read0x4014()
{
return HighReg0x4014;
}
void NES_PPU::Write0x4014(uint8 data)
{
uint32 addr;
// LOG("PPU Write 0x4014 = " << HEX(data,2) << endl);
HighReg0x4014 = data;
addr = ((uint32)data) << 8;
// do SPR-RAM DMA
/*
for(uint32 i = 0; i < 256; i++)
{
spr_ram[i] = parent_NES->cpu->GetByte(addr++);
}
*/
memcpy(spr_ram, parent_NES->cpu->Get_DMA_mem_ptr(addr), 256);
}
#define NEW_BG_PIXEL() \
if (col) {\
col |= attrib_bits; \
*solid = BG_WRITTEN_FLAG; \
} else { \
*solid = 0; \
} \
*p = NES::NES_COLOR_BASE + bg_pal[col]; \
solid++; \
p++; \
void NES_PPU::render_bg(uint8* buf)
{
uint8 *p;
uint32 i;
//uint32 *solid;
uint8 *solid;
//uint8 col;
uint32 col;
uint32 tile_x; // pixel coords within nametable
uint32 tile_y;
uint32 name_addr;
uint32 pattern_addr;
/*
uint8 pattern_lo;
uint8 pattern_hi;
uint8 pattern_mask;*/
uint32 attrib_addr;
//uint8 attrib_bits;
uint32 attrib_bits;
tile_x = (loopy_v & 0x001F);
tile_y = (loopy_v & 0x03E0) >> 5;
name_addr = 0x2000 + (loopy_v & 0x0FFF);
attrib_addr = 0x2000 + (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 - loopy_x);
solid = solid_buf + (SIDE_MARGIN - loopy_x); // set "solid" buffer ptr
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)VRAM(name_addr) << 4) + line;
//this_tile |= (attrib_bits << 8);
//const uint8 *data = tile_data + (pattern_addr >> 4) * BYTES_PER_MERGED_TILE + line * 8;
uint8 *data = TILE(pattern_addr) + TILE_OFFSET(line);
CHECK_MMC2(pattern_addr);
int col2;
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();
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;
}
}
if(bg_clip_left8())
{
// clip left 8 pixels
memset(buf + SIDE_MARGIN, NES::NES_COLOR_BASE + bg_pal[0], 8);
memset(solid + SIDE_MARGIN, 0, sizeof(solid[0])*8);
}
}
void NES_PPU::render_spr(uint8* buf)
{
int32 s; // sprite #
int32 spr_x; // sprite coordinates
uint32 spr_y;
uint8* spr; // pointer to sprite RAM entry
uint8* p; // draw pointer
//uint32 *solid;
uint8 *solid;
uint32 priority;
int32 inc_x; // drawing vars
int32 start_x, end_x;
int32 x,y; // in-sprite coords
uint32 num_sprites = 0;
uint32 spr_height;
spr_height = sprites_8x16() ? 16 : 8;
//for(s = 0; s < 64; s++)
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?
if((spr_y > current_frame_line) || ((spr_y+(spr_height)) <= current_frame_line))
continue;
num_sprites++;
if(num_sprites > 8)
{
if(!NESTER_settings.nes.graphics.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 = current_frame_line - spr_y;
CHECK_MMC2(spr[1] << 4);
// calc offsets into buffers
p = &buf[SIDE_MARGIN + spr_x + start_x];
solid = &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;
}
int line = y & 7;
// get priority bit
priority = spr[2] & 0x20;
uint32 tile_addr = spr[1] << 4;
if(sprites_8x16()) {
if(spr[1] & 0x01) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -