/*************************************************************************** atarimo.c Common motion object management functions for Atari raster games. ***************************************************************************/ #include "emu.h" #include "atarimo.h" /*************************************************************************** TYPES & STRUCTURES ***************************************************************************/ /* internal structure containing a word index, shift and mask */ struct atarimo_mask { int word; /* word index */ int shift; /* shift amount */ int mask; /* final mask */ }; /* internal structure containing the state of the motion objects */ class atarimo_data { public: atarimo_data(running_machine &machine) : m_machine(machine) { } running_machine &machine() const { return m_machine; } UINT32 gfxchanged; /* true if the gfx info has changed */ gfx_element * gfxelement[MAX_GFX_ELEMENTS]; /* local copy of graphics elements */ int gfxgranularity[MAX_GFX_ELEMENTS]; bitmap_t *bitmap; /* temporary bitmap to render to */ int linked; /* are the entries linked? */ int split; /* are entries split or together? */ int reverse; /* render in reverse order? */ int swapxy; /* render in swapped X/Y order? */ UINT8 nextneighbor; /* does the neighbor bit affect the next object? */ int slipshift; /* log2(pixels_per_SLIP) */ int slipoffset; /* pixel offset for SLIP */ int entrycount; /* number of entries per bank */ int entrybits; /* number of bits needed to represent entrycount */ int bankcount; /* number of banks */ int tilewidth; /* width of non-rotated tile */ int tileheight; /* height of non-rotated tile */ int tilexshift; /* bits to shift X coordinate when drawing */ int tileyshift; /* bits to shift Y coordinate when drawing */ int bitmapwidth; /* width of the full playfield bitmap */ int bitmapheight; /* height of the full playfield bitmap */ int bitmapxmask; /* x coordinate mask for the playfield bitmap */ int bitmapymask; /* y coordinate mask for the playfield bitmap */ int spriterammask; /* combined mask when accessing sprite RAM with raw addresses */ int spriteramsize; /* total size of sprite RAM, in entries */ int sliprammask; /* combined mask when accessing SLIP RAM with raw addresses */ int slipramsize; /* total size of SLIP RAM, in entries */ UINT32 palettebase; /* base palette entry */ int maxcolors; /* maximum number of colors */ int transpen; /* transparent pen index */ UINT32 bank; /* current bank number */ UINT32 xscroll; /* current x scroll offset */ UINT32 yscroll; /* current y scroll offset */ int maxperline; /* maximum number of entries/line */ atarimo_mask linkmask; /* mask for the link */ atarimo_mask gfxmask; /* mask for the graphics bank */ atarimo_mask codemask; /* mask for the code index */ atarimo_mask codehighmask; /* mask for the upper code index */ atarimo_mask colormask; /* mask for the color */ atarimo_mask xposmask; /* mask for the X position */ atarimo_mask yposmask; /* mask for the Y position */ atarimo_mask widthmask; /* mask for the width, in tiles*/ atarimo_mask heightmask; /* mask for the height, in tiles */ atarimo_mask hflipmask; /* mask for the horizontal flip */ atarimo_mask vflipmask; /* mask for the vertical flip */ atarimo_mask prioritymask; /* mask for the priority */ atarimo_mask neighbormask; /* mask for the neighbor */ atarimo_mask absolutemask; /* mask for absolute coordinates */ atarimo_mask specialmask; /* mask for the special value */ int specialvalue; /* resulting value to indicate "special" */ atarimo_special_func specialcb; /* callback routine for special entries */ int codehighshift; /* shift count for the upper code */ atarimo_entry * spriteram; /* pointer to sprite RAM */ UINT16 * sprite_ram; /* pointer to the sprite RAM */ UINT16 * slip_ram; /* pointer to the SLIP RAM */ UINT16 * codelookup; /* lookup table for codes */ UINT8 * colorlookup; /* lookup table for colors */ UINT8 * gfxlookup; /* lookup table for graphics */ atarimo_entry * activelist[ATARIMO_MAXPERBANK]; /* pointers to active motion objects */ atarimo_entry ** activelast; /* pointer to the last pointer in the active list */ UINT8 * dirtygrid; /* grid of dirty rects for blending */ int dirtywidth; /* width of dirty grid */ int dirtyheight; /* height of dirty grid */ rectangle rectlist[ATARIMO_MAXPERBANK]; /* list of bounding rectangles */ int rectcount; UINT32 last_xpos; /* (during processing) the previous X position */ UINT32 next_xpos; /* (during processing) the next X position */ private: running_machine & m_machine; /* pointer back to the machine */ }; /*************************************************************************** MACROS ***************************************************************************/ /* data extraction */ #define EXTRACT_DATA(_input, _mask) (((_input)->data[(_mask).word] >> (_mask).shift) & (_mask).mask) /*************************************************************************** STATIC VARIABLES ***************************************************************************/ static atarimo_data *atarimo[ATARIMO_MAX]; static emu_timer *force_update_timer; /*************************************************************************** STATIC FUNCTION DECLARATIONS ***************************************************************************/ static int mo_render_object(atarimo_data *mo, const atarimo_entry *entry, const rectangle *cliprect); /*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ /*--------------------------------------------------------------- compute_log: Computes the number of bits necessary to hold a given value. The input must be an even power of two. ---------------------------------------------------------------*/ INLINE int compute_log(int value) { int log = 0; if (value == 0) return -1; while (!(value & 1)) log++, value >>= 1; if (value != 1) return -1; return log; } /*--------------------------------------------------------------- round_to_powerof2: Rounds a number up to the nearest power of 2. Even powers of 2 are rounded up to the next greatest power (e.g., 4 returns 8). ---------------------------------------------------------------*/ INLINE int round_to_powerof2(int value) { int log = 0; if (value == 0) return 1; while ((value >>= 1) != 0) log++; return 1 << (log + 1); } /*--------------------------------------------------------------- convert_mask: Converts a 4-word mask into a word index, shift, and adjusted mask. Returns 0 if invalid. ---------------------------------------------------------------*/ INLINE int convert_mask(const atarimo_entry *input, atarimo_mask *result) { int i, temp; /* determine the word and make sure it's only 1 */ result->word = -1; for (i = 0; i < 4; i++) if (input->data[i]) { if (result->word == -1) result->word = i; else return 0; } /* if all-zero, it's valid */ if (result->word == -1) { result->word = result->shift = result->mask = 0; return 1; } /* determine the shift and final mask */ result->shift = 0; temp = input->data[result->word]; while (!(temp & 1)) { result->shift++; temp >>= 1; } result->mask = temp; return 1; } /*--------------------------------------------------------------- init_gfxelement: Make a copy of the main gfxelement that gives us full control over colors. ---------------------------------------------------------------*/ INLINE void init_gfxelement(atarimo_data *mo, int idx) { mo->gfxelement[idx] = auto_alloc(mo->machine(), gfx_element(mo->machine())); memcpy(mo->gfxelement[idx], mo->machine().gfx[idx], sizeof(*mo->gfxelement[idx])); mo->gfxgranularity[idx] = mo->gfxelement[idx]->color_granularity; mo->gfxelement[idx]->color_granularity = 1; mo->gfxelement[idx]->color_base = 0; mo->gfxelement[idx]->total_colors = 65536; } /*--------------------------------------------------------------- init_savestate: Initialize save states ---------------------------------------------------------------*/ static void init_savestate(running_machine &machine, int index, atarimo_data *mo) { state_save_register_item(machine, "atarimo", NULL, index, mo->gfxchanged); state_save_register_item(machine, "atarimo", NULL, index, mo->palettebase); state_save_register_item(machine, "atarimo", NULL, index, mo->bank); state_save_register_item(machine, "atarimo", NULL, index, mo->xscroll); state_save_register_item(machine, "atarimo", NULL, index, mo->yscroll); state_save_register_item(machine, "atarimo", NULL, index, mo->last_xpos); state_save_register_item(machine, "atarimo", NULL, index, mo->next_xpos); #if 0 // These are not modified in code // Left in for completeness state_save_register_item(machine, "atarimo", NULL, index, mo->reverse); state_save_register_item(machine, "atarimo", NULL, index, mo->split); state_save_register_item(machine, "atarimo", NULL, index, mo->linked); state_save_register_item(machine, "atarimo", NULL, index, mo->swapxy); state_save_register_item(machine, "atarimo", NULL, index, mo->nextneighbor); state_save_register_item(machine, "atarimo", NULL, index, mo->slipshift); state_save_register_item(machine, "atarimo", NULL, index, mo->slipoffset); state_save_register_item(machine, "atarimo", NULL, index, mo->slipramsize); state_save_register_item(machine, "atarimo", NULL, index, mo->sliprammask); state_save_register_item(machine, "atarimo", NULL, index, mo->entrycount); state_save_register_item(machine, "atarimo", NULL, index, mo->entrybits); state_save_register_item(machine, "atarimo", NULL, index, mo->bankcount); state_save_register_item(machine, "atarimo", NULL, index, mo->tilewidth); state_save_register_item(machine, "atarimo", NULL, index, mo->tileheight); state_save_register_item(machine, "atarimo", NULL, index, mo->tilexshift); state_save_register_item(machine, "atarimo", NULL, index, mo->tileyshift); state_save_register_item(machine, "atarimo", NULL, index, mo->bitmapwidth); state_save_register_item(machine, "atarimo", NULL, index, mo->bitmapheight); state_save_register_item(machine, "atarimo", NULL, index, mo->bitmapxmask); state_save_register_item(machine, "atarimo", NULL, index, mo->bitmapymask); state_save_register_item(machine, "atarimo", NULL, index, mo->spriteramsize); state_save_register_item(machine, "atarimo", NULL, index, mo->spriterammask); state_save_register_item(machine, "atarimo", NULL, index, mo->maxcolors); state_save_register_item(machine, "atarimo", NULL, index, mo->transpen); state_save_register_item(machine, "atarimo", NULL, index, mo->maxperline); state_save_register_item(machine, "atarimo", NULL, index, mo->specialvalue); state_save_register_item(machine, "atarimo", NULL, index, mo->codehighshift); state_save_register_item(machine, "atarimo", NULL, index, mo->dirtywidth); state_save_register_item(machine, "atarimo", NULL, index, mo->dirtyheight); #endif machine.save().save_item("atarimo", NULL, index, *mo->bitmap, "bitmap"); machine.save().save_memory("atarimo", NULL, index, "spriteram", mo->spriteram, sizeof(atarimo_entry), mo->spriteramsize); state_save_register_item_pointer(machine, "atarimo", NULL, index, mo->codelookup, round_to_powerof2(mo->codemask.mask)); state_save_register_item_pointer(machine, "atarimo", NULL, index, mo->colorlookup, round_to_powerof2(mo->colormask.mask)); state_save_register_item_pointer(machine, "atarimo", NULL, index, mo->dirtygrid, mo->dirtywidth * mo->dirtyheight); state_save_register_item_pointer(machine, "atarimo", NULL, index, mo->gfxlookup, round_to_powerof2(mo->gfxmask.mask)); } /*************************************************************************** GLOBAL FUNCTIONS ***************************************************************************/ static TIMER_CALLBACK( force_update ) { int scanline = param; if (scanline > 0) machine.primary_screen->update_partial(scanline - 1); scanline += 64; if (scanline >= machine.primary_screen->visible_area().max_y) scanline = 0; force_update_timer->adjust(machine.primary_screen->time_until_pos(scanline), scanline); } /*--------------------------------------------------------------- atarimo_init: Configures the motion objects using the input description. Allocates all memory necessary and generates the attribute lookup table. ---------------------------------------------------------------*/ void atarimo_init(running_machine &machine, int map, const atarimo_desc *desc) { gfx_element *gfx = machine.gfx[desc->gfxindex]; int i; assert_always(map >= 0 && map < ATARIMO_MAX, "atarimo_init: map out of range"); atarimo_data *mo = atarimo[map] = auto_alloc(machine, atarimo_data(machine)); /* determine the masks first */ convert_mask(&desc->linkmask, &mo->linkmask); convert_mask(&desc->gfxmask, &mo->gfxmask); convert_mask(&desc->codemask, &mo->codemask); convert_mask(&desc->codehighmask, &mo->codehighmask); convert_mask(&desc->colormask, &mo->colormask); convert_mask(&desc->xposmask, &mo->xposmask); convert_mask(&desc->yposmask, &mo->yposmask); convert_mask(&desc->widthmask, &mo->widthmask); convert_mask(&desc->heightmask, &mo->heightmask); convert_mask(&desc->hflipmask, &mo->hflipmask); convert_mask(&desc->vflipmask, &mo->vflipmask); convert_mask(&desc->prioritymask, &mo->prioritymask); convert_mask(&desc->neighbormask, &mo->neighbormask); convert_mask(&desc->absolutemask, &mo->absolutemask); /* copy in the basic data */ mo->gfxchanged = FALSE; mo->linked = desc->linked; mo->split = desc->split; mo->reverse = desc->reverse; mo->swapxy = desc->swapxy; mo->nextneighbor = desc->nextneighbor; mo->slipshift = desc->slipheight ? compute_log(desc->slipheight) : 0; mo->slipoffset = desc->slipoffset; mo->entrycount = round_to_powerof2(mo->linkmask.mask); mo->entrybits = compute_log(mo->entrycount); mo->bankcount = desc->banks; mo->tilewidth = gfx->width; mo->tileheight = gfx->height; mo->tilexshift = compute_log(mo->tilewidth); mo->tileyshift = compute_log(mo->tileheight); mo->bitmapwidth = round_to_powerof2(mo->xposmask.mask); mo->bitmapheight = round_to_powerof2(mo->yposmask.mask); mo->bitmapxmask = mo->bitmapwidth - 1; mo->bitmapymask = mo->bitmapheight - 1; mo->spriteramsize = mo->bankcount * mo->entrycount; mo->spriterammask = mo->spriteramsize - 1; mo->slipramsize = mo->bitmapheight >> mo->slipshift; mo->sliprammask = mo->slipramsize - 1; mo->palettebase = desc->palettebase; mo->maxcolors = desc->maxcolors / gfx->color_granularity; mo->transpen = desc->transpen; mo->bank = 0; mo->xscroll = 0; mo->yscroll = 0; mo->maxperline = desc->maxlinks ? desc->maxlinks : 0x400; convert_mask(&desc->specialmask, &mo->specialmask); mo->specialvalue = desc->specialvalue; mo->specialcb = desc->specialcb; mo->codehighshift = compute_log(round_to_powerof2(mo->codemask.mask)); mo->sprite_ram = auto_alloc_array_clear(machine, UINT16, 0x2000); mo->slip_ram = auto_alloc_array_clear(machine, UINT16, 0x80); /* allocate the temp bitmap */ mo->bitmap = auto_bitmap_alloc(machine, machine.primary_screen->width(), machine.primary_screen->height(), BITMAP_FORMAT_INDEXED16); bitmap_fill(mo->bitmap, NULL, desc->transpen); /* allocate the spriteram */ mo->spriteram = auto_alloc_array_clear(machine, atarimo_entry, mo->spriteramsize); /* allocate the code lookup */ mo->codelookup = auto_alloc_array(machine, UINT16, round_to_powerof2(mo->codemask.mask)); /* initialize it 1:1 */ for (i = 0; i < round_to_powerof2(mo->codemask.mask); i++) mo->codelookup[i] = i; /* allocate the color lookup */ mo->colorlookup = auto_alloc_array(machine, UINT8, round_to_powerof2(mo->colormask.mask)); /* initialize it 1:1 */ for (i = 0; i < round_to_powerof2(mo->colormask.mask); i++) mo->colorlookup[i] = i; /* allocate dirty grid */ mo->dirtywidth = (machine.primary_screen->width() >> mo->tilexshift) + 2; mo->dirtyheight = (machine.primary_screen->height() >> mo->tileyshift) + 2; mo->dirtygrid = auto_alloc_array(machine, UINT8, mo->dirtywidth * mo->dirtyheight); /* allocate the gfx lookup */ mo->gfxlookup = auto_alloc_array(machine, UINT8, round_to_powerof2(mo->gfxmask.mask)); /* initialize it with the gfxindex we were passed in */ for (i = 0; i < round_to_powerof2(mo->gfxmask.mask); i++) mo->gfxlookup[i] = desc->gfxindex; /* initialize the gfx elements so we have full control over colors */ init_gfxelement(mo, desc->gfxindex); /* start a timer to update a few times during refresh */ force_update_timer = machine.scheduler().timer_alloc(FUNC(force_update)); force_update_timer->adjust(machine.primary_screen->time_until_pos(0)); init_savestate(machine, map, mo); logerror("atarimo_init:\n"); logerror(" width=%d (shift=%d), height=%d (shift=%d)\n", mo->tilewidth, mo->tilexshift, mo->tileheight, mo->tileyshift); logerror(" spriteram mask=%X, size=%d\n", mo->spriterammask, mo->spriteramsize); logerror(" slipram mask=%X, size=%d\n", mo->sliprammask, mo->slipramsize); logerror(" bitmap size=%dx%d\n", mo->bitmapwidth, mo->bitmapheight); } /*--------------------------------------------------------------- atarimo_get_code_lookup: Returns a pointer to the code lookup table. ---------------------------------------------------------------*/ UINT16 *atarimo_get_code_lookup(int map, int *size) { atarimo_data *mo = atarimo[map]; if (size) *size = round_to_powerof2(mo->codemask.mask); return mo->codelookup; } /*--------------------------------------------------------------- atarimo_get_color_lookup: Returns a pointer to the color lookup table. ---------------------------------------------------------------*/ UINT8 *atarimo_get_color_lookup(int map, int *size) { atarimo_data *mo = atarimo[map]; if (size) *size = round_to_powerof2(mo->colormask.mask); return mo->colorlookup; } /*--------------------------------------------------------------- atarimo_get_gfx_lookup: Returns a pointer to the graphics lookup table. ---------------------------------------------------------------*/ UINT8 *atarimo_get_gfx_lookup(int map, int *size) { atarimo_data *mo = atarimo[map]; mo->gfxchanged = TRUE; if (size) *size = round_to_powerof2(mo->gfxmask.mask); return mo->gfxlookup; } /*--------------------------------------------------------------- build_active_list: Build a list of active objects. ---------------------------------------------------------------*/ static void build_active_list(atarimo_data *mo, int link) { atarimo_entry *bankbase = &mo->spriteram[mo->bank << mo->entrybits]; UINT8 movisit[ATARIMO_MAXPERBANK]; atarimo_entry **current; int i; /* reset the visit map */ memset(movisit, 0, mo->entrycount); /* visit all the motion objects and copy their data into the display list */ for (i = 0, current = mo->activelist; i < mo->maxperline && !movisit[link]; i++) { atarimo_entry *modata = &bankbase[link]; /* copy the current entry into the list */ *current++ = modata; /* link to the next object */ movisit[link] = 1; if (mo->linked) link = EXTRACT_DATA(modata, mo->linkmask); else link = (link + 1) & mo->linkmask.mask; } /* note the last entry */ mo->activelast = current; } /*--------------------------------------------------------------- get_dirty_base: Return the dirty grid pointer for a given X and Y position. ---------------------------------------------------------------*/ INLINE UINT8 *get_dirty_base(atarimo_data *mo, int x, int y) { UINT8 *result = mo->dirtygrid; result += ((y >> mo->tileyshift) + 1) * mo->dirtywidth; result += (x >> mo->tilexshift) + 1; return result; } /*--------------------------------------------------------------- erase_dirty_grid: Erases the dirty grid within a given cliprect. ---------------------------------------------------------------*/ static void erase_dirty_grid(atarimo_data *mo, const rectangle *cliprect) { int sx = cliprect->min_x >> mo->tilexshift; int ex = cliprect->max_x >> mo->tilexshift; int sy = cliprect->min_y >> mo->tileyshift; int ey = cliprect->max_y >> mo->tileyshift; int y; /* loop over all grid rows that intersect our cliprect */ for (y = sy; y <= ey; y++) { /* get the base pointer and memset the row */ UINT8 *dirtybase = get_dirty_base(mo, cliprect->min_x, y << mo->tileyshift); memset(dirtybase, 0, ex - sx + 1); } } /*--------------------------------------------------------------- convert_dirty_grid_to_rects: Converts a dirty grid into a series of cliprects. ---------------------------------------------------------------*/ static void convert_dirty_grid_to_rects(atarimo_data *mo, const rectangle *cliprect, atarimo_rect_list *rectlist) { int sx = cliprect->min_x >> mo->tilexshift; int ex = cliprect->max_x >> mo->tilexshift; int sy = cliprect->min_y >> mo->tileyshift; int ey = cliprect->max_y >> mo->tileyshift; int tilewidth = 1 << mo->tilexshift; int tileheight = 1 << mo->tileyshift; rectangle *rect; int x, y; /* initialize the rect list */ rectlist->numrects = 0; rectlist->rect = mo->rectlist; rect = &mo->rectlist[-1]; /* loop over all grid rows that intersect our cliprect */ for (y = sy; y <= ey; y++) { UINT8 *dirtybase = get_dirty_base(mo, cliprect->min_x, y << mo->tileyshift); int can_add_to_existing = 0; /* loop over all grid columns that intersect our cliprect */ for (x = sx; x <= ex; x++) { /* if this tile is dirty, add that to our rectlist */ if (*dirtybase++) { /* if we can't add to an existing rect, create a new one */ if (!can_add_to_existing) { /* advance pointers */ rectlist->numrects++; rect++; /* make a rect describing this grid square */ rect->min_x = x << mo->tilexshift; rect->max_x = rect->min_x + tilewidth - 1; rect->min_y = y << mo->tileyshift; rect->max_y = rect->min_y + tileheight - 1; /* neighboring grid squares can add to this one */ can_add_to_existing = 1; } /* if we can add to the previous rect, just expand its width */ else rect->max_x += tilewidth; } /* once we hit a non-dirty square, we can no longer add on */ else can_add_to_existing = 0; } } } /*--------------------------------------------------------------- atarimo_render: Render the motion objects to the destination bitmap. ---------------------------------------------------------------*/ bitmap_t *atarimo_render(int map, const rectangle *cliprect, atarimo_rect_list *rectlist) { atarimo_data *mo = atarimo[map]; int startband, stopband, band, i; rectangle *rect; /* if the graphics info has changed, recompute */ if (mo->gfxchanged) { mo->gfxchanged = FALSE; for (i = 0; i < round_to_powerof2(mo->gfxmask.mask); i++) init_gfxelement(mo, mo->gfxlookup[i]); } /* compute start/stop bands */ startband = ((cliprect->min_y + mo->yscroll - mo->slipoffset) & mo->bitmapymask) >> mo->slipshift; stopband = ((cliprect->max_y + mo->yscroll - mo->slipoffset) & mo->bitmapymask) >> mo->slipshift; if (startband > stopband) startband -= mo->bitmapheight >> mo->slipshift; if (!mo->slipshift) stopband = startband; /* erase the dirty grid */ erase_dirty_grid(mo, cliprect); /* loop over SLIP bands */ for (band = startband; band <= stopband; band++) { atarimo_entry **first, **current, **last; rectangle bandclip; int link, step; /* if we don't use SLIPs, just recapture from 0 */ if (!mo->slipshift) { link = 0; bandclip = *cliprect; } /* otherwise, grab the SLIP and compute the bandrect */ else { int slipentry = band & mo->sliprammask; link = (mo->slip_ram[slipentry] >> mo->linkmask.shift) & mo->linkmask.mask; /* start with the cliprect */ bandclip = *cliprect; /* compute minimum Y and wrap around if necessary */ bandclip.min_y = ((band << mo->slipshift) - mo->yscroll + mo->slipoffset) & mo->bitmapymask; if (bandclip.min_y > mo->machine().primary_screen->visible_area().max_y) bandclip.min_y -= mo->bitmapheight; /* maximum Y is based on the minimum */ bandclip.max_y = bandclip.min_y + (1 << mo->slipshift) - 1; /* keep within the cliprect */ sect_rect(&bandclip, cliprect); } /* if this matches the last link, we don't need to re-process the list */ build_active_list(mo, link); /* set the start and end points */ if (mo->reverse) { first = mo->activelast - 1; last = mo->activelist - 1; step = -1; } else { first = mo->activelist; last = mo->activelast; step = 1; } /* initialize the parameters */ mo->next_xpos = 123456; /* render the mos */ for (current = first; current != last; current += step) mo_render_object(mo, *current, &bandclip); } /* convert the dirty grid to a rectlist */ convert_dirty_grid_to_rects(mo, cliprect, rectlist); /* clip the rectlist */ for (i = 0, rect = rectlist->rect; i < rectlist->numrects; i++, rect++) sect_rect(rect, cliprect); /* return the bitmap */ return mo->bitmap; } /*--------------------------------------------------------------- mo_render_object: Internal processing callback that renders to the backing bitmap and then copies the result to the destination. ---------------------------------------------------------------*/ static int mo_render_object(atarimo_data *mo, const atarimo_entry *entry, const rectangle *cliprect) { int gfxindex = mo->gfxlookup[EXTRACT_DATA(entry, mo->gfxmask)]; const gfx_element *gfx = mo->gfxelement[gfxindex]; bitmap_t *bitmap = mo->bitmap; int x, y, sx, sy; /* extract data from the various words */ int code = mo->codelookup[EXTRACT_DATA(entry, mo->codemask)] | (EXTRACT_DATA(entry, mo->codehighmask) << mo->codehighshift); int color = mo->colorlookup[EXTRACT_DATA(entry, mo->colormask)]; int xpos = EXTRACT_DATA(entry, mo->xposmask); int ypos = -EXTRACT_DATA(entry, mo->yposmask); int hflip = EXTRACT_DATA(entry, mo->hflipmask); int vflip = EXTRACT_DATA(entry, mo->vflipmask); int width = EXTRACT_DATA(entry, mo->widthmask) + 1; int height = EXTRACT_DATA(entry, mo->heightmask) + 1; int priority = EXTRACT_DATA(entry, mo->prioritymask); int xadv, yadv, rendered = 0; UINT8 *dirtybase; #ifdef TEMPDEBUG int temp = EXTRACT_DATA(entry, mo->codemask); if ((temp & 0xff00) == 0xc800) { static UINT8 hits[256]; if (!hits[temp & 0xff]) { fprintf(stderr, "code = %04X\n", temp); hits[temp & 0xff] = 1; } } #endif /* compute the effective color, merging in priority */ color = (color * mo->gfxgranularity[gfxindex]) | (priority << ATARIMO_PRIORITY_SHIFT); color += mo->palettebase; /* add in the scroll positions if we're not in absolute coordinates */ if (!EXTRACT_DATA(entry, mo->absolutemask)) { xpos -= mo->xscroll; ypos -= mo->yscroll; } /* adjust for height */ ypos -= height << mo->tileyshift; /* handle previous hold bits */ if (mo->next_xpos != 123456) xpos = mo->next_xpos; mo->next_xpos = 123456; /* check for the hold bit */ if (EXTRACT_DATA(entry, mo->neighbormask)) { if (!mo->nextneighbor) xpos = mo->last_xpos + mo->tilewidth; else mo->next_xpos = xpos + mo->tilewidth; } mo->last_xpos = xpos; /* adjust the final coordinates */ xpos &= mo->bitmapxmask; ypos &= mo->bitmapymask; if (xpos > mo->machine().primary_screen->visible_area().max_x) xpos -= mo->bitmapwidth; if (ypos > mo->machine().primary_screen->visible_area().max_y) ypos -= mo->bitmapheight; /* is this one special? */ if (mo->specialmask.mask != 0 && EXTRACT_DATA(entry, mo->specialmask) == mo->specialvalue) { if (mo->specialcb) return (*mo->specialcb)(bitmap, cliprect, code, color, xpos, ypos, NULL); return 0; } /* adjust for h flip */ xadv = mo->tilewidth; if (hflip) { xpos += (width - 1) << mo->tilexshift; xadv = -xadv; } /* adjust for v flip */ yadv = mo->tileheight; if (vflip) { ypos += (height - 1) << mo->tileyshift; yadv = -yadv; } /* standard order is: loop over Y first, then X */ if (!mo->swapxy) { /* loop over the height */ for (y = 0, sy = ypos; y < height; y++, sy += yadv) { /* clip the Y coordinate */ if (sy <= cliprect->min_y - mo->tileheight) { code += width; continue; } else if (sy > cliprect->max_y) break; /* loop over the width */ for (x = 0, sx = xpos; x < width; x++, sx += xadv, code++) { /* clip the X coordinate */ if (sx <= -cliprect->min_x - mo->tilewidth || sx > cliprect->max_x) continue; /* draw the sprite */ drawgfx_transpen_raw(bitmap, cliprect, gfx, code, color, hflip, vflip, sx, sy, mo->transpen); rendered = 1; /* mark the grid dirty */ dirtybase = get_dirty_base(mo, sx, sy); dirtybase[0] = 1; dirtybase[1] = 1; dirtybase[mo->dirtywidth] = 1; dirtybase[mo->dirtywidth + 1] = 1; } } } /* alternative order is swapped */ else { /* loop over the width */ for (x = 0, sx = xpos; x < width; x++, sx += xadv) { /* clip the X coordinate */ if (sx <= cliprect->min_x - mo->tilewidth) { code += height; continue; } else if (sx > cliprect->max_x) break; /* loop over the height */ dirtybase = get_dirty_base(mo, sx, ypos); for (y = 0, sy = ypos; y < height; y++, sy += yadv, code++) { /* clip the X coordinate */ if (sy <= -cliprect->min_y - mo->tileheight || sy > cliprect->max_y) continue; /* draw the sprite */ drawgfx_transpen_raw(bitmap, cliprect, gfx, code, color, hflip, vflip, sx, sy, mo->transpen); rendered = 1; /* mark the grid dirty */ dirtybase = get_dirty_base(mo, sx, sy); dirtybase[0] = 1; dirtybase[1] = 1; dirtybase[mo->dirtywidth] = 1; dirtybase[mo->dirtywidth + 1] = 1; } } } return rendered; } /*--------------------------------------------------------------- atarimo_set_bank: Set the banking value for the motion objects. ---------------------------------------------------------------*/ void atarimo_set_bank(int map, int bank) { atarimo_data *mo = atarimo[map]; mo->bank = bank; } /*--------------------------------------------------------------- atarimo_set_xscroll: Set the horizontal scroll value for the motion objects. ---------------------------------------------------------------*/ void atarimo_set_xscroll(int map, int xscroll) { atarimo_data *mo = atarimo[map]; mo->xscroll = xscroll; } /*--------------------------------------------------------------- atarimo_set_yscroll: Set the vertical scroll value for the motion objects. ---------------------------------------------------------------*/ void atarimo_set_yscroll(int map, int yscroll) { atarimo_data *mo = atarimo[map]; mo->yscroll = yscroll; } /*--------------------------------------------------------------- atarimo_get_bank: Returns the banking value for the motion objects. ---------------------------------------------------------------*/ int atarimo_get_bank(int map) { return atarimo[map]->bank; } /*--------------------------------------------------------------- atarimo_get_xscroll: Returns the horizontal scroll value for the motion objects. ---------------------------------------------------------------*/ int atarimo_get_xscroll(int map) { return atarimo[map]->xscroll; } /*--------------------------------------------------------------- atarimo_get_yscroll: Returns the vertical scroll value for the motion objects. ---------------------------------------------------------------*/ int atarimo_get_yscroll(int map) { return atarimo[map]->yscroll; } /*--------------------------------------------------------------- atarimo_0_spriteram_r: Read handler for the spriteram. ---------------------------------------------------------------*/ READ16_HANDLER( atarimo_0_spriteram_r ) { atarimo_data *mo = atarimo[0]; return mo->sprite_ram[offset] & mem_mask; } /*--------------------------------------------------------------- atarimo_0_spriteram_w: Write handler for the spriteram. ---------------------------------------------------------------*/ WRITE16_HANDLER( atarimo_0_spriteram_w ) { int entry, idx, bank; atarimo_data *mo = atarimo[0]; COMBINE_DATA(&mo->sprite_ram[offset]); if (mo->split) { entry = offset & mo->linkmask.mask; idx = (offset >> mo->entrybits) & 3; } else { entry = (offset >> 2) & mo->linkmask.mask; idx = offset & 3; } bank = offset >> (2 + mo->entrybits); COMBINE_DATA(&mo->spriteram[(bank << mo->entrybits) + entry].data[idx]); } /*--------------------------------------------------------------- atarimo_1_spriteram_r: Read handler for the spriteram. ---------------------------------------------------------------*/ READ16_HANDLER( atarimo_1_spriteram_r ) { atarimo_data *mo = atarimo[1]; return mo->sprite_ram[offset] & mem_mask; } /*--------------------------------------------------------------- atarimo_1_spriteram_w: Write handler for the spriteram. ---------------------------------------------------------------*/ WRITE16_HANDLER( atarimo_1_spriteram_w ) { int entry, idx, bank; atarimo_data *mo = atarimo[1]; COMBINE_DATA(&mo->sprite_ram[offset]); if (mo->split) { entry = offset & mo->linkmask.mask; idx = (offset >> mo->entrybits) & 3; } else { entry = (offset >> 2) & mo->linkmask.mask; idx = offset & 3; } bank = offset >> (2 + mo->entrybits); COMBINE_DATA(&mo->spriteram[(bank << mo->entrybits) + entry].data[idx]); } /*--------------------------------------------------------------- atarimo_0_spriteram_expanded_w: Write handler for the expanded form of spriteram. ---------------------------------------------------------------*/ WRITE16_HANDLER( atarimo_0_spriteram_expanded_w ) { int entry, idx, bank; atarimo_data *mo = atarimo[0]; COMBINE_DATA(&mo->sprite_ram[offset]); if (!(offset & 1)) { offset >>= 1; if (mo->split) { entry = offset & mo->linkmask.mask; idx = (offset >> mo->entrybits) & 3; } else { entry = (offset >> 2) & mo->linkmask.mask; idx = offset & 3; } bank = offset >> (2 + mo->entrybits); COMBINE_DATA(&mo->spriteram[(bank << mo->entrybits) + entry].data[idx]); } } /*--------------------------------------------------------------- atarimo_set_slipram: Set slipram pointer. ---------------------------------------------------------------*/ void atarimo_set_slipram(int map, UINT16 *ram) { atarimo_data *mo = atarimo[map]; mo->slip_ram = ram; } /*--------------------------------------------------------------- atarimo_0_slipram_r: Read handler for the slipram. ---------------------------------------------------------------*/ READ16_HANDLER( atarimo_0_slipram_r ) { atarimo_data *mo = atarimo[0]; logerror("READ: %04x:%04x\n", offset, mo->slip_ram[offset] & mem_mask); return mo->slip_ram[offset] & mem_mask; } /*--------------------------------------------------------------- atarimo_0_slipram_w: Write handler for the slipram. ---------------------------------------------------------------*/ WRITE16_HANDLER( atarimo_0_slipram_w ) { atarimo_data *mo = atarimo[0]; logerror("WRITE: %04x:%04x:%04x:%04x\n", offset, mo->slip_ram[offset], data, mem_mask); COMBINE_DATA(&mo->slip_ram[offset]); } /*--------------------------------------------------------------- atarimo_1_slipram_r: Read handler for the slipram. ---------------------------------------------------------------*/ READ16_HANDLER( atarimo_1_slipram_r ) { atarimo_data *mo = atarimo[1]; return mo->slip_ram[offset] & mem_mask; } /*--------------------------------------------------------------- atarimo_1_slipram_w: Write handler for the slipram. ---------------------------------------------------------------*/ WRITE16_HANDLER( atarimo_1_slipram_w ) { atarimo_data *mo = atarimo[1]; COMBINE_DATA(&mo->slip_ram[offset]); } /*--------------------------------------------------------------- atarimo_mark_high_palette: Mark high palette bits starting at the given X,Y and continuing until a stop or the end of line. ---------------------------------------------------------------*/ void atarimo_mark_high_palette(bitmap_t *bitmap, UINT16 *pf, UINT16 *mo, int x, int y) { #define START_MARKER ((4 << ATARIMO_PRIORITY_SHIFT) | 2) #define END_MARKER ((4 << ATARIMO_PRIORITY_SHIFT) | 4) int offnext = 0; for ( ; x < bitmap->width; x++) { pf[x] |= 0x400; if (offnext && (mo[x] & START_MARKER) != START_MARKER) break; offnext = ((mo[x] & END_MARKER) == END_MARKER); } }