// license:BSD-3-Clause // copyright-holders:Paul Priest, David Haywood /* Magical Cat Adventure / Nostradamus Video Hardware */ /* Notes: Tilemap drawing is a killer on the first level of Nost due to the whole tilemap being dirty every frame. Sprite drawing is quite fast (See USER1 in the profiler) ToDo: Fix Sprites & Rowscroll/Select for Cocktail */ #include "emu.h" #include "includes/mcatadv.h" #include "screen.h" #include template TILE_GET_INFO_MEMBER(mcatadv_state::get_mcatadv_tile_info) { int tileno = m_vram[Chip][tile_index * 2 + 1]; int colour = (m_vram[Chip][tile_index * 2] & 0x3f00) >> 8; int pri = (m_vram[Chip][tile_index * 2] & 0xc000) >> 14; pri |= 0x8; SET_TILE_INFO_MEMBER(Chip,tileno,colour + m_palette_bank[Chip] * 0x40, 0); tileinfo.category = pri; } void mcatadv_state::draw_sprites( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect ) { uint16_t *source = (m_spriteram_old.get() + (m_spriteram.bytes() / 2) /2); source -= 4; uint16_t *finish = m_spriteram_old.get(); int global_x = m_vidregs[0] - 0x184; int global_y = m_vidregs[1] - 0x1f1; uint16_t *destline; uint8_t *priline; int sprmask = m_sprdata.bytes()-1; int xstart, xend, xinc; int ystart, yend, yinc; if (m_vidregs_old[2] == 0x0001) /* Double Buffered */ { source += (m_spriteram.bytes() / 2) / 2; finish += (m_spriteram.bytes() / 2) / 2; } else if (m_vidregs_old[2]) /* I suppose it's possible that there is 4 banks, haven't seen it used though */ { logerror("Spritebank != 0/1\n"); } while (source >= finish) { int pen = (source[0] & 0x3f00) >> 8; int tileno = source[1] & 0xffff; int pri = (source[0] & 0xc000) >> 14; pri |= 0x8; int x = source[2] & 0x3ff; int y = source[3] & 0x3ff; int flipy = source[0] & 0x0040; int flipx = source[0] & 0x0080; int height = ((source[3] & 0xf000) >> 12) * 16; int width = ((source[2] & 0xf000) >> 12) * 16; int offset = tileno * 256; int drawxpos, drawypos; int xcnt, ycnt; int pix; if (x & 0x200) x-=0x400; if (y & 0x200) y-=0x400; #if 0 // For Flipscreen/Cocktail if(m_vidregs[0] & 0x8000) { flipx = !flipx; } if(m_vidregs[1] & 0x8000) { flipy = !flipy; } #endif if (source[3] != source[0]) // 'hack' don't draw sprites while its testing the ram! { if(!flipx) { xstart = 0; xend = width; xinc = 1; } else { xstart = width-1; xend = -1; xinc = -1; } if(!flipy) { ystart = 0; yend = height; yinc = 1; } else { ystart = height-1; yend = -1; yinc = -1; } for (ycnt = ystart; ycnt != yend; ycnt += yinc) { drawypos = y + ycnt - global_y; if ((drawypos >= cliprect.min_y) && (drawypos <= cliprect.max_y)) { destline = &bitmap.pix16(drawypos); priline = &screen.priority().pix8(drawypos); for (xcnt = xstart; xcnt != xend; xcnt += xinc) { drawxpos = x + xcnt - global_x; if ((drawxpos >= cliprect.min_x) && (drawxpos <= cliprect.max_x)) { int pridata = priline[drawxpos]; if (!(pridata & 0x10)) // if we haven't already drawn a sprite pixel here (sprite masking) { pix = m_sprdata[(offset / 2)&sprmask]; if (offset & 1) pix = pix >> 4; pix &= 0x0f; if (pix) { if ((priline[drawxpos] < pri)) destline[drawxpos] = (pix + (pen << 4)); priline[drawxpos] |= 0x10; } } } offset++; } } else { offset += width; } } } source -= 4; } } void mcatadv_state::mcatadv_draw_tilemap_part( screen_device &screen, int layer, int i, bitmap_ind16 &bitmap, const rectangle &cliprect ) { int flip; uint32_t drawline; rectangle clip; clip.min_x = cliprect.min_x; clip.max_x = cliprect.max_x; for (drawline = cliprect.min_y; drawline <= cliprect.max_y; drawline++) { int scrollx, scrolly; clip.min_y = drawline; clip.max_y = drawline; scrollx = (m_scroll[layer][0] & 0x1ff) - 0x194; scrolly = (m_scroll[layer][1] & 0x1ff) - 0x1df; if ((m_scroll[layer][1] & 0x4000) == 0x4000) { int rowselect = m_vram[layer][0x1000 / 2 + (((drawline + scrolly) & 0x1ff) * 2) + 1]; scrolly = rowselect - drawline; } if ((m_scroll[layer][0] & 0x4000) == 0x4000) { int rowscroll = m_vram[layer][0x1000 / 2 + (((drawline + scrolly) & 0x1ff) * 2) + 0]; scrollx += rowscroll; } /* Global Flip */ if (!(m_scroll[layer][0] & 0x8000)) scrollx -= 0x19; if (!(m_scroll[layer][1] & 0x8000)) scrolly -= 0x141; flip = ((m_scroll[layer][0] & 0x8000) ? 0 : TILEMAP_FLIPX) | ((m_scroll[layer][1] & 0x8000) ? 0 : TILEMAP_FLIPY); m_tilemap[layer]->set_scrollx(0, scrollx); m_tilemap[layer]->set_scrolly(0, scrolly); m_tilemap[layer]->set_flip(flip); m_tilemap[layer]->draw(screen, bitmap, clip, i, i); } } uint32_t mcatadv_state::screen_update_mcatadv(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { int i; bitmap.fill(0x3f0, cliprect); screen.priority().fill(0, cliprect); if (m_scroll[0][2] != m_palette_bank[0]) { m_palette_bank[0] = m_scroll[0][2]&0xf; m_tilemap[0]->mark_all_dirty(); } if (m_scroll[1][2] != m_palette_bank[1]) { m_palette_bank[1] = m_scroll[1][2]&0xf; m_tilemap[1]->mark_all_dirty(); } /* popmessage("%02x %02x %02x %02x", (mcatadv_scroll1[0] & 0x4000) >> 8, (mcatadv_scroll1[1] & 0x4000) >> 8, (mcatadv_scroll2[0] & 0x4000) >> 8, (mcatadv_scroll2[1] & 0x4000) >> 8); */ for (i = 0; i <= 3; i++) { #ifdef MAME_DEBUG if (!machine().input().code_pressed(KEYCODE_Q)) #endif if (!(m_scroll[0][2]&0x10)) mcatadv_draw_tilemap_part(screen, 0, i|0x8, bitmap, cliprect); #ifdef MAME_DEBUG if (!machine().input().code_pressed(KEYCODE_W)) #endif if (!(m_scroll[1][2]&0x10)) // tilemap flicker effect on large shadow, nost level 7 mcatadv_draw_tilemap_part(screen, 1, i|0x8, bitmap, cliprect); } g_profiler.start(PROFILER_USER1); #ifdef MAME_DEBUG if (!machine().input().code_pressed(KEYCODE_E)) #endif draw_sprites (screen, bitmap, cliprect); g_profiler.stop(); return 0; } void mcatadv_state::video_start() { m_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(mcatadv_state::get_mcatadv_tile_info<0>),this), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_tilemap[0]->set_transparent_pen(0); m_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(mcatadv_state::get_mcatadv_tile_info<1>),this), TILEMAP_SCAN_ROWS, 16, 16, 32, 32); m_tilemap[1]->set_transparent_pen(0); m_spriteram_old = make_unique_clear(m_spriteram.bytes() / 2); m_vidregs_old = std::make_unique(m_vidregs.bytes() / 2); m_palette_bank[0] = m_palette_bank[1] = 0; save_pointer(NAME(m_spriteram_old.get()), m_spriteram.bytes() / 2); save_pointer(NAME(m_vidregs_old.get()), m_vidregs.bytes() / 2); } WRITE_LINE_MEMBER(mcatadv_state::screen_vblank_mcatadv) { // rising edge if (state) { std::copy(&m_spriteram[0], &m_spriteram[m_spriteram.bytes() / 2], &m_spriteram_old[0]); std::copy(&m_vidregs[0], &m_vidregs[m_vidregs.bytes() / 2], &m_vidregs_old[0]); } }