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