1 // license:BSD-3-Clause
2 // copyright-holders:Ernesto Corvi, Brad Oliver, Fabio Priuli
3 /******************************************************************************
4 
5     Nintendo 2C0x PPU emulation.
6 
7     Written by Ernesto Corvi.
8     This code is heavily based on Brad Oliver's MESS implementation.
9 
10     2009-04: Changed NES PPU to be a device (Nathan Woods)
11     2009-07: Changed NES PPU to use a device memory map (Robert Bohms)
12 
13     Current known bugs
14 
15     General:
16 
17     * PPU timing is imprecise for updates that happen mid-scanline. Some games
18       may demand more precision.
19 
20     NES-specific:
21 
22     * Micro Machines has minor rendering glitches (needs better timing).
23     * Mach Rider has minor road rendering glitches (needs better timing).
24     * Rad Racer demonstrates road glitches: it changes horizontal scrolling mid-line.
25 
26 ******************************************************************************/
27 
28 #include "emu.h"
29 #include "video/ppu2c0x.h"
30 
31 #include "screen.h"
32 
33 //**************************************************************************
34 //  GLOBAL VARIABLES
35 //**************************************************************************
36 
37 // devices
38 DEFINE_DEVICE_TYPE(PPU_2C02,    ppu2c02_device,       "ppu2c02",    "2C02 PPU")
39 DEFINE_DEVICE_TYPE(PPU_2C03B,   ppu2c03b_device,      "ppu2c03b",   "2C03B PPC")
40 DEFINE_DEVICE_TYPE(PPU_2C04,    ppu2c04_device,       "ppu2c04",    "2C04 PPU")
41 DEFINE_DEVICE_TYPE(PPU_2C07,    ppu2c07_device,       "ppu2c07",    "2C07 PPU")
42 DEFINE_DEVICE_TYPE(PPU_PALC,    ppupalc_device,       "ppupalc",    "Generic PAL Clone PPU")
43 DEFINE_DEVICE_TYPE(PPU_2C05_01, ppu2c05_01_device,    "ppu2c05_01", "2C05_01 PPU")
44 DEFINE_DEVICE_TYPE(PPU_2C05_02, ppu2c05_02_device,    "ppu2c05_02", "2C05_02 PPU")
45 DEFINE_DEVICE_TYPE(PPU_2C05_03, ppu2c05_03_device,    "ppu2c05_03", "2C05_03 PPU")
46 DEFINE_DEVICE_TYPE(PPU_2C05_04, ppu2c05_04_device,    "ppu2c05_04", "2C05_04 PPU")
47 DEFINE_DEVICE_TYPE(PPU_2C04C,   ppu2c04_clone_device, "ppu2c04c",   "2C04 Clone PPU")
48 
49 
50 // default address map
ppu2c0x(address_map & map)51 void ppu2c0x_device::ppu2c0x(address_map& map)
52 {
53 	if (!has_configured_map(0))
54 	{
55 		map(0x0000, 0x3eff).ram();
56 		map(0x3f00, 0x3fff).rw(FUNC(ppu2c0x_device::palette_read), FUNC(ppu2c0x_device::palette_write));
57 		//map(0x0000, 0x3fff).ram();
58 	}
59 }
60 
61 //-------------------------------------------------
62 //  memory_space_config - return a description of
63 //  any address spaces owned by this device
64 //-------------------------------------------------
65 
memory_space_config() const66 device_memory_interface::space_config_vector ppu2c0x_device::memory_space_config() const
67 {
68 	return space_config_vector
69 	{
70 		std::make_pair(0, &m_space_config)
71 	};
72 }
73 
74 
75 //-------------------------------------------------
76 //  ppu2c0x_device - constructor
77 //-------------------------------------------------
78 
device_config_complete()79 void ppu2c0x_device::device_config_complete()
80 {
81 	/* reset the callbacks */
82 	m_scanline_callback_proc.set(nullptr);
83 	m_hblank_callback_proc.set(nullptr);
84 	m_vidaccess_callback_proc.set(nullptr);
85 	m_latch.set(nullptr);
86 }
87 
ppu2c0x_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock,address_map_constructor internal_map)88 ppu2c0x_device::ppu2c0x_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock, address_map_constructor internal_map) :
89 	device_t(mconfig, type, tag, owner, clock),
90 	device_memory_interface(mconfig, *this),
91 	device_video_interface(mconfig, *this),
92 	m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, internal_map),
93 	m_cpu(*this, finder_base::DUMMY_TAG),
94 	m_scanline(0),  // reset the scanline count
95 	m_videoram_addr_mask(0x3fff),
96 	m_global_refresh_mask(0x7fff),
97 	m_line_write_increment_large(32),
98 	m_paletteram_in_ppuspace(false),
99 	m_tile_page(0),
100 	m_back_color(0),
101 	m_refresh_data(0),
102 	m_x_fine(0),
103 	m_toggle(0),
104 	m_tilecount(0),
105 	m_latch(*this),
106 	m_scanline_callback_proc(*this),
107 	m_hblank_callback_proc(*this),
108 	m_vidaccess_callback_proc(*this),
109 	m_int_callback(*this),
110 	m_refresh_latch(0),
111 	m_add(1),
112 	m_videomem_addr(0),
113 	m_data_latch(0),
114 	m_buffered_data(0),
115 	m_sprite_page(0),
116 	m_scan_scale(1), // set the scan scale (this is for dual monitor vertical setups)
117 	m_draw_phase(0),
118 	m_use_sprite_write_limitation(true)
119 {
120 	for (auto& elem : m_regs)
121 		elem = 0;
122 
123 	m_scanlines_per_frame = NTSC_SCANLINES_PER_FRAME;
124 	m_vblank_first_scanline = VBLANK_FIRST_SCANLINE;
125 
126 	/* usually, no security value... */
127 	m_security_value = 0;
128 }
129 
ppu2c0x_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)130 ppu2c0x_device::ppu2c0x_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock) :
131 	ppu2c0x_device(mconfig, type, tag, owner, clock, address_map_constructor(FUNC(ppu2c0x_device::ppu2c0x), this))
132 {
133 	m_paletteram_in_ppuspace = true;
134 }
135 
ppu2c0x_rgb_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)136 ppu2c0x_rgb_device::ppu2c0x_rgb_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock) :
137 	ppu2c0x_device(mconfig, type, tag, owner, clock),
138 	m_palette_data(*this, "palette", 0xc0)
139 {
140 }
141 
142 // NTSC NES
ppu2c02_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)143 ppu2c02_device::ppu2c02_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
144 	ppu2c0x_device(mconfig, PPU_2C02, tag, owner, clock)
145 {
146 }
147 
148 // Playchoice 10
ppu2c03b_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)149 ppu2c03b_device::ppu2c03b_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
150 	ppu2c0x_rgb_device(mconfig, PPU_2C03B, tag, owner, clock)
151 {
152 }
153 
154 // Vs. Unisystem
ppu2c04_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)155 ppu2c04_device::ppu2c04_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
156 	ppu2c0x_rgb_device(mconfig, PPU_2C04, tag, owner, clock)
157 {
158 }
159 
160 // PAL NES
ppu2c07_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)161 ppu2c07_device::ppu2c07_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
162 	ppu2c0x_device(mconfig, PPU_2C07, tag, owner, clock)
163 {
164 	m_scanlines_per_frame = PAL_SCANLINES_PER_FRAME;
165 }
166 
167 // PAL clones
ppupalc_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)168 ppupalc_device::ppupalc_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
169 	ppu2c0x_device(mconfig, PPU_PALC, tag, owner, clock)
170 {
171 	m_scanlines_per_frame = PAL_SCANLINES_PER_FRAME;
172 	m_vblank_first_scanline = VBLANK_FIRST_SCANLINE_PALC;
173 }
174 
175 // The PPU_2C05 variants have different protection value, set at device start, but otherwise are all the same...
176 // Vs. Unisystem (Ninja Jajamaru Kun)
ppu2c05_01_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)177 ppu2c05_01_device::ppu2c05_01_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
178 	ppu2c0x_rgb_device(mconfig, PPU_2C05_01, tag, owner, clock)
179 {
180 	m_security_value = 0x1b;    // game (jajamaru) doesn't seem to ever actually check it
181 }
182 // Vs. Unisystem (Mighty Bomb Jack)
ppu2c05_02_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)183 ppu2c05_02_device::ppu2c05_02_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
184 	ppu2c0x_rgb_device(mconfig, PPU_2C05_02, tag, owner, clock)
185 {
186 	m_security_value = 0x3d;
187 }
188 // Vs. Unisystem (Gumshoe)
ppu2c05_03_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)189 ppu2c05_03_device::ppu2c05_03_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
190 	ppu2c0x_rgb_device(mconfig, PPU_2C05_03, tag, owner, clock)
191 {
192 	m_security_value = 0x1c;
193 }
194 // Vs. Unisystem (Top Gun)
ppu2c05_04_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)195 ppu2c05_04_device::ppu2c05_04_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
196 	ppu2c0x_rgb_device(mconfig, PPU_2C05_04, tag, owner, clock)
197 {
198 	m_security_value = 0x1b;
199 }
200 
201 // Vs. Unisystem (Super Mario Bros. bootlegs)
ppu2c04_clone_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)202 ppu2c04_clone_device::ppu2c04_clone_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
203 	ppu2c0x_device(mconfig, PPU_2C04C, tag, owner, clock),
204 	m_palette_data(*this, "palette", 0x100)
205 {
206 	m_scanlines_per_frame = VS_CLONE_SCANLINES_PER_FRAME;
207 	m_vblank_first_scanline = VBLANK_FIRST_SCANLINE_VS_CLONE;
208 
209 	// background and sprites are always enabled; monochrome and color emphasis aren't supported
210 	m_regs[PPU_CONTROL1] = ~(PPU_CONTROL1_COLOR_EMPHASIS | PPU_CONTROL1_DISPLAY_MONO);
211 }
212 
213 //-------------------------------------------------
214 //  device_start - device-specific startup
215 //-------------------------------------------------
216 
start_nopalram()217 void ppu2c0x_device::start_nopalram()
218 {
219 	// bind our handler
220 	m_int_callback.resolve_safe();
221 
222 	// allocate timers
223 	m_hblank_timer = timer_alloc(TIMER_HBLANK);
224 	m_nmi_timer = timer_alloc(TIMER_NMI);
225 	m_scanline_timer = timer_alloc(TIMER_SCANLINE);
226 
227 	/* initialize the scanline handling portion */
228 	m_scanline_timer->adjust(screen().time_until_pos(1));
229 	m_hblank_timer->adjust(m_cpu->cycles_to_attotime(260) / 3); // ??? FIXME - hardcoding NTSC, need better calculation
230 	m_nmi_timer->adjust(attotime::never);
231 
232 	/* allocate a screen bitmap, videomem and spriteram, a dirtychar array and the monochromatic colortable */
233 	m_bitmap = std::make_unique<bitmap_rgb32>(VISIBLE_SCREEN_WIDTH, VISIBLE_SCREEN_HEIGHT);
234 	m_spriteram = make_unique_clear<uint8_t[]>(SPRITERAM_SIZE);
235 
236 	init_palette_tables();
237 
238 	// register for state saving
239 	save_item(NAME(m_scanline));
240 	save_item(NAME(m_refresh_data));
241 	save_item(NAME(m_refresh_latch));
242 	save_item(NAME(m_x_fine));
243 	save_item(NAME(m_toggle));
244 	save_item(NAME(m_add));
245 	save_item(NAME(m_videomem_addr));
246 	save_item(NAME(m_data_latch));
247 	save_item(NAME(m_buffered_data));
248 	save_item(NAME(m_tile_page));
249 	save_item(NAME(m_sprite_page));
250 	save_item(NAME(m_back_color));
251 	save_item(NAME(m_scan_scale));
252 	save_item(NAME(m_scanlines_per_frame));
253 	save_item(NAME(m_vblank_first_scanline));
254 	save_item(NAME(m_regs));
255 	save_item(NAME(m_draw_phase));
256 	save_item(NAME(m_tilecount));
257 	save_pointer(NAME(m_spriteram), SPRITERAM_SIZE);
258 
259 	save_item(NAME(*m_bitmap));
260 }
261 
device_start()262 void ppu2c0x_device::device_start()
263 {
264 	start_nopalram();
265 
266 	m_palette_ram.resize(0x20);
267 
268 	for (int i = 0; i < 0x20; i++)
269 		m_palette_ram[i] = 0x00;
270 
271 	save_item(NAME(m_palette_ram));
272 }
273 
device_start()274 void ppu2c04_clone_device::device_start()
275 {
276 	ppu2c0x_device::device_start();
277 
278 	/* this PPU clone draws sprites into a frame buffer before displaying them,
279 	causing sprite rendering to be one frame behind tile/background rendering
280 	(mainly noticeable during scrolling)
281 	to simulate that, we can just have a secondary OAM buffer and swap them
282 	at the end of each frame.
283 
284 	(theoretically this can cause the wrong sprite tiles to be drawn for
285 	one frame after changing CHR banks, but the Vs. SMB bootlegs that use
286 	this clone hardware don't actually have CHR bank switching anyway.
287 	also generally affects PPU-side read timings involving the OAM, but
288 	this still doesn't seem to matter for Vs. SMB specifically)
289 	*/
290 	m_spritebuf = make_unique_clear<uint8_t[]>(SPRITERAM_SIZE);
291 	save_pointer(NAME(m_spritebuf), SPRITERAM_SIZE);
292 }
293 
294 //**************************************************************************
295 //  INLINE HELPERS
296 //**************************************************************************
297 
298 //-------------------------------------------------
299 //  readbyte - read a byte at the given address
300 //-------------------------------------------------
301 
readbyte(offs_t address)302 uint8_t ppu2c0x_device::readbyte(offs_t address)
303 {
304 	return space().read_byte(address);
305 }
306 
307 
308 //-------------------------------------------------
309 //  writebyte - write a byte at the given address
310 //-------------------------------------------------
311 
writebyte(offs_t address,uint8_t data)312 inline void ppu2c0x_device::writebyte(offs_t address, uint8_t data)
313 {
314 	space().write_byte(address, data);
315 }
316 
317 
318 /***************************************************************************
319     IMPLEMENTATION
320 ***************************************************************************/
321 
322 /*************************************
323  *
324  *  PPU Palette Initialization
325  *
326  *************************************/
327 
apply_color_emphasis_and_clamp(bool is_pal_or_dendy,int color_emphasis,double & R,double & G,double & B)328 void ppu2c0x_device::apply_color_emphasis_and_clamp(bool is_pal_or_dendy, int color_emphasis, double& R, double& G, double& B)
329 {
330 	if (is_pal_or_dendy) // PAL machines swap the colour emphasis bits, this means the red/blue highlighting on rampart tally bar doesn't look as good
331 	{
332 		color_emphasis = bitswap<3>(color_emphasis, 2, 0, 1);
333 	}
334 
335 	double r_mod = 0.0;
336 	double g_mod = 0.0;
337 	double b_mod = 0.0;
338 
339 	switch (color_emphasis)
340 	{
341 	case 0: r_mod = 1.0;  g_mod = 1.0;  b_mod = 1.0;  break;
342 	case 1: r_mod = 1.24; g_mod = .915; b_mod = .743; break;
343 	case 2: r_mod = .794; g_mod = 1.09; b_mod = .882; break;
344 	case 3: r_mod = .905; g_mod = 1.03; b_mod = 1.28; break;
345 	case 4: r_mod = .741; g_mod = .987; b_mod = 1.0;  break;
346 	case 5: r_mod = 1.02; g_mod = .908; b_mod = .979; break;
347 	case 6: r_mod = 1.02; g_mod = .98;  b_mod = .653; break;
348 	case 7: r_mod = .75;  g_mod = .75;  b_mod = .75;  break;
349 	}
350 
351 	R = R * r_mod;
352 	G = G * g_mod;
353 	B = B * b_mod;
354 
355 	/* Clipping, in case of saturation */
356 	if (R < 0)
357 		R = 0;
358 	if (R > 255)
359 		R = 255;
360 	if (G < 0)
361 		G = 0;
362 	if (G > 255)
363 		G = 255;
364 	if (B < 0)
365 		B = 0;
366 	if (B > 255)
367 		B = 255;
368 }
369 
nespal_to_RGB(int color_intensity,int color_num,int color_emphasis,bool is_pal_or_dendy)370 rgb_t ppu2c0x_device::nespal_to_RGB(int color_intensity, int color_num, int color_emphasis, bool is_pal_or_dendy)
371 {
372 	const double tint = 0.22; /* adjust to taste */
373 	const double hue = 287.0;
374 
375 	const double Kr = 0.2989;
376 	const double Kb = 0.1145;
377 	const double Ku = 2.029;
378 	const double Kv = 1.140;
379 
380 	static const double brightness[3][4] =
381 	{
382 		{ 0.50, 0.75, 1.0, 1.0 },
383 		{ 0.29, 0.45, 0.73, 0.9 },
384 		{ 0, 0.24, 0.47, 0.77 }
385 	};
386 
387 	double sat;
388 	double y, u, v;
389 	double rad;
390 
391 	switch (color_num)
392 	{
393 	case 0:
394 		sat = 0; rad = 0;
395 		y = brightness[0][color_intensity];
396 		break;
397 
398 	case 13:
399 		sat = 0; rad = 0;
400 		y = brightness[2][color_intensity];
401 		break;
402 
403 	case 14:
404 	case 15:
405 		sat = 0; rad = 0; y = 0;
406 		break;
407 
408 	default:
409 		sat = tint;
410 		rad = M_PI * ((color_num * 30 + hue) / 180.0);
411 		y = brightness[1][color_intensity];
412 		break;
413 	}
414 
415 	u = sat * cos(rad);
416 	v = sat * sin(rad);
417 
418 	/* Transform to RGB */
419 	double R = (y + Kv * v) * 255.0;
420 	double G = (y - (Kb * Ku * u + Kr * Kv * v) / (1 - Kb - Kr)) * 255.0;
421 	double B = (y + Ku * u) * 255.0;
422 
423 	apply_color_emphasis_and_clamp(is_pal_or_dendy, color_emphasis, R, G, B);
424 
425 	return rgb_t(floor(R + .5), floor(G + .5), floor(B + .5));
426 }
427 
init_palette_tables()428 void ppu2c0x_device::init_palette_tables()
429 {
430 	bool is_pal = m_scanlines_per_frame != NTSC_SCANLINES_PER_FRAME;
431 
432 	/* This routine builds a palette using a transformation from */
433 	/* the YUV (Y, B-Y, R-Y) to the RGB color space */
434 
435 	/* The NES has a 64 color palette                        */
436 	/* 16 colors, with 4 luminance levels for each color     */
437 	/* The 16 colors circle around the YUV color space,      */
438 
439 	int entry = 0;
440 
441 	/* Loop through the emphasis modes (8 total) */
442 	for (int color_emphasis = 0; color_emphasis < 8; color_emphasis++)
443 	{
444 		/* loop through the 4 intensities */
445 		for (int color_intensity = 0; color_intensity < 4; color_intensity++)
446 		{
447 			/* loop through the 16 colors */
448 			for (int color_num = 0; color_num < 16; color_num++)
449 			{
450 				rgb_t col = nespal_to_RGB(color_intensity, color_num, color_emphasis, is_pal);
451 
452 				m_nespens[entry] = (uint32_t)col;
453 				entry++;
454 
455 			}
456 		}
457 	}
458 }
459 
init_palette_tables()460 void ppu2c0x_rgb_device::init_palette_tables()
461 {
462 	/* Loop through the emphasis modes (8 total) */
463 	int entry = 0;
464 	for (int color_emphasis = 0; color_emphasis < 8; color_emphasis++)
465 	{
466 		for (int color_num = 0; color_num < 64; color_num++)
467 		{
468 			int R = ((color_emphasis & 1) ? 7 : m_palette_data[color_num * 3]);
469 			int G = ((color_emphasis & 2) ? 7 : m_palette_data[color_num * 3 + 1]);
470 			int B = ((color_emphasis & 4) ? 7 : m_palette_data[color_num * 3 + 2]);
471 
472 			m_nespens[entry] = (pal3bit(R)<<16) | (pal3bit(G)<<8) | pal3bit(B);
473 
474 			//set_pen_color(entry++, pal3bit(R), pal3bit(G), pal3bit(B));
475 			entry++;
476 		}
477 	}
478 }
479 
init_palette_tables()480 void ppu2c04_clone_device::init_palette_tables()
481 {
482 	/* clone HW doesn't use color emphasis bits.
483 	   however, it does have two separate palettes: colors 0-63 for background, and 64-127 for sprites
484 	   (although the tile and sprite colors are identical in the Vs. SMB bootleg ROMs)
485 	*/
486 	for (int color_num = 0; color_num < 64*2; color_num++)
487 	{
488 		/* A7 line on palette ROMs is always high, color bits are in reverse order */
489 		u8 color = m_palette_data[color_num | 0x80];
490 		int R = bitswap<3>(color, 0, 1, 2);
491 		int G = bitswap<3>(color, 3, 4, 5);
492 		int B = bitswap<2>(color, 6, 7);
493 
494 		m_nespens[color_num] = (pal3bit(R) << 16) | (pal3bit(G) << 8) | pal2bit(B);
495 	}
496 }
497 
498 /*************************************
499  *
500  *  PPU Initialization and Disposal
501  *
502  *************************************/
503 
504 //-------------------------------------------------
505 //  device_timer - handle timer events
506 //-------------------------------------------------
507 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)508 void ppu2c0x_device::device_timer(emu_timer& timer, device_timer_id id, int param, void* ptr)
509 {
510 	int blanked, vblank;
511 
512 	switch (id)
513 	{
514 	case TIMER_HBLANK:
515 		blanked = (m_regs[PPU_CONTROL1] & (PPU_CONTROL1_BACKGROUND | PPU_CONTROL1_SPRITES)) == 0;
516 		vblank = ((m_scanline >= m_vblank_first_scanline - 1) && (m_scanline < m_scanlines_per_frame - 1)) ? 1 : 0;
517 
518 		//update_scanline();
519 
520 		if (!m_hblank_callback_proc.isnull())
521 			m_hblank_callback_proc(m_scanline, vblank, blanked);
522 
523 		m_hblank_timer->adjust(attotime::never);
524 		break;
525 
526 	case TIMER_NMI:
527 		// Actually fire the VMI
528 		m_int_callback(ASSERT_LINE);
529 		m_int_callback(CLEAR_LINE);
530 
531 		m_nmi_timer->adjust(attotime::never);
532 		break;
533 
534 	case TIMER_SCANLINE:
535 		blanked = (m_regs[PPU_CONTROL1] & (PPU_CONTROL1_BACKGROUND | PPU_CONTROL1_SPRITES)) == 0;
536 		vblank = ((m_scanline >= m_vblank_first_scanline - 1) && (m_scanline < m_scanlines_per_frame - 1)) ? 1 : 0;
537 		int next_scanline;
538 
539 		/* if a callback is available, call it */
540 		if (!m_scanline_callback_proc.isnull())
541 			m_scanline_callback_proc(m_scanline, vblank, blanked);
542 
543 		/* update the scanline that just went by */
544 		update_scanline();
545 
546 		/* increment our scanline count */
547 		m_scanline++;
548 
549 		//logerror("starting scanline %d (MAME %d, beam %d)\n", m_scanline, device->screen().vpos(), device->screen().hpos());
550 
551 		/* Note: this is called at the _end_ of each scanline */
552 		if (m_scanline == m_vblank_first_scanline)
553 		{
554 			// logerror("vblank starting\n");
555 			/* We just entered VBLANK */
556 			m_regs[PPU_STATUS] |= PPU_STATUS_VBLANK;
557 
558 			/* If NMI's are set to be triggered, go for it */
559 			if (m_regs[PPU_CONTROL0] & PPU_CONTROL0_NMI)
560 			{
561 				// We need an ever-so-slight delay between entering vblank and firing an NMI - enough so that
562 				// a game can read the high bit of $2002 before the NMI is called (potentially resetting the bit
563 				// via a read from $2002 in the NMI handler).
564 				// B-Wings is an example game that needs this.
565 				m_nmi_timer->adjust(m_cpu->cycles_to_attotime(4));
566 			}
567 		}
568 
569 		if (m_scanline == m_scanlines_per_frame - 1)
570 		{
571 			//logerror("vblank ending\n");
572 			/* clear the vblank & sprite hit flag */
573 			m_regs[PPU_STATUS] &= ~(PPU_STATUS_VBLANK | PPU_STATUS_SPRITE0_HIT | PPU_STATUS_8SPRITES);
574 		}
575 
576 		/* see if we rolled */
577 		else if (m_scanline == m_scanlines_per_frame)
578 		{
579 			/* if background or sprites are enabled, copy the ppu address latch */
580 			if (!blanked)
581 				m_refresh_data = m_refresh_latch;
582 
583 			/* reset the scanline count */
584 			m_scanline = 0;
585 			//logerror("sprite 0 x: %d y: %d num: %d\n", m_spriteram[3], m_spriteram[0] + 1, m_spriteram[1]);
586 		}
587 
588 		next_scanline = m_scanline + 1;
589 		if (next_scanline == m_scanlines_per_frame)
590 			next_scanline = 0;
591 
592 		// Call us back when the hblank starts for this scanline
593 		m_hblank_timer->adjust(m_cpu->cycles_to_attotime(260) / 3); // ??? FIXME - hardcoding NTSC, need better calculation
594 
595 		// trigger again at the start of the next scanline
596 		m_scanline_timer->adjust(screen().time_until_pos(next_scanline * m_scan_scale));
597 		break;
598 	}
599 }
600 
601 
read_tile_plane_data(int address,int color)602 void ppu2c0x_device::read_tile_plane_data(int address, int color)
603 {
604 	m_planebuf[0] = readbyte((address & 0x1fff));
605 	m_planebuf[1] = readbyte((address + 8) & 0x1fff);
606 }
607 
shift_tile_plane_data(uint8_t & pix)608 void ppu2c0x_device::shift_tile_plane_data(uint8_t& pix)
609 {
610 	pix = ((m_planebuf[0] >> 7) & 1) | (((m_planebuf[1] >> 7) & 1) << 1);
611 	m_planebuf[0] = m_planebuf[0] << 1;
612 	m_planebuf[1] = m_planebuf[1] << 1;
613 }
614 
draw_tile_pixel(uint8_t pix,int color,uint32_t back_pen,uint32_t * & dest)615 void ppu2c0x_device::draw_tile_pixel(uint8_t pix, int color, uint32_t back_pen, uint32_t*& dest)
616 {
617 	uint32_t usepen;
618 
619 	if (pix)
620 	{
621 		uint8_t pen = ((4 * color) + pix) & 0x1f;
622 		uint16_t palval = m_palette_ram[pen];
623 
624 		if (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO)
625 			palval &= 0x30;
626 
627 		// apply colour emphasis
628 		palval |= ((m_regs[PPU_CONTROL1] & PPU_CONTROL1_COLOR_EMPHASIS) << 1);
629 
630 		usepen = m_nespens[palval];
631 	}
632 	else
633 	{
634 		usepen = m_nespens[back_pen];
635 	}
636 
637 
638 
639 	*dest = usepen;
640 }
641 
draw_tile(uint8_t * line_priority,int color_byte,int color_bits,int address,int start_x,uint32_t back_pen,uint32_t * & dest)642 void ppu2c0x_device::draw_tile(uint8_t* line_priority, int color_byte, int color_bits, int address, int start_x, uint32_t back_pen, uint32_t*& dest)
643 {
644 	int color = (((color_byte >> color_bits) & 0x03));
645 
646 	read_tile_plane_data(address, color);
647 
648 	/* render the pixel */
649 	for (int i = 0; i < 8; i++)
650 	{
651 		uint8_t pix;
652 		shift_tile_plane_data(pix);
653 
654 		if ((start_x + i) >= 0 && (start_x + i) < VISIBLE_SCREEN_WIDTH)
655 		{
656 			draw_tile_pixel(pix, color, back_pen, dest);
657 
658 			// priority marking
659 			if (pix)
660 				line_priority[start_x + i] |= 0x02;
661 		}
662 		dest++;
663 	}
664 }
665 
666 
667 // m_refresh_data is important as it is updated during rendering, and overwritten when you write new scroll values
668 // making raster effects more complex than on other systems
669 // https://retrocomputing.stackexchange.com/questions/1898/how-can-i-create-a-split-scroll-effect-in-an-nes-game
670 
draw_background(uint8_t * line_priority)671 void ppu2c0x_device::draw_background(uint8_t* line_priority)
672 {
673 	bitmap_rgb32& bitmap = *m_bitmap;
674 
675 	/* determine where in the nametable to start drawing from */
676 	/* based on the current scanline and scroll regs */
677 	uint8_t  scroll_x_coarse = m_refresh_data & 0x001f;
678 	uint8_t  scroll_y_coarse = (m_refresh_data & 0x03e0) >> 5;
679 	uint16_t nametable = (m_refresh_data & 0x0c00);
680 	uint8_t  scroll_y_fine = (m_refresh_data & 0x7000) >> 12;
681 
682 	int x = scroll_x_coarse;
683 
684 	/* get the tile index */
685 	int tile_index = (nametable | 0x2000) + scroll_y_coarse * 32;
686 
687 	/* set up dest */
688 	int start_x = (m_x_fine ^ 0x07) - 7;
689 	uint32_t* dest = &bitmap.pix(m_scanline, start_x);
690 
691 	m_tilecount = 0;
692 
693 	/* draw the 32 or 33 tiles that make up a line */
694 	while (m_tilecount < 34)
695 	{
696 		int color_byte;
697 		int color_bits;
698 		int pos;
699 		int index1;
700 		int page, page2, address;
701 
702 		index1 = tile_index + x;
703 
704 		// page2 is the output of the nametable read (this section is the FIRST read per tile!)
705 		page2 = readbyte(index1);
706 
707 		// this is attribute table stuff! (actually read 2 in PPUspeak)!
708 		/* Figure out which byte in the color table to use */
709 		pos = ((index1 & 0x380) >> 4) | ((index1 & 0x1f) >> 2);
710 		page = (index1 & 0x0c00) >> 10;
711 		address = 0x3c0 + pos;
712 		color_byte = readbyte((((page * 0x400) + address) & 0xfff) + 0x2000);
713 
714 		/* figure out which bits in the color table to use */
715 		color_bits = ((index1 & 0x40) >> 4) + (index1 & 0x02);
716 
717 		// 27/12/2002
718 		if (!m_latch.isnull())
719 			m_latch((m_tile_page << 10) | (page2 << 4));
720 
721 		if (start_x < VISIBLE_SCREEN_WIDTH)
722 		{
723 			// need to read 0x0000 or 0x1000 + 16*nametable data
724 			address = ((m_tile_page) ? 0x1000 : 0) + (page2 * 16);
725 			// plus something that accounts for y
726 			address += scroll_y_fine;
727 
728 			draw_tile(line_priority, color_byte, color_bits, address, start_x, m_back_color, dest);
729 
730 			start_x += 8;
731 
732 			/* move to next tile over and toggle the horizontal name table if necessary */
733 			x++;
734 			if (x > 31)
735 			{
736 				x = 0;
737 				tile_index ^= 0x400;
738 			}
739 		}
740 		m_tilecount++;
741 	}
742 
743 	/* if the left 8 pixels for the background are off, blank 'em */
744 	if (!(m_regs[PPU_CONTROL1] & PPU_CONTROL1_BACKGROUND_L8))
745 	{
746 		dest = &bitmap.pix(m_scanline);
747 		for (int i = 0; i < 8; i++)
748 		{
749 			draw_back_pen(dest, m_back_color);
750 			dest++;
751 
752 			line_priority[i] ^= 0x02;
753 		}
754 	}
755 }
756 
draw_background(uint8_t * line_priority)757 void ppu2c04_clone_device::draw_background(uint8_t* line_priority)
758 {
759 	// nametable selection is ignored below the hardwired scroll split position
760 	if (m_scanline < 31)
761 		m_refresh_data &= ~0x0c00;
762 
763 	ppu2c0x_device::draw_background(line_priority);
764 }
765 
draw_back_pen(uint32_t * dst,int back_pen)766 void ppu2c0x_device::draw_back_pen(uint32_t* dst, int back_pen)
767 {
768 	*dst = m_nespens[back_pen];
769 }
770 
draw_background_pen()771 void ppu2c0x_device::draw_background_pen()
772 {
773 	bitmap_rgb32& bitmap = *m_bitmap;
774 
775 	/* setup the color mask and colortable to use */
776 	uint8_t color_mask = (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO) ? 0x30 : 0x3f;
777 
778 	// Fill this scanline with the background pen.
779 	for (int i = 0; i < bitmap.width(); i++)
780 		draw_back_pen(&bitmap.pix(m_scanline, i), m_back_color & color_mask);
781 }
782 
read_sprite_plane_data(int address)783 void ppu2c0x_device::read_sprite_plane_data(int address)
784 {
785 	m_planebuf[0] = readbyte((address + 0) & 0x1fff);
786 	m_planebuf[1] = readbyte((address + 8) & 0x1fff);
787 }
788 
make_sprite_pixel_data(uint8_t & pixel_data,int flipx)789 void ppu2c0x_device::make_sprite_pixel_data(uint8_t& pixel_data, int flipx)
790 {
791 	if (flipx)
792 	{
793 		pixel_data = (m_planebuf[0] & 1) + ((m_planebuf[1] & 1) << 1);
794 		m_planebuf[0] = m_planebuf[0] >> 1;
795 		m_planebuf[1] = m_planebuf[1] >> 1;
796 	}
797 	else
798 	{
799 		pixel_data = ((m_planebuf[0] >> 7) & 1) | (((m_planebuf[1] >> 7) & 1) << 1);
800 		m_planebuf[0] = m_planebuf[0] << 1;
801 		m_planebuf[1] = m_planebuf[1] << 1;
802 	}
803 }
804 
draw_sprite_pixel(int sprite_xpos,int color,int pixel,uint8_t pixel_data,bitmap_rgb32 & bitmap)805 void ppu2c0x_device::draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_rgb32& bitmap)
806 {
807 	uint16_t palval = m_palette_ram[((4 * color) | pixel_data) & 0x1f];
808 
809 	if (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO)
810 		palval &= 0x30;
811 
812 	// apply colour emphasis
813 	palval |= ((m_regs[PPU_CONTROL1] & PPU_CONTROL1_COLOR_EMPHASIS) << 1);
814 
815 	uint32_t pix = m_nespens[palval];
816 	bitmap.pix(m_scanline, sprite_xpos + pixel) = pix;
817 }
818 
draw_sprite_pixel(int sprite_xpos,int color,int pixel,uint8_t pixel_data,bitmap_rgb32 & bitmap)819 void ppu2c04_clone_device::draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_rgb32 &bitmap)
820 {
821 	/* clone PPU clips sprites at the screen edges */
822 	if ((sprite_xpos + pixel < 8) || (sprite_xpos + pixel) >= (VISIBLE_SCREEN_WIDTH - 6))
823 		return;
824 
825 	uint16_t palval = m_palette_ram[((4 * color) | pixel_data) & 0x1f];
826 	uint32_t pix = m_nespens[palval | 0x40];
827 	bitmap.pix(m_scanline, sprite_xpos + pixel) = pix;
828 }
829 
read_extra_sprite_bits(int sprite_index)830 void ppu2c0x_device::read_extra_sprite_bits(int sprite_index)
831 {
832 	// needed for some clones
833 }
834 
is_spritepixel_opaque(int pixel_data,int color)835 bool ppu2c0x_device::is_spritepixel_opaque(int pixel_data, int color)
836 {
837 	if (pixel_data)
838 		return true;
839 	else
840 		return false;
841 }
842 
draw_sprite_pixel_low(bitmap_rgb32 & bitmap,int pixel_data,int pixel,int sprite_xpos,int color,int sprite_index,uint8_t * line_priority)843 void ppu2c0x_device::draw_sprite_pixel_low(bitmap_rgb32& bitmap, int pixel_data, int pixel, int sprite_xpos, int color, int sprite_index, uint8_t* line_priority)
844 {
845 	if (is_spritepixel_opaque(pixel_data, color))
846 	{
847 		/* has the background (or another sprite) already been drawn here? */
848 		if ((sprite_xpos + pixel) < VISIBLE_SCREEN_WIDTH)
849 		{
850 			if (!line_priority[sprite_xpos + pixel])
851 			{
852 				/* no, draw */
853 				draw_sprite_pixel(sprite_xpos, color, pixel, pixel_data, bitmap);
854 			}
855 			/* indicate that a sprite was drawn at this location, even if it's not seen */
856 			line_priority[sprite_xpos + pixel] |= 0x01;
857 		}
858 	}
859 
860 	/* set the "sprite 0 hit" flag if appropriate */
861 	if (sprite_index == 0 && (pixel_data & 0x03) && ((sprite_xpos + pixel) < 255) && (line_priority[sprite_xpos + pixel] & 0x02))
862 		m_regs[PPU_STATUS] |= PPU_STATUS_SPRITE0_HIT;
863 }
864 
draw_sprite_pixel_high(bitmap_rgb32 & bitmap,int pixel_data,int pixel,int sprite_xpos,int color,int sprite_index,uint8_t * line_priority)865 void ppu2c0x_device::draw_sprite_pixel_high(bitmap_rgb32& bitmap, int pixel_data, int pixel, int sprite_xpos, int color, int sprite_index, uint8_t* line_priority)
866 {
867 	if (is_spritepixel_opaque(pixel_data, color))
868 	{
869 		if ((sprite_xpos + pixel) < VISIBLE_SCREEN_WIDTH)
870 		{
871 			/* has another sprite been drawn here? */
872 			if (!(line_priority[sprite_xpos + pixel] & 0x01))
873 			{
874 				/* no, draw */
875 				draw_sprite_pixel(sprite_xpos, color, pixel, pixel_data, bitmap);
876 				line_priority[sprite_xpos + pixel] |= 0x01;
877 			}
878 		}
879 	}
880 
881 	/* set the "sprite 0 hit" flag if appropriate */
882 	if (sprite_index == 0 && (pixel_data & 0x03) && ((sprite_xpos + pixel) < 255) && (line_priority[sprite_xpos + pixel] & 0x02))
883 		m_regs[PPU_STATUS] |= PPU_STATUS_SPRITE0_HIT;
884 }
885 
apply_sprite_pattern_page(int index1,int size)886 int ppu2c0x_device::apply_sprite_pattern_page(int index1, int size)
887 {
888 	if (size == 8)
889 		index1 += ((m_sprite_page == 0) ? 0 : 0x1000);
890 
891 	return index1;
892 }
893 
draw_sprites(uint8_t * line_priority)894 void ppu2c0x_device::draw_sprites(uint8_t* line_priority)
895 {
896 	bitmap_rgb32& bitmap = *m_bitmap;
897 
898 	int sprite_xpos, sprite_ypos, sprite_index;
899 	int tile, index1;
900 	int pri;
901 
902 	int flipx, flipy, color;
903 	int size;
904 	int sprite_count = 0;
905 	int sprite_line;
906 
907 	int first_pixel;
908 	int pixel;
909 
910 	/* determine if the sprites are 8x8 or 8x16 */
911 	size = (m_regs[PPU_CONTROL0] & PPU_CONTROL0_SPRITE_SIZE) ? 16 : 8;
912 
913 	first_pixel = (m_regs[PPU_CONTROL1] & PPU_CONTROL1_SPRITES_L8) ? 0 : 8;
914 
915 	for (sprite_index = 0; sprite_index < SPRITERAM_SIZE; sprite_index += 4)
916 	{
917 		sprite_ypos = m_spriteram[sprite_index] + 1;
918 		sprite_xpos = m_spriteram[sprite_index + 3];
919 
920 		// The sprite collision acts funny on the last pixel of a scanline.
921 		// The various scanline latches update while the last few pixels
922 		// are being drawn. Since we don't do cycle-by-cycle PPU emulation,
923 		// we fudge it a bit here so that sprite 0 collisions are detected
924 		// when, e.g., sprite x is 254, sprite y is 29 and we're rendering
925 		// at the end of scanline 28.
926 		// Battletoads needs this level of precision to be playable.
927 		if ((sprite_index == 0) && (sprite_xpos == 254))
928 		{
929 			sprite_ypos--;
930 			/* set the "sprite 0 hit" flag if appropriate */
931 			if (line_priority[sprite_xpos] & 0x02)
932 				m_regs[PPU_STATUS] |= PPU_STATUS_SPRITE0_HIT;
933 		}
934 
935 		/* if the sprite isn't visible, skip it */
936 		if ((sprite_ypos + size <= m_scanline) || (sprite_ypos > m_scanline))
937 			continue;
938 
939 		tile = m_spriteram[sprite_index + 1];
940 		color = (m_spriteram[sprite_index + 2] & 0x03) + 4;
941 		pri = m_spriteram[sprite_index + 2] & 0x20;
942 		flipx = m_spriteram[sprite_index + 2] & 0x40;
943 		flipy = m_spriteram[sprite_index + 2] & 0x80;
944 		read_extra_sprite_bits(sprite_index);
945 
946 		if (size == 16)
947 		{
948 			/* if it's 8x16 and odd-numbered, draw the other half instead */
949 			if (tile & 0x01)
950 			{
951 				tile &= ~0x01;
952 				tile |= 0x100;
953 			}
954 		}
955 
956 		if (!m_latch.isnull())
957 			m_latch((m_sprite_page << 10) | ((tile & 0xff) << 4));
958 
959 		/* compute the character's line to draw */
960 		sprite_line = m_scanline - sprite_ypos;
961 
962 		if (flipy)
963 			sprite_line = (size - 1) - sprite_line;
964 
965 		if (size == 16 && sprite_line > 7)
966 		{
967 			tile++;
968 			sprite_line -= 8;
969 		}
970 
971 		index1 = tile * 16;
972 
973 		index1 = apply_sprite_pattern_page(index1, size);
974 
975 		read_sprite_plane_data(index1 + sprite_line);
976 
977 		/* if there are more than 8 sprites on this line, set the flag */
978 		if (sprite_count == 8)
979 		{
980 			m_regs[PPU_STATUS] |= PPU_STATUS_8SPRITES;
981 			//logerror ("> 8 sprites, scanline: %d\n", m_scanline);
982 
983 			/* the real NES only draws up to 8 sprites - the rest should be invisible */
984 			break;
985 		}
986 
987 		sprite_count++;
988 
989 		/* abort drawing if sprites aren't rendered */
990 		if (!(m_regs[PPU_CONTROL1] & PPU_CONTROL1_SPRITES))
991 			continue;
992 
993 		if (pri)
994 		{
995 			/* draw the low-priority sprites */
996 			for (pixel = 0; pixel < 8; pixel++)
997 			{
998 				uint8_t pixel_data;
999 				make_sprite_pixel_data(pixel_data, flipx);
1000 
1001 				/* is this pixel non-transparent? */
1002 				if (sprite_xpos + pixel >= first_pixel)
1003 				{
1004 					draw_sprite_pixel_low(bitmap, pixel_data, pixel, sprite_xpos, color, sprite_index, line_priority);
1005 				}
1006 			}
1007 		}
1008 		else
1009 		{
1010 			/* draw the high-priority sprites */
1011 			for (pixel = 0; pixel < 8; pixel++)
1012 			{
1013 				uint8_t pixel_data;
1014 				make_sprite_pixel_data(pixel_data, flipx);
1015 
1016 				/* is this pixel non-transparent? */
1017 				if (sprite_xpos + pixel >= first_pixel)
1018 				{
1019 					draw_sprite_pixel_high(bitmap, pixel_data, pixel, sprite_xpos, color, sprite_index, line_priority);
1020 				}
1021 			}
1022 		}
1023 	}
1024 }
1025 
draw_sprites(uint8_t * line_priority)1026 void ppu2c04_clone_device::draw_sprites(uint8_t *line_priority)
1027 {
1028 	ppu2c0x_device::draw_sprites(line_priority);
1029 
1030 	if (m_scanline == BOTTOM_VISIBLE_SCANLINE)
1031 	{
1032 		/* this frame's sprite buffer is cleared after being displayed
1033 		and the other one that was filled this frame will be displayed next frame */
1034 		m_spriteram.swap(m_spritebuf);
1035 		memset(m_spritebuf.get(), 0, SPRITERAM_SIZE);
1036 	}
1037 }
1038 
1039 /*************************************
1040  *
1041  *  Scanline Rendering and Update
1042  *
1043  *************************************/
1044 
render_scanline()1045 void ppu2c0x_device::render_scanline()
1046 {
1047 	uint8_t line_priority[VISIBLE_SCREEN_WIDTH];
1048 
1049 	/* lets see how long it takes */
1050 	g_profiler.start(PROFILER_USER1);
1051 
1052 	/* clear the line priority for this scanline */
1053 	memset(line_priority, 0, VISIBLE_SCREEN_WIDTH);
1054 
1055 	m_draw_phase = PPU_DRAW_BG;
1056 
1057 	/* see if we need to render the background */
1058 	if (m_regs[PPU_CONTROL1] & PPU_CONTROL1_BACKGROUND)
1059 	{
1060 		draw_background(line_priority);
1061 	}
1062 	else
1063 	{
1064 		draw_background_pen();
1065 	}
1066 
1067 	m_draw_phase = PPU_DRAW_OAM;
1068 
1069 	/* if sprites are on, draw them, but we call always to process them */
1070 	draw_sprites(line_priority);
1071 
1072 	m_draw_phase = PPU_DRAW_BG;
1073 
1074 	/* done updating, whew */
1075 	g_profiler.stop();
1076 }
1077 
scanline_increment_fine_ycounter()1078 void ppu2c0x_device::scanline_increment_fine_ycounter()
1079 {
1080 	/* increment the fine y-scroll */
1081 	m_refresh_data += 0x1000;
1082 
1083 	/* if it's rolled, increment the coarse y-scroll */
1084 	if (m_refresh_data & 0x8000)
1085 	{
1086 		uint16_t tmp;
1087 		tmp = (m_refresh_data & 0x03e0) + 0x20;
1088 		m_refresh_data &= 0x7c1f;
1089 
1090 		/* handle bizarro scrolling rollover at the 30th (not 32nd) vertical tile */
1091 		if (tmp == 0x03c0)
1092 			m_refresh_data ^= 0x0800;
1093 		else
1094 			m_refresh_data |= (tmp & 0x03e0);
1095 
1096 		//logerror("updating refresh_data: %04x\n", m_refresh_data);
1097 	}
1098 }
1099 
update_visible_enabled_scanline()1100 void ppu2c0x_device::update_visible_enabled_scanline()
1101 {
1102 	if (m_scanline_timer->remaining() == attotime::zero)
1103 	{
1104 		/* If background or sprites are enabled, copy the ppu address latch */
1105 		/* Copy only the scroll x-coarse and the x-overflow bit */
1106 		m_refresh_data &= ~0x041f;
1107 		m_refresh_data |= (m_refresh_latch & 0x041f);
1108 	}
1109 
1110 	//logerror("updating refresh_data: %04x (scanline: %d)\n", m_refresh_data, m_scanline);
1111 	render_scanline();
1112 }
1113 
update_visible_disabled_scanline()1114 void ppu2c0x_device::update_visible_disabled_scanline()
1115 {
1116 	bitmap_rgb32& bitmap = *m_bitmap;
1117 	uint32_t back_pen;
1118 
1119 	/* setup the color mask and colortable to use */
1120 	uint8_t color_mask = (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO) ? 0x30 : 0x3f;
1121 
1122 	uint16_t palval = m_back_color & color_mask;
1123 
1124 	back_pen = palval;
1125 
1126 	if (m_paletteram_in_ppuspace)
1127 	{
1128 		/* cache the background pen */
1129 		if (m_videomem_addr >= 0x3f00)
1130 		{
1131 			// If the PPU's VRAM address happens to point into palette ram space while
1132 			// both the sprites and background are disabled, the PPU paints the scanline
1133 			// with the palette entry at the VRAM address instead of the usual background
1134 			// pen. Micro Machines makes use of this feature.
1135 			int pen_num = m_palette_ram[(m_videomem_addr & 0x03) ? (m_videomem_addr & 0x1f) : 0];
1136 
1137 			back_pen = pen_num;
1138 		}
1139 	}
1140 
1141 	// Fill this scanline with the background pen.
1142 	for (int i = 0; i < bitmap.width(); i++)
1143 		draw_back_pen(&bitmap.pix(m_scanline, i), back_pen);
1144 }
1145 
update_visible_scanline()1146 void ppu2c0x_device::update_visible_scanline()
1147 {
1148 	/* Render this scanline if appropriate */
1149 	if (m_regs[PPU_CONTROL1] & (PPU_CONTROL1_BACKGROUND | PPU_CONTROL1_SPRITES))
1150 	{
1151 		update_visible_enabled_scanline();
1152 	}
1153 	else
1154 	{
1155 		update_visible_disabled_scanline();
1156 	}
1157 
1158 	if (m_scanline_timer->remaining() == attotime::zero)
1159 	{
1160 		scanline_increment_fine_ycounter();
1161 	}
1162 }
1163 
update_scanline()1164 void ppu2c0x_device::update_scanline()
1165 {
1166 	if (m_scanline <= BOTTOM_VISIBLE_SCANLINE)
1167 	{
1168 		update_visible_scanline();
1169 	}
1170 }
1171 
1172 /*************************************
1173 *
1174 *   PPU Memory functions
1175 *
1176 *************************************/
1177 
palette_write(offs_t offset,uint8_t data)1178 void ppu2c0x_device::palette_write(offs_t offset, uint8_t data)
1179 {
1180 	// palette RAM is only 6 bits wide
1181 	data &= 0x3f;
1182 
1183 	if (offset & 0x3)
1184 	{
1185 		// regular pens, no mirroring
1186 		m_palette_ram[offset & 0x1f] = data;
1187 	}
1188 	else
1189 	{
1190 		// transparent pens are mirrored!
1191 		if (0 == (offset & 0xf))
1192 		{
1193 			m_back_color = data;
1194 		}
1195 		m_palette_ram[offset & 0xf] = m_palette_ram[(offset & 0xf) + 0x10] = data;
1196 	}
1197 }
1198 
palette_read(offs_t offset)1199 uint8_t ppu2c0x_device::palette_read(offs_t offset)
1200 {
1201 	if (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO)
1202 		return (m_palette_ram[offset & 0x1f] & 0x30);
1203 
1204 	else
1205 		return (m_palette_ram[offset & 0x1f]);
1206 }
1207 
1208 /*************************************
1209  *
1210  *  PPU Registers Read
1211  *
1212  *************************************/
1213 
read(offs_t offset)1214 uint8_t ppu2c0x_device::read(offs_t offset)
1215 {
1216 	if (offset >= PPU_MAX_REG)
1217 	{
1218 		logerror("PPU %s: Attempting to read past the chip: offset %x\n", this->tag(), offset);
1219 		offset &= PPU_MAX_REG - 1;
1220 	}
1221 
1222 	// see which register to read
1223 	switch (offset & 7)
1224 	{
1225 	case PPU_STATUS: /* 2 */
1226 		// The top 3 bits of the status register are the only ones that report data. The
1227 		// remainder contain whatever was last in the PPU data latch, except on the RC2C05 (protection)
1228 		if (m_security_value)
1229 			m_data_latch = (m_regs[PPU_STATUS] & 0xc0) | m_security_value;
1230 		else
1231 			m_data_latch = m_regs[PPU_STATUS] | (m_data_latch & 0x1f);
1232 
1233 		// Reset hi/lo scroll toggle
1234 		m_toggle = 0;
1235 
1236 		// If the vblank bit is set, clear all status bits but the 2 sprite flags
1237 		if (m_data_latch & PPU_STATUS_VBLANK)
1238 			m_regs[PPU_STATUS] &= 0x60;
1239 		break;
1240 
1241 	case PPU_SPRITE_DATA: /* 4 */
1242 		m_data_latch = m_spriteram[m_regs[PPU_SPRITE_ADDRESS]];
1243 		break;
1244 
1245 	case PPU_DATA: /* 7 */
1246 		if (!m_latch.isnull())
1247 			m_latch(m_videomem_addr & 0x3fff);
1248 
1249 		if ((m_videomem_addr >= 0x3f00) && m_paletteram_in_ppuspace)
1250 		{
1251 			m_data_latch = readbyte(m_videomem_addr);
1252 			// buffer the mirrored NT data
1253 			m_buffered_data = readbyte(m_videomem_addr & 0x2fff);
1254 		}
1255 		else
1256 		{
1257 			m_data_latch = m_buffered_data;
1258 			m_buffered_data = readbyte(m_videomem_addr);
1259 		}
1260 
1261 		m_videomem_addr += m_add;
1262 		break;
1263 
1264 	default:
1265 		break;
1266 	}
1267 
1268 	return m_data_latch;
1269 }
1270 
read(offs_t offset)1271 uint8_t ppu2c04_clone_device::read(offs_t offset)
1272 {
1273 	switch (offset & 7)
1274 	{
1275 	case PPU_STATUS: /* 2 */
1276 		// $2002 on this clone only contains the sprite 0 hit flag,
1277 		// and it's hardwired to trigger after a specific scanline, not based on actual sprite positions
1278 		if (m_scanline < 31 || m_scanline >= (m_vblank_first_scanline - 1))
1279 			return ~PPU_STATUS_SPRITE0_HIT;
1280 		return 0xff;
1281 
1282 	case PPU_SPRITE_DATA: /* 4 */
1283 		return m_spritebuf[m_regs[PPU_SPRITE_ADDRESS]];
1284 	}
1285 
1286 	return ppu2c0x_device::read(offset);
1287 }
1288 
1289 /*************************************
1290  *
1291  *  PPU Registers Write
1292  *
1293  *************************************/
1294 
write(offs_t offset,uint8_t data)1295 void ppu2c0x_device::write(offs_t offset, uint8_t data)
1296 {
1297 	if (offset >= PPU_MAX_REG)
1298 	{
1299 		logerror("PPU %s: Attempting to write past the chip: offset %x, data %x\n", this->tag(), offset, data);
1300 		offset &= PPU_MAX_REG - 1;
1301 	}
1302 
1303 #ifdef MAME_DEBUG
1304 	if (m_scanline <= BOTTOM_VISIBLE_SCANLINE)
1305 	{
1306 		logerror("PPU register %d write %02x during non-vblank scanline %d (MAME %d, beam pos: %d)\n", offset, data, m_scanline, screen().vpos(), screen().hpos());
1307 	}
1308 #endif
1309 
1310 	/* on the RC2C05, PPU_CONTROL0 and PPU_CONTROL1 are swapped (protection) */
1311 	if ((m_security_value) && !(offset & 6))
1312 		offset ^= 1;
1313 
1314 	switch (offset & 7)
1315 	{
1316 	case PPU_CONTROL0: /* 0 */
1317 		m_regs[PPU_CONTROL0] = data;
1318 
1319 		/* update the name table number on our refresh latches */
1320 		m_refresh_latch &= 0x73ff;
1321 		m_refresh_latch |= (data & 3) << 10;
1322 
1323 		/* the char ram bank points either 0x0000 or 0x1000 (page 0 or page 4) */
1324 		m_tile_page = (data & PPU_CONTROL0_CHR_SELECT) >> 2;
1325 		m_sprite_page = (data & PPU_CONTROL0_SPR_SELECT) >> 1;
1326 
1327 		m_add = (data & PPU_CONTROL0_INC) ? m_line_write_increment_large : 1;
1328 		//logerror("control0 write: %02x (scanline: %d)\n", data, m_scanline);
1329 		break;
1330 
1331 	case PPU_CONTROL1: /* 1 */
1332 		//logerror("control1 write: %02x (scanline: %d)\n", data, m_scanline);
1333 		m_regs[PPU_CONTROL1] = data;
1334 		break;
1335 
1336 	case PPU_SPRITE_ADDRESS: /* 3 */
1337 		m_regs[PPU_SPRITE_ADDRESS] = data;
1338 		break;
1339 
1340 	case PPU_SPRITE_DATA: /* 4 */
1341 		// If the PPU is currently rendering the screen, 0xff is written instead of the desired data.
1342 		if (m_use_sprite_write_limitation)
1343 			if (m_scanline <= BOTTOM_VISIBLE_SCANLINE)
1344 				data = 0xff;
1345 		m_spriteram[m_regs[PPU_SPRITE_ADDRESS]] = data;
1346 		m_regs[PPU_SPRITE_ADDRESS] = (m_regs[PPU_SPRITE_ADDRESS] + 1) & 0xff;
1347 		break;
1348 
1349 	case PPU_SCROLL: /* 5 */
1350 		if (m_toggle)
1351 		{
1352 			/* second write */
1353 			m_refresh_latch &= 0x0c1f;
1354 			m_refresh_latch |= (data & 0xf8) << 2;
1355 			m_refresh_latch |= (data & 0x07) << 12;
1356 			//logerror("scroll write 2: %d, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline);
1357 		}
1358 		else
1359 		{
1360 			/* first write */
1361 			m_refresh_latch &= (0xffe0 & m_global_refresh_mask);
1362 			m_refresh_latch |= (data & 0xf8) >> 3;
1363 
1364 			m_x_fine = data & 7;
1365 			//logerror("scroll write 1: %d, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline);
1366 		}
1367 
1368 		m_toggle ^= 1;
1369 		break;
1370 
1371 	case PPU_ADDRESS: /* 6 */
1372 		if (m_toggle)
1373 		{
1374 			/* second write */
1375 			m_refresh_latch &= (0xff00 & m_global_refresh_mask);
1376 			m_refresh_latch |= data;
1377 			m_refresh_data = m_refresh_latch;
1378 
1379 			m_videomem_addr = m_refresh_latch;
1380 			//logerror("vram addr write 2: %02x, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline);
1381 		}
1382 		else
1383 		{
1384 			/* first write */
1385 			m_refresh_latch &= 0x00ff;
1386 			m_refresh_latch |= (data & (m_videoram_addr_mask >>8) ) << 8;
1387 			//logerror("vram addr write 1: %02x, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline);
1388 		}
1389 
1390 		m_toggle ^= 1;
1391 		break;
1392 
1393 	case PPU_DATA: /* 7 */
1394 	{
1395 		int tempAddr = m_videomem_addr & m_videoram_addr_mask;
1396 
1397 		if (!m_latch.isnull())
1398 			m_latch(tempAddr);
1399 
1400 		/* if there's a callback, call it now */
1401 		if (!m_vidaccess_callback_proc.isnull())
1402 			data = m_vidaccess_callback_proc(tempAddr, data);
1403 
1404 		/* see if it's on the chargen portion */
1405 		if (tempAddr < 0x2000)
1406 		{
1407 			/* store the data */
1408 			writebyte(tempAddr, data);
1409 		}
1410 		else // this codepath is identical?
1411 		{
1412 			writebyte(tempAddr, data);
1413 		}
1414 		/* increment the address */
1415 		m_videomem_addr += m_add;
1416 	}
1417 	break;
1418 
1419 	default:
1420 		/* ignore other registers writes */
1421 		break;
1422 	}
1423 
1424 	m_data_latch = data;
1425 }
1426 
write(offs_t offset,uint8_t data)1427 void ppu2c04_clone_device::write(offs_t offset, uint8_t data)
1428 {
1429 	switch (offset & 7)
1430 	{
1431 	case PPU_CONTROL0: /* 0 */
1432 		data &= (0x01 | PPU_CONTROL0_INC | PPU_CONTROL0_NMI); /* other bits of $2000 are ignored by this clone */
1433 		data |= PPU_CONTROL0_CHR_SELECT;
1434 		break;
1435 
1436 	case PPU_CONTROL1: /* 1 */
1437 	case PPU_SPRITE_ADDRESS: /* 3 */
1438 		return; /* $2001 and $2003 do nothing on this clone */
1439 
1440 	case PPU_SPRITE_DATA: /* 4 */
1441 		m_spritebuf[m_regs[PPU_SPRITE_ADDRESS]] = data;
1442 		m_regs[PPU_SPRITE_ADDRESS] = (m_regs[PPU_SPRITE_ADDRESS] + 1) & 0xff;
1443 		return;
1444 
1445 	case PPU_SCROLL: /* 5 */
1446 		if (m_toggle)
1447 			data = 0; /* no vertical scroll */
1448 		break;
1449 
1450 	case PPU_ADDRESS: /* 6 */
1451 		/* $2006 doesn't affect scroll latching */
1452 		if (m_toggle)
1453 		{
1454 			/* second write */
1455 			set_vram_dest((get_vram_dest() & 0xff00) | data);
1456 		}
1457 		else
1458 		{
1459 			/* first write */
1460 			set_vram_dest((get_vram_dest() & 0x00ff) | (data << 8));
1461 		}
1462 
1463 		m_toggle ^= 1;
1464 		return;
1465 	}
1466 
1467 	ppu2c0x_device::write(offset, data);
1468 }
1469 
get_vram_dest()1470 uint16_t ppu2c0x_device::get_vram_dest()
1471 {
1472 	return m_videomem_addr;
1473 }
1474 
set_vram_dest(uint16_t dest)1475 void ppu2c0x_device::set_vram_dest(uint16_t dest)
1476 {
1477 	m_videomem_addr = dest;
1478 }
1479 
1480 /*************************************
1481  *
1482  *  Sprite DMA
1483  *
1484  *************************************/
1485 
spriteram_dma(address_space & space,const uint8_t page)1486 void ppu2c0x_device::spriteram_dma(address_space& space, const uint8_t page)
1487 {
1488 	int i;
1489 	int address = page << 8;
1490 
1491 	for (i = 0; i < SPRITERAM_SIZE; i++)
1492 	{
1493 		uint8_t spriteData = space.read_byte(address + i);
1494 		space.write_byte(0x2004, spriteData);
1495 	}
1496 
1497 	// should last 513 CPU cycles.
1498 	space.device().execute().adjust_icount(-513);
1499 }
1500 
1501 /*************************************
1502  *
1503  *  PPU Rendering
1504  *
1505  *************************************/
1506 
render(bitmap_rgb32 & bitmap,int flipx,int flipy,int sx,int sy,const rectangle & cliprect)1507 void ppu2c0x_device::render(bitmap_rgb32& bitmap, int flipx, int flipy, int sx, int sy, const rectangle& cliprect)
1508 {
1509 	if (m_scanline_timer->remaining() != attotime::zero)
1510 	{
1511 		// Partial line update, need to render first (especially for light gun emulation).
1512 		update_scanline();
1513 	}
1514 	copybitmap(bitmap, *m_bitmap, flipx, flipy, sx, sy, cliprect);
1515 }
1516 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)1517 uint32_t ppu2c0x_device::screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect)
1518 {
1519 	render(bitmap, 0, 0, 0, 0, cliprect);
1520 	return 0;
1521 }
1522