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