1 // license:BSD-3-Clause
2 // copyright-holders:Nicola Salmoria
3 /*************************************************************************
4 
5     Namco Pac Man
6 
7 **************************************************************************
8 
9     This file is used by the Pac Man, Pengo & Jr Pac Man drivers.
10 
11     Pengo & Pac Man are almost identical, the only differences being the
12     extra gfx bank in Pengo, and the need to compensate for an hardware
13     sprite positioning "bug" in Pac Man.
14 
15     Jr Pac Man has the same sprite hardware as Pac Man, the extra bank
16     from Pengo and a scrolling playfield at the expense of one color per row
17     for the playfield so it can fit in the same amount of ram.
18 
19 **************************************************************************/
20 
21 #include "emu.h"
22 #include "includes/pacman.h"
23 #include "video/resnet.h"
24 
25 
26 
27 /***************************************************************************
28 
29   Convert the color PROMs into a more useable format.
30 
31 
32   Pac Man has a 32x8 palette PROM and a 256x4 color lookup table PROM.
33 
34   Pengo has a 32x8 palette PROM and a 1024x4 color lookup table PROM.
35 
36   The palette PROM is connected to the RGB output this way:
37 
38   bit 7 -- 220 ohm resistor  -- BLUE
39         -- 470 ohm resistor  -- BLUE
40         -- 220 ohm resistor  -- GREEN
41         -- 470 ohm resistor  -- GREEN
42         -- 1  kohm resistor  -- GREEN
43         -- 220 ohm resistor  -- RED
44         -- 470 ohm resistor  -- RED
45   bit 0 -- 1  kohm resistor  -- RED
46 
47 
48   Jr. Pac Man has two 256x4 palette PROMs (the three msb of the address are
49   grounded, so the effective colors are only 32) and one 256x4 color lookup
50   table PROM.
51 
52   The palette PROMs are connected to the RGB output this way:
53 
54   bit 3 -- 220 ohm resistor  -- BLUE
55         -- 470 ohm resistor  -- BLUE
56         -- 220 ohm resistor  -- GREEN
57   bit 0 -- 470 ohm resistor  -- GREEN
58 
59   bit 3 -- 1  kohm resistor  -- GREEN
60         -- 220 ohm resistor  -- RED
61         -- 470 ohm resistor  -- RED
62   bit 0 -- 1  kohm resistor  -- RED
63 
64 ***************************************************************************/
65 
pacman_palette(palette_device & palette) const66 void pacman_state::pacman_palette(palette_device &palette) const
67 {
68 	const uint8_t *color_prom = memregion("proms")->base();
69 	static constexpr int resistances[3] = { 1000, 470, 220 };
70 
71 	// compute the color output resistor weights
72 	double rweights[3], gweights[3], bweights[2];
73 	compute_resistor_weights(0, 255, -1.0,
74 			3, &resistances[0], rweights, 0, 0,
75 			3, &resistances[0], gweights, 0, 0,
76 			2, &resistances[1], bweights, 0, 0);
77 
78 	// create a lookup table for the palette
79 	for (int i = 0; i < 32; i++)
80 	{
81 		int bit0, bit1, bit2;
82 
83 		// red component
84 		bit0 = BIT(color_prom[i], 0);
85 		bit1 = BIT(color_prom[i], 1);
86 		bit2 = BIT(color_prom[i], 2);
87 		int const r = combine_weights(rweights, bit0, bit1, bit2);
88 
89 		// green component
90 		bit0 = BIT(color_prom[i], 3);
91 		bit1 = BIT(color_prom[i], 4);
92 		bit2 = BIT(color_prom[i], 5);
93 		int const g = combine_weights(gweights, bit0, bit1, bit2);
94 
95 		// blue component
96 		bit0 = BIT(color_prom[i], 6);
97 		bit1 = BIT(color_prom[i], 7);
98 		int const b = combine_weights(bweights, bit0, bit1);
99 
100 		palette.set_indirect_color(i, rgb_t(r, g, b));
101 	}
102 
103 	// color_prom now points to the beginning of the lookup table
104 	color_prom += 32;
105 
106 	// allocate the colortable
107 	for (int i = 0; i < 64*4; i++)
108 	{
109 		uint8_t const ctabentry = color_prom[i] & 0x0f;
110 
111 		// first palette bank
112 		palette.set_pen_indirect(i, ctabentry);
113 
114 		// second palette bank
115 		palette.set_pen_indirect(i + 64*4, 0x10 | ctabentry);
116 	}
117 }
118 
TILEMAP_MAPPER_MEMBER(pacman_state::pacman_scan_rows)119 TILEMAP_MAPPER_MEMBER(pacman_state::pacman_scan_rows)
120 {
121 	int offs;
122 
123 	row += 2;
124 	col -= 2;
125 	if (col & 0x20)
126 		offs = row + ((col & 0x1f) << 5);
127 	else
128 		offs = col + (row << 5);
129 
130 	return offs;
131 }
132 
TILE_GET_INFO_MEMBER(pacman_state::pacman_get_tile_info)133 TILE_GET_INFO_MEMBER(pacman_state::pacman_get_tile_info)
134 {
135 	int code = m_videoram[tile_index] | (m_charbank << 8);
136 	int attr = (m_colorram[tile_index] & 0x1f) | (m_colortablebank << 5) | (m_palettebank << 6 );
137 
138 	tileinfo.set(0,code,attr,0);
139 }
140 
141 /***************************************************************************
142 
143   Start the video hardware emulation.
144 
145 ***************************************************************************/
146 
init_save_state()147 void pacman_state::init_save_state()
148 {
149 	save_item(NAME(m_charbank));
150 	save_item(NAME(m_spritebank));
151 	save_item(NAME(m_palettebank));
152 	save_item(NAME(m_colortablebank));
153 	save_item(NAME(m_flipscreen));
154 	save_item(NAME(m_bgpriority));
155 }
156 
157 
VIDEO_START_MEMBER(pacman_state,pacman)158 VIDEO_START_MEMBER(pacman_state,pacman)
159 {
160 	init_save_state();
161 
162 	m_charbank = 0;
163 	m_spritebank = 0;
164 	m_palettebank = 0;
165 	m_colortablebank = 0;
166 	m_flipscreen = 0;
167 	m_bgpriority = 0;
168 	m_inv_spr = 0;
169 
170 	/* In the Pac Man based games (NOT Pengo) the first two sprites must be offset */
171 	/* one pixel to the left to get a more correct placement */
172 	m_xoffsethack = 1;
173 
174 	m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(pacman_state::pacman_get_tile_info)), tilemap_mapper_delegate(*this, FUNC(pacman_state::pacman_scan_rows)), 8, 8, 36, 28);
175 }
176 
VIDEO_START_MEMBER(pacman_state,birdiy)177 VIDEO_START_MEMBER(pacman_state,birdiy)
178 {
179 	VIDEO_START_CALL_MEMBER( pacman );
180 	m_xoffsethack = 0;
181 	m_inv_spr = 1; // sprites are mirrored in X-axis compared to normal behaviour
182 }
183 
pacman_videoram_w(offs_t offset,uint8_t data)184 void pacman_state::pacman_videoram_w(offs_t offset, uint8_t data)
185 {
186 	m_videoram[offset] = data;
187 	m_bg_tilemap->mark_tile_dirty(offset );
188 }
189 
pacman_colorram_w(offs_t offset,uint8_t data)190 void pacman_state::pacman_colorram_w(offs_t offset, uint8_t data)
191 {
192 	m_colorram[offset] = data;
193 	m_bg_tilemap->mark_tile_dirty(offset );
194 }
195 
WRITE_LINE_MEMBER(pacman_state::flipscreen_w)196 WRITE_LINE_MEMBER(pacman_state::flipscreen_w)
197 {
198 	m_flipscreen = state;
199 	m_bg_tilemap->set_flip(m_flipscreen * ( TILEMAP_FLIPX + TILEMAP_FLIPY ) );
200 }
201 
202 
screen_update_pacman(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)203 uint32_t pacman_state::screen_update_pacman(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
204 {
205 	if (m_bgpriority != 0)
206 		bitmap.fill(0, cliprect);
207 	else
208 		m_bg_tilemap->draw(screen, bitmap, cliprect, TILEMAP_DRAW_OPAQUE,0);
209 
210 	if( m_spriteram != nullptr )
211 	{
212 		uint8_t *spriteram = m_spriteram;
213 		uint8_t *spriteram_2 = m_spriteram2;
214 		int offs;
215 
216 		rectangle spriteclip(2*8, 34*8-1, 0*8, 28*8-1);
217 		spriteclip &= cliprect;
218 
219 		/* Draw the sprites. Note that it is important to draw them exactly in this */
220 		/* order, to have the correct priorities. */
221 		for (offs = m_spriteram.bytes() - 2;offs > 2*2;offs -= 2)
222 		{
223 			int color;
224 			int sx,sy;
225 			uint8_t fx,fy;
226 
227 			if(m_inv_spr)
228 			{
229 				sx = spriteram_2[offs + 1];
230 				sy = 240 - (spriteram_2[offs]);
231 			}
232 			else
233 			{
234 				sx = 272 - spriteram_2[offs + 1];
235 				sy = spriteram_2[offs] - 31;
236 			}
237 
238 			fx = (spriteram[offs] & 1) ^ m_inv_spr;
239 			fy = (spriteram[offs] & 2) ^ ((m_inv_spr) << 1);
240 
241 			color = ( spriteram[offs + 1] & 0x1f ) | (m_colortablebank << 5) | (m_palettebank << 6 );
242 
243 			m_gfxdecode->gfx(1)->transmask(bitmap,spriteclip,
244 					( spriteram[offs] >> 2 ) | (m_spritebank << 6),
245 					color,
246 					fx,fy,
247 					sx,sy,
248 					m_palette->transpen_mask(*m_gfxdecode->gfx(1), color & 0x3f, 0));
249 
250 			/* also plot the sprite with wraparound (tunnel in Crush Roller) */
251 			m_gfxdecode->gfx(1)->transmask(bitmap,spriteclip,
252 					( spriteram[offs] >> 2 ) | (m_spritebank << 6),
253 					color,
254 					fx,fy,
255 					sx - 256,sy,
256 					m_palette->transpen_mask(*m_gfxdecode->gfx(1), color & 0x3f, 0));
257 		}
258 		/* In the Pac Man based games (NOT Pengo) the first two sprites must be offset */
259 		/* one pixel to the left to get a more correct placement */
260 		for (offs = 2*2;offs >= 0;offs -= 2)
261 		{
262 			int color;
263 			int sx,sy;
264 			uint8_t fx,fy;
265 
266 			if(m_inv_spr)
267 			{
268 				sx = spriteram_2[offs + 1];
269 				sy = 240 - (spriteram_2[offs]);
270 			}
271 			else
272 			{
273 				sx = 272 - spriteram_2[offs + 1];
274 				sy = spriteram_2[offs] - 31;
275 			}
276 			color = ( spriteram[offs + 1] & 0x1f ) | (m_colortablebank << 5) | (m_palettebank << 6 );
277 
278 			fx = (spriteram[offs] & 1) ^ m_inv_spr;
279 			fy = (spriteram[offs] & 2) ^ ((m_inv_spr) << 1);
280 
281 			m_gfxdecode->gfx(1)->transmask(bitmap,spriteclip,
282 					( spriteram[offs] >> 2 ) | (m_spritebank << 6),
283 					color,
284 					fx,fy,
285 					sx,sy + m_xoffsethack,
286 					m_palette->transpen_mask(*m_gfxdecode->gfx(1), color & 0x3f, 0));
287 
288 			/* also plot the sprite with wraparound (tunnel in Crush Roller) */
289 			m_gfxdecode->gfx(1)->transmask(bitmap,spriteclip,
290 					( spriteram[offs] >> 2 ) | (m_spritebank << 6),
291 					color,
292 					fx,fy,
293 					sx - 256,sy + m_xoffsethack,
294 					m_palette->transpen_mask(*m_gfxdecode->gfx(1), color & 0x3f, 0));
295 		}
296 	}
297 
298 	if (m_bgpriority != 0)
299 		m_bg_tilemap->draw(screen, bitmap, cliprect, 0,0);
300 	return 0;
301 }
302 
303 
304 /*************************************************************************
305 
306     Sega Pengo
307 
308 **************************************************************************/
309 
VIDEO_START_MEMBER(pacman_state,pengo)310 VIDEO_START_MEMBER(pacman_state,pengo)
311 {
312 	init_save_state();
313 
314 	m_charbank = 0;
315 	m_spritebank = 0;
316 	m_palettebank = 0;
317 	m_colortablebank = 0;
318 	m_flipscreen = 0;
319 	m_bgpriority = 0;
320 	m_inv_spr = 0;
321 	m_xoffsethack = 0;
322 
323 	m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(pacman_state::pacman_get_tile_info)), tilemap_mapper_delegate(*this, FUNC(pacman_state::pacman_scan_rows)), 8, 8, 36, 28);
324 }
325 
WRITE_LINE_MEMBER(pacman_state::pengo_palettebank_w)326 WRITE_LINE_MEMBER(pacman_state::pengo_palettebank_w)
327 {
328 	m_palettebank = state;
329 	m_bg_tilemap->mark_all_dirty();
330 }
331 
WRITE_LINE_MEMBER(pacman_state::pengo_colortablebank_w)332 WRITE_LINE_MEMBER(pacman_state::pengo_colortablebank_w)
333 {
334 	m_colortablebank = state;
335 	m_bg_tilemap->mark_all_dirty();
336 }
337 
WRITE_LINE_MEMBER(pacman_state::pengo_gfxbank_w)338 WRITE_LINE_MEMBER(pacman_state::pengo_gfxbank_w)
339 {
340 	m_spritebank = state;
341 	m_charbank = state;
342 	m_bg_tilemap->mark_all_dirty();
343 }
344 
345 
346 /*************************************************************************
347 
348 S2650 Games
349 
350 **************************************************************************/
351 
TILE_GET_INFO_MEMBER(pacman_state::s2650_get_tile_info)352 TILE_GET_INFO_MEMBER(pacman_state::s2650_get_tile_info)
353 {
354 	int colbank, code, attr;
355 
356 	colbank = m_s2650games_tileram[tile_index & 0x1f] & 0x3;
357 
358 	code = m_videoram[tile_index] + (colbank << 8);
359 	attr = m_colorram[tile_index & 0x1f];
360 
361 	tileinfo.set(0,code,attr & 0x1f,0);
362 }
363 
VIDEO_START_MEMBER(pacman_state,s2650games)364 VIDEO_START_MEMBER(pacman_state,s2650games)
365 {
366 	init_save_state();
367 
368 	m_charbank = 0;
369 	m_spritebank = 0;
370 	m_palettebank = 0;
371 	m_colortablebank = 0;
372 	m_flipscreen = 0;
373 	m_bgpriority = 0;
374 	m_inv_spr = 0;
375 	m_xoffsethack = 1;
376 
377 	m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(pacman_state::s2650_get_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
378 
379 	m_bg_tilemap->set_scroll_cols(32);
380 }
381 
screen_update_s2650games(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)382 uint32_t pacman_state::screen_update_s2650games(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
383 {
384 	uint8_t *spriteram = m_spriteram;
385 	uint8_t *spriteram_2 = m_spriteram2;
386 	int offs;
387 
388 	m_bg_tilemap->draw(screen, bitmap, cliprect, 0,0);
389 
390 	for (offs = m_spriteram.bytes() - 2;offs > 2*2;offs -= 2)
391 	{
392 		int color;
393 		int sx,sy;
394 
395 
396 		sx = 255 - spriteram_2[offs + 1];
397 		sy = spriteram_2[offs] - 15;
398 		color = spriteram[offs + 1] & 0x1f;
399 
400 		/* TODO: ?? */
401 		m_gfxdecode->gfx(1)->transmask(bitmap,cliprect,
402 				(spriteram[offs] >> 2) | ((m_s2650_spriteram[offs] & 3) << 6),
403 				color,
404 				spriteram[offs] & 1,spriteram[offs] & 2,
405 				sx,sy,
406 				m_palette->transpen_mask(*m_gfxdecode->gfx(1), color & 0x3f, 0));
407 	}
408 	/* In the Pac Man based games (NOT Pengo) the first two sprites must be offset */
409 	/* one pixel to the left to get a more correct placement */
410 	for (offs = 2*2;offs >= 0;offs -= 2)
411 	{
412 		int color;
413 		int sx,sy;
414 
415 
416 		sx = 255 - spriteram_2[offs + 1];
417 		sy = spriteram_2[offs] - 15;
418 		color = spriteram[offs + 1] & 0x1f;
419 
420 		/* TODO: ?? */
421 		m_gfxdecode->gfx(1)->transmask(bitmap,cliprect,
422 				(spriteram[offs] >> 2) | ((m_s2650_spriteram[offs] & 3)<<6),
423 				color,
424 				spriteram[offs] & 1,spriteram[offs] & 2,
425 				sx,sy + m_xoffsethack,
426 				m_palette->transpen_mask(*m_gfxdecode->gfx(1), color & 0x3f, 0));
427 	}
428 	return 0;
429 }
430 
s2650games_videoram_w(offs_t offset,uint8_t data)431 void pacman_state::s2650games_videoram_w(offs_t offset, uint8_t data)
432 {
433 	m_videoram[offset] = data;
434 	m_bg_tilemap->mark_tile_dirty(offset);
435 }
436 
s2650games_colorram_w(offs_t offset,uint8_t data)437 void pacman_state::s2650games_colorram_w(offs_t offset, uint8_t data)
438 {
439 	int i;
440 	m_colorram[offset & 0x1f] = data;
441 	for (i = offset; i < 0x0400; i += 32)
442 		m_bg_tilemap->mark_tile_dirty(i);
443 }
444 
s2650games_scroll_w(offs_t offset,uint8_t data)445 void pacman_state::s2650games_scroll_w(offs_t offset, uint8_t data)
446 {
447 	m_bg_tilemap->set_scrolly(offset, data);
448 }
449 
s2650games_tilesbank_w(offs_t offset,uint8_t data)450 void pacman_state::s2650games_tilesbank_w(offs_t offset, uint8_t data)
451 {
452 	m_s2650games_tileram[offset] = data;
453 	m_bg_tilemap->mark_all_dirty();
454 }
455 
456 
457 /*************************************************************************
458 
459 Jr. Pac-Man
460 
461 **************************************************************************/
462 
463 /*
464    0 -   31 = column 2 - 33 attr (used for all 54 rows)
465   64 - 1791 = column 2 - 33 code (54 rows)
466 1794 - 1821 = column 34 code (28 rows)
467 1826 - 1853 = column 35 code (28 rows)
468 1858 - 1885 = column 0 code (28 rows)
469 1890 - 1917 = column 1 code (28 rows)
470 1922 - 1949 = column 34 attr (28 rows)
471 1954 - 1981 = column 35 attr (28 rows)
472 1986 - 2013 = column 0 attr (28 rows)
473 2018 - 2045 = column 1 attr (28 rows)
474 */
475 
TILEMAP_MAPPER_MEMBER(pacman_state::jrpacman_scan_rows)476 TILEMAP_MAPPER_MEMBER(pacman_state::jrpacman_scan_rows)
477 {
478 	int offs;
479 
480 	row += 2;
481 	col -= 2;
482 	if ((col & 0x20) && (row & 0x20))
483 		offs = 0;
484 	else if (col & 0x20)
485 		offs = row + (((col&0x3) | 0x38)<< 5);
486 	else
487 		offs = col + (row << 5);
488 	return offs;
489 }
490 
TILE_GET_INFO_MEMBER(pacman_state::jrpacman_get_tile_info)491 TILE_GET_INFO_MEMBER(pacman_state::jrpacman_get_tile_info)
492 {
493 	int color_index, code, attr;
494 	if( tile_index < 1792 )
495 	{
496 		color_index = tile_index & 0x1f;
497 	}
498 	else
499 	{
500 		color_index = tile_index + 0x80;
501 	}
502 
503 	code = m_videoram[tile_index] | (m_charbank << 8);
504 	attr = (m_videoram[color_index] & 0x1f) | (m_colortablebank << 5) | (m_palettebank << 6 );
505 
506 	tileinfo.set(0,code,attr,0);
507 }
508 
jrpacman_mark_tile_dirty(int offset)509 void pacman_state::jrpacman_mark_tile_dirty( int offset )
510 {
511 	if( offset < 0x20 )
512 	{
513 		/* line color - mark whole line as dirty */
514 		int i;
515 		for( i = 2 * 0x20; i < 56 * 0x20; i += 0x20 )
516 		{
517 			m_bg_tilemap->mark_tile_dirty(offset + i );
518 		}
519 	}
520 	else if (offset < 1792)
521 	{
522 		/* tiles for playfield */
523 		m_bg_tilemap->mark_tile_dirty(offset );
524 	}
525 	else
526 	{
527 		/* tiles & colors for top and bottom two rows */
528 		m_bg_tilemap->mark_tile_dirty(offset & ~0x80 );
529 	}
530 }
531 
VIDEO_START_MEMBER(pacman_state,jrpacman)532 VIDEO_START_MEMBER(pacman_state,jrpacman)
533 {
534 	init_save_state();
535 
536 	m_charbank = 0;
537 	m_spritebank = 0;
538 	m_palettebank = 0;
539 	m_colortablebank = 0;
540 	m_flipscreen = 0;
541 	m_bgpriority = 0;
542 	m_inv_spr = 0;
543 	m_xoffsethack = 1;
544 
545 	m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(pacman_state::jrpacman_get_tile_info)), tilemap_mapper_delegate(*this, FUNC(pacman_state::jrpacman_scan_rows)), 8, 8, 36, 54);
546 
547 	m_bg_tilemap->set_transparent_pen(0 );
548 	m_bg_tilemap->set_scroll_cols(36 );
549 }
550 
jrpacman_videoram_w(offs_t offset,uint8_t data)551 void pacman_state::jrpacman_videoram_w(offs_t offset, uint8_t data)
552 {
553 	m_videoram[offset] = data;
554 	jrpacman_mark_tile_dirty(offset);
555 }
556 
WRITE_LINE_MEMBER(pacman_state::jrpacman_charbank_w)557 WRITE_LINE_MEMBER(pacman_state::jrpacman_charbank_w)
558 {
559 	m_charbank = state;
560 	m_bg_tilemap->mark_all_dirty();
561 }
562 
WRITE_LINE_MEMBER(pacman_state::jrpacman_spritebank_w)563 WRITE_LINE_MEMBER(pacman_state::jrpacman_spritebank_w)
564 {
565 	m_spritebank = state;
566 }
567 
jrpacman_scroll_w(uint8_t data)568 void pacman_state::jrpacman_scroll_w(uint8_t data)
569 {
570 	int i;
571 	for( i = 2; i < 34; i++ )
572 	{
573 		m_bg_tilemap->set_scrolly(i, data );
574 	}
575 }
576 
WRITE_LINE_MEMBER(pacman_state::jrpacman_bgpriority_w)577 WRITE_LINE_MEMBER(pacman_state::jrpacman_bgpriority_w)
578 {
579 	m_bgpriority = state;
580 }
581