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