summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/exidy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/video/exidy.c')
-rw-r--r--src/mame/video/exidy.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/src/mame/video/exidy.c b/src/mame/video/exidy.c
new file mode 100644
index 00000000000..a72cb0ea231
--- /dev/null
+++ b/src/mame/video/exidy.c
@@ -0,0 +1,502 @@
+/*************************************************************************
+
+ Exidy 6502 hardware
+
+*************************************************************************/
+
+#include "driver.h"
+#include "exidy.h"
+
+UINT8 *exidy_characterram;
+
+UINT8 exidy_collision_mask;
+UINT8 exidy_collision_invert;
+
+UINT8 *exidy_palette;
+UINT16 *exidy_colortable;
+
+static mame_bitmap *motion_object_1_vid;
+static mame_bitmap *motion_object_2_vid;
+static mame_bitmap *motion_object_2_clip;
+
+static UINT8 chardirty[256];
+static UINT8 update_complete;
+
+static UINT8 int_condition;
+
+static UINT8 spriteno;
+static UINT8 sprite_enable;
+static UINT8 sprite1_xpos;
+static UINT8 sprite1_ypos;
+static UINT8 sprite2_xpos;
+static UINT8 sprite2_ypos;
+
+static UINT8 color_latch[3];
+
+
+
+/*************************************
+ *
+ * Hard coded palettes
+ *
+ *************************************/
+
+/* Sidetrack/Targ/Spectar don't have a color PROM; colors are changed by the means of 8x3 */
+/* dip switches on the board. Here are the colors they map to. */
+UINT8 sidetrac_palette[PALETTE_LEN*3] =
+{
+ 0x00,0x00,0x00, /* BACKGND */
+ 0x00,0x00,0x00, /* CSPACE0 */
+ 0x00,0xff,0x00, /* CSPACE1 */
+ 0xff,0xff,0xff, /* CSPACE2 */
+ 0xff,0xff,0xff, /* CSPACE3 */
+ 0xff,0x00,0xff, /* 5LINES (unused?) */
+ 0xff,0xff,0x00, /* 5MO2VID */
+ 0xff,0xff,0xff /* 5MO1VID */
+};
+
+/* Targ has different colors */
+UINT8 targ_palette[PALETTE_LEN*3] =
+{
+ /* color use */
+ 0x00,0x00,0xff, /* blue background */
+ 0x00,0xff,0xff, /* cyan characters 192-255 */
+ 0xff,0xff,0x00, /* yellow characters 128-191 */
+ 0xff,0xff,0xff, /* white characters 64-127 */
+ 0xff,0x00,0x00, /* red characters 0- 63 */
+ 0x00,0xff,0xff, /* cyan not used */
+ 0xff,0xff,0xff, /* white bullet sprite */
+ 0x00,0xff,0x00, /* green wummel sprite */
+};
+
+/* Spectar has different colors */
+UINT8 spectar_palette[PALETTE_LEN*3] =
+{
+ /* color use */
+ 0x00,0x00,0xff, /* blue background */
+ 0x00,0xff,0x00, /* green characters 192-255 */
+ 0x00,0xff,0x00, /* green characters 128-191 */
+ 0xff,0xff,0xff, /* white characters 64-127 */
+ 0xff,0x00,0x00, /* red characters 0- 63 */
+ 0x00,0xff,0x00, /* green not used */
+ 0xff,0xff,0x00, /* yellow bullet sprite */
+ 0x00,0xff,0x00, /* green wummel sprite */
+};
+
+
+
+/*************************************
+ *
+ * Hard coded color tables
+ *
+ *************************************/
+
+UINT16 exidy_1bpp_colortable[COLORTABLE_LEN] =
+{
+ /* one-bit characters */
+ 0, 4, /* chars 0x00-0x3F */
+ 0, 3, /* chars 0x40-0x7F */
+ 0, 2, /* chars 0x80-0xBF */
+ 0, 1, /* chars 0xC0-0xFF */
+
+ /* Motion Object 1 */
+ 0, 7,
+
+ /* Motion Object 2 */
+ 0, 6,
+};
+
+UINT16 exidy_2bpp_colortable[COLORTABLE_LEN] =
+{
+ /* two-bit characters */
+ /* (Because this is 2-bit color, the colorspace is only divided
+ in half instead of in quarters. That's why 00-3F = 40-7F and
+ 80-BF = C0-FF) */
+ 0, 0, 4, 3, /* chars 0x00-0x3F */
+ 0, 0, 4, 3, /* chars 0x40-0x7F */
+ 0, 0, 2, 1, /* chars 0x80-0xBF */
+ 0, 0, 2, 1, /* chars 0xC0-0xFF */
+
+ /* Motion Object 1 */
+ 0, 7,
+
+ /* Motion Object 2 */
+ 0, 6,
+};
+
+
+
+/*************************************
+ *
+ * Palettes and colors
+ *
+ *************************************/
+
+PALETTE_INIT( exidy )
+{
+ if (exidy_palette)
+ {
+ int i;
+
+ for (i = 0; i < PALETTE_LEN; i++)
+ palette_set_color_rgb(machine,i,exidy_palette[i*3+0],exidy_palette[i*3+1],exidy_palette[i*3+2]);
+ }
+ memcpy(colortable, exidy_colortable, COLORTABLE_LEN * sizeof(colortable[0]));
+}
+
+
+
+/*************************************
+ *
+ * Video startup
+ *
+ *************************************/
+
+VIDEO_START( exidy )
+{
+ video_start_generic(machine);
+
+ motion_object_1_vid = auto_bitmap_alloc(16, 16, machine->screen[0].format);
+ motion_object_2_vid = auto_bitmap_alloc(16, 16, machine->screen[0].format);
+ motion_object_2_clip = auto_bitmap_alloc(16, 16, machine->screen[0].format);
+}
+
+
+
+/*************************************
+ *
+ * Interrupt generation
+ *
+ *************************************/
+
+INLINE void latch_condition(int collision)
+{
+ collision ^= exidy_collision_invert;
+ int_condition = (readinputportbytag("INTSOURCE") & ~0x1c) | (collision & exidy_collision_mask);
+}
+
+
+INTERRUPT_GEN( exidy_vblank_interrupt )
+{
+ /* latch the current condition */
+ latch_condition(0);
+ int_condition &= ~0x80;
+
+ /* set the IRQ line */
+ cpunum_set_input_line(0, 0, ASSERT_LINE);
+}
+
+
+INTERRUPT_GEN( teetert_vblank_interrupt )
+{
+ /* standard stuff */
+ if (cpu_getiloops() == 0)
+ exidy_vblank_interrupt();
+
+ /* plus a pulse on the NMI line */
+ cpunum_set_input_line(0, INPUT_LINE_NMI, PULSE_LINE);
+}
+
+
+READ8_HANDLER( exidy_interrupt_r )
+{
+ /* clear any interrupts */
+ cpunum_set_input_line(0, 0, CLEAR_LINE);
+
+ /* return the latched condition */
+ return int_condition;
+}
+
+
+
+/*************************************
+ *
+ * Character RAM
+ *
+ *************************************/
+
+WRITE8_HANDLER( exidy_characterram_w )
+{
+ if (exidy_characterram[offset] != data)
+ {
+ exidy_characterram[offset] = data;
+ chardirty[offset / 8 % 256] = 1;
+ }
+}
+
+
+
+/*************************************
+ *
+ * Palette RAM
+ *
+ *************************************/
+
+WRITE8_HANDLER( exidy_color_w )
+{
+ int i;
+
+ color_latch[offset] = data;
+
+ for (i = 0; i < 8; i++)
+ palette_set_color_rgb(Machine, i, pal1bit(color_latch[2] >> i), pal1bit(color_latch[1] >> i), pal1bit(color_latch[0] >> i));
+}
+
+
+
+/*************************************
+ *
+ * Sprite controls
+ *
+ *************************************/
+
+WRITE8_HANDLER( exidy_sprite1_xpos_w )
+{
+ sprite1_xpos = data;
+}
+
+WRITE8_HANDLER( exidy_sprite1_ypos_w )
+{
+ sprite1_ypos = data;
+}
+
+WRITE8_HANDLER( exidy_sprite2_xpos_w )
+{
+ sprite2_xpos = data;
+}
+
+WRITE8_HANDLER( exidy_sprite2_ypos_w )
+{
+ sprite2_ypos = data;
+}
+
+WRITE8_HANDLER( exidy_spriteno_w )
+{
+ spriteno = data;
+}
+
+WRITE8_HANDLER( exidy_sprite_enable_w )
+{
+ sprite_enable = data;
+}
+
+
+
+/*************************************
+ *
+ * Background update
+ *
+ *************************************/
+
+static void update_background(running_machine *machine)
+{
+ int x, y, offs;
+
+ /* update the background and any dirty characters in it */
+ for (y = offs = 0; y < 32; y++)
+ for (x = 0; x < 32; x++, offs++)
+ {
+ int code = videoram[offs];
+
+ /* see if the character is dirty */
+ if (chardirty[code] == 1)
+ {
+ decodechar(machine->gfx[0], code, exidy_characterram, machine->drv->gfxdecodeinfo[0].gfxlayout);
+ chardirty[code] = 2;
+ }
+
+ /* see if the bitmap is dirty */
+ if (dirtybuffer[offs] || chardirty[code])
+ {
+ int color = code >> 6;
+ drawgfx(tmpbitmap, machine->gfx[0], code, color, 0, 0, x * 8, y * 8, NULL, TRANSPARENCY_NONE, 0);
+ dirtybuffer[offs] = 0;
+ }
+ }
+
+ /* reset the char dirty array */
+ for (y = 0; y < 256; y++)
+ if (chardirty[y] == 2)
+ chardirty[y] = 0;
+}
+
+
+
+static TIMER_CALLBACK( collision_irq_callback )
+{
+ /* latch the collision bits */
+ latch_condition(param);
+
+ /* set the IRQ line */
+ cpunum_set_input_line(0, 0, ASSERT_LINE);
+}
+
+
+
+/*************************************
+ *
+ * End-of-frame callback
+ *
+ *************************************/
+
+/***************************************************************************
+
+ Exidy hardware checks for two types of collisions based on the video
+ signals. If the Motion Object 1 and Motion Object 2 signals are on at
+ the same time, an M1M2 collision bit gets set. If the Motion Object 1
+ and Background Character signals are on at the same time, an M1CHAR
+ collision bit gets set. So effectively, there's a pixel-by-pixel
+ collision check comparing Motion Object 1 (the player) to the
+ background and to the other Motion Object (typically a bad guy).
+
+***************************************************************************/
+
+INLINE int sprite_1_enabled(void)
+{
+ /* if the exidy_collision_mask is 0x00, then we are on old hardware that always has */
+ /* sprite 1 enabled regardless */
+ return (!(sprite_enable & 0x80) || (sprite_enable & 0x10) || (exidy_collision_mask == 0x00));
+}
+
+INLINE int sprite_2_enabled(void)
+{
+ return (!(sprite_enable & 0x40));
+}
+
+VIDEO_EOF( exidy )
+{
+ UINT8 enable_set = ((sprite_enable & 0x20) != 0);
+ static const rectangle clip = { 0, 15, 0, 15 };
+ int pen0 = machine->pens[0];
+ int org_1_x = 0, org_1_y = 0;
+ int org_2_x = 0, org_2_y = 0;
+ int sx, sy;
+ int count = 0;
+
+ /* if there is nothing to detect, bail */
+ if (exidy_collision_mask == 0)
+ return;
+
+ /* if the sprites aren't enabled, we can't collide */
+ if (!sprite_1_enabled() && !sprite_2_enabled())
+ {
+ update_complete = 0;
+ return;
+ }
+
+ /* update the background if necessary */
+ if (!update_complete)
+ update_background(machine);
+ update_complete = 0;
+
+ /* draw sprite 1 */
+ if (sprite_1_enabled())
+ {
+ org_1_x = 236 - sprite1_xpos - 4;
+ org_1_y = 244 - sprite1_ypos - 4;
+ drawgfx(motion_object_1_vid, machine->gfx[1],
+ (spriteno & 0x0f) + 16 * enable_set, 0,
+ 0, 0, 0, 0, &clip, TRANSPARENCY_NONE, 0);
+ }
+ else
+ fillbitmap(motion_object_1_vid, pen0, &clip);
+
+ /* draw sprite 2 */
+ if (sprite_2_enabled())
+ {
+ org_2_x = 236 - sprite2_xpos - 4;
+ org_2_y = 244 - sprite2_ypos - 4;
+ drawgfx(motion_object_2_vid, machine->gfx[1],
+ ((spriteno >> 4) & 0x0f) + 32, 0,
+ 0, 0, 0, 0, &clip, TRANSPARENCY_NONE, 0);
+ }
+ else
+ fillbitmap(motion_object_2_vid, pen0, &clip);
+
+ /* draw sprite 2 clipped to sprite 1's location */
+ fillbitmap(motion_object_2_clip, pen0, &clip);
+ if (sprite_1_enabled() && sprite_2_enabled())
+ {
+ sx = org_2_x - org_1_x;
+ sy = org_2_y - org_1_y;
+ drawgfx(motion_object_2_clip, machine->gfx[1],
+ ((spriteno >> 4) & 0x0f) + 32, 0,
+ 0, 0, sx, sy, &clip, TRANSPARENCY_NONE, 0);
+ }
+
+ /* scan for collisions */
+ for (sy = 0; sy < 16; sy++)
+ for (sx = 0; sx < 16; sx++)
+ {
+ if (*BITMAP_ADDR16(motion_object_1_vid, sy, sx) != pen0)
+ {
+ UINT8 collision_mask = 0;
+
+ /* check for background collision (M1CHAR) */
+ if (*BITMAP_ADDR16(tmpbitmap, org_1_y + sy, org_1_x + sx) != pen0)
+ collision_mask |= 0x04;
+
+ /* check for motion object collision (M1M2) */
+ if (*BITMAP_ADDR16(motion_object_2_clip, sy, sx) != pen0)
+ collision_mask |= 0x10;
+
+ /* if we got one, trigger an interrupt */
+ if ((collision_mask & exidy_collision_mask) && count++ < 128)
+ timer_set(video_screen_get_time_until_pos(0, org_1_x + sx, org_1_y + sy), collision_mask, collision_irq_callback);
+ }
+ if (*BITMAP_ADDR16(motion_object_2_vid, sy, sx) != pen0)
+ {
+ /* check for background collision (M2CHAR) */
+ if (*BITMAP_ADDR16(tmpbitmap, org_2_y + sy, org_2_x + sx) != pen0)
+ if ((exidy_collision_mask & 0x08) && count++ < 128)
+ timer_set(video_screen_get_time_until_pos(0, org_2_x + sx, org_2_y + sy), 0x08, collision_irq_callback);
+ }
+ }
+}
+
+
+
+/*************************************
+ *
+ * Standard screen refresh callback
+ *
+ *************************************/
+
+VIDEO_UPDATE( exidy )
+{
+ int sx, sy;
+
+ /* update the background and draw it */
+ update_background(machine);
+ copybitmap(bitmap, tmpbitmap, 0, 0, 0, 0, cliprect, TRANSPARENCY_NONE, 0);
+
+ /* draw sprite 2 first */
+ if (sprite_2_enabled())
+ {
+ sx = 236 - sprite2_xpos - 4;
+ sy = 244 - sprite2_ypos - 4;
+
+ drawgfx(bitmap, machine->gfx[1],
+ ((spriteno >> 4) & 0x0f) + 32, 1,
+ 0, 0, sx, sy, cliprect, TRANSPARENCY_PEN, 0);
+ }
+
+ /* draw sprite 1 next */
+ if (sprite_1_enabled())
+ {
+ UINT8 enable_set = ((sprite_enable & 0x20) != 0);
+
+ sx = 236 - sprite1_xpos - 4;
+ sy = 244 - sprite1_ypos - 4;
+
+ if (sy < 0) sy = 0;
+
+ drawgfx(bitmap, machine->gfx[1],
+ (spriteno & 0x0f) + 16 * enable_set, 0,
+ 0, 0, sx, sy, cliprect, TRANSPARENCY_PEN, 0);
+ }
+
+ /* indicate that we already updated the background */
+ update_complete = 1;
+ return 0;
+}