/*******************************************************************
Namco System 1 Video Hardware
*******************************************************************/
#include "emu.h"
#include "includes/namcos1.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 bg_get_info(running_machine &machine,tile_data *tileinfo,int tile_index,UINT8 *info_vram)
{
namcos1_state *state = machine.driver_data<namcos1_state>();
int code;
tile_index <<= 1;
code = info_vram[tile_index + 1] + ((info_vram[tile_index] & 0x3f) << 8);
SET_TILE_INFO(0,code,0,0);
tileinfo->mask_data = &state->m_tilemap_maskdata[code << 3];
}
INLINE void fg_get_info(running_machine &machine,tile_data *tileinfo,int tile_index,UINT8 *info_vram)
{
namcos1_state *state = machine.driver_data<namcos1_state>();
int code;
tile_index <<= 1;
code = info_vram[tile_index + 1] + ((info_vram[tile_index] & 0x3f) << 8);
SET_TILE_INFO(0,code,0,0);
tileinfo->mask_data = &state->m_tilemap_maskdata[code << 3];
}
static TILE_GET_INFO( bg_get_info0 )
{
namcos1_state *state = machine.driver_data<namcos1_state>();
bg_get_info(machine,tileinfo,tile_index,&state->m_videoram[0x0000]);
}
static TILE_GET_INFO( bg_get_info1 )
{
namcos1_state *state = machine.driver_data<namcos1_state>();
bg_get_info(machine,tileinfo,tile_index,&state->m_videoram[0x2000]);
}
static TILE_GET_INFO( bg_get_info2 )
{
namcos1_state *state = machine.driver_data<namcos1_state>();
bg_get_info(machine,tileinfo,tile_index,&state->m_videoram[0x4000]);
}
static TILE_GET_INFO( bg_get_info3 )
{
namcos1_state *state = machine.driver_data<namcos1_state>();
bg_get_info(machine,tileinfo,tile_index,&state->m_videoram[0x6000]);
}
static TILE_GET_INFO( fg_get_info4 )
{
namcos1_state *state = machine.driver_data<namcos1_state>();
fg_get_info(machine,tileinfo,tile_index,&state->m_videoram[0x7010]);
}
static TILE_GET_INFO( fg_get_info5 )
{
namcos1_state *state = machine.driver_data<namcos1_state>();
fg_get_info(machine,tileinfo,tile_index,&state->m_videoram[0x7810]);
}
/***************************************************************************
Start the video hardware emulation.
***************************************************************************/
VIDEO_START( namcos1 )
{
namcos1_state *state = machine.driver_data<namcos1_state>();
int i;
state->m_tilemap_maskdata = (UINT8 *)machine.region("gfx1")->base();
/* allocate videoram */
state->m_videoram = auto_alloc_array(machine, UINT8, 0x8000);
state->m_spriteram = auto_alloc_array(machine, UINT8, 0x1000);
/* initialize playfields */
state->m_bg_tilemap[0] = tilemap_create(machine, bg_get_info0,tilemap_scan_rows,8,8,64,64);
state->m_bg_tilemap[1] = tilemap_create(machine, bg_get_info1,tilemap_scan_rows,8,8,64,64);
state->m_bg_tilemap[2] = tilemap_create(machine, bg_get_info2,tilemap_scan_rows,8,8,64,64);
state->m_bg_tilemap[3] = tilemap_create(machine, bg_get_info3,tilemap_scan_rows,8,8,64,32);
state->m_bg_tilemap[4] = tilemap_create(machine, fg_get_info4,tilemap_scan_rows,8,8,36,28);
state->m_bg_tilemap[5] = tilemap_create(machine, fg_get_info5,tilemap_scan_rows,8,8,36,28);
tilemap_set_scrolldx(state->m_bg_tilemap[4],73,512-73);
tilemap_set_scrolldx(state->m_bg_tilemap[5],73,512-73);
tilemap_set_scrolldy(state->m_bg_tilemap[4],0x10,0x110);
tilemap_set_scrolldy(state->m_bg_tilemap[5],0x10,0x110);
/* register videoram to the save state system (post-allocation) */
state_save_register_global_pointer(machine, state->m_videoram, 0x8000);
state_save_register_global_array(machine, state->m_cus116);
state_save_register_global_pointer(machine, state->m_spriteram, 0x1000);
state_save_register_global_array(machine, state->m_playfield_control);
/* set table for sprite color == 0x7f */
for (i = 0;i < 15;i++)
state->m_drawmode_table[i] = DRAWMODE_SHADOW;
state->m_drawmode_table[15] = DRAWMODE_NONE;
/* clear paletteram */
memset(state->m_paletteram, 0, 0x8000);
memset(state->m_cus116, 0, 0x10);
for (i = 0; i < 0x2000; i++)
palette_set_color(machine, i, MAKE_RGB(0, 0, 0));
/* all palette entries are not affected by shadow sprites... */
for (i = 0;i < 0x2000;i++)
machine.shadow_table[i] = i;
/* ... except for tilemap colors */
for (i = 0x0800;i < 0x1000;i++)
machine.shadow_table[i] = i + 0x0800;
memset(state->m_playfield_control, 0, sizeof(state->m_playfield_control));
state->m_copy_sprites = 0;
}
/***************************************************************************
Memory handlers
***************************************************************************/
READ8_HANDLER( namcos1_videoram_r )
{
namcos1_state *state = space->machine().driver_data<namcos1_state>();
return state->m_videoram[offset];
}
WRITE8_HANDLER( namcos1_videoram_w )
{
namcos1_state *state = space->machine().driver_data<namcos1_state>();
state->m_videoram[offset] = data;
if (offset < 0x7000)
{ /* background 0-3 */
int layer = offset >> 13;
int num = (offset & 0x1fff) >> 1;
tilemap_mark_tile_dirty(state->m_bg_tilemap[layer],num);
}
else
{ /* foreground 4-5 */
int layer = (offset >> 11 & 1) + 4;
int num = ((offset & 0x7ff) - 0x10) >> 1;
if (num >= 0 && num < 0x3f0)
tilemap_mark_tile_dirty(state->m_bg_tilemap[layer],num);
}
}
WRITE8_HANDLER( namcos1_paletteram_w )
{
namcos1_state *state = space->machine().driver_data<namcos1_state>();
if (state->m_paletteram[offset] == data)
return;
if ((offset & 0x1800) != 0x1800)
{
int r,g,b;
int color = ((offset & 0x6000) >> 2) | (offset & 0x7ff);
state->m_paletteram[offset] = data;
offset &= ~0x1800;
r = state->m_paletteram[offset];
g = state->m_paletteram[offset + 0x0800];
b = state->m_paletteram[offset + 0x1000];
palette_set_color(space->machine(),color,MAKE_RGB(r,g,b));
}
else
{
int i, j;
state->m_cus116[offset & 0x0f] = data;
for (i = 0x1800; i < 0x8000; i += 0x2000)
{
offset = (offset & 0x0f) | i;
for (j = 0; j < 0x80; j++, offset += 0x10)
state->m_paletteram[offset] = data;
}
}
}
READ8_HANDLER( namcos1_spriteram_r )
{
namcos1_state *state = space->machine().driver_data<namcos1_state>();
/* 0000-07ff work ram */
/* 0800-0fff sprite ram */
if (offset < 0x1000)
return state->m_spriteram[offset];
/* 1xxx playfield control ram */
else
return state->m_playfield_control[offset & 0x1f];
}
WRITE8_HANDLER( namcos1_spriteram_w )
{
namcos1_state *state = space->machine().driver_data<namcos1_state>();
/* 0000-07ff work ram */
/* 0800-0fff sprite ram */
if (offset < 0x1000)
{
state->m_spriteram[offset] = data;
/* a write to this offset tells the sprite chip to buffer the sprite list */
if (offset == 0x0ff2)
state->m_copy_sprites = 1;
}
/* 1xxx playfield control ram */
else
state->m_playfield_control[offset & 0x1f] = data;
}
/***************************************************************************
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
*/
static void draw_sprites(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect)
{
namcos1_state *state = machine.driver_data<namcos1_state>();
UINT8 *spriteram = state->m_spriteram + 0x800;
const UINT8 *source = &spriteram[0x800-0x20]; /* the last is NOT a sprite */
const UINT8 *finish = &spriteram[0];
gfx_element *gfx = machine.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_get(machine))
{
sx = -sx - sizex;
sy = -sy - sizey;
flipx ^= 1;
flipy ^= 1;
}
sy++; /* sprites are buffered and delayed by one scanline */
gfx_element_set_source_clip(gfx, tx, sizex, ty, sizey);
if (color != 0x7f)
pdrawgfx_transpen( bitmap, cliprect, gfx,
sprite,
color,
flipx,flipy,
sx & 0x1ff,
((sy + 16) & 0xff) - 16,
machine.priority_bitmap, pri_mask,
0xf);
else
pdrawgfx_transtable( bitmap, cliprect, gfx,
sprite,
color,
flipx,flipy,
sx & 0x1ff,
((sy + 16) & 0xff) - 16,
machine.priority_bitmap, pri_mask,
state->m_drawmode_table, machine.shadow_table);
source -= 0x10;
}
}
SCREEN_UPDATE( namcos1 )
{
namcos1_state *state = screen->machine().driver_data<namcos1_state>();
int i, j, scrollx, scrolly, priority;
rectangle new_clip = *cliprect;
/* flip screen is embedded in the sprite control registers */
/* can't use flip_screen_set(screen->machine(), ) because the visible area is asymmetrical */
flip_screen_set_no_update(screen->machine(), state->m_spriteram[0x0ff6] & 1);
tilemap_set_flip_all(screen->machine(),flip_screen_get(screen->machine()) ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0);
/* background color */
bitmap_fill(bitmap, cliprect, get_black_pen(screen->machine()));
/* berabohm uses asymmetrical visibility windows to iris on the character */
i = ((state->m_cus116[0] << 8) | state->m_cus116[1]) - 1; // min x
if (new_clip.min_x < i) new_clip.min_x = i;
i = ((state->m_cus116[2] << 8) | state->m_cus116[3]) - 1 - 1; // max x
if (new_clip.max_x > i) new_clip.max_x = i;
i = ((state->m_cus116[4] << 8) | state->m_cus116[5]) - 0x11; // min y
if (new_clip.min_y < i) new_clip.min_y = i;
i = ((state->m_cus116[6] << 8) | state->m_cus116[7]) - 0x11 - 1; // max y
if (new_clip.max_y > i) new_clip.max_y = i;
if (new_clip.max_x < new_clip.min_x || new_clip.max_y < new_clip.min_y)
return 0;
/* set palette base */
for (i = 0;i < 6;i++)
tilemap_set_palette_offset(state->m_bg_tilemap[i],(state->m_playfield_control[i + 24] & 7) * 256);
for (i = 0;i < 4;i++)
{
static const int disp_x[] = { 25, 27, 28, 29 };
j = i << 2;
scrollx = ( state->m_playfield_control[j+1] + (state->m_playfield_control[j+0]<<8) ) - disp_x[i];
scrolly = ( state->m_playfield_control[j+3] + (state->m_playfield_control[j+2]<<8) ) + 8;
if (flip_screen_get(screen->machine()))
{
scrollx = -scrollx;
scrolly = -scrolly;
}
tilemap_set_scrollx(state->m_bg_tilemap[i],0,scrollx);
tilemap_set_scrolly(state->m_bg_tilemap[i],0,scrolly);
}
bitmap_fill(screen->machine().priority_bitmap, &new_clip, 0);
/* bit 0-2 priority */
/* bit 3 disable */
for (priority = 0; priority < 8;priority++)
{
for (i = 0;i < 6;i++)
{
if (state->m_playfield_control[16 + i] == priority)
tilemap_draw_primask(bitmap,&new_clip,state->m_bg_tilemap[i],0,priority,0);
}
}
draw_sprites(screen->machine(), bitmap, &new_clip);
return 0;
}
SCREEN_EOF( namcos1 )
{
namcos1_state *state = machine.driver_data<namcos1_state>();
if (state->m_copy_sprites)
{
UINT8 *spriteram = state->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];
}
state->m_copy_sprites = 0;
}
}