summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/atarisy1.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/video/atarisy1.c')
-rw-r--r--src/mame/video/atarisy1.c664
1 files changed, 664 insertions, 0 deletions
diff --git a/src/mame/video/atarisy1.c b/src/mame/video/atarisy1.c
new file mode 100644
index 00000000000..16276fb83d1
--- /dev/null
+++ b/src/mame/video/atarisy1.c
@@ -0,0 +1,664 @@
+/***************************************************************************
+
+ Atari System 1 hardware
+
+****************************************************************************/
+
+#include "driver.h"
+#include "machine/atarigen.h"
+#include "atarisy1.h"
+
+
+
+/*************************************
+ *
+ * Constants
+ *
+ *************************************/
+
+/* the color and remap PROMs are mapped as follows */
+#define PROM1_BANK_4 0x80 /* active low */
+#define PROM1_BANK_3 0x40 /* active low */
+#define PROM1_BANK_2 0x20 /* active low */
+#define PROM1_BANK_1 0x10 /* active low */
+#define PROM1_OFFSET_MASK 0x0f /* postive logic */
+
+#define PROM2_BANK_6_OR_7 0x80 /* active low */
+#define PROM2_BANK_5 0x40 /* active low */
+#define PROM2_PLANE_5_ENABLE 0x20 /* active high */
+#define PROM2_PLANE_4_ENABLE 0x10 /* active high */
+#define PROM2_PF_COLOR_MASK 0x0f /* negative logic */
+#define PROM2_BANK_7 0x08 /* active low, plus PROM2_BANK_6_OR_7 low as well */
+#define PROM2_MO_COLOR_MASK 0x07 /* negative logic */
+
+
+
+/*************************************
+ *
+ * Globals we own
+ *
+ *************************************/
+
+UINT16 *atarisy1_bankselect;
+
+
+
+/*************************************
+ *
+ * Statics
+ *
+ *************************************/
+
+/* playfield parameters */
+static UINT16 playfield_lookup[256];
+static UINT8 playfield_tile_bank;
+static UINT16 playfield_priority_pens;
+static emu_timer *yscroll_reset_timer;
+
+/* INT3 tracking */
+static int next_timer_scanline;
+static emu_timer *scanline_timer;
+static emu_timer *int3off_timer;
+
+/* graphics bank tracking */
+static UINT8 bank_gfx[3][8];
+static UINT8 bank_color_shift[MAX_GFX_ELEMENTS];
+
+/* basic form of a graphics bank */
+static gfx_layout objlayout =
+{
+ 8,8, /* 8*8 sprites */
+ 4096, /* 4096 of them */
+ 6, /* 6 bits per pixel */
+ { 5*8*0x10000, 4*8*0x10000, 3*8*0x10000, 2*8*0x10000, 1*8*0x10000, 0*8*0x10000 },
+ { 0, 1, 2, 3, 4, 5, 6, 7 },
+ { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
+ 8*8 /* every sprite takes 8 consecutive bytes */
+};
+
+
+
+/*************************************
+ *
+ * Prototypes
+ *
+ *************************************/
+
+static void update_timers(int scanline);
+static void decode_gfx(running_machine *machine, UINT16 *pflookup, UINT16 *molookup);
+static int get_bank(running_machine *machine, UINT8 prom1, UINT8 prom2, int bpp);
+static TIMER_CALLBACK( int3_callback );
+static TIMER_CALLBACK( int3off_callback );
+static TIMER_CALLBACK( reset_yscroll_callback );
+
+
+
+/*************************************
+ *
+ * Tilemap callbacks
+ *
+ *************************************/
+
+static TILE_GET_INFO( get_alpha_tile_info )
+{
+ UINT16 data = atarigen_alpha[tile_index];
+ int code = data & 0x3ff;
+ int color = (data >> 10) & 0x07;
+ int opaque = data & 0x2000;
+ SET_TILE_INFO(0, code, color, opaque ? TILE_FORCE_LAYER0 : 0);
+}
+
+
+static TILE_GET_INFO( get_playfield_tile_info )
+{
+ UINT16 data = atarigen_playfield[tile_index];
+ UINT16 lookup = playfield_lookup[((data >> 8) & 0x7f) | (playfield_tile_bank << 7)];
+ int gfxindex = (lookup >> 8) & 15;
+ int code = ((lookup & 0xff) << 8) | (data & 0xff);
+ int color = 0x20 + (((lookup >> 12) & 15) << bank_color_shift[gfxindex]);
+ SET_TILE_INFO(gfxindex, code, color, (data >> 15) & 1);
+}
+
+
+
+/*************************************
+ *
+ * Generic video system start
+ *
+ *************************************/
+
+VIDEO_START( atarisy1 )
+{
+ static const struct atarimo_desc modesc =
+ {
+ 0, /* index to which gfx system */
+ 8, /* number of motion object banks */
+ 1, /* are the entries linked? */
+ 1, /* are the entries split? */
+ 0, /* render in reverse order? */
+ 0, /* render in swapped X/Y order? */
+ 0, /* does the neighbor bit affect the next object? */
+ 0, /* pixels per SLIP entry (0 for no-slip) */
+ 0, /* pixel offset for SLIPs */
+ 0x38, /* maximum number of links to visit/scanline (0=all) */
+
+ 0x100, /* base palette entry */
+ 0x100, /* maximum number of colors */
+ 0, /* transparent pen index */
+
+ {{ 0,0,0,0x003f }}, /* mask for the link */
+ {{ 0,0xff00,0,0 }}, /* mask for the graphics bank */
+ {{ 0,0xffff,0,0 }}, /* mask for the code index */
+ {{ 0 }}, /* mask for the upper code index */
+ {{ 0,0xff00,0,0 }}, /* mask for the color */
+ {{ 0,0,0x3fe0,0 }}, /* mask for the X position */
+ {{ 0x3fe0,0,0,0 }}, /* mask for the Y position */
+ {{ 0 }}, /* mask for the width, in tiles*/
+ {{ 0x000f,0,0,0 }}, /* mask for the height, in tiles */
+ {{ 0x8000,0,0,0 }}, /* mask for the horizontal flip */
+ {{ 0 }}, /* mask for the vertical flip */
+ {{ 0,0,0x8000,0 }}, /* mask for the priority */
+ {{ 0 }}, /* mask for the neighbor */
+ {{ 0 }}, /* mask for absolute coordinates */
+
+ {{ 0,0xffff,0,0 }}, /* mask for the special value */
+ 0xffff, /* resulting value to indicate "special" */
+ 0 /* callback routine for special entries */
+ };
+
+ UINT16 motable[256];
+ UINT16 *codelookup;
+ UINT8 *colorlookup, *gfxlookup;
+ int i, size;
+
+ /* first decode the graphics */
+ decode_gfx(machine, playfield_lookup, motable);
+
+ /* initialize the playfield */
+ atarigen_playfield_tilemap = tilemap_create(get_playfield_tile_info, tilemap_scan_rows, TILEMAP_TYPE_PEN, 8,8, 64,64);
+
+ /* initialize the motion objects */
+ atarimo_init(machine, 0, &modesc);
+
+ /* initialize the alphanumerics */
+ atarigen_alpha_tilemap = tilemap_create(get_alpha_tile_info, tilemap_scan_rows, TILEMAP_TYPE_PEN, 8,8, 64,32);
+ tilemap_set_transparent_pen(atarigen_alpha_tilemap, 0);
+
+ /* modify the motion object code lookup */
+ codelookup = atarimo_get_code_lookup(0, &size);
+ for (i = 0; i < size; i++)
+ codelookup[i] = (i & 0xff) | ((motable[i >> 8] & 0xff) << 8);
+
+ /* modify the motion object color and gfx lookups */
+ colorlookup = atarimo_get_color_lookup(0, &size);
+ gfxlookup = atarimo_get_gfx_lookup(0, &size);
+ for (i = 0; i < size; i++)
+ {
+ colorlookup[i] = ((motable[i] >> 12) & 15) << 1;
+ gfxlookup[i] = (motable[i] >> 8) & 15;
+ }
+
+ /* reset the statics */
+ atarimo_set_yscroll(0, 256);
+ next_timer_scanline = -1;
+ scanline_timer = timer_alloc(int3_callback);
+ int3off_timer = timer_alloc(int3off_callback);
+ yscroll_reset_timer = timer_alloc(reset_yscroll_callback);
+}
+
+
+
+/*************************************
+ *
+ * Graphics bank selection
+ *
+ *************************************/
+
+WRITE16_HANDLER( atarisy1_bankselect_w )
+{
+ UINT16 oldselect = *atarisy1_bankselect;
+ UINT16 newselect = oldselect, diff;
+ int scanline = video_screen_get_vpos(0);
+
+ /* update memory */
+ COMBINE_DATA(&newselect);
+ diff = oldselect ^ newselect;
+
+ /* sound CPU reset */
+ if (diff & 0x0080)
+ {
+ cpunum_set_input_line(1, INPUT_LINE_RESET, (newselect & 0x0080) ? CLEAR_LINE : ASSERT_LINE);
+ if (!(newselect & 0x0080)) atarigen_sound_reset();
+ }
+
+ /* if MO or playfield banks change, force a partial update */
+ if (diff & 0x003c)
+ video_screen_update_partial(0, scanline);
+
+ /* motion object bank select */
+ atarimo_set_bank(0, (newselect >> 3) & 7);
+ update_timers(scanline);
+
+ /* playfield bank select */
+ if (diff & 0x0004)
+ {
+ playfield_tile_bank = (newselect >> 2) & 1;
+ tilemap_mark_all_tiles_dirty(atarigen_playfield_tilemap);
+ }
+
+ /* stash the new value */
+ *atarisy1_bankselect = newselect;
+}
+
+
+
+/*************************************
+ *
+ * Playfield priority pens
+ *
+ *************************************/
+
+WRITE16_HANDLER( atarisy1_priority_w )
+{
+ UINT16 oldpens = playfield_priority_pens;
+ UINT16 newpens = oldpens;
+
+ /* force a partial update in case this changes mid-screen */
+ COMBINE_DATA(&newpens);
+ if (oldpens != newpens)
+ video_screen_update_partial(0, video_screen_get_vpos(0));
+ playfield_priority_pens = newpens;
+}
+
+
+
+/*************************************
+ *
+ * Playfield horizontal scroll
+ *
+ *************************************/
+
+WRITE16_HANDLER( atarisy1_xscroll_w )
+{
+ UINT16 oldscroll = *atarigen_xscroll;
+ UINT16 newscroll = oldscroll;
+
+ /* force a partial update in case this changes mid-screen */
+ COMBINE_DATA(&newscroll);
+ if (oldscroll != newscroll)
+ video_screen_update_partial(0, video_screen_get_vpos(0));
+
+ /* set the new scroll value */
+ tilemap_set_scrollx(atarigen_playfield_tilemap, 0, newscroll);
+
+ /* update the data */
+ *atarigen_xscroll = newscroll;
+}
+
+
+
+/*************************************
+ *
+ * Playfield vertical scroll
+ *
+ *************************************/
+
+static TIMER_CALLBACK( reset_yscroll_callback )
+{
+ tilemap_set_scrolly(atarigen_playfield_tilemap, 0, param);
+}
+
+
+WRITE16_HANDLER( atarisy1_yscroll_w )
+{
+ UINT16 oldscroll = *atarigen_yscroll;
+ UINT16 newscroll = oldscroll;
+ int scanline = video_screen_get_vpos(0);
+ int adjusted_scroll;
+
+ /* force a partial update in case this changes mid-screen */
+ COMBINE_DATA(&newscroll);
+ video_screen_update_partial(0, scanline);
+
+ /* because this latches a new value into the scroll base,
+ we need to adjust for the scanline */
+ adjusted_scroll = newscroll;
+ if (scanline <= Machine->screen[0].visarea.max_y)
+ adjusted_scroll -= (scanline + 1);
+ tilemap_set_scrolly(atarigen_playfield_tilemap, 0, adjusted_scroll);
+
+ /* but since we've adjusted it, we must reset it to the normal value
+ once we hit scanline 0 again */
+ timer_adjust(yscroll_reset_timer, video_screen_get_time_until_pos(0, 0, 0), newscroll, attotime_zero);
+
+ /* update the data */
+ *atarigen_yscroll = newscroll;
+}
+
+
+
+/*************************************
+ *
+ * Sprite RAM write handler
+ *
+ *************************************/
+
+WRITE16_HANDLER( atarisy1_spriteram_w )
+{
+ int active_bank = atarimo_get_bank(0);
+ int oldword = atarimo_0_spriteram[offset];
+ int newword = oldword;
+ COMBINE_DATA(&newword);
+
+ /* if the data changed, and it modified the live sprite bank, do some extra work */
+ if (oldword != newword && (offset >> 8) == active_bank)
+ {
+ /* if modifying a timer, beware */
+ if (((offset & 0xc0) == 0x00 && atarimo_0_spriteram[offset | 0x40] == 0xffff) ||
+ ((offset & 0xc0) == 0x40 && (newword == 0xffff || oldword == 0xffff)))
+ {
+ /* if the timer is in the active bank, update the display list */
+ atarimo_0_spriteram_w(offset, data, 0);
+ update_timers(video_screen_get_vpos(0));
+ }
+
+ /* if we're about to modify data in the active sprite bank, make sure the video is up-to-date */
+ /* Road Runner needs this to work; note the +2 kludge -- +1 would be correct since the video */
+ /* renders the next scanline's sprites to the line buffers, but Road Runner still glitches */
+ /* without the extra +1 */
+ else
+ video_screen_update_partial(0, video_screen_get_vpos(0) + 2);
+ }
+
+ /* let the MO handler do the basic work */
+ atarimo_0_spriteram_w(offset, data, 0);
+}
+
+
+
+/*************************************
+ *
+ * MO interrupt handlers
+ *
+ *************************************/
+
+static TIMER_CALLBACK( int3off_callback )
+{
+ /* clear the state */
+ atarigen_scanline_int_ack_w(0, 0, 0);
+}
+
+
+static TIMER_CALLBACK( int3_callback )
+{
+ int scanline = param;
+
+ /* update the state */
+ atarigen_scanline_int_gen();
+
+ /* set a timer to turn it off */
+ timer_adjust(int3off_timer, video_screen_get_scan_period(0), 0, attotime_zero);
+
+ /* determine the time of the next one */
+ next_timer_scanline = -1;
+ update_timers(scanline);
+}
+
+
+
+/*************************************
+ *
+ * MO interrupt state read
+ *
+ *************************************/
+
+READ16_HANDLER( atarisy1_int3state_r )
+{
+ return atarigen_scanline_int_state ? 0x0080 : 0x0000;
+}
+
+
+
+/*************************************
+ *
+ * Timer updater
+ *
+ *************************************/
+
+static void update_timers(int scanline)
+{
+ UINT16 *base = &atarimo_0_spriteram[atarimo_get_bank(0) * 64 * 4];
+ int link = 0, best = scanline, found = 0;
+ UINT8 spritevisit[64];
+
+ /* track which ones we've visited */
+ memset(spritevisit, 0, sizeof(spritevisit));
+
+ /* walk the list until we loop */
+ while (!spritevisit[link])
+ {
+ /* timers are indicated by 0xffff in entry 2 */
+ if (base[link + 0x40] == 0xffff)
+ {
+ int data = base[link];
+ int vsize = (data & 15) + 1;
+ int ypos = (256 - (data >> 5) - vsize * 8 - 1) & 0x1ff;
+
+ /* note that we found something */
+ found = 1;
+
+ /* is this a better entry than the best so far? */
+ if (best <= scanline)
+ {
+ if ((ypos <= scanline && ypos < best) || ypos > scanline)
+ best = ypos;
+ }
+ else
+ {
+ if (ypos < best)
+ best = ypos;
+ }
+ }
+
+ /* link to the next */
+ spritevisit[link] = 1;
+ link = base[link + 0xc0] & 0x3f;
+ }
+
+ /* if nothing was found, use scanline -1 */
+ if (!found)
+ best = -1;
+
+ /* update the timer */
+ if (best != next_timer_scanline)
+ {
+ next_timer_scanline = best;
+
+ /* set a new one */
+ if (best != -1)
+ timer_adjust(scanline_timer, video_screen_get_time_until_pos(0, best, 0), best, attotime_zero);
+ else
+ timer_adjust(scanline_timer, attotime_never, 0, attotime_zero);
+ }
+}
+
+
+
+/*************************************
+ *
+ * Main refresh
+ *
+ *************************************/
+
+VIDEO_UPDATE( atarisy1 )
+{
+ struct atarimo_rect_list rectlist;
+ mame_bitmap *mobitmap;
+ int x, y, r;
+
+ /* draw the playfield */
+ tilemap_draw(bitmap, cliprect, atarigen_playfield_tilemap, 0, 0);
+
+ /* draw and merge the MO */
+ mobitmap = atarimo_render(machine, 0, cliprect, &rectlist);
+ for (r = 0; r < rectlist.numrects; r++, rectlist.rect++)
+ for (y = rectlist.rect->min_y; y <= rectlist.rect->max_y; y++)
+ {
+ UINT16 *mo = (UINT16 *)mobitmap->base + mobitmap->rowpixels * y;
+ UINT16 *pf = (UINT16 *)bitmap->base + bitmap->rowpixels * y;
+ for (x = rectlist.rect->min_x; x <= rectlist.rect->max_x; x++)
+ if (mo[x])
+ {
+ /* high priority MO? */
+ if (mo[x] & ATARIMO_PRIORITY_MASK)
+ {
+ /* only gets priority if MO pen is not 1 */
+ if ((mo[x] & 0x0f) != 1)
+ pf[x] = 0x300 + ((pf[x] & 0x0f) << 4) + (mo[x] & 0x0f);
+ }
+
+ /* low priority */
+ else
+ {
+ /* priority pens for playfield color 0 */
+ if ((pf[x] & 0xf8) != 0 || !(playfield_priority_pens & (1 << (pf[x] & 0x07))))
+ pf[x] = mo[x];
+ }
+
+ /* erase behind ourselves */
+ mo[x] = 0;
+ }
+ }
+
+ /* add the alpha on top */
+ tilemap_draw(bitmap, cliprect, atarigen_alpha_tilemap, 0, 0);
+ return 0;
+}
+
+
+
+/*************************************
+ *
+ * Graphics decoding
+ *
+ *************************************/
+
+static void decode_gfx(running_machine *machine, UINT16 *pflookup, UINT16 *molookup)
+{
+ UINT8 *prom1 = &memory_region(REGION_PROMS)[0x000];
+ UINT8 *prom2 = &memory_region(REGION_PROMS)[0x200];
+ int obj, i;
+
+ /* reset the globals */
+ memset(&bank_gfx[0][0], 0, sizeof(bank_gfx));
+
+ /* loop for two sets of objects */
+ for (obj = 0; obj < 2; obj++)
+ {
+ /* loop for 256 objects in the set */
+ for (i = 0; i < 256; i++, prom1++, prom2++)
+ {
+ int bank, bpp, color, offset;
+
+ /* determine the bpp */
+ bpp = 4;
+ if (*prom2 & PROM2_PLANE_4_ENABLE)
+ {
+ bpp = 5;
+ if (*prom2 & PROM2_PLANE_5_ENABLE)
+ bpp = 6;
+ }
+
+ /* determine the offset */
+ offset = *prom1 & PROM1_OFFSET_MASK;
+
+ /* determine the bank */
+ bank = get_bank(machine, *prom1, *prom2, bpp);
+
+ /* set the value */
+ if (obj == 0)
+ {
+ /* playfield case */
+ color = (~*prom2 & PROM2_PF_COLOR_MASK) >> (bpp - 4);
+ if (bank == 0)
+ {
+ bank = 1;
+ offset = color = 0;
+ }
+ pflookup[i] = offset | (bank << 8) | (color << 12);
+ }
+ else
+ {
+ /* motion objects (high bit ignored) */
+ color = (~*prom2 & PROM2_MO_COLOR_MASK) >> (bpp - 4);
+ molookup[i] = offset | (bank << 8) | (color << 12);
+ }
+ }
+ }
+}
+
+
+
+/*************************************
+ *
+ * Graphics bank mapping
+ *
+ *************************************/
+
+static int get_bank(running_machine *machine, UINT8 prom1, UINT8 prom2, int bpp)
+{
+ int bank_index, i, gfx_index;
+
+ /* determine the bank index */
+ if ((prom1 & PROM1_BANK_1) == 0)
+ bank_index = 1;
+ else if ((prom1 & PROM1_BANK_2) == 0)
+ bank_index = 2;
+ else if ((prom1 & PROM1_BANK_3) == 0)
+ bank_index = 3;
+ else if ((prom1 & PROM1_BANK_4) == 0)
+ bank_index = 4;
+ else if ((prom2 & PROM2_BANK_5) == 0)
+ bank_index = 5;
+ else if ((prom2 & PROM2_BANK_6_OR_7) == 0)
+ {
+ if ((prom2 & PROM2_BANK_7) == 0)
+ bank_index = 7;
+ else
+ bank_index = 6;
+ }
+ else
+ return 0;
+
+ /* find the bank */
+ if (bank_gfx[bpp - 4][bank_index])
+ return bank_gfx[bpp - 4][bank_index];
+
+ /* if the bank is out of range, call it 0 */
+ if (0x80000 * (bank_index - 1) >= memory_region_length(REGION_GFX2))
+ return 0;
+
+ /* don't have one? let's make it ... first find any empty slot */
+ for (gfx_index = 0; gfx_index < MAX_GFX_ELEMENTS; gfx_index++)
+ if (machine->gfx[gfx_index] == NULL)
+ break;
+ assert(gfx_index != MAX_GFX_ELEMENTS);
+
+ /* tweak the structure for the number of bitplanes we have */
+ objlayout.planes = bpp;
+ for (i = 0; i < bpp; i++)
+ objlayout.planeoffset[i] = (bpp - i - 1) * 0x10000 * 8;
+
+ /* decode the graphics */
+ machine->gfx[gfx_index] = allocgfx(&objlayout);
+ decodegfx(machine->gfx[gfx_index], &memory_region(REGION_GFX2)[0x80000 * (bank_index - 1)], 0, machine->gfx[gfx_index]->total_elements);
+
+ /* set the color information */
+ machine->gfx[gfx_index]->color_base = 256;
+ machine->gfx[gfx_index]->color_granularity = 8;
+ machine->gfx[gfx_index]->total_colors = 0x40;
+ bank_color_shift[gfx_index] = bpp - 3;
+
+ /* set the entry and return it */
+ return bank_gfx[bpp - 4][bank_index] = gfx_index;
+}