1 // license:BSD-3-Clause
2 // copyright-holders:Nicola Salmoria, Lee Taylor
3 /***************************************************************************
4
5 cosmic.cpp
6
7 emulation of video hardware of cosmic machines of 1979-1980(ish)
8
9 ***************************************************************************/
10
11 #include "emu.h"
12 #include "includes/cosmic.h"
13
14
cosmic_color_register_w(offs_t offset,uint8_t data)15 void cosmic_state::cosmic_color_register_w(offs_t offset, uint8_t data)
16 {
17 m_color_registers[offset] = data ? 1 : 0;
18 }
19
20
panic_map_color(uint8_t x,uint8_t y)21 pen_t cosmic_state::panic_map_color( uint8_t x, uint8_t y )
22 {
23 offs_t offs = (m_color_registers[0] << 9) | (m_color_registers[2] << 10) | ((x >> 4) << 5) | (y >> 3);
24 pen_t pen = memregion("user1")->base()[offs];
25
26 if (m_color_registers[1])
27 pen >>= 4;
28
29 return pen & 0x0f;
30 }
31
cosmica_map_color(uint8_t x,uint8_t y)32 pen_t cosmic_state::cosmica_map_color( uint8_t x, uint8_t y )
33 {
34 offs_t offs = (m_color_registers[0] << 9) | ((x >> 4) << 5) | (y >> 3);
35 pen_t pen = memregion("user1")->base()[offs];
36
37 if (m_color_registers[1]) // 0 according to the schematics, but that breaks alien formation colors
38 pen >>= 4;
39
40 return pen & 0x07;
41 }
42
cosmicg_map_color(uint8_t x,uint8_t y)43 pen_t cosmic_state::cosmicg_map_color( uint8_t x, uint8_t y )
44 {
45 offs_t offs = (m_color_registers[0] << 8) | (m_color_registers[1] << 9) | ((y >> 4) << 4) | (x >> 4);
46 pen_t pen = memregion("user1")->base()[offs];
47
48 /* the upper 4 bits are for cocktail mode support */
49 return pen & 0x0f;
50 }
51
magspot_map_color(uint8_t x,uint8_t y)52 pen_t cosmic_state::magspot_map_color( uint8_t x, uint8_t y )
53 {
54 offs_t offs = (m_color_registers[0] << 9) | ((x >> 3) << 4) | (y >> 4);
55 pen_t pen = memregion("user1")->base()[offs];
56
57 if (m_color_registers[1])
58 pen >>= 4;
59
60 return pen & m_magspot_pen_mask;
61 }
62
63
64
65 /*
66 * Panic Color table setup
67 *
68 * Bit 0 = RED, Bit 1 = GREEN, Bit 2 = BLUE
69 *
70 * First 8 colors are normal intensities
71 *
72 * But, bit 3 can be used to pull Blue via a 2k resistor to 5v
73 * (1k to ground) so second version of table has blue set to 2/3
74 */
75
panic_palette(palette_device & palette)76 void cosmic_state::panic_palette(palette_device &palette)
77 {
78 uint8_t const *const color_prom = memregion("proms")->base();
79
80 // create a lookup table for the palette
81 for (int i = 0; i < 0x10; i++)
82 {
83 int const r = pal1bit(i >> 0);
84 int const g = pal1bit(i >> 1);
85 int const b = ((i & 0x0c) == 0x08) ? 0xaa : pal1bit(i >> 2);
86
87 palette.set_indirect_color(i, rgb_t(r, g, b));
88 }
89
90 // background uses colors 0x00-0x0f
91 for (int i = 0; i < 0x0f; i++)
92 palette.set_pen_indirect(i, i);
93
94 // sprites use colors 0x00-0x07
95 for (int i = 0; i < 0x20; i++)
96 palette.set_pen_indirect(i + 0x10, color_prom[i] & 0x07);
97
98 m_map_color = &cosmic_state::panic_map_color;
99 }
100
101
102 /*
103 * Cosmic Alien Color table setup
104 *
105 * 8 colors, 16 sprite color codes
106 *
107 * Bit 0 = RED, Bit 1 = GREEN, Bit 2 = BLUE
108 *
109 */
110
cosmica_palette(palette_device & palette)111 void cosmic_state::cosmica_palette(palette_device &palette)
112 {
113 uint8_t const *const color_prom = memregion("proms")->base();
114
115 // create a lookup table for the palette
116 for (int i = 0; i < 0x08; i++)
117 palette.set_indirect_color(i, rgb_t(pal1bit(i >> 0), pal1bit(i >> 1), pal1bit(i >> 2)));
118
119 // background and sprites use colors 0x00-0x07
120 for (int i = 0; i < 0x08; i++)
121 palette.set_pen_indirect(i, i);
122
123 for (int i = 0; i < 0x20; i++)
124 {
125 palette.set_pen_indirect(i + 0x08, (color_prom[i] >> 0) & 0x07);
126 palette.set_pen_indirect(i + 0x28, (color_prom[i] >> 4) & 0x07);
127 }
128
129 m_map_color = &cosmic_state::cosmica_map_color;
130 }
131
132
133 /*
134 * Cosmic guerilla table setup
135 *
136 * Use AA for normal, FF for Full Red
137 * Bit 0 = R, bit 1 = G, bit 2 = B, bit 4 = High Red
138 *
139 * It's possible that the background is dark gray and not black, as the
140 * resistor chain would never drop to zero, Anybody know ?
141 */
cosmicg_palette(palette_device & palette)142 void cosmic_state::cosmicg_palette(palette_device &palette)
143 {
144 for (int i = 0; i < palette.entries(); i++)
145 {
146 int const r = (i > 8) ? 0xff : 0xaa * BIT(i, 0);
147 int const g = 0xaa * BIT(i, 1);
148 int const b = 0xaa * BIT(i, 2);
149
150 palette.set_pen_color(i, rgb_t(r, g, b));
151 }
152
153 m_map_color = &cosmic_state::cosmicg_map_color;
154 }
155
156
magspot_palette(palette_device & palette)157 void cosmic_state::magspot_palette(palette_device &palette)
158 {
159 uint8_t const *const color_prom = memregion("proms")->base();
160
161 // create a lookup table for the palette
162 for (int i = 0; i < 0x10; i++)
163 {
164 int const r = ((i & 0x09) == 0x08) ? 0xaa : pal1bit(i >> 0);
165 int const g = pal1bit(i >> 1);
166 int const b = pal1bit(i >> 2);
167
168 palette.set_indirect_color(i, rgb_t(r, g, b));
169 }
170
171 // background uses colors 0x00-0x0f
172 for (int i = 0; i < 0x0f; i++)
173 palette.set_pen_indirect(i, i);
174
175 // sprites use colors 0x00-0x0f
176 for (int i = 0; i < 0x20; i++)
177 palette.set_pen_indirect(i + 0x10, color_prom[i] & 0x0f);
178
179 m_map_color = &cosmic_state::magspot_map_color;
180 m_magspot_pen_mask = 0x0f;
181 }
182
183
nomnlnd_palette(palette_device & palette)184 void cosmic_state::nomnlnd_palette(palette_device &palette)
185 {
186 uint8_t const *const color_prom = memregion("proms")->base();
187
188 // create a lookup table for the palette
189 for (int i = 0; i < 0x10; i++)
190 {
191 rgb_t const color = rgb_t(pal1bit(i >> 0), pal1bit(i >> 1), pal1bit(i >> 2));
192 palette.set_indirect_color(i, color);
193 }
194
195 // background uses colors 0x00-0x07
196 for (int i = 0; i < 0x07; i++)
197 palette.set_pen_indirect(i, i);
198
199 // sprites use colors 0x00-0x07 */
200 for (int i = 0; i < 0x20; i++)
201 palette.set_pen_indirect(i + 0x10, color_prom[i] & 0x07);
202
203 m_map_color = &cosmic_state::magspot_map_color;
204 m_magspot_pen_mask = 0x07;
205 }
206
207
cosmic_background_enable_w(uint8_t data)208 void cosmic_state::cosmic_background_enable_w(uint8_t data)
209 {
210 m_background_enable = data;
211 }
212
213
draw_bitmap(bitmap_ind16 & bitmap,const rectangle & cliprect)214 void cosmic_state::draw_bitmap( bitmap_ind16 &bitmap, const rectangle &cliprect )
215 {
216 for (offs_t offs = 0; offs < m_videoram.bytes(); offs++)
217 {
218 uint8_t data = m_videoram[offs];
219
220 uint8_t x = offs << 3;
221 uint8_t const y = offs >> 5;
222
223 pen_t pen = (this->*m_map_color)(x, y);
224
225 for (int i = 0; i < 8; i++)
226 {
227 if (data & 0x80)
228 {
229 if (flip_screen())
230 bitmap.pix(255-y, 255-x) = pen;
231 else
232 bitmap.pix(y, x) = pen;
233 }
234
235 x++;
236 data <<= 1;
237 }
238 }
239 }
240
241
draw_sprites(bitmap_ind16 & bitmap,const rectangle & cliprect,int color_mask,int extra_sprites)242 void cosmic_state::draw_sprites( bitmap_ind16 &bitmap, const rectangle &cliprect, int color_mask, int extra_sprites )
243 {
244 int offs;
245
246 for (offs = m_spriteram.bytes() - 4;offs >= 0;offs -= 4)
247 {
248 if (m_spriteram[offs] != 0)
249 {
250 int code, color;
251
252 code = ~m_spriteram[offs] & 0x3f;
253 color = ~m_spriteram[offs + 3] & color_mask;
254
255 if (extra_sprites)
256 code |= (m_spriteram[offs + 3] & 0x08) << 3;
257
258 if (m_spriteram[offs] & 0x80)
259 /* 16x16 sprite */
260 m_gfxdecode->gfx(0)->transpen(bitmap,cliprect,
261 code, color,
262 0, ~m_spriteram[offs] & 0x40,
263 256-m_spriteram[offs + 2],m_spriteram[offs + 1],0);
264 else
265 /* 32x32 sprite */
266 m_gfxdecode->gfx(1)->transpen(bitmap,cliprect,
267 code >> 2, color,
268 0, ~m_spriteram[offs] & 0x40,
269 256-m_spriteram[offs + 2],m_spriteram[offs + 1],0);
270 }
271 }
272 }
273
274
cosmica_draw_starfield(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)275 void cosmic_state::cosmica_draw_starfield( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect )
276 {
277 uint8_t y = 0;
278 uint8_t map = 0;
279 uint8_t *PROM = memregion("user2")->base();
280
281 while (1)
282 {
283 int va = y & 0x01;
284 int vb = (y >> 1) & 0x01;
285
286 uint8_t x = 0;
287
288 while (1)
289 {
290 uint8_t x1;
291 int hc, hb_;
292
293 if (flip_screen())
294 x1 = x - screen.frame_number();
295 else
296 x1 = x + screen.frame_number();
297
298 hc = (x1 >> 2) & 0x01;
299 hb_ = (x >> 5) & 0x01; /* not a bug, this one is the real x */
300
301 if ((x1 & 0x1f) == 0)
302 // flip-flop at IC11 is clocked
303 map = PROM[(x1 >> 5) | (y >> 1 << 3)];
304
305 if (((!(hc & va)) & (vb ^ hb_)) && /* right network */
306 (((x1 ^ map) & (hc | 0x1e)) == 0x1e)) /* left network */
307 {
308 /* RGB order is reversed -- bit 7=R, 6=G, 5=B */
309 int col = (map >> 7) | ((map >> 5) & 0x02) | ((map >> 3) & 0x04);
310
311 bitmap.pix(y, x) = col;
312 }
313
314 x++;
315 if (x == 0) break;
316 }
317
318 y++;
319 if (y == 0) break;
320 }
321 }
322
323
devzone_draw_grid(bitmap_ind16 & bitmap,const rectangle & cliprect)324 void cosmic_state::devzone_draw_grid( bitmap_ind16 &bitmap, const rectangle &cliprect )
325 {
326 uint8_t const *const horz_PROM = memregion("user2")->base();
327 uint8_t const *const vert_PROM = memregion("user3")->base();
328 offs_t horz_addr = 0;
329
330 uint8_t count = 0;
331 uint8_t horz_data = 0;
332 uint8_t vert_data;
333
334 for (int y = 32; y < 224; y++)
335 {
336 uint8_t x = 0;
337
338 while (1)
339 {
340 /* for the vertical lines, each bit indicates
341 if there should be a line at the x position */
342 vert_data = vert_PROM[x >> 3];
343
344 /* the horizontal (perspective) lines are RLE encoded.
345 When the screen is flipped, the address should be
346 decrementing. But since it's just a mirrored image,
347 this is easier. */
348 if (count == 0)
349 count = horz_PROM[horz_addr++];
350
351 count++;
352
353 if (count == 0)
354 horz_data = horz_PROM[horz_addr++];
355
356 for (int x1 = 0; x1 < 8; x1++)
357 {
358 if (!(vert_data & horz_data & 0x80)) /* NAND gate */
359 {
360 /* blue */
361 if (flip_screen())
362 bitmap.pix(255-y, 255-x) = 4;
363 else
364 bitmap.pix(y, x) = 4;
365 }
366
367 horz_data = (horz_data << 1) | 0x01;
368 vert_data = (vert_data << 1) | 0x01;
369
370 x++;
371 }
372
373 if (x == 0) break;
374 }
375 }
376 }
377
378
nomnlnd_draw_background(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)379 void cosmic_state::nomnlnd_draw_background( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect )
380 {
381 uint8_t y = 0;
382 uint8_t water = screen.frame_number();
383 uint8_t *PROM = memregion("user2")->base();
384
385 /* all positioning is via logic gates:
386
387 tree is displayed where
388 __ __
389 HD' ^ HC' ^ HB'
390
391 and
392 __ __ __
393 (VB' ^ VC' ^ VD') X (VB' ^ VC' ^ VD')
394
395 water is displayed where
396 __ __
397 HD' ^ HC' ^ HB ^ HA'
398
399 and vertically the same equation as the trees,
400 but the final result is inverted.
401
402
403 The colors are coded in logic gates:
404
405 trees:
406 P1 P2 BGR
407 R = Plane1 ^ Plane2 0 0 000
408 G = Plane2 0 1 010
409 B = Plane1 ^ ~Plane2 1 0 100
410 1 1 011
411 water:
412 P1 P2 BGR or
413 R = Plane1 ^ Plane2 0 0 100 000
414 G = Plane2 v Plane2 0 1 110 010
415 B = ~Plane1 or 1 0 010 010
416 0 based oh HD 1 1 011 011
417
418 Not sure about B, the logic seems convulated for such
419 a simple result.
420 */
421
422 while (1)
423 {
424 int vb_ = (y >> 5) & 0x01;
425 int vc_ = (y >> 6) & 0x01;
426 int vd_ = y >> 7;
427
428 uint8_t x = 0;
429
430 while (1)
431 {
432 int color = 0;
433
434 int hd = (x >> 3) & 0x01;
435 int ha_ = (x >> 4) & 0x01;
436 int hb_ = (x >> 5) & 0x01;
437 int hc_ = (x >> 6) & 0x01;
438 int hd_ = x >> 7;
439
440 if (((!vb_) & vc_ & (!vd_)) ^ (vb_ & (!vc_) & vd_))
441 {
442 /* tree */
443 if (!hd_ && hc_ && !hb_)
444 {
445 offs_t offs = ((x >> 3) & 0x03) | ((y & 0x1f) << 2) |
446 (flip_screen() ? 0x80 : 0);
447
448 uint8_t plane1 = PROM[offs ] << (x & 0x07);
449 uint8_t plane2 = PROM[offs | 0x0400] << (x & 0x07);
450
451 plane1 >>= 7;
452 plane2 >>= 7;
453
454 color = (plane1 & plane2) | // R
455 (plane2 ) << 1 | // G
456 (plane1 & ~plane2) << 2; // B
457 }
458 }
459 else
460 {
461 /* water */
462 if (hd_ && !hc_ && hb_ && !ha_)
463 {
464 offs_t offs = hd | (water << 1) | 0x0200;
465
466 uint8_t plane1 = PROM[offs ] << (x & 0x07);
467 uint8_t plane2 = PROM[offs | 0x0400] << (x & 0x07);
468
469 plane1 >>= 7;
470 plane2 >>= 7;
471
472 color = ( plane1 & plane2) | // R
473 ( plane1 | plane2) << 1 | // G
474 ((!plane1) & hd) << 2; // B - see above
475 }
476 }
477
478 if (color != 0)
479 {
480 if (flip_screen())
481 bitmap.pix(255-y, 255-x) = color;
482 else
483 bitmap.pix(y, x) = color;
484 }
485
486 x++;
487 if (x == 0) break;
488 }
489
490
491 // this is obviously wrong
492 // if (vb_)
493 water++;
494
495 y++;
496 if (y == 0) break;
497 }
498 }
499
500
screen_update_cosmicg(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)501 uint32_t cosmic_state::screen_update_cosmicg(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
502 {
503 bitmap.fill(0, cliprect);
504 draw_bitmap(bitmap, cliprect);
505 return 0;
506 }
507
508
screen_update_panic(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)509 uint32_t cosmic_state::screen_update_panic(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
510 {
511 bitmap.fill(0, cliprect);
512 draw_bitmap(bitmap, cliprect);
513 draw_sprites(bitmap, cliprect, 0x07, 1);
514 return 0;
515 }
516
517
screen_update_cosmica(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)518 uint32_t cosmic_state::screen_update_cosmica(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
519 {
520 bitmap.fill(0, cliprect);
521 cosmica_draw_starfield(screen, bitmap, cliprect);
522 draw_bitmap(bitmap, cliprect);
523 draw_sprites(bitmap, cliprect, 0x0f, 0);
524 return 0;
525 }
526
527
screen_update_magspot(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)528 uint32_t cosmic_state::screen_update_magspot(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
529 {
530 bitmap.fill(0, cliprect);
531 draw_bitmap(bitmap, cliprect);
532 draw_sprites(bitmap, cliprect, 0x07, 0);
533 return 0;
534 }
535
536
screen_update_devzone(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)537 uint32_t cosmic_state::screen_update_devzone(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
538 {
539 bitmap.fill(0, cliprect);
540
541 if (m_background_enable)
542 devzone_draw_grid(bitmap, cliprect);
543
544 draw_bitmap(bitmap, cliprect);
545 draw_sprites(bitmap, cliprect, 0x07, 0);
546 return 0;
547 }
548
549
screen_update_nomnlnd(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)550 uint32_t cosmic_state::screen_update_nomnlnd(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
551 {
552 /* according to the video summation logic on pg4, the trees and river
553 have the highest priority */
554
555 bitmap.fill(0, cliprect);
556 draw_bitmap(bitmap, cliprect);
557 draw_sprites(bitmap, cliprect, 0x07, 0);
558
559 if (m_background_enable)
560 nomnlnd_draw_background(screen, bitmap, cliprect);
561
562 return 0;
563 }
564