⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nes_ppu.cpp

📁 PocketNester的c++源代码,很好的学习例子,仅供大家学习
💻 CPP
📖 第 1 页 / 共 3 页
字号:
				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 + -