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

📄 nes_ppu.cpp

📁 PocketNester的c++源代码,很好的学习例子,仅供大家学习
💻 CPP
📖 第 1 页 / 共 3 页
字号:
}

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 + -