Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/quicknes/nes_emu/Nes_Ppu_Impl.cpp
2 views
1
2
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
3
4
#include "Nes_Ppu_Impl.h"
5
6
#include <string.h>
7
#include "blargg_endian.h"
8
#include "Nes_State.h"
9
#include <stdint.h>
10
11
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
12
can redistribute it and/or modify it under the terms of the GNU Lesser
13
General Public License as published by the Free Software Foundation; either
14
version 2.1 of the License, or (at your option) any later version. This
15
module is distributed in the hope that it will be useful, but WITHOUT ANY
16
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
18
more details. You should have received a copy of the GNU Lesser General
19
Public License along with this module; if not, write to the Free Software
20
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
21
22
#include "blargg_source.h"
23
24
int const cache_line_size = 128; // tile cache is kept aligned to this boundary
25
26
Nes_Ppu_Impl::Nes_Ppu_Impl()
27
{
28
impl = NULL;
29
chr_data = NULL;
30
chr_size = 0;
31
tile_cache = NULL;
32
host_palette = NULL;
33
max_palette_size = 0;
34
tile_cache_mem = NULL;
35
ppu_state_t::unused = 0;
36
37
mmc24_enabled = false;
38
mmc24_latched[0] = 0;
39
mmc24_latched[1] = 0;
40
41
#ifndef NDEBUG
42
// verify that unaligned accesses work
43
static unsigned char b [19] = { 0 };
44
static unsigned char b2 [19] = { 1,2,3,4,0,5,6,7,8,0,9,0,1,2,0,3,4,5,6 };
45
for ( int i = 0; i < 19; i += 5 )
46
*(volatile BOOST::uint32_t*) &b [i] = *(volatile BOOST::uint32_t*) &b2 [i];
47
assert( !memcmp( b, b2, 19 ) );
48
#endif
49
}
50
51
Nes_Ppu_Impl::~Nes_Ppu_Impl()
52
{
53
close_chr();
54
delete impl;
55
}
56
57
int Nes_Ppu_Impl::peekaddr(int addr)
58
{
59
if (addr < 0x2000)
60
return chr_data[map_chr_addr_peek(addr)];
61
else
62
return get_nametable(addr)[addr & 0x3ff];
63
}
64
65
66
67
void Nes_Ppu_Impl::all_tiles_modified()
68
{
69
any_tiles_modified = true;
70
memset( modified_tiles, ~0, sizeof modified_tiles );
71
}
72
73
blargg_err_t Nes_Ppu_Impl::open_chr( byte const* new_chr, long chr_data_size )
74
{
75
close_chr();
76
77
if ( !impl )
78
{
79
impl = BLARGG_NEW impl_t;
80
CHECK_ALLOC( impl );
81
chr_ram = impl->chr_ram;
82
}
83
84
chr_data = new_chr;
85
chr_size = chr_data_size;
86
chr_is_writable = false;
87
88
if ( chr_data_size == 0 )
89
{
90
// CHR RAM
91
chr_data = impl->chr_ram;
92
chr_size = sizeof impl->chr_ram;
93
chr_is_writable = true;
94
}
95
96
// allocate aligned memory for cache
97
assert( chr_size % chr_addr_size == 0 );
98
long tile_count = chr_size / bytes_per_tile;
99
tile_cache_mem = BLARGG_NEW byte [tile_count * sizeof (cached_tile_t) * 2 + cache_line_size];
100
CHECK_ALLOC( tile_cache_mem );
101
tile_cache = (cached_tile_t*) (tile_cache_mem + cache_line_size -
102
(uintptr_t) tile_cache_mem % cache_line_size);
103
flipped_tiles = tile_cache + tile_count;
104
105
// rebuild cache
106
all_tiles_modified();
107
if ( !chr_is_writable )
108
{
109
any_tiles_modified = false;
110
rebuild_chr( 0, chr_size );
111
}
112
113
return 0;
114
}
115
116
void Nes_Ppu_Impl::close_chr()
117
{
118
delete [] tile_cache_mem;
119
tile_cache_mem = NULL;
120
}
121
122
void Nes_Ppu_Impl::set_chr_bank( int addr, int size, long data )
123
{
124
check( !chr_is_writable || addr == data ); // to do: is CHR RAM ever bank-switched?
125
//dprintf( "Tried to set CHR RAM bank at %04X to CHR+%04X\n", addr, data );
126
127
if ( data + size > chr_size )
128
data %= chr_size;
129
130
int count = (unsigned) size / chr_page_size;
131
assert( chr_page_size * count == size );
132
assert( addr + size <= chr_addr_size );
133
134
int page = (unsigned) addr / chr_page_size;
135
while ( count-- )
136
{
137
chr_pages [page] = data - page * chr_page_size;
138
page++;
139
data += chr_page_size;
140
}
141
}
142
143
void Nes_Ppu_Impl::set_chr_bank_ex( int addr, int size, long data )
144
{
145
mmc24_enabled = true;
146
147
check( !chr_is_writable || addr == data ); // to do: is CHR RAM ever bank-switched?
148
//dprintf( "Tried to set CHR RAM bank at %04X to CHR+%04X\n", addr, data );
149
150
if ( data + size > chr_size )
151
data %= chr_size;
152
153
int count = (unsigned) size / chr_page_size;
154
assert( chr_page_size * count == size );
155
assert( addr + size <= chr_addr_size );
156
157
int page = (unsigned) addr / chr_page_size;
158
while ( count-- )
159
{
160
chr_pages_ex [page] = data - page * chr_page_size;
161
page++;
162
data += chr_page_size;
163
}
164
}
165
166
void Nes_Ppu_Impl::save_state( Nes_State_* out ) const
167
{
168
*out->ppu = *this;
169
out->ppu_valid = true;
170
171
memcpy( out->spr_ram, spr_ram, out->spr_ram_size );
172
out->spr_ram_valid = true;
173
174
out->nametable_size = 0x800;
175
memcpy( out->nametable, impl->nt_ram, 0x800 );
176
if ( nt_banks [3] >= &impl->nt_ram [0xC00] )
177
{
178
// save extra nametable data in chr
179
out->nametable_size = 0x1000;
180
memcpy( out->chr, &impl->nt_ram [0x800], 0x800 );
181
}
182
183
out->chr_size = 0;
184
if ( chr_is_writable )
185
{
186
out->chr_size = chr_size;
187
check( out->nametable_size <= 0x800 );
188
assert( out->nametable_size <= 0x800 );
189
assert( out->chr_size <= out->chr_max );
190
memcpy( out->chr, impl->chr_ram, out->chr_size );
191
}
192
}
193
194
void Nes_Ppu_Impl::load_state( Nes_State_ const& in )
195
{
196
set_nt_banks( 0, 0, 0, 0 );
197
set_chr_bank( 0, 0x2000, 0 );
198
199
if ( in.ppu_valid )
200
STATIC_CAST(ppu_state_t&,*this) = *in.ppu;
201
202
if ( in.spr_ram_valid )
203
memcpy( spr_ram, in.spr_ram, sizeof spr_ram );
204
205
assert( in.nametable_size <= (int) sizeof impl->nt_ram );
206
if ( in.nametable_size >= 0x800 )
207
{
208
if ( in.nametable_size > 0x800 )
209
memcpy( &impl->nt_ram [0x800], in.chr, 0x800 );
210
memcpy( impl->nt_ram, in.nametable, 0x800 );
211
}
212
213
if ( chr_is_writable && in.chr_size )
214
{
215
assert( in.chr_size <= (int) sizeof impl->chr_ram );
216
memcpy( impl->chr_ram, in.chr, in.chr_size );
217
all_tiles_modified();
218
}
219
}
220
221
static BOOST::uint8_t const initial_palette [0x20] =
222
{
223
0x0f,0x01,0x00,0x01,0x00,0x02,0x02,0x0D,0x08,0x10,0x08,0x24,0x00,0x00,0x04,0x2C,
224
0x00,0x01,0x34,0x03,0x00,0x04,0x00,0x14,0x00,0x3A,0x00,0x02,0x00,0x20,0x2C,0x08
225
};
226
227
void Nes_Ppu_Impl::reset( bool full_reset )
228
{
229
w2000 = 0;
230
w2001 = 0;
231
r2002 = 0x80;
232
r2007 = 0;
233
open_bus = 0;
234
decay_low = 0;
235
decay_high = 0;
236
second_write = false;
237
vram_temp = 0;
238
pixel_x = 0;
239
240
if ( full_reset )
241
{
242
vram_addr = 0;
243
w2003 = 0;
244
memset( impl->chr_ram, 0xff, sizeof impl->chr_ram );
245
memset( impl->nt_ram, 0xff, sizeof impl->nt_ram );
246
memcpy( palette, initial_palette, sizeof palette );
247
}
248
249
set_nt_banks( 0, 0, 0, 0 );
250
set_chr_bank( 0, chr_addr_size, 0 );
251
memset( spr_ram, 0xff, sizeof spr_ram );
252
all_tiles_modified();
253
if ( max_palette_size > 0 )
254
memset( host_palette, 0, max_palette_size * sizeof *host_palette );
255
}
256
257
void Nes_Ppu_Impl::capture_palette()
258
{
259
if ( palette_size + palette_increment <= max_palette_size )
260
{
261
palette_offset = (palette_begin + palette_size) * 0x01010101;
262
263
short* out = host_palette + palette_size;
264
palette_size += palette_increment;
265
266
int i;
267
268
int emph = w2001 << 1 & 0x1C0;
269
int mono = (w2001 & 1 ? 0x30 : 0x3F);
270
271
for ( i = 0; i < 32; i++ )
272
out [i] = (palette [i] & mono) | emph;
273
274
int bg = out [0];
275
for ( i = 4; i < 32; i += 4 )
276
out [i] = bg;
277
278
memcpy( out + 32, out, 32 * sizeof *out );
279
}
280
}
281
282
void Nes_Ppu_Impl::run_hblank( int count )
283
{
284
require( count >= 0 );
285
286
long addr = (vram_addr & 0x7be0) + (vram_temp & 0x41f) + (count * 0x1000);
287
if ( w2001 & 0x08 )
288
{
289
while ( addr >= 0x8000 )
290
{
291
int y = (addr + 0x20) & 0x3e0;
292
addr = (addr - 0x8000) & ~0x3e0;
293
if ( y == 30 * 0x20 )
294
y = 0x800;
295
addr ^= y;
296
}
297
vram_addr = addr;
298
}
299
}
300
301
#ifdef __MWERKS__
302
#pragma ppc_unroll_factor_limit 1 // messes up calc_sprite_max_scanlines loop
303
static int zero = 0;
304
#else
305
const int zero = 0;
306
#endif
307
308
// Tile cache
309
310
inline unsigned long reorder( unsigned long n )
311
{
312
n |= n << 7;
313
return ((n << 14) | n);
314
}
315
316
inline void Nes_Ppu_Impl::update_tile( int index )
317
{
318
const byte* in = chr_data + (index) * bytes_per_tile;
319
byte* out = (byte*) tile_cache [index];
320
byte* flipped_out = (byte*) flipped_tiles [index];
321
322
unsigned long bit_mask = 0x11111111 + zero;
323
324
for ( int n = 4; n--; )
325
{
326
// Reorder two lines of two-bit pixels. No bits are wasted, so
327
// reordered version is also four bytes.
328
//
329
// 12345678 to A0E4B1F5C2G6D3H7
330
// ABCDEFGH
331
unsigned long c =
332
((reorder( in [0] ) & bit_mask) << 0) |
333
((reorder( in [8] ) & bit_mask) << 1) |
334
((reorder( in [1] ) & bit_mask) << 2) |
335
((reorder( in [9] ) & bit_mask) << 3);
336
in += 2;
337
338
SET_BE32( out, c );
339
out += 4;
340
341
// make horizontally-flipped version
342
c = ((c >> 28) & 0x000f) |
343
((c >> 20) & 0x00f0) |
344
((c >> 12) & 0x0f00) |
345
((c >> 4) & 0xf000) |
346
((c & 0xf000) << 4) |
347
((c & 0x0f00) << 12) |
348
((c & 0x00f0) << 20) |
349
((c & 0x000f) << 28);
350
SET_BE32( flipped_out, c );
351
flipped_out += 4;
352
}
353
}
354
355
void Nes_Ppu_Impl::rebuild_chr( unsigned long begin, unsigned long end )
356
{
357
unsigned end_index = (end + bytes_per_tile - 1) / bytes_per_tile;
358
for ( unsigned index = begin / bytes_per_tile; index < end_index; index++ )
359
update_tile( index );
360
}
361
362
void Nes_Ppu_Impl::update_tiles( int first_tile )
363
{
364
int chunk = 0;
365
do
366
{
367
if ( !(uint32_t&) modified_tiles [chunk] )
368
{
369
chunk += 4;
370
}
371
else
372
{
373
do
374
{
375
int modified = modified_tiles [chunk];
376
if ( modified )
377
{
378
modified_tiles [chunk] = 0;
379
380
int index = first_tile + chunk * 8;
381
do
382
{
383
if ( modified & 1 )
384
update_tile( index );
385
index++;
386
}
387
while ( (modified >>= 1) != 0 );
388
}
389
}
390
while ( ++chunk & 3 );
391
}
392
}
393
while ( chunk < chr_tile_count / 8 );
394
}
395
396
// Sprite max
397
398
template<int height>
399
struct calc_sprite_max_scanlines
400
{
401
static unsigned long func( byte const* sprites, byte* scanlines, int begin )
402
{
403
typedef BOOST::uint32_t uint32_t;
404
405
unsigned long any_hits = 0;
406
unsigned long const offset = 0x01010101 + zero;
407
unsigned limit = 239 + height - begin;
408
for ( int n = 64; n; --n )
409
{
410
int top = *sprites;
411
sprites += 4;
412
byte* p = scanlines + top;
413
if ( (unsigned) (239 - top) < limit )
414
{
415
unsigned long p0 = (uint32_t&) p [0] + offset;
416
unsigned long p4 = (uint32_t&) p [4] + offset;
417
(uint32_t&) p [0] = p0;
418
any_hits |= p0;
419
(uint32_t&) p [4] = p4;
420
any_hits |= p4;
421
if ( height > 8 )
422
{
423
unsigned long p0 = (uint32_t&) p [ 8] + offset;
424
unsigned long p4 = (uint32_t&) p [12] + offset;
425
(uint32_t&) p [ 8] = p0;
426
any_hits |= p0;
427
(uint32_t&) p [12] = p4;
428
any_hits |= p4;
429
}
430
}
431
}
432
433
return any_hits;
434
}
435
};
436
437
long Nes_Ppu_Impl::recalc_sprite_max( int scanline )
438
{
439
int const max_scanline_count = image_height;
440
441
byte sprite_max_scanlines [256 + 16];
442
443
// recalculate sprites per scanline
444
memset( sprite_max_scanlines + scanline, 0x78, last_sprite_max_scanline - scanline );
445
unsigned long any_hits;
446
if ( w2000 & 0x20 )
447
any_hits = calc_sprite_max_scanlines<16>::func( spr_ram, sprite_max_scanlines, scanline );
448
else
449
any_hits = calc_sprite_max_scanlines<8 >::func( spr_ram, sprite_max_scanlines, scanline );
450
451
// cause search to terminate past max_scanline_count if none have 8 or more sprites
452
(uint32_t&) sprite_max_scanlines [max_scanline_count] = 0;
453
sprite_max_scanlines [max_scanline_count + 3] = 0x80;
454
455
// avoid scan if no possible hits
456
if ( !(any_hits & 0x80808080) )
457
return 0;
458
459
// find soonest scanline with 8 or more sprites
460
while ( true )
461
{
462
unsigned long const mask = 0x80808080 + zero;
463
464
// check four at a time
465
byte* pos = &sprite_max_scanlines [scanline];
466
unsigned long n = (uint32_t&) *pos;
467
while ( 1 )
468
{
469
unsigned long x = n & mask;
470
pos += 4;
471
n = (uint32_t&) *pos;
472
if ( x )
473
break;
474
}
475
476
int height = sprite_height();
477
int remain = 8;
478
int i = 0;
479
480
// find which of the four
481
pos -= 3 + (pos [-4] >> 7 & 1);
482
pos += 1 - (*pos >> 7 & 1);
483
pos += 1 - (*pos >> 7 & 1);
484
assert( *pos & 0x80 );
485
486
scanline = pos - sprite_max_scanlines;
487
if ( scanline >= max_scanline_count )
488
break;
489
490
// find time that max sprites flag is set (or that it won't be set)
491
do
492
{
493
int relative = scanline - spr_ram [i];
494
i += 4;
495
if ( (unsigned) relative < (unsigned) height && !--remain )
496
{
497
// now use screwey search for 9th sprite
498
int offset = 0;
499
while ( i < 0x100 )
500
{
501
int relative = scanline - spr_ram [i + offset];
502
//dprintf( "Checking sprite %d [%d]\n", i / 4, offset );
503
i += 4;
504
offset = (offset + 1) & 3;
505
if ( (unsigned) relative < (unsigned) height )
506
{
507
//dprintf( "sprite max on scanline %d\n", scanline );
508
return scanline * scanline_len + (unsigned) i / 2;
509
}
510
}
511
break;
512
}
513
}
514
while ( i < 0x100 );
515
scanline++;
516
}
517
518
return 0;
519
}
520
521
522