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

📄 nes_ppu.cpp

📁 PocketNester的c++源代码,很好的学习例子,仅供大家学习
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/*
** nester - NES emulator
** Copyright (C) 2000  Darren Ranalli
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful, 
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
** Library General Public License for more details.  To obtain a 
** copy of the GNU Library General Public License, write to the Free 
** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** Any permitted reproduction of these routines, in whole or in part,
** must bear this legend.
*/
/* $Id: Nes_ppu.cpp,v 1.8 2003/03/24 14:47:34 Rick Exp $ */

#include <string.h>
#include "NES_PPU.h"
#include "NES.h"
#include "pixmap.h"
#include "settings.h"

#include "debug.h"
#include "profile.h"

CompiledTiles::CompiledTiles(int maxTiles) : _maxTiles(maxTiles)
{
    _tiles = new uint8[maxTiles * BYTES_PER_MERGED_TILE];
    //memset(_tiles, 0, maxTiles * BYTES_PER_MERGED_TILE);
}

CompiledTiles::~CompiledTiles()
{
    delete _tiles;
}

#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 CompiledTiles::compile(int start, int count, uint8 *data)
{
    //assert(start >= 0 && start + count <= _maxTiles);

    uint8 *p = _tiles + start * BYTES_PER_MERGED_TILE;
	CompiledTiles::compile(count, data, p);
}

void CompiledTiles::compile(int count, uint8 *src, uint8 *dest)
{
    uint8 *p = dest;
    uint8 col;

    for (int i = 0; i < count; i++) {
        for (int line = 0; line < 8; line++) {
            uint8 pattern_lo = *src;
            uint8 pattern_hi = *(src + 8);

            int pattern_mask = 0x80;

			EXTRACT_4_PIXELS();
            pattern_mask >>= 1;
			EXTRACT_4_PIXELS();

            src++;
        }
		src += 8;
    }
}

#define UPDATE_PIXEL() \
  if(data & pattern_mask) *p |= bit; \
  p++;

#define VRAM(addr) \
  PPU_VRAM_banks[(addr) >> 10][(addr) & 0x3FF]

#define TILE(addr) \
  (PPU_tile_banks[(addr) >> 10] + ((addr) & 0x3FF) / 16 * BYTES_PER_MERGED_TILE)

#define TILE_OFFSET(line) \
  ((line) * (BYTES_PER_MERGED_TILE / 8))

/*
scanline start (if background or sprites are enabled):
	v:0000010000011111=t:0000010000011111
*/
#define LOOPY_SCANLINE_START(v,t) \
  { \
    v = (v & 0xFBE0) | (t & 0x041F); \
  }

/*
bits 12-14 are the tile Y offset.
you can think of bits 5,6,7,8,9 as the "y scroll"(*8).  this functions
slightly different from the X.  it wraps to 0 and bit 11 is switched when
it's incremented from _29_ instead of 31.  there are some odd side effects
from this.. if you manually set the value above 29 (from either 2005 or
2006), the wrapping from 29 obviously won't happen, and attrib data will be
used as name table data.  the "y scroll" still wraps to 0 from 31, but
without switching bit 11.  this explains why writing 240+ to 'Y' in 2005
appeared as a negative scroll value.
*/
#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 */ \
    } \
  }

/*
you can think of bits 0,1,2,3,4 of the vram address as the "x scroll"(*8)
that the ppu increments as it draws.  as it wraps from 31 to 0, bit 10 is
switched.  you should see how this causes horizontal wrapping between name
tables (0,1) and (2,3).
*/
#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++; \
    } \
  }

#define CHECK_MMC2(addr) \
  if(((addr) & 0x0FC0) == 0x0FC0) \
  { \
    if((((addr) & 0x0FF0) == 0x0FD0) || (((addr) & 0x0FF0) == 0x0FE0)) \
    { \
      parent_NES->mapper->PPU_Latch_FDFE(addr); \
    } \
  }


NES_PPU::NES_PPU(NES* parent)
{
  parent_NES = parent;
}

void NES_PPU::reset()
{
  // reset registers
  memset(LowRegs, 0x00, sizeof(LowRegs));
  HighReg0x4014 = 0x00;

  // clear sprite RAM
  memset(spr_ram, 0x00, sizeof(spr_ram));

  // clear palettes
  memset(bg_pal,  0x00, sizeof(bg_pal));
  memset(spr_pal, 0x00, sizeof(spr_pal));

  // clear solid buffer
  memset(solid_buf, 0x00, sizeof(solid_buf));

  // clear pattern tables
  /*
  memset(PPU_patterntable0, 0x00, sizeof(PPU_patterntable0));
  memset(PPU_patterntable1, 0x00, sizeof(PPU_patterntable1));
  */
  memset(PPU_patterntables, 0x00, sizeof(PPU_patterntables));
  memset(PPU_patterntype, 0x00, sizeof(PPU_patterntype));

  memset(PPU_tile_tables, 0, sizeof(PPU_tile_tables));

  // clear internal name tables
  memset(PPU_nametables, 0x00, sizeof(PPU_nametables));

  // clear VRAM page table
  memset(PPU_VRAM_banks, 0x00, sizeof(PPU_VRAM_banks));

  memset(PPU_tile_banks, 0, sizeof(PPU_tile_banks));

  // set up PPU memory space table
  /*
  PPU_VRAM_banks[0x00] = PPU_patterntable0 + (0*0x400);
  PPU_VRAM_banks[0x01] = PPU_patterntable0 + (1*0x400);
  PPU_VRAM_banks[0x02] = PPU_patterntable0 + (2*0x400);
  PPU_VRAM_banks[0x03] = PPU_patterntable0 + (3*0x400);

  PPU_VRAM_banks[0x04] = PPU_patterntable1 + (0*0x400);
  PPU_VRAM_banks[0x05] = PPU_patterntable1 + (1*0x400);
  PPU_VRAM_banks[0x06] = PPU_patterntable1 + (2*0x400);
  PPU_VRAM_banks[0x07] = PPU_patterntable1 + (3*0x400);
  */

  PPU_VRAM_banks[0x00] = PPU_patterntables + (0*0x400);
  PPU_VRAM_banks[0x01] = PPU_patterntables + (1*0x400);
  PPU_VRAM_banks[0x02] = PPU_patterntables + (2*0x400);
  PPU_VRAM_banks[0x03] = PPU_patterntables + (3*0x400);
    
  PPU_VRAM_banks[0x04] = PPU_patterntables + (4*0x400);
  PPU_VRAM_banks[0x05] = PPU_patterntables + (5*0x400);
  PPU_VRAM_banks[0x06] = PPU_patterntables + (6*0x400);
  PPU_VRAM_banks[0x07] = PPU_patterntables + (7*0x400);

  // point nametables at internal name table 0
  PPU_VRAM_banks[0x08] = PPU_nametables;
  PPU_VRAM_banks[0x09] = PPU_nametables;
  PPU_VRAM_banks[0x0A] = PPU_nametables;
  PPU_VRAM_banks[0x0B] = PPU_nametables;

  PPU_tile_banks[0x00] = PPU_tile_tables + (0*0x400/16*BYTES_PER_MERGED_TILE);
  PPU_tile_banks[0x01] = PPU_tile_tables + (1*0x400/16*BYTES_PER_MERGED_TILE);
  PPU_tile_banks[0x02] = PPU_tile_tables + (2*0x400/16*BYTES_PER_MERGED_TILE);
  PPU_tile_banks[0x03] = PPU_tile_tables + (3*0x400/16*BYTES_PER_MERGED_TILE);
  PPU_tile_banks[0x04] = PPU_tile_tables + (4*0x400/16*BYTES_PER_MERGED_TILE);
  PPU_tile_banks[0x05] = PPU_tile_tables + (5*0x400/16*BYTES_PER_MERGED_TILE);
  PPU_tile_banks[0x06] = PPU_tile_tables + (6*0x400/16*BYTES_PER_MERGED_TILE);
  PPU_tile_banks[0x07] = PPU_tile_tables + (7*0x400/16*BYTES_PER_MERGED_TILE);

  read_2007_buffer = 0x00;
  in_vblank = 0;
  bg_pattern_table_addr = 0;
  spr_pattern_table_addr = 0;
  ppu_addr_inc = 0;
  loopy_v = 0;
  loopy_t = 0;
  loopy_x = 0;
  toggle_2005_2006 = 0;
  spr_ram_rw_ptr = 0;
  read_2007_buffer = 0;
  current_frame_line = 0;

  // set mirroring
  set_mirroring(parent_NES->ROM->get_mirroring());

  // Rick
  ppu_buf = 0;
  tile_bank_switched = 0;
  memcpy(lazy_tile_banks, PPU_tile_banks, sizeof(PPU_tile_banks));
  name_table_switched = FALSE;
  memcpy(lazy_VRAM_banks, PPU_VRAM_banks, sizeof(PPU_VRAM_banks));
  lazy_v = lazy_t = lazy_x = 0;
}

void NES_PPU::set_mirroring(uint32 nt0, uint32 nt1, uint32 nt2, uint32 nt3)
{
  ASSERT(nt0 < 4); ASSERT(nt1 < 4); ASSERT(nt2 < 4); ASSERT(nt3 < 4);
  PPU_VRAM_banks[0x08] = PPU_nametables + (nt0 << 10); // * 0x0400
  PPU_VRAM_banks[0x09] = PPU_nametables + (nt1 << 10);
  PPU_VRAM_banks[0x0A] = PPU_nametables + (nt2 << 10);
  PPU_VRAM_banks[0x0B] = PPU_nametables + (nt3 << 10);
  name_table_switched = TRUE;
}

void NES_PPU::set_mirroring(mirroring_type m)
{
  if(MIRROR_FOUR_SCREEN == m)
  {
    set_mirroring(0,1,2,3);
  }
  else if(MIRROR_HORIZ == m)
  {
    set_mirroring(0,0,1,1);
  }
  else if(MIRROR_VERT == m)
  {
    set_mirroring(0,1,0,1);
  }
  else
  {
    LOG("Invalid mirroring type" << endl);
    set_mirroring(MIRROR_FOUR_SCREEN);
  }
}

void NES_PPU::start_frame()
{
  current_frame_line = 0;

  if(spr_enabled() || bg_enabled())
  {
    loopy_v = loopy_t;
  }
}

uint8 NES_PPU::getBGColor() { return NES::NES_COLOR_BASE + bg_pal[0]; }

void NES_PPU::do_scanline_and_draw(uint8* buf)
{
  if(!bg_enabled())
  {
    // set to background color
    memset(buf, NES::NES_COLOR_BASE + bg_pal[0], NES_BACKBUF_WIDTH);
  }

  if(spr_enabled() || bg_enabled())
  {
    LOOPY_SCANLINE_START(loopy_v, loopy_t);

    if(bg_enabled())
    {
      // draw background
      render_bg(buf);
    }
    else
    {
      // clear out solid buffer
      memset(solid_buf, 0x00, sizeof(solid_buf));
    }

    if(spr_enabled())
    {
      // draw sprites
      render_spr(buf);
    }

    LOOPY_NEXT_LINE(loopy_v);
  }

  current_frame_line++;
}

void NES_PPU::do_scanline_and_dont_draw()
{
	// mmc2 / punchout -- we must simulate the ppu for every line
	/*if(parent_NES->ROM->get_mapper_num() == 9)
	{
		do_scanline_and_draw(dummy_buffer);
	}
	else*/
	{
		// if sprite 0 flag not set and sprite 0 on current line
		if((!sprite0_hit()) && 
			(current_frame_line >= ((uint32)(spr_ram[0]+1))) && 
			(current_frame_line <  ((uint32)(spr_ram[0]+1+(sprites_8x16()?16:8))))
			)
		{
			// render line to dummy buffer
			do_scanline_and_draw(dummy_buffer);
		}
		else
		{
			if(spr_enabled() || bg_enabled())
			{
				LOOPY_SCANLINE_START(loopy_v, loopy_t);
				LOOPY_NEXT_LINE(loopy_v);
			}
			current_frame_line++;
		}
	}
}

void NES_PPU::end_frame()
{
}

void NES_PPU::start_frame(uint8 *buf, int ypitch)
{
	ppu_buf = buf;
	ppu_ypitch = ypitch;
	current_frame_line = 0;
	lazy_bg_start_line = 0;
	lazy_spr_start_line = 0;
	lazy_bg_need_update = 0;
	lazy_spr_need_update = 0;
	if (tile_bank_switched) {
		memcpy(lazy_tile_banks, PPU_tile_banks, sizeof(PPU_tile_banks));
		tile_bank_switched = FALSE;
	}
	if (name_table_switched) {
		lazy_VRAM_banks[8] = PPU_VRAM_banks[8];
		lazy_VRAM_banks[9] = PPU_VRAM_banks[9];
		lazy_VRAM_banks[10] = PPU_VRAM_banks[10];
		lazy_VRAM_banks[11] = PPU_VRAM_banks[11];
		name_table_switched = FALSE;
	}
	lazy_v = loopy_v;
	lazy_t = loopy_t;
	lazy_x = loopy_x;
	lazy_bg_pattern_table_addr = bg_pattern_table_addr;
	lazy_spr_pattern_table_addr = spr_pattern_table_addr;

	memcpy(lazy_bg_pal, bg_pal, sizeof(lazy_bg_pal));
	lazy_bg_pal[4] = lazy_bg_pal[8] = lazy_bg_pal[12] = lazy_bg_pal[0];
	memcpy(lazy_spr_pal, spr_pal, sizeof(lazy_spr_pal));
	lazy_spr_pal[4] = lazy_spr_pal[8] = lazy_spr_pal[12] = lazy_spr_pal[0];

	lazy_bg_enabled = bg_enabled();
	lazy_spr_enabled = spr_enabled();

	if(lazy_bg_enabled || lazy_spr_enabled) {
		loopy_v = loopy_t;
		lazy_v = lazy_t;
	}

	mapper_num = parent_NES->ROM->get_mapper_num();
}

void NES_PPU::lazy_do_scanline()
{
	// mmc2 / punchout -- we must simulate the ppu for every line
	/*
	if(parent_NES->ROM->get_mapper_num() == 9)
	{
		do_scanline_and_draw(dummy_buffer);
	}
	else
	{*/
	if (!lazy_bg_enabled) {
			memset(ppu_buf + current_frame_line * ppu_ypitch,
				NES::NES_COLOR_BASE + bg_pal[0], NES_BACKBUF_WIDTH);
	}

	if (tile_bank_switched) {
		if (memcmp(lazy_tile_banks, PPU_tile_banks, sizeof(PPU_tile_banks))) {
			lazy_bg_need_update = TRUE;
			lazy_spr_need_update = TRUE;
		} else {
			tile_bank_switched = FALSE;
		}
	}
	
	if (name_table_switched) {
		if (memcmp(&lazy_VRAM_banks[8], &PPU_VRAM_banks[8], sizeof(lazy_VRAM_banks[0]) * 4)) {
			lazy_bg_need_update = TRUE;
		}
	}

	//if (lazy_bg_need_update) {
	// we should always update BG before SPR
	if (lazy_bg_need_update || lazy_spr_need_update) {
		if (lazy_bg_enabled) {
			lazy_update_bg();
		}
		lazy_bg_start_line = current_frame_line;
		lazy_bg_need_update = FALSE;
		lazy_bg_enabled = bg_enabled();
		lazy_bg_pattern_table_addr = bg_pattern_table_addr;
		lazy_v = loopy_v;
		lazy_t = loopy_t;
		lazy_x = loopy_x;
		memcpy(lazy_bg_pal, bg_pal, sizeof(lazy_bg_pal));
		lazy_bg_pal[4] = lazy_bg_pal[8] = lazy_bg_pal[12] = lazy_bg_pal[0];
	}
	
	if (lazy_spr_need_update) {
		if (lazy_spr_enabled) {
			lazy_update_spr();
		}
		lazy_spr_start_line = current_frame_line;
		lazy_spr_need_update = FALSE;
		lazy_spr_enabled = spr_enabled();
		lazy_spr_pattern_table_addr = spr_pattern_table_addr;
		memcpy(lazy_spr_pal, spr_pal, sizeof(lazy_spr_pal));
		lazy_spr_pal[4] = lazy_spr_pal[8] = lazy_spr_pal[12] = lazy_spr_pal[0];
	}
	
	if (tile_bank_switched) {
		memcpy(lazy_tile_banks, PPU_tile_banks, sizeof(PPU_tile_banks));
		tile_bank_switched = FALSE;
	}
	
	if (name_table_switched) {
		lazy_VRAM_banks[8] = PPU_VRAM_banks[8];
		lazy_VRAM_banks[9] = PPU_VRAM_banks[9];
		lazy_VRAM_banks[10] = PPU_VRAM_banks[10];
		lazy_VRAM_banks[11] = PPU_VRAM_banks[11];
		name_table_switched = FALSE;
	}

	// if sprite 0 flag not set and sprite 0 on current line
	/*
	if((!sprite0_hit()) && 
		(current_frame_line >= ((uint32)(spr_ram[0]+1))) && 
		(current_frame_line <  ((uint32)(spr_ram[0]+1+(sprites_8x16()?16:8))))
		)
	{
		// render line to dummy buffer
		do_scanline_and_draw(dummy_buffer);
	}
	else
	{*/
	if(spr_enabled() || bg_enabled())
	{
		LOOPY_SCANLINE_START(loopy_v, loopy_t);
		// if sprite 0 flag not set and sprite 0 on current line
		if(!sprite0_hit()
			&& spr_enabled()
			&& bg_enabled()
			&& (current_frame_line >= ((uint32)(spr_ram[0]+1)))
			&& (current_frame_line <  ((uint32)(spr_ram[0]+1+(sprites_8x16()?16:8))))
			)
		{
			fast_test_sprite0_hit();
		}

		LOOPY_NEXT_LINE(loopy_v);
	}
	current_frame_line++;
	//}
}

void NES_PPU::lazy_do_scanline_dont_draw()
{
	// mmc2 / punchout -- we must simulate the ppu for every line
	/*if(parent_NES->ROM->get_mapper_num() == 9)
	{
		do_scanline_and_draw(dummy_buffer);
	}
	else*/

	if(spr_enabled() || bg_enabled())
	{
		LOOPY_SCANLINE_START(loopy_v, loopy_t);
		// if sprite 0 flag not set and sprite 0 on current line
		if(!sprite0_hit()
			&& spr_enabled()
			&& bg_enabled()
			&& (current_frame_line >= ((uint32)(spr_ram[0]+1)))
			&& (current_frame_line <  ((uint32)(spr_ram[0]+1+(sprites_8x16()?16:8))))
			)
		{
			fast_test_sprite0_hit();
		}

		LOOPY_NEXT_LINE(loopy_v);
	}
	current_frame_line++;
}

void NES_PPU::end_frame(uint8 *buf)
{
	if (lazy_bg_enabled) {
		lazy_update_bg();
	}
	if (lazy_spr_enabled) {
		lazy_update_spr();
	}
	ppu_buf = 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -