// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** atarimo.c Common motion object management functions for Atari raster games. **************************************************************************** gfx bks lnk spl rev swp nei pps sof max basepal maxcols trn link code upper color xpos ypos width height hflip vflip priority neighbor absolute special arcadecl 0 1 1 0 0 0 0 0 0 0 0x100 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0 0 0 0 0 0 atarisy1 0 8 1 1 0 0 0 0 0 x38 0x100 0x100 0 0,0,0,003f 0,ffff,0,0 0 0,ff00,0,0 0,0,3fe0,0 3fe0,0,0,0 0 000f,0,0,0 8000,0,0,0 0 0,0,8000,0 0 0 0,ffff,0,0 == ffff atarisy2 1 1 1 0 0 0 0 0 0 0 0x000 0x040 15 0,0,0,07f8 0,07ff,0,0 0007,0,0,0 0,0,0,3000 0,0,ffc0,0 7fc0,0,0,0 0 0,3800,0,0 0,4000,0,0 0 0,0,0,c000 0,8000,0,0 0 0 badlands 1 1 0 1 0 0 0 0 0 0 0x080 0x080 0 003f,0,0,0 0fff,0,0,0 0 0,0,0,0007 0,0,0,ff80 0,ff80,0,0 0 0,000f,0,0 0 0 0,0,0,0008 0 0 0 batman 1 1 1 0 1 0 0 8 0 0 0x100 0x100 0 03ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0,0,0070,0 0 0 0 blstroid 1 1 1 0 0 0 0 0 0 0 0x000 0x100 0 0,0,0ff8,0 0,3fff,0,0 0 0,0,0,000f 0,0,0,ffc0 ff80,0,0,0 0 000f,0,0,0 0,8000,0,0 0,4000,0,0 0 0 0 0 cyberbal 1 1 1 0 0 0 1 1k 0 0 0x600 0x100 0 0,0,07f8,0 7fff,0,0,0 0 0,0,0,000f 0,0,0,ffc0 0,ff80,0,0 0 0,000f,0,0 8000,0,0,0 0 0 0,0,0,0010 0 0 eprom 0 1 1 0 1 0 0 8 0 0 0x100 0x100 0 03ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,0,0,0008 0 0,0,0070,0 0 0 0 guts 0 1 1 0 0 0 0 8 0 0 0x100 0x100 0 03ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,000f 0,8000,0,0 0 0,0,0070,0 0 0 0 gauntlet 0 1 1 1 0 0 0 8 1 0 0x100 0x100 0 0,0,0,03ff 7fff,0,0,0 0 0,000f,0,0 0,ff80,0,0 0,0,ff80,0 0,0,0038,0 0,0,0007,0 0,0,0040,0 0 0 0 0 0 klax 1 1 1 0 0 0 0 8 0 0 0x000 0x100 0 00ff,0,0,0 0,0fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,0,0,0008 0 0 0 0 0 offtwall 0 1 1 0 0 0 0 8 0 0 0x100 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0 0 0 0 rampart 0 1 1 0 0 0 0 8 0 0 0x100 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0 0 0 0 relief 1 1 1 0 0 0 0 8 0 0 0x100 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0 0 0 0 shuuz 1 1 1 0 0 0 0 8 0 0 0x000 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0 0 0 0 skullxbo 0 2 1 0 0 0 0 8 0 0 0x000 0x200 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ffc0,0 0,0,0,ff80 0,0,0,0070 0,0,0,000f 0,8000,0,0 0 0,0,0030,0 0 0 0 thunderj 1 1 1 0 1 0 0 8 0 0 0x100 0x100 0 03ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0,0,0070,0 0 0 0 toobin 1 1 1 0 0 1 0 1k 0 0 0x100 0x100 0 0,0,00ff,0 0,3fff,0,0 0 0,0,0,000f 0,0,0,ffc0 7fc0,0,0,0 0007,0,0,0 0038,0,0,0 0,4000,0,0 0,8000,0,0 0 0 8000,0,0,0 0 vindictr 0 1 1 0 0 0 0 8 0 0 0x100 0x100 0 0,0,0,03ff 7fff,0,0,0 0 0,000f,0,0 0,ff80,0,0 0,0,ff80,0 0,0,0038,0 0,0,0007,0 0,0,0040,0 0 0,0070,0,0 0 0 0 xybots 1 1 0 0 0 0 0 0 0 0 0x100 0x300 0 003f,0,0,0 3fff,0,0,0 0 0,0,0,000f 0,0,0,ff80 0,0,ff80,0 0 0,0,0007,0 8000,0,0,0 0 0,000f,0,0 0 0 0 Sorted atarisy1 0 8 1 1 0 0 0 0 0 x38 0x100 0x100 0 0,0,0,003f 0,ffff,0,0 0 0,ff00,0,0 0,0,3fe0,0 3fe0,0,0,0 0 000f,0,0,0 8000,0,0,0 0 0,0,8000,0 0 0 0,ffff,0,0 == ffff atarisy2 1 1 1 0 0 0 0 0 0 0 0x000 0x040 15 0,0,0,07f8 0,07ff,0,0 0007,0,0,0 0,0,0,3000 0,0,ffc0,0 7fc0,0,0,0 0 0,3800,0,0 0,4000,0,0 0 0,0,0,c000 0,8000,0,0 0 0 toobin 1 1 1 0 0 1 0 1k 0 0 0x100 0x100 0 0,0,00ff,0 0,3fff,0,0 0 0,0,0,000f 0,0,0,ffc0 7fc0,0,0,0 0007,0,0,0 0038,0,0,0 0,4000,0,0 0,8000,0,0 0 0 8000,0,0,0 0 gauntlet 0 1 1 1 0 0 0 8 1 0 0x100 0x100 0 0,0,0,03ff 7fff,0,0,0 0 0,000f,0,0 0,ff80,0,0 0,0,ff80,0 0,0,0038,0 0,0,0007,0 0,0,0040,0 0 0 0 0 0 vindictr 0 1 1 0 0 0 0 8 0 0 0x100 0x100 0 0,0,0,03ff 7fff,0,0,0 0 0,000f,0,0 0,ff80,0,0 0,0,ff80,0 0,0,0038,0 0,0,0007,0 0,0,0040,0 0 0,0070,0,0 0 0 0 xybots 1 1 0 0 0 0 0 0 0 0 0x100 0x300 0 003f,0,0,0 3fff,0,0,0 0 0,0,0,000f 0,0,0,ff80 0,0,ff80,0 0 0,0,0007,0 8000,0,0,0 0 0,000f,0,0 0 0 0 blstroid 1 1 1 0 0 0 0 0 0 0 0x000 0x100 0 0,0,0ff8,0 0,3fff,0,0 0 0,0,0,000f 0,0,0,ffc0 ff80,0,0,0 0 000f,0,0,0 0,8000,0,0 0,4000,0,0 0 0 0 0 badlands 1 1 0 1 0 0 0 0 0 0 0x080 0x080 0 003f,0,0,0 0fff,0,0,0 0 0,0,0,0007 0,0,0,ff80 0,ff80,0,0 0 0,000f,0,0 0 0 0,0,0,0008 0 0 0 cyberbal 1 1 1 0 0 0 1 1k 0 0 0x600 0x100 0 0,0,07f8,0 7fff,0,0,0 0 0,0,0,000f 0,0,0,ffc0 0,ff80,0,0 0 0,000f,0,0 8000,0,0,0 0 0 0,0,0,0010 0 0 batman 1 1 1 0 1 0 0 8 0 0 0x100 0x100 0 03ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0,0,0070,0 0 0 0 eprom 0 1 1 0 1 0 0 8 0 0 0x100 0x100 0 03ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,0,0,0008 0 0,0,0070,0 0 0 0 guts 0 1 1 0 0 0 0 8 0 0 0x100 0x100 0 03ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,000f 0,8000,0,0 0 0,0,0070,0 0 0 0 skullxbo 0 2 1 0 0 0 0 8 0 0 0x000 0x200 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ffc0,0 0,0,0,ff80 0,0,0,0070 0,0,0,000f 0,8000,0,0 0 0,0,0030,0 0 0 0 thunderj 1 1 1 0 1 0 0 8 0 0 0x100 0x100 0 03ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0,0,0070,0 0 0 0 arcadecl 0 1 1 0 0 0 0 0 0 0 0x100 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0 0 0 0 0 0 klax 1 1 1 0 0 0 0 8 0 0 0x000 0x100 0 00ff,0,0,0 0,0fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,0,0,0008 0 0 0 0 0 offtwall 0 1 1 0 0 0 0 8 0 0 0x100 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0 0 0 0 rampart 0 1 1 0 0 0 0 8 0 0 0x100 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0 0 0 0 relief 1 1 1 0 0 0 0 8 0 0 0x100 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0 0 0 0 shuuz 1 1 1 0 0 0 0 8 0 0 0x000 0x100 0 00ff,0,0,0 0,7fff,0,0 0 0,0,000f,0 0,0,ff80,0 0,0,0,ff80 0,0,0,0070 0,0,0,0007 0,8000,0,0 0 0 0 0 0 ***************************************************************************/ #include "emu.h" #include "atarimo.h" #include "screen.h" //************************************************************************** // 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 atari_motion_objects_device::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 atari_motion_objects_device::round_to_powerof2(int value) { int log = 0; if (value == 0) return 1; while ((value >>= 1) != 0) log++; return 1 << (log + 1); } //************************************************************************** // CORE IMPLEMENTATION //************************************************************************** // device type definition DEFINE_DEVICE_TYPE(ATARI_MOTION_OBJECTS, atari_motion_objects_device, "atarimo", "Atari Motion Objects") //------------------------------------------------- // atari_motion_objects_device - constructor //------------------------------------------------- atari_motion_objects_device::atari_motion_objects_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : sprite16_device_ind16(mconfig, ATARI_MOTION_OBJECTS, tag, owner) , device_video_interface(mconfig, *this) , m_tilewidth(0) , m_tileheight(0) , m_tilexshift(0) , m_tileyshift(0) , m_bitmapwidth(0) , m_bitmapheight(0) , m_bitmapxmask(0) , m_bitmapymask(0) , m_entrycount(0) , m_entrybits(0) , m_spriterammask(0) , m_spriteramsize(0) , m_slipshift(0) , m_sliprammask(0) , m_slipramsize(0) , m_bank(0) , m_xscroll(0) , m_yscroll(0) , m_slipram(*this, "slip") , m_activelast(nullptr) , m_last_xpos(0) , m_next_xpos(0) , m_gfxdecode(*this, finder_base::DUMMY_TAG) { } //------------------------------------------------- // draw: Render the motion objects to the // destination bitmap. //------------------------------------------------- void atari_motion_objects_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect) { // compute start/stop bands int startband = ((cliprect.min_y + m_yscroll - m_slipoffset) & m_bitmapymask) >> m_slipshift; int stopband = ((cliprect.max_y + m_yscroll - m_slipoffset) & m_bitmapymask) >> m_slipshift; if (startband > stopband) startband -= m_bitmapheight >> m_slipshift; if (m_slipshift == 0) stopband = startband; // loop over SLIP bands for (int band = startband; band <= stopband; band++) { // compute the starting link and clip for the current band rectangle bandclip = cliprect; int link = 0; if (m_slipshift != 0) { // extract the link from the SLIP RAM link = (m_slipram[band & m_sliprammask] >> m_linkmask.shift()) & m_linkmask.mask(); // compute minimum Y and wrap around if necessary bandclip.min_y = ((band << m_slipshift) - m_yscroll + m_slipoffset) & m_bitmapymask; if (bandclip.min_y >= bitmap.height()) bandclip.min_y -= m_bitmapheight; // maximum Y is based on the minimum bandclip.max_y = bandclip.min_y + (1 << m_slipshift) - 1; // keep within the cliprect bandclip &= cliprect; } // if this matches the last link, we don't need to re-process the list build_active_list(link); // initialize the parameters m_next_xpos = 123456; // safety check if (m_activelist == m_activelast) continue; // set the start and end points uint16_t *first, *last; int step; if (m_reverse) { first = m_activelast - 4; last = m_activelist; step = -4; } else { first = m_activelist; last = m_activelast - 4; step = 4; } // render the mos for (uint16_t *current = first; ; current += step) { render_object(bitmap, bandclip, current); if (current == last) break; } } } //------------------------------------------------- // apply_stain: Mark high palette bits // starting at the given X,Y and continuing until // a stop or the end of line. //------------------------------------------------- void atari_motion_objects_device::apply_stain(bitmap_ind16 &bitmap, uint16_t *pf, uint16_t *mo, int x, int y) { const uint16_t START_MARKER = ((4 << PRIORITY_SHIFT) | 2); const uint16_t END_MARKER = ((4 << PRIORITY_SHIFT) | 4); bool offnext = false; for ( ; x < bitmap.width(); x++) { pf[x] |= 0x400; if (offnext && ((mo[x] == 0xffff) || (mo[x] & START_MARKER) != START_MARKER)) break; offnext = (mo[x] != 0xffff) && ((mo[x] & END_MARKER) == END_MARKER); } } //------------------------------------------------- // device_start: Start up the device //------------------------------------------------- void atari_motion_objects_device::device_start() { // call parent sprite16_device_ind16::device_start(); // verify configuration gfx_element *gfx = m_gfxdecode->gfx(m_gfxindex); if (gfx == nullptr) throw emu_fatalerror("No gfxelement #%d!", m_gfxindex); // determine the masks m_linkmask.set(m_link_entry); m_codemask.set(m_code_entry); m_colormask.set(m_color_entry); m_xposmask.set(m_xpos_entry); m_yposmask.set(m_ypos_entry); m_widthmask.set(m_width_entry); m_heightmask.set(m_height_entry); m_hflipmask.set(m_hflip_entry); m_vflipmask.set(m_vflip_entry); m_prioritymask.set(m_priority_entry); m_neighbormask.set(m_neighbor_entry); m_absolutemask.set(m_absolute_entry); // derive tile information m_tilewidth = gfx->width(); m_tileheight = gfx->height(); m_tilexshift = compute_log(m_tilewidth); m_tileyshift = compute_log(m_tileheight); // derive bitmap information m_bitmapwidth = round_to_powerof2(m_xposmask.mask()); m_bitmapheight = round_to_powerof2(m_yposmask.mask()); m_bitmapxmask = m_bitmapwidth - 1; m_bitmapymask = m_bitmapheight - 1; // derive sprite information m_entrycount = round_to_powerof2(m_linkmask.mask()); m_entrybits = compute_log(m_entrycount); m_spriteramsize = m_bankcount * m_entrycount; m_spriterammask = m_spriteramsize - 1; m_slipshift = (m_slipheight != 0) ? compute_log(m_slipheight) : 0; m_slipramsize = m_bitmapheight >> m_slipshift; m_sliprammask = m_slipramsize - 1; if (m_maxperline == 0) m_maxperline = MAX_PER_BANK; // allocate and initialize the code lookup int codesize = round_to_powerof2(m_codemask.mask()); m_codelookup.resize(codesize); for (int i = 0; i < codesize; i++) m_codelookup[i] = i; // allocate and initialize the color lookup int colorsize = round_to_powerof2(m_colormask.mask()); m_colorlookup.resize(colorsize); for (int i = 0; i < colorsize; i++) m_colorlookup[i] = i; // allocate and the gfx lookup int gfxsize = codesize / 256; m_gfxlookup.resize(gfxsize); for (int i = 0; i < gfxsize; i++) m_gfxlookup[i] = m_gfxindex; // allocate a timer to periodically force update m_force_update_timer = timer_alloc(TID_FORCE_UPDATE); m_force_update_timer->adjust(screen().time_until_pos(0)); // register for save states save_item(NAME(m_bank)); save_item(NAME(m_xscroll)); save_item(NAME(m_yscroll)); } //------------------------------------------------- // device_reset: Handle a device reset by // clearing the interrupt lines and states //------------------------------------------------- void atari_motion_objects_device::device_reset() { // call parent sprite16_device_ind16::device_reset(); // reset the live state m_bank = 0; } //------------------------------------------------- // device_timer: Handle device-specific timer // calbacks //------------------------------------------------- void atari_motion_objects_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch (id) { case TID_FORCE_UPDATE: if (param > 0) screen().update_partial(param - 1); param += 64; if (param >= screen().visible_area().max_y) param = 0; timer.adjust(screen().time_until_pos(param), param); break; } } //------------------------------------------------- // build_active_list: Build a list of active // objects. //------------------------------------------------- void atari_motion_objects_device::build_active_list(int link) { uint16_t *bankbase = &spriteram()[m_bank << (m_entrybits + 2)]; uint16_t *current = &m_activelist[0]; // visit all the motion objects and copy their data into the display list uint8_t visited[MAX_PER_BANK] = {0}; for (int i = 0; i < m_maxperline && !visited[link]; i++) { // copy the current entry into the list uint16_t *modata = current; if (!m_split) { uint16_t *srcdata = &bankbase[link * 4]; *current++ = srcdata[0]; *current++ = srcdata[1]; *current++ = srcdata[2]; *current++ = srcdata[3]; } else { uint16_t *srcdata = &bankbase[link]; *current++ = srcdata[uint32_t(0 << m_entrybits)]; *current++ = srcdata[uint32_t(1 << m_entrybits)]; *current++ = srcdata[uint32_t(2 << m_entrybits)]; *current++ = srcdata[uint32_t(3 << m_entrybits)]; } // link to the next object visited[link] = 1; if (m_linked) link = m_linkmask.extract(modata); else link = (link + 1) & m_linkmask.mask(); } // note the last entry m_activelast = current; } //------------------------------------------------- // render_object: Internal processing callback // that renders to the backing bitmap and then // copies the result to the destination. //------------------------------------------------- void atari_motion_objects_device::render_object(bitmap_ind16 &bitmap, const rectangle &cliprect, const uint16_t *entry) { // select the gfx element and save off key information int rawcode = m_codemask.extract(entry); gfx_element *gfx = m_gfxdecode->gfx(m_gfxlookup[rawcode >> 8]); int save_granularity = gfx->granularity(); int save_colorbase = gfx->colorbase(); int save_colors = gfx->colors(); gfx->set_granularity(1); gfx->set_colorbase(0); gfx->set_colors(65536); // extract data from the various words int code = m_codelookup[rawcode]; int color = m_colorlookup[m_colormask.extract(entry)]; int xpos = m_xposmask.extract(entry); int ypos = -m_yposmask.extract(entry); int hflip = m_hflipmask.extract(entry); int vflip = m_vflipmask.extract(entry); int width = m_widthmask.extract(entry) + 1; int height = m_heightmask.extract(entry) + 1; int priority = m_prioritymask.extract(entry); // compute the effective color, merging in priority color = (color * save_granularity) | (priority << PRIORITY_SHIFT); color += m_palettebase; // add in the scroll positions if we're not in absolute coordinates if (!m_absolutemask.extract(entry)) { xpos -= m_xscroll; ypos -= m_yscroll; } // adjust for height ypos -= height << m_tileyshift; // handle previous hold bits if (m_next_xpos != 123456) xpos = m_next_xpos; m_next_xpos = 123456; // check for the hold bit if (m_neighbormask.extract(entry) != 0) { if (!m_nextneighbor) xpos = m_last_xpos + m_tilewidth; else m_next_xpos = xpos + m_tilewidth; } m_last_xpos = xpos; // adjust the final coordinates xpos &= m_bitmapxmask; ypos &= m_bitmapymask; if (xpos >= bitmap.width()) xpos -= m_bitmapwidth; if (ypos >= bitmap.height()) ypos -= m_bitmapheight; // is this one special? if (m_specialmask.mask() == 0 || m_specialmask.extract(entry) != m_specialvalue) { // adjust for h flip int xadv = m_tilewidth; if (hflip) { xpos += (width - 1) << m_tilexshift; xadv = -xadv; } // adjust for v flip int yadv = m_tileheight; if (vflip) { ypos += (height - 1) << m_tileyshift; yadv = -yadv; } // standard order is: loop over Y first, then X if (!m_swapxy) { // loop over the height for (int y = 0, sy = ypos; y < height; y++, sy += yadv) { // clip the Y coordinate if (sy <= cliprect.min_y - m_tileheight) { code += width; continue; } else if (sy > cliprect.max_y) break; // loop over the width for (int x = 0, sx = xpos; x < width; x++, sx += xadv, code++) { // clip the X coordinate if (sx <= -cliprect.min_x - m_tilewidth || sx > cliprect.max_x) continue; // draw the sprite gfx->transpen_raw(bitmap,cliprect, code, color, hflip, vflip, sx, sy, m_transpen); mark_dirty(sx, sx + m_tilewidth - 1, sy, sy + m_tileheight - 1); } } } // alternative order is swapped else { // loop over the width for (int x = 0, sx = xpos; x < width; x++, sx += xadv) { // clip the X coordinate if (sx <= cliprect.min_x - m_tilewidth) { code += height; continue; } else if (sx > cliprect.max_x) break; // loop over the height for (int y = 0, sy = ypos; y < height; y++, sy += yadv, code++) { // clip the X coordinate if (sy <= -cliprect.min_y - m_tileheight || sy > cliprect.max_y) continue; // draw the sprite gfx->transpen_raw(bitmap,cliprect, code, color, hflip, vflip, sx, sy, m_transpen); mark_dirty(sx, sx + m_tilewidth - 1, sy, sy + m_tileheight - 1); } } } } // restore original gfx information gfx->set_granularity(save_granularity); gfx->set_colorbase(save_colorbase); gfx->set_colors(save_colors); } //************************************************************************** // SPRITE PARAMETER //************************************************************************** //------------------------------------------------- // sprite_parameter: Constructor //------------------------------------------------- atari_motion_objects_device::sprite_parameter::sprite_parameter() : m_word(0), m_shift(0), m_mask(0) { } //------------------------------------------------- // set: Sets the mask via an input 4-word mask. //------------------------------------------------- bool atari_motion_objects_device::sprite_parameter::set(const uint16_t input[4]) { // determine the word and make sure it's only 1 m_word = 0xffff; for (int i = 0; i < 4; i++) if (input[i]) { if (m_word == 0xffff) m_word = i; else return false; } // if all-zero, it's valid if (m_word == 0xffff) { m_word = m_shift = m_mask = 0; return true; } // determine the shift and final mask m_shift = 0; uint16_t temp = input[m_word]; while (!(temp & 1)) { m_shift++; temp >>= 1; } m_mask = temp; return true; } //------------------------------------------------- // dual_sprite_parameter: Constructor //------------------------------------------------- atari_motion_objects_device::dual_sprite_parameter::dual_sprite_parameter() : m_uppershift(0) { } //------------------------------------------------- // set: Sets the mask via an input 4-word mask. //------------------------------------------------- bool atari_motion_objects_device::dual_sprite_parameter::set(const atari_motion_objects_config::dual_entry &input) { // convert the lower and upper parts if (!m_lower.set(input.data_lower)) return false; if (!m_upper.set(input.data_upper)) return false; // determine the upper shift amount uint16_t temp = m_lower.mask(); m_uppershift = 0; while (temp != 0) { m_uppershift++; temp >>= 1; } return true; }