// license:BSD-3-Clause // copyright-holders:Nicola Salmoria /*************************************************************************** taitosj.cpp Functions to emulate the video hardware of the machine. ***************************************************************************/ #include "emu.h" #include "video/resnet.h" #include "includes/taitosj.h" #define GLOBAL_FLIP_X (*m_video_mode & 0x01) #define GLOBAL_FLIP_Y (*m_video_mode & 0x02) #define SPRITE_RAM_PAGE_OFFSET ((*m_video_mode & 0x04) ? 0x80 : 0x00) #define SPRITES_ON (*m_video_mode & 0x80) #define TRANSPARENT_PEN (0x40) static const int layer_enable_mask[3] = { 0x10, 0x20, 0x40 }; /*************************************************************************** I call the three layers with the conventional names "front", "middle" and "back", because that's their default order, but they can be arranged, together with the sprites, in any order. The priority is selected by register 0xd300, which works as follow: bits 0-3 go to A4-A7 of a 256x4 PROM bit 4 selects D0/D1 or D2/D3 of the PROM bit 5-7 n.c. A0-A3 of the PROM is fed with a mask of the inactive layers (i.e. all-zero) in the order sprites-front-middle-back the 2-bit code which comes out from the PROM selects the layer to display. Here is a dump of one of these PROMs; on the right is the resulting order (s = sprites f = front m = middle b = back). Note that, in theory, the PROM could encode some really funky priority schemes which couldn't be reconducted to the simple layer order given here. Luckily, none of the games seem to do that. Actually, all of them seem to use the same PROM, with the exception of Wild Western. d300 pri d300 pri 00: 08 09 08 0A 00 05 00 0F 08 09 08 0A 00 05 00 0F | 00 sfmb 10 msfb 10: 08 09 08 0B 00 0D 00 0F 08 09 08 0A 00 05 00 0F | 01 sfbm 11 msbf 20: 08 0A 08 0A 04 05 00 0F 08 0A 08 0A 04 05 00 0F | 02 smfb 12 mfsb 30: 08 0A 08 0A 04 07 0C 0F 08 0A 08 0A 04 05 00 0F | 03 smbf 13 mfbs 40: 08 0B 08 0B 0C 0F 0C 0F 08 09 08 0A 00 05 00 0F | 04 sbfm 14 mbsf 50: 08 0B 08 0B 0C 0F 0C 0F 08 0A 08 0A 04 05 00 0F | 05 sbmf 15 mbfs 60: 0D 0D 0C 0E 0D 0D 0C 0F 01 05 00 0A 01 05 00 0F | 06 fsmb 16 bsfm 70: 0D 0D 0C 0F 0D 0D 0C 0F 01 09 00 0A 01 05 00 0F | 07 fsbm 17 bsmf 80: 0D 0D 0E 0E 0D 0D 0C 0F 05 05 02 0A 05 05 00 0F | 08 fmsb 18 bfsm 90: 0D 0D 0E 0E 0D 0D 0F 0F 05 05 0A 0A 05 05 00 0F | 09 fmbs 19 bfms A0: 0D 0D 0F 0F 0D 0D 0F 0F 09 09 08 0A 01 05 00 0F | 0A fbsm 1A bmsf B0: 0D 0D 0F 0F 0D 0D 0F 0F 09 09 0A 0A 05 05 00 0F | 0B fbms 1B bmfs C0: 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F | 0C - 1C - D0: 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F | 0D - 1D - E0: 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F | 0E - 1E - F0: 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F | 0F - 1F - ***************************************************************************/ /*************************************************************************** Convert the color PROMs into a more useable format. The Taito games don't have a color PROM. They use RAM to dynamically create the palette. The resolution is 9 bit (3 bits per gun). The RAM is connected to the RGB output this way: bit 0 -- inverter -- 270 ohm resistor -- RED bit 7 -- inverter -- 470 ohm resistor -- RED -- inverter -- 1 kohm resistor -- RED -- inverter -- 270 ohm resistor -- GREEN -- inverter -- 470 ohm resistor -- GREEN -- inverter -- 1 kohm resistor -- GREEN -- inverter -- 270 ohm resistor -- BLUE -- inverter -- 470 ohm resistor -- BLUE bit 0 -- inverter -- 1 kohm resistor -- BLUE ***************************************************************************/ void taitosj_state::set_pens() { static const int resistances[3] = { 1000, 470, 270 }; double rweights[3], gweights[3], bweights[3]; // compute the color output resistor weights compute_resistor_weights(0, 255, -1.0, 3, resistances, rweights, 0, 0, 3, resistances, gweights, 0, 0, 3, resistances, bweights, 0, 0); for (int i = 0; i < 0x40; i++) { // red component int val = m_paletteram[(i << 1) | 0x01]; int bit0 = (~val >> 6) & 0x01; int bit1 = (~val >> 7) & 0x01; val = m_paletteram[(i << 1) | 0x00]; int bit2 = (~val >> 0) & 0x01; int r = combine_weights(rweights, bit0, bit1, bit2); // green component val = m_paletteram[(i << 1) | 0x01]; bit0 = (~val >> 3) & 0x01; bit1 = (~val >> 4) & 0x01; bit2 = (~val >> 5) & 0x01; int g = combine_weights(gweights, bit0, bit1, bit2); // blue component val = m_paletteram[(i << 1) | 0x01]; bit0 = (~val >> 0) & 0x01; bit1 = (~val >> 1) & 0x01; bit2 = (~val >> 2) & 0x01; int b = combine_weights(bweights, bit0, bit1, bit2); m_palette->set_pen_color(i, rgb_t(r, g, b)); } } /*************************************************************************** Start the video hardware emulation. ***************************************************************************/ void taitosj_state::compute_draw_order() { uint8_t *color_prom = memregion("proms")->base(); /* do a simple conversion of the PROM into layer priority order. Note that this is a simplification, which assumes the PROM encodes a sensible priority scheme. */ for (int i = 0; i < 32; i++) { int mask = 0; /* start with all four layers active, so we'll get the highest priority one in the first loop */ for (int j = 3; j >= 0; j--) { int data = color_prom[0x10 * (i & 0x0f) + mask] & 0x0f; if (i & 0x10) data = data >> 2; else data = data & 0x03; mask |= (1 << data); /* in next loop, we'll see which of the remaining layers has top priority when this one is transparent */ m_draw_order[i][j] = data; } } } void taitosj_state::device_post_load() { m_gfxdecode->gfx(0)->mark_all_dirty(); m_gfxdecode->gfx(1)->mark_all_dirty(); m_gfxdecode->gfx(2)->mark_all_dirty(); m_gfxdecode->gfx(3)->mark_all_dirty(); } void taitosj_state::video_start() { m_sprite_layer_collbitmap1.allocate(16,16); for (int i = 0; i < 3; i++) { m_screen->register_screen_bitmap(m_layer_bitmap[i]); m_screen->register_screen_bitmap(m_sprite_layer_collbitmap2[i]); } m_sprite_sprite_collbitmap1.allocate(32,32); m_sprite_sprite_collbitmap2.allocate(32,32); m_gfxdecode->gfx(0)->set_source(m_characterram); m_gfxdecode->gfx(1)->set_source(m_characterram); m_gfxdecode->gfx(2)->set_source(m_characterram + 0x1800); m_gfxdecode->gfx(3)->set_source(m_characterram + 0x1800); compute_draw_order(); } uint8_t taitosj_state::gfxrom_r() { uint8_t ret; offs_t offs = m_gfxpointer[0] | (m_gfxpointer[1] << 8); if (offs < 0x8000) ret = m_gfx[offs]; else ret = 0; offs = offs + 1; m_gfxpointer[0] = offs & 0xff; m_gfxpointer[1] = offs >> 8; return ret; } void taitosj_state::characterram_w(offs_t offset, uint8_t data) { if (m_characterram[offset] != data) { if (offset < 0x1800) { m_gfxdecode->gfx(0)->mark_dirty((offset / 8) & 0xff); m_gfxdecode->gfx(1)->mark_dirty((offset / 32) & 0x3f); } else { m_gfxdecode->gfx(2)->mark_dirty((offset / 8) & 0xff); m_gfxdecode->gfx(3)->mark_dirty((offset / 32) & 0x3f); } m_characterram[offset] = data; } } void taitosj_state::junglhbr_characterram_w(offs_t offset, uint8_t data) { characterram_w(offset, data ^ 0xfc); } void taitosj_state::collision_reg_clear_w(uint8_t data) { m_collision_reg[0] = 0; m_collision_reg[1] = 0; m_collision_reg[2] = 0; m_collision_reg[3] = 0; } inline int taitosj_state::get_sprite_xy(uint8_t which, uint8_t* sx, uint8_t* sy) { offs_t offs = which * 4; *sx = m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 0] - 1; *sy = 240 - m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 1]; return (*sy < 240); } inline gfx_element *taitosj_state::get_sprite_gfx_element(uint8_t which) { offs_t offs = which * 4; return m_gfxdecode->gfx((m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 3] & 0x40) ? 3 : 1); } int taitosj_state::check_sprite_sprite_bitpattern(int sx1, int sy1, int which1, int sx2, int sy2, int which2) { int minx, miny, maxx = 16, maxy = 16; offs_t offs1 = which1 * 4; offs_t offs2 = which2 * 4; // normalize coordinates to (0,0) and compute overlap if (sx1 < sx2) { sx2 -= sx1; sx1 = 0; minx = sx2; } else { sx1 -= sx2; sx2 = 0; minx = sx1; } if (sy1 < sy2) { sy2 -= sy1; sy1 = 0; miny = sy2; } else { sy1 -= sy2; sy2 = 0; miny = sy1; } // draw the sprites into separate bitmaps and check overlapping region m_sprite_sprite_collbitmap1.fill(TRANSPARENT_PEN); get_sprite_gfx_element(which1)->transpen(m_sprite_sprite_collbitmap1, m_sprite_sprite_collbitmap1.cliprect(), m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs1 + 3] & 0x3f, 0, m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs1 + 2] & 0x01, m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs1 + 2] & 0x02, sx1, sy1, 0); m_sprite_sprite_collbitmap2.fill(TRANSPARENT_PEN); get_sprite_gfx_element(which2)->transpen(m_sprite_sprite_collbitmap2, m_sprite_sprite_collbitmap2.cliprect(), m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs2 + 3] & 0x3f, 0, m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs2 + 2] & 0x01, m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs2 + 2] & 0x02, sx2, sy2, 0); for (int y = miny; y < maxy; y++) for (int x = minx; x < maxx; x++) if ((m_sprite_sprite_collbitmap1.pix(y, x) != TRANSPARENT_PEN) && (m_sprite_sprite_collbitmap2.pix(y, x) != TRANSPARENT_PEN)) return 1; /* collided */ return 0; } void taitosj_state::check_sprite_sprite_collision() { if (SPRITES_ON) { // check each pair of sprites for (int which1 = 0; which1 < 0x20; which1++) { uint8_t sx1, sy1; if ((which1 >= 0x10) && (which1 <= 0x17)) continue; // no sprites here if (!get_sprite_xy(which1, &sx1, &sy1)) continue; for (int which2 = which1 + 1; which2 < 0x20; which2++) { uint8_t sx2, sy2; if ((which2 >= 0x10) && (which2 <= 0x17)) continue; // no sprites here if (!get_sprite_xy(which2, &sx2, &sy2)) continue; // quickly rule out any pairs that cannot be touching if ((abs((int8_t)sx1 - (int8_t)sx2) < 16) && (abs((int8_t)sy1 - (int8_t)sy2) < 16)) { int reg; if (!check_sprite_sprite_bitpattern(sx1, sy1, which1, sx2, sy2, which2)) continue; /* mark sprite as collided note that only the sprite with the higher number is marked as collided. This is how the hardware works and required by Pirate Pete to be able to finish the last round. */ // the last sprite has to be moved at the start of the list if (which2 == 0x1f) { reg = which1 >> 3; if (reg == 3) reg = 2; m_collision_reg[reg] |= (1 << (which1 & 0x07)); } else { reg = which2 >> 3; if (reg == 3) reg = 2; m_collision_reg[reg] |= (1 << (which2 & 0x07)); } } } } } } void taitosj_state::calculate_sprite_areas(int *sprites_on, rectangle *sprite_areas) { int width = m_screen->width(); int height = m_screen->height(); for (int which = 0; which < 0x20; which++) { uint8_t sx, sy; if ((which >= 0x10) && (which <= 0x17)) continue; // no sprites here if (get_sprite_xy(which, &sx, &sy)) { int minx, miny, maxx, maxy; if (GLOBAL_FLIP_X) sx = 238 - sx; if (GLOBAL_FLIP_Y) sy = 242 - sy; minx = sx; miny = sy; maxx = minx + 15; maxy = miny + 15; // check for bitmap bounds to avoid illegal memory access if (minx < 0) minx = 0; if (miny < 0) miny = 0; if (maxx >= width - 1) maxx = width - 1; if (maxy >= height - 1) maxy = height - 1; sprite_areas[which].min_x = minx; sprite_areas[which].max_x = maxx; sprite_areas[which].min_y = miny; sprite_areas[which].max_y = maxy; sprites_on[which] = 1; } // sprite is off else sprites_on[which] = 0; } } int taitosj_state::check_sprite_layer_bitpattern(int which, rectangle *sprite_areas) { offs_t offs = which * 4; int result = 0; // no collisions int check_layer_1 = *m_video_mode & layer_enable_mask[0]; int check_layer_2 = *m_video_mode & layer_enable_mask[1]; int check_layer_3 = *m_video_mode & layer_enable_mask[2]; int minx = sprite_areas[which].min_x; int miny = sprite_areas[which].min_y; int maxx = sprite_areas[which].max_x + 1; int maxy = sprite_areas[which].max_y + 1; int flip_x = (m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] & 0x01) ^ GLOBAL_FLIP_X; int flip_y = (m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] & 0x02) ^ GLOBAL_FLIP_Y; // draw sprite into a bitmap and check if layers collide m_sprite_layer_collbitmap1.fill(TRANSPARENT_PEN); get_sprite_gfx_element(which)->transpen(m_sprite_layer_collbitmap1, m_sprite_layer_collbitmap1.cliprect(), m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 3] & 0x3f, 0, flip_x, flip_y, 0,0,0); for (int y = miny; y < maxy; y++) for (int x = minx; x < maxx; x++) if (m_sprite_layer_collbitmap1.pix(y - miny, x - minx) != TRANSPARENT_PEN) // is there anything to check for ? { if (check_layer_1 && (m_sprite_layer_collbitmap2[0].pix(y, x) != TRANSPARENT_PEN)) result |= 0x01; // collided with layer 1 if (check_layer_2 && (m_sprite_layer_collbitmap2[1].pix(y, x) != TRANSPARENT_PEN)) result |= 0x02; // collided with layer 2 if (check_layer_3 && (m_sprite_layer_collbitmap2[2].pix(y, x) != TRANSPARENT_PEN)) result |= 0x04; // collided with layer 3 } return result; } void taitosj_state::check_sprite_layer_collision(int *sprites_on, rectangle *sprite_areas) { if (SPRITES_ON) { // check each sprite for (int which = 0; which < 0x20; which++) { if ((which >= 0x10) && (which <= 0x17)) continue; // no sprites here if (sprites_on[which]) m_collision_reg[3] |= check_sprite_layer_bitpattern(which, sprite_areas); } } } void taitosj_state::draw_layers() { m_layer_bitmap[0].fill(TRANSPARENT_PEN); m_layer_bitmap[1].fill(TRANSPARENT_PEN); m_layer_bitmap[2].fill(TRANSPARENT_PEN); for (offs_t offs = 0; offs < 0x0400; offs++) { int sx = offs % 32; int sy = offs / 32; if (GLOBAL_FLIP_X) sx = 31 - sx; if (GLOBAL_FLIP_Y) sy = 31 - sy; m_gfxdecode->gfx(m_colorbank[0] & 0x08 ? 2 : 0)->transpen(m_layer_bitmap[0], m_layer_bitmap[0].cliprect(), m_videoram[0][offs], m_colorbank[0] & 0x07, GLOBAL_FLIP_X, GLOBAL_FLIP_Y, 8 * sx, 8 * sy, 0); m_gfxdecode->gfx(m_colorbank[0] & 0x80 ? 2 : 0)->transpen(m_layer_bitmap[1], m_layer_bitmap[1].cliprect(), m_videoram[1][offs], (m_colorbank[0] >> 4) & 0x07, GLOBAL_FLIP_X, GLOBAL_FLIP_Y, 8 * sx, 8 * sy, 0); m_gfxdecode->gfx(m_colorbank[1] & 0x08 ? 2 : 0)->transpen(m_layer_bitmap[2], m_layer_bitmap[2].cliprect(), m_videoram[2][offs], m_colorbank[1] & 0x07, GLOBAL_FLIP_X, GLOBAL_FLIP_Y, 8 * sx, 8 * sy, 0); } } void taitosj_state::draw_sprites(bitmap_ind16 &bitmap) { /* sprite visibility area is missing 4 pixels from the sides, surely to reduce wraparound side effects. This was verified on a real Elevator Action. Note that the clipping is asymmetrical. This matches the real thing. I'm not sure of what should happen when the screen is flipped, though. */ const rectangle spritevisiblearea(0*8+3, 32*8-1-1, 2*8, 30*8-1); const rectangle spritevisibleareaflip(0*8+1, 32*8-3-1, 2*8, 30*8-1); if (SPRITES_ON) { // drawing order is a bit strange. The last sprite has to be moved at the start of the list. for (int sprite = 0x1f; sprite >= 0; sprite--) { uint8_t sx, sy; int which = (sprite - 1) & 0x1f; // move last sprite at the head of the list offs_t offs = which * 4; if ((which >= 0x10) && (which <= 0x17)) continue; // no sprites here if (get_sprite_xy(which, &sx, &sy)) { int code = m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 3] & 0x3f; int color = 2 * ((m_colorbank[1] >> 4) & 0x03) + ((m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] >> 2) & 0x01); int flip_x = m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] & 0x01; int flip_y = m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] & 0x02; if (GLOBAL_FLIP_X) { sx = 238 - sx; flip_x = !flip_x; } if (GLOBAL_FLIP_Y) { sy = 242 - sy; flip_y = !flip_y; } get_sprite_gfx_element(which)->transpen(bitmap, GLOBAL_FLIP_X ? spritevisibleareaflip : spritevisiblearea, code, color, flip_x, flip_y, sx, sy, 0); // draw with wrap around. The horizontal games (eg. sfposeid) need this get_sprite_gfx_element(which)->transpen(bitmap, GLOBAL_FLIP_X ? spritevisibleareaflip : spritevisiblearea, code, color, flip_x, flip_y, sx - 0x100, sy, 0); } } } } void taitosj_state::copy_layer(bitmap_ind16 &bitmap, const rectangle &cliprect, int which, int *sprites_on, rectangle *sprite_areas) { static const int fudge1[3] = { 3, 1, -1 }; static const int fudge2[3] = { 8, 10, 12 }; if (*m_video_mode & layer_enable_mask[which]) { int i, scrollx, scrolly[32]; scrollx = m_scroll[2 * which]; if (GLOBAL_FLIP_X) scrollx = (scrollx & 0xf8) + ((scrollx + fudge1[which]) & 7) + fudge2[which]; else scrollx = -(scrollx & 0xf8) + ((scrollx + fudge1[which]) & 7) + fudge2[which]; if (GLOBAL_FLIP_Y) for (i = 0;i < 32;i++) scrolly[31 - i] = m_colscrolly[32 * which + i] + m_scroll[2 * which + 1]; else for (i = 0;i < 32;i++) scrolly[i] = -m_colscrolly[32 * which + i] - m_scroll[2 * which + 1]; copyscrollbitmap_trans(bitmap, m_layer_bitmap[which], 1, &scrollx, 32, scrolly, cliprect, TRANSPARENT_PEN); // store parts covered with sprites for sprites/layers collision detection for (i = 0; i < 0x20; i++) { if ((i >= 0x10) && (i <= 0x17)) continue; // no sprites here if (sprites_on[i]) copyscrollbitmap(m_sprite_layer_collbitmap2[which], m_layer_bitmap[which], 1, &scrollx, 32, scrolly, sprite_areas[i]); } } } void taitosj_state::kikstart_copy_layer(bitmap_ind16 &bitmap, const rectangle &cliprect, int which, int *sprites_on, rectangle *sprite_areas) { if (*m_video_mode & layer_enable_mask[which]) { int i, scrolly, scrollx[32 * 8]; for (i = 1; i < 32*8; i++) // 1-255 ! if (GLOBAL_FLIP_Y) switch (which) { case 0: scrollx[32 * 8 - i] = 0 ;break; case 1: scrollx[32 * 8 - i] = m_kikstart_scrollram[i] + ((m_scroll[2 * which] + 0x0a) & 0xff);break; case 2: scrollx[32 * 8 - i] = m_kikstart_scrollram[0x100 + i] + ((m_scroll[2 * which] + 0xc) & 0xff);break; } else switch (which) { case 0: scrollx[i] = 0 ;break; case 1: scrollx[i] = 0xff - m_kikstart_scrollram[i - 1] - ((m_scroll[2 * which] - 0x10) & 0xff);break; case 2: scrollx[i] = 0xff - m_kikstart_scrollram[0x100 + i - 1] - ((m_scroll[2 * which] - 0x12) & 0xff);break; } scrolly = m_scroll[2 * which + 1]; // always 0 copyscrollbitmap_trans(bitmap, m_layer_bitmap[which], 32 * 8, scrollx, 1, &scrolly, cliprect, TRANSPARENT_PEN); // store parts covered with sprites for sprites/layers collision detection for (i = 0; i < 0x20; i++) { if ((i >= 0x10) && (i <= 0x17)) continue; // no sprites here if (sprites_on[i]) copyscrollbitmap(m_sprite_layer_collbitmap2[which], m_layer_bitmap[which], 32 * 8, scrollx, 1, &scrolly, sprite_areas[i]); } } } void taitosj_state::copy_layer(bitmap_ind16 &bitmap, const rectangle &cliprect, copy_layer_func_t copy_layer_func, int which, int *sprites_on, rectangle *sprite_areas) { if (which == 0) draw_sprites(bitmap); else (this->*copy_layer_func)(bitmap, cliprect, which - 1, sprites_on, sprite_areas); } void taitosj_state::copy_layers(bitmap_ind16 &bitmap, const rectangle &cliprect, copy_layer_func_t copy_layer_func, int *sprites_on, rectangle *sprite_areas) { // fill the screen with the background color bitmap.fill(8 * (m_colorbank[1] & 0x07), cliprect); for (int i = 0; i < 4; i++) { int which = m_draw_order[*m_video_priority & 0x1f][i]; copy_layer(bitmap, cliprect, copy_layer_func, which, sprites_on, sprite_areas); } } void taitosj_state::check_collision(int *sprites_on, rectangle *sprite_areas) { check_sprite_sprite_collision(); check_sprite_layer_collision(sprites_on, sprite_areas); // check_layer_layer_collision(); // not implemented !!! } int taitosj_state::video_update_common(bitmap_ind16 &bitmap, const rectangle &cliprect, copy_layer_func_t copy_layer_func) { int sprites_on[0x20]; // 1 if sprite is active rectangle sprite_areas[0x20]; // areas on bitmap (sprite locations) set_pens(); draw_layers(); calculate_sprite_areas(sprites_on, sprite_areas); copy_layers(bitmap, cliprect, copy_layer_func, sprites_on, sprite_areas); //check_sprite_layer_collision() uses drawn bitmaps, so it must me called _AFTER_ draw_layers() check_collision(sprites_on, sprite_areas); return 0; } uint32_t taitosj_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { return video_update_common(bitmap, cliprect, &taitosj_state::copy_layer); } uint32_t taitosj_state::screen_update_kikstart(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { return video_update_common(bitmap, cliprect, &taitosj_state::kikstart_copy_layer); }