// license:BSD-3-Clause
// copyright-holders:Ernesto Corvi
/*******************************************************************
Namco System 1 Video Hardware
*******************************************************************/
#include "emu.h"
#include "includes/namcos1.h"
#include "screen.h"
/*
video ram map
0000-1fff : scroll playfield (0) : 64*64*2
2000-3fff : scroll playfield (1) : 64*64*2
4000-5fff : scroll playfield (2) : 64*64*2
6000-6fff : scroll playfield (3) : 64*32*2
7000-700f : ?
7010-77ef : fixed playfield (4) : 36*28*2
77f0-77ff : ?
7800-780f : ?
7810-7fef : fixed playfield (5) : 36*28*2
7ff0-7fff : ?
*/
/*
paletteram map (s1ram 0x0000-0x7fff)
0000-17ff : palette page0 : sprite
2000-37ff : palette page1 : playfield
4000-57ff : palette page2 : playfield (shadow)
6000-77ff : palette page3 : work RAM
x800-x807 : CUS116 registers (visibility window)
so there is just 3x0x2000 RAM, plus the CUS116 internal registers.
*/
/*
spriteram map (s1ram 0x10000-0x10fff)
0000-07ff : work ram
0800-0fef : sprite ram : 0x10 * 127
0ff0-0fff : display control registers
1000-1fff : playfield control registers
*/
/***************************************************************************
Callbacks for the TileMap code
***************************************************************************/
inline void namcos1_state::get_tile_info(tile_data &tileinfo,int tile_index,uint8_t *info_vram)
{
int code;
tile_index <<= 1;
code = info_vram[tile_index + 1] + ((info_vram[tile_index] & 0x3f) << 8);
SET_TILE_INFO_MEMBER(0,code,0,0);
tileinfo.mask_data = &m_tilemap_maskdata[code << 3];
}
TILE_GET_INFO_MEMBER(namcos1_state::bg_get_info0)
{
get_tile_info(tileinfo,tile_index,&m_videoram[0x0000]);
}
TILE_GET_INFO_MEMBER(namcos1_state::bg_get_info1)
{
get_tile_info(tileinfo,tile_index,&m_videoram[0x2000]);
}
TILE_GET_INFO_MEMBER(namcos1_state::bg_get_info2)
{
get_tile_info(tileinfo,tile_index,&m_videoram[0x4000]);
}
TILE_GET_INFO_MEMBER(namcos1_state::bg_get_info3)
{
get_tile_info(tileinfo,tile_index,&m_videoram[0x6000]);
}
TILE_GET_INFO_MEMBER(namcos1_state::fg_get_info4)
{
get_tile_info(tileinfo,tile_index,&m_videoram[0x7010]);
}
TILE_GET_INFO_MEMBER(namcos1_state::fg_get_info5)
{
get_tile_info(tileinfo,tile_index,&m_videoram[0x7810]);
}
/***************************************************************************
Start the video hardware emulation.
***************************************************************************/
void namcos1_state::video_start()
{
int i;
m_tilemap_maskdata = (uint8_t *)memregion("gfx1")->base();
/* initialize playfields */
m_bg_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(namcos1_state::bg_get_info0),this),TILEMAP_SCAN_ROWS,8,8,64,64);
m_bg_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(namcos1_state::bg_get_info1),this),TILEMAP_SCAN_ROWS,8,8,64,64);
m_bg_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(namcos1_state::bg_get_info2),this),TILEMAP_SCAN_ROWS,8,8,64,64);
m_bg_tilemap[3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(namcos1_state::bg_get_info3),this),TILEMAP_SCAN_ROWS,8,8,64,32);
m_bg_tilemap[4] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(namcos1_state::fg_get_info4),this),TILEMAP_SCAN_ROWS,8,8,36,28);
m_bg_tilemap[5] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(namcos1_state::fg_get_info5),this),TILEMAP_SCAN_ROWS,8,8,36,28);
for (i = 0; i < 4; i++)
{
static const int xdisp[] = { 25, 27, 28, 29 };
m_bg_tilemap[i]->set_scrolldx(xdisp[i], 434 - xdisp[i]);
m_bg_tilemap[i]->set_scrolldy(-8, 256 + 8);
}
m_bg_tilemap[4]->set_scrolldx(73, 73);
m_bg_tilemap[5]->set_scrolldx(73, 73);
m_bg_tilemap[4]->set_scrolldy(16, 16);
m_bg_tilemap[5]->set_scrolldy(16, 16);
/* set table for sprite color == 0x7f */
for (i = 0;i < 15;i++)
m_drawmode_table[i] = DRAWMODE_SHADOW;
m_drawmode_table[15] = DRAWMODE_NONE;
/* all palette entries are not affected by shadow sprites... */
for (i = 0;i < 0x2000;i++)
m_palette->shadow_table()[i] = i;
/* ... except for tilemap colors */
for (i = 0x0800;i < 0x1000;i++)
m_palette->shadow_table()[i] = i + 0x0800;
memset(m_playfield_control, 0, sizeof(m_playfield_control));
m_copy_sprites = 0;
save_item(NAME(m_copy_sprites));
}
/***************************************************************************
Memory handlers
***************************************************************************/
WRITE8_MEMBER( namcos1_state::videoram_w )
{
m_videoram[offset] = data;
if (offset < 0x7000)
{ /* background 0-3 */
int layer = offset >> 13;
int num = (offset & 0x1fff) >> 1;
m_bg_tilemap[layer]->mark_tile_dirty(num);
}
else
{ /* foreground 4-5 */
int layer = (offset >> 11 & 1) + 4;
int num = ((offset & 0x7ff) - 0x10) >> 1;
if (num >= 0 && num < 0x3f0)
m_bg_tilemap[layer]->mark_tile_dirty(num);
}
}
WRITE8_MEMBER( namcos1_state::spriteram_w )
{
/* 0000-07ff work ram */
/* 0800-0fff sprite ram */
m_spriteram[offset] = data;
/* a write to this offset tells the sprite chip to buffer the sprite list */
if (offset == 0x0ff2)
m_copy_sprites = 1;
}
/***************************************************************************
Display refresh
***************************************************************************/
/*
sprite format:
0-3 scratchpad RAM
4-9 CPU writes here, hardware copies from here to 10-15
10 xx------ X size (16, 8, 32, 4)
10 --x----- X flip
10 ---xx--- X offset inside 32x32 tile
10 -----xxx tile bank
11 xxxxxxxx tile number
12 xxxxxxx- color
12 -------x X position MSB
13 xxxxxxxx X position
14 xxx----- priority
14 ---xx--- Y offset inside 32x32 tile
14 -----xx- Y size (16, 8, 32, 4)
14 -------x Y flip
15 xxxxxxxx Y position
*/
void namcos1_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
uint8_t *spriteram = m_spriteram + 0x800;
const uint8_t *source = &spriteram[0x800-0x20]; /* the last is NOT a sprite */
const uint8_t *finish = &spriteram[0];
gfx_element *gfx = m_gfxdecode->gfx(1);
int sprite_xoffs = spriteram[0x07f5] + ((spriteram[0x07f4] & 1) << 8);
int sprite_yoffs = spriteram[0x07f7];
while (source >= finish)
{
static const int sprite_size[4] = { 16, 8, 32, 4 };
int attr1 = source[10];
int attr2 = source[14];
int color = source[12];
int flipx = (attr1 & 0x20) >> 5;
int flipy = (attr2 & 0x01);
int sizex = sprite_size[(attr1 & 0xc0) >> 6];
int sizey = sprite_size[(attr2 & 0x06) >> 1];
int tx = (attr1 & 0x18) & (~(sizex-1));
int ty = (attr2 & 0x18) & (~(sizey-1));
int sx = source[13] + ((color & 0x01) << 8);
int sy = -source[15] - sizey;
int sprite = source[11];
int sprite_bank = attr1 & 7;
int priority = (source[14] & 0xe0) >> 5;
int pri_mask = (0xff << (priority + 1)) & 0xff;
sprite += sprite_bank * 256;
color = color >> 1;
sx += sprite_xoffs;
sy -= sprite_yoffs;
if (flip_screen())
{
sx = -sx - sizex;
sy = -sy - sizey;
flipx ^= 1;
flipy ^= 1;
}
sy++; /* sprites are buffered and delayed by one scanline */
gfx->set_source_clip(tx, sizex, ty, sizey);
if (color != 0x7f)
gfx->prio_transpen(bitmap,cliprect,
sprite,
color,
flipx,flipy,
sx & 0x1ff,
((sy + 16) & 0xff) - 16,
screen.priority(), pri_mask,
0xf);
else
gfx->prio_transtable(bitmap,cliprect,
sprite,
color,
flipx,flipy,
sx & 0x1ff,
((sy + 16) & 0xff) - 16,
screen.priority(), pri_mask,
m_drawmode_table);
source -= 0x10;
}
}
uint32_t namcos1_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
int i, j, scrollx, scrolly, priority;
rectangle new_clip = cliprect;
/* flip screen is embedded in the sprite control registers */
flip_screen_set(m_spriteram[0x0ff6] & 1);
/* background color */
bitmap.fill(m_palette->black_pen(), cliprect);
/* berabohm uses asymmetrical visibility windows to iris on the character */
i = m_c116->get_reg(0) - 1; // min x
if (new_clip.min_x < i) new_clip.min_x = i;
i = m_c116->get_reg(1) - 1 - 1; // max x
if (new_clip.max_x > i) new_clip.max_x = i;
i = m_c116->get_reg(2) - 0x11; // min y
if (new_clip.min_y < i) new_clip.min_y = i;
i = m_c116->get_reg(3) - 0x11 - 1; // max y
if (new_clip.max_y > i) new_clip.max_y = i;
if (new_clip.empty())
return 0;
/* set palette base */
for (i = 0;i < 6;i++)
m_bg_tilemap[i]->set_palette_offset((m_playfield_control[i + 24] & 7) * 256);
for (i = 0;i < 4;i++)
{
j = i << 2;
scrollx = ( m_playfield_control[j+1] + (m_playfield_control[j+0]<<8) );
scrolly = ( m_playfield_control[j+3] + (m_playfield_control[j+2]<<8) );
if (flip_screen())
{
scrollx = -scrollx;
scrolly = -scrolly;
}
m_bg_tilemap[i]->set_scrollx(0,scrollx);
m_bg_tilemap[i]->set_scrolly(0,scrolly);
}
screen.priority().fill(0, new_clip);
/* bit 0-2 priority */
/* bit 3 disable */
for (priority = 0; priority < 8;priority++)
{
for (i = 0;i < 6;i++)
{
if (m_playfield_control[16 + i] == priority)
m_bg_tilemap[i]->draw(screen, bitmap, new_clip, 0,priority,0);
}
}
draw_sprites(screen, bitmap, new_clip);
return 0;
}
WRITE_LINE_MEMBER(namcos1_state::screen_vblank)
{
// rising edge
if (state)
{
if (m_copy_sprites)
{
uint8_t *spriteram = m_spriteram + 0x800;
int i,j;
for (i = 0;i < 0x800;i += 16)
{
for (j = 10;j < 16;j++)
spriteram[i+j] = spriteram[i+j - 6];
}
m_copy_sprites = 0;
}
}
}