summaryrefslogtreecommitdiffstats
path: root/docs/release/src/mame/video/sega16sp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'docs/release/src/mame/video/sega16sp.cpp')
-rw-r--r--docs/release/src/mame/video/sega16sp.cpp1456
1 files changed, 1456 insertions, 0 deletions
diff --git a/docs/release/src/mame/video/sega16sp.cpp b/docs/release/src/mame/video/sega16sp.cpp
new file mode 100644
index 00000000000..bd02c6004bb
--- /dev/null
+++ b/docs/release/src/mame/video/sega16sp.cpp
@@ -0,0 +1,1456 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ Sega 16-bit sprite hardware
+
+***************************************************************************/
+
+#include "emu.h"
+#include "sega16sp.h"
+#include "segaic16.h"
+
+
+
+//****************************************************************************
+// CONSTANTS
+//****************************************************************************
+
+// device type definition
+DEFINE_DEVICE_TYPE(SEGA_HANGON_SPRITES, sega_hangon_sprite_device, "sega_hangon_sprite", "Sega Custom Sprites (Hang On)")
+DEFINE_DEVICE_TYPE(SEGA_SHARRIER_SPRITES, sega_sharrier_sprite_device, "sega_sharrier_sprite", "Sega Custom Sprites (Space Harrier)")
+DEFINE_DEVICE_TYPE(SEGA_OUTRUN_SPRITES, sega_outrun_sprite_device, "sega_outrun_sprite", "Sega Custom Sprites (Out Run)")
+DEFINE_DEVICE_TYPE(SEGA_SYS16A_SPRITES, sega_sys16a_sprite_device, "sega_sys16a_sprite", "Sega System 16A Sprites")
+DEFINE_DEVICE_TYPE(BOOTLEG_SYS16A_SPRITES, bootleg_sys16a_sprite_device, "bootleg_sys16a_sprite", "Sega System 16A Sprites (bootleg)")
+DEFINE_DEVICE_TYPE(SEGA_SYS16B_SPRITES, sega_sys16b_sprite_device, "sega_sys16b_sprite", "Sega System 16B Sprites")
+DEFINE_DEVICE_TYPE(SEGA_XBOARD_SPRITES, sega_xboard_sprite_device, "sega_xboard_sprite", "Sega X-Board Sprites")
+DEFINE_DEVICE_TYPE(SEGA_YBOARD_SPRITES, sega_yboard_sprite_device, "sega_yboard_sprite", "Sega Y-Board Sprites")
+
+
+
+//****************************************************************************
+// DEVICE INTERFACE
+//****************************************************************************
+
+//-------------------------------------------------
+// sega_16bit_sprite_device -- core constructor
+//-------------------------------------------------
+
+sega_16bit_sprite_device::sega_16bit_sprite_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner)
+ : sprite16_device_ind16(mconfig, type, tag, owner)
+ , m_flip(false)
+{
+ // default to 1:1 bank mapping
+ for (int bank = 0; bank < std::size(m_bank); bank++)
+ m_bank[bank] = bank;
+}
+
+
+//-------------------------------------------------
+// device_start -- device startup
+//-------------------------------------------------
+
+void sega_16bit_sprite_device::device_start()
+{
+ // let the parent do its work
+ sprite16_device_ind16::device_start();
+
+ // save states
+ save_item(NAME(m_flip));
+ save_item(NAME(m_bank));
+}
+
+
+//-------------------------------------------------
+// draw_write -- trigger a buffer flip
+//-------------------------------------------------
+
+void sega_16bit_sprite_device::draw_write(uint16_t data)
+{
+ uint32_t *src = reinterpret_cast<uint32_t *>(spriteram());
+ uint32_t *dst = reinterpret_cast<uint32_t *>(buffer());
+
+ // swap the halves of the sprite RAM
+ for (int i = 0; i < spriteram_bytes()/4; i++)
+ {
+ uint32_t temp = *src;
+ *src++ = *dst;
+ *dst++ = temp;
+ }
+
+ // hack for thunderblade
+ *spriteram() = 0xffff;
+
+ // we will render the sprites when the video update happens
+}
+
+
+
+//****************************************************************************
+// HANG ON-STYLE SPRITES
+//****************************************************************************
+
+//-------------------------------------------------
+// sega_hangon_sprite_device -- constructor
+//-------------------------------------------------
+
+sega_hangon_sprite_device::sega_hangon_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sega_16bit_sprite_device(mconfig, SEGA_HANGON_SPRITES, tag, owner)
+ , m_sprite_region_ptr(*this, DEVICE_SELF)
+{
+ set_local_origin(189, -1);
+}
+
+
+//-------------------------------------------------
+// draw -- render the sprites within the cliprect
+//-------------------------------------------------
+
+void sega_hangon_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ //
+ // Hang On-style sprites
+ //
+ // Offs Bits Usage
+ // +0 bbbbbbbb -------- Bottom scanline of sprite - 1
+ // +0 -------- tttttttt Top scanline of sprite - 1
+ // +2 bbbb---- -------- Sprite bank
+ // +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
+ // +4 pppppppp pppppppp Signed 16-bit pitch value between scanlines
+ // +6 -ooooooo oooooooo Offset within selected sprite bank
+ // +6 f------- -------- Horizontal flip: read the data backwards if set
+ // +8 --cccccc -------- Sprite color palette
+ // +8 -------- zzzzzz-- Zoom factor
+ // +8 -------- ------pp Sprite priority
+ // +E dddddddd dddddddd Scratch space for current address
+ //
+ // Final bitmap format:
+ //
+ // ----pp-- -------- Sprite priority
+ // ------cc cccc---- Sprite color palette
+ // -------- ----llll 4-bit pixel data
+ //
+ // Special notes:
+ //
+ // There is an interaction between the horizonal flip bit and the offset.
+ // The offset is maintained as a 16-bit value, even though only the lower
+ // 15 bits are used for the address. The top bit is used to control flipping.
+ // This means that if the low 15 bits overflow during rendering, the sprite
+ // data will be read backwards after the overflow. This is important to
+ // emulate correctly as many games make use of this feature to render sprites
+ // at the beginning of a bank.
+ //
+
+ // render the sprites in order
+ const uint16_t *spritebase = &m_sprite_region_ptr[0];
+ uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x10000;
+ const uint8_t *zoom = memregion("zoom")->base();
+ uint16_t *ramend = spriteram() + spriteram_elements();
+ for (uint16_t *data = spriteram(); data < ramend; data += 8)
+ {
+ // fetch the bottom; stop when we get something out of range
+ int bottom = data[0] >> 8;
+ if (bottom > 0xf0)
+ break;
+
+ // extract remaining parameters
+ int top = data[0] & 0xff;
+ int bank = m_bank[(data[1] >> 12) & 0xf];
+ int xpos = data[1] & 0x1ff;
+ int pitch = int16_t(data[2]);
+ uint16_t addr = data[3];
+ int colpri = (((data[4] >> 8) & 0x3f) << 4) | (((data[4] >> 0) & 0x3) << 10);
+ int vzoom = (data[4] >> 2) & 0x3f;
+ int hzoom = vzoom << 1;
+
+ // initialize the end address to the start address
+ data[7] = addr;
+
+ // if top greater than/equal to bottom, or invalid bank, punt
+ if (top >= bottom || bank == 255)
+ continue;
+
+ // clamp to within the memory region size
+ if (numbanks)
+ bank %= numbanks;
+ const uint16_t *spritedata = spritebase + 0x8000 * bank;
+
+ // determine the starting zoom address and mask
+ int zaddr = (vzoom & 0x38) << 5;
+ int zmask = 1 << (vzoom & 7);
+
+ // loop from top to bottom
+ int minx = xpos;
+ int maxx = cliprect.min_x - 1;
+ int miny = cliprect.max_y + 1;
+ int maxy = cliprect.min_y - 1;
+ for (int y = top; y < bottom; y++)
+ {
+ // advance a row
+ addr += pitch;
+
+ // if the zoom bit says so, add pitch a second time
+ if (zoom[zaddr++] & zmask)
+ addr += pitch;
+
+ // skip drawing if not within the cliprect
+ if (y >= cliprect.min_y && y <= cliprect.max_y)
+ {
+ uint16_t *dest = &bitmap.pix(y);
+ int xacc = 0x00;
+ int x;
+
+ // note that the System 16A sprites have a design flaw that allows the address
+ // to carry into the flip flag, which is the topmost bit -- it is very important
+ // to emulate this as the games compensate for it
+
+ // non-flipped case
+ if (!(addr & 0x8000))
+ {
+ // start at the word before because we preincrement below
+ data[7] = addr - 1;
+ for (x = xpos; x <= cliprect.max_x; )
+ {
+ uint16_t pixels = spritedata[++data[7] & 0x7fff];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ }
+ }
+
+ // flipped case
+ else
+ {
+ // start at the word after because we predecrement below
+ data[7] = addr + 1;
+ for (x = xpos; x <= cliprect.max_x; )
+ {
+ uint16_t pixels = spritedata[--data[7] & 0x7fff];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ }
+ }
+
+ // update bounds
+ if (x > maxx) maxx = x;
+ if (y < miny) miny = y;
+ maxy = y;
+ }
+ }
+
+ // mark dirty
+ if (minx <= maxx && miny <= maxy)
+ mark_dirty(minx, maxx, miny, maxy);
+ }
+}
+
+
+
+//****************************************************************************
+// SPACE HARRIER-STYLE SPRITES
+//****************************************************************************
+
+//-------------------------------------------------
+// sega_sharrier_sprite_device -- constructor
+//-------------------------------------------------
+
+sega_sharrier_sprite_device::sega_sharrier_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sega_16bit_sprite_device(mconfig, SEGA_SHARRIER_SPRITES, tag, owner)
+ , m_sprite_region_ptr(*this, DEVICE_SELF)
+{
+ set_local_origin(189, -1);
+}
+
+
+//-------------------------------------------------
+// draw -- render the sprites within the cliprect
+//-------------------------------------------------
+
+void sega_sharrier_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ //
+ // Space Harrier-style sprites
+ //
+ // Offs Bits Usage
+ // +0 bbbbbbbb -------- Bottom scanline of sprite - 1
+ // +0 -------- tttttttt Top scanline of sprite - 1
+ // +2 bbbb---- -------- Sprite bank
+ // +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
+ // +4 s------- -------- Sprite shadow disable (0=enable, 1=disable)
+ // +4 -p------ -------- Sprite priority
+ // +4 --cccccc -------- Sprite color palette
+ // +4 -------- -ppppppp Signed 7-bit pitch value between scanlines
+ // +6 f------- -------- Horizontal flip: read the data backwards if set
+ // +6 -ooooooo oooooooo Offset within selected sprite bank
+ // +8 --zzzzzz -------- Horizontal zoom factor
+ // +8 -------- --zzzzzz Vertical zoom factor
+ // +E dddddddd dddddddd Scratch space for current address
+ //
+ // Final bitmap format:
+ //
+ // ----s--- -------- Sprite shadow disable
+ // -----p-- -------- Sprite priority
+ // ------cc cccc---- Sprite color palette
+ // -------- ----llll 4-bit pixel data
+ //
+ // Special notes:
+ //
+ // There is an interaction between the horizonal flip bit and the offset.
+ // The offset is maintained as a 16-bit value, even though only the lower
+ // 15 bits are used for the address. The top bit is used to control flipping.
+ // This means that if the low 15 bits overflow during rendering, the sprite
+ // data will be read backwards after the overflow. This is important to
+ // emulate correctly as many games make use of this feature to render sprites
+ // at the beginning of a bank.
+ //
+
+ // render the sprites in order
+ const uint32_t *spritebase = &m_sprite_region_ptr[0];
+ uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x20000;
+ const uint8_t *zoom = memregion("zoom")->base();
+ uint16_t *ramend = spriteram() + spriteram_elements();
+ for (uint16_t *data = spriteram(); data < ramend; data += 8)
+ {
+ // fetch the bottom; stop when we get something out of range
+ int bottom = data[0] >> 8;
+ if (bottom > 0xf0)
+ break;
+
+ // extract remaining parameters
+ int top = data[0] & 0xff;
+ int bank = m_bank[(data[1] >> 12) & 0x7];
+ int xpos = data[1] & 0x1ff;
+ int colpri = ((data[2] >> 8) & 0xff) << 4;
+ int pitch = int16_t(data[2] << 9) >> 9;
+ uint16_t addr = data[3];
+ int hzoom = ((data[4] >> 8) & 0x3f) << 1;
+ int vzoom = (data[4] >> 0) & 0x3f;
+
+ // initialize the end address to the start address
+ data[7] = addr;
+
+ // if top greater than/equal to bottom, or invalid bank, punt
+ if (top >= bottom || bank == 255)
+ continue;
+
+ // clamp to within the memory region size
+ if (numbanks)
+ bank %= numbanks;
+ const uint32_t *spritedata = spritebase + 0x8000 * bank;
+
+ // determine the starting zoom address and mask
+ int zaddr = (vzoom & 0x38) << 5;
+ int zmask = 1 << (vzoom & 7);
+
+ // loop from top to bottom
+ int minx = xpos;
+ int maxx = cliprect.min_x - 1;
+ int miny = cliprect.max_y + 1;
+ int maxy = cliprect.min_y - 1;
+ for (int y = top; y < bottom; y++)
+ {
+ // advance a row
+ addr += pitch;
+
+ // if the zoom bit says so, add pitch a second time
+ if (zoom[zaddr++] & zmask)
+ addr += pitch;
+
+ // skip drawing if not within the cliprect
+ if (y >= cliprect.min_y && y <= cliprect.max_y)
+ {
+ uint16_t *dest = &bitmap.pix(y);
+ int xacc = 0x00;
+ int x;
+
+ // note that the System 16A sprites have a design flaw that allows the address
+ // to carry into the flip flag, which is the topmost bit -- it is very important
+ // to emulate this as the games compensate for it
+
+ // non-flipped case
+ if (!(addr & 0x8000))
+ {
+ // start at the word before because we preincrement below
+ data[7] = addr - 1;
+ for (x = xpos; x <= cliprect.max_x; )
+ {
+ uint32_t pixels = spritedata[++data[7] & 0x7fff];
+
+ // draw 8 pixels
+ int pix;
+ pix = (pixels >> 28) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 24) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 20) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 16) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ }
+ }
+
+ // flipped case
+ else
+ {
+ // start at the word after because we predecrement below
+ data[7] = addr + 1;
+ for (x = xpos; x <= cliprect.max_x; )
+ {
+ uint32_t pixels = spritedata[--data[7] & 0x7fff];
+
+ // draw 8 pixels
+ int pix;
+ pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 16) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 20) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 24) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+ pix = (pixels >> 28) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ }
+ }
+
+ // update bounds
+ if (x > maxx) maxx = x;
+ if (y < miny) miny = y;
+ maxy = y;
+ }
+ }
+
+ // mark dirty
+ if (minx <= maxx && miny <= maxy)
+ mark_dirty(minx, maxx, miny, maxy);
+ }
+}
+
+
+
+//****************************************************************************
+// SYSTEM 16A-STYLE SPRITES
+//****************************************************************************
+
+//-------------------------------------------------
+// sega_sys16a_sprite_device -- constructor
+//-------------------------------------------------
+
+sega_sys16a_sprite_device::sega_sys16a_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sega_16bit_sprite_device(mconfig, SEGA_SYS16A_SPRITES, tag, owner)
+ , m_sprite_region_ptr(*this, DEVICE_SELF)
+{
+ set_local_origin(189, -1, -189, -1);
+}
+
+
+//-------------------------------------------------
+// draw -- render the sprites within the cliprect
+//-------------------------------------------------
+
+void sega_sys16a_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ //
+ // System 16A-style sprites
+ //
+ // Offs Bits Usage
+ // +0 bbbbbbbb -------- Bottom scanline of sprite - 1
+ // +0 -------- tttttttt Top scanline of sprite - 1
+ // +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
+ // +4 pppppppp pppppppp Signed 16-bit pitch value between scanlines
+ // +6 -ooooooo oooooooo Offset within selected sprite bank
+ // +6 f------- -------- Horizontal flip: read the data backwards if set
+ // +8 --cccccc -------- Sprite color palette
+ // +8 -------- -bbb---- Sprite bank
+ // +8 -------- ------pp Sprite priority
+ // +E dddddddd dddddddd Scratch space for current address
+ //
+ // Final bitmap format:
+ //
+ // ----pp-- -------- Sprite priority
+ // ------cc cccc---- Sprite color palette
+ // -------- ----llll 4-bit pixel data
+ //
+ // Special notes:
+ //
+ // There is an interaction between the horizonal flip bit and the offset.
+ // The offset is maintained as a 16-bit value, even though only the lower
+ // 15 bits are used for the address. The top bit is used to control flipping.
+ // This means that if the low 15 bits overflow during rendering, the sprite
+ // data will be read backwards after the overflow. This is important to
+ // emulate correctly as many games make use of this feature to render sprites
+ // at the beginning of a bank.
+ //
+
+ // render the sprites in order
+ const uint16_t *spritebase = &m_sprite_region_ptr[0];
+ uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x10000;
+ uint16_t *ramend = spriteram() + spriteram_elements();
+ for (uint16_t *data = spriteram(); data < ramend; data += 8)
+ {
+ // fetch the bottom; stop when we get something out of range
+ int bottom = data[0] >> 8;
+ if (bottom > 0xf0)
+ break;
+
+ // extract remaining parameters
+ int top = data[0] & 0xff;
+ int xpos = data[1] & 0x1ff;
+ int pitch = int16_t(data[2]);
+ uint16_t addr = data[3];
+ int colpri = (((data[4] >> 8) & 0x3f) << 4) | (((data[4] >> 0) & 0x3) << 10);
+ int bank = m_bank[(data[4] >> 4) & 0x7];
+
+ // initialize the end address to the start address
+ data[7] = addr;
+
+ // if top greater than/equal to bottom, or invalid bank, punt
+ if (top >= bottom || bank == 255)
+ continue;
+
+ // clamp to within the memory region size
+ if (numbanks)
+ bank %= numbanks;
+ const uint16_t *spritedata = spritebase + 0x8000 * bank;
+
+ // adjust positions for screen flipping
+ int xdelta = 1;
+ if (m_flip)
+ {
+ int temp = top;
+ top = 224 - bottom;
+ bottom = 224 - temp;
+ xpos = 320 - xpos;
+ xdelta = -1;
+ set_origin(m_xoffs_flipped, m_yoffs_flipped);
+ }
+ else
+ {
+ set_origin(m_xoffs, m_yoffs);
+ }
+
+ // loop from top to bottom
+ int minx = xpos;
+ int maxx = xpos;
+ int miny = cliprect.max_y + 1;
+ int maxy = cliprect.min_y - 1;
+ for (int y = top; y < bottom; y++)
+ {
+ // advance a row
+ addr += pitch;
+
+ // skip drawing if not within the cliprect
+ if (y >= cliprect.min_y && y <= cliprect.max_y)
+ {
+ uint16_t *dest = &bitmap.pix(y);
+ int x;
+
+ // note that the System 16A sprites have a design flaw that allows the address
+ // to carry into the flip flag, which is the topmost bit -- it is very important
+ // to emulate this as the games compensate for it
+
+ // non-flipped case
+ if (!(addr & 0x8000))
+ {
+ // start at the word before because we preincrement below
+ data[7] = addr - 1;
+ for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
+ {
+ uint16_t pixels = spritedata[++data[7] & 0x7fff];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 12) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 8) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 4) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 0) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ }
+ }
+
+ // flipped case
+ else
+ {
+ // start at the word after because we predecrement below
+ data[7] = addr + 1;
+ for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
+ {
+ uint16_t pixels = spritedata[--data[7] & 0x7fff];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 0) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 4) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 8) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 12) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ }
+ }
+
+ // update bounds
+ if (x > maxx) maxx = x;
+ if (x < minx) minx = x;
+ if (y < miny) miny = y;
+ maxy = y;
+ }
+ }
+
+ // mark dirty
+ if (minx <= maxx && miny <= maxy)
+ mark_dirty(minx, maxx, miny, maxy);
+ }
+}
+
+
+//****************************************************************************
+// BOOTLEG SYSTEM 16A-STYLE SPRITES
+//****************************************************************************
+
+//-------------------------------------------------
+// bootleg_sys16a_sprite_device -- constructor
+//-------------------------------------------------
+
+bootleg_sys16a_sprite_device::bootleg_sys16a_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sega_16bit_sprite_device(mconfig, BOOTLEG_SYS16A_SPRITES, tag, owner)
+ , m_sprite_region_ptr(*this, DEVICE_SELF)
+{
+ m_addrmap[0] = 0;
+ m_addrmap[1] = 1;
+ m_addrmap[2] = 2;
+ m_addrmap[3] = 3;
+ m_addrmap[4] = 4;
+ m_addrmap[5] = 5;
+ m_addrmap[6] = 6;
+ m_addrmap[7] = 7;
+ set_local_origin(189, -1);
+}
+
+
+//-------------------------------------------------
+// set_remap -- configure sprite address
+// remapping
+//-------------------------------------------------
+
+void bootleg_sys16a_sprite_device::set_remap(uint8_t offs0, uint8_t offs1, uint8_t offs2, uint8_t offs3, uint8_t offs4, uint8_t offs5, uint8_t offs6, uint8_t offs7)
+{
+ m_addrmap[0] = offs0;
+ m_addrmap[1] = offs1;
+ m_addrmap[2] = offs2;
+ m_addrmap[3] = offs3;
+ m_addrmap[4] = offs4;
+ m_addrmap[5] = offs5;
+ m_addrmap[6] = offs6;
+ m_addrmap[7] = offs7;
+}
+
+
+//-------------------------------------------------
+// draw -- render the sprites within the cliprect
+//-------------------------------------------------
+
+void bootleg_sys16a_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ //
+ // Bootleg System 16A-style sprites
+ //
+ // These are identical to regular System 16A sprites (see above), with two exceptions:
+ //
+ // 1. Addresses within each sprite entry are generally shuffled relative
+ // to the original, and
+ //
+ // 2. The pitch increment happens at the end, not at the beginning of
+ // the loop.
+ //
+
+ // render the sprites in order
+ const uint16_t *spritebase = &m_sprite_region_ptr[0];
+ uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x10000;
+ uint16_t *ramend = spriteram() + spriteram_elements();
+ for (uint16_t *data = spriteram(); data < ramend; data += 8)
+ {
+ // fetch the bottom; stop when we get something out of range
+ int bottom = data[m_addrmap[0]] >> 8;
+ if (bottom > 0xf0)
+ break;
+
+ // extract remaining parameters
+ int top = data[m_addrmap[0]] & 0xff;
+ int xpos = data[m_addrmap[1]] & 0x1ff;
+ int pitch = int16_t(data[m_addrmap[2]]);
+ uint16_t addr = data[m_addrmap[3]];
+ int colpri = (((data[m_addrmap[4]] >> 8) & 0x3f) << 4) | (((data[m_addrmap[4]] >> 0) & 0x3) << 10);
+ int bank = m_bank[(data[m_addrmap[4]] >> 4) & 0x7];
+
+ // initialize the end address to the start address
+ uint16_t &data7 = data[m_addrmap[7]];
+ data7 = addr;
+
+ // if top greater than/equal to bottom, or invalid bank, punt
+ if (top >= bottom || bank == 255)
+ continue;
+
+ // clamp to within the memory region size
+ if (numbanks)
+ bank %= numbanks;
+ const uint16_t *spritedata = spritebase + 0x8000 * bank;
+
+ // adjust positions for screen flipping
+ int xdelta = 1;
+ if (m_flip)
+ {
+ int temp = top;
+ top = 224 - bottom;
+ bottom = 224 - temp;
+ xpos = 320 - xpos;
+ xdelta = -1;
+ set_origin(m_xoffs_flipped, m_yoffs_flipped);
+ }
+ else
+ {
+ set_origin(m_xoffs, m_yoffs);
+ }
+
+ // loop from top to bottom
+ int minx = xpos;
+ int maxx = xpos;
+ int miny = cliprect.max_y + 1;
+ int maxy = cliprect.min_y - 1;
+ for (int y = top; y < bottom; y++)
+ {
+ // skip drawing if not within the cliprect
+ if (y >= cliprect.min_y && y <= cliprect.max_y)
+ {
+ uint16_t *dest = &bitmap.pix(y);
+ int x;
+
+ // note that the System 16A sprites have a design flaw that allows the address
+ // to carry into the flip flag, which is the topmost bit -- it is very important
+ // to emulate this as the games compensate for it
+
+ // non-flipped case
+ if (!(addr & 0x8000))
+ {
+ // start at the word before because we preincrement below
+ data7 = addr - 1;
+ for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
+ {
+ uint16_t pixels = spritedata[++data7 & 0x7fff];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 12) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 8) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 4) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 0) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ }
+ }
+
+ // flipped case
+ else
+ {
+ // start at the word after because we predecrement below
+ data7 = addr + 1;
+ for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
+ {
+ uint16_t pixels = spritedata[--data7 & 0x7fff];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 0) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 4) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 8) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+ pix = (pixels >> 12) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ }
+ }
+
+ // update bounds
+ if (x > maxx) maxx = x;
+ if (x < minx) minx = x;
+ if (y < miny) miny = y;
+ maxy = y;
+ }
+
+ // advance a row - must be done at the end on the bootlegs!
+ addr += pitch;
+ }
+
+ // mark dirty
+ if (minx <= maxx && miny <= maxy)
+ mark_dirty(minx, maxx, miny, maxy);
+ }
+}
+
+
+
+//****************************************************************************
+// SYSTEM 16B-STYLE SPRITES
+//****************************************************************************
+
+//-------------------------------------------------
+// sega_sys16b_sprite_device -- constructor
+//-------------------------------------------------
+
+sega_sys16b_sprite_device::sega_sys16b_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sega_16bit_sprite_device(mconfig, SEGA_SYS16B_SPRITES, tag, owner)
+ , m_sprite_region_ptr(*this, DEVICE_SELF)
+{
+ set_local_origin(184, 0x00, -184, 0);
+}
+
+
+//-------------------------------------------------
+// draw -- render the sprites within the cliprect
+//-------------------------------------------------
+
+void sega_sys16b_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ //
+ // System 16B-style sprites
+ //
+ // Offs Bits Usage
+ // +0 bbbbbbbb -------- Bottom scanline of sprite - 1
+ // +0 -------- tttttttt Top scanline of sprite - 1
+ // +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
+ // +2 ---iiii- -------- Sprite/sprite priority for Y-board
+ // +4 e------- -------- Signify end of sprite list
+ // +4 -h------ -------- Hide this sprite
+ // +4 -------f -------- Horizontal flip: read the data backwards if set
+ // +4 -------- pppppppp Signed 8-bit pitch value between scanlines
+ // +6 oooooooo oooooooo Offset within selected sprite bank
+ // +8 ----bbbb -------- Sprite bank
+ // +8 -------- pp------ Sprite priority, relative to tilemaps
+ // +8 -------- --cccccc Sprite color palette
+ // +A ------vv vvv----- Vertical zoom factor (0 = full size, 0x10 = half size)
+ // +A -------- ---hhhhh Horizontal zoom factor (0 = full size, 0x10 = half size)
+ // +E dddddddd dddddddd Scratch space for current address
+ //
+ // Final bitmap format:
+ //
+ // iiii---- -------- Sprite/sprite priority for Y-board
+ // ----pp-- -------- Sprite priority
+ // ------cc cccc---- Sprite color palette
+ // -------- ----llll 4-bit pixel data
+ //
+ // Note that the zooming described below is 100% accurate to the real board.
+ //
+
+ // render the sprites in order
+ const uint16_t *spritebase = &m_sprite_region_ptr[0];
+ uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x20000;
+ uint16_t *ramend = spriteram() + spriteram_elements();
+ for (uint16_t *data = spriteram(); data < ramend; data += 8)
+ {
+ // stop when we hit the end of sprite list
+ if (data[2] & 0x8000)
+ break;
+
+ // extract parameters
+ int bottom = data[0] >> 8;
+ int top = data[0] & 0xff;
+ int xpos = data[1] & 0x1ff;
+ int hide = data[2] & 0x4000;
+ int flip = data[2] & 0x100;
+ int pitch = int8_t(data[2] & 0xff);
+ uint16_t addr = data[3];
+ int bank = m_bank[(data[4] >> 8) & 0xf];
+ int colpri = ((data[4] & 0xff) << 4) | (((data[1] >> 9) & 0xf) << 12);
+ int vzoom = (data[5] >> 5) & 0x1f;
+ int hzoom = data[5] & 0x1f;
+ const uint16_t *spritedata;
+
+ xpos &= 0x1ff;
+
+ // initialize the end address to the start address
+ data[7] = addr;
+
+ // if hidden, or top greater than/equal to bottom, or invalid bank, punt
+ if (hide || top >= bottom || bank == 255)
+ continue;
+
+ // clamp to within the memory region size
+ if (numbanks)
+ bank %= numbanks;
+ spritedata = spritebase + 0x10000 * bank;
+
+ // reset the yzoom counter
+ data[5] &= 0x03ff;
+
+ // adjust positions for screen flipping
+ int xdelta = 1;
+ if (m_flip)
+ {
+ int temp = top;
+ top = 224 - bottom;
+ bottom = 224 - temp;
+ xpos = 320 - xpos;
+ xdelta = -1;
+ set_origin(m_xoffs_flipped, m_yoffs_flipped);
+ }
+ else
+ {
+ set_origin(m_xoffs, m_yoffs);
+ }
+
+ // loop from top to bottom
+ int minx = xpos;
+ int maxx = xpos;
+ int miny = cliprect.max_y + 1;
+ int maxy = cliprect.min_y - 1;
+ for (int y = top; y < bottom; y++)
+ {
+ // advance a row
+ addr += pitch;
+
+ // accumulate zoom factors; if we carry into the high bit, skip an extra row
+ data[5] += vzoom << 10;
+ if (data[5] & 0x8000)
+ {
+ addr += pitch;
+ data[5] &= ~0x8000;
+ }
+
+ // skip drawing if not within the cliprect
+ if (y >= cliprect.min_y && y <= cliprect.max_y)
+ {
+ uint16_t *dest = &bitmap.pix(y);
+ int x;
+
+ // compute the initial X zoom accumulator; this is verified on the real PCB
+ int xacc = 4 * hzoom;
+
+ // non-flipped case
+ if (!flip)
+ {
+ // start at the word before because we preincrement below
+ data[7] = addr - 1;
+ for (x = xpos+11; ((xpos - x) & 0x1ff) != 1; ) // HBMAME - added offset to centre the sprite in the box.
+ {
+ uint16_t pixels = spritedata[++data[7]];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 12) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
+ pix = (pixels >> 8) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
+ pix = (pixels >> 4) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
+ pix = (pixels >> 0) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ if ((data[7] - addr) == 7) break; // HBMAME - added this to stop the freezing - the 7 is critical.
+ }
+ }
+
+ // flipped case
+ else
+ {
+ // start at the word after because we predecrement below
+ data[7] = addr + 1;
+ for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
+ {
+ uint16_t pixels = spritedata[--data[7]];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 0) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
+ pix = (pixels >> 4) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
+ pix = (pixels >> 8) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
+ pix = (pixels >> 12) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 15)
+ break;
+ }
+ }
+
+ // update bounds
+ if (x > maxx) maxx = x;
+ if (x < minx) minx = x;
+ if (y < miny) miny = y;
+ maxy = y;
+ }
+ }
+
+ // mark dirty
+ if (minx <= maxx && miny <= maxy)
+ mark_dirty(minx, maxx, miny, maxy);
+ }
+}
+
+
+
+//****************************************************************************
+// OUT RUN/X-BOARD-STYLE SPRITES
+//****************************************************************************
+
+//-------------------------------------------------
+// sega_outrun_sprite_device -- constructor
+//-------------------------------------------------
+
+sega_outrun_sprite_device::sega_outrun_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sega_outrun_sprite_device(mconfig, SEGA_OUTRUN_SPRITES, tag, owner, clock, false)
+{
+}
+
+sega_outrun_sprite_device::sega_outrun_sprite_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool xboard_variant)
+ : sega_16bit_sprite_device(mconfig, type, tag, owner)
+ , m_is_xboard(xboard_variant)
+ , m_sprite_region_ptr(*this, DEVICE_SELF)
+{
+ set_local_origin(xboard_variant ? 190 : 189, 0x00);
+}
+
+
+//-------------------------------------------------
+// sega_xboard_sprite_device -- constructor
+//-------------------------------------------------
+
+sega_xboard_sprite_device::sega_xboard_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sega_outrun_sprite_device(mconfig, SEGA_XBOARD_SPRITES, tag, owner, clock, true)
+{
+}
+
+
+//-------------------------------------------------
+// draw -- render the sprites within the cliprect
+//-------------------------------------------------
+
+void sega_outrun_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ //
+ // Out Run/X-Board-style sprites
+ //
+ // Offs Bits Usage
+ // +0 e------- -------- Signify end of sprite list
+ // +0 -h-h---- -------- Hide this sprite if either bit is set
+ // +0 ----bbb- -------- Sprite bank
+ // +0 -------t tttttttt Top scanline of sprite + 256
+ // +2 oooooooo oooooooo Offset within selected sprite bank
+ // +4 ppppppp- -------- Signed 7-bit pitch value between scanlines
+ // +4 -------x xxxxxxxx X position of sprite (position $BE is screen position 0)
+ // +6 -s------ -------- Enable shadows
+ // +6 --pp---- -------- Sprite priority, relative to tilemaps
+ // +6 ------vv vvvvvvvv Vertical zoom factor (0x200 = full size, 0x100 = half size, 0x300 = 2x size)
+ // +8 y------- -------- Render from top-to-bottom (1) or bottom-to-top (0) on screen
+ // +8 -f------ -------- Horizontal flip: read the data backwards if set
+ // +8 --x----- -------- Render from left-to-right (1) or right-to-left (0) on screen
+ // +8 ------hh hhhhhhhh Horizontal zoom factor (0x200 = full size, 0x100 = half size, 0x300 = 2x size)
+ // +E dddddddd dddddddd Scratch space for current address
+ //
+ // Out Run only:
+ // +A hhhhhhhh -------- Height in scanlines - 1
+ // +A -------- -ccccccc Sprite color palette
+ //
+ // X-Board only:
+ // +A ----hhhh hhhhhhhh Height in scanlines - 1
+ // +C -------- cccccccc Sprite color palette
+ //
+ // Final bitmap format:
+ //
+ // -s------ -------- Shadow control
+ // --pp---- -------- Sprite priority
+ // ----cccc cccc---- Sprite color palette
+ // -------- ----llll 4-bit pixel data
+ //
+
+ set_origin(m_xoffs, m_yoffs);
+
+ // render the sprites in order
+ const uint32_t *spritebase = &m_sprite_region_ptr[0];
+ uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x40000;
+ uint16_t *ramend = buffer() + spriteram_elements();
+ for (uint16_t *data = buffer(); data < ramend; data += 8)
+ {
+ // stop when we hit the end of sprite list
+ if (data[0] & 0x8000)
+ break;
+
+ // extract parameters
+ int hide = (data[0] & 0x5000);
+ int bank = (data[0] >> 9) & 7;
+ int top = (data[0] & 0x1ff) - 0x100;
+ uint16_t addr = data[1];
+ int pitch = int16_t((data[2] >> 1) | ((data[4] & 0x1000) << 3)) >> 8;
+ int xpos = data[2] & 0x1ff;
+ int vzoom = data[3] & 0x7ff;
+ int ydelta = (data[4] & 0x8000) ? 1 : -1;
+ int flip = (~data[4] >> 14) & 1;
+ int xdelta = (data[4] & 0x2000) ? 1 : -1;
+ int hzoom = data[4] & 0x7ff;
+ int height = (m_is_xboard ? (data[5] & 0xfff) : (data[5] >> 8)) + 1;
+ int colpri = ((m_is_xboard ? (data[6] & 0xff) : (data[5] & 0x7f)) << 4) | (((data[3] >> 12) & 7) << 12);
+
+ // adjust X coordinate
+ // note: the threshhold below is a guess. If it is too high, rachero will draw garbage
+ // If it is too low, smgp won't draw the bottom part of the road
+ if (xpos < 0x80 && xdelta < 0)
+ xpos += 0x200;
+
+ // initialize the end address to the start address
+ data[7] = addr;
+
+ // if hidden, punt
+ if (hide)
+ continue;
+
+ // clamp to within the memory region size
+ if (numbanks)
+ bank %= numbanks;
+ const uint32_t *spritedata = spritebase + 0x10000 * bank;
+
+ // clamp to a maximum of 8x (not 100% confirmed)
+ if (vzoom < 0x40) vzoom = 0x40;
+ if (hzoom < 0x40) hzoom = 0x40;
+
+ // loop from top to bottom
+ int minx = xpos;
+ int maxx = xpos;
+ int miny = cliprect.max_y + 1;
+ int maxy = cliprect.min_y - 1;
+ int yacc = 0;
+ int ytarget = top + ydelta * height;
+ for (int y = top; y != ytarget; y += ydelta)
+ {
+ // skip drawing if not within the cliprect
+ if (y >= cliprect.min_y && y <= cliprect.max_y)
+ {
+ uint16_t *dest = &bitmap.pix(y);
+ int xacc = 0;
+ int x;
+
+ // non-flipped case
+ if (!flip)
+ {
+ // start at the word before because we preincrement below
+ data[7] = addr - 1;
+ for (x = xpos; (xdelta > 0 && x <= cliprect.max_x) || (xdelta < 0 && x >= cliprect.min_x); )
+ {
+ uint32_t pixels = spritedata[++data[7]];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 28) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 24) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 20) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 16) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 12) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 8) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 4) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 0) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+
+ // stop if the second-to-last pixel in the group was 0xf
+ if ((pixels & 0x000000f0) == 0x000000f0)
+ break;
+ }
+ }
+
+ // flipped case
+ else
+ {
+ // start at the word after because we predecrement below
+ data[7] = addr + 1;
+ for (x = xpos; (xdelta > 0 && x <= cliprect.max_x) || (xdelta < 0 && x >= cliprect.min_x); )
+ {
+ uint32_t pixels = spritedata[--data[7]];
+
+ // draw four pixels
+ int pix;
+ pix = (pixels >> 0) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 4) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 8) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 12) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 16) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 20) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 24) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+ pix = (pixels >> 28) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
+
+ // stop if the second-to-last pixel in the group was 0xf
+ if ((pixels & 0x0f000000) == 0x0f000000)
+ break;
+ }
+ }
+
+ // update bounds
+ if (x > maxx) maxx = x;
+ if (x < minx) minx = x;
+ if (y < miny) miny = y;
+ if (y > maxy) maxy = y;
+ }
+
+ // accumulate zoom factors; if we carry into the high bit, skip an extra row
+ yacc += vzoom;
+ addr += pitch * (yacc >> 9);
+ yacc &= 0x1ff;
+ }
+
+ // mark dirty
+ if (minx <= maxx && miny <= maxy)
+ mark_dirty(minx, maxx, miny, maxy);
+ }
+}
+
+
+
+//****************************************************************************
+// Y BOARD-STYLE SPRITES
+//****************************************************************************
+
+//-------------------------------------------------
+// sega_yboard_sprite_device -- constructor
+//-------------------------------------------------
+
+sega_yboard_sprite_device::sega_yboard_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : sega_16bit_sprite_device(mconfig, SEGA_YBOARD_SPRITES, tag, owner)
+ , m_sprite_region_ptr(*this, DEVICE_SELF)
+{
+ set_local_origin(0x600, 0x600);
+}
+
+
+//-------------------------------------------------
+// draw -- render the sprites within the cliprect
+//-------------------------------------------------
+
+void sega_yboard_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ //
+ // Y-Board-style sprites
+ //
+ // Offs Bits Usage
+ // +0 e------- -------- Signify end of sprite list
+ // +0 -----iii iiiiiiii Address of indirection table (/16)
+ // +2 bbbb---- -------- Upper 4 bits of bank index
+ // +2 ----xxxx xxxxxxxx X position of sprite (position $600 is screen position 0)
+ // +4 bbbb---- -------- Lower 4 bits of bank index
+ // +4 ----yyyy yyyyyyyy Y position of sprite (position $600 is screen position 0)
+ // +6 oooooooo oooooooo Offset within selected sprite bank
+ // +8 hhhhhhhh hhhhhhhh Height of sprite
+ // +A -y------ -------- Render from top-to-bottom (1) or bottom-to-top (0) on screen
+ // +A --f----- -------- Horizontal flip: read the data backwards if set
+ // +A ---x---- -------- Render from left-to-right (1) or right-to-left (0) on screen
+ // +A -----zzz zzzzzzzz Zoom factor
+ // +C -ccc---- -------- Sprite color
+ // +C ----rrrr -------- Sprite priority
+ // +C -------- pppppppp Signed 8-bit pitch value between scanlines
+ // +E ----nnnn nnnnnnnn Index of next sprite
+ //
+ // Final bitmap format:
+ //
+ // ccc----- -------- Sprite color
+ // ---rrrr- -------- Sprite priority
+ // -------i iiiiiiii Indirected color data
+ //
+ // In addition to these parameters, the sprite area is clipped using scanline extents
+ // stored for every pair of scanlines in the rotation RAM. It's a bit of a cheat for us
+ // to poke our nose into the rotation structure, but there are no known cases of Y-board
+ // sprites without rotation RAM.
+ //
+
+ set_origin(m_xoffs, m_yoffs);
+
+ // clear out any scanlines we might be using
+ const uint16_t *rotatebase = m_segaic16_rotate[0].buffer ? m_segaic16_rotate[0].buffer.get() : m_segaic16_rotate[0].rotateram;
+ rotatebase -= yorigin();
+ for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
+ if (!(rotatebase[y & ~1] & 0xc000))
+ memset(&bitmap.pix(y, cliprect.min_x), 0xff, cliprect.width() * sizeof(uint16_t));
+
+ // reset the visited list
+ uint8_t visited[0x1000];
+ memset(visited, 0, sizeof(visited));
+
+ // render the sprites in order
+ const uint64_t *spritebase = &m_sprite_region_ptr[0];
+ uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x80000;
+ int next = 0;
+ for (uint16_t *data = spriteram(); !(data[0] & 0x8000) && !visited[next]; data = spriteram() + next * 8)
+ {
+ int hide = (data[0] & 0x5000);
+ const uint16_t *indirect = spriteram() + ((data[0] & 0x7ff) << 4);
+ int bank = ((data[1] >> 8) & 0x10) | ((data[2] >> 12) & 0x0f);
+ int xpos = data[1] & 0xfff;
+ int top = data[2] & 0xfff;
+ uint16_t addr = data[3];
+ int height = data[4];
+ int ydelta = (data[5] & 0x4000) ? 1 : -1;
+ int flip = (~data[5] >> 13) & 1;
+ int xdelta = (data[5] & 0x1000) ? 1 : -1;
+ int zoom = data[5] & 0x7ff;
+ int colpri = (data[6] << 1) & 0xfe00;
+ int pitch = int8_t(data[6]);
+
+ // note that we've visited this entry and get the offset of the next one
+ visited[next] = 1;
+ next = data[7] & 0xfff;
+
+ // if hidden, or invalid height, punt
+ if (hide || height == 0)
+ continue;
+
+ // clamp to within the memory region size
+ if (numbanks)
+ bank %= numbanks;
+ const uint64_t *spritedata = spritebase + 0x10000 * bank;
+
+ // clamp to a maximum of 8x (not 100% confirmed)
+ if (zoom == 0) zoom = 1;
+
+ // loop from top to bottom
+ int dminx = xpos;
+ int dmaxx = xpos;
+ int dminy = cliprect.max_y + 1;
+ int dmaxy = cliprect.min_y - 1;
+ int ytarget = top + ydelta * height;
+ int yacc = 0;
+ for (int y = top; y != ytarget; y += ydelta)
+ {
+ // skip drawing if not within the cliprect
+ if (y >= cliprect.min_y && y <= cliprect.max_y)
+ {
+ uint16_t *dest = &bitmap.pix(y);
+ int minx = rotatebase[y & ~1];
+ int maxx = rotatebase[y | 1];
+ int xacc = 0;
+
+ // bit 0x8000 from rotate RAM means that Y is above the top of the screen
+ if ((minx & 0x8000) && ydelta < 0)
+ break;
+
+ // bit 0x4000 from rotate RAM means that Y is below the bottom of the screen
+ if ((minx & 0x4000) && ydelta > 0)
+ break;
+
+ // if either bit is set, skip the rest for this scanline
+ if (!(minx & 0xc000))
+ {
+ // clamp min/max to the cliprect
+ if (minx < cliprect.min_x)
+ minx = cliprect.min_x;
+ if (maxx > cliprect.max_x)
+ maxx = cliprect.max_x;
+
+ // non-flipped case
+ int x;
+ if (!flip)
+ {
+ // start at the word before because we preincrement below
+ uint16_t offs = addr - 1;
+ for (x = xpos; (xdelta > 0 && x <= maxx) || (xdelta < 0 && x >= minx); )
+ {
+ uint64_t pixels = spritedata[++offs];
+
+ // draw 16 pixels
+ int pix, ind;
+ pix = (pixels >> 60) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 56) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 52) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 48) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 44) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 40) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 36) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 32) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 28) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 24) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 20) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 16) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 12) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 8) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 4) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 0) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 0x0f)
+ break;
+ }
+ }
+
+ // flipped case
+ else
+ {
+ // start at the word after because we predecrement below
+ uint16_t offs = addr + 1;
+ for (x = xpos; (xdelta > 0 && x <= maxx) || (xdelta < 0 && x >= minx); )
+ {
+ uint64_t pixels = spritedata[--offs];
+
+ // draw 16 pixels
+ int pix, ind;
+ pix = (pixels >> 0) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 4) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 8) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 12) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 16) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 20) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 24) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 28) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 32) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 36) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 40) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 44) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 48) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 52) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 56) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+ pix = (pixels >> 60) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
+
+ // stop if the last pixel in the group was 0xf
+ if (pix == 0x0f)
+ break;
+ }
+ }
+
+ // update bounds
+ if (x > dmaxx) dmaxx = x;
+ if (x < dminx) dminx = x;
+ if (y < dminy) dminy = y;
+ if (y > dmaxy) dmaxy = y;
+ }
+ }
+
+ // accumulate zoom factors; if we carry into the high bit, skip an extra row
+ yacc += zoom;
+ addr += pitch * (yacc >> 9);
+ yacc &= 0x1ff;
+ }
+
+ // mark dirty
+ if (dminx <= dmaxx && dminy <= dmaxy)
+ mark_dirty(dminx, dmaxx, dminy, dmaxy);
+ }
+}