// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** Incredible Technologies/Strata system (32-bit blitter variant) ***************************************************************************/ #include "emu.h" #include "cpu/m68000/m68000.h" #include "includes/itech32.h" #include /************************************* * * Debugging * *************************************/ #define BLIT_LOGGING 0 /************************************* * * Blitter constants * *************************************/ #define VIDEO_UNKNOWN00 m_video[0x00/2] /* $0087 at startup */ #define VIDEO_STATUS m_video[0x00/2] #define VIDEO_INTSTATE m_video[0x02/2] #define VIDEO_INTACK m_video[0x02/2] #define VIDEO_TRANSFER m_video[0x04/2] #define VIDEO_TRANSFER_FLAGS m_video[0x06/2] /* $5080 at startup (kept at $1512) */ #define VIDEO_COMMAND m_video[0x08/2] /* $0005 at startup */ #define VIDEO_INTENABLE m_video[0x0a/2] /* $0144 at startup (kept at $1514) */ #define VIDEO_TRANSFER_HEIGHT m_video[0x0c/2] #define VIDEO_TRANSFER_WIDTH m_video[0x0e/2] #define VIDEO_TRANSFER_ADDRLO m_video[0x10/2] #define VIDEO_TRANSFER_X m_video[0x12/2] #define VIDEO_TRANSFER_Y m_video[0x14/2] #define VIDEO_SRC_YSTEP m_video[0x16/2] /* $0011 at startup */ #define VIDEO_SRC_XSTEP m_video[0x18/2] #define VIDEO_DST_XSTEP m_video[0x1a/2] #define VIDEO_DST_YSTEP m_video[0x1c/2] #define VIDEO_YSTEP_PER_X m_video[0x1e/2] #define VIDEO_XSTEP_PER_Y m_video[0x20/2] #define VIDEO_UNKNOWN22 m_video[0x22/2] /* $0033 at startup */ #define VIDEO_LEFTCLIP m_video[0x24/2] #define VIDEO_RIGHTCLIP m_video[0x26/2] #define VIDEO_TOPCLIP m_video[0x28/2] #define VIDEO_BOTTOMCLIP m_video[0x2a/2] #define VIDEO_INTSCANLINE m_video[0x2c/2] /* $00ef at startup */ #define VIDEO_TRANSFER_ADDRHI m_video[0x2e/2] /* $0000 at startup */ #define VIDEO_UNKNOWN30 m_video[0x30/2] /* $0040 at startup */ #define VIDEO_VTOTAL m_video[0x32/2] /* $0106 at startup */ #define VIDEO_VSYNC m_video[0x34/2] /* $0101 at startup */ #define VIDEO_VBLANK_START m_video[0x36/2] /* $00f3 at startup */ #define VIDEO_VBLANK_END m_video[0x38/2] /* $0003 at startup */ #define VIDEO_HTOTAL m_video[0x3a/2] /* $01fc at startup */ #define VIDEO_HSYNC m_video[0x3c/2] /* $01e4 at startup */ #define VIDEO_HBLANK_START m_video[0x3e/2] /* $01b2 at startup */ #define VIDEO_HBLANK_END m_video[0x40/2] /* $0032 at startup */ #define VIDEO_UNKNOWN42 m_video[0x42/2] /* $0015 at startup */ #define VIDEO_DISPLAY_YORIGIN1 m_video[0x44/2] /* $0000 at startup */ #define VIDEO_DISPLAY_YORIGIN2 m_video[0x46/2] /* $0000 at startup */ #define VIDEO_DISPLAY_YSCROLL2 m_video[0x48/2] /* $0000 at startup */ #define VIDEO_UNKNOWN4a m_video[0x4a/2] /* $0000 at startup */ #define VIDEO_DISPLAY_XORIGIN1 m_video[0x4c/2] /* $0000 at startup */ #define VIDEO_DISPLAY_XORIGIN2 m_video[0x4e/2] /* $0000 at startup */ #define VIDEO_DISPLAY_XSCROLL2 m_video[0x50/2] /* $0000 at startup */ #define VIDEO_UNKNOWN52 m_video[0x52/2] /* $0000 at startup */ #define VIDEO_UNKNOWN54 m_video[0x54/2] /* $0080 at startup */ #define VIDEO_UNKNOWN56 m_video[0x56/2] /* $00c0 at startup */ #define VIDEO_UNKNOWN58 m_video[0x58/2] /* $01c0 at startup */ #define VIDEO_UNKNOWN5a m_video[0x5a/2] /* $01c0 at startup */ #define VIDEO_UNKNOWN5c m_video[0x5c/2] /* $01cf at startup */ #define VIDEO_UNKNOWN5e m_video[0x5e/2] /* $01cf at startup */ #define VIDEO_UNKNOWN60 m_video[0x60/2] /* $01e3 at startup */ #define VIDEO_UNKNOWN62 m_video[0x62/2] /* $01cf at startup */ #define VIDEO_UNKNOWN64 m_video[0x64/2] /* $01ff at startup */ #define VIDEO_UNKNOWN66 m_video[0x66/2] /* $0183 at startup */ #define VIDEO_UNKNOWN68 m_video[0x68/2] /* $01ff at startup */ #define VIDEO_UNKNOWN6a m_video[0x6a/2] /* $000f at startup */ #define VIDEO_UNKNOWN6c m_video[0x6c/2] /* $018f at startup */ #define VIDEO_UNKNOWN6e m_video[0x6e/2] /* $01ff at startup */ #define VIDEO_UNKNOWN70 m_video[0x70/2] /* $000f at startup */ #define VIDEO_UNKNOWN72 m_video[0x72/2] /* $000f at startup */ #define VIDEO_UNKNOWN74 m_video[0x74/2] /* $01ff at startup */ #define VIDEO_UNKNOWN76 m_video[0x76/2] /* $01ff at startup */ #define VIDEO_UNKNOWN78 m_video[0x78/2] /* $01ff at startup */ #define VIDEO_UNKNOWN7a m_video[0x7a/2] /* $01ff at startup */ #define VIDEO_UNKNOWN7c m_video[0x7c/2] /* $0820 at startup */ #define VIDEO_UNKNOWN7e m_video[0x7e/2] /* $0100 at startup */ #define VIDEO_STARTSTEP m_video[0x80/2] /* drivedge only? */ #define VIDEO_LEFTSTEPLO m_video[0x82/2] /* drivedge only? */ #define VIDEO_LEFTSTEPHI m_video[0x84/2] /* drivedge only? */ #define VIDEO_RIGHTSTEPLO m_video[0x86/2] /* drivedge only? */ #define VIDEO_RIGHTSTEPHI m_video[0x88/2] /* drivedge only? */ #define VIDEOINT_SCANLINE 0x0004 #define VIDEOINT_BLITTER 0x0040 #define XFERFLAG_TRANSPARENT 0x0001 #define XFERFLAG_XFLIP 0x0002 #define XFERFLAG_YFLIP 0x0004 #define XFERFLAG_DSTXSCALE 0x0008 #define XFERFLAG_DYDXSIGN 0x0010 #define XFERFLAG_DXDYSIGN 0x0020 #define XFERFLAG_UNKNOWN8 0x0100 #define XFERFLAG_CLIP 0x0400 #define XFERFLAG_UNKNOWN15 0x8000 #define XFERFLAG_KNOWNFLAGS (XFERFLAG_TRANSPARENT | XFERFLAG_XFLIP | XFERFLAG_YFLIP | XFERFLAG_DSTXSCALE | XFERFLAG_DYDXSIGN | XFERFLAG_DXDYSIGN | XFERFLAG_CLIP) #define VRAM_WIDTH 512 /************************************* * * Macros and inlines * *************************************/ #define ADJUSTED_HEIGHT(x) ((((x) >> 1) & 0x100) | ((x) & 0xff)) inline offs_t itech32_state::compute_safe_address(int x, int y) { return ((y & m_vram_ymask) * 512) + (x & m_vram_xmask); } inline void itech32_state::disable_clipping() { m_clip_save = m_clip_rect; m_clip_rect.set(0, 0xfff, 0, 0xfff); m_scaled_clip_rect.set(0, 0xfff << 8, 0, 0xfff << 8); } inline void itech32_state::enable_clipping() { m_clip_rect = m_clip_save; m_scaled_clip_rect.min_x = m_clip_rect.min_x << 8; m_scaled_clip_rect.max_x = m_clip_rect.max_x << 8; m_scaled_clip_rect.min_y = m_clip_rect.min_y << 8; m_scaled_clip_rect.max_y = m_clip_rect.max_y << 8; } /************************************* * * Video start * *************************************/ void itech32_state::video_start() { int i; /* allocate memory */ m_videoram = std::make_unique(VRAM_WIDTH * (m_vram_height + 16) * 2); std::fill_n(&m_videoram[0], VRAM_WIDTH * (m_vram_height + 16) * 2, 0xff); /* videoplane[0] is the foreground; videoplane[1] is the background */ m_videoplane[0] = &m_videoram[0 * VRAM_WIDTH * (m_vram_height + 16) + 8 * VRAM_WIDTH]; m_videoplane[1] = &m_videoram[1 * VRAM_WIDTH * (m_vram_height + 16) + 8 * VRAM_WIDTH]; /* set the masks */ m_vram_mask = VRAM_WIDTH * m_vram_height - 1; m_vram_xmask = VRAM_WIDTH - 1; m_vram_ymask = m_vram_height - 1; /* clear the planes initially */ for (i = 0; i < VRAM_WIDTH * m_vram_height; i++) m_videoplane[0][i] = m_videoplane[1][i] = 0xff; /* fetch the GROM base */ m_grom_bank = 0; m_grom_bank_mask = m_grom.length() >> 24; if (m_grom_bank_mask == 2) m_grom_bank_mask = 3; /* reset statics */ std::fill_n(&m_video[0], m_video.bytes() >> 1, 0); m_scanline_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(itech32_state::scanline_interrupt),this)); m_enable_latch[0] = 1; m_enable_latch[1] = (m_planes > 1) ? 1 : 0; save_item(NAME(m_xfer_xcount)); save_item(NAME(m_xfer_ycount)); save_item(NAME(m_xfer_xcur)); save_item(NAME(m_xfer_ycur)); save_item(NAME(m_grom_bank)); save_item(NAME(m_color_latch)); save_item(NAME(m_enable_latch)); save_pointer(NAME(m_videoram), VRAM_WIDTH * (m_vram_height + 16) * 2); } /************************************* * * Latches * *************************************/ void itech32_state::timekill_colora_w(u8 data) { m_enable_latch[0] = (~data >> 5) & 1; m_enable_latch[1] = (~data >> 7) & 1; m_color_latch[0] = (data & 0x0f) << 8; } void itech32_state::timekill_colorbc_w(u8 data) { m_color_latch[1] = ((data & 0xf0) << 4) | 0x1000; } void itech32_state::timekill_intensity_w(u8 data) { double intensity = (double)(data & 0xff) / (double)0x60; for (int i = 0; i < 8192; i++) m_palette->set_pen_contrast(i, intensity); } void itech32_state::bloodstm_plane_w(u8 data) { m_enable_latch[0] = (~data >> 1) & 1; m_enable_latch[1] = (~data >> 2) & 1; } void itech32_state::itech020_plane_w(u8 data) { m_enable_latch[0] = (~data >> 1) & 1; m_enable_latch[1] = (~data >> 2) & 1; m_grom_bank = ((data >> 6) & m_grom_bank_mask) << 24; } /************************************* * * Palette I/O * *************************************/ WRITE16_MEMBER(itech32_state::bloodstm_paletteram_w) { /* in test mode, the LSB is used; in game mode, the MSB is used */ if (!ACCESSING_BITS_0_7 && (offset & 1)) { data >>= 8; mem_mask >>= 8; } m_palette->write16(offset, data, mem_mask); } /************************************* * * Debugging * *************************************/ void drivedge_state::logblit(const char *tag) { if (!machine().input().code_pressed(KEYCODE_L)) return; if (VIDEO_TRANSFER_FLAGS == 0x5490) { /* polygon drawing */ logerror("%s: e=%d%d f=%04x s=(%03x-%03x,%03x) w=%03x h=%03x b=%02x%04x c=%02x%02x ss=%04x,%04x ds=%04x,%04x ls=%04x%04x rs=%04x%04x u80=%04x", tag, m_enable_latch[0], m_enable_latch[1], VIDEO_TRANSFER_FLAGS, VIDEO_TRANSFER_X, VIDEO_RIGHTCLIP, VIDEO_TRANSFER_Y, VIDEO_TRANSFER_WIDTH, VIDEO_TRANSFER_HEIGHT, VIDEO_TRANSFER_ADDRHI, VIDEO_TRANSFER_ADDRLO, m_color_latch[0] >> 8, m_color_latch[1] >> 8, VIDEO_SRC_XSTEP, VIDEO_SRC_YSTEP, VIDEO_DST_XSTEP, VIDEO_DST_YSTEP, VIDEO_LEFTSTEPHI, VIDEO_LEFTSTEPLO, VIDEO_RIGHTSTEPHI, VIDEO_RIGHTSTEPLO, VIDEO_STARTSTEP); logerror(" v0=%08x v1=%08x v2=%08x v3=%08x\n", m_zbuf_control[0], m_zbuf_control[1], m_zbuf_control[2], m_zbuf_control[3]); } else { itech32_state::logblit(tag); } } void itech32_state::logblit(const char *tag) { if (!machine().input().code_pressed(KEYCODE_L)) return; if (m_video[0x16/2] == 0x100 && m_video[0x18/2] == 0x100 && m_video[0x1a/2] == 0x000 && m_video[0x1c/2] == 0x100 && m_video[0x1e/2] == 0x000 && m_video[0x20/2] == 0x000) { logerror("%s: e=%d%d f=%04x c=%02x%02x %02x%04x -> (%03x,%03x) %3dx%3dc=(%03x,%03x)-(%03x,%03x)", tag, m_enable_latch[0], m_enable_latch[1], VIDEO_TRANSFER_FLAGS, m_color_latch[0] >> 8, m_color_latch[1] >> 8, VIDEO_TRANSFER_ADDRHI, VIDEO_TRANSFER_ADDRLO, VIDEO_TRANSFER_X, VIDEO_TRANSFER_Y, VIDEO_TRANSFER_WIDTH, ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT), VIDEO_LEFTCLIP, VIDEO_TOPCLIP, VIDEO_RIGHTCLIP, VIDEO_BOTTOMCLIP); } else { logerror("%s: e=%d%d f=%04x c=%02x%02x %02x%04x -> (%03x,%03x) %3dx%3d c=(%03x,%03x)-(%03x,%03x) s=%04x %04x %04x %04x %04x %04x", tag, m_enable_latch[0], m_enable_latch[1], VIDEO_TRANSFER_FLAGS, m_color_latch[0] >> 8, m_color_latch[1] >> 8, VIDEO_TRANSFER_ADDRHI, VIDEO_TRANSFER_ADDRLO, VIDEO_TRANSFER_X, VIDEO_TRANSFER_Y, VIDEO_TRANSFER_WIDTH, ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT), VIDEO_LEFTCLIP, VIDEO_TOPCLIP, VIDEO_RIGHTCLIP, VIDEO_BOTTOMCLIP, m_video[0x16/2], m_video[0x18/2], m_video[0x1a/2], m_video[0x1c/2], m_video[0x1e/2], m_video[0x20/2]); } logerror("\n"); } /************************************* * * Video interrupts * *************************************/ void itech32_state::update_interrupts(int fast) { int scanline_state = 0, blitter_state = 0; if (VIDEO_INTSTATE & VIDEO_INTENABLE & VIDEOINT_SCANLINE) scanline_state = 1; if (VIDEO_INTSTATE & VIDEO_INTENABLE & VIDEOINT_BLITTER) blitter_state = 1; update_interrupts(-1, blitter_state, scanline_state); } TIMER_CALLBACK_MEMBER(itech32_state::scanline_interrupt) { /* set timer for next frame */ m_scanline_timer->adjust(m_screen->time_until_pos(VIDEO_INTSCANLINE)); /* set the interrupt bit in the status reg */ //logerror("-------------- (DISPLAY INT @ %d) ----------------\n", m_screen->vpos()); VIDEO_INTSTATE |= VIDEOINT_SCANLINE; /* update the interrupt state */ update_interrupts(0); } /************************************* * * Uncompressed blitter functions * *************************************/ void itech32_state::draw_raw(u16 *base, u16 color) { u8 *src = &m_grom[(m_grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % m_grom.length()]; int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1; int width = VIDEO_TRANSFER_WIDTH << 8; int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT) << 8; int xsrcstep = VIDEO_SRC_XSTEP; int ysrcstep = VIDEO_SRC_YSTEP; int sx, sy = (VIDEO_TRANSFER_Y & 0xfff) << 8; int startx = (VIDEO_TRANSFER_X & 0xfff) << 8; int xdststep = 0x100; int ydststep = VIDEO_DST_YSTEP; int x, y; /* adjust for (lack of) clipping */ if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP)) disable_clipping(); /* adjust for scaling */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DSTXSCALE) xdststep = VIDEO_DST_XSTEP; /* adjust for flipping */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_XFLIP) xdststep = -xdststep; if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP) ydststep = -ydststep; /* loop over Y in src pixels */ for (y = 0; y < height; y += ysrcstep, sy += ydststep) { u8 *rowsrc = &src[(y >> 8) * (width >> 8)]; /* simpler case: VIDEO_YSTEP_PER_X is zero */ if (VIDEO_YSTEP_PER_X == 0) { /* clip in the Y direction */ if (sy >= m_scaled_clip_rect.min_y && sy < m_scaled_clip_rect.max_y) { u32 dstoffs; /* direction matters here */ sx = startx; if (xdststep > 0) { /* skip left pixels */ for (x = 0; x < width && sx < m_scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep) ; /* compute the address */ dstoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8); /* render middle pixels */ for ( ; x < width && sx < m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen) base[(dstoffs + (sx >> 8)) & m_vram_mask] = pixel | color; } } else { /* skip right pixels */ for (x = 0; x < width && sx >= m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep) ; /* compute the address */ dstoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8); /* render middle pixels */ for ( ; x < width && sx >= m_scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen) base[(dstoffs + (sx >> 8)) & m_vram_mask] = pixel | color; } } } } /* slow case: VIDEO_YSTEP_PER_X is non-zero */ else { int ystep = (VIDEO_TRANSFER_FLAGS & XFERFLAG_DYDXSIGN) ? -VIDEO_YSTEP_PER_X : VIDEO_YSTEP_PER_X; int ty = sy; /* render all pixels */ sx = startx; for (x = 0; x < width && sx < m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep, ty += ystep) if (m_scaled_clip_rect.contains(sx, ty)) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen) base[compute_safe_address(sx >> 8, ty >> 8)] = pixel | color; } } /* apply skew */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DXDYSIGN) startx += VIDEO_XSTEP_PER_Y; else startx -= VIDEO_XSTEP_PER_Y; } /* restore cliprects */ if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP)) enable_clipping(); } void drivedge_state::draw_raw(u16 *base, u16 *zbase, u16 color) { u8 *src = &m_grom[(m_grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % m_grom.length()]; int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1; int width = VIDEO_TRANSFER_WIDTH << 8; int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT) << 8; int xsrcstep = VIDEO_SRC_XSTEP; int ysrcstep = VIDEO_SRC_YSTEP; int sx, sy = ((VIDEO_TRANSFER_Y & 0xfff) << 8) + 0x80; int startx = ((VIDEO_TRANSFER_X & 0xfff) << 8) + 0x80; int xdststep = 0x100; int ydststep = VIDEO_DST_YSTEP; int32_t z0 = m_zbuf_control[2] & 0x7ff00; int32_t zmatch = (m_zbuf_control[2] & 0x1f) << 11; int32_t srcdelta = 0; int x, y; /* adjust for (lack of) clipping */ if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP)) disable_clipping(); /* adjust for scaling */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DSTXSCALE) xdststep = VIDEO_DST_XSTEP; /* adjust for flipping */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_XFLIP) xdststep = -xdststep; if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP) ydststep = -ydststep; /* loop over Y in src pixels */ for (y = 0; y < height; y += ysrcstep, sy += ydststep) { u8 *rowsrc = src + (srcdelta >> 8); /* in the polygon case, we don't factor in the Y */ if (VIDEO_TRANSFER_FLAGS != 0x5490) rowsrc += (y >> 8) * (width >> 8); else width = 1000 << 8; /* simpler case: VIDEO_YSTEP_PER_X is zero */ if (VIDEO_YSTEP_PER_X == 0) { /* clip in the Y direction */ if (sy >= m_scaled_clip_rect.min_y && sy < m_scaled_clip_rect.max_y) { u32 dstoffs, zbufoffs; int32_t z = z0; /* direction matters here */ sx = startx; if (xdststep > 0) { /* skip left pixels */ for (x = 0; x < width && sx < m_scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep) z += (int32_t)m_zbuf_control[0]; /* compute the address */ dstoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8); zbufoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8); /* render middle pixels */ if (m_zbuf_control[3] & 0x8000) { for ( ; x < width && sx < m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen) { base[(dstoffs + (sx >> 8)) & m_vram_mask] = pixel | color; zbase[(zbufoffs + (sx >> 8)) & m_vram_mask] = (z >> 8) | zmatch; } z += (int32_t)m_zbuf_control[0]; } } else if (m_zbuf_control[3] & 0x4000) { for ( ; x < width && sx < m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen && zmatch == (zbase[(zbufoffs + (sx >> 8)) & m_vram_mask] & (0x1f << 11))) base[(dstoffs + (sx >> 8)) & m_vram_mask] = pixel | color; z += (int32_t)m_zbuf_control[0]; } } else { for ( ; x < width && sx < m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen && ((z >> 8) <= (zbase[(zbufoffs + (sx >> 8)) & m_vram_mask] & 0x7ff))) { base[(dstoffs + (sx >> 8)) & m_vram_mask] = pixel | color; zbase[(zbufoffs + (sx >> 8)) & m_vram_mask] = (z >> 8) | zmatch; } z += (int32_t)m_zbuf_control[0]; } } } else { /* skip right pixels */ for (x = 0; x < width && sx >= m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep) z += (int32_t)m_zbuf_control[0]; /* compute the address */ dstoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8); zbufoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8); /* render middle pixels */ if (m_zbuf_control[3] & 0x8000) { for ( ; x < width && sx >= m_scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen) { base[(dstoffs + (sx >> 8)) & m_vram_mask] = pixel | color; zbase[(zbufoffs + (sx >> 8)) & m_vram_mask] = (z >> 8) | zmatch; } z += (int32_t)m_zbuf_control[0]; } } else if (m_zbuf_control[3] & 0x4000) { for ( ; x < width && sx >= m_scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen && zmatch == (zbase[(zbufoffs + (sx >> 8)) & m_vram_mask] & (0x1f << 11))) base[(dstoffs + (sx >> 8)) & m_vram_mask] = pixel | color; z += (int32_t)m_zbuf_control[0]; } } else { for ( ; x < width && sx >= m_scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen && ((z >> 8) <= (zbase[(zbufoffs + (sx >> 8)) & m_vram_mask] & 0x7ff))) { base[(dstoffs + (sx >> 8)) & m_vram_mask] = pixel | color; zbase[(zbufoffs + (sx >> 8)) & m_vram_mask] = (z >> 8) | zmatch; } z += (int32_t)m_zbuf_control[0]; } } } } } /* slow case: VIDEO_YSTEP_PER_X is non-zero */ else { int ystep = (VIDEO_TRANSFER_FLAGS & XFERFLAG_DYDXSIGN) ? -VIDEO_YSTEP_PER_X : VIDEO_YSTEP_PER_X; int ty = sy; int32_t z = z0; /* render all pixels */ sx = startx; if (m_zbuf_control[3] & 0x8000) { for (x = 0; x < width && sx < m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep, ty += ystep) if (m_scaled_clip_rect.contains(sx, ty)) { int pixel = rowsrc[x >> 8]; if (pixel != transparent_pen) { base[compute_safe_address(sx >> 8, ty >> 8)] = pixel | color; zbase[compute_safe_address(sx >> 8, ty >> 8)] = (z >> 8) | zmatch; } z += (int32_t)m_zbuf_control[0]; } } else if (m_zbuf_control[3] & 0x4000) { for (x = 0; x < width && sx < m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep, ty += ystep) if (m_scaled_clip_rect.contains(sx, ty)) { int pixel = rowsrc[x >> 8]; u16 *zbuf = &zbase[compute_safe_address(sx >> 8, ty >> 8)]; if (pixel != transparent_pen && zmatch == (*zbuf & (0x1f << 11))) { base[compute_safe_address(sx >> 8, ty >> 8)] = pixel | color; *zbuf = (z >> 8) | zmatch; } z += (int32_t)m_zbuf_control[0]; } } else { for (x = 0; x < width && sx < m_scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep, ty += ystep) if (m_scaled_clip_rect.contains(sx, ty)) { int pixel = rowsrc[x >> 8]; u16 *zbuf = &zbase[compute_safe_address(sx >> 8, ty >> 8)]; if (pixel != transparent_pen && ((z >> 8) <= (*zbuf & 0x7ff))) { base[compute_safe_address(sx >> 8, ty >> 8)] = pixel | color; *zbuf = (z >> 8) | zmatch; } z += (int32_t)m_zbuf_control[0]; } } } /* apply skew */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DXDYSIGN) startx += VIDEO_XSTEP_PER_Y; else startx -= VIDEO_XSTEP_PER_Y; /* update the per-scanline parameters */ if (VIDEO_TRANSFER_FLAGS == 0x5490) { startx += (int32_t)((VIDEO_LEFTSTEPHI << 16) | VIDEO_LEFTSTEPLO); m_scaled_clip_rect.max_x += (int32_t)((VIDEO_RIGHTSTEPHI << 16) | VIDEO_RIGHTSTEPLO); srcdelta += (int16_t)VIDEO_STARTSTEP; } z0 += (int32_t)m_zbuf_control[1]; } /* restore cliprects */ if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP)) enable_clipping(); /* reflect the final values into registers */ VIDEO_TRANSFER_X = (VIDEO_TRANSFER_X & ~0xfff) | (startx >> 8); VIDEO_RIGHTCLIP = (VIDEO_RIGHTCLIP & ~0xfff) | (m_scaled_clip_rect.max_x >> 8); VIDEO_TRANSFER_Y = (VIDEO_TRANSFER_Y & ~0xfff) | ((VIDEO_TRANSFER_Y + (y >> 8)) & 0xfff); VIDEO_TRANSFER_ADDRLO += srcdelta >> 8; m_zbuf_control[2] = (m_zbuf_control[2] & ~0x7ff00) | (z0 & 0x7ff00); } /************************************* * * Compressed blitter macros * *************************************/ #define GET_NEXT_RUN(xleft, count, innercount, src) \ do { \ /* load next RLE chunk if needed */ \ if (!count) \ { \ count = *src++; \ val = (count & 0x80) ? -1 : *src++; \ count &= 0x7f; \ } \ \ /* determine how much to bite off */ \ innercount = (xleft > count) ? count : xleft; \ count -= innercount; \ xleft -= innercount; \ } while (0) #define SKIP_RLE(skip, xleft, count, innercount, src)\ do { \ /* scan RLE until done */ \ for (xleft = skip; xleft > 0; ) \ { \ /* load next RLE chunk if needed */ \ GET_NEXT_RUN(xleft, count, innercount, src);\ \ /* skip past the data */ \ if (val == -1) src += innercount; \ } \ } while (0) /************************************* * * Fast compressed blitter functions * *************************************/ inline void itech32_state::draw_rle_fast(u16 *base, u16 color) { u8 *src = &m_grom[(m_grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % m_grom.length()]; int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1; int width = VIDEO_TRANSFER_WIDTH; int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT); int sx = VIDEO_TRANSFER_X & 0xfff; int sy = (VIDEO_TRANSFER_Y & 0xfff) << 8; int xleft, y, count = 0, val = 0, innercount; int ydststep = VIDEO_DST_YSTEP; int lclip, rclip; /* determine clipping */ lclip = m_clip_rect.min_x - sx; if (lclip < 0) lclip = 0; rclip = sx + width - m_clip_rect.max_x; if (rclip < 0) rclip = 0; width -= lclip + rclip; sx += lclip; /* adjust for flipping */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP) ydststep = -ydststep; /* loop over Y in src pixels */ for (y = 0; y < height; y++, sy += ydststep) { u32 dstoffs; /* clip in the Y direction */ if (sy < m_scaled_clip_rect.min_y || sy >= m_scaled_clip_rect.max_y) { SKIP_RLE(width + lclip + rclip, xleft, count, innercount, src); continue; } /* compute the address */ dstoffs = compute_safe_address(sx, sy >> 8); /* left clip */ SKIP_RLE(lclip, xleft, count, innercount, src); /* loop until gone */ for (xleft = width; xleft > 0; ) { /* load next RLE chunk if needed */ GET_NEXT_RUN(xleft, count, innercount, src); /* run of literals */ if (val == -1) while (innercount--) { int pixel = *src++; if (pixel != transparent_pen) base[dstoffs & m_vram_mask] = color | pixel; dstoffs++; } /* run of non-transparent repeats */ else if (val != transparent_pen) { val |= color; while (innercount--) base[dstoffs++ & m_vram_mask] = val; } /* run of transparent repeats */ else dstoffs += innercount; } /* right clip */ SKIP_RLE(rclip, xleft, count, innercount, src); } } inline void itech32_state::draw_rle_fast_xflip(u16 *base, u16 color) { u8 *src = &m_grom[(m_grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % m_grom.length()]; int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1; int width = VIDEO_TRANSFER_WIDTH; int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT); int sx = VIDEO_TRANSFER_X & 0xfff; int sy = (VIDEO_TRANSFER_Y & 0xfff) << 8; int xleft, y, count = 0, val = 0, innercount; int ydststep = VIDEO_DST_YSTEP; int lclip, rclip; /* determine clipping */ lclip = sx - m_clip_rect.max_x; if (lclip < 0) lclip = 0; rclip = m_clip_rect.min_x - (sx - width); if (rclip < 0) rclip = 0; width -= lclip + rclip; sx -= lclip; /* adjust for flipping */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP) ydststep = -ydststep; /* loop over Y in src pixels */ for (y = 0; y < height; y++, sy += ydststep) { u32 dstoffs; /* clip in the Y direction */ if (sy < m_scaled_clip_rect.min_y || sy >= m_scaled_clip_rect.max_y) { SKIP_RLE(width + lclip + rclip, xleft, count, innercount, src); continue; } /* compute the address */ dstoffs = compute_safe_address(sx, sy >> 8); /* left clip */ SKIP_RLE(lclip, xleft, count, innercount, src); /* loop until gone */ for (xleft = width; xleft > 0; ) { /* load next RLE chunk if needed */ GET_NEXT_RUN(xleft, count, innercount, src); /* run of literals */ if (val == -1) while (innercount--) { int pixel = *src++; if (pixel != transparent_pen) base[dstoffs & m_vram_mask] = color | pixel; dstoffs--; } /* run of non-transparent repeats */ else if (val != transparent_pen) { val |= color; while (innercount--) base[dstoffs-- & m_vram_mask] = val; } /* run of transparent repeats */ else dstoffs -= innercount; } /* right clip */ SKIP_RLE(rclip, xleft, count, innercount, src); } } /************************************* * * Slow compressed blitter functions * *************************************/ inline void itech32_state::draw_rle_slow(u16 *base, u16 color) { u8 *src = &m_grom[(m_grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % m_grom.length()]; int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1; int width = VIDEO_TRANSFER_WIDTH; int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT); int sx, sy = (VIDEO_TRANSFER_Y & 0xfff) << 8; int xleft, y, count = 0, val = 0, innercount; int xdststep = 0x100; int ydststep = VIDEO_DST_YSTEP; int startx = (VIDEO_TRANSFER_X & 0xfff) << 8; /* adjust for scaling */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DSTXSCALE) xdststep = VIDEO_DST_XSTEP; /* adjust for flipping */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_XFLIP) xdststep = -xdststep; if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP) ydststep = -ydststep; /* loop over Y in src pixels */ for (y = 0; y < height; y++, sy += ydststep) { u32 dstoffs; /* clip in the Y direction */ if (sy < m_scaled_clip_rect.min_y || sy >= m_scaled_clip_rect.max_y) { SKIP_RLE(width, xleft, count, innercount, src); continue; } /* compute the address */ sx = startx; dstoffs = compute_safe_address(m_clip_rect.min_x, sy >> 8) - m_clip_rect.min_x; /* loop until gone */ for (xleft = width; xleft > 0; ) { /* load next RLE chunk if needed */ GET_NEXT_RUN(xleft, count, innercount, src); /* run of literals */ if (val == -1) for ( ; innercount--; sx += xdststep) { int pixel = *src++; if (pixel != transparent_pen) if (sx >= m_scaled_clip_rect.min_x && sx < m_scaled_clip_rect.max_x) base[(dstoffs + (sx >> 8)) & m_vram_mask] = color | pixel; } /* run of non-transparent repeats */ else if (val != transparent_pen) { val |= color; for ( ; innercount--; sx += xdststep) if (sx >= m_scaled_clip_rect.min_x && sx < m_scaled_clip_rect.max_x) base[(dstoffs + (sx >> 8)) & m_vram_mask] = val; } /* run of transparent repeats */ else sx += xdststep * innercount; } /* apply skew */ if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DXDYSIGN) startx += VIDEO_XSTEP_PER_Y; else startx -= VIDEO_XSTEP_PER_Y; } } void itech32_state::draw_rle(u16 *base, u16 color) { /* adjust for (lack of) clipping */ if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP)) disable_clipping(); /* if we have an X scale, draw it slow */ if (((VIDEO_TRANSFER_FLAGS & XFERFLAG_DSTXSCALE) && VIDEO_DST_XSTEP != 0x100) || VIDEO_XSTEP_PER_Y) draw_rle_slow(base, color); /* else draw it fast */ else if (VIDEO_TRANSFER_FLAGS & XFERFLAG_XFLIP) draw_rle_fast_xflip(base, color); else draw_rle_fast(base, color); /* restore cliprects */ if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP)) enable_clipping(); } /************************************* * * Shift register manipulation * *************************************/ void itech32_state::shiftreg_clear(u16 *base, u16 *zbase) { const int ydir = (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP) ? -1 : 1; const int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT); const int sx = VIDEO_TRANSFER_X & 0xfff; int sy = VIDEO_TRANSFER_Y & 0xfff; /* first line is the source */ u16 *src = &base[compute_safe_address(sx, sy)]; sy += ydir; /* loop over height */ for (int y = 1; y < height; y++) { memcpy(&base[compute_safe_address(sx, sy)], src, 512*2); sy += ydir; } } void drivedge_state::shiftreg_clear(u16 *base, u16 *zbase) { int ydir = (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP) ? -1 : 1; int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT); int sx = VIDEO_TRANSFER_X & 0xfff; int sy = VIDEO_TRANSFER_Y & 0xfff; /* first line is the source */ u16 *src = &base[compute_safe_address(sx, sy)]; sy += ydir; /* loop over height */ for (int y = 1; y < height; y++) { memcpy(&base[compute_safe_address(sx, sy)], src, 512*2); if (zbase) { u16 zval = ((m_zbuf_control[2] >> 8) & 0x7ff) | ((m_zbuf_control[2] & 0x1f) << 11); u16 *dst = &zbase[compute_safe_address(sx, sy)]; int x; for (x = 0; x < 512; x++) *dst++ = zval; } sy += ydir; } } /************************************* * * Video commands * *************************************/ void itech32_state::handle_video_command() { /* only 6 known commands */ switch (VIDEO_COMMAND) { /* command 1: blit raw data */ case 1: command_blit_raw(); break; /* command 2: blit RLE-compressed data */ case 2: g_profiler.start(PROFILER_USER2); if (BLIT_LOGGING) logblit("Blit RLE"); if (m_enable_latch[0]) draw_rle(m_videoplane[0], m_color_latch[0]); if (m_enable_latch[1]) draw_rle(m_videoplane[1], m_color_latch[1]); g_profiler.stop(); break; /* command 3: set up raw data transfer */ case 3: if (BLIT_LOGGING) logblit("Raw Xfer"); m_xfer_xcount = VIDEO_TRANSFER_WIDTH; m_xfer_ycount = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT); m_xfer_xcur = VIDEO_TRANSFER_X & 0xfff; m_xfer_ycur = VIDEO_TRANSFER_Y & 0xfff; break; /* command 4: flush? */ case 4: break; /* command 5: reset? */ case 5: break; /* command 6: perform shift register copy */ case 6: command_shift_reg(); break; default: if (BLIT_LOGGING) logerror("Unknown blit command %d\n", VIDEO_COMMAND); break; } /* tell the processor we're done */ VIDEO_INTSTATE |= VIDEOINT_BLITTER; update_interrupts(1); } void itech32_state::command_blit_raw() { g_profiler.start(PROFILER_USER1); if (BLIT_LOGGING) logblit("Blit Raw"); if (m_enable_latch[0]) draw_raw(m_videoplane[0], m_color_latch[0]); if (m_enable_latch[1]) draw_raw(m_videoplane[1], m_color_latch[1]); g_profiler.stop(); } void drivedge_state::command_blit_raw() { g_profiler.start(PROFILER_USER1); if (BLIT_LOGGING) logblit("Blit Raw"); if (m_enable_latch[0]) draw_raw(m_videoplane[0], m_videoplane[1], m_color_latch[0]); g_profiler.stop(); } void itech32_state::command_shift_reg() { g_profiler.start(PROFILER_USER3); if (BLIT_LOGGING) logblit("ShiftReg"); if (m_enable_latch[0]) shiftreg_clear(m_videoplane[0], nullptr); if (m_enable_latch[1]) shiftreg_clear(m_videoplane[1], nullptr); g_profiler.stop(); } void drivedge_state::command_shift_reg() { g_profiler.start(PROFILER_USER3); if (BLIT_LOGGING) logblit("ShiftReg"); if (m_enable_latch[0]) shiftreg_clear(m_videoplane[0], m_videoplane[1]); g_profiler.stop(); } /************************************* * * Video I/O * *************************************/ void itech32_state::video_w(offs_t offset, u16 data, u16 mem_mask) { rectangle visarea; int old = m_video[offset]; COMBINE_DATA(&m_video[offset]); switch (offset) { case 0x02/2: /* VIDEO_INTACK */ VIDEO_INTSTATE = old & ~data; update_interrupts(1); break; case 0x04/2: /* VIDEO_TRANSFER */ if (VIDEO_COMMAND == 3 && m_xfer_ycount) { offs_t addr = compute_safe_address(m_xfer_xcur, m_xfer_ycur); if (m_enable_latch[0]) { VIDEO_TRANSFER = m_videoplane[0][addr]; m_videoplane[0][addr] = (data & 0xff) | m_color_latch[0]; } if (m_enable_latch[1]) { VIDEO_TRANSFER = m_videoplane[1][addr]; m_videoplane[1][addr] = (data & 0xff) | m_color_latch[1]; } if (--m_xfer_xcount) m_xfer_xcur++; else if (--m_xfer_ycount) m_xfer_xcur = VIDEO_TRANSFER_X, m_xfer_xcount = VIDEO_TRANSFER_WIDTH, m_xfer_ycur++; } break; case 0x08/2: /* VIDEO_COMMAND */ handle_video_command(); break; case 0x0a/2: /* VIDEO_INTENABLE */ update_interrupts(1); break; case 0x24/2: /* VIDEO_LEFTCLIP */ m_clip_rect.min_x = VIDEO_LEFTCLIP; m_scaled_clip_rect.min_x = VIDEO_LEFTCLIP << 8; break; case 0x26/2: /* VIDEO_RIGHTCLIP */ m_clip_rect.max_x = VIDEO_RIGHTCLIP; m_scaled_clip_rect.max_x = VIDEO_RIGHTCLIP << 8; break; case 0x28/2: /* VIDEO_TOPCLIP */ m_clip_rect.min_y = VIDEO_TOPCLIP; m_scaled_clip_rect.min_y = VIDEO_TOPCLIP << 8; break; case 0x2a/2: /* VIDEO_BOTTOMCLIP */ m_clip_rect.max_y = VIDEO_BOTTOMCLIP; m_scaled_clip_rect.max_y = VIDEO_BOTTOMCLIP << 8; break; case 0x2c/2: /* VIDEO_INTSCANLINE */ m_scanline_timer->adjust(m_screen->time_until_pos(VIDEO_INTSCANLINE)); break; case 0x32/2: /* VIDEO_VTOTAL */ case 0x36/2: /* VIDEO_VBLANK_START */ case 0x38/2: /* VIDEO_VBLANK_END */ case 0x3a/2: /* VIDEO_HTOTAL */ case 0x3e/2: /* VIDEO_HBLANK_START */ case 0x40/2: /* VIDEO_HBLANK_END */ /* do some sanity checks first */ if ((VIDEO_HTOTAL > 0) && (VIDEO_VTOTAL > 0) && (VIDEO_VBLANK_START != VIDEO_VBLANK_END) && (VIDEO_HBLANK_START != VIDEO_HBLANK_END) && (VIDEO_HBLANK_START < VIDEO_HTOTAL) && (VIDEO_HBLANK_END < VIDEO_HTOTAL) && (VIDEO_VBLANK_START < VIDEO_VTOTAL) && (VIDEO_VBLANK_END < VIDEO_VTOTAL)) { visarea.min_x = visarea.min_y = 0; if (VIDEO_HBLANK_START > VIDEO_HBLANK_END) visarea.max_x = VIDEO_HBLANK_START - VIDEO_HBLANK_END - 1; else visarea.max_x = VIDEO_HTOTAL - VIDEO_HBLANK_END + VIDEO_HBLANK_START - 1; if (VIDEO_VBLANK_START > VIDEO_VBLANK_END) visarea.max_y = VIDEO_VBLANK_START - VIDEO_VBLANK_END - 1; else visarea.max_y = VIDEO_VTOTAL - VIDEO_VBLANK_END + VIDEO_VBLANK_START - 1; logerror("Configure Screen: HTOTAL: %x HBSTART: %x HBEND: %x VTOTAL: %x VBSTART: %x VBEND: %x\n", VIDEO_HTOTAL, VIDEO_HBLANK_START, VIDEO_HBLANK_END, VIDEO_VTOTAL, VIDEO_VBLANK_START, VIDEO_VBLANK_END); m_screen->configure(VIDEO_HTOTAL, VIDEO_VTOTAL, visarea, HZ_TO_ATTOSECONDS(VIDEO_CLOCK) * VIDEO_HTOTAL * VIDEO_VTOTAL); } break; } } u16 itech32_state::video_r(offs_t offset) { if (offset == 0) { return (m_video[offset] & ~0x08) | 0x04 | 0x01; } else if (offset == 3) { return 0xef;/*m_screen->vpos() - 1;*/ } return m_video[offset]; } /************************************* * * Alternate video I/O * *************************************/ void itech32_state::bloodstm_video_w(offs_t offset, u16 data, u16 mem_mask) { video_w(offset / 2, data, mem_mask); } u16 itech32_state::bloodstm_video_r(offs_t offset) { return video_r(offset / 2); } void drivedge_state::zbuf_control_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_zbuf_control[offset]); } /************************************* * * Main refresh * *************************************/ u32 itech32_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { int y; /* loop over height */ for (y = cliprect.min_y; y <= cliprect.max_y; y++) { u16 *src1 = &m_videoplane[0][compute_safe_address(VIDEO_DISPLAY_XORIGIN1, VIDEO_DISPLAY_YORIGIN1 + y)]; /* handle multi-plane case */ if (m_planes > 1) { u16 *src2 = &m_videoplane[1][compute_safe_address(VIDEO_DISPLAY_XORIGIN2 + VIDEO_DISPLAY_XSCROLL2, VIDEO_DISPLAY_YORIGIN2 + VIDEO_DISPLAY_YSCROLL2 + y)]; u16 scanline[384]; int x; /* blend the pixels in the scanline; color xxFF is transparent */ for (x = cliprect.min_x; x <= cliprect.max_x; x++) { u16 pixel = src1[x]; if ((pixel & 0xff) == 0xff) pixel = src2[x]; scanline[x] = pixel; } /* draw from the buffer */ draw_scanline16(bitmap, cliprect.min_x, y, cliprect.width(), &scanline[cliprect.min_x], nullptr); } /* otherwise, draw directly from VRAM */ else draw_scanline16(bitmap, cliprect.min_x, y, cliprect.width(), &src1[cliprect.min_x], nullptr); } return 0; }