// license:BSD-3-Clause // copyright-holders:David Haywood /* Various Tecmo Sprite implementations - the various sprite implementations here are slightly different but can clearly be refactored to use a common base class for the chained drawing even if the position of the attributes etc. varies between PCB / chip. - what chips are involved in implementing these schemes? ttl logic on early ones? customs on later? */ #include "emu.h" #include "tecmo_spr.h" #include "screen.h" DEFINE_DEVICE_TYPE(TECMO_SPRITE, tecmo_spr_device, "tecmo_spr", "Tecmo Chained Sprites") tecmo_spr_device::tecmo_spr_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, TECMO_SPRITE, tag, owner, clock) , m_bootleg(false) , m_yoffset(0) { } void tecmo_spr_device::device_start() { } void tecmo_spr_device::device_reset() { } static const uint8_t layout[8][8] = { { 0, 1, 4, 5, 16, 17, 20, 21 }, { 2, 3, 6, 7, 18, 19, 22, 23 }, { 8, 9, 12, 13, 24, 25, 28, 29 }, { 10, 11, 14, 15, 26, 27, 30, 31 }, { 32, 33, 36, 37, 48, 49, 52, 53 }, { 34, 35, 38, 39, 50, 51, 54, 55 }, { 40, 41, 44, 45, 56, 57, 60, 61 }, { 42, 43, 46, 47, 58, 59, 62, 63 } }; /* sprite format (gaiden): * * word bit usage * --------+-fedcba9876543210-+---------------- * 0 | ---------------x | flip x * | --------------x- | flip y * | -------------x-- | enable * | ----------x----- | blend * | --------xx------ | sprite-tile priority * 1 | xxxxxxxxxxxxxxxx | number * 2 | --------xxxx---- | palette * | --------------xx | size: 8x8, 16x16, 32x32, 64x64 * 3 | xxxxxxxxxxxxxxxx | y position * 4 | xxxxxxxxxxxxxxxx | x position * 5,6,7| | unused */ #define NUM_SPRITES 256 void tecmo_spr_device::gaiden_draw_sprites(screen_device &screen, gfx_element *gfx, const rectangle &cliprect, uint16_t* spriteram, int sprite_sizey, int spr_offset_y, int flip_screen, bitmap_ind16 &sprite_bitmap) { uint16_t *source = spriteram; int sourceinc = 8; int count = NUM_SPRITES; int screenwidth = screen.visible_area().width(); int attributes_word = 0; int tilenumber_word = 1; int colour_word = 2; int yposition_word = 3; int xposition_word = 4; int xmask; if (screenwidth == 512) xmask = 512; else xmask = 256; /* draw all sprites from front to back */ while (count--) { uint32_t attributes = source[attributes_word]; int enabled = source[attributes_word] & 0x04; if (enabled) { if (m_bootleg) { // I don't think the galspinbl / hotpinbl bootlegs have blending, instead they use this bit to flicker sprites on/off each frame, so handle it here (we can't handle it in the mixing) // alternatively these sprites could just be disabled like the tiles marked with the 'mix' bit appear to be (they're only used for ball / flipper trails afaik) if (source[attributes_word] & 0x0040) { int frame = screen.frame_number() & 1; if (frame==1) enabled = 0; } } } if (enabled) { uint32_t flipx = (attributes & 1); uint32_t flipy = (attributes & 2); uint32_t color = source[colour_word]; uint32_t sizex = 1 << ((color >> 0) & 3); /* 1,2,4,8 */ uint32_t sizey = 1 << ((color >> sprite_sizey) & 3); /* 1,2,4,8 */ /* raiga & fstarfrc need something like this */ uint32_t number = (source[tilenumber_word]); if (sizex >= 2) number &= ~0x01; if (sizey >= 2) number &= ~0x02; if (sizex >= 4) number &= ~0x04; if (sizey >= 4) number &= ~0x08; if (sizex >= 8) number &= ~0x10; if (sizey >= 8) number &= ~0x20; int ypos = (source[yposition_word] + spr_offset_y) & 0x01ff; int xpos = source[xposition_word] & ((xmask*2)-1); color = (color >> 4) & 0x0f; /* wraparound */ if (xpos >= xmask) xpos -= (xmask*2); if (ypos >= 256) ypos -= 512; if (flip_screen) { flipx = !flipx; flipy = !flipy; xpos = 256 - (8 * sizex) - xpos; ypos = 256 - (8 * sizey) - ypos; if (xpos <= -256) xpos += 512; if (ypos <= -256) ypos += 512; } // this contains the blend bit and the priority bits, the spbactn proto uses 0x0300 for priority, spbactn uses 0x0030, others use 0x00c0 color |= (source[attributes_word] & 0x03f0); bitmap_ind16* bitmap = &sprite_bitmap; for (int row = 0; row < sizey; row++) { for (int col = 0; col < sizex; col++) { int sx = xpos + 8 * (flipx ? (sizex - 1 - col) : col); int sy = ypos + 8 * (flipy ? (sizey - 1 - row) : row); gfx->transpen_raw(*bitmap, cliprect, number + layout[row][col], gfx->colorbase() + color * gfx->granularity(), flipx, flipy, sx, sy, 0); } } } source += sourceinc; } } /* NOT identical to the version above */ /* sprite format (tecmo.cpp): * * byte bit usage * --------+-76543210-+---------------- 0 | xxxxx--- | bank / upper tile bits | -----x-- | enable | ------x- | flip y | -------x | flip x 1 | xxxxxxxx | tile number (low bits) 2 | ------xx | size 3 | xx-------| priority | --x----- | upper y co-ord | ---x---- | upper x co-ord | ----xxxx | colour 4 | xxxxxxxx | ypos 5 | xxxxxxxx | xpos 6 | -------- | 7 | -------- | */ void tecmo_spr_device::draw_sprites_8bit(screen_device &screen, bitmap_ind16 &bitmap, gfx_element *gfx, const rectangle &cliprect, uint8_t* spriteram, int size, int video_type, int flip_screen) { for (int offs = size-8; offs >= 0; offs -= 8) { int flags = spriteram[offs+3]; int priority = flags>>6; int bank = spriteram[offs+0]; if (bank & 4) { /* visible */ int which = spriteram[offs+1]; int code,priority_mask; int size = spriteram[offs + 2] & 3; if (video_type != 0) /* gemini, silkworm */ code = which + ((bank & 0xf8) << 5); else /* rygar */ code = which + ((bank & 0xf0) << 4); code &= ~((1 << (size*2)) - 1); size = 1 << size; int xpos = spriteram[offs + 5] - ((flags & 0x10) << 4); int ypos = spriteram[offs + 4] - ((flags & 0x20) << 3); int flipx = bank & 1; int flipy = bank & 2; if (flip_screen) { xpos = 256 - (8 * size) - xpos; ypos = 256 - (8 * size) - ypos; flipx = !flipx; flipy = !flipy; } /* bg: 1; fg:2; text: 4 */ switch (priority) { default: case 0x0: priority_mask = 0; break; case 0x1: priority_mask = 0xf0; break; /* obscured by text layer */ case 0x2: priority_mask = 0xf0|0xcc; break; /* obscured by foreground */ case 0x3: priority_mask = 0xf0|0xcc|0xaa; break; /* obscured by bg and fg */ } for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { int sx = xpos + 8*(flipx?(size-1-x):x); int sy = ypos + 8*(flipy?(size-1-y):y); gfx->prio_transpen(bitmap,cliprect, code + layout[y][x], flags & 0xf, flipx,flipy, sx,sy, screen.priority(), priority_mask,0); } } } } } /* sprite format (wc90.cpp): - similar to the 16-bit one * * byte bit usage * --------+-76543210-+---------------- */ void tecmo_spr_device::draw_wc90_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect, gfx_element *gfx, uint8_t* spriteram, int size, int priority) { /* draw all visible sprites of specified priority */ for (int offs = 0; offs < size; offs += 16) { int bank = spriteram[offs+0]; if ((bank >> 4) == priority) { if (bank & 4) { /* visible */ int code = ( spriteram[offs+2] ) + ( spriteram[offs+3] << 8 ); int xpos = spriteram[offs + 8] + ( (spriteram[offs + 9] & 3 ) << 8 ); int ypos = spriteram[offs + 6] + m_yoffset; ypos &= 0xff; // sprite wrap right on edge (top @ ROT0) of pac90 ypos = ypos - ((spriteram[offs + 7] & 1) << 8); // sprite wrap on top of wc90 if (xpos >= 0x0300) xpos -= 0x0400; int flags = spriteram[offs+4]; int sizex = 1 << ((flags >> 0) & 3); int sizey = 1 << ((flags >> 2) & 3); int flipx = bank & 1; int flipy = bank & 2; for (int y = 0; y < sizey; y++) { for (int x = 0; x < sizex; x++) { int sx = xpos + 8*(flipx?(sizex-1-x):x); int sy = ypos + 8*(flipy?(sizey-1-y):y); gfx->transpen(bitmap,cliprect, code + layout[y][x], (flags>>4) & 0xf, flipx,flipy, sx,sy, 0); } } } } } } void tecmo_spr_device::tbowl_draw_sprites(bitmap_ind16 &bitmap,const rectangle &cliprect, gfx_element *gfx, int xscroll, uint8_t* spriteram) { for (int offs = 0; offs < 0x800; offs += 8) { if (spriteram[offs+0] & 0x80) /* enable */ { int code = (spriteram[offs+2])+(spriteram[offs+1]<<8); int color = (spriteram[offs+3])&0x1f; int sizex = 1 << ((spriteram[offs+0] & 0x03) >> 0); int sizey = 1 << ((spriteram[offs+0] & 0x0c) >> 2); int flipx = (spriteram[offs+0])&0x20; int flipy = 0; int xpos = (spriteram[offs+6])+((spriteram[offs+4]&0x03)<<8); int ypos = (spriteram[offs+5])+((spriteram[offs+4]&0x10)<<4); /* bg: 1; fg:2; text: 4 */ for (int y = 0; y < sizey; y++) { for (int x = 0; x < sizex; x++) { int sx = xpos + 8*(flipx?(sizex-1-x):x); int sy = ypos + 8*(flipy?(sizey-1-y):y); sx -= xscroll; gfx->transpen(bitmap,cliprect, code + layout[y][x], color, flipx,flipy, sx,sy,0 ); /* wraparound */ gfx->transpen(bitmap,cliprect, code + layout[y][x], color, flipx,flipy, sx,sy-0x200,0 ); /* wraparound */ gfx->transpen(bitmap,cliprect, code + layout[y][x], color, flipx,flipy, sx-0x400,sy,0 ); /* wraparound */ gfx->transpen(bitmap,cliprect, code + layout[y][x], color, flipx,flipy, sx-0x400,sy-0x200,0 ); } } } } }