// license:BSD-3-Clause // copyright-holders:David Graves, Angelo Salese, David Haywood, Tomasz Slanina /*************************************************************************** Legionnaire / Heated Barrel video hardware (derived from D-Con) priority test (used by Legionnaire, front to bottom): - OBJ 0 - TXT - OBJ 1 - BK3 - OBJ 2 - MBK - OBJ 3 - LBK TODO: Anything else doesn't match this scheme (most notably Denjin Makai), guess it's selectable by PROM, CRTC or COP ... ***************************************************************************/ #include "emu.h" #include "includes/legionna.h" #include "screen.h" /******************************************************************************/ void legionna_state::tilemap_enable_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_layer_disable); } void legionna_state::tile_scroll_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(m_scrollvals + offset); data = m_scrollvals[offset]; tilemap_t *tm = nullptr; switch (offset/2) { case 0: tm = m_background_layer; break; case 1: tm = m_midground_layer; break; case 2: tm = m_foreground_layer; break; } if (offset & 1) tm->set_scrolly(0, data); else tm->set_scrollx(0, data); } void legionna_state::tile_vreg_1a_w(u16 data) { flip_screen_set(data & 1); // TODO: other bits ... } void legionna_state::tile_scroll_base_w(offs_t offset, u16 data) { // TODO: specific for Godzilla, needs visible area changes. if (offset == 7) m_text_layer->set_scrolldy(0x1ef - data,0x1ef - data); //printf("%02x %04x\n",offset,data); } void legionna_state::heatbrl_setgfxbank(u16 data) { m_back_gfx_bank = (data & 0x4000) >> 2; m_background_layer->mark_all_dirty(); } /*xxx- --- ---- ---- banking*/ void legionna_state::denjinmk_setgfxbank(u16 data) { // this is either 0x0000 or 0xe000, except in two endings (MT #07416) m_back_gfx_bank = (data & 0x2000) >> 1; // Makai/Tarukusu endings m_mid_gfx_bank = (data & 0x4000) >> 2; //??? m_fore_gfx_bank = (data & 0x8000) >> 3; //??? m_foreground_layer->mark_all_dirty(); m_background_layer->mark_all_dirty(); m_midground_layer->mark_all_dirty(); } void legionna_state::videowrite_cb_w(offs_t offset, u16 data) { // map(0x101000, 0x1017ff).ram(); // .w(FUNC(legionna_state::background_w)).share("back_data"); // map(0x101800, 0x101fff).ram(); // .w(FUNC(legionna_state::foreground_w)).share("fore_data"); // map(0x102000, 0x1027ff).ram(); // .w(FUNC(legionna_state::midground_w)).share("mid_data"); // map(0x102800, 0x1037ff).ram(); // .w(FUNC(legionna_state::text_w)).share("textram"); if (offset < 0x800 / 2) { background_w(offset, data); } else if (offset < 0x1000 /2) { offset -= 0x800 / 2; foreground_w(offset, data); } else if (offset < 0x1800/2) { offset -= 0x1000 / 2; midground_w(offset, data); } else if (offset < 0x2800/2) { offset -= 0x1800 / 2; text_w(offset, data); } } // TODO: move to COP device void legionna_state::grainbow_layer_config_w(offs_t offset, u16 data, u16 mem_mask) { // (0x8000|0x1ff), 0x200, 0x1ff, 0x200 written in sequence at startup COMBINE_DATA(&m_layer_config[offset]); } void legionna_state::background_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_back_data[offset]); m_background_layer->mark_tile_dirty(offset); } void legionna_state::midground_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_mid_data[offset]); m_midground_layer->mark_tile_dirty(offset); } void legionna_state::foreground_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_fore_data[offset]); m_foreground_layer->mark_tile_dirty(offset); } void legionna_state::text_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_textram[offset]); m_text_layer->mark_tile_dirty(offset); } TILE_GET_INFO_MEMBER(legionna_state::get_back_tile_info) { const u16 tile = m_back_data[tile_index]; tileinfo.set(2, (tile & 0xfff) | m_back_gfx_bank, (tile >> 12) & 0xf, 0); } TILE_GET_INFO_MEMBER(legionna_state::get_mid_tile_info_split) { const u16 tile = m_mid_data[tile_index]; tileinfo.set(4, (tile & 0xfff) | m_mid_gfx_bank, (tile >> 12) & 0xf, 0); } TILE_GET_INFO_MEMBER(legionna_state::get_mid_tile_info_share_bgrom) { const u16 tile = m_mid_data[tile_index]; tileinfo.set(2, (tile & 0xfff) | 0x1000, ((tile >> 12) & 0xf) | 0x10, 0); } TILE_GET_INFO_MEMBER(legionna_state::get_fore_tile_info) { const u16 tile = m_fore_data[tile_index]; tileinfo.set(3, (tile & 0xfff) | m_fore_gfx_bank, (tile >> 12) & 0xf, 0); } TILE_GET_INFO_MEMBER(legionna_state::get_text_tile_info) { const u16 tile = m_textram[tile_index]; tileinfo.set(1, tile & 0xfff, (tile >> 12) & 0xf, 0); } void legionna_state::common_video_allocate_ptr() { m_back_data = make_unique_clear(0x800/2); m_fore_data = make_unique_clear(0x800/2); m_mid_data = make_unique_clear(0x800/2); m_textram = make_unique_clear(0x1000/2); m_scrollram16 = std::make_unique(0x60/2); m_paletteram = make_unique_clear(0x1000/2); m_palette->basemem().set(m_paletteram.get(), 0x1000/2 * sizeof(u16), 16, ENDIANNESS_BIG, 2); m_sprite_xoffs = 0; m_sprite_yoffs = 0; save_pointer(NAME(m_back_data), 0x800/2); save_pointer(NAME(m_fore_data), 0x800/2); save_pointer(NAME(m_mid_data), 0x800/2); save_pointer(NAME(m_textram), 0x1000/2); save_pointer(NAME(m_scrollram16), 0x60/2); save_pointer(NAME(m_paletteram), 0x1000/2); // saved for debugging save_pointer(NAME(m_sprite_pri_mask), 4); save_item(NAME(m_back_gfx_bank)); save_item(NAME(m_mid_gfx_bank)); save_item(NAME(m_fore_gfx_bank)); save_item(NAME(m_layer_disable)); } void legionna_state::common_video_start(bool split, bool has_extended_banking, bool has_extended_priority) { common_video_allocate_ptr(); m_background_layer = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(legionna_state::get_back_tile_info)), TILEMAP_SCAN_ROWS,16,16,32,32); if (split) { m_midground_layer = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(legionna_state::get_mid_tile_info_split)), TILEMAP_SCAN_ROWS,16,16,32,32); } else { m_midground_layer = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(legionna_state::get_mid_tile_info_share_bgrom)), TILEMAP_SCAN_ROWS,16,16,32,32); } m_foreground_layer = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(legionna_state::get_fore_tile_info)), TILEMAP_SCAN_ROWS,16,16,32,32); m_text_layer = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(legionna_state::get_text_tile_info)), TILEMAP_SCAN_ROWS, 8, 8,64,32); m_has_extended_banking = has_extended_banking; m_has_extended_priority = has_extended_priority; m_background_layer->set_transparent_pen(15); m_midground_layer->set_transparent_pen(15); m_foreground_layer->set_transparent_pen(15); m_text_layer->set_transparent_pen(15); } VIDEO_START_MEMBER(legionna_state,legionna) { common_video_start(false, false, false); m_sprite_pri_mask[0] = 0x0000; m_sprite_pri_mask[1] = 0xfff0; m_sprite_pri_mask[2] = 0xfffc; m_sprite_pri_mask[3] = 0xfffe; } VIDEO_START_MEMBER(legionna_state,heatbrl) { common_video_start(true, false, false); m_sprite_pri_mask[0] = 0xfff0; m_sprite_pri_mask[1] = 0xfffc; m_sprite_pri_mask[2] = 0xfffe; // TODO: not shown? m_sprite_pri_mask[3] = 0xffff; } VIDEO_START_MEMBER(legionna_state,godzilla) { common_video_start(false, true, false); m_sprite_pri_mask[0] = 0xfff0; m_sprite_pri_mask[1] = 0xfffc; m_sprite_pri_mask[2] = 0xfffe; // TODO: not shown? m_sprite_pri_mask[3] = 0xffff; } VIDEO_START_MEMBER(legionna_state,denjinmk) { common_video_start(true, true, false); m_sprite_pri_mask[0] = 0xfff0; // normal sprites m_sprite_pri_mask[1] = 0xfffc; // luna park horse rides m_sprite_pri_mask[2] = 0xfffe; // door at the end of sewers part in level 1 m_sprite_pri_mask[3] = 0x0000; // briefing guy in pre-stage and portraits before a boss fight m_text_layer->set_transparent_pen(7);//? } VIDEO_START_MEMBER(legionna_state,cupsoc) { common_video_start(false, false, false); m_sprite_pri_mask[0] = 0xfff0; // title screen "Seibu Cup Soccer" elements m_sprite_pri_mask[1] = 0xfffc; // ? m_sprite_pri_mask[2] = 0xfffe; // ? m_sprite_pri_mask[3] = 0x0000; // 1P logo and radar dots (latter confirmed to stay above text layer) } VIDEO_START_MEMBER(legionna_state,grainbow) { common_video_start(false, false, true); m_sprite_xoffs = m_sprite_yoffs = 16; m_sprite_pri_mask[0] = 0xfff0; // m_sprite_pri_mask[1] = 0xfffc; // level 2 and 3 m_sprite_pri_mask[2] = 0xfffe; // swamp monster mask effect m_sprite_pri_mask[3] = 0x0000; // Insert coin m_layer_config = std::make_unique(0x8/2); } /************************************************************************* Legionnaire Spriteram (similar to Dcon) --------------------- It has "big sprites" created by setting width or height >0. Tile numbers are read consecutively. +0 x....... ........ Sprite enable +0 .x...... ........ Flip x +0 ..x..... ........ Flip y ??? +0 ...xxx.. ........ Width: do this many tiles horizontally +0 ......xx x....... Height: do this many tiles vertically +0 ........ .x...... Tile bank,used in Denjin Makai / extra Priority in Grainbow (to external pin?) +0 ........ ..xxxxxx Color bank +1 xx...... ........ Priority? (1=high?) +1 ..xxxxxx xxxxxxxx Tile number +2 ----xxxx xxxxxxxx X coordinate (signed) +3 b------- -------- more tile banking used by Denjin Makai +3 ----xxxx xxxxxxxx Y coordinate (signed) *************************************************************************/ void legionna_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap,const rectangle &cliprect) { for (int offs = 0; offs < 0x400; offs += 4) { const u16 data = m_spriteram[offs]; if (!(data & 0x8000)) continue; u8 cur_pri = 0; int pri_mask = 0; if (m_has_extended_priority) { // SD Gundam uses this arrangement, with bit 14 seemingly unused // side effect of using the COP sprite DMA? cur_pri = (m_spriteram[offs+1] & 0x8000) >> 14; if (data & 0x0040) cur_pri |= 1; } else cur_pri = (m_spriteram[offs+1] & 0xc000) >> 14; pri_mask = m_sprite_pri_mask[cur_pri]; u32 sprite = m_spriteram[offs+1]; sprite &= 0x3fff; if (m_has_extended_banking) { if (data & 0x0040) { sprite |= 0x4000;//tile banking,used in Denjin Makai } if (m_spriteram[offs+3] & 0x8000) { sprite |= 0x8000;//tile banking?,used in Denjin Makai } } int y = m_spriteram[offs+3]; int x = m_spriteram[offs+2]; /* heated barrel hardware seems to need 0x1ff with 0x100 sign bit for sprite wrap, this doesn't work on denjin makai as the visible area is larger */ if (cliprect.max_x<(320-1)) { x &= 0x1ff; y &= 0x1ff; if (x & 0x100) x -= 0x200; if (y & 0x100) y -= 0x200; } else { x &= 0xfff; y &= 0xfff; if (x & 0x800) x -= 0x1000; if (y & 0x800) y -= 0x1000; } const u32 color = (data & 0x003f); const u8 fx = (data & 0x4000) >> 14; const u8 fy = (data & 0x2000) >> 13; const u8 dy = ((data & 0x0380) >> 7) + 1; const u8 dx = ((data & 0x1c00) >> 10) + 1; if (!fx) { if (!fy) { for (int ax = 0; ax < dx; ax++) for (int ay = 0; ay < dy; ay++) { m_gfxdecode->gfx(0)->prio_transpen(bitmap,cliprect, sprite++, color,fx,fy,(x+ax*16)+m_sprite_xoffs,y+ay*16+m_sprite_yoffs, screen.priority(),pri_mask, 15); } } else { for (int ax = 0; ax < dx; ax++) for (int ay = 0; ay < dy; ay++) { m_gfxdecode->gfx(0)->prio_transpen(bitmap,cliprect, sprite++, color,fx,fy,(x+ax*16)+m_sprite_xoffs,y+(dy-ay-1)*16+m_sprite_yoffs, screen.priority(),pri_mask,15); } } } else { if (!fy) { for (int ax = 0; ax < dx; ax++) for (int ay = 0; ay < dy; ay++) { m_gfxdecode->gfx(0)->prio_transpen(bitmap,cliprect, sprite++, color,fx,fy,(x+(dx-ax-1)*16)+m_sprite_xoffs,y+ay*16+m_sprite_yoffs, screen.priority(),pri_mask,15); } } else { for (int ax = 0; ax < dx; ax++) for (int ay = 0; ay < dy; ay++) { m_gfxdecode->gfx(0)->prio_transpen(bitmap,cliprect, sprite++, color,fx,fy,(x+(dx-ax-1)*16)+m_sprite_xoffs,y+(dy-ay-1)*16+m_sprite_yoffs, screen.priority(),pri_mask, 15); } } } } } u32 legionna_state::screen_update_legionna(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { /* Setup the tilemaps */ screen.priority().fill(0, cliprect); bitmap.fill(m_palette->black_pen(), cliprect); /* wrong color? */ if (!(m_layer_disable & 0x0001)) m_midground_layer->draw(screen, bitmap, cliprect, 0, 0); if (!(m_layer_disable & 0x0002)) m_background_layer->draw(screen, bitmap, cliprect, 0, 1); if (!(m_layer_disable & 0x0004)) m_foreground_layer->draw(screen, bitmap, cliprect, 0, 2); if (!(m_layer_disable & 0x0008)) m_text_layer->draw(screen, bitmap, cliprect, 0, 4); if (!(m_layer_disable & 0x0010)) draw_sprites(screen,bitmap,cliprect); //if (machine().input().code_pressed_once(KEYCODE_Z)) // if (m_raiden2cop) m_raiden2cop->dump_table(); return 0; } u32 legionna_state::screen_update_heatbrl(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { /* Setup the tilemaps */ screen.priority().fill(0, cliprect); bitmap.fill(m_palette->black_pen(), cliprect); /* wrong color? */ // TODO: priority order is different than anything else? if (!(m_layer_disable & 0x0004)) m_foreground_layer->draw(screen, bitmap, cliprect, 0, 0); if (!(m_layer_disable & 0x0002)) m_midground_layer->draw(screen, bitmap, cliprect, 0, 1); if (!(m_layer_disable & 0x0001)) m_background_layer->draw(screen, bitmap, cliprect, 0, 2); if (!(m_layer_disable & 0x0008)) m_text_layer->draw(screen, bitmap, cliprect, 0, 4); if (!(m_layer_disable & 0x0010)) draw_sprites(screen,bitmap,cliprect); //if (machine().input().code_pressed_once(KEYCODE_Z)) // if (m_raiden2cop) m_raiden2cop->dump_table(); return 0; } u32 legionna_state::screen_update_godzilla(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { screen.priority().fill(0, cliprect); // matches PCB recording for Denjin Makai, settable thru CRTC? bitmap.fill(0xff, cliprect); if (!(m_layer_disable & 0x0001)) m_background_layer->draw(screen, bitmap, cliprect, 0, 0); if (!(m_layer_disable & 0x0002)) m_midground_layer->draw(screen, bitmap, cliprect, 0, 1); if (!(m_layer_disable & 0x0004)) m_foreground_layer->draw(screen, bitmap, cliprect, 0, 2); if (!(m_layer_disable & 0x0008)) m_text_layer->draw(screen, bitmap, cliprect, 0, 4); if (!(m_layer_disable & 0x0010)) draw_sprites(screen,bitmap,cliprect); //if (machine().input().code_pressed_once(KEYCODE_Z)) // if (m_raiden2cop) m_raiden2cop->dump_table(); return 0; } u32 legionna_state::screen_update_grainbow(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { bitmap.fill(m_palette->black_pen(), cliprect); screen.priority().fill(0, cliprect); if (!(m_layer_disable & 1)) m_background_layer->draw(screen, bitmap, cliprect, 0,0); if (!(m_layer_disable & 2)) m_midground_layer->draw(screen, bitmap, cliprect, 0,1); if (!(m_layer_disable & 4)) m_foreground_layer->draw(screen, bitmap, cliprect, 0,2); if (!(m_layer_disable & 8)) m_text_layer->draw(screen, bitmap, cliprect, 0,4); if (!(m_layer_disable & 0x0010)) draw_sprites(screen,bitmap,cliprect); //if (machine().input().code_pressed_once(KEYCODE_Z)) // if (m_raiden2cop) m_raiden2cop->dump_table(); return 0; }