1 // license:BSD-3-Clause
2 // copyright-holders:Nicola Salmoria
3 /*************************************************************************
4
5 Atari Centipede hardware
6
7 *************************************************************************/
8
9 #include "emu.h"
10 #include "includes/centiped.h"
11
12
13 /*************************************
14 *
15 * Tilemap callback
16 *
17 *************************************/
18
TILE_GET_INFO_MEMBER(centiped_state::centiped_get_tile_info)19 TILE_GET_INFO_MEMBER(centiped_state::centiped_get_tile_info)
20 {
21 uint8_t *videoram = m_videoram;
22
23 int data = videoram[tile_index];
24 tileinfo.set(0, (data & 0x3f) + 0x40, 0, TILE_FLIPYX(data >> 6));
25 }
26
27
TILE_GET_INFO_MEMBER(centiped_state::warlords_get_tile_info)28 TILE_GET_INFO_MEMBER(centiped_state::warlords_get_tile_info)
29 {
30 uint8_t *videoram = m_videoram;
31 int data = videoram[tile_index];
32 int color = ((tile_index & 0x10) >> 4) | ((tile_index & 0x200) >> 8) | (m_flipscreen >> 5);
33
34 tileinfo.set(0, data & 0x3f, color, TILE_FLIPYX(data >> 6));
35 }
36
37
TILE_GET_INFO_MEMBER(centiped_state::milliped_get_tile_info)38 TILE_GET_INFO_MEMBER(centiped_state::milliped_get_tile_info)
39 {
40 uint8_t *videoram = m_videoram;
41 int data = videoram[tile_index];
42 int bank = ((data >> 6) & 1) | (m_gfx_bank << 1);
43 int color = (data >> 6) & 3;
44 /* Flip both x and y if flipscreen is non-zero */
45 int flip_tiles = (m_flipscreen) ? 0x03 : 0;
46
47 tileinfo.set(0, (data & 0x3f) + 0x40 + (bank * 0x80), color, TILE_FLIPYX(flip_tiles));
48 }
49
50
TILE_GET_INFO_MEMBER(centiped_state::bullsdrt_get_tile_info)51 TILE_GET_INFO_MEMBER(centiped_state::bullsdrt_get_tile_info)
52 {
53 uint8_t *videoram = m_videoram;
54 int data = videoram[tile_index];
55 int bank = m_bullsdrt_tiles_bankram[tile_index & 0x1f] & 0x0f;
56
57 tileinfo.set(0, (data & 0x3f) + 0x40 * bank, 0, TILE_FLIPYX(data >> 6));
58 }
59
60
61
62 /*************************************
63 *
64 * Video system start
65 *
66 *************************************/
67
init_penmask()68 void centiped_state::init_penmask()
69 {
70 int i;
71
72 for (i = 0; i < 64; i++)
73 {
74 uint8_t mask = 1;
75 if (((i >> 0) & 3) == 0) mask |= 2;
76 if (((i >> 2) & 3) == 0) mask |= 4;
77 if (((i >> 4) & 3) == 0) mask |= 8;
78 m_penmask[i] = mask;
79 }
80 }
81
82
init_common()83 void centiped_state::init_common()
84 {
85 save_item(NAME(m_flipscreen));
86 save_item(NAME(m_gfx_bank));
87 save_item(NAME(m_bullsdrt_sprites_bank));
88
89 m_flipscreen = 0;
90 m_gfx_bank = 0;
91 m_bullsdrt_sprites_bank = 0;
92 }
93
94
VIDEO_START_MEMBER(centiped_state,centiped)95 VIDEO_START_MEMBER(centiped_state,centiped)
96 {
97 init_common();
98 init_penmask();
99
100 m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(centiped_state::centiped_get_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
101 }
102
103
VIDEO_START_MEMBER(centiped_state,warlords)104 VIDEO_START_MEMBER(centiped_state,warlords)
105 {
106 init_common();
107
108 m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(centiped_state::warlords_get_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
109 }
110
111
VIDEO_START_MEMBER(centiped_state,milliped)112 VIDEO_START_MEMBER(centiped_state,milliped)
113 {
114 init_common();
115 init_penmask();
116
117 m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(centiped_state::milliped_get_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
118 }
119
120
VIDEO_START_MEMBER(centiped_state,bullsdrt)121 VIDEO_START_MEMBER(centiped_state,bullsdrt)
122 {
123 init_common();
124 init_penmask();
125
126 m_bg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(centiped_state::bullsdrt_get_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 32);
127 }
128
129
130
131 /*************************************
132 *
133 * Video RAM writes
134 *
135 *************************************/
136
centiped_videoram_w(offs_t offset,uint8_t data)137 void centiped_state::centiped_videoram_w(offs_t offset, uint8_t data)
138 {
139 m_videoram[offset] = data;
140 m_bg_tilemap->mark_tile_dirty(offset);
141 }
142
143
144
145 /*************************************
146 *
147 * Screen flip
148 *
149 *************************************/
150
WRITE_LINE_MEMBER(centiped_state::flip_screen_w)151 WRITE_LINE_MEMBER(centiped_state::flip_screen_w)
152 {
153 m_flipscreen = state;
154 }
155
156
157
158 /*************************************
159 *
160 * Graphics bank
161 *
162 *************************************/
163
multiped_gfxbank_w(uint8_t data)164 void centiped_state::multiped_gfxbank_w(uint8_t data)
165 {
166 // d0-d6: N/C?
167 // d7: gfx rom bank
168 int bank = m_prg_bank | (data >> 6 & 2);
169 if (bank != m_gfx_bank)
170 {
171 m_gfx_bank = bank;
172 m_bg_tilemap->mark_all_dirty();
173 }
174 }
175
176
bullsdrt_tilesbank_w(offs_t offset,uint8_t data)177 void centiped_state::bullsdrt_tilesbank_w(offs_t offset, uint8_t data)
178 {
179 m_bullsdrt_tiles_bankram[offset] = data;
180 m_bg_tilemap->mark_all_dirty();
181 }
182
183
bullsdrt_sprites_bank_w(uint8_t data)184 void centiped_state::bullsdrt_sprites_bank_w(uint8_t data)
185 {
186 m_bullsdrt_sprites_bank = data;
187 }
188
189
190
191 /***************************************************************************
192
193 Centipede doesn't have a color PROM. Eight RAM locations control
194 the color of characters and sprites. The meanings of the four bits are
195 (all bits are inverted):
196
197 bit 3 alternate
198 blue
199 green
200 bit 0 red
201
202 The alternate bit affects blue and green, not red. The way I weighted its
203 effect might not be perfectly accurate, but is reasonably close.
204
205 Centipede is unusual because the sprite color code specifies the
206 colors to use one by one, instead of a combination code.
207
208 FIXME: handle this using standard indirect colors instead of
209 custom implementation
210
211 bit 5-4 = color to use for pen 11
212 bit 3-2 = color to use for pen 10
213 bit 1-0 = color to use for pen 01
214 pen 00 is transparent
215
216 ***************************************************************************/
217
centiped_paletteram_w(offs_t offset,uint8_t data)218 void centiped_state::centiped_paletteram_w(offs_t offset, uint8_t data)
219 {
220 m_paletteram[offset] = data;
221
222 /* bit 2 of the output palette RAM is always pulled high, so we ignore */
223 /* any palette changes unless the write is to a palette RAM address */
224 /* that is actually used */
225 if (offset & 4)
226 {
227 rgb_t color;
228
229 int r = 0xff * ((~data >> 0) & 1);
230 int g = 0xff * ((~data >> 1) & 1);
231 int b = 0xff * ((~data >> 2) & 1);
232
233 if (~data & 0x08) /* alternate = 1 */
234 {
235 /* when blue component is not 0, decrease it. When blue component is 0, */
236 /* decrease green component. */
237 if (b) b = 0xc0;
238 else if (g) g = 0xc0;
239 }
240
241 color = rgb_t(r, g, b);
242
243 /* character colors, set directly */
244 if ((offset & 0x08) == 0)
245 m_palette->set_pen_color(offset & 0x03, color);
246
247 /* sprite colors - set all the applicable ones */
248 else
249 {
250 int i;
251
252 offset = offset & 0x03;
253
254 for (i = 0; i < 0x100; i += 4)
255 {
256 if (offset == ((i >> 2) & 0x03))
257 m_palette->set_pen_color(i + 4 + 1, color);
258
259 if (offset == ((i >> 4) & 0x03))
260 m_palette->set_pen_color(i + 4 + 2, color);
261
262 if (offset == ((i >> 6) & 0x03))
263 m_palette->set_pen_color(i + 4 + 3, color);
264 }
265 }
266 }
267 }
268
269
270
271 /***************************************************************************
272
273 Convert the color PROM into a more useable format.
274
275 The palette PROM are connected to the RGB output this way:
276
277 bit 2 -- RED
278 -- GREEN
279 bit 0 -- BLUE
280
281 ***************************************************************************/
282
warlords_palette(palette_device & palette) const283 void centiped_state::warlords_palette(palette_device &palette) const
284 {
285 uint8_t const *const color_prom = memregion("proms")->base();
286
287 for (unsigned i = 0; i < palette.entries(); i++)
288 {
289 uint8_t pen;
290
291 if (i < 0x20)
292 /* characters */
293 pen = (((i - 0x00) & 0x1c) << 2) | (((i - 0x00) & 0x03) << 0);
294 else
295 /* sprites */
296 pen = (((i - 0x20) & 0x1c) << 2) | (((i - 0x20) & 0x03) << 2);
297
298 int r = ((color_prom[pen] >> 2) & 0x01) * 0xff;
299 int g = ((color_prom[pen] >> 1) & 0x01) * 0xff;
300 int b = ((color_prom[pen] >> 0) & 0x01) * 0xff;
301
302 /* colors 0x40-0x7f are converted to grey scale as it's used on the
303 upright version that had an overlay */
304 if (pen >= 0x40)
305 {
306 /* use the standard ratios: r = 30%, g = 59%, b = 11% */
307 int const grey = (r * 0x4d / 0xff) + (g * 0x96 / 0xff) + (b * 0x1c / 0xff);
308 r = g = b = grey;
309 }
310
311 palette.set_pen_color(i, rgb_t(r, g, b));
312 }
313 }
314
315
316
317 /***************************************************************************
318
319 Millipede doesn't have a color PROM, it uses RAM.
320 The RAM seems to be conncted to the video output this way:
321
322 bit 7 red
323 red
324 red
325 green
326 green
327 blue
328 blue
329 bit 0 blue
330
331 Millipede is unusual because the sprite color code specifies the
332 colors to use one by one, instead of a combination code.
333
334 FIXME: handle this using standard indirect colors instead of
335 custom implementation
336
337 bit 7-6 = palette bank (there are 4 groups of 4 colors)
338 bit 5-4 = color to use for pen 11
339 bit 3-2 = color to use for pen 10
340 bit 1-0 = color to use for pen 01
341 pen 00 is transparent
342
343 ***************************************************************************/
344
milliped_set_color(offs_t offset,uint8_t data)345 void centiped_state::milliped_set_color(offs_t offset, uint8_t data)
346 {
347 rgb_t color;
348 int bit0, bit1, bit2;
349 int r, g, b;
350
351 /* red component */
352 bit0 = (~data >> 5) & 0x01;
353 bit1 = (~data >> 6) & 0x01;
354 bit2 = (~data >> 7) & 0x01;
355 r = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2;
356
357 /* green component */
358 bit0 = 0;
359 bit1 = (~data >> 3) & 0x01;
360 bit2 = (~data >> 4) & 0x01;
361 g = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2;
362
363 /* blue component */
364 bit0 = (~data >> 0) & 0x01;
365 bit1 = (~data >> 1) & 0x01;
366 bit2 = (~data >> 2) & 0x01;
367 b = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2;
368
369 color = rgb_t(r, g, b);
370
371 /* character colors, set directly */
372 if (offset < 0x10)
373 m_palette->set_pen_color(offset, color);
374
375 /* sprite colors - set all the applicable ones */
376 else
377 {
378 int i;
379
380 int base = offset & 0x0c;
381
382 offset = offset & 0x03;
383
384 for (i = (base << 6); i < (base << 6) + 0x100; i += 4)
385 {
386 if (offset == ((i >> 2) & 0x03))
387 m_palette->set_pen_color(i + 0x10 + 1, color);
388
389 if (offset == ((i >> 4) & 0x03))
390 m_palette->set_pen_color(i + 0x10 + 2, color);
391
392 if (offset == ((i >> 6) & 0x03))
393 m_palette->set_pen_color(i + 0x10 + 3, color);
394 }
395 }
396 }
397
398
milliped_paletteram_w(offs_t offset,uint8_t data)399 void centiped_state::milliped_paletteram_w(offs_t offset, uint8_t data)
400 {
401 m_paletteram[offset] = data;
402
403 milliped_set_color(offset, data);
404 }
405
406
mazeinv_paletteram_w(offs_t offset,uint8_t data)407 void centiped_state::mazeinv_paletteram_w(offs_t offset, uint8_t data)
408 {
409 m_paletteram[offset] = data;
410
411 /* the value passed in is a look-up index into the color PROM */
412 milliped_set_color(offset, ~memregion("proms")->base()[~data & 0x0f]);
413 }
414
415
416
417 /*************************************
418 *
419 * Video update
420 *
421 *************************************/
422
screen_update_centiped(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)423 uint32_t centiped_state::screen_update_centiped(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
424 {
425 uint8_t *spriteram = m_spriteram;
426 rectangle spriteclip = cliprect;
427 int offs;
428
429 /* draw the background */
430 m_bg_tilemap->draw(screen, bitmap, cliprect, 0, 0);
431
432 /* apply the sprite clip */
433 if (m_flipscreen)
434 spriteclip.min_x += 8;
435 else
436 spriteclip.max_x -= 8;
437
438 /* draw the sprites */
439 for (offs = 0; offs < 0x10; offs++)
440 {
441 int code = ((spriteram[offs] & 0x3e) >> 1) | ((spriteram[offs] & 0x01) << 6);
442 int color = spriteram[offs + 0x30];
443 int flipx = (spriteram[offs] >> 6) & 1;
444 int flipy = (spriteram[offs] >> 7) & 1;
445 int x = spriteram[offs + 0x20];
446 int y = 240 - spriteram[offs + 0x10];
447
448 m_gfxdecode->gfx(1)->transmask(bitmap,spriteclip, code, color, flipx, flipy, x, y, m_penmask[color & 0x3f]);
449 }
450 return 0;
451 }
452
453
screen_update_warlords(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)454 uint32_t centiped_state::screen_update_warlords(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
455 {
456 uint8_t *spriteram = m_spriteram;
457 int upright_mode = ioport("IN0")->read() & 0x80;
458 int offs;
459
460 /* if the cocktail/upright switch flipped, force refresh */
461 if (m_flipscreen != upright_mode)
462 {
463 m_flipscreen = upright_mode;
464 m_bg_tilemap->set_flip(upright_mode ? TILEMAP_FLIPX : 0);
465 m_bg_tilemap->mark_all_dirty();
466 }
467
468 /* draw the background */
469 m_bg_tilemap->draw(screen, bitmap, cliprect, 0, 0);
470
471 /* draw the sprites */
472 for (offs = 0; offs < 0x10; offs++)
473 {
474 int code = spriteram[offs] & 0x3f;
475 int flipx = (spriteram[offs] >> 6) & 1;
476 int flipy = (spriteram[offs] >> 7) & 1;
477 int x = spriteram[offs + 0x20];
478 int y = 248 - spriteram[offs + 0x10];
479
480 /* The four quadrants have different colors. This is not 100% accurate,
481 because right on the middle the sprite could actually have two or more
482 different color, but this is not noticeable, as the color that
483 changes between the quadrants is mostly used on the paddle sprites */
484 int color = ((y & 0x80) >> 6) | ((x & 0x80) >> 7) | (upright_mode >> 5);
485
486 /* in upright mode, sprites are flipped */
487 if (upright_mode)
488 {
489 x = 248 - x;
490 flipx = !flipx;
491 }
492
493 m_gfxdecode->gfx(1)->transpen(bitmap,cliprect, code, color, flipx, flipy, x, y, 0);
494 }
495 return 0;
496 }
497
498
screen_update_bullsdrt(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)499 uint32_t centiped_state::screen_update_bullsdrt(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
500 {
501 uint8_t *spriteram = m_spriteram;
502 rectangle spriteclip = cliprect;
503
504 int offs;
505
506 /* draw the background */
507 m_bg_tilemap->draw(screen, bitmap, cliprect, 0, 0);
508
509 /* apply the sprite clip */
510 if (m_flipscreen)
511 spriteclip.min_x += 8;
512 else
513 spriteclip.max_x -= 8;
514
515 /* draw the sprites */
516 for (offs = 0; offs < 0x10; offs++)
517 {
518 int code = ((spriteram[offs] & 0x3e) >> 1) | ((spriteram[offs] & 0x01) << 6) | (m_bullsdrt_sprites_bank * 0x20);
519 int color = spriteram[offs + 0x30];
520 int flipy = (spriteram[offs] >> 7) & 1;
521 int x = spriteram[offs + 0x20];
522 int y = 240 - spriteram[offs + 0x10];
523
524 m_gfxdecode->gfx(1)->transpen(bitmap,spriteclip, code, color & 0x3f, 1, flipy, x, y, 0);
525 }
526 return 0;
527 }
528
529 /*
530 * This varies from Centipede, in that flipx is not in
531 * the data, but is determined by VIDROT value at 0x2506.
532 */
screen_update_milliped(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)533 uint32_t centiped_state::screen_update_milliped(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
534 {
535 uint8_t *spriteram = m_spriteram;
536 rectangle spriteclip = cliprect;
537 int offs;
538
539 /* draw the background */
540 m_bg_tilemap->draw(screen, bitmap, cliprect, 0, 0);
541
542 /* apply the sprite clip */
543 if (m_flipscreen)
544 spriteclip.min_x += 8;
545 else
546 spriteclip.max_x -= 8;
547
548 /* draw the sprites */
549 for (offs = 0; offs < 0x10; offs++)
550 {
551 int code = ((spriteram[offs] & 0x3e) >> 1) | ((spriteram[offs] & 0x01) << 6) | (m_gfx_bank << 7);
552 int color = spriteram[offs + 0x30];
553 int flipx = m_flipscreen;
554 int flipy = (spriteram[offs] & 0x80);
555 int x = spriteram[offs + 0x20];
556 int y = 240 - spriteram[offs + 0x10];
557 if (flipx) {
558 flipy = !flipy;
559 }
560
561 m_gfxdecode->gfx(1)->transmask(bitmap,spriteclip, code, color, flipx, flipy, x, y, m_penmask[color & 0x3f]);
562 }
563 return 0;
564 }
565