// license:BSD-3-Clause // copyright-holders:Bryan McPhail /*************************************************************************** Taito F3 Video emulation - Bryan McPhail, mish@tendril.co.uk **************************************************************************** Brief overview: 4 scrolling layers (512x512 or 1024x512) of 4/5/6 bpp tiles. 1 scrolling text layer (512x512, characters generated in vram), 4bpp chars. 1 scrolling pixel layer (512x256 pixels generated in pivot ram), 4bpp pixels. 2 sprite banks (for double buffering of sprites) Sprites can be 4, 5 or 6 bpp Sprite scaling. Rowscroll on all playfields Line by line zoom on all playfields Column scroll on all playfields Line by line sprite and playfield priority mixing Notes: All special effects are controlled by an area in 'line ram'. Typically there are 256 values, one for each line of the screen (including clipped lines at the top of the screen). For example, at 0x8000 in lineram, there are 4 sets of 256 values (one for each playfield) and each value is the scale control for that line in the destination bitmap (screen). Therefore each line can have a different zoom value for special effects. This also applies to playfield priority, rowscroll, column scroll, sprite priority and VRAM/pivot layers. However - at the start of line ram there are also sets of 256 values controlling each effect - effects can be selectively applied to individual playfields or only certain lines out of the 256 can be active - in which case the last allowed value can be latched (typically used so a game can use one zoom or row value over the whole playfield). The programmers of some of these games made strange use of flipscreen - some games have all their graphics flipped in ROM, and use the flipscreen bit to display them correctly!. Most games display 232 scanlines, but some use lineram effects to clip themselves to 224 or less. **************************************************************************** Line ram memory map: Here 'playfield 1' refers to the first playfield in memory, etc 0x0000: Column line control ram (256 lines) 100x: Where bit 0 of x enables effect on playfield 1 Where bit 1 of x enables effect on playfield 2 Where bit 2 of x enables effect on playfield 3 Where bit 3 of x enables effect on playfield 4 0x0200: Line control ram for 0x5000 section. 0x0400: Line control ram for 0x6000 section. 180x: Where bit 0 of x latches sprite alpha value Where bit 1 of x latches tilemap alpha value 0x0600: Sprite control ram 1c0x: Where x enables sprite control word for that line 0x0800: Zoom line control ram (256 lines) 200x: Where bit 0 of x enables effect on playfield 1 Where bit 1 of x enables effect on playfield 2 Where bit 2 of x enables effect on playfield 3 Where bit 3 of x enables effect on playfield 4 0x0a00: Assumed unused. 0x0c00: Rowscroll line control ram (256 lines) 280x: Where bit 0 of x enables effect on playfield 1 Where bit 1 of x enables effect on playfield 2 Where bit 2 of x enables effect on playfield 3 Where bit 3 of x enables effect on playfield 4 0x0e00: Priority line control ram (256 lines) 2c0x: Where bit 0 of x enables effect on playfield 1 Where bit 1 of x enables effect on playfield 2 Where bit 2 of x enables effect on playfield 3 Where bit 3 of x enables effect on playfield 4 0x4000: Column scroll & clipping info 0x5000: Clip plane 0 (low bits) 0x5200: Clip plane 1 (low bits) 0x5400: Unused? 0x5600: Used by quizhuhu (taito logo and ingame text), and landmakr (winning text), special clip used in tandem with b*00 bit 11? [unemulated] 0x6000: Sync register 0x6004: Sprite alpha control 0xff00: VRAM/Pixel layer control 0xa000 enables pixel layer for this line (Disables VRAM layer) 0x2000 enables garbage pixels for this line (maybe another pixel bank?) [unemulated] 0x0800 seems to set the vram layer to be opaque [unemulated] Effect of other bits is unknown. 0x00c0: Alpha mode for sprites with pri value 0x00 0x0030: Alpha mode for sprites with pri value 0x00 0x000c: Alpha mode for sprites with pri value 0x00 0x0003: Alpha mode for sprites with pri value 0x00 0x6200: Alpha blending control 0x6400 - controls X zoom of tile - each tile collapses to 16 single colour lines xxx1 - affects bottom playfield xxx2 - xxx4 - xxx8 - affects top playfield xxfx - x zoom - 0 = 1st pixel 16 times 1 = 1st pixel 8, then 2nd 8 8 = 2 per pixel (these effects only known to be used on spcinvdj title screen and riding fight - not emulated) 1xxx = interpret palette ram for this playfield line as 15 bit and bilinear filter framebuffer(??) 3xxx = interpret palette ram for this playfield line as 15 bit 7xxx = interpret palette ram for this playfield line as 24 bit palette 8xxx = interpret palette ram for this playfield line as 21 bit palette (effect not emulated) 0x6600: Background - background palette entry (under all tilemaps) (takes part in alpha blending) (effect not emulated) 0x7000: Pivot/vram layer enable 0x7200: Cram layer priority 0x7400: Sprite clipping 0x7600: Sprite priority values 0xf000: Relative priority for sprites with pri value 0xc0 0x0f00: Relative priority for sprites with pri value 0x80 0x00f0: Relative priority for sprites with pri value 0x40 0x000f: Relative priority for sprites with pri value 0x00 0x8000: Playfield 1 scale (1 word per line, 256 lines, 0x80 = default no scale) 0x8200: Playfield 2/4 scale 0x8400: Playfield 3 scale 0x8600: Playfield 2/4 scale 0x00ff = Y scale (0x80 = no scale, > 0x80 is zoom in, < 0x80 is zoom out [0xc0 is half height of 0x80]) 0xff00 = X scale (0 = no scale, > 0 = zoom in, [0x8000 would be double length]) Playfield 2 & 4 registers seem to be interleaved, playfield 2 Y zoom is stored where you would expect playfield 4 y zoom to be and vice versa. 0x9000: Palette add (can affect opacity) [ not emulated ] 0xa000: Playfield 1 rowscroll (1 word per line, 256 lines) 0xa200: Playfield 2 rowscroll 0xa400: Playfield 3 rowscroll 0xa600: Playfield 4 rowscroll 0xb000: Playfield 1 priority (1 word per line, 256 lines) 0xb200: Playfield 2 priority 0xb400: Playfield 3 priority 0xb600: Playfield 4 priority 0xf000 = Blend mode, 0x3000 = Normal, 0x7000 = Alpha A, 0xb000 = Alpha B, others disable line 0x0800 = Clip line (totally disable line) 0x0400 = Assumed unused 0x0200 = If set enable clip plane 1 0x0100 = If set enable clip plane 0 0x00c0 = Assumed unused 0x0020 = Enable clip inverse mode for plane 1 0x0010 = Enable clip inverse mode for plane 0 0x000f = Layer priority 0xc000 - 0xffff: Unused. When sprite priority==playfield priority sprite takes precedence (Bubble Symph title) **************************************************************************** F3 sprite format: Word 0: 0xffff Tile number (LSB) Word 1: 0xff00 X zoom 0x00ff Y zoom Word 2: 0x03ff X position Word 3: 0x03ff Y position Word 4: 0xf000 Sprite block controls 0x0800 Sprite block start 0x0400 Use same colour on this sprite as block start 0x0200 Y flip 0x0100 X flip 0x00ff Colour Word 5: 0xffff Tile number (MSB), probably only low bits used Word 6: 0x8000 If set, jump to sprite location in low bits 0x03ff Location to jump to. Word 7: 0xffff Unused? Always zero? **************************************************************************** Playfield control information (0x660000-1f): Word 0- 3: X scroll values for the 4 playfields. Word 4- 7: Y scroll values for the 4 playfields. Word 8-11: Unused. Always zero. Word 12: Pixel + VRAM playfields X scroll Word 13: Pixel + VRAM playfields Y scroll Word 14: Unused. Always zero. Word 15: If set to 0x80, then 1024x512 playfields are used, else 512x512 top bits unused - writing to low 4 bits is desync or blank tilemap Playfield tile info: 0xc000 0000 - X/Y Flip 0x3000 ffff - Tile index 0x0c00 0000 - Extra planes enable (00 = 4bpp, 01 = 5bpp, 10 = unused?, 11 = 6bpp) 0x0200 0000 - Alpha blend mode 0x01ff 0000 - Colour ***************************************************************************/ #include "emu.h" #include "includes/taito_f3.h" #include "render.h" #include #define VERBOSE 0 #define DARIUSG_KLUDGE //#define DEBUG_F3 1 /* Game specific data, some of this can be removed when the software values are figured out */ struct F3config { int name; int extend; int sprite_lag; }; static const F3config f3_config_table[] = { /* Name Extend Lag */ { RINGRAGE, 0, 2 }, { ARABIANM, 0, 2 }, { RIDINGF, 1, 1 }, { GSEEKER, 0, 1 }, { COMMANDW, 1, 1 }, { SCFINALS, 0, 1 }, { TRSTAR, 1, 0 }, { GUNLOCK, 1, 2 }, { LIGHTBR, 1, 2 }, { KAISERKN, 0, 2 }, { DARIUSG, 0, 2 }, { BUBSYMPH, 1, 1 }, { SPCINVDX, 1, 1 }, { HTHERO95, 0, 1 }, { QTHEATER, 1, 1 }, { EACTION2, 1, 2 }, { RECALH, 1, 1 }, { SPCINV95, 0, 1 }, { TWINQIX, 1, 1 }, { QUIZHUHU, 1, 1 }, { PBOBBLE2, 0, 1 }, { GEKIRIDO, 0, 1 }, { KTIGER2, 0, 0 }, { BUBBLEM, 1, 1 }, { CLEOPATR, 0, 1 }, { PBOBBLE3, 0, 1 }, { ARKRETRN, 1, 1 }, { KIRAMEKI, 0, 1 }, { PUCHICAR, 1, 1 }, { PBOBBLE4, 0, 1 }, { POPNPOP, 1, 1 }, { LANDMAKR, 1, 1 }, { TMDRILL, 1, 0 }, {0} }; /* alpha_mode ---- --xx 0:disable 1:nomal 2:alpha 7000 3:alpha b000 ---1 ---- alpha level a --1- ---- alpha level b 1-------- opaque line */ /* pri_alp_bitmap ---- ---1 sprite priority 0 ---- --1- sprite priority 1 ---- -1-- sprite priority 2 ---- 1--- sprite priority 3 ---1 ---- alpha level a 7000 --1- ---- alpha level b 7000 -1-- ---- alpha level a b000 1--- ---- alpha level b b000 1111 1111 opaque pixel */ void taito_f3_state::device_post_load() { /* force a reread of the dynamic tiles in the pixel layer */ m_gfxdecode->gfx(0)->mark_all_dirty(); m_gfxdecode->gfx(1)->mark_all_dirty(); } /******************************************************************************/ void taito_f3_state::print_debug_info(bitmap_rgb32 &bitmap) { /* u16 *line_ram = m_line_ram; int l[16]; char buf[64*16]; char *bufptr = buf; bufptr += sprintf(bufptr,"%04X %04X %04X %04X\n", m_control_0[0] >> 6, m_control_0[1] >> 6, m_control_0[2] >> 6, m_control_0[3] >> 6); bufptr += sprintf(bufptr,"%04X %04X %04X %04X\n", m_control_0[4] >> 7, m_control_0[5] >> 7, m_control_0[6] >> 7, m_control_0[7] >> 7); bufptr += sprintf(bufptr,"%04X %04X %04X %04X\n", m_control_1[0], m_control_1[1], m_control_1[2], m_control_1[3]); bufptr += sprintf(bufptr,"%04X %04X %04X %04X\n", m_control_1[4], m_control_1[5], m_control_1[6], m_control_1[7]); bufptr += sprintf(bufptr,"%04X %04X %04X %04X %04X %04X %04X %04X\n", m_spriteram16_buffered[0], m_spriteram16_buffered[1], m_spriteram16_buffered[2], m_spriteram16_buffered[3], m_spriteram16_buffered[4], m_spriteram16_buffered[5], m_spriteram16_buffered[6], m_spriteram16_buffered[7]); bufptr += sprintf(bufptr,"%04X %04X %04X %04X %04X %04X %04X %04X\n", m_spriteram16_buffered[8], m_spriteram16_buffered[9], m_spriteram16_buffered[10], m_spriteram16_buffered[11], m_spriteram16_buffered[12], m_spriteram16_buffered[13], m_spriteram16_buffered[14], m_spriteram16_buffered[15]); bufptr += sprintf(bufptr,"%04X %04X %04X %04X %04X %04X %04X %04X\n", m_spriteram16_buffered[16], m_spriteram16_buffered[17], m_spriteram16_buffered[18], m_spriteram16_buffered[19], m_spriteram16_buffered[20], m_spriteram16_buffered[21], m_spriteram16_buffered[22], m_spriteram16_buffered[23]); l[0] = line_ram[0x0040*2] & 0xffff; l[1] = line_ram[0x00c0*2] & 0xffff; l[2] = line_ram[0x0140*2] & 0xffff; l[3] = line_ram[0x01c0*2] & 0xffff; bufptr += sprintf(bufptr,"Ctr1: %04x %04x %04x %04x\n", l[0], l[1], l[2], l[3]); l[0] = line_ram[0x0240*2] & 0xffff; l[1] = line_ram[0x02c0*2] & 0xffff; l[2] = line_ram[0x0340*2] & 0xffff; l[3] = line_ram[0x03c0*2] & 0xffff; bufptr += sprintf(bufptr,"Ctr2: %04x %04x %04x %04x\n", l[0], l[1], l[2], l[3]); l[0] = line_ram[0x2c60*2] & 0xffff; l[1] = line_ram[0x2ce0*2] & 0xffff; l[2] = line_ram[0x2d60*2] & 0xffff; l[3] = line_ram[0x2de0*2] & 0xffff; bufptr += sprintf(bufptr,"Pri : %04x %04x %04x %04x\n", l[0], l[1], l[2], l[3]); l[0] = line_ram[0x2060*2] & 0xffff; l[1] = line_ram[0x20e0*2] & 0xffff; l[2] = line_ram[0x2160*2] & 0xffff; l[3] = line_ram[0x21e0*2] & 0xffff; bufptr += sprintf(bufptr,"Zoom: %04x %04x %04x %04x\n", l[0], l[1], l[2], l[3]); l[0] = line_ram[0x2860*2] & 0xffff; l[1] = line_ram[0x28e0*2] & 0xffff; l[2] = line_ram[0x2960*2] & 0xffff; l[3] = line_ram[0x29e0*2] & 0xffff; bufptr += sprintf(bufptr,"Line: %04x %04x %04x %04x\n", l[0], l[1], l[2], l[3]); l[0] = line_ram[0x1c60*2] & 0xffff; l[1] = line_ram[0x1ce0*2] & 0xffff; l[2] = line_ram[0x1d60*2] & 0xffff; l[3] = line_ram[0x1de0*2] & 0xffff; bufptr += sprintf(bufptr,"Sprt: %04x %04x %04x %04x\n", l[0], l[1], l[2], l[3]); l[0] = line_ram[0x1860*2] & 0xffff; l[1] = line_ram[0x18e0*2] & 0xffff; l[2] = line_ram[0x1960*2] & 0xffff; l[3] = line_ram[0x19e0*2] & 0xffff; bufptr += sprintf(bufptr,"Pivt: %04x %04x %04x %04x\n", l[0], l[1], l[2], l[3]); l[0] = line_ram[0x1060*2] & 0xffff; l[1] = line_ram[0x10e0*2] & 0xffff; l[2] = line_ram[0x1160*2] & 0xffff; l[3] = line_ram[0x11e0*2] & 0xffff; bufptr += sprintf(bufptr,"Colm: %04x %04x %04x %04x\n", l[0], l[1], l[2], l[3]); l[0] = line_ram[0x1460*2] & 0xffff; l[1] = line_ram[0x14e0*2] & 0xffff; l[2] = line_ram[0x1560*2] & 0xffff; l[3] = line_ram[0x15e0*2] & 0xffff; bufptr += sprintf(bufptr,"5000: %04x %04x %04x %04x\n", l[0], l[1], l[2], l[3]); machine().ui().draw_text(&machine().render().ui_container(), buf, 60, 40); */ } /******************************************************************************/ template TILE_GET_INFO_MEMBER(taito_f3_state::get_tile_info) { const u32 tile = (m_pf_data[Layer][tile_index * 2 + 0] << 16) | (m_pf_data[Layer][tile_index * 2 + 1] & 0xffff); const u8 abtype = (tile >> (16 + 9)) & 1; // tiles can be configured to use 4, 5, or 6 bpp data. // if tiles use more than 4bpp, the bottom bits of the color code must be masked out. // This fixes (at least) the rain in round 6 of Arabian Magic. const u8 extra_planes = ((tile >> (16 + 10)) & 3); // 0 = 4bpp, 1 = 5bpp, 2 = unused?, 3 = 6bpp SET_TILE_INFO_MEMBER(3, tile & 0xffff, (tile >> 16) & 0x1ff & (~extra_planes), TILE_FLIPYX(tile >> 30)); tileinfo.category = abtype & 1; /* alpha blending type */ tileinfo.pen_mask = (extra_planes << 4) | 0x0f; } TILE_GET_INFO_MEMBER(taito_f3_state::get_tile_info_text) { int flags = 0; const u16 vram_tile = (m_textram[tile_index] & 0xffff); if (vram_tile & 0x0100) flags |= TILE_FLIPX; if (vram_tile & 0x8000) flags |= TILE_FLIPY; SET_TILE_INFO_MEMBER(0, vram_tile & 0xff, (vram_tile >> 9) & 0x3f, flags); } TILE_GET_INFO_MEMBER(taito_f3_state::get_tile_info_pixel) { int col_off; int flags = 0; int y_offs = (m_control_1[5] & 0x1ff); if (m_flipscreen) y_offs += 0x100; /* Colour is shared with VRAM layer */ if ((((tile_index & 0x1f) * 8 + y_offs) & 0x1ff) > 0xff) col_off = 0x800 + ((tile_index & 0x1f) << 6) + ((tile_index & 0xfe0) >> 5); else col_off = ((tile_index & 0x1f) << 6) + ((tile_index & 0xfe0) >> 5); const u16 vram_tile = (m_textram[col_off] & 0xffff); if (vram_tile & 0x0100) flags |= TILE_FLIPX; if (vram_tile & 0x8000) flags |= TILE_FLIPY; SET_TILE_INFO_MEMBER(1, tile_index, (vram_tile >> 9) & 0x3f, flags); } /******************************************************************************/ WRITE_LINE_MEMBER(taito_f3_state::screen_vblank) { // rising edge if (state) { if (m_sprite_lag==2) { if (machine().video().skip_this_frame() == 0) { get_sprite_info(m_spriteram16_buffered.get()); } memcpy(m_spriteram16_buffered.get(), m_spriteram.target(), 0x10000); } else if (m_sprite_lag == 1) { if (machine().video().skip_this_frame() == 0) { get_sprite_info(m_spriteram.target()); } } } } void taito_f3_state::video_start() { const F3config *pCFG = &f3_config_table[0]; m_alpha_level_2as = 127; m_alpha_level_2ad = 127; m_alpha_level_3as = 127; m_alpha_level_3ad = 127; m_alpha_level_2bs = 127; m_alpha_level_2bd = 127; m_alpha_level_3bs = 127; m_alpha_level_3bd = 127; m_alpha_level_last = -1; m_pdest_2a = 0x10; m_pdest_2b = 0x20; m_tr_2a = 0; m_tr_2b = 1; m_pdest_3a = 0x40; m_pdest_3b = 0x80; m_tr_3a = 0; m_tr_3b = 1; m_spritelist = nullptr; m_spriteram16_buffered = nullptr; m_pf_line_inf = nullptr; m_tile_opaque_sp = nullptr; /* Setup individual game */ do { if (pCFG->name == m_game) { break; } pCFG++; } while (pCFG->name); m_game_config=pCFG; if (m_game_config->extend) { m_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<0>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32); m_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<1>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32); m_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<2>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32); m_tilemap[3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<3>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 32); m_pf_data[0] = m_pf_ram + (0x0000 / 2); m_pf_data[1] = m_pf_ram + (0x2000 / 2); m_pf_data[2] = m_pf_ram + (0x4000 / 2); m_pf_data[3] = m_pf_ram + (0x6000 / 2); m_width_mask = 0x3ff; m_twidth_mask = 0x7f; m_twidth_mask_bit = 7; m_tilemap[0]->set_transparent_pen(0); m_tilemap[1]->set_transparent_pen(0); m_tilemap[2]->set_transparent_pen(0); m_tilemap[3]->set_transparent_pen(0); } else { m_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<0>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<1>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<2>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_tilemap[3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<3>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_tilemap[4] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<4>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_tilemap[5] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<5>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_tilemap[6] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<6>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_tilemap[7] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info<7>)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_pf_data[0] = m_pf_ram + (0x0000 / 2); m_pf_data[1] = m_pf_ram + (0x1000 / 2); m_pf_data[2] = m_pf_ram + (0x2000 / 2); m_pf_data[3] = m_pf_ram + (0x3000 / 2); m_pf_data[4] = m_pf_ram + (0x4000 / 2); m_pf_data[5] = m_pf_ram + (0x5000 / 2); m_pf_data[6] = m_pf_ram + (0x6000 / 2); m_pf_data[7] = m_pf_ram + (0x7000 / 2); m_width_mask = 0x1ff; m_twidth_mask = 0x3f; m_twidth_mask_bit = 6; m_tilemap[0]->set_transparent_pen(0); m_tilemap[1]->set_transparent_pen(0); m_tilemap[2]->set_transparent_pen(0); m_tilemap[3]->set_transparent_pen(0); m_tilemap[4]->set_transparent_pen(0); m_tilemap[5]->set_transparent_pen(0); m_tilemap[6]->set_transparent_pen(0); m_tilemap[7]->set_transparent_pen(0); } m_spriteram16_buffered = std::make_unique(0x10000 / 2); m_spritelist = std::make_unique(0x400); m_sprite_end = &m_spritelist[0]; m_vram_layer = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info_text)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); m_pixel_layer = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(taito_f3_state::get_tile_info_pixel)), TILEMAP_SCAN_COLS, 8, 8, 64, 32); m_pf_line_inf = std::make_unique(5); m_sa_line_inf = std::make_unique(1); m_screen->register_screen_bitmap(m_pri_alp_bitmap); m_tile_opaque_sp = std::make_unique(m_gfxdecode->gfx(2)->elements()); for (int i = 0; i < 8; i++) m_tile_opaque_pf[i] = std::make_unique(m_gfxdecode->gfx(3)->elements()); m_vram_layer->set_transparent_pen(0); m_pixel_layer->set_transparent_pen(0); // Palettes have 4 bpp indexes despite up to 6 bpp data. The unused top bits in the gfx data are cleared later. m_gfxdecode->gfx(2)->set_granularity(16); m_gfxdecode->gfx(3)->set_granularity(16); m_flipscreen = 0; memset(m_spriteram16_buffered.get(), 0, 0x10000); memset(&m_spriteram[0], 0, 0x10000); save_item(NAME(m_control_0)); save_item(NAME(m_control_1)); m_gfxdecode->gfx(0)->set_source((u8 *)m_charram.target()); m_gfxdecode->gfx(1)->set_source((u8 *)m_pivot_ram.target()); m_sprite_lag = m_game_config->sprite_lag; init_alpha_blend_func(); { gfx_element *sprite_gfx = m_gfxdecode->gfx(2); for (int c = 0; c < sprite_gfx->elements(); c++) { int chk_trans_or_opa = 0; const u8 *dp = sprite_gfx->get_data(c); for (int y = 0; y < sprite_gfx->height(); y++) { for (int x = 0; x < sprite_gfx->width(); x++) { if (!dp[x]) chk_trans_or_opa |= 2; else chk_trans_or_opa |= 1; } dp += sprite_gfx->rowbytes(); } if (chk_trans_or_opa == 1) m_tile_opaque_sp[c] = 1; else m_tile_opaque_sp[c] = 0; } } { gfx_element *pf_gfx = m_gfxdecode->gfx(3); for (int c = 0; c < pf_gfx->elements(); c++) { for (int extra_planes = 0; extra_planes < 4; extra_planes++) { int chk_trans_or_opa = 0; /* 0 = 4bpp, 1=5bpp, 2=?, 3=6bpp */ const u8 extra_mask = ((extra_planes << 4) | 0x0f); const u8 *dp = pf_gfx->get_data(c); for (int y = 0; y < pf_gfx->height(); y++) { for (int x = 0; x < pf_gfx->width(); x++) { if (!(dp[x] & extra_mask)) chk_trans_or_opa |= 2; else chk_trans_or_opa |= 1; } dp += pf_gfx->rowbytes(); } m_tile_opaque_pf[extra_planes][c] = chk_trans_or_opa; } } } } /******************************************************************************/ u16 taito_f3_state::pf_ram_r(offs_t offset) { return m_pf_ram[offset]; } void taito_f3_state::pf_ram_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_pf_ram[offset]); if (m_game_config->extend) { if (offset < 0x4000) m_tilemap[offset >> 12]->mark_tile_dirty((offset & 0xfff) >> 1); } else { if (offset < 0x4000) m_tilemap[offset >> 11]->mark_tile_dirty((offset & 0x7ff) >> 1); } } void taito_f3_state::control_0_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_control_0[offset]); } void taito_f3_state::control_1_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_control_1[offset]); } u16 taito_f3_state::spriteram_r(offs_t offset) { return m_spriteram[offset]; } void taito_f3_state::spriteram_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_spriteram[offset]); } u16 taito_f3_state::textram_r(offs_t offset) { return m_textram[offset]; } void taito_f3_state::textram_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_textram[offset]); m_vram_layer->mark_tile_dirty(offset); //m_vram_layer->mark_tile_dirty(offset + 1); if (offset > 0x7ff) offset -= 0x800; const int tile = offset; const int col_off = ((tile & 0x3f) << 5) + ((tile & 0xfc0) >> 6); m_pixel_layer->mark_tile_dirty(col_off); //m_pixel_layer->mark_tile_dirty(col_off+32); } u16 taito_f3_state::charram_r(offs_t offset) { return m_charram[offset]; } void taito_f3_state::charram_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_charram[offset]); m_gfxdecode->gfx(0)->mark_dirty(offset >> 4); } u16 taito_f3_state::pivot_r(offs_t offset) { return m_pivot_ram[offset]; } void taito_f3_state::pivot_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_pivot_ram[offset]); m_gfxdecode->gfx(1)->mark_dirty(offset >> 4); } u16 taito_f3_state::lineram_r(offs_t offset) { return m_line_ram[offset]; } void taito_f3_state::lineram_w(offs_t offset, u16 data, u16 mem_mask) { #ifdef UNUSED_FUNCTION /* DariusGX has an interesting bug at the start of Round D - the clearing of lineram (0xa000->0x0xa7ff) overflows into priority RAM (0xb000) and creates garbage priority values. I'm not sure what the real machine would do with these values, and this emulation certainly doesn't like it, so I've chosen to catch the bug here, and prevent the trashing of priority ram. If anyone has information on what the real machine does, please let me know! */ /* Update: this doesn't seem to occur anymore, I'll leave this snippet in but commented out. * fwiw PC=0x1768a0/0x1768a4 is where the game clears lineram in round D, which is a * move.w Dn, (An,D7.w*2) , a kind of opcode that could've been bugged back then. */ if (m_game == DARIUSG) { if (m_f3_skip_this_frame) return; if (offset == 0xb000 / 2 && data == 0x003f) { m_f3_skip_this_frame = 1; return; } } #endif COMBINE_DATA(&m_line_ram[offset]); } void taito_f3_state::palette_24bit_w(offs_t offset, u32 data, u32 mem_mask) { int r, g, b; COMBINE_DATA(&m_paletteram32[offset]); /* 12 bit palette games - there has to be a palette select bit somewhere */ if (m_game == SPCINVDX || m_game == RIDINGF || m_game == ARABIANM || m_game == RINGRAGE) { b = 15 * ((m_paletteram32[offset] >> 4) & 0xf); g = 15 * ((m_paletteram32[offset] >> 8) & 0xf); r = 15 * ((m_paletteram32[offset] >> 12) & 0xf); } /* This is weird - why are only the sprites and VRAM palettes 21 bit? */ else if (m_game == CLEOPATR) { if (offset < 0x100 || offset > 0x1000) { r = ((m_paletteram32[offset] >> 16) & 0x7f) << 1; g = ((m_paletteram32[offset] >> 8) & 0x7f) << 1; b = ((m_paletteram32[offset] >> 0) & 0x7f) << 1; } else { r = (m_paletteram32[offset] >> 16) & 0xff; g = (m_paletteram32[offset] >> 8) & 0xff; b = (m_paletteram32[offset] >> 0) & 0xff; } } /* Another weird couple - perhaps this is alpha blending related? */ else if (m_game == TWINQIX || m_game == RECALH) { if (offset > 0x1c00) { r = ((m_paletteram32[offset] >> 16) & 0x7f) << 1; g = ((m_paletteram32[offset] >> 8) & 0x7f) << 1; b = ((m_paletteram32[offset] >> 0) & 0x7f) << 1; } else { r = (m_paletteram32[offset] >> 16) & 0xff; g = (m_paletteram32[offset] >> 8) & 0xff; b = (m_paletteram32[offset] >> 0) & 0xff; } } /* All other games - standard 24 bit palette */ else { r = (m_paletteram32[offset] >> 16) & 0xff; g = (m_paletteram32[offset] >> 8) & 0xff; b = (m_paletteram32[offset] >> 0) & 0xff; } m_palette->set_pen_color(offset, rgb_t(r, g, b)); } /******************************************************************************/ /*============================================================================*/ #define SET_ALPHA_LEVEL(d, s) \ { \ int level = s; \ if (level == 0) level = -1; \ d = level + 1; \ } inline void taito_f3_state::alpha_set_level() { // SET_ALPHA_LEVEL(m_alpha_s_1_1, m_alpha_level_2ad) SET_ALPHA_LEVEL(m_alpha_s_1_1, 255 - m_alpha_level_2as) // SET_ALPHA_LEVEL(m_alpha_s_1_2, m_alpha_level_2bd) SET_ALPHA_LEVEL(m_alpha_s_1_2, 255 - m_alpha_level_2bs) SET_ALPHA_LEVEL(m_alpha_s_1_4, m_alpha_level_3ad) // SET_ALPHA_LEVEL(m_alpha_s_1_5, m_alpha_level_3ad*m_alpha_level_2ad / 255) SET_ALPHA_LEVEL(m_alpha_s_1_5, m_alpha_level_3ad * (255 - m_alpha_level_2as) / 255) // SET_ALPHA_LEVEL(m_alpha_s_1_6, m_alpha_level_3ad*m_alpha_level_2bd / 255) SET_ALPHA_LEVEL(m_alpha_s_1_6, m_alpha_level_3ad * (255 - m_alpha_level_2bs) / 255) SET_ALPHA_LEVEL(m_alpha_s_1_8, m_alpha_level_3bd) // SET_ALPHA_LEVEL(m_alpha_s_1_9, m_alpha_level_3bd*m_alpha_level_2ad / 255) SET_ALPHA_LEVEL(m_alpha_s_1_9, m_alpha_level_3bd * (255 - m_alpha_level_2as) / 255) // SET_ALPHA_LEVEL(m_alpha_s_1_a, m_alpha_level_3bd*m_alpha_level_2bd / 255) SET_ALPHA_LEVEL(m_alpha_s_1_a, m_alpha_level_3bd * (255 - m_alpha_level_2bs) / 255) SET_ALPHA_LEVEL(m_alpha_s_2a_0, m_alpha_level_2as) SET_ALPHA_LEVEL(m_alpha_s_2a_4, m_alpha_level_2as * m_alpha_level_3ad / 255) SET_ALPHA_LEVEL(m_alpha_s_2a_8, m_alpha_level_2as * m_alpha_level_3bd / 255) SET_ALPHA_LEVEL(m_alpha_s_2b_0, m_alpha_level_2bs) SET_ALPHA_LEVEL(m_alpha_s_2b_4, m_alpha_level_2bs * m_alpha_level_3ad / 255) SET_ALPHA_LEVEL(m_alpha_s_2b_8, m_alpha_level_2bs * m_alpha_level_3bd / 255) SET_ALPHA_LEVEL(m_alpha_s_3a_0, m_alpha_level_3as) SET_ALPHA_LEVEL(m_alpha_s_3a_1, m_alpha_level_3as * m_alpha_level_2ad / 255) SET_ALPHA_LEVEL(m_alpha_s_3a_2, m_alpha_level_3as * m_alpha_level_2bd / 255) SET_ALPHA_LEVEL(m_alpha_s_3b_0, m_alpha_level_3bs) SET_ALPHA_LEVEL(m_alpha_s_3b_1, m_alpha_level_3bs * m_alpha_level_2ad / 255) SET_ALPHA_LEVEL(m_alpha_s_3b_2, m_alpha_level_3bs * m_alpha_level_2bd / 255) } #undef SET_ALPHA_LEVEL /*============================================================================*/ #define COLOR1 BYTE4_XOR_LE(0) #define COLOR2 BYTE4_XOR_LE(1) #define COLOR3 BYTE4_XOR_LE(2) inline void taito_f3_state::alpha_blend32_s(int alphas, u32 s) { u8 *sc = (u8 *)&s; u8 *dc = (u8 *)&m_dval; dc[COLOR1] = (alphas * sc[COLOR1]) >> 8; dc[COLOR2] = (alphas * sc[COLOR2]) >> 8; dc[COLOR3] = (alphas * sc[COLOR3]) >> 8; } inline void taito_f3_state::alpha_blend32_d(int alphas, u32 s) { u8 *sc = (u8 *)&s; u8 *dc = (u8 *)&m_dval; dc[COLOR1] = m_add_sat[dc[COLOR1]][(alphas * sc[COLOR1]) >> 8]; dc[COLOR2] = m_add_sat[dc[COLOR2]][(alphas * sc[COLOR2]) >> 8]; dc[COLOR3] = m_add_sat[dc[COLOR3]][(alphas * sc[COLOR3]) >> 8]; } /*============================================================================*/ inline void taito_f3_state::alpha_blend_1_1(u32 s) { alpha_blend32_d(m_alpha_s_1_1, s); } inline void taito_f3_state::alpha_blend_1_2(u32 s) { alpha_blend32_d(m_alpha_s_1_2, s); } inline void taito_f3_state::alpha_blend_1_4(u32 s) { alpha_blend32_d(m_alpha_s_1_4, s); } inline void taito_f3_state::alpha_blend_1_5(u32 s) { alpha_blend32_d(m_alpha_s_1_5, s); } inline void taito_f3_state::alpha_blend_1_6(u32 s) { alpha_blend32_d(m_alpha_s_1_6, s); } inline void taito_f3_state::alpha_blend_1_8(u32 s) { alpha_blend32_d(m_alpha_s_1_8, s); } inline void taito_f3_state::alpha_blend_1_9(u32 s) { alpha_blend32_d(m_alpha_s_1_9, s); } inline void taito_f3_state::alpha_blend_1_a(u32 s) { alpha_blend32_d(m_alpha_s_1_a, s); } inline void taito_f3_state::alpha_blend_2a_0(u32 s) { alpha_blend32_s(m_alpha_s_2a_0, s); } inline void taito_f3_state::alpha_blend_2a_4(u32 s) { alpha_blend32_d(m_alpha_s_2a_4, s); } inline void taito_f3_state::alpha_blend_2a_8(u32 s) { alpha_blend32_d(m_alpha_s_2a_8, s); } inline void taito_f3_state::alpha_blend_2b_0(u32 s) { alpha_blend32_s(m_alpha_s_2b_0, s); } inline void taito_f3_state::alpha_blend_2b_4(u32 s) { alpha_blend32_d(m_alpha_s_2b_4, s); } inline void taito_f3_state::alpha_blend_2b_8(u32 s) { alpha_blend32_d(m_alpha_s_2b_8, s); } inline void taito_f3_state::alpha_blend_3a_0(u32 s) { alpha_blend32_s(m_alpha_s_3a_0, s); } inline void taito_f3_state::alpha_blend_3a_1(u32 s) { alpha_blend32_d(m_alpha_s_3a_1, s); } inline void taito_f3_state::alpha_blend_3a_2(u32 s) { alpha_blend32_d(m_alpha_s_3a_2, s); } inline void taito_f3_state::alpha_blend_3b_0(u32 s) { alpha_blend32_s(m_alpha_s_3b_0, s); } inline void taito_f3_state::alpha_blend_3b_1(u32 s) { alpha_blend32_d(m_alpha_s_3b_1, s); } inline void taito_f3_state::alpha_blend_3b_2(u32 s) { alpha_blend32_d(m_alpha_s_3b_2, s); } /*============================================================================*/ int taito_f3_state::dpix_1_noalpha(u32 s_pix) { m_dval = s_pix; return 1; } int taito_f3_state::dpix_ret1(u32 s_pix) { return 1; } int taito_f3_state::dpix_ret0(u32 s_pix) { return 0; } int taito_f3_state::dpix_1_1(u32 s_pix) { if (s_pix) alpha_blend_1_1(s_pix); return 1; } int taito_f3_state::dpix_1_2(u32 s_pix) { if (s_pix) alpha_blend_1_2(s_pix); return 1; } int taito_f3_state::dpix_1_4(u32 s_pix) { if (s_pix) alpha_blend_1_4(s_pix); return 1; } int taito_f3_state::dpix_1_5(u32 s_pix) { if (s_pix) alpha_blend_1_5(s_pix); return 1; } int taito_f3_state::dpix_1_6(u32 s_pix) { if (s_pix) alpha_blend_1_6(s_pix); return 1; } int taito_f3_state::dpix_1_8(u32 s_pix) { if (s_pix) alpha_blend_1_8(s_pix); return 1; } int taito_f3_state::dpix_1_9(u32 s_pix) { if (s_pix) alpha_blend_1_9(s_pix); return 1; } int taito_f3_state::dpix_1_a(u32 s_pix) { if (s_pix) alpha_blend_1_a(s_pix); return 1; } int taito_f3_state::dpix_2a_0(u32 s_pix) { if (s_pix) alpha_blend_2a_0(s_pix); else m_dval = 0; if (m_pdest_2a) { m_pval |= m_pdest_2a; return 0; } return 1; } int taito_f3_state::dpix_2a_4(u32 s_pix) { if (s_pix) alpha_blend_2a_4(s_pix); if (m_pdest_2a) { m_pval |= m_pdest_2a; return 0; } return 1; } int taito_f3_state::dpix_2a_8(u32 s_pix) { if (s_pix) alpha_blend_2a_8(s_pix); if (m_pdest_2a) { m_pval |= m_pdest_2a; return 0; } return 1; } int taito_f3_state::dpix_3a_0(u32 s_pix) { if (s_pix) alpha_blend_3a_0(s_pix); else m_dval = 0; if (m_pdest_3a) { m_pval |= m_pdest_3a; return 0; } return 1; } int taito_f3_state::dpix_3a_1(u32 s_pix) { if (s_pix) alpha_blend_3a_1(s_pix); if (m_pdest_3a) { m_pval |= m_pdest_3a; return 0; } return 1; } int taito_f3_state::dpix_3a_2(u32 s_pix) { if (s_pix) alpha_blend_3a_2(s_pix); if (m_pdest_3a) { m_pval |= m_pdest_3a; return 0; } return 1; } int taito_f3_state::dpix_2b_0(u32 s_pix) { if (s_pix) alpha_blend_2b_0(s_pix); else m_dval = 0; if (m_pdest_2b) { m_pval |= m_pdest_2b; return 0; } return 1; } int taito_f3_state::dpix_2b_4(u32 s_pix) { if (s_pix) alpha_blend_2b_4(s_pix); if (m_pdest_2b) { m_pval |= m_pdest_2b; return 0; } return 1; } int taito_f3_state::dpix_2b_8(u32 s_pix) { if (s_pix) alpha_blend_2b_8(s_pix); if (m_pdest_2b) { m_pval |= m_pdest_2b; return 0; } return 1; } int taito_f3_state::dpix_3b_0(u32 s_pix) { if (s_pix) alpha_blend_3b_0(s_pix); else m_dval = 0; if (m_pdest_3b) { m_pval |= m_pdest_3b; return 0; } return 1; } int taito_f3_state::dpix_3b_1(u32 s_pix) { if (s_pix) alpha_blend_3b_1(s_pix); if (m_pdest_3b) { m_pval |= m_pdest_3b; return 0; } return 1; } int taito_f3_state::dpix_3b_2(u32 s_pix) { if (s_pix) alpha_blend_3b_2(s_pix); if (m_pdest_3b) { m_pval |= m_pdest_3b; return 0; } return 1; } int taito_f3_state::dpix_2_0(u32 s_pix) { const u8 tr2 = m_tval & 1; if (s_pix) { if (tr2 == m_tr_2b) { alpha_blend_2b_0(s_pix); if (m_pdest_2b) m_pval |= m_pdest_2b; else return 1; } else if (tr2 == m_tr_2a) { alpha_blend_2a_0(s_pix); if (m_pdest_2a) m_pval |= m_pdest_2a; else return 1; } } else { if (tr2 == m_tr_2b) { m_dval = 0; if (m_pdest_2b) m_pval |= m_pdest_2b; else return 1; } else if (tr2 == m_tr_2a) { m_dval = 0; if (m_pdest_2a) m_pval |= m_pdest_2a; else return 1; } } return 0; } int taito_f3_state::dpix_2_4(u32 s_pix) { const u8 tr2 = m_tval & 1; if (s_pix) { if (tr2 == m_tr_2b) { alpha_blend_2b_4(s_pix); if (m_pdest_2b) m_pval |= m_pdest_2b; else return 1; } else if (tr2 == m_tr_2a) { alpha_blend_2a_4(s_pix); if (m_pdest_2a) m_pval |= m_pdest_2a; else return 1; } } else { if (tr2 == m_tr_2b) { if (m_pdest_2b) m_pval |= m_pdest_2b; else return 1; } else if (tr2 == m_tr_2a) { if (m_pdest_2a) m_pval |= m_pdest_2a; else return 1; } } return 0; } int taito_f3_state::dpix_2_8(u32 s_pix) { const u8 tr2 = m_tval & 1; if (s_pix) { if (tr2 == m_tr_2b) { alpha_blend_2b_8(s_pix); if (m_pdest_2b) m_pval |= m_pdest_2b; else return 1; } else if (tr2 == m_tr_2a) { alpha_blend_2a_8(s_pix); if (m_pdest_2a) m_pval |= m_pdest_2a; else return 1; } } else { if (tr2 == m_tr_2b) { if (m_pdest_2b) m_pval |= m_pdest_2b; else return 1; } else if (tr2 == m_tr_2a) { if (m_pdest_2a) m_pval |= m_pdest_2a; else return 1; } } return 0; } int taito_f3_state::dpix_3_0(u32 s_pix) { const u8 tr2 = m_tval & 1; if (s_pix) { if (tr2 == m_tr_3b) { alpha_blend_3b_0(s_pix); if (m_pdest_3b) m_pval |= m_pdest_3b; else return 1; } else if (tr2 == m_tr_3a) { alpha_blend_3a_0(s_pix); if (m_pdest_3a) m_pval |= m_pdest_3a; else return 1; } } else { if (tr2 == m_tr_3b) { m_dval = 0; if (m_pdest_3b) m_pval |= m_pdest_3b; else return 1; } else if (tr2 == m_tr_3a) { m_dval = 0; if (m_pdest_3a) m_pval |= m_pdest_3a; else return 1; } } return 0; } int taito_f3_state::dpix_3_1(u32 s_pix) { const u8 tr2 = m_tval & 1; if (s_pix) { if (tr2 == m_tr_3b) { alpha_blend_3b_1(s_pix); if (m_pdest_3b) m_pval |= m_pdest_3b; else return 1; } else if (tr2 == m_tr_3a) { alpha_blend_3a_1(s_pix); if (m_pdest_3a) m_pval |= m_pdest_3a; else return 1; } } else { if (tr2 == m_tr_3b) { if (m_pdest_3b) m_pval |= m_pdest_3b; else return 1; } else if (tr2 == m_tr_3a) { if (m_pdest_3a) m_pval |= m_pdest_3a; else return 1; } } return 0; } int taito_f3_state::dpix_3_2(u32 s_pix) { const u8 tr2 = m_tval & 1; if (s_pix) { if (tr2 == m_tr_3b) { alpha_blend_3b_2(s_pix); if (m_pdest_3b) m_pval |= m_pdest_3b; else return 1; } else if (tr2 == m_tr_3a) { alpha_blend_3a_2(s_pix); if (m_pdest_3a) m_pval |= m_pdest_3a; else return 1; } } else { if (tr2 == m_tr_3b) { if (m_pdest_3b) m_pval |= m_pdest_3b; else return 1; } else if (tr2 == m_tr_3a) { if (m_pdest_3a) m_pval |= m_pdest_3a; else return 1; } } return 0; } inline void taito_f3_state::dpix_1_sprite(u32 s_pix) { if (s_pix) { const u8 p1 = m_pval & 0xf0; if (p1 == 0x10) alpha_blend_1_1(s_pix); else if (p1 == 0x20) alpha_blend_1_2(s_pix); else if (p1 == 0x40) alpha_blend_1_4(s_pix); else if (p1 == 0x50) alpha_blend_1_5(s_pix); else if (p1 == 0x60) alpha_blend_1_6(s_pix); else if (p1 == 0x80) alpha_blend_1_8(s_pix); else if (p1 == 0x90) alpha_blend_1_9(s_pix); else if (p1 == 0xa0) alpha_blend_1_a(s_pix); } } inline void taito_f3_state::dpix_bg(u32 bgcolor) { const u8 p1 = m_pval & 0xf0; if (!p1) m_dval = bgcolor; else if (p1 == 0x10) alpha_blend_1_1(bgcolor); else if (p1 == 0x20) alpha_blend_1_2(bgcolor); else if (p1 == 0x40) alpha_blend_1_4(bgcolor); else if (p1 == 0x50) alpha_blend_1_5(bgcolor); else if (p1 == 0x60) alpha_blend_1_6(bgcolor); else if (p1 == 0x80) alpha_blend_1_8(bgcolor); else if (p1 == 0x90) alpha_blend_1_9(bgcolor); else if (p1 == 0xa0) alpha_blend_1_a(bgcolor); } /******************************************************************************/ void taito_f3_state::init_alpha_blend_func() { m_dpix_n[0][0x0] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0x1] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0x2] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0x3] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0x4] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0x5] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0x6] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0x7] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0x8] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0x9] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0xa] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0xb] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0xc] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0xd] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0xe] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[0][0xf] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[1][0x0] = &taito_f3_state::dpix_1_noalpha; m_dpix_n[1][0x1] = &taito_f3_state::dpix_1_1; m_dpix_n[1][0x2] = &taito_f3_state::dpix_1_2; m_dpix_n[1][0x3] = &taito_f3_state::dpix_ret1; m_dpix_n[1][0x4] = &taito_f3_state::dpix_1_4; m_dpix_n[1][0x5] = &taito_f3_state::dpix_1_5; m_dpix_n[1][0x6] = &taito_f3_state::dpix_1_6; m_dpix_n[1][0x7] = &taito_f3_state::dpix_ret1; m_dpix_n[1][0x8] = &taito_f3_state::dpix_1_8; m_dpix_n[1][0x9] = &taito_f3_state::dpix_1_9; m_dpix_n[1][0xa] = &taito_f3_state::dpix_1_a; m_dpix_n[1][0xb] = &taito_f3_state::dpix_ret1; m_dpix_n[1][0xc] = &taito_f3_state::dpix_ret1; m_dpix_n[1][0xd] = &taito_f3_state::dpix_ret1; m_dpix_n[1][0xe] = &taito_f3_state::dpix_ret1; m_dpix_n[1][0xf] = &taito_f3_state::dpix_ret1; m_dpix_n[2][0x0] = &taito_f3_state::dpix_2a_0; m_dpix_n[2][0x1] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0x2] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0x3] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0x4] = &taito_f3_state::dpix_2a_4; m_dpix_n[2][0x5] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0x6] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0x7] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0x8] = &taito_f3_state::dpix_2a_8; m_dpix_n[2][0x9] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0xa] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0xb] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0xc] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0xd] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0xe] = &taito_f3_state::dpix_ret0; m_dpix_n[2][0xf] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0x0] = &taito_f3_state::dpix_3a_0; m_dpix_n[3][0x1] = &taito_f3_state::dpix_3a_1; m_dpix_n[3][0x2] = &taito_f3_state::dpix_3a_2; m_dpix_n[3][0x3] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0x4] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0x5] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0x6] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0x7] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0x8] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0x9] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0xa] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0xb] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0xc] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0xd] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0xe] = &taito_f3_state::dpix_ret0; m_dpix_n[3][0xf] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0x0] = &taito_f3_state::dpix_2b_0; m_dpix_n[4][0x1] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0x2] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0x3] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0x4] = &taito_f3_state::dpix_2b_4; m_dpix_n[4][0x5] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0x6] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0x7] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0x8] = &taito_f3_state::dpix_2b_8; m_dpix_n[4][0x9] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0xa] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0xb] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0xc] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0xd] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0xe] = &taito_f3_state::dpix_ret0; m_dpix_n[4][0xf] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0x0] = &taito_f3_state::dpix_3b_0; m_dpix_n[5][0x1] = &taito_f3_state::dpix_3b_1; m_dpix_n[5][0x2] = &taito_f3_state::dpix_3b_2; m_dpix_n[5][0x3] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0x4] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0x5] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0x6] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0x7] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0x8] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0x9] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0xa] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0xb] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0xc] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0xd] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0xe] = &taito_f3_state::dpix_ret0; m_dpix_n[5][0xf] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0x0] = &taito_f3_state::dpix_2_0; m_dpix_n[6][0x1] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0x2] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0x3] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0x4] = &taito_f3_state::dpix_2_4; m_dpix_n[6][0x5] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0x6] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0x7] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0x8] = &taito_f3_state::dpix_2_8; m_dpix_n[6][0x9] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0xa] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0xb] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0xc] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0xd] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0xe] = &taito_f3_state::dpix_ret0; m_dpix_n[6][0xf] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0x0] = &taito_f3_state::dpix_3_0; m_dpix_n[7][0x1] = &taito_f3_state::dpix_3_1; m_dpix_n[7][0x2] = &taito_f3_state::dpix_3_2; m_dpix_n[7][0x3] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0x4] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0x5] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0x6] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0x7] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0x8] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0x9] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0xa] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0xb] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0xc] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0xd] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0xe] = &taito_f3_state::dpix_ret0; m_dpix_n[7][0xf] = &taito_f3_state::dpix_ret0; for (int i = 0; i < 256; i++) for (int j = 0; j < 256; j++) m_add_sat[i][j] = std::min(i + j, 255); } /******************************************************************************/ #define GET_PIXMAP_POINTER(pf_num) \ { \ const f3_playfield_line_inf *line_tmp = line_t[pf_num]; \ m_src[pf_num] = line_tmp->src[y]; \ m_src_s[pf_num] = line_tmp->src_s[y]; \ m_src_e[pf_num] = line_tmp->src_e[y]; \ m_tsrc[pf_num] = line_tmp->tsrc[y]; \ m_tsrc_s[pf_num] = line_tmp->tsrc_s[y]; \ m_x_count[pf_num] = line_tmp->x_count[y]; \ m_x_zoom[pf_num] = line_tmp->x_zoom[y]; \ m_clip_al[pf_num] = line_tmp->clip0[y] & 0xffff; \ m_clip_ar[pf_num] = line_tmp->clip0[y] >> 16; \ m_clip_bl[pf_num] = line_tmp->clip1[y] & 0xffff; \ m_clip_br[pf_num] = line_tmp->clip1[y] >> 16; \ } #define CULC_PIXMAP_POINTER(pf_num) \ { \ m_x_count[pf_num] += m_x_zoom[pf_num]; \ if (m_x_count[pf_num] >> 16) \ { \ m_x_count[pf_num] &= 0xffff; \ m_src[pf_num]++; \ m_tsrc[pf_num]++; \ if (m_src[pf_num] == m_src_e[pf_num]) { m_src[pf_num] = m_src_s[pf_num]; m_tsrc[pf_num] = m_tsrc_s[pf_num]; } \ } \ } #define UPDATE_PIXMAP_SP(pf_num) \ if (cx >= clip_als && cx < clip_ars && !(cx >= clip_bls && cx < clip_brs)) \ { \ sprite_pri = sprite[pf_num] & m_pval; \ if (sprite_pri) \ { \ if (sprite[pf_num] & 0x100) break; \ if (!m_dpix_sp[sprite_pri]) \ { \ if (!(m_pval & 0xf0)) break; \ else { dpix_1_sprite(*dsti); *dsti = m_dval; break; } \ } \ if ((this->*m_dpix_sp[sprite_pri][m_pval >> 4])(*dsti)) { *dsti = m_dval; break; } \ } \ } #define UPDATE_PIXMAP_LP(pf_num) \ if (cx >= m_clip_al[pf_num] && cx < m_clip_ar[pf_num] && !(cx >= m_clip_bl[pf_num] && cx < m_clip_br[pf_num])) \ { \ m_tval = *m_tsrc[pf_num]; \ if (m_tval & 0xf0) \ if ((this->*m_dpix_lp[pf_num][m_pval >> 4])(clut[*m_src[pf_num]])) { *dsti = m_dval; break; } \ } /*============================================================================*/ inline void taito_f3_state::draw_scanlines( bitmap_rgb32 &bitmap, int xsize, s16 *draw_line_num, const f3_playfield_line_inf **line_t, const int *sprite, u32 orient, int skip_layer_num) { const pen_t *clut = &m_palette->pen(0); const u32 bgcolor = clut[0]; const int x = 46; u16 clip_als = 0, clip_ars = 0, clip_bls = 0, clip_brs = 0; int yadv = bitmap.rowpixels(); int yadvp = m_pri_alp_bitmap.rowpixels(); int i = 0, y = draw_line_num[0]; int ty = y; if (orient & ORIENTATION_FLIP_Y) { ty = bitmap.height() - 1 - ty; yadv = -yadv; yadvp = -yadvp; } u8 *dstp0 = &m_pri_alp_bitmap.pix8(ty, x); m_pdest_2a = m_alpha_level_2ad ? 0x10 : 0; m_pdest_2b = m_alpha_level_2bd ? 0x20 : 0; m_tr_2a =(m_alpha_level_2as == 0 && m_alpha_level_2ad == 255) ? -1 : 0; m_tr_2b =(m_alpha_level_2bs == 0 && m_alpha_level_2bd == 255) ? -1 : 1; m_pdest_3a = m_alpha_level_3ad ? 0x40 : 0; m_pdest_3b = m_alpha_level_3bd ? 0x80 : 0; m_tr_3a =(m_alpha_level_3as == 0 && m_alpha_level_3ad == 255) ? -1 : 0; m_tr_3b =(m_alpha_level_3bs == 0 && m_alpha_level_3bd == 255) ? -1 : 1; { u32 *dsti0 = &bitmap.pix32(ty, x); while (1) { int cx = 0; clip_als = m_sa_line_inf[0].sprite_clip0[y] & 0xffff; clip_ars = m_sa_line_inf[0].sprite_clip0[y] >> 16; clip_bls = m_sa_line_inf[0].sprite_clip1[y] & 0xffff; clip_brs = m_sa_line_inf[0].sprite_clip1[y] >> 16; int length = xsize; u32 *dsti = dsti0; u8 *dstp = dstp0; switch (skip_layer_num) { case 0: GET_PIXMAP_POINTER(0) case 1: GET_PIXMAP_POINTER(1) case 2: GET_PIXMAP_POINTER(2) case 3: GET_PIXMAP_POINTER(3) case 4: GET_PIXMAP_POINTER(4) } while (1) { m_pval = *dstp; if (m_pval != 0xff) { u8 sprite_pri; switch (skip_layer_num) { case 0: UPDATE_PIXMAP_SP(0) UPDATE_PIXMAP_LP(0) case 1: UPDATE_PIXMAP_SP(1) UPDATE_PIXMAP_LP(1) case 2: UPDATE_PIXMAP_SP(2) UPDATE_PIXMAP_LP(2) case 3: UPDATE_PIXMAP_SP(3) UPDATE_PIXMAP_LP(3) case 4: UPDATE_PIXMAP_SP(4) UPDATE_PIXMAP_LP(4) case 5: UPDATE_PIXMAP_SP(5) if (!bgcolor) { if (!(m_pval & 0xf0)) { *dsti = 0; break; } } else dpix_bg(bgcolor); *dsti = m_dval; } } if (!(--length)) break; dsti++; dstp++; cx++; switch (skip_layer_num) { case 0: CULC_PIXMAP_POINTER(0) case 1: CULC_PIXMAP_POINTER(1) case 2: CULC_PIXMAP_POINTER(2) case 3: CULC_PIXMAP_POINTER(3) case 4: CULC_PIXMAP_POINTER(4) } } i++; if (draw_line_num[i] < 0) break; if (draw_line_num[i] == y + 1) { dsti0 += yadv; dstp0 += yadvp; y++; continue; } else { dsti0 += (draw_line_num[i] - y) * yadv; dstp0 += (draw_line_num[i] - y) * yadvp; y = draw_line_num[i]; } } } } #undef GET_PIXMAP_POINTER #undef CULC_PIXMAP_POINTER /******************************************************************************/ void taito_f3_state::visible_tile_check( f3_playfield_line_inf *line_t, int line, u32 x_index_fx,u32 y_index, u16 *pf_data_n) { u16 *pf_base; int alpha_mode = line_t->alpha_mode[line]; if (!alpha_mode) return; const u32 total_elements = m_gfxdecode->gfx(3)->elements(); int tile_index = x_index_fx >> 16; const int tile_num = (((line_t->x_zoom[line] * 320 + (x_index_fx & 0xffff) + 0xffff) >> 16) + (tile_index & 0xf) + 15) >> 4; tile_index >>= 4; if (m_flipscreen) { pf_base = pf_data_n + ((31 - (y_index >> 4)) << m_twidth_mask_bit); tile_index = (m_twidth_mask - tile_index) - tile_num + 1; } else pf_base = pf_data_n + ((y_index >> 4) << m_twidth_mask_bit); bool trans_all = true; bool opaque_all = true; int alpha_type = 0; for (int i = 0; i < tile_num; i++) { const u32 tile = (pf_base[(tile_index * 2 + 0) & m_twidth_mask] << 16) | (pf_base[(tile_index * 2 + 1) & m_twidth_mask]); const u8 extra_planes = (tile >> (16 + 10)) & 3; if (tile & 0xffff) { trans_all = false; if (opaque_all) { if (m_tile_opaque_pf[extra_planes][(tile & 0xffff) % total_elements] != 1) opaque_all = false; } if (alpha_mode == 1) { if (!opaque_all) return; } else { if (alpha_type != 3) { if ((tile >> (16 + 9)) & 1) alpha_type |= 2; else alpha_type |= 1; } else if (!opaque_all) break; } } else if (opaque_all) opaque_all = false; tile_index++; } if (trans_all) { line_t->alpha_mode[line] = 0; return; } if (alpha_mode > 1) { line_t->alpha_mode[line] |= alpha_type << 4; } if (opaque_all) line_t->alpha_mode[line] |= 0x80; } /******************************************************************************/ void taito_f3_state::calculate_clip(int y, u16 pri, u32 *clip0, u32 *clip1, int *line_enable) { const f3_spritealpha_line_inf *sa_line_t = &m_sa_line_inf[0]; switch (pri) { case 0x0100: /* Clip plane 1 enable */ { if (sa_line_t->clip0_l[y] > sa_line_t->clip0_r[y]) *line_enable = 0; else *clip0 = (sa_line_t->clip0_l[y]) | (sa_line_t->clip0_r[y] << 16); *clip1 = 0; } break; case 0x0110: /* Clip plane 1 enable, inverted */ { *clip1 = (sa_line_t->clip0_l[y]) | (sa_line_t->clip0_r[y] << 16); *clip0 = 0x7fff0000; } break; case 0x0200: /* Clip plane 2 enable */ { if (sa_line_t->clip1_l[y] > sa_line_t->clip1_r[y]) *line_enable = 0; else *clip0 = (sa_line_t->clip1_l[y]) | (sa_line_t->clip1_r[y] << 16); *clip1 = 0; } break; case 0x0220: /* Clip plane 2 enable, inverted */ { *clip1 = (sa_line_t->clip1_l[y]) | (sa_line_t->clip1_r[y] << 16); *clip0 = 0x7fff0000; } break; case 0x0300: /* Clip plane 1 & 2 enable */ { int clipl = 0, clipr = 0; if (sa_line_t->clip1_l[y] > sa_line_t->clip0_l[y]) clipl = sa_line_t->clip1_l[y]; else clipl = sa_line_t->clip0_l[y]; if (sa_line_t->clip1_r[y] < sa_line_t->clip0_r[y]) clipr = sa_line_t->clip1_r[y]; else clipr = sa_line_t->clip0_r[y]; if (clipl > clipr) *line_enable = 0; else *clip0 = (clipl) | (clipr << 16); *clip1 = 0; } break; case 0x0310: /* Clip plane 1 & 2 enable, plane 1 inverted */ { if (sa_line_t->clip1_l[y] > sa_line_t->clip1_r[y]) line_enable = nullptr; else *clip0 = (sa_line_t->clip1_l[y]) | (sa_line_t->clip1_r[y] << 16); *clip1 = (sa_line_t->clip0_l[y]) | (sa_line_t->clip0_r[y] << 16); } break; case 0x0320: /* Clip plane 1 & 2 enable, plane 2 inverted */ { if (sa_line_t->clip0_l[y] > sa_line_t->clip0_r[y]) line_enable = nullptr; else *clip0 = (sa_line_t->clip0_l[y]) | (sa_line_t->clip0_r[y] << 16); *clip1 = (sa_line_t->clip1_l[y]) | (sa_line_t->clip1_r[y] << 16); } break; case 0x0330: /* Clip plane 1 & 2 enable, both inverted */ { int clipl = 0, clipr = 0; if (sa_line_t->clip1_l[y] < sa_line_t->clip0_l[y]) clipl = sa_line_t->clip1_l[y]; else clipl = sa_line_t->clip0_l[y]; if (sa_line_t->clip1_r[y] > sa_line_t->clip0_r[y]) clipr = sa_line_t->clip1_r[y]; else clipr = sa_line_t->clip0_r[y]; if (clipl > clipr) *line_enable = 0; else *clip1 = (clipl) | (clipr << 16); *clip0 = 0x7fff0000; } break; default: // popmessage("Illegal clip mode"); break; } } void taito_f3_state::get_spritealphaclip_info() { f3_spritealpha_line_inf *line_t = &m_sa_line_inf[0]; int y, y_end, y_inc; int spri_base, clip_base_low, clip_base_high, inc; u16 spri = 0; u16 sprite_clip = 0; u16 clip0_low = 0, clip0_high = 0, clip1_low = 0; int alpha_level = 0; u16 sprite_alpha = 0; if (m_flipscreen) { spri_base = 0x77fe; clip_base_low = 0x51fe; clip_base_high = 0x45fe; inc = -2; y = 255; y_end = -1; y_inc = -1; } else { spri_base = 0x7600; clip_base_low = 0x5000; clip_base_high = 0x4400; inc = 2; y = 0; y_end = 256; y_inc = 1; } while (y != y_end) { /* The zoom, column and row values can latch according to control ram */ { if (m_line_ram[0x100 + y] & 1) clip0_low = (m_line_ram[clip_base_low / 2] >> 0) & 0xffff; if (m_line_ram[0x000 + y] & 4) clip0_high = (m_line_ram[clip_base_high / 2] >> 0) & 0xffff; if (m_line_ram[0x100 + y] & 2) clip1_low = (m_line_ram[(clip_base_low + 0x200) / 2] >> 0) & 0xffff; if (m_line_ram[(0x0600 / 2) + y] & 0x8) spri = m_line_ram[spri_base / 2] & 0xffff; if (m_line_ram[(0x0600 / 2) + y] & 0x4) sprite_clip = m_line_ram[(spri_base-0x200) / 2] & 0xffff; if (m_line_ram[(0x0400 / 2) + y] & 0x1) sprite_alpha = m_line_ram[(spri_base-0x1600) / 2] & 0xffff; if (m_line_ram[(0x0400 / 2) + y] & 0x2) alpha_level = m_line_ram[(spri_base-0x1400) / 2] & 0xffff; } line_t->alpha_level[y] = alpha_level; line_t->spri[y] = spri; line_t->sprite_alpha[y] = sprite_alpha; line_t->clip0_l[y] = ((clip0_low & 0xff) | ((clip0_high & 0x1000) >> 4)) - 47; line_t->clip0_r[y] = (((clip0_low & 0xff00) >> 8) | ((clip0_high & 0x2000) >> 5)) - 47; line_t->clip1_l[y] = ((clip1_low & 0xff) | ((clip0_high & 0x4000) >> 6)) - 47; line_t->clip1_r[y] = (((clip1_low & 0xff00) >> 8) | ((clip0_high & 0x8000) >> 7)) - 47; if (line_t->clip0_l[y] < 0) line_t->clip0_l[y] = 0; if (line_t->clip0_r[y] < 0) line_t->clip0_r[y] = 0; if (line_t->clip1_l[y] < 0) line_t->clip1_l[y] = 0; if (line_t->clip1_r[y] < 0) line_t->clip1_r[y] = 0; /* Evaluate sprite clipping */ if (sprite_clip & 0x080) { line_t->sprite_clip0[y] = 0x7fff7fff; line_t->sprite_clip1[y] = 0; } else if (sprite_clip & 0x33) { int line_enable = 1; calculate_clip(y, ((sprite_clip & 0x33) << 4), &line_t->sprite_clip0[y], &line_t->sprite_clip1[y], &line_enable); if (line_enable == 0) line_t->sprite_clip0[y] = 0x7fff7fff; } else { line_t->sprite_clip0[y] = 0x7fff0000; line_t->sprite_clip1[y] = 0; } spri_base += inc; clip_base_low += inc; clip_base_high += inc; y += y_inc; } } /* sx and sy are 16.16 fixed point numbers */ void taito_f3_state::get_line_ram_info(tilemap_t *tmap, int sx, int sy, int pos, u16 *pf_data_n) { f3_playfield_line_inf *line_t = &m_pf_line_inf[pos]; int y_start, y_end, y_inc; int line_base, zoom_base, col_base, pri_base, inc; int line_enable; int colscroll = 0, x_offset = 0, line_zoom = 0; u32 _y_zoom[256]; u16 pri = 0; int bit_select = 1 << pos; int _colscroll[256]; u32 _x_offset[256]; int y_index_fx; sx += ((46 << 16)); if (m_flipscreen) { line_base = 0xa1fe + (pos << 9); zoom_base = 0x81fe;// + (pos << 9); col_base = 0x41fe + (pos << 9); pri_base = 0xb1fe + (pos << 9); inc = -2; y_start = 255; y_end = -1; y_inc = -1; /* Adjust for flipped scroll position */ if (m_game_config->extend) sx = -sx + (((188 - 512) & 0xffff) << 16); else sx = -sx + (188 << 16); y_index_fx = -sy - (256 << 16); /* Adjust for flipped scroll position */ } else { line_base = 0xa000 + (pos << 9); zoom_base = 0x8000;// + (pos << 9); col_base = 0x4000 + (pos << 9); pri_base = 0xb000 + (pos << 9); inc = 2; y_start = 0; y_end = 256; y_inc = 1; y_index_fx = sy; } int y = y_start; while (y != y_end) { /* The zoom, column and row values can latch according to control ram */ { if (m_line_ram[0x600 + y] & bit_select) x_offset = (m_line_ram[line_base / 2] & 0xffff) << 10; if (m_line_ram[0x700 + y] & bit_select) pri = m_line_ram[pri_base / 2] & 0xffff; // Zoom for playfields 1 & 3 is interleaved, as is the latch select switch (pos) { case 0: if (m_line_ram[0x400 + y] & bit_select) line_zoom = m_line_ram[(zoom_base + 0x000) / 2] & 0xffff; break; case 1: if (m_line_ram[0x400 + y] & 0x2) line_zoom = ((m_line_ram[(zoom_base + 0x200) / 2] & 0xffff) & 0xff00) | (line_zoom & 0x00ff); if (m_line_ram[0x400 + y] & 0x8) line_zoom = ((m_line_ram[(zoom_base + 0x600) / 2] & 0xffff) & 0x00ff) | (line_zoom & 0xff00); break; case 2: if (m_line_ram[0x400 + y] & bit_select) line_zoom = m_line_ram[(zoom_base + 0x400) / 2] & 0xffff; break; case 3: if (m_line_ram[0x400 + y] & 0x8) line_zoom = ((m_line_ram[(zoom_base + 0x600) / 2] & 0xffff) & 0xff00) | (line_zoom & 0x00ff); if (m_line_ram[0x400 + y] & 0x2) line_zoom = ((m_line_ram[(zoom_base + 0x200) / 2] & 0xffff) & 0x00ff) | (line_zoom & 0xff00); break; default: break; } // Column scroll only affects playfields 2 & 3 if (pos >= 2 && m_line_ram[0x000 + y] & bit_select) colscroll = (m_line_ram[col_base / 2] >> 0) & 0x3ff; } if (!pri || (!m_flipscreen && y < 24) || (m_flipscreen && y > 231) || (pri & 0xc000) == 0xc000 || !(pri & 0x2000)/**/) line_enable = 0; else if (pri & 0x4000) //alpha1 line_enable = 2; else if (pri & 0x8000) //alpha2 line_enable = 3; else line_enable = 1; _colscroll[y] = colscroll; _x_offset[y] = (x_offset & 0xffff0000) - (x_offset & 0x0000ffff); _y_zoom[y] = (line_zoom & 0xff) << 9; /* Evaluate clipping */ if (pri & 0x0800) line_enable = 0; else if (pri & 0x0330) { //fast path todo - remove line enable calculate_clip(y, pri & 0x0330, &line_t->clip0[y], &line_t->clip1[y], &line_enable); } else { /* No clipping */ line_t->clip0[y] = 0x7fff0000; line_t->clip1[y] = 0; } line_t->x_zoom[y] = 0x10000 - (line_zoom & 0xff00); line_t->alpha_mode[y] = line_enable; line_t->pri[y] = pri; zoom_base += inc; line_base += inc; col_base += inc; pri_base += inc; y += y_inc; } tilemap_t* tm = tmap; y = y_start; while (y != y_end) { u32 x_index_fx; u32 y_index; /* The football games use values in the range 0x200 - 0x3ff where the crowd should be drawn - !? This appears to cause it to reference outside of the normal tilemap RAM area into the unused area on the 32x32 tilemap configuration.. but exactly how isn't understood Until this is understood we're creating additional tilemaps for the otherwise unused area of RAM and forcing it to look up there. the crowd area still seems to 'lag' behind the pitch area however.. but these are the values in ram?? */ const int cs = _colscroll[y]; if (cs & 0x200) { if (m_tilemap[4] && m_tilemap[5]) { if (tmap == m_tilemap[2]) tmap = m_tilemap[4]; // pitch -> crowd if (tmap == m_tilemap[3]) tmap = m_tilemap[5]; // corruption on goals -> blank (hthero94) } } else { tmap = tm; } /* set pixmap pointer */ bitmap_ind16 &srcbitmap = tmap->pixmap(); bitmap_ind8 &flagsbitmap = tmap->flagsmap(); if (line_t->alpha_mode[y] != 0) { u16 *src_s; u8 *tsrc_s; x_index_fx = (sx+_x_offset[y]-(10*0x10000) + (10*line_t->x_zoom[y]))&((m_width_mask << 16)|0xffff); y_index = ((y_index_fx >> 16)+_colscroll[y]) & 0x1ff; /* check tile status */ visible_tile_check(line_t, y, x_index_fx, y_index, pf_data_n); /* If clipping enabled for this line have to disable 'all opaque' optimisation */ if (line_t->clip0[y] != 0x7fff0000 || line_t->clip1[y] != 0) line_t->alpha_mode[y] &= ~0x80; /* set pixmap index */ line_t->x_count[y]=x_index_fx & 0xffff; // Fractional part line_t->src_s[y] = src_s = &srcbitmap.pix16(y_index); line_t->src_e[y] = &src_s[m_width_mask + 1]; line_t->src[y] = &src_s[x_index_fx >> 16]; line_t->tsrc_s[y]=tsrc_s = &flagsbitmap.pix8(y_index); line_t->tsrc[y] = &tsrc_s[x_index_fx >> 16]; } y_index_fx += _y_zoom[y]; y += y_inc; } } void taito_f3_state::get_vram_info(tilemap_t *vram_tilemap, tilemap_t *pixel_tilemap, int sx, int sy) { const f3_spritealpha_line_inf *sprite_alpha_line_t = &m_sa_line_inf[0]; f3_playfield_line_inf *line_t = &m_pf_line_inf[4]; int y_start, y_end, y_inc; int pri_base, inc; int line_enable; u16 pri = 0; const int vram_width_mask = 0x3ff; if (m_flipscreen) { pri_base = 0x73fe; inc = -2; y_start = 255; y_end = -1; y_inc = -1; } else { pri_base = 0x7200; inc = 2; y_start = 0; y_end = 256; y_inc = 1; } int y = y_start; while (y != y_end) { /* The zoom, column and row values can latch according to control ram */ { if (m_line_ram[(0x0600 / 2) + y] & 0x2) pri = (m_line_ram[pri_base / 2] & 0xffff); } if (!pri || (!m_flipscreen && y < 24) || (m_flipscreen && y > 231) || (pri & 0xc000) == 0xc000 || !(pri & 0x2000)/**/) line_enable = 0; else if (pri & 0x4000) //alpha1 line_enable = 2; else if (pri & 0x8000) //alpha2 line_enable = 3; else line_enable = 1; line_t->pri[y] = pri; /* Evaluate clipping */ if (pri & 0x0800) line_enable = 0; else if (pri & 0x0330) { //fast path todo - remove line enable calculate_clip(y, pri & 0x0330, &line_t->clip0[y], &line_t->clip1[y], &line_enable); } else { /* No clipping */ line_t->clip0[y] = 0x7fff0000; line_t->clip1[y] = 0; } line_t->x_zoom[y] = 0x10000; line_t->alpha_mode[y] = line_enable; if (line_t->alpha_mode[y] > 1) line_t->alpha_mode[y] |= 0x10; pri_base += inc; y += y_inc; } sx &= 0x1ff; /* set pixmap pointer */ bitmap_ind16 &srcbitmap_pixel = pixel_tilemap->pixmap(); bitmap_ind8 &flagsbitmap_pixel = pixel_tilemap->flagsmap(); bitmap_ind16 &srcbitmap_vram = vram_tilemap->pixmap(); bitmap_ind8 &flagsbitmap_vram = vram_tilemap->flagsmap(); y = y_start; while (y != y_end) { if (line_t->alpha_mode[y] != 0) { u16 *src_s; u8 *tsrc_s; // These bits in control ram indicate whether the line is taken from // the VRAM tilemap layer or pixel layer. const bool usePixelLayer = ((sprite_alpha_line_t->sprite_alpha[y] & 0xa000) == 0xa000); /* set pixmap index */ line_t->x_count[y] = 0xffff; if (usePixelLayer) line_t->src_s[y] = src_s = &srcbitmap_pixel.pix16(sy & 0xff); else line_t->src_s[y] = src_s = &srcbitmap_vram.pix16(sy & 0x1ff); line_t->src_e[y] = &src_s[vram_width_mask + 1]; line_t->src[y] = &src_s[sx]; if (usePixelLayer) line_t->tsrc_s[y]=tsrc_s = &flagsbitmap_pixel.pix8(sy & 0xff); else line_t->tsrc_s[y]=tsrc_s = &flagsbitmap_vram.pix8(sy & 0x1ff); line_t->tsrc[y] = &tsrc_s[sx]; } sy++; y += y_inc; } } /******************************************************************************/ void taito_f3_state::scanline_draw(bitmap_rgb32 &bitmap, const rectangle &cliprect) { int i, ys, ye; int y_start, y_end, y_start_next, y_end_next; u8 draw_line[256]; s16 draw_line_num[256]; u32 rot = 0; if (m_flipscreen) { rot = ORIENTATION_FLIP_Y; ys = 0; ye = 232; } else { ys = 24; ye = 256; } y_start = ys; y_end = ye; std::fill(std::begin(draw_line), std::end(draw_line), 0); while (1) { int pos; int pri[5],alpha_mode[5],alpha_mode_flag[5]; u8 sprite_alpha_check; u8 sprite_alpha_all_2a; int layer_tmp[5]; f3_playfield_line_inf *pf_line_inf = m_pf_line_inf.get(); f3_spritealpha_line_inf *sa_line_inf = m_sa_line_inf.get(); int count_skip_layer = 0; int sprite[6] = {0, 0, 0, 0, 0, 0}; const f3_playfield_line_inf *line_t[5]; /* find same status of scanlines */ pri[0] = pf_line_inf[0].pri[y_start]; pri[1] = pf_line_inf[1].pri[y_start]; pri[2] = pf_line_inf[2].pri[y_start]; pri[3] = pf_line_inf[3].pri[y_start]; pri[4] = pf_line_inf[4].pri[y_start]; alpha_mode[0] = pf_line_inf[0].alpha_mode[y_start]; alpha_mode[1] = pf_line_inf[1].alpha_mode[y_start]; alpha_mode[2] = pf_line_inf[2].alpha_mode[y_start]; alpha_mode[3] = pf_line_inf[3].alpha_mode[y_start]; alpha_mode[4] = pf_line_inf[4].alpha_mode[y_start]; const int alpha_level = sa_line_inf[0].alpha_level[y_start]; const int spri = sa_line_inf[0].spri[y_start]; const u16 sprite_alpha = sa_line_inf[0].sprite_alpha[y_start]; draw_line[y_start] = 1; draw_line_num[i = 0] = y_start; y_start_next = -1; y_end_next = -1; for (int y = y_start + 1; y < y_end; y++) { if (!draw_line[y]) { if (pri[0] != pf_line_inf[0].pri[y]) y_end_next = y + 1; else if (pri[1] != pf_line_inf[1].pri[y]) y_end_next = y + 1; else if (pri[2] != pf_line_inf[2].pri[y]) y_end_next = y + 1; else if (pri[3] != pf_line_inf[3].pri[y]) y_end_next = y + 1; else if (pri[4] != pf_line_inf[4].pri[y]) y_end_next = y + 1; else if (alpha_mode[0] != pf_line_inf[0].alpha_mode[y]) y_end_next = y + 1; else if (alpha_mode[1] != pf_line_inf[1].alpha_mode[y]) y_end_next = y + 1; else if (alpha_mode[2] != pf_line_inf[2].alpha_mode[y]) y_end_next = y + 1; else if (alpha_mode[3] != pf_line_inf[3].alpha_mode[y]) y_end_next = y + 1; else if (alpha_mode[4] != pf_line_inf[4].alpha_mode[y]) y_end_next = y + 1; else if (alpha_level!=sa_line_inf[0].alpha_level[y]) y_end_next = y + 1; else if (spri!=sa_line_inf[0].spri[y]) y_end_next = y + 1; else if (sprite_alpha!=sa_line_inf[0].sprite_alpha[y]) y_end_next = y + 1; else { draw_line[y] = 1; draw_line_num[++i] = y; continue; } if (y_start_next < 0) y_start_next = y; } } y_end = y_end_next; y_start = y_start_next; draw_line_num[++i] = -1; /* alpha blend */ alpha_mode_flag[0] = alpha_mode[0] & ~3; alpha_mode_flag[1] = alpha_mode[1] & ~3; alpha_mode_flag[2] = alpha_mode[2] & ~3; alpha_mode_flag[3] = alpha_mode[3] & ~3; alpha_mode_flag[4] = alpha_mode[4] & ~3; alpha_mode[0] &= 3; alpha_mode[1] &= 3; alpha_mode[2] &= 3; alpha_mode[3] &= 3; alpha_mode[4] &= 3; if (alpha_mode[0] > 1 || alpha_mode[1] > 1 || alpha_mode[2] > 1 || alpha_mode[3] > 1 || alpha_mode[4] > 1 || (sprite_alpha & 0xff) != 0xff) { /* set alpha level */ if (alpha_level != m_alpha_level_last) { int a = alpha_level; const int b = (a >> 8) & 0xf; const int c = (a >> 4) & 0xf; const int d = (a >> 0) & 0xf; a >>= 12; /* b000 7000 */ int al_s = std::min(255, ((15 - d) * 256) / 8); int al_d = std::min(255, ((15 - b) * 256) / 8); m_alpha_level_3as = al_s; m_alpha_level_3ad = al_d; m_alpha_level_2as = al_d; m_alpha_level_2ad = al_s; al_s = std::min(255, ((15 - c) * 256) / 8); al_d = std::min(255, ((15 - a) * 256) / 8); m_alpha_level_3bs = al_s; m_alpha_level_3bd = al_d; m_alpha_level_2bs = al_d; m_alpha_level_2bd = al_s; alpha_set_level(); m_alpha_level_last = alpha_level; } /* set sprite alpha mode */ sprite_alpha_check = 0; sprite_alpha_all_2a=1; m_dpix_sp[1] = nullptr; m_dpix_sp[2] = nullptr; m_dpix_sp[4] = nullptr; m_dpix_sp[8] = nullptr; for (i = 0; i < 4; i++) /* i = sprite priority offset */ { const u8 sprite_alpha_mode = (sprite_alpha >> (i * 2)) & 3; const u8 sftbit = 1 << i; if (m_sprite_pri_usage & sftbit) { if (sprite_alpha_mode == 1) { if (m_alpha_level_2as == 0 && m_alpha_level_2ad == 255) m_sprite_pri_usage &= ~sftbit; // Disable sprite priority block else { m_dpix_sp[sftbit] = m_dpix_n[2]; sprite_alpha_check |= sftbit; } } else if (sprite_alpha_mode == 2) { if (sprite_alpha & 0xff00) { if (m_alpha_level_3as == 0 && m_alpha_level_3ad == 255) m_sprite_pri_usage &= ~sftbit; else { m_dpix_sp[sftbit] = m_dpix_n[3]; sprite_alpha_check |= sftbit; sprite_alpha_all_2a = 0; } } else { if (m_alpha_level_3bs == 0 && m_alpha_level_3bd == 255) m_sprite_pri_usage &= ~sftbit; else { m_dpix_sp[sftbit] = m_dpix_n[5]; sprite_alpha_check |= sftbit; sprite_alpha_all_2a = 0; } } } } } /* check alpha level */ for (i = 0; i < 5; i++) /* i = playfield num (pos) */ { const int alpha_type = (alpha_mode_flag[i] >> 4) & 3; if (alpha_mode[i] == 2) { if (alpha_type == 1) { /* if (m_alpha_level_2as == 0 && m_alpha_level_2ad == 255) * alpha_mode[i]=3; alpha_mode_flag[i] |= 0x80; } * will display continue screen in gseeker (mt 00026) */ if (m_alpha_level_2as == 0 && m_alpha_level_2ad == 255) alpha_mode[i] = 0; else if (m_alpha_level_2as == 255 && m_alpha_level_2ad == 0) alpha_mode[i] = 1; } else if (alpha_type == 2) { if (m_alpha_level_2bs == 0 && m_alpha_level_2bd == 255) alpha_mode[i] = 0; else if (m_alpha_level_2as == 255 && m_alpha_level_2ad == 0 && m_alpha_level_2bs == 255 && m_alpha_level_2bd == 0) alpha_mode[i] = 1; } else if (alpha_type == 3) { if (m_alpha_level_2as == 0 && m_alpha_level_2ad == 255 && m_alpha_level_2bs == 0 && m_alpha_level_2bd == 255) alpha_mode[i] = 0; else if (m_alpha_level_2as == 255 && m_alpha_level_2ad == 0 && m_alpha_level_2bs == 255 && m_alpha_level_2bd == 0) alpha_mode[i] = 1; } } else if (alpha_mode[i] == 3) { if (alpha_type == 1) { if (m_alpha_level_3as == 0 && m_alpha_level_3ad == 255) alpha_mode[i] = 0; else if (m_alpha_level_3as == 255 && m_alpha_level_3ad == 0) alpha_mode[i] = 1; } else if (alpha_type == 2) { if (m_alpha_level_3bs == 0 && m_alpha_level_3bd == 255) alpha_mode[i] = 0; else if (m_alpha_level_3as == 255 && m_alpha_level_3ad == 0 && m_alpha_level_3bs == 255 && m_alpha_level_3bd == 0) alpha_mode[i] = 1; } else if (alpha_type == 3) { if (m_alpha_level_3as == 0 && m_alpha_level_3ad == 255 && m_alpha_level_3bs == 0 && m_alpha_level_3bd == 255) alpha_mode[i] = 0; else if (m_alpha_level_3as == 255 && m_alpha_level_3ad == 0 && m_alpha_level_3bs == 255 && m_alpha_level_3bd == 0) alpha_mode[i] = 1; } } } if ((alpha_mode[0] == 1 || alpha_mode[0] == 2 || !alpha_mode[0]) && (alpha_mode[1] == 1 || alpha_mode[1] == 2 || !alpha_mode[1]) && (alpha_mode[2] == 1 || alpha_mode[2] == 2 || !alpha_mode[2]) && (alpha_mode[3] == 1 || alpha_mode[3] == 2 || !alpha_mode[3]) && (alpha_mode[4] == 1 || alpha_mode[4] == 2 || !alpha_mode[4]) && sprite_alpha_all_2a) { int alpha_type = (alpha_mode_flag[0] | alpha_mode_flag[1] | alpha_mode_flag[2] | alpha_mode_flag[3]) & 0x30; if ((alpha_type == 0x10 && m_alpha_level_2as == 255) || (alpha_type == 0x20 && m_alpha_level_2as == 255 && m_alpha_level_2bs == 255) || (alpha_type == 0x30 && m_alpha_level_2as == 255 && m_alpha_level_2bs == 255)) { if (alpha_mode[0] > 1) alpha_mode[0] = 1; if (alpha_mode[1] > 1) alpha_mode[1] = 1; if (alpha_mode[2] > 1) alpha_mode[2] = 1; if (alpha_mode[3] > 1) alpha_mode[3] = 1; if (alpha_mode[4] > 1) alpha_mode[4] = 1; sprite_alpha_check = 0; m_dpix_sp[1] = nullptr; m_dpix_sp[2] = nullptr; m_dpix_sp[4] = nullptr; m_dpix_sp[8] = nullptr; } } } else { sprite_alpha_check = 0; m_dpix_sp[1] = nullptr; m_dpix_sp[2] = nullptr; m_dpix_sp[4] = nullptr; m_dpix_sp[8] = nullptr; } /* set scanline priority */ { int pri_max_opa = -1; for (i = 0; i < 5; i++) /* i = playfield num (pos) */ { const int p0 = pri[i]; const int pri_sl1 = p0 & 0x0f; layer_tmp[i] = i + (pri_sl1 << 3); if (!alpha_mode[i]) { layer_tmp[i] |= 0x80; count_skip_layer++; } else if (alpha_mode[i] == 1 && (alpha_mode_flag[i] & 0x80)) { if (layer_tmp[i] > pri_max_opa) pri_max_opa = layer_tmp[i]; } } if (pri_max_opa != -1) { if (pri_max_opa > layer_tmp[0]) { layer_tmp[0] |= 0x80; count_skip_layer++; } if (pri_max_opa > layer_tmp[1]) { layer_tmp[1] |= 0x80; count_skip_layer++; } if (pri_max_opa > layer_tmp[2]) { layer_tmp[2] |= 0x80; count_skip_layer++; } if (pri_max_opa > layer_tmp[3]) { layer_tmp[3] |= 0x80; count_skip_layer++; } if (pri_max_opa > layer_tmp[4]) { layer_tmp[4] |= 0x80; count_skip_layer++; } } } /* sort layer_tmp */ for (i = 0; i < 4; i++) { for (int j = i + 1; j < 5; j++) { if (layer_tmp[i] < layer_tmp[j]) { const int temp = layer_tmp[i]; layer_tmp[i] = layer_tmp[j]; layer_tmp[j] = temp; } } } /* check sprite & layer priority */ { int pri_sp[5]; const int l0 = layer_tmp[0] >> 3; const int l1 = layer_tmp[1] >> 3; const int l2 = layer_tmp[2] >> 3; const int l3 = layer_tmp[3] >> 3; const int l4 = layer_tmp[4] >> 3; pri_sp[0] = spri & 0xf; pri_sp[1] = (spri >> 4) & 0xf; pri_sp[2] = (spri >> 8) & 0xf; pri_sp[3] = spri >> 12; for (i = 0; i < 4; i++) /* i = sprite priority offset */ { const int sflg = 1 << i; if (!(m_sprite_pri_usage & sflg)) continue; int sp = pri_sp[i]; /* sprite priority==playfield priority GSEEKER (plane leaving hangar) --> sprite BUBSYMPH (title) ---> sprite DARIUSG (ZONE V' BOSS) ---> playfield */ if (m_game == BUBSYMPH) sp++; //BUBSYMPH (title) if (m_game == GSEEKER) sp++; //GSEEKER (plane leaving hangar) if ( sp > l0) sprite[0] |= sflg; else if (sp <= l0 && sp > l1) sprite[1] |= sflg; else if (sp <= l1 && sp > l2) sprite[2] |= sflg; else if (sp <= l2 && sp > l3) sprite[3] |= sflg; else if (sp <= l3 && sp > l4) sprite[4] |= sflg; else if (sp <= l4 ) sprite[5] |= sflg; } } /* draw scanlines */ bool alpha = false; for (i = count_skip_layer; i < 5; i++) { pos = layer_tmp[i] & 7; line_t[i] = &pf_line_inf[pos]; if (sprite[i] & sprite_alpha_check) alpha = true; else if (!alpha) sprite[i] |= 0x100; if (alpha_mode[pos] > 1) { int alpha_type = (((alpha_mode_flag[pos] >> 4) & 3) - 1) * 2; m_dpix_lp[i] = m_dpix_n[alpha_mode[pos]+alpha_type]; alpha = true; } else { if (alpha) m_dpix_lp[i] = m_dpix_n[1]; else m_dpix_lp[i] = m_dpix_n[0]; } } if (sprite[5] & sprite_alpha_check) alpha = true; else if (!alpha) sprite[5] |= 0x100; draw_scanlines(bitmap, 320, draw_line_num, line_t, sprite, rot, count_skip_layer); if (y_start < 0) break; } } /******************************************************************************/ #define PSET_T \ c = *source & m_sprite_pen_mask; \ if (c) \ { \ p = *pri; \ if (!p || p == 0xff) \ { \ *dest = pal[c]; \ *pri = pri_dst; \ } \ } #define PSET_O \ p = *pri; \ if (!p || p == 0xff) \ { \ *dest = pal[*source & m_sprite_pen_mask]; \ *pri = pri_dst; \ } #define NEXT_P \ source += dx; \ dest++; \ pri++; inline void taito_f3_state::f3_drawgfx(bitmap_rgb32 &dest_bmp, const rectangle &clip, gfx_element *gfx, int code, int color, int flipx, int flipy, int sx, int sy, u8 pri_dst) { rectangle myclip; pri_dst = 1 << pri_dst; /* KW 991012 -- Added code to force clip to bitmap boundary */ myclip = clip; myclip &= dest_bmp.cliprect(); if (gfx) { const pen_t *pal = &m_palette->pen(gfx->colorbase() + gfx->granularity() * (color % gfx->colors())); const u8 *code_base = gfx->get_data(code % gfx->elements()); { /* compute sprite increment per screen pixel */ int dx = 1; int dy = 1; int ex = sx + 16; int ey = sy + 16; int x_index_base; int y_index; if (flipx) { x_index_base = 15; dx = -1; } else { x_index_base = 0; } if (flipy) { y_index = 15; dy = -1; } else { y_index = 0; } if (sx < myclip.min_x) { /* clip left */ int pixels = myclip.min_x - sx; sx += pixels; x_index_base += pixels * dx; } if (sy < myclip.min_y) { /* clip top */ int pixels = myclip.min_y - sy; sy += pixels; y_index += pixels * dy; } /* NS 980211 - fixed incorrect clipping */ if (ex > myclip.max_x + 1) { /* clip right */ int pixels = ex - myclip.max_x - 1; ex -= pixels; } if (ey > myclip.max_y + 1) { /* clip bottom */ int pixels = ey - myclip.max_y - 1; ey -= pixels; } if (ex > sx && ey > sy) { /* skip if inner loop doesn't draw anything */ // if (dest_bmp.bpp == 32) { int y = ey - sy; const int x = (ex - sx - 1) | (m_tile_opaque_sp[code % gfx->elements()] << 4); const u8 *source0 = code_base + y_index * 16 + x_index_base; u32 *dest0 = &dest_bmp.pix32(sy, sx); u8 *pri0 = &m_pri_alp_bitmap.pix8(sy, sx); const int yadv = dest_bmp.rowpixels(); const int yadvp = m_pri_alp_bitmap.rowpixels(); dy = dy * 16; while (1) { const u8 *source = source0; u32 *dest = dest0; u8 *pri = pri0; switch (x) { int c; u8 p; case 31: PSET_O NEXT_P case 30: PSET_O NEXT_P case 29: PSET_O NEXT_P case 28: PSET_O NEXT_P case 27: PSET_O NEXT_P case 26: PSET_O NEXT_P case 25: PSET_O NEXT_P case 24: PSET_O NEXT_P case 23: PSET_O NEXT_P case 22: PSET_O NEXT_P case 21: PSET_O NEXT_P case 20: PSET_O NEXT_P case 19: PSET_O NEXT_P case 18: PSET_O NEXT_P case 17: PSET_O NEXT_P case 16: PSET_O break; case 15: PSET_T NEXT_P case 14: PSET_T NEXT_P case 13: PSET_T NEXT_P case 12: PSET_T NEXT_P case 11: PSET_T NEXT_P case 10: PSET_T NEXT_P case 9: PSET_T NEXT_P case 8: PSET_T NEXT_P case 7: PSET_T NEXT_P case 6: PSET_T NEXT_P case 5: PSET_T NEXT_P case 4: PSET_T NEXT_P case 3: PSET_T NEXT_P case 2: PSET_T NEXT_P case 1: PSET_T NEXT_P case 0: PSET_T } if (!(--y)) break; source0 += dy; dest0 += yadv; pri0 += yadvp; } } } } } } #undef PSET_T #undef PSET_O #undef NEXT_P inline void taito_f3_state::f3_drawgfxzoom(bitmap_rgb32 &dest_bmp, const rectangle &clip, gfx_element *gfx, int code, int color, int flipx, int flipy, int sx, int sy, int scalex, int scaley, u8 pri_dst) { rectangle myclip; pri_dst = 1 << pri_dst; /* KW 991012 -- Added code to force clip to bitmap boundary */ myclip = clip; myclip &= dest_bmp.cliprect(); if (gfx) { const pen_t *pal = &m_palette->pen(gfx->colorbase() + gfx->granularity() * (color % gfx->colors())); const u8 *code_base = gfx->get_data(code % gfx->elements()); { /* compute sprite increment per screen pixel */ int dx = (16 << 16) / scalex; int dy = (16 << 16) / scaley; int ex = sx + scalex; int ey = sy + scaley; int x_index_base; int y_index; if (flipx) { x_index_base = (scalex - 1) * dx; dx = -dx; } else { x_index_base = 0; } if (flipy) { y_index = (scaley - 1) * dy; dy = -dy; } else { y_index = 0; } if (sx < myclip.min_x) { /* clip left */ int pixels = myclip.min_x - sx; sx += pixels; x_index_base += pixels * dx; } if (sy < myclip.min_y) { /* clip top */ int pixels = myclip.min_y - sy; sy += pixels; y_index += pixels * dy; } /* NS 980211 - fixed incorrect clipping */ if (ex > myclip.max_x + 1) { /* clip right */ int pixels = ex - myclip.max_x - 1; ex -= pixels; } if (ey > myclip.max_y + 1) { /* clip bottom */ int pixels = ey - myclip.max_y - 1; ey -= pixels; } if (ex > sx) { /* skip if inner loop doesn't draw anything */ // if (dest_bmp.bpp == 32) { for (int y = sy; y < ey; y++) { const u8 *source = code_base + (y_index >> 16) * 16; u32 *dest = &dest_bmp.pix32(y); u8 *pri = &m_pri_alp_bitmap.pix8(y); int x_index = x_index_base; for (int x = sx; x < ex; x++) { int c = source[x_index >> 16] & m_sprite_pen_mask; if (c) { const u8 p = pri[x]; if (p == 0 || p == 0xff) { dest[x] = pal[c]; pri[x] = pri_dst; } } x_index += dx; } y_index += dy; } } } } } } #define CALC_ZOOM(p) { \ p##_addition = 0x100 - block_zoom_##p + p##_addition_left; \ p##_addition_left = p##_addition & 0xf; \ p##_addition = p##_addition >> 4; \ /*zoom##p = p##_addition << 12; */ \ } void taito_f3_state::get_sprite_info(const u16 *spriteram16_ptr) { const rectangle &visarea = m_screen->visible_area(); const int min_x = visarea.min_x, max_x = visarea.max_x; const int min_y = visarea.min_y, max_y = visarea.max_y; int global_x = 0,global_y = 0, subglobal_x = 0, subglobal_y = 0; int block_x = 0, block_y = 0; int last_color = 0, last_x = 0, last_y = 0,block_zoom_x = 0,block_zoom_y = 0; int this_x, this_y; int y_addition = 16, x_addition = 16; int multi = 0; int x_addition_left = 8, y_addition_left = 8; tempsprite *sprite_ptr = &m_spritelist[0]; int total_sprites = 0; int color = 0; int flipx = 0, flipy = 0; //int old_x = 0; int y = 0, x = 0; int sprite_top = 0x2000; for (int offs = 0; offs < sprite_top && (total_sprites < 0x400); offs += 8) { const int current_offs = offs; /* Offs can change during loop, current_offs cannot */ /* Check if the sprite list jump command bit is set */ if ((spriteram16_ptr[current_offs + 6 + 0]) & 0x8000) { const u32 jump = (spriteram16_ptr[current_offs + 6 + 0]) & 0x3ff; const u32 new_offs = ((offs & 0x4000) | ((jump << 4) / 2)); if (new_offs == offs) break; offs = new_offs - 8; } /* Check if special command bit is set */ if (spriteram16_ptr[current_offs + 2 + 1] & 0x8000) { const u32 cntrl = (spriteram16_ptr[current_offs + 4 + 1]) & 0xffff; m_flipscreen = cntrl & 0x2000; /* cntrl & 0x1000 = disabled? (From F2 driver, doesn't seem used anywhere) cntrl & 0x0010 = ??? cntrl & 0x0020 = ??? cntrl & 0x0002 = enabled when Darius Gaiden sprite trail effect should occur (MT #1922) Notice that sprites also completely disappear due of a bug/missing feature in the alpha routines. */ m_sprite_extra_planes = (cntrl & 0x0300) >> 8; // 0 = 4bpp, 1 = 5bpp, 2 = unused?, 3 = 6bpp m_sprite_pen_mask = (m_sprite_extra_planes << 4) | 0x0f; /* Sprite bank select */ if (cntrl & 1) { offs = offs | 0x4000; sprite_top = sprite_top | 0x4000; } } /* Set global sprite scroll */ if (((spriteram16_ptr[current_offs + 2 + 0]) & 0xf000) == 0xa000) { global_x = (spriteram16_ptr[current_offs + 2 + 0]) & 0xfff; if (global_x >= 0x800) global_x -= 0x1000; global_y = spriteram16_ptr[current_offs + 2 + 1] & 0xfff; if (global_y >= 0x800) global_y -= 0x1000; } /* And sub-global sprite scroll */ if (((spriteram16_ptr[current_offs + 2 + 0]) & 0xf000) == 0x5000) { subglobal_x = (spriteram16_ptr[current_offs + 2 + 0]) & 0xfff; if (subglobal_x >= 0x800) subglobal_x -= 0x1000; subglobal_y = spriteram16_ptr[current_offs + 2 + 1] & 0xfff; if (subglobal_y >= 0x800) subglobal_y -= 0x1000; } if (((spriteram16_ptr[current_offs + 2 + 0]) & 0xf000) == 0xb000) { subglobal_x = (spriteram16_ptr[current_offs + 2 + 0]) & 0xfff; if (subglobal_x >= 0x800) subglobal_x -= 0x1000; subglobal_y = spriteram16_ptr[current_offs + 2 + 1] & 0xfff; if (subglobal_y >= 0x800) subglobal_y -= 0x1000; global_y = subglobal_y; global_x = subglobal_x; } /* A real sprite to process! */ const int sprite = (spriteram16_ptr[current_offs + 0 + 0]) | ((spriteram16_ptr[current_offs + 4 + 1] & 1) << 16); const int spritecont = spriteram16_ptr[current_offs + 4 + 0] >> 8; /* These games either don't set the XY control bits properly (68020 bug?), or have some different mode from the others */ #ifdef DARIUSG_KLUDGE if (m_game == DARIUSG || m_game == GEKIRIDO || m_game == CLEOPATR || m_game == RECALH) multi = spritecont & 0xf0; #endif /* Check if this sprite is part of a continued block */ if (multi) { /* Bit 0x4 is 'use previous colour' for this block part */ if (spritecont & 0x4) color=last_color; else color = (spriteram16_ptr[current_offs + 4 + 0]) & 0xff; #ifdef DARIUSG_KLUDGE if (m_game == DARIUSG || m_game == GEKIRIDO || m_game == CLEOPATR || m_game == RECALH) { /* Adjust X Position */ if ((spritecont & 0x40) == 0) { if (spritecont & 0x4) x = block_x; else { this_x = spriteram16_ptr[current_offs + 2 + 0]; if (this_x & 0x800) this_x = 0 - (0x800 - (this_x & 0x7ff)); else this_x &= 0x7ff; if ((spriteram16_ptr[current_offs + 2 + 0]) & 0x8000) this_x += 0; else if ((spriteram16_ptr[current_offs + 2 + 0]) & 0x4000) /* Ignore subglobal (but apply global) */ this_x += global_x; else /* Apply both scroll offsets */ this_x += global_x + subglobal_x; x = block_x = this_x; } x_addition_left = 8; CALC_ZOOM(x) } else if ((spritecont & 0x80) != 0) { x = last_x + x_addition; CALC_ZOOM(x) } /* Adjust Y Position */ if ((spritecont & 0x10) == 0) { if (spritecont & 0x4) y = block_y; else { this_y = spriteram16_ptr[current_offs + 2 + 1] & 0xffff; if (this_y & 0x800) this_y = 0 - (0x800 - (this_y & 0x7ff)); else this_y &= 0x7ff; if ((spriteram16_ptr[current_offs + 2 + 0]) & 0x8000) this_y += 0; else if ((spriteram16_ptr[current_offs + 2 + 0]) & 0x4000) /* Ignore subglobal (but apply global) */ this_y += global_y; else /* Apply both scroll offsets */ this_y += global_y + subglobal_y; y = block_y = this_y; } y_addition_left = 8; CALC_ZOOM(y) } else if ((spritecont & 0x20) != 0) { y = last_y + y_addition; CALC_ZOOM(y) } } else #endif { /* Adjust X Position */ if ((spritecont & 0x40) == 0) { x = block_x; x_addition_left = 8; CALC_ZOOM(x) } else if ((spritecont & 0x80) != 0) { x = last_x + x_addition; CALC_ZOOM(x) } /* Adjust Y Position */ if ((spritecont & 0x10) == 0) { y = block_y; y_addition_left = 8; CALC_ZOOM(y) } else if ((spritecont & 0x20) != 0) { y = last_y + y_addition; CALC_ZOOM(y) } /* Both zero = reread block latch? */ } } /* Else this sprite is the possible start of a block */ else { color = (spriteram16_ptr[current_offs + 4 + 0]) & 0xff; last_color = color; /* Sprite positioning */ this_y = spriteram16_ptr[current_offs + 2 + 1] & 0xffff; this_x = spriteram16_ptr[current_offs + 2 + 0] & 0xffff; if (this_y & 0x800) this_y = 0 - (0x800 - (this_y & 0x7ff)); else this_y &= 0x7ff; if (this_x & 0x800) this_x = 0 - (0x800 - (this_x & 0x7ff)); else this_x &= 0x7ff; /* Ignore both scroll offsets for this block */ if ((spriteram16_ptr[current_offs + 2 + 0]) & 0x8000) { this_x += 0; this_y += 0; } else if ((spriteram16_ptr[current_offs + 2 + 0]) & 0x4000) /* Ignore subglobal (but apply global) */ { this_x += global_x; this_y += global_y; } else /* Apply both scroll offsets */ { this_x += global_x + subglobal_x; this_y += global_y + subglobal_y; } block_y = y = this_y; block_x = x = this_x; block_zoom_x = spriteram16_ptr[current_offs + 0 + 1]; block_zoom_y = (block_zoom_x >> 8) & 0xff; block_zoom_x &= 0xff; x_addition_left = 8; CALC_ZOOM(x) y_addition_left = 8; CALC_ZOOM(y) } /* These features are common to sprite and block parts */ flipx = spritecont & 0x1; flipy = spritecont & 0x2; multi = spritecont & 0x8; last_x=x; last_y = y; if (!sprite) continue; if (!x_addition || !y_addition) continue; if (m_flipscreen) { const int tx = 512 - x_addition - x; const int ty = 256 - y_addition - y; if (tx + x_addition <= min_x || tx > max_x || ty + y_addition <= min_y || ty > max_y) continue; sprite_ptr->x = tx; sprite_ptr->y = ty; sprite_ptr->flipx = !flipx; sprite_ptr->flipy = !flipy; } else { if (x + x_addition <= min_x || x > max_x || y + y_addition <= min_y || y > max_y) continue; sprite_ptr->x = x; sprite_ptr->y = y; sprite_ptr->flipx = flipx; sprite_ptr->flipy = flipy; } sprite_ptr->code = sprite; sprite_ptr->color = color; sprite_ptr->zoomx = x_addition; sprite_ptr->zoomy = y_addition; sprite_ptr->pri = (color & 0xc0) >> 6; sprite_ptr++; total_sprites++; } m_sprite_end = sprite_ptr; } #undef CALC_ZOOM void taito_f3_state::draw_sprites(bitmap_rgb32 &bitmap, const rectangle &cliprect) { const tempsprite *sprite_ptr; gfx_element *sprite_gfx = m_gfxdecode->gfx(2); sprite_ptr = m_sprite_end; m_sprite_pri_usage = 0; // if sprites use more than 4bpp, the bottom bits of the color code must be masked out. // This fixes (at least) stage 1 battle ships and attract mode explosions in Ray Force. while (sprite_ptr != &m_spritelist[0]) { sprite_ptr--; const int pri = sprite_ptr->pri; m_sprite_pri_usage |= 1 << pri; if (sprite_ptr->zoomx == 16 && sprite_ptr->zoomy == 16) f3_drawgfx( bitmap, cliprect, sprite_gfx, sprite_ptr->code, sprite_ptr->color & (~m_sprite_extra_planes), sprite_ptr->flipx, sprite_ptr->flipy, sprite_ptr->x, sprite_ptr->y, pri); else f3_drawgfxzoom( bitmap, cliprect, sprite_gfx, sprite_ptr->code, sprite_ptr->color & (~m_sprite_extra_planes), sprite_ptr->flipx, sprite_ptr->flipy, sprite_ptr->x, sprite_ptr->y, sprite_ptr->zoomx, sprite_ptr->zoomy, pri); } } /******************************************************************************/ u32 taito_f3_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { u32 sy_fix[5], sx_fix[5]; machine().tilemap().set_flip_all(m_flipscreen ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0); /* Setup scroll */ sy_fix[0] = ((m_control_0[4] & 0xffff) << 9) + (1 << 16); sy_fix[1] = ((m_control_0[5] & 0xffff) << 9) + (1 << 16); sy_fix[2] = ((m_control_0[6] & 0xffff) << 9) + (1 << 16); sy_fix[3] = ((m_control_0[7] & 0xffff) << 9) + (1 << 16); sx_fix[0] = ((m_control_0[0] & 0xffc0) << 10) - (6 << 16); sx_fix[1] = ((m_control_0[1] & 0xffc0) << 10) - (10 << 16); sx_fix[2] = ((m_control_0[2] & 0xffc0) << 10) - (14 << 16); sx_fix[3] = ((m_control_0[3] & 0xffc0) << 10) - (18 << 16); sx_fix[4] = -(m_control_1[4]) + 41; sy_fix[4] = -(m_control_1[5] & 0x1ff); sx_fix[0]-=((m_control_0[0] & 0x003f) << 10) + 0x0400 - 0x10000; sx_fix[1]-=((m_control_0[1] & 0x003f) << 10) + 0x0400 - 0x10000; sx_fix[2]-=((m_control_0[2] & 0x003f) << 10) + 0x0400 - 0x10000; sx_fix[3]-=((m_control_0[3] & 0x003f) << 10) + 0x0400 - 0x10000; if (m_flipscreen) { sy_fix[0] = 0x3000000 - sy_fix[0]; sy_fix[1] = 0x3000000 - sy_fix[1]; sy_fix[2] = 0x3000000 - sy_fix[2]; sy_fix[3] = 0x3000000 - sy_fix[3]; sx_fix[0] = -0x1a00000 - sx_fix[0]; sx_fix[1] = -0x1a00000 - sx_fix[1]; sx_fix[2] = -0x1a00000 - sx_fix[2]; sx_fix[3] = -0x1a00000 - sx_fix[3]; sx_fix[4] = -sx_fix[4] + 75; sy_fix[4] = -sy_fix[4]; } m_pri_alp_bitmap.fill(0, cliprect); /* sprites */ if (m_sprite_lag == 0) get_sprite_info(m_spriteram.target()); /* Update sprite buffer */ draw_sprites(bitmap, cliprect); /* Parse sprite, alpha & clipping parts of lineram */ get_spritealphaclip_info(); /* Parse playfield effects */ get_line_ram_info(m_tilemap[0], sx_fix[0], sy_fix[0], 0, m_pf_data[0]); get_line_ram_info(m_tilemap[1], sx_fix[1], sy_fix[1], 1, m_pf_data[1]); get_line_ram_info(m_tilemap[2], sx_fix[2], sy_fix[2], 2, m_pf_data[2]); get_line_ram_info(m_tilemap[3], sx_fix[3], sy_fix[3], 3, m_pf_data[3]); get_vram_info(m_vram_layer, m_pixel_layer, sx_fix[4], sy_fix[4]); /* Draw final framebuffer */ scanline_draw(bitmap, cliprect); if (VERBOSE) print_debug_info(bitmap); return 0; }