1 
2 // NES PPU misc functions and setup
3 
4 // Nes_Emu 0.7.0
5 
6 #ifndef NES_PPU_IMPL_H
7 #define NES_PPU_IMPL_H
8 
9 #include "nes_data.h"
10 class Nes_State_;
11 
12 class Nes_Ppu_Impl : public ppu_state_t {
13 public:
14 	Nes_Ppu_Impl();
15 	~Nes_Ppu_Impl();
16 
17 	void reset( bool full_reset );
18 
19 	// Setup
20 	const char * open_chr( const uint8_t*, long size );
21 	void rebuild_chr( unsigned long begin, unsigned long end );
22 	void close_chr();
23 	void save_state( Nes_State_* out ) const;
24 	void load_state( Nes_State_ const& );
25 
26 	enum { image_width = 256 };
27 	enum { image_height = 240 };
28 	enum { image_left = 8 };
29 	enum { buffer_width = image_width + 16 };
30 	enum { buffer_height = image_height };
31 
32 	int write_2007( int );
33 
34 	// Host palette
35 	enum { palette_increment = 64 };
36 	short* host_palette;
37 	int palette_begin;
38 	int max_palette_size;
39 	int palette_size; // set after frame is rendered
40 
41 	// Mapping
42 	enum { vaddr_clock_mask = 0x1000 };
43 	void set_nt_banks( int bank0, int bank1, int bank2, int bank3 );
44 	void set_chr_bank( int addr, int size, long data );
45 	void set_chr_bank_ex( int addr, int size, long data );
46 
47 	// Nametable and CHR RAM
48 	enum { nt_ram_size = 0x1000 };
49 	enum { chr_addr_size = 0x2000 };
50 	enum { bytes_per_tile = 16 };
51 	enum { chr_tile_count = chr_addr_size / bytes_per_tile };
52 	enum { mini_offscreen_height = 16 }; // double-height sprite
53 	struct impl_t
54 	{
55 		uint8_t nt_ram [nt_ram_size];
56 		uint8_t chr_ram [chr_addr_size];
57 		union {
58 			uint32_t clip_buf [256 * 2];
59 			uint8_t mini_offscreen [buffer_width * mini_offscreen_height];
60 		};
61 	};
62 	impl_t* impl;
63 	enum { scanline_len = 341 };
64 
65 protected:
66 	uint8_t spr_ram [0x100];
67 	void begin_frame();
68 	void run_hblank( int );
sprite_height()69 	int sprite_height() const { return (w2000 >> 2 & 8) + 8; }
70 
71 protected: //friend class Nes_Ppu; private:
72 
73 	int addr_inc; // pre-calculated $2007 increment (based on w2001 & 0x04)
74 	int read_2007( int addr );
75 
76 	enum { last_sprite_max_scanline = 240 };
77 	long recalc_sprite_max( int scanline );
78 	int first_opaque_sprite_line();
79 
80 protected: //friend class Nes_Ppu_Rendering; private:
81 
82 	unsigned long palette_offset;
83 	int palette_changed;
84 	void capture_palette();
85 
86 	bool any_tiles_modified;
87 	bool chr_is_writable;
88 	void update_tiles( int first_tile );
89 
90 	typedef uint32_t cache_t;
91 	typedef cache_t cached_tile_t [4];
92 	cached_tile_t const& get_bg_tile( int index );
93 	cached_tile_t const& get_sprite_tile( uint8_t const* sprite );
get_nametable(int addr)94 	uint8_t* get_nametable( int addr ) { return nt_banks [addr >> 10 & 3]; };
95 
96 private:
97 
98 	static int map_palette( int addr );
99 	int sprite_tile_index( uint8_t const* sprite ) const;
100 
101 	// Mapping
102 	enum { chr_page_size = 0x400 };
103 	long chr_pages [chr_addr_size / chr_page_size];
104 	long chr_pages_ex [chr_addr_size / chr_page_size];
map_chr_addr(unsigned a)105 	long map_chr_addr( unsigned a ) /*const*/
106 	{
107 		if (!mmc24_enabled)
108 			return chr_pages [a / chr_page_size] + a;
109 
110 		int page = a >> 12 & 1;
111 		int newval0 = (a & 0xff0) != 0xfd0;
112 		int newval1 = (a & 0xff0) == 0xfe0;
113 
114 		long ret;
115 		if (mmc24_latched[page])
116 			ret = chr_pages_ex [a / chr_page_size] + a;
117 		else
118 			ret = chr_pages [a / chr_page_size] + a;
119 
120 		mmc24_latched[page] &= newval0;
121 		mmc24_latched[page] |= newval1;
122 
123 		return ret;
124 	}
125 	uint8_t* nt_banks [4];
126 
127 	bool mmc24_enabled;
128 	uint8_t mmc24_latched [2];
129 
130 	// CHR data
131 	uint8_t const* chr_data; // points to chr ram when there is no read-only data
132 	uint8_t* chr_ram; // always points to impl->chr_ram; makes write_2007() faster
133 	long chr_size;
map_chr(int addr)134 	uint8_t const* map_chr( int addr ) { return &chr_data [map_chr_addr( addr )]; }
135 
136 	// CHR cache
137 	cached_tile_t* tile_cache;
138 	cached_tile_t* flipped_tiles;
139 	uint8_t* tile_cache_mem;
140 	union {
141 		uint8_t modified_tiles [chr_tile_count / 8];
142 		uint32_t align_;
143 	};
144 	void all_tiles_modified();
145 	void update_tile( int index );
146 };
147 
set_nt_banks(int bank0,int bank1,int bank2,int bank3)148 inline void Nes_Ppu_Impl::set_nt_banks( int bank0, int bank1, int bank2, int bank3 )
149 {
150 	uint8_t* nt_ram = impl->nt_ram;
151 	nt_banks [0] = &nt_ram [bank0 * 0x400];
152 	nt_banks [1] = &nt_ram [bank1 * 0x400];
153 	nt_banks [2] = &nt_ram [bank2 * 0x400];
154 	nt_banks [3] = &nt_ram [bank3 * 0x400];
155 }
156 
map_palette(int addr)157 inline int Nes_Ppu_Impl::map_palette( int addr )
158 {
159 	if ( (addr & 3) == 0 )
160 		addr &= 0x0f; // 0x10, 0x14, 0x18, 0x1c map to 0x00, 0x04, 0x08, 0x0c
161 	return addr & 0x1f;
162 }
163 
sprite_tile_index(uint8_t const * sprite)164 inline int Nes_Ppu_Impl::sprite_tile_index( uint8_t const* sprite ) const
165 {
166 	int tile = sprite [1] + (w2000 << 5 & 0x100);
167 	if ( w2000 & 0x20 )
168 		tile = (tile & 1) * 0x100 + (tile & 0xfe);
169 	return tile;
170 }
171 
write_2007(int data)172 inline int Nes_Ppu_Impl::write_2007( int data )
173 {
174 	int addr = vram_addr;
175 	uint8_t * chr_ram = this->chr_ram; // pre-read
176 	int changed = addr + addr_inc;
177 	unsigned const divisor = bytes_per_tile * 8;
178 	int mod_index = (unsigned) addr / divisor % (0x4000 / divisor);
179 	vram_addr = changed;
180 	changed ^= addr;
181 	addr &= 0x3fff;
182 
183 	// use index into modified_tiles [] since it's calculated sooner than addr is masked
184 	if ( (unsigned) mod_index < 0x2000 / divisor )
185 	{
186 		// Avoid overhead of checking for read-only CHR; if that is the case,
187 		// this modification will be ignored.
188 		int mod = modified_tiles [mod_index];
189 		chr_ram [addr] = data;
190 		any_tiles_modified = true;
191 		modified_tiles [mod_index] = mod | (1 << ((unsigned) addr / bytes_per_tile % 8));
192 	}
193 	else if ( addr < 0x3f00 )
194 	{
195 		get_nametable( addr ) [addr & 0x3ff] = data;
196 	}
197 	else
198 	{
199 		data &= 0x3f;
200 		uint8_t& entry = palette [map_palette( addr )];
201 		int changed = entry ^ data;
202 		entry = data;
203 		if ( changed )
204 			palette_changed = 0x18;
205 	}
206 
207 	return changed;
208 }
209 
begin_frame()210 inline void Nes_Ppu_Impl::begin_frame()
211 {
212 	palette_changed = 0x18;
213 	palette_size = 0;
214 	palette_offset = palette_begin * 0x01010101;
215 	addr_inc = w2000 & 4 ? 32 : 1;
216 }
217 
218 #endif
219