// 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 <algorithm>
template<int Chip>
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<uint16_t[]>(m_spriteram.bytes() / 2);
m_vidregs_old = std::make_unique<uint16_t[]>(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]);
}
}