/***************************************************************************
Sega Zaxxon hardware
***************************************************************************/
#include "emu.h"
#include "video/resnet.h"
#include "includes/zaxxon.h"
/*************************************
*
* Palette conversion
*
*************************************/
PALETTE_INIT( zaxxon )
{
zaxxon_state *state = machine.driver_data<zaxxon_state>();
static const int resistances[3] = { 1000, 470, 220 };
double rweights[3], gweights[3], bweights[2];
int i;
/* compute the color output resistor weights */
compute_resistor_weights(0, 255, -1.0,
3, &resistances[0], rweights, 470, 0,
3, &resistances[0], gweights, 470, 0,
2, &resistances[1], bweights, 470, 0);
/* initialize the palette with these colors */
for (i = 0; i < machine.total_colors(); i++)
{
int bit0, bit1, bit2;
int r, g, b;
/* red component */
bit0 = (color_prom[i] >> 0) & 0x01;
bit1 = (color_prom[i] >> 1) & 0x01;
bit2 = (color_prom[i] >> 2) & 0x01;
r = combine_3_weights(rweights, bit0, bit1, bit2);
/* green component */
bit0 = (color_prom[i] >> 3) & 0x01;
bit1 = (color_prom[i] >> 4) & 0x01;
bit2 = (color_prom[i] >> 5) & 0x01;
g = combine_3_weights(gweights, bit0, bit1, bit2);
/* blue component */
bit0 = (color_prom[i] >> 6) & 0x01;
bit1 = (color_prom[i] >> 7) & 0x01;
b = combine_2_weights(bweights, bit0, bit1);
palette_set_color(machine, i, MAKE_RGB(r, g, b));
}
/* color_prom now points to the beginning of the character color codes */
state->m_color_codes = &color_prom[256];
}
/*************************************
*
* Tilemap callbacks
*
*************************************/
static TILE_GET_INFO( get_bg_tile_info )
{
const UINT8 *source = machine.region("tilemap_dat")->base();
int size = machine.region("tilemap_dat")->bytes() / 2;
int eff_index = tile_index & (size - 1);
int code = source[eff_index] + 256 * (source[eff_index + size] & 3);
int color = source[eff_index + size] >> 4;
SET_TILE_INFO(1, code, color, 0);
}
static TILE_GET_INFO( zaxxon_get_fg_tile_info )
{
zaxxon_state *state = machine.driver_data<zaxxon_state>();
int sx = tile_index % 32;
int sy = tile_index / 32;
int code = state->m_videoram[tile_index];
int color = state->m_color_codes[sx + 32 * (sy / 4)] & 0x0f;
SET_TILE_INFO(0, code, color * 2, 0);
}
static TILE_GET_INFO( razmataz_get_fg_tile_info )
{
zaxxon_state *state = machine.driver_data<zaxxon_state>();
int code = state->m_videoram[tile_index];
int color = state->m_color_codes[code] & 0x0f;
SET_TILE_INFO(0, code, color * 2, 0);
}
static TILE_GET_INFO( congo_get_fg_tile_info )
{
zaxxon_state *state = machine.driver_data<zaxxon_state>();
int code = state->m_videoram[tile_index] + (state->m_congo_fg_bank << 8);
int color = state->m_colorram[tile_index] & 0x1f;
SET_TILE_INFO(0, code, color * 2, 0);
}
/*************************************
*
* Video startup
*
*************************************/
static void video_start_common(running_machine &machine, tile_get_info_func fg_tile_info)
{
zaxxon_state *state = machine.driver_data<zaxxon_state>();
/* reset globals */
state->m_bg_enable = 0;
state->m_bg_color = 0;
state->m_bg_position = 0;
state->m_fg_color = 0;
state->m_congo_fg_bank = 0;
state->m_congo_color_bank = 0;
memset(state->m_congo_custom, 0, sizeof(state->m_congo_custom));
/* create a background and foreground tilemap */
state->m_bg_tilemap = tilemap_create(machine, get_bg_tile_info, tilemap_scan_rows, 8,8, 32,512);
state->m_fg_tilemap = tilemap_create(machine, fg_tile_info, tilemap_scan_rows, 8,8, 32,32);
/* configure the foreground tilemap */
tilemap_set_transparent_pen(state->m_fg_tilemap, 0);
tilemap_set_scrolldx(state->m_fg_tilemap, 0, machine.primary_screen->width() - 256);
tilemap_set_scrolldy(state->m_fg_tilemap, 0, machine.primary_screen->height() - 256);
/* register for save states */
state->save_item(NAME(state->m_bg_enable));
state->save_item(NAME(state->m_bg_color));
state->save_item(NAME(state->m_bg_position));
state->save_item(NAME(state->m_fg_color));
}
VIDEO_START( zaxxon )
{
video_start_common(machine, zaxxon_get_fg_tile_info);
}
VIDEO_START( razmataz )
{
video_start_common(machine, razmataz_get_fg_tile_info);
}
VIDEO_START( congo )
{
zaxxon_state *state = machine.driver_data<zaxxon_state>();
/* allocate our own spriteram since it is not accessible by the main CPU */
state->m_spriteram = auto_alloc_array(machine, UINT8, 0x100);
/* register for save states */
state->save_item(NAME(state->m_congo_fg_bank));
state->save_item(NAME(state->m_congo_color_bank));
state->save_item(NAME(state->m_congo_custom));
state->save_pointer(NAME(state->m_spriteram), 0x100);
video_start_common(machine, congo_get_fg_tile_info);
}
/*************************************
*
* Video latches and controls
*
*************************************/
WRITE8_HANDLER( zaxxon_flipscreen_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
/* low bit controls flip; background and sprite flip are handled at render time */
flip_screen_set_no_update(space->machine(), ~data & 1);
tilemap_set_flip(state->m_fg_tilemap, flip_screen_get(space->machine()) ? (TILEMAP_FLIPX | TILEMAP_FLIPY) : 0);
}
WRITE8_HANDLER( zaxxon_fg_color_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
/* low bit selects high color palette index */
state->m_fg_color = (data & 1) * 0x80;
tilemap_set_palette_offset(state->m_fg_tilemap, state->m_fg_color + (state->m_congo_color_bank << 8));
}
WRITE8_HANDLER( zaxxon_bg_position_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
/* 11 bits of scroll position are stored */
if (offset == 0)
state->m_bg_position = (state->m_bg_position & 0x700) | ((data << 0) & 0x0ff);
else
state->m_bg_position = (state->m_bg_position & 0x0ff) | ((data << 8) & 0x700);
}
WRITE8_HANDLER( zaxxon_bg_color_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
/* low bit selects high color palette index */
state->m_bg_color = (data & 1) * 0x80;
}
WRITE8_HANDLER( zaxxon_bg_enable_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
/* low bit enables/disables the background layer */
state->m_bg_enable = data & 1;
}
WRITE8_HANDLER( congo_fg_bank_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
/* low bit controls the topmost character bit */
state->m_congo_fg_bank = data & 1;
tilemap_mark_all_tiles_dirty(state->m_fg_tilemap);
}
WRITE8_HANDLER( congo_color_bank_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
/* low bit controls the topmost bit into the color PROM */
state->m_congo_color_bank = data & 1;
tilemap_set_palette_offset(state->m_fg_tilemap, state->m_fg_color + (state->m_congo_color_bank << 8));
}
/*************************************
*
* Foreground tilemap access
*
*************************************/
WRITE8_HANDLER( zaxxon_videoram_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
state->m_videoram[offset] = data;
tilemap_mark_tile_dirty(state->m_fg_tilemap, offset);
}
WRITE8_HANDLER( congo_colorram_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
state->m_colorram[offset] = data;
tilemap_mark_tile_dirty(state->m_fg_tilemap, offset);
}
/*************************************
*
* Congo Bongo custom sprite DMA
*
*************************************/
WRITE8_HANDLER( congo_sprite_custom_w )
{
zaxxon_state *state = space->machine().driver_data<zaxxon_state>();
UINT8 *spriteram = state->m_spriteram;
state->m_congo_custom[offset] = data;
/* seems to trigger on a write of 1 to the 4th byte */
if (offset == 3 && data == 0x01)
{
UINT16 saddr = state->m_congo_custom[0] | (state->m_congo_custom[1] << 8);
int count = state->m_congo_custom[2];
/* count cycles (just a guess) */
device_adjust_icount(&space->device(), -count * 5);
/* this is just a guess; the chip is hardwired to the spriteram */
while (count-- >= 0)
{
UINT8 daddr = space->read_byte(saddr + 0) * 4;
spriteram[(daddr + 0) & 0xff] = space->read_byte(saddr + 1);
spriteram[(daddr + 1) & 0xff] = space->read_byte(saddr + 2);
spriteram[(daddr + 2) & 0xff] = space->read_byte(saddr + 3);
spriteram[(daddr + 3) & 0xff] = space->read_byte(saddr + 4);
saddr += 0x20;
}
}
}
/*************************************
*
* Background pixmap drawing
*
*************************************/
static void draw_background(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect, int skew)
{
zaxxon_state *state = machine.driver_data<zaxxon_state>();
/* only draw if enabled */
if (state->m_bg_enable)
{
bitmap_t *pixmap = tilemap_get_pixmap(state->m_bg_tilemap);
int colorbase = state->m_bg_color + (state->m_congo_color_bank << 8);
int xmask = pixmap->width - 1;
int ymask = pixmap->height - 1;
int flipmask = flip_screen_get(machine) ? 0xff : 0x00;
int flipoffs = flip_screen_get(machine) ? 0x38 : 0x40;
int x, y;
/* the starting X value is offset by 1 pixel (normal) or 7 pixels */
/* (flipped) due to a delay in the loading */
if (!flip_screen_get(machine))
flipoffs -= 1;
else
flipoffs += 7;
/* loop over visible rows */
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
{
UINT16 *dst = (UINT16 *)bitmap->base + y * bitmap->rowpixels;
int srcx, srcy, vf;
UINT16 *src;
/* VF = flipped V signals */
vf = y ^ flipmask;
/* base of the source row comes from VF plus the scroll value */
/* this is done by the 3 4-bit adders at U56, U74, U75 */
srcy = vf + ((state->m_bg_position << 1) ^ 0xfff) + 1;
src = (UINT16 *)pixmap->base + (srcy & ymask) * pixmap->rowpixels;
/* loop over visible colums */
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
{
/* start with HF = flipped H signals */
srcx = x ^ flipmask;
if (skew)
{
/* position within source row is a two-stage addition */
/* first stage is HF plus half the VF, done by the 2 4-bit */
/* adders at U53, U54 */
srcx += ((vf >> 1) ^ 0xff) + 1;
/* second stage is first stage plus a constant based on the flip */
/* value is 0x40 for non-flipped, or 0x38 for flipped */
srcx += flipoffs;
}
/* store the pixel, offset by the color offset */
dst[x] = src[srcx & xmask] + colorbase;
}
}
}
/* if not enabled, fill the background with black */
else
bitmap_fill(bitmap, cliprect, get_black_pen(machine));
}
/*************************************
*
* Sprite drawing
*
*************************************/
INLINE int find_minimum_y(UINT8 value, int flip)
{
int flipmask = flip ? 0xff : 0x00;
int flipconst = flip ? 0xef : 0xf1;
int y;
/* the sum of the Y position plus a constant based on the flip state */
/* is added to the current flipped VF; if the top 3 bits are 1, we hit */
/* first find a 16-pixel bucket where we hit */
for (y = 0; y < 256; y += 16)
{
int sum = (value + flipconst + 1) + (y ^ flipmask);
if ((sum & 0xe0) == 0xe0)
break;
}
/* then scan backwards until we no longer match */
while (1)
{
int sum = (value + flipconst + 1) + ((y - 1) ^ flipmask);
if ((sum & 0xe0) != 0xe0)
break;
y--;
}
/* add one line since we draw sprites on the previous line */
return (y + 1) & 0xff;
}
INLINE int find_minimum_x(UINT8 value, int flip)
{
int flipmask = flip ? 0xff : 0x00;
int x;
/* the sum of the X position plus a constant specifies the address within */
/* the line bufer; if we're flipped, we will write backwards */
x = (value + 0xef + 1) ^ flipmask;
if (flipmask)
x -= 31;
return x & 0xff;
}
static void draw_sprites(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect, UINT16 flipxmask, UINT16 flipymask)
{
zaxxon_state *state = machine.driver_data<zaxxon_state>();
UINT8 *spriteram = state->m_spriteram;
const gfx_element *gfx = machine.gfx[2];
int flip = flip_screen_get(machine);
int flipmask = flip ? 0xff : 0x00;
int offs;
/* only the lower half of sprite RAM is read during rendering */
for (offs = 0x7c; offs >= 0; offs -= 4)
{
int sy = find_minimum_y(spriteram[offs], flip);
int flipy = (spriteram[offs + (flipymask >> 8)] ^ flipmask) & flipymask;
int flipx = (spriteram[offs + (flipxmask >> 8)] ^ flipmask) & flipxmask;
int code = spriteram[offs + 1];
int color = (spriteram[offs + 2] & 0x1f) + (state->m_congo_color_bank << 5);
int sx = find_minimum_x(spriteram[offs + 3], flip);
/* draw with 256 pixel offsets to ensure we wrap properly */
drawgfx_transpen(bitmap, cliprect, gfx, code, color, flipx, flipy, sx, sy, 0);
drawgfx_transpen(bitmap, cliprect, gfx, code, color, flipx, flipy, sx, sy - 0x100, 0);
drawgfx_transpen(bitmap, cliprect, gfx, code, color, flipx, flipy, sx - 0x100, sy, 0);
drawgfx_transpen(bitmap, cliprect, gfx, code, color, flipx, flipy, sx - 0x100, sy - 0x100, 0);
}
}
/*************************************
*
* Core video updates
*
*************************************/
SCREEN_UPDATE( zaxxon )
{
zaxxon_state *state = screen->machine().driver_data<zaxxon_state>();
draw_background(screen->machine(), bitmap, cliprect, TRUE);
draw_sprites(screen->machine(), bitmap, cliprect, 0x140, 0x180);
tilemap_draw(bitmap, cliprect, state->m_fg_tilemap, 0, 0);
return 0;
}
SCREEN_UPDATE( futspy )
{
zaxxon_state *state = screen->machine().driver_data<zaxxon_state>();
draw_background(screen->machine(), bitmap, cliprect, TRUE);
draw_sprites(screen->machine(), bitmap, cliprect, 0x180, 0x180);
tilemap_draw(bitmap, cliprect, state->m_fg_tilemap, 0, 0);
return 0;
}
SCREEN_UPDATE( razmataz )
{
zaxxon_state *state = screen->machine().driver_data<zaxxon_state>();
draw_background(screen->machine(), bitmap, cliprect, FALSE);
draw_sprites(screen->machine(), bitmap, cliprect, 0x140, 0x180);
tilemap_draw(bitmap, cliprect, state->m_fg_tilemap, 0, 0);
return 0;
}
SCREEN_UPDATE( congo )
{
zaxxon_state *state = screen->machine().driver_data<zaxxon_state>();
draw_background(screen->machine(), bitmap, cliprect, TRUE);
draw_sprites(screen->machine(), bitmap, cliprect, 0x280, 0x180);
tilemap_draw(bitmap, cliprect, state->m_fg_tilemap, 0, 0);
return 0;
}