1 // license:BSD-3-Clause
2 // copyright-holders:BUT
3 /*
4  *  Thunder Ceptor board
5  *  emulate video hardware
6  */
7 
8 #include "emu.h"
9 #include "includes/tceptor.h"
10 
11 #include <algorithm>
12 
13 
14 #define TX_TILE_OFFSET_CENTER   (32 * 2)
15 #define TX_TILE_OFFSET_RIGHT    (32 * 0 + 2)
16 #define TX_TILE_OFFSET_LEFT (32 * 31 + 2)
17 
18 #define SPR_TRANS_COLOR     (0xff + 0x300)
19 #define SPR_MASK_COLOR      (0xfe + 0x300)
20 
21 
22 /*******************************************************************/
23 
tceptor_palette(palette_device & palette)24 void tceptor_state::tceptor_palette(palette_device &palette)
25 {
26 	const uint8_t *color_prom = memregion("proms")->base();
27 
28 	// create a lookup table for the palette
29 	for (int i = 0; i < 0x400; i++)
30 	{
31 		int const r = pal4bit(color_prom[i + 0x000]);
32 		int const g = pal4bit(color_prom[i + 0x400]);
33 		int const b = pal4bit(color_prom[i + 0x800]);
34 
35 		palette.set_indirect_color(i, rgb_t(r, g, b));
36 	}
37 
38 	// color_prom now points to the beginning of the lookup table
39 	color_prom += 0xc00;
40 
41 	/*
42 	    color lookup table:
43 	     0-    +1024 ( 4 * 256) colors: text   (use 0-   256 colors)
44 	     1024- +1024 (16 *  64) colors: sprite (use 768- 256 colors)
45 	     2048-  +512 ( 8 *  64) colors: bg     (use 0-   512 colors)
46 	     3840-  +256 ( 4 *  64) colors: road   (use 512- 256 colors)
47 	*/
48 
49 	// tiles lookup table (1024 colors)
50 	for (int i = 0; i < 0x0400; i++)
51 	{
52 		int const ctabentry = color_prom[i];
53 		palette.set_pen_indirect(i, ctabentry);
54 	}
55 
56 	// sprites lookup table (1024 colors)
57 	for (int i = 0x0400; i < 0x0800; i++)
58 	{
59 		int const ctabentry = color_prom[i] | 0x300;
60 		palette.set_pen_indirect(i, ctabentry);
61 	}
62 
63 	// background: no lookup PROM, use directly (512 colors)
64 	for (int i = 0x0a00; i < 0x0c00; i++)
65 	{
66 		int const ctabentry = i & 0x1ff;
67 		palette.set_pen_indirect(i, ctabentry);
68 	}
69 
70 	// road lookup table (256 colors)
71 	for (int i = 0x0f00; i < 0x1000; i++)
72 	{
73 		int const ctabentry = color_prom[i - 0x700] | 0x200;
74 		palette.set_pen_indirect(i, ctabentry);
75 	}
76 
77 	// setup sprite mask color map
78 	// tceptor2: only 0x23
79 	std::fill(std::begin(m_is_mask_spr), std::end(m_is_mask_spr), 0);
80 	for (int i = 0; i < 0x400; i++)
81 	{
82 		if (palette.pen_indirect(i | 0x400) == SPR_MASK_COLOR)
83 			m_is_mask_spr[i >> 4] = 1;
84 	}
85 }
86 
87 
88 /*******************************************************************/
89 
get_tile_addr(int tile_index)90 inline int tceptor_state::get_tile_addr(int tile_index)
91 {
92 	int x = tile_index / 28;
93 	int y = tile_index % 28;
94 
95 	switch (x)
96 	{
97 	case 0:
98 		return TX_TILE_OFFSET_LEFT + y;
99 	case 33:
100 		return TX_TILE_OFFSET_RIGHT + y;
101 	}
102 
103 	return TX_TILE_OFFSET_CENTER + (x - 1) + y * 32;
104 }
105 
TILE_GET_INFO_MEMBER(tceptor_state::get_tx_tile_info)106 TILE_GET_INFO_MEMBER(tceptor_state::get_tx_tile_info)
107 {
108 	int offset = get_tile_addr(tile_index);
109 	int code = m_tile_ram[offset];
110 	int color = m_tile_attr[offset];
111 
112 	tileinfo.group = color;
113 
114 	tileinfo.set(0, code, color, 0);
115 }
116 
tile_mark_dirty(int offset)117 void tceptor_state::tile_mark_dirty(int offset)
118 {
119 	int x = -1;
120 	int y = -1;
121 
122 	if (offset >= TX_TILE_OFFSET_LEFT && offset < TX_TILE_OFFSET_LEFT + 28)
123 	{
124 		x = 0;
125 		y = offset - TX_TILE_OFFSET_LEFT;
126 	}
127 	else if (offset >= TX_TILE_OFFSET_RIGHT && offset < TX_TILE_OFFSET_RIGHT + 28)
128 	{
129 		x = 33;
130 		y = offset - TX_TILE_OFFSET_RIGHT;
131 	}
132 	else if (offset >= TX_TILE_OFFSET_CENTER && offset < TX_TILE_OFFSET_CENTER + 32 * 28)
133 	{
134 		offset -= TX_TILE_OFFSET_CENTER;
135 		x = (offset % 32) + 1;
136 		y = offset / 32;
137 	}
138 
139 	if (x >= 0)
140 		m_tx_tilemap->mark_tile_dirty(x * 28 + y);
141 }
142 
143 
tceptor_tile_ram_w(offs_t offset,uint8_t data)144 void tceptor_state::tceptor_tile_ram_w(offs_t offset, uint8_t data)
145 {
146 	if (m_tile_ram[offset] != data)
147 	{
148 		m_tile_ram[offset] = data;
149 		tile_mark_dirty(offset);
150 	}
151 }
152 
tceptor_tile_attr_w(offs_t offset,uint8_t data)153 void tceptor_state::tceptor_tile_attr_w(offs_t offset, uint8_t data)
154 {
155 	if (m_tile_attr[offset] != data)
156 	{
157 		m_tile_attr[offset] = data;
158 		tile_mark_dirty(offset);
159 	}
160 }
161 
162 
163 /*******************************************************************/
164 
TILE_GET_INFO_MEMBER(tceptor_state::get_bg1_tile_info)165 TILE_GET_INFO_MEMBER(tceptor_state::get_bg1_tile_info)
166 {
167 	uint16_t data = m_bg_ram[tile_index * 2] | (m_bg_ram[tile_index * 2 + 1] << 8);
168 	int code = (data & 0x3ff) | 0x000;
169 	int color = (data & 0xfc00) >> 10;
170 
171 	tileinfo.set(m_bg, code, color, 0);
172 }
173 
TILE_GET_INFO_MEMBER(tceptor_state::get_bg2_tile_info)174 TILE_GET_INFO_MEMBER(tceptor_state::get_bg2_tile_info)
175 {
176 	uint16_t data = m_bg_ram[tile_index * 2 + 0x1000] | (m_bg_ram[tile_index * 2 + 1 + 0x1000] << 8);
177 	int code = (data & 0x3ff) | 0x400;
178 	int color = (data & 0xfc00) >> 10;
179 
180 	tileinfo.set(m_bg, code, color, 0);
181 }
182 
tceptor_bg_ram_w(offs_t offset,uint8_t data)183 void tceptor_state::tceptor_bg_ram_w(offs_t offset, uint8_t data)
184 {
185 	m_bg_ram[offset] = data;
186 
187 	m_bg_tilemap[offset >> 12]->mark_tile_dirty((offset & 0xfff) >> 1);
188 }
189 
tceptor_bg_scroll_w(offs_t offset,uint8_t data)190 void tceptor_state::tceptor_bg_scroll_w(offs_t offset, uint8_t data)
191 {
192 	switch (offset)
193 	{
194 	case 0:
195 		m_bg_scroll_x[0] &= 0xff;
196 		m_bg_scroll_x[0] |= data << 8;
197 		break;
198 	case 1:
199 		m_bg_scroll_x[0] &= 0xff00;
200 		m_bg_scroll_x[0] |= data;
201 		break;
202 	case 2:
203 		m_bg_scroll_y[0] = data;
204 		break;
205 
206 	case 4:
207 		m_bg_scroll_x[1] &= 0xff;
208 		m_bg_scroll_x[1] |= data << 8;
209 		break;
210 	case 5:
211 		m_bg_scroll_x[1] &= 0xff00;
212 		m_bg_scroll_x[1] |= data;
213 		break;
214 	case 6:
215 		m_bg_scroll_y[1] = data;
216 		break;
217 	}
218 }
219 
220 
221 /*******************************************************************/
222 
decode_bg(const char * region)223 void tceptor_state::decode_bg(const char * region)
224 {
225 	static const gfx_layout bg_layout =
226 	{
227 		8, 8,
228 		2048,
229 		3,
230 		{ 0x40000+4, 0, 4 },
231 		{ 0, 1, 2, 3, 8, 9, 10, 11 },
232 		{ 0, 16, 32, 48, 64, 80, 96, 112 },
233 		128
234 	};
235 
236 	int gfx_index = m_bg;
237 	uint8_t *src = memregion(region)->base() + 0x8000;
238 	int len = 0x8000;
239 	int i;
240 
241 	std::vector<uint8_t> buffer(len);
242 
243 	/* expand rom tc2-19.10d */
244 	for (i = 0; i < len / 2; i++)
245 	{
246 		buffer[i*2+1] = src[i] & 0x0f;
247 		buffer[i*2] = (src[i] & 0xf0) >> 4;
248 	}
249 
250 	memcpy(src, &buffer[0], len);
251 
252 	/* decode the graphics */
253 	m_gfxdecode->set_gfx(gfx_index, std::make_unique<gfx_element>(m_palette, bg_layout, memregion(region)->base(), 0, 64, 0x0a00));
254 }
255 
decode_sprite(int gfx_index,const gfx_layout * layout,const void * data)256 void tceptor_state::decode_sprite(int gfx_index, const gfx_layout *layout, const void *data)
257 {
258 	/* decode the graphics */
259 	m_gfxdecode->set_gfx(gfx_index, std::make_unique<gfx_element>(m_palette, *layout, (const uint8_t *)data, 0, 64, 1024));
260 }
261 
262 // fix sprite order
decode_sprite16(const char * region)263 void tceptor_state::decode_sprite16(const char * region)
264 {
265 	static const gfx_layout spr16_layout =
266 	{
267 		16, 16,
268 		512,
269 		4,
270 		{ 0x00000, 0x00004, 0x40000, 0x40004 },
271 		{
272 			0*8, 0*8+1, 0*8+2, 0*8+3, 1*8, 1*8+1, 1*8+2, 1*8+3,
273 			2*8, 2*8+1, 2*8+2, 2*8+3, 3*8, 3*8+1, 3*8+2, 3*8+3
274 		},
275 		{
276 				0*2*16,  1*2*16,  2*2*16,  3*2*16,  4*2*16,  5*2*16,  6*2*16,  7*2*16,
277 				8*2*16,  9*2*16, 10*2*16, 11*2*16, 12*2*16, 13*2*16, 14*2*16, 15*2*16
278 		},
279 		2*16*16
280 	};
281 
282 	uint8_t *src = memregion(region)->base();
283 	int len = memregion(region)->bytes();
284 	int i, y;
285 
286 	m_decoded_16 = std::make_unique<uint8_t[]>(len);
287 
288 	for (i = 0; i < len / (4*4*16); i++)
289 		for (y = 0; y < 16; y++)
290 		{
291 			memcpy(&m_decoded_16[(i*4 + 0) * (2*16*16/8) + y * (2*16/8)],
292 					&src[i * (2*32*32/8) + y * (2*32/8)],
293 					4);
294 			memcpy(&m_decoded_16[(i*4 + 1) * (2*16*16/8) + y * (2*16/8)],
295 					&src[i * (2*32*32/8) + y * (2*32/8) + (4*8/8)],
296 					4);
297 			memcpy(&m_decoded_16[(i*4 + 2) * (2*16*16/8) + y * (2*16/8)],
298 					&src[i * (2*32*32/8) + y * (2*32/8) + (16*2*32/8)],
299 					4);
300 			memcpy(&m_decoded_16[(i*4 + 3) * (2*16*16/8) + y * (2*16/8)],
301 					&src[i * (2*32*32/8) + y * (2*32/8) + (4*8/8) + (16*2*32/8)],
302 					4);
303 		}
304 
305 	decode_sprite(m_sprite16, &spr16_layout, m_decoded_16.get());
306 }
307 
308 // fix sprite order
decode_sprite32(const char * region)309 void tceptor_state::decode_sprite32(const char * region)
310 {
311 	static const gfx_layout spr32_layout =
312 	{
313 		32, 32,
314 		1024,
315 		4,
316 		{ 0x000000, 0x000004, 0x200000, 0x200004 },
317 		{
318 			0*8, 0*8+1, 0*8+2, 0*8+3, 1*8, 1*8+1, 1*8+2, 1*8+3,
319 			2*8, 2*8+1, 2*8+2, 2*8+3, 3*8, 3*8+1, 3*8+2, 3*8+3,
320 			4*8, 4*8+1, 4*8+2, 4*8+3, 5*8, 5*8+1, 5*8+2, 5*8+3,
321 			6*8, 6*8+1, 6*8+2, 6*8+3, 7*8, 7*8+1, 7*8+2, 7*8+3
322 		},
323 		{
324 				0*2*32,  1*2*32,  2*2*32,  3*2*32,  4*2*32,  5*2*32,  6*2*32,  7*2*32,
325 				8*2*32,  9*2*32, 10*2*32, 11*2*32, 12*2*32, 13*2*32, 14*2*32, 15*2*32,
326 			16*2*32, 17*2*32, 18*2*32, 19*2*32, 20*2*32, 21*2*32, 22*2*32, 23*2*32,
327 			24*2*32, 25*2*32, 26*2*32, 27*2*32, 28*2*32, 29*2*32, 30*2*32, 31*2*32
328 		},
329 		2*32*32
330 	};
331 
332 	uint8_t *src = memregion(region)->base();
333 	int len = memregion(region)->bytes();
334 	int total = spr32_layout.total;
335 	int size = spr32_layout.charincrement / 8;
336 	int i;
337 
338 	m_decoded_32 = make_unique_clear<uint8_t[]>(len);
339 
340 	for (i = 0; i < total; i++)
341 	{
342 		int code;
343 
344 		code = (i & 0x07f) | ((i & 0x180) << 1) | 0x80;
345 		code &= ~((i & 0x200) >> 2);
346 
347 		memcpy(&m_decoded_32[size * (i + 0)],     &src[size * (code + 0)],     size);
348 		memcpy(&m_decoded_32[size * (i + total)], &src[size * (code + total)], size);
349 	}
350 
351 	decode_sprite(m_sprite32, &spr32_layout, m_decoded_32.get());
352 }
353 
video_start()354 void tceptor_state::video_start()
355 {
356 	int gfx_index;
357 
358 	m_sprite_ram_buffered = make_unique_clear<uint16_t[]>(0x200/2);
359 
360 	/* find first empty slot to decode gfx */
361 	for (gfx_index = 0; gfx_index < MAX_GFX_ELEMENTS; gfx_index++)
362 		if (m_gfxdecode->gfx(gfx_index) == nullptr)
363 			break;
364 	assert(gfx_index + 4 <= MAX_GFX_ELEMENTS);
365 
366 	m_bg = gfx_index++;
367 	decode_bg("gfx2");
368 
369 	m_sprite16 = gfx_index++;
370 	decode_sprite16("gfx3");
371 
372 	m_sprite32 = gfx_index++;
373 	decode_sprite32("gfx4");
374 
375 	/* allocate temp bitmaps */
376 	m_screen->register_screen_bitmap(m_temp_bitmap);
377 
378 	m_c45_road->set_transparent_color(m_palette->pen_indirect(0xfff));
379 
380 	m_tx_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tceptor_state::get_tx_tile_info)), TILEMAP_SCAN_COLS, 8, 8, 34, 28);
381 
382 	m_tx_tilemap->set_scrollx(0, -2*8);
383 	m_tx_tilemap->set_scrolly(0, 0);
384 	m_tx_tilemap->configure_groups(*m_gfxdecode->gfx(0), 7);
385 
386 	m_bg_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tceptor_state::get_bg1_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32);
387 	m_bg_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tceptor_state::get_bg2_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32);
388 
389 	save_pointer(NAME(m_sprite_ram_buffered), 0x200 / 2);
390 	save_item(NAME(m_bg_scroll_x[0]));
391 	save_item(NAME(m_bg_scroll_y[0]));
392 	save_item(NAME(m_bg_scroll_x[1]));
393 	save_item(NAME(m_bg_scroll_y[1]));
394 }
395 
396 
397 /*******************************************************************/
398 
399 /*
400     Sprite data format
401 
402     000: zzzzzzBB BTTTTTTT
403     002: ZZZZZZPP PPCCCCCC
404     100: fFL---YY YYYYYYYY
405     102: ------XX XXXXXXXX
406 
407     B: bank
408     T: number
409     P: priority
410     C: color
411     X: x
412     Y: y
413     L: large sprite
414     F: flip x
415     f: flip y
416     Z: zoom x
417     z: zoom y
418 */
419 
draw_sprites(bitmap_ind16 & bitmap,const rectangle & cliprect,int sprite_priority)420 void tceptor_state::draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect, int sprite_priority)
421 {
422 	uint16_t *mem1 = &m_sprite_ram_buffered[0x000/2];
423 	uint16_t *mem2 = &m_sprite_ram_buffered[0x100/2];
424 	int need_mask = 0;
425 	int i;
426 
427 	for (i = 0; i < 0x100; i += 2)
428 	{
429 		int scalex = (mem1[1 + i] & 0xfc00) << 1;
430 		int scaley = (mem1[0 + i] & 0xfc00) << 1;
431 		int pri = 7 - ((mem1[1 + i] & 0x3c0) >> 6);
432 
433 		if (pri == sprite_priority && scalex && scaley)
434 		{
435 			int x = mem2[1 + i] & 0x3ff;
436 			int y = 512 - (mem2[0 + i] & 0x3ff);
437 			int flipx = mem2[0 + i] & 0x4000;
438 			int flipy = mem2[0 + i] & 0x8000;
439 			int color = mem1[1 + i] & 0x3f;
440 			int gfx;
441 			int code;
442 
443 			if (mem2[0 + i] & 0x2000)
444 			{
445 				gfx = m_sprite32;
446 				code = mem1[0 + i] & 0x3ff;
447 
448 			}
449 			else
450 			{
451 				gfx = m_sprite16;
452 				code = mem1[0 + i] & 0x1ff;
453 				scaley *= 2;
454 			}
455 
456 			if (m_is_mask_spr[color])
457 			{
458 				if (!need_mask)
459 					// backup previous bitmap
460 					copybitmap(m_temp_bitmap, bitmap, 0, 0, 0, 0, cliprect);
461 
462 				need_mask = 1;
463 			}
464 
465 			// round off
466 			scalex += 0x800;
467 			scaley += 0x800;
468 
469 			x -= 64;
470 			y -= 78;
471 
472 
473 						m_gfxdecode->gfx(gfx)->zoom_transmask(bitmap,
474 						cliprect,
475 						code,
476 						color,
477 						flipx, flipy,
478 						x, y,
479 						scalex,
480 						scaley,
481 						m_palette->transpen_mask(*m_gfxdecode->gfx(gfx), color, SPR_TRANS_COLOR));
482 		}
483 	}
484 
485 	/* if SPR_MASK_COLOR pen is used, restore pixels from previous bitmap */
486 	if (need_mask)
487 	{
488 		for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
489 			for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
490 				if (m_palette->pen_indirect(bitmap.pix(y, x)) == SPR_MASK_COLOR)
491 					// restore pixel
492 					bitmap.pix(y, x) = m_temp_bitmap.pix(y, x);
493 	}
494 }
495 
496 
screen_update_tceptor(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)497 uint32_t tceptor_state::screen_update_tceptor(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
498 {
499 	rectangle rect;
500 	int pri;
501 	int bg_center = 144 - ((((m_bg_scroll_x[0] + m_bg_scroll_x[1] ) & 0x1ff) - 288) / 2);
502 
503 	// left background
504 	rect = cliprect;
505 	rect.max_x = bg_center;
506 	m_bg_tilemap[0]->set_scrollx(0, m_bg_scroll_x[0] + 12);
507 	m_bg_tilemap[0]->set_scrolly(0, m_bg_scroll_y[0] + 20); //32?
508 	m_bg_tilemap[0]->draw(screen, bitmap, rect, 0, 0);
509 
510 	// right background
511 	rect.min_x = bg_center;
512 	rect.max_x = cliprect.max_x;
513 	m_bg_tilemap[1]->set_scrollx(0, m_bg_scroll_x[1] + 20);
514 	m_bg_tilemap[1]->set_scrolly(0, m_bg_scroll_y[1] + 20); // 32?
515 	m_bg_tilemap[1]->draw(screen, bitmap, rect, 0, 0);
516 
517 	for (pri = 0; pri < 8; pri++)
518 	{
519 		m_c45_road->draw(bitmap, cliprect, pri * 2);
520 		m_c45_road->draw(bitmap, cliprect, pri * 2 + 1);
521 		draw_sprites(bitmap, cliprect, pri);
522 	}
523 
524 	m_tx_tilemap->draw(screen, bitmap, cliprect, 0, 0);
525 	return 0;
526 }
527 
WRITE_LINE_MEMBER(tceptor_state::screen_vblank_tceptor)528 WRITE_LINE_MEMBER(tceptor_state::screen_vblank_tceptor)
529 {
530 	// rising edge
531 	if (state)
532 	{
533 		memcpy(m_sprite_ram_buffered.get(), m_sprite_ram, 0x200);
534 
535 		if (m_m6809_irq_enable)
536 			m_maincpu->set_input_line(0, HOLD_LINE);
537 		else
538 			m_m6809_irq_enable = 1;
539 
540 		if (m_m68k_irq_enable)
541 			m_subcpu->set_input_line(1, HOLD_LINE);
542 
543 		if (m_mcu_irq_enable)
544 			m_mcu->set_input_line(0, HOLD_LINE);
545 		else
546 			m_mcu_irq_enable = 1;
547 	}
548 }
549 
tceptor2_shutter_w(uint8_t data)550 void tceptor_state::tceptor2_shutter_w(uint8_t data)
551 {
552 	// 3D scope shutter control
553 	m_shutter = BIT(data, 0);
554 }
555