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