// license:BSD-3-Clause // copyright-holders:Nicola Salmoria, David Haywood // Video System Sprites // todo: // update drivers which call multiple priority passes to use the pdrawgfx version (aerofgt, gstriker) // according to gstriker this is probably the Fujitsu CG10103 // Aero Fighters (newer hardware) // Quiz & Variety Sukusuku Inufuku // 3 On 3 Dunk Madness // Super Slams // Formula 1 Grand Prix 2 // (Lethal) Crash Race // Grand Striker // V Goal Soccer // Tecmo World Cup '94 // Tao Taido /* old notes */ /*** Fujitsu CG10103 **********************************************/ /* Fujitsu CG10103 sprite generator -------------------------------- - Tile based - 16x16 4bpp tiles - Up to 7x7 in each block - 5 bit of palette selection for the mixer - Scaling (x/y) - Flipping - Indipendent sorting list - 1 bit of pri for the mixer Note that this chip can be connected to a VS9210 which adds a level of indirection for tile numbers. Basically, the VS9210 indirects the tilet number through a table in its attached memory, before accessing the ROMs. Sorting list format (VideoRAM offset 0) --------------------------------------- de-- ---f ssss ssss e=end of list f=sprite present in this position s=sprite index d=disable sprite? TODO: Priorities should be right, but they probably need to be orthogonal with the mixer priorities. Zoom factor is not correct, the scale is probably non-linear Horizontal wrapping is just a hack. The chip probably calculates if it needs to draw the sprite at the normal position, or wrapped along X/Y. Abstracts the VS9210 */ #include "emu.h" #include "vsystem_spr.h" #include "screen.h" DEFINE_DEVICE_TYPE(VSYSTEM_SPR, vsystem_spr_device, "vsystem_spr", "Video System VS9108 Sprites") vsystem_spr_device::vsystem_spr_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, VSYSTEM_SPR, tag, owner, clock) , m_newtilecb(*this, DEVICE_SELF, FUNC(vsystem_spr_device::tile_callback_noindirect)) , m_xoffs(0) , m_yoffs(0) , m_pdraw(false) , m_pal_mask(0x3f) , m_gfx_region(-1) , m_transpen(15) , m_pal_base(0) , m_curr_sprite() , m_gfxdecode(*this, finder_base::DUMMY_TAG) { } uint32_t vsystem_spr_device::tile_callback_noindirect(uint32_t tile) { return tile; } void vsystem_spr_device::device_start() { // bind our handler m_newtilecb.resolve(); save_item(NAME(m_pal_base)); save_item(NAME(m_curr_sprite.ox)); save_item(NAME(m_curr_sprite.xsize)); save_item(NAME(m_curr_sprite.zoomx)); save_item(NAME(m_curr_sprite.oy)); save_item(NAME(m_curr_sprite.ysize)); save_item(NAME(m_curr_sprite.zoomy)); save_item(NAME(m_curr_sprite.flipx)); save_item(NAME(m_curr_sprite.flipy)); save_item(NAME(m_curr_sprite.color)); save_item(NAME(m_curr_sprite.pri)); save_item(NAME(m_curr_sprite.map)); } void vsystem_spr_device::device_reset() { } void vsystem_spr_device::sprite_attributes::get(uint16_t const *ram) { /* attr_start + 0x0000 ---- ---x xxxx xxxx oy ---- xxx- ---- ---- ysize xxxx ---- ---- ---- zoomy attr_start + 0x0001 ---- ---x xxxx xxxx ox ---- xxx- ---- ---- xsize xxxx ---- ---- ---- zoomx attr_start + 0x0002 -x-- ---- ---- ---- flipx x--- ---- ---- ---- flipy --xx xxxx ---- ---- color --xx ---- ---- ---- priority? (upper color bits) ---- ---- ---- ---x map start (msb) attr_start + 0x0003 xxxx xxxx xxxx xxxx map start (lsb) */ oy = (ram[0] & 0x01ff); ysize = (ram[0] & 0x0e00) >> 9; zoomy = (ram[0] & 0xf000) >> 12; ox = (ram[1] & 0x01ff); xsize = (ram[1] & 0x0e00) >> 9; zoomx = (ram[1] & 0xf000) >> 12; flipx = (ram[2] & 0x4000); flipy = (ram[2] & 0x8000); color = (ram[2] & 0x3f00) >> 8; pri = (ram[2] & 0x3000) >> 12; map = (ram[2] & 0x0001) << 16; map |= (ram[3] & 0xffff); } void vsystem_spr_device::common_sprite_drawgfx(bitmap_ind16 &bitmap, const rectangle &cliprect, bitmap_ind8 &priority_bitmap) { gfx_element *gfx = m_gfxdecode->gfx(m_gfx_region); int priority_mask = 0x00; m_curr_sprite.oy += m_yoffs; m_curr_sprite.ox += m_xoffs; if (m_pdraw) { switch (m_curr_sprite.pri) { default: case 0: priority_mask = 0x00; break; case 3: priority_mask = 0xfe; break; case 2: priority_mask = 0xfc; break; case 1: priority_mask = 0xf0; break; } } m_curr_sprite.zoomx = 32 - m_curr_sprite.zoomx; m_curr_sprite.zoomy = 32 - m_curr_sprite.zoomy; int ystart, yend, yinc; if (!m_curr_sprite.flipy) { ystart = 0; yend = m_curr_sprite.ysize+1; yinc = 1; } else { ystart = m_curr_sprite.ysize; yend = -1; yinc = -1; } int ycnt = ystart; while (ycnt != yend) { int xstart, xend, xinc; if (!m_curr_sprite.flipx) { xstart = 0; xend = m_curr_sprite.xsize+1; xinc = 1; } else { xstart = m_curr_sprite.xsize; xend = -1; xinc = -1; } int xcnt = xstart; while (xcnt != xend) { int startno = m_newtilecb(m_curr_sprite.map++); if (m_pdraw) { gfx->prio_zoom_transpen(bitmap,cliprect, startno, m_curr_sprite.color + m_pal_base, m_curr_sprite.flipx, m_curr_sprite.flipy, m_curr_sprite.ox + xcnt * m_curr_sprite.zoomx/2, m_curr_sprite.oy + ycnt * m_curr_sprite.zoomy/2, m_curr_sprite.zoomx << 11, m_curr_sprite.zoomy << 11, priority_bitmap, priority_mask, m_transpen); gfx->prio_zoom_transpen(bitmap,cliprect, startno, m_curr_sprite.color + m_pal_base, m_curr_sprite.flipx, m_curr_sprite.flipy, -0x200+m_curr_sprite.ox + xcnt * m_curr_sprite.zoomx/2, m_curr_sprite.oy + ycnt * m_curr_sprite.zoomy/2, m_curr_sprite.zoomx << 11, m_curr_sprite.zoomy << 11, priority_bitmap, priority_mask, m_transpen); gfx->prio_zoom_transpen(bitmap,cliprect, startno, m_curr_sprite.color + m_pal_base, m_curr_sprite.flipx, m_curr_sprite.flipy, m_curr_sprite.ox + xcnt * m_curr_sprite.zoomx/2, -0x200+m_curr_sprite.oy + ycnt * m_curr_sprite.zoomy/2, m_curr_sprite.zoomx << 11, m_curr_sprite.zoomy << 11, priority_bitmap, priority_mask, m_transpen); gfx->prio_zoom_transpen(bitmap,cliprect, startno, m_curr_sprite.color + m_pal_base, m_curr_sprite.flipx, m_curr_sprite.flipy, -0x200+m_curr_sprite.ox + xcnt * m_curr_sprite.zoomx/2, -0x200+m_curr_sprite.oy + ycnt * m_curr_sprite.zoomy/2, m_curr_sprite.zoomx << 11, m_curr_sprite.zoomy << 11, priority_bitmap, priority_mask, m_transpen); } else { gfx->zoom_transpen(bitmap,cliprect, startno, m_curr_sprite.color + m_pal_base, m_curr_sprite.flipx, m_curr_sprite.flipy, m_curr_sprite.ox + xcnt * m_curr_sprite.zoomx/2, m_curr_sprite.oy + ycnt * m_curr_sprite.zoomy/2, m_curr_sprite.zoomx << 11, m_curr_sprite.zoomy << 11, m_transpen); gfx->zoom_transpen(bitmap,cliprect, startno, m_curr_sprite.color + m_pal_base, m_curr_sprite.flipx, m_curr_sprite.flipy, -0x200+m_curr_sprite.ox + xcnt * m_curr_sprite.zoomx/2, m_curr_sprite.oy + ycnt * m_curr_sprite.zoomy/2, m_curr_sprite.zoomx << 11, m_curr_sprite.zoomy << 11, m_transpen); gfx->zoom_transpen(bitmap,cliprect, startno, m_curr_sprite.color + m_pal_base, m_curr_sprite.flipx, m_curr_sprite.flipy, m_curr_sprite.ox + xcnt * m_curr_sprite.zoomx/2, -0x200+m_curr_sprite.oy + ycnt * m_curr_sprite.zoomy/2, m_curr_sprite.zoomx << 11, m_curr_sprite.zoomy << 11, m_transpen); gfx->zoom_transpen(bitmap,cliprect, startno, m_curr_sprite.color + m_pal_base, m_curr_sprite.flipx, m_curr_sprite.flipy, -0x200+m_curr_sprite.ox + xcnt * m_curr_sprite.zoomx/2, -0x200+m_curr_sprite.oy + ycnt * m_curr_sprite.zoomy/2, m_curr_sprite.zoomx << 11, m_curr_sprite.zoomy << 11, m_transpen); } xcnt+=xinc; } ycnt+=yinc; } } void vsystem_spr_device::draw_sprites(uint16_t const *spriteram, int spriteram_bytes, screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int prihack_mask, int prihack_val ) { int offs; int end = 0; // find the end of the list for (offs = 0; offs < (spriteram_bytes / 16 ); offs++) { if (spriteram[offs] & 0x4000) break; } end = offs; // decide our drawing order (if we're using pdrawgfx we must go in reverse) int first, last, inc; if (m_pdraw) { first = end - 1; last = -1; inc = -1; } else { first = 0; last = end; inc = 1; } // draw offs = first; while (offs != last) { if ((spriteram[offs] & 0x8000) == 0x0000) { int attr_start; attr_start = 4 * (spriteram[offs] & 0x03ff); m_curr_sprite.get(&spriteram[attr_start]); m_curr_sprite.color &= m_pal_mask; // hack for aero fighters and other which still call us multiple times with different priorities instead of using the pdrawgfx version if (prihack_mask != -1) { if ((m_curr_sprite.pri & prihack_mask) == prihack_val) common_sprite_drawgfx(bitmap, cliprect, screen.priority()); } else { common_sprite_drawgfx(bitmap, cliprect, screen.priority()); } } offs+=inc; } }