// license:BSD-3-Clause
// copyright-holders:Luca Elia, Roberto Zandona, David Haywood
/***************************************************************************
-= Seta, Sammy, Visco (SSV) System =-
driver by Luca Elia (l.elia@tin.it)
This hardware only generates sprites. But they're of various types,
including some large "floating tilemap" ones.
Sprites RAM is 0x40000 bytes long. The first 0x2000 bytes hold a list of
sprites to display (the list can be made shorter using an end-of-list marker).
Each entry in the list (8 bytes) is a multi-sprite (e.g it tells the
hardware to display up to 32 single-sprites).
The list looks like this:
Offset: Bits: Value:
0.h f--- ---- ---- ---- Shadow
-edc ---- ---- ---- Each bit enables 2 bitplanes*
---- ba-- ---- ---- X Size (1,2,4,8 tiles)
---- --98 ---- ---- Y Size (1,2,4,8 tiles)
---- ---- 765- ---- Index of a scroll to apply to the single-sprite(s)
---- ---- ---4 3210 Number of single-sprites, minus 1
2.h f--- ---- ---- ---- List end
-edc ba98 7654 3210 Offset of the single-sprite(s) data
4.h fedc ba-- ---- ----
---- --98 7654 3210 X displacement (ignored by tilemap sprites?)
6.h fedc ba-- ---- ----
---- --98 7654 3210 Y displacement (ignored by tilemap sprites?)
* bit c, which enables/disables the 2 high order bitplanes (256 / 64 color tiles)
is needed by keithlcy (logo), drifto94 (wheels).
eaglshot masks even more bits, needed for 'birdie' text etc.
A single-sprite can be:
1. a rectangle of tiles (only 1 tile code needs to be specified)
2. a row of tiles of a tilemap in ram. The row is (always?) as wide
as the screen and 64 pixels tall.
Rectangle case(1):
Offset: Bits: Value:
0.h Code (low bits)
2.h f--- ---- ---- ---- Flip X
-e-- ---- ---- ---- Flip Y
--dc ba-- ---- ---- Code (high bits)
---- --9- ---- ---- Code? Color?
---- ---8 7654 3210 Color code (64 color steps)
4.h f--- ---- ---- ---- Shadow
-edc ---- ---- ---- Each bit enables 2 bitplanes*
---- ba-- ---- ---- X Size (1,2,4,8 tiles)
---- --98 7654 3210 X
6.h fedc ---- ---- ----
---- ba-- ---- ---- Y Size (1,2,4 tiles) **
---- --98 7654 3210 Y
Tilemap case(2):
Offset: Bits: Value:
0.h fedc ba98 7654 3---
---- ---- ---- -210 Scroll index (see below)
2.h Always 0
4.h fedc ba-- ---- ----
---- --98 7654 3210 X?
6.h fedc ---- ---- ----
---- ba-- ---- ---- **
---- --98 7654 3210 Y
** ? both bits set means "Row Sprite" if the single-sprite type hasn't been
specified in the sprites list ?
There are 8 scroll values for the tilemap sprites, in the
1c0000-1c003f area (each scroll value uses 8 bytes):
Offset: Bits: Value:
0.h Scroll X
2.h Scroll Y
4.h Priority ? (0000, 0401, 0440, 057f, 05ff) (seems to control offsets too)
6.h fed- ---- ---- ---- Tilemap width (games only use 1 -> $200, 2 -> $400)
---c ---- ---- ---- Rowscroll enable
---- b--- ---- ---- Shadow mode
---- -a98 ---- ---- Each bit enables 2 bitplanes*
---- ---- 7654 3210 Rowscroll base (multiply by 0x400)
Where scroll x&y refer to a virtual $8000 x $200 tilemap (filling the
whole spriteram) made of 16x16 tiles. A tile uses 4 bytes:
Offset: Bits: Value:
0.h Code (low bits)***
2.h f--- ---- ---- ---- Flip X
-e-- ---- ---- ---- Flip Y
--dc ba-- ---- ---- Code (high bits)
---- --9- ---- ---- Code? Color?
---- ---8 7654 3210 Color code (64 color steps)
The tilemap is stored in ram by column.
*** The tiles size is (always?) 16x16 so Code and Code+1 are used.
Note that there is a "background layer": a series of tilemap sprites
that fill up the screen and use scroll 0 as the source tilemap are
always displayed before the sprites in the sprites list.
Shadows:
The low bits of the pens from a "shadowing" tile (regardless of color code)
substitute the top bits of the color index (0-7fff) in the frame buffer.
The number of low bits from the "shadowing tile" is 4 or 2, depending on
bit 7 of 1c0076.
***************************************************************************/
#include "emu.h"
#include "includes/ssv.h"
#include "render.h"
void ssv_state::drawgfx_line(bitmap_ind16 &bitmap, const rectangle &cliprect, int gfx, uint32_t code, uint32_t color, int flipx, int flipy, int base_sx, int base_sy, int shadow, int realline, int line)
{
gfx_element *gfxelement = m_gfxdecode->gfx(0);
const uint8_t *const addr = gfxelement->get_data(code % gfxelement->elements());
const uint32_t realcolor = gfxelement->granularity() * (color % gfxelement->colors());
const uint8_t *const source = flipy ? addr + (7 - line) * gfxelement->rowbytes() : addr + line * gfxelement->rowbytes();
if (realline >= cliprect.min_y && realline <= cliprect.max_y)
{
struct drawmodes
{
int gfx_mask;
int gfx_shift;
};
// comments at top suggest that each bit of 'gfx' enables 2 bitplanes, but ultrax case disagrees, also that would require 4 bits to cover all cases, and we only have 3
// see also seta2.cpp where the same logic is used
static constexpr drawmodes BPP_MASK_TABLE[8] = {
{ 0x3f,0 }, // 0: ultrax, twineag2 text - is there a local / global mixup somewhere, or is this an 'invalid' setting that just enables all planes?
{ 0xff,0 }, // 1: unverified case, mimic old driver behavior of only using lowest bit
{ 0x3f,0 }, // 2: unverified case, mimic old driver behavior of only using lowest bit
{ 0xff,0 }, // 3: unverified case, mimic old driver behavior of only using lowest bit (pastelis sets this on shadows, but seems to need behavior beyond what we currently emulate, maybe also changes number of shadow bits in addition to global shadow mask/shift?)
{ 0x0f,0 }, // 4: eagle shot 4bpp birdie text
{ 0xf0,4 }, // 5: eagle shot 4bpp Japanese text
{ 0x3f,0 }, // 6: common 6bpp case + keithlcy (logo), drifto94 (wheels) masking
{ 0xff,0 } // 7: common 8bpp case
};
const uint8_t gfxbppmask = BPP_MASK_TABLE[gfx & 0x07].gfx_mask;
const uint8_t gfxshift = BPP_MASK_TABLE[gfx & 0x07].gfx_shift;
uint16_t *dest = &bitmap.pix16(realline);
const int x0 = flipx ? (base_sx + gfxelement->width() - 1) : base_sx;
const int x1 = flipx ? (base_sx - 1) : (x0 + gfxelement->width());
const int dx = flipx ? -1 : 1;
int column = 0;
for (int sx = x0; sx != x1; sx += dx)
{
const uint8_t pen = (source[column] & gfxbppmask) >> gfxshift;
if (pen && sx >= cliprect.min_x && sx <= cliprect.max_x)
{
if (shadow)
dest[sx] = ((dest[sx] & m_shadow_pen_mask) | (pen << m_shadow_pen_shift)) & 0x7fff;
else
dest[sx] = (realcolor + pen) & 0x7fff;
}
column++;
}
}
}
void ssv_state::drawgfx(bitmap_ind16 &bitmap, const rectangle &cliprect, int gfx, uint32_t code, uint32_t color, int flipx, int flipy, int base_sx, int base_sy, int shadow)
{
for (int line = 0; line < 8; line++)
{
drawgfx_line(bitmap, cliprect, gfx, code, color, flipx, flipy, base_sx, base_sy, shadow, base_sy+line, line);
}
}
void ssv_state::video_start()
{
m_gfxdecode->gfx(0)->set_granularity(64); /* 256 colour sprites with palette selectable on 64 colour boundaries */
save_item(NAME(m_enable_video));
save_item(NAME(m_shadow_pen_mask));
save_item(NAME(m_shadow_pen_shift));
}
VIDEO_START_MEMBER(ssv_state,eaglshot)
{
ssv_state::video_start();
m_eaglshot_gfxram = std::make_unique<uint16_t[]>(16 * 0x40000 / 2);
m_gfxdecode->gfx(0)->set_source((uint8_t *)m_eaglshot_gfxram.get());
save_pointer(NAME(m_eaglshot_gfxram), 16 * 0x40000 / 2);
}
TILE_GET_INFO_MEMBER(ssv_state::get_tile_info_0)
{
uint16_t tile = m_gdfs_tmapram[tile_index];
SET_TILE_INFO_MEMBER(1, tile, 0, TILE_FLIPXY( tile >> 14 ));
}
WRITE16_MEMBER(ssv_state::gdfs_tmapram_w)
{
COMBINE_DATA(&m_gdfs_tmapram[offset]);
m_gdfs_tmap->mark_tile_dirty(offset);
}
VIDEO_START_MEMBER(ssv_state,gdfs)
{
ssv_state::video_start();
m_gdfs_tmap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(ssv_state::get_tile_info_0)), TILEMAP_SCAN_ROWS, 16,16, 0x100,0x100);
m_gdfs_tmap->set_transparent_pen(0);
}
/***************************************************************************
CRT controller, registers that are read
(vblank etc.?)
1c0000 (wait for bit .. to become ..)
keithlcy: bit D, 0 -> 1
mslider: bit A, 0
hypreact:
meosism:
srmp7:
sxyreact:
ultrax: bit F, 0
twineag2:
hypreac2: bit C, 1 -> 0
bit F, 0
janjans1:
srmp4:
survarts: no checks
ryorioh:
drifto94: bit D, 0 -> 1
bit A, 0
CRT controller, registers that are written
(resolution, visible area, flipping etc.)
1c0060-61 ---- ---- ---- ---- ? 21 or 2b for all games
1c0062-63 fedc ba98 7654 3210 x start visible area
1c0064-65 fedc ba98 7654 3210 x end visible area
1c0066-67 ---- ---- ---- ---- ? 1c6 for all games
1c0068-69 ---- ---- ---- ---- ? 1 for all games
1c006a-6b fedc ba98 7654 3210 y start visible area
1c006c-6d fedc ba98 7654 3210 y end visible area
1c006e-6f ---- ---- ---- ---- ? 106 for all games
1c0070-71 fedc ba98 7654 3210 signed y global tilemap x offset
1c0072-73 ---- ---- ---- ---- ?
1c0074-75 ---- ---- ---- ---- ?
-e-- ---- ---- ---- y flipscreen
---c ---- ---- ---- x flipscreen
---- ba98 ---- ---- ? 0101 for all games
---- ---- 7654 3210 signed global tilemap x offset
1c0076-77 -e-- ---- ---- ---- global/local sprites coordinates
---- ---- 7--- ---- shadow (0 = 2 bits, 1 = 4 bits)
1c0078-79 ---- ---- ---- ---- ?
1c007a-7b ---- ---- ---- ---- ?
---- b--- ---- ---- sprite coordinate mode
1c0060-7f:
drifto94: 0000 0025 00cd 01c6 - 0001 0013 0101 0106
0300 0711 0500 0000 - 0015 5940
03ea 5558 (flip)
dynagear: 002b 002c 00d4 01c6 - 0001 0012 0102 0106
02fd 0000 0500 0000 - 0015 5940
03ed 5558 (flip)
eaglshot: 0021 002a 00ca 01c6 - 0001 0016 00f6 0106
0301 0000 0500 d000 - 0015 5940
03f1 5560 d00f (flip)
gdfs: 002b 002c 00d5 01c6 - 0001 0012 0102 0106
03ec 0711 0500 0000 - 00d5 5950
03ec 1557 (flip)
hypreact: 0021 0022 00cb 01c6 - 0001 000e 00fe 0106
0301 0000 0500 c000 - 0015 5140
03f0 5558 (flip)
hypreac2: 0021 0022 00cb 01c6 - 0001 000e 00fe 0106
0301 0000 05ff c000 - 0015 5140
03ea 5558 (flip)
janjans1: 0021 0023 00cb 01c6 - 0001 000f 00fe 0106
0300 0000 0500 c000 - 0015 5140
0300 0500 (flip)
keithlcy: 002b 0025 00cd 01c6 - 0001 0013 0101 0106
0300 0711 0500 0000 - 0015 5940
03ea 5558 (flip)
meosism: 002b 002c 00d5 01c6 - 0001 0012 00fe 0106
0301 0000 0500 c000 - 0015 5140
(no flip)
mslider: 0021 0026 00d6 01c6 - 0001 000e 00fe 0106
03f1 0711 5550 c080 - 0015 5940
0301 0500 (flip)
ryorioh: 0021 0023*00cb 01c6 - 0001 000f 00fe 0106
0300 0000 0500 c000 - 0015 5140
03ed 5558 (flip) *0025
srmp4: 002b 002c 00d4 01c6 - 0001 0012 0102 0106
0301 0711 0500 0000 - 0015 4940
ffe8 5557 (flip)
srmp7: 002b 002c 00d4 01c6 - 0001 000e 00fd 0106
0000 0000 e500 0000 - 0015 7140
02f2 b558 (flip)
stmblade: 0021 0026 00d6 01c6 - 0001 000e 00fe 0106
03f1 0711 5550 c080 - 0015 5940 <- 711 becomes 0 during gameplay
0301 0500 (flip)
survarts: 002b 002c 00d4 01c6 - 0001 0012 0102 0106
0301 0000 0500 0000 - 0015 5140
03e9 5558 (flip)
sxyreact: 0021 0022 00cb 01c6 - 0001 000e 00fe 0106
0301 0000 0500 c000 - 0015 5140
03ef 5558 (flip)
sxyreac2: 0021 0023 00cb 01c6 - 0001 000e 00fe 0106
0301 0000 0500 c000 - 0015 5140
???? ???? (flip)
twineag2: 002b 002c 00d4 01c6 - 0001 0012 0102 0106
ffec 0000 e500 4000 - 0315 7940
???? ???? (flip)
ultrax: 002b 002c 00d4 01c6 - 0001 0012 0102 0106
ffec 0000 e500 4000 - 0315 7940
02fe b558 (flip)
vasara & 0021 0024 00cc 01c6 - 0001 000e 00fe 0106
vasara2: 03f1 0000 6500 c000 - 0015 5140
0301 3558 (flip)
***************************************************************************/
READ16_MEMBER(ssv_state::vblank_r)
{
// maybe reads scanline counter? pastelis reads this on the 'for use in Japan' screen + end credits
// and adjusts y position to do a polled raster effect by changing scroll values midscreen
uint16_t ret = 0x0000;
if (m_screen->vblank())
ret |= 0x3000;
if (m_screen->hblank())
ret |= 0x0800;
return ret;
}
WRITE16_MEMBER(ssv_state::scroll_w)
{
m_screen->update_partial(m_screen->vpos() - 1); // pastelis FOR USE IN JAPAN screen polls the vblank / hblank to do a raster effect
COMBINE_DATA(m_scroll + offset);
/* offsets 60-7f: CRT Controller */
// if(((offset*2) & 0x70) == 0x60)
// printf("%04x %04x\n",data,offset*2);
}
/***************************************************************************
Sprites Drawing
[mslider]
"Insert coins" 101f60: 0006 0825 00b0 000c
104128: 1a3a 0000 63d4 0400 (16x16)
Character tilemap 100080: 000f 0410 0000 0000
102080: 0002 0000 6200 0c00
0002 0000 6200 0c40
0002 0000 6200 0c80
0002 0000 6200 0cc0
0002 0000 6200 0d00
..
0002 0000 6200 0fc0
1c0010: 73f0 00f1 05ff 4600
[hypreact]
intro 100140: 6003 04ca 0000 0000 (tilemap sprite)
102650: 0003 0000 0000 0c00
0003 0000 0000 0c40
0003 0000 0000 0c80
0003 0000 0000 0cc0
1c0018: 15f8 00e8 057f 2600
game 100058: 6003 00db 0050 0040 (normal sprite)
1006d8: 037c 001b 6400 0400
tiles 100060: 6019 0120 0014 000a (sometimes 6619!)
100900: 0a8c 0012 6400 0420 (32x16)
0400 0012 6400 0800 (32x32)
...
[hypreac2]
"warning" 100f60: 6106 3893 0068 00c8 (128x32)
11c498: 00e0 00b2 6c00 0800
0000 0000 0000 0000
text below "warning" 100f70: 6016 389b 0048 00b0 ((16 or 8)x8)
11c4d8: 0054 0007 6000 0000
black regions 100af8: 6303 2d64 0000 03f8 (yoffs should be used)
116b20: 0004 0000 0000 0fa0 <- move up and down
0004 0000 0000 0fe0
0004 0000 0000 0ce0
0004 0000 0000 0d20
1c0020: 6c00 00ef 057f 2600
"credits" 1012f0: 6007 440e 00e0 0008 (16x8)
122070: 0043 0006 6000 0000
tiles 100ce8: 6111 3464 0008 0030 (16x32,empty tile frames)
11a320: 0460 006b 6110 0800
0460 006b 6100 0800
..
100d68: 6205 34de 0008 0030 (16x32, tiles on frames)
11a6f0: 050c 0059 6000 0800
04c0 005a 6010 0800
04d4 005a 6020 0800
0500 0059 6030 0800
..
100cf8: 6105 348a 0008 fff8 (16x16 x 2x3, big tiles below)
11a450: 61d6 0059 6000 0420
61f2 0059 6000 0410
6212 0059 6000 0400
61d8 0059 6010 0420
61f4 0059 6010 0410
6214 0059 6010 0400
[keithlcy]
high scores 101030: 717f 40c0 0010 0000
120600: 0000 0000 00c0 0025 (16x16)
0000 0000 00d0 0025
..
0000 0000 0000 ffff (ysize should be 16!)
0000 0000 0010 ffff
..
K of "KEITH" 101180: 610e 4600 0020 0090
123000: 4ef4 016a 0000 0040 (16x16)
4ef6 016a 0010 0040
..
floating robot (demo) 101000: 713d 4000 03b0 0088
120000: ..
71f6 0020 0000 0030
71f8 0020 0010 0030
..
cityscape 100030: 7304 1600 0000 007c (yoffs should not be used)
10b000: 0001 0000 0200 0000
0001 0000 0200 0040
0001 0000 0200 0080
0001 0000 0200 00c0
1c0008: 0800 00f2 05ff 5729
[meosism]
shadows 100100: 701f 051b 0041 0020 (16x16 shadow)
1028d8: 05aa 0030 f000 0470
[srmp4]
logo 100000: 6303 1680 0180 0078 (yoffs?)
10b400: 0001 0000 0000 0060
0001 0000 0000 0020
0001 0000 0000 ffe0
0001 0000 0000 ffa0
1c0008: 0800 00ec 05ff 5629
tiles 100088: 6103 25b7 0028 000a (16x16)
112db8: f4aa 0009 03f8 0018
f4a8 0009 0008 0018
f4ba 0009 03f8 0008
f4bc 0009 0008 0008
[survarts]
tilemap 100030: 6303 2048 0000 0000
110240: 0002 0000 0160 03fc
0002 0000 0160 003c
0002 0000 0160 007c
0002 0000 0160 00bc
1c0010: 0c2e 01f5 05ff 4628
player 100030: 611f 1da0 0000 fffc
10ed00: eb16 200f 005c fffa (16x16)
"push start" 100130: 601d 1920 0048 0000
10c900: 0441 0004 0000 004c (16x8)
[drifto94]
car shadow 100010: 8137 0640 0080 0030
103200: ..
544a 21e4 0030 0030 (16x16)
writings on finish 100130: 6109 4840 004e 0058 "good work"
124200: ee6e 0135 0024 0000 (16x16)
100158: 611e 4860 0058 0020 "you have proved yOur"..
124300: ee92 0137 0024 0014 (16x16)
..
ee7e 0137 fff4 0000 (16x16!!) ; 'O'
..
[ultrax]
sprite begin of lev1 100010: 6b60 4280 0016 00a0
121400: 51a0 0042 6800 0c00 (64x64)
[eaglshot]
title logo 100040: 001b 2920 0048 00e0
114900: 2130 0060 7018 0fd0 (16x64)
play 100020: 0003 290c 0000 0000
114860: 0003 0000 03f0 0ce0 (tilemap)
sammy logo 100020: 0003 1000 0000 0000
108000: 0001 0000 4380 0ce0 (tilemap)
From the above some noteworthy cases are:
101f60: 0006 0825 00b0 000c
104128: 1a3a 0000 63d4 0400 consider y size
101030: 717f 40c0 0010 0000
120600: 0000 0000 0000 ffff ignore y size & depth
100158: 611e 4860 0058 0020
124300 ee7e 0137 fff4 0000 ignore x size & depth
100f60: 6106 3893 0068 00c8
11c498: 00e0 00b2 6c00 0800 consider x size & y size
100100: 701f 051b 0041 0020
1028d8: 05aa 0030 f000 0470 consider shadow (16x16 shadow)
100010: 6b60 4280 0016 00a0
121400: 51a0 0042 6800 0c00 (64x64)
100140: 6003 04ca 0000 0000 tilemap
102650: 0003 0000 0000 0c00
100080: 000f 0410 0000 0000 tilemap
102080: 0002 0000 6200 0c00
**************************************************************************/
/* Draw a tilemap sprite */
void ssv_state::draw_16x16_tile_line(bitmap_ind16 &bitmap, const rectangle &cliprect, int flipx, int flipy, int mode, int code, int color, int sx, int sy, int realline, int line)
{
/* Force 16x16 tiles ? */
int realcode;
if (flipy)
{
if (line & 8)
realcode = code ;
else
realcode = code + 1;
}
else
{
if (line & 8)
realcode = code + 1;
else
realcode = code;
}
int tileline = line & 7;
int shadow = (mode & 0x0800);
/* Select 256 or 64 color tiles */
int gfx = ((mode & 0x0700) >> 8);
drawgfx_line(bitmap, cliprect, gfx, realcode, color, flipx, flipy, sx, sy, shadow, realline, tileline);
}
inline void ssv_state::get_tile(int x, int y, int size, int page, int& code, int& attr, int& flipx, int& flipy)
{
uint16_t* s3 = &m_spriteram[page * (size * ((0x1000 / 0x200) / 2)) +
((x & ((size - 1) & ~0xf)) << 2) +
((y & ((0x200 - 1) & ~0xf)) >> 3)];
code = s3[0]; // code high bits
attr = s3[1]; // code low bits + color
/* Code's high bits are scrambled */
code += m_tile_code[(attr & 0x3c00) >> 10];
flipy = (attr & 0x4000);
flipx = (attr & 0x8000);
if ((m_scroll[0x74 / 2] & 0x1000) && ((m_scroll[0x74 / 2] & 0x2000) == 0))
{
if (flipx == 0) flipx = 1; else flipx = 0;
}
if ((m_scroll[0x74 / 2] & 0x4000) && ((m_scroll[0x74 / 2] & 0x2000) == 0))
{
if (flipy == 0) flipy = 1; else flipy = 0;
}
}
void ssv_state::draw_row_64pixhigh(bitmap_ind16 &bitmap, const rectangle &cliprect, int in_sy, int scrollreg)
{
scrollreg &= 0x7; // scroll register index
/* in_sy will always be 0x00, 0x40, 0x80, 0xc0 in 'draw layer' */
in_sy = (in_sy & 0x1ff) - (in_sy & 0x200);
/* Set up a clipping region for the tilemap slice .. */
rectangle outclip;
outclip.set(0, 0x20/*width in tiles*/ * 0x10, in_sy, in_sy + 0x8/*height in tiles, always 64 pixels*/ * 0x8);
/* .. and clip it against the visible screen */
if (outclip.min_x > cliprect.max_x) return;
if (outclip.min_y > cliprect.max_y) return;
if (outclip.max_x < cliprect.min_x) return;
if (outclip.max_y < cliprect.min_y) return;
outclip &= cliprect;
for (int line = outclip.min_y; line <= outclip.max_y; line++)
{
rectangle clip;
clip.set(outclip.min_x, outclip.max_x, line, line);
/* Get the scroll data */
int tilemap_scrollx = m_scroll[scrollreg * 4 + 0]; // x scroll
int tilemap_scrolly = m_scroll[scrollreg * 4 + 1]; // y scroll
int unknown = m_scroll[scrollreg * 4 + 2]; // ???
int mode = m_scroll[scrollreg * 4 + 3]; // layer disabled, shadow, depth etc.
/* Background layer disabled */
if ((mode & 0xe000) == 0)
return;
/* Decide the actual size of the tilemap */
int size = 1 << (8 + ((mode & 0xe000) >> 13));
int page = (tilemap_scrollx & 0x7fff) / size;
/* Given a fixed scroll value, the portion of tilemap displayed changes with the sprite position */
tilemap_scrolly += in_sy;
/* Tweak the scroll values */
tilemap_scrolly += ((m_scroll[0x70 / 2] & 0x1ff) - (m_scroll[0x70 / 2] & 0x200) + m_scroll[0x6a / 2] + 2);
// Kludge for eaglshot
if ((unknown & 0x05ff) == 0x0440) tilemap_scrollx += -0x10;
if ((unknown & 0x05ff) == 0x0401) tilemap_scrollx += -0x20;
int realy = tilemap_scrolly + (line - in_sy);
if ((mode & 0x1000))
{
uint32_t scrolltable_base = ((mode & 0x00ff) * 0x400 ) /2;
//logerror("line %d realy %04x: scrolltable base is %08x\n", line,realy&0x1ff, scrolltable_base*2);
tilemap_scrollx += m_spriteram[(scrolltable_base+(realy&0x1ff)) & 0x1ffff];
}
/* Draw the rows */
int sx1 = 0 - (tilemap_scrollx & 0xf);
int x = tilemap_scrollx;
for (int sx = sx1; sx <= clip.max_x; sx += 0x10)
{
int code, attr, flipx, flipy;
get_tile(x, realy, size, page, code, attr, flipx, flipy);
draw_16x16_tile_line(bitmap, clip, flipx, flipy, mode, code, attr, sx, realy, line,realy & 0xf);
x += 0x10;
} /* sx */
} /* line */
}
/* Draw the "background layer" using multiple tilemap sprites */
void ssv_state::draw_layer(bitmap_ind16 &bitmap, const rectangle &cliprect, int nr)
{
for ( int sy = 0; sy <= m_screen->visible_area().max_y; sy += 0x40 )
draw_row_64pixhigh(bitmap, cliprect, sy, nr);
}
void ssv_state::draw_sprites_tiles(bitmap_ind16 &bitmap, const rectangle &cliprect, int code, int flipx, int flipy, int gfx, int shadow, int color, int sx, int sy, int xnum, int ynum)
{
int xstart, xend, xinc;
int ystart, yend, yinc;
/* Draw the tiles */
if (flipx) { xstart = xnum - 1; xend = -1; xinc = -1; }
else { xstart = 0; xend = xnum; xinc = +1; }
if (flipy) { ystart = ynum - 1; yend = -1; yinc = -1; }
else { ystart = 0; yend = ynum; yinc = +1; }
for (int x = xstart; x != xend; x += xinc)
{
for (int y = ystart; y != yend; y += yinc)
{
drawgfx(bitmap, cliprect, gfx,
code++,
color,
flipx, flipy,
sx + x * 16, sy + y * 8,
shadow);
}
}
}
/* Draw sprites in the sprites list */
void ssv_state::draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect)
{
/* Sprites list */
uint16_t *spritelist_global = m_spriteram;
uint16_t *spritelist_global_end = m_spriteram + 0x02000 / 2;
for (; spritelist_global < spritelist_global_end; spritelist_global += 4)
{
int sprite = spritelist_global[1];
/* Last sprite */
if (sprite & 0x8000) break;
/* Single-sprite address */
uint16_t* spritelist_local = &m_spriteram[(sprite & 0x7fff) * 4];
int tilemaps_offsy = ((spritelist_local[3] & 0x1ff) - (spritelist_local[3] & 0x200));
int mode = spritelist_global[0];
/* Number of single-sprites int local list (1-32) */
int local_num = (mode & 0x001f);
for (int count = 0; count <= local_num; count++, spritelist_local += 4)
{
uint16_t *spritelist_local_end = m_spriteram + 0x40000 / 2;
if (spritelist_local >= spritelist_local_end) break;
int sx = spritelist_local[2];
int sy = spritelist_local[3];
/* do we use local sizes (set here) or global ones (set in previous list) */
int use_local = m_scroll[0x76 / 2] & 0x4000;
int xnum = use_local ? (sx & 0x0c00) : (mode & 0x0c00);
int ynum = use_local ? (sy & 0x0c00) : (mode & 0x0300) << 2;
int depth = use_local ? (sx & 0xf000) : (mode & 0xf000);
if (spritelist_local[0] <= 7 && spritelist_local[1] == 0 && xnum == 0 && ynum == 0x0c00)
{
// Tilemap Sprite
int scroll;
scroll = spritelist_local[0]; // scroll index
if (m_scroll[0x76 / 2] & 0x1000)
sy -= 0x20; // eaglshot
else
{
if (m_scroll[0x7a / 2] & 0x0800)
{
if (m_scroll[0x7a / 2] & 0x1000) // drifto94, dynagear, keithlcy, mslider, stmblade, gdfs, ultrax, twineag2
sy -= tilemaps_offsy;
else // srmp4
sy += tilemaps_offsy;
}
}
if (local_num != 0)
{
if (spritelist_local[0] != 0) // index 0 is the 'always automatically drawn background layer' so don't draw it twice even if it's specified later?
{
draw_row_64pixhigh(bitmap, cliprect, sy, scroll);
}
}
}
else
{
// "Normal" Sprite
/*
hot spots:
"warning" in hypreac2 has mode & 0x0100 and is not 16x16
keithlcy high scores has mode & 0x0100 and y & 0x0c00 can be 0x0c00
drifto94 "you have proved yOur".. has mode & 0x0100 and x & 0x0c00 can be 0x0c00
ultrax (begin of lev1): 100010: 6b60 4280 0016 00a0
121400: 51a0 0042 6800 0c00 needs to be a normal sprite
*/
int code = spritelist_local[0]; // code high bits
int attr = spritelist_local[1]; // code low bits + color
/* Code's high bits are scrambled */
code += m_tile_code[(attr & 0x3c00) >> 10];
int flipy = (attr & 0x4000);
int flipx = (attr & 0x8000);
if ((m_scroll[0x74 / 2] & 0x1000) && ((m_scroll[0x74 / 2] & 0x2000) == 0))
{
if (flipx == 0) flipx = 1; else flipx = 0;
}
if ((m_scroll[0x74 / 2] & 0x4000) && ((m_scroll[0x74 / 2] & 0x2000) == 0))
{
if (flipy == 0) flipy = 1; else flipy = 0;
}
/* Select 256 or 64 color tiles */
int gfx = (depth & 0x7000) >>12;
int shadow = (depth & 0x8000);
/* Every single sprite is offset by x & yoffs, and additionally
by one of the 8 x & y offsets in the 1c0040-1c005f area */
/* Apply global offsets */
int scrollreg = ((mode & 0x00e0) >> 4);
sx += spritelist_global[2] + m_scroll[scrollreg + (0x40 / 2)];
sy += spritelist_global[3] + m_scroll[scrollreg + (0x42 / 2)];
/* Sign extend the position */
sx = (sx & 0x1ff) - (sx & 0x200);
sy = (sy & 0x1ff) - (sy & 0x200);
int sprites_offsx = ((m_scroll[0x74 / 2] & 0x7f) - (m_scroll[0x74 / 2] & 0x80));
int sprites_offsy = -((m_scroll[0x70 / 2] & 0x1ff) - (m_scroll[0x70 / 2] & 0x200) + m_scroll[0x6a / 2] + 1);
if (m_scroll[0x74 / 2] & 0x4000) // flipscreen y
{
sy = -sy;
if (m_scroll[0x74 / 2] & 0x8000)
sy += 0x00; //
else
sy -= 0x10; // vasara (hack)
}
if (m_scroll[0x74 / 2] & 0x1000) // flipscreen x
{
sx = -sx + 0x100;
}
/* Single-sprite tile size */
xnum = 1 << (xnum >> 10); // 1, 2, 4 or 8 tiles
ynum = 1 << (ynum >> 10); // 1, 2, 4 tiles (8 means tilemap sprite?)
// sprites can be relative to a side, the other side or the center
if (m_scroll[0x7a / 2] == 0x7140)
{
// srmp7
sx = sprites_offsx + sx;
sy = sprites_offsy - sy;
}
else if (m_scroll[0x7a / 2] & 0x0800)
{
// dynagear, drifto94, eaglshot, keithlcy, mslider, srmp4, stmblade, twineag2, ultrax
sx = sprites_offsx + sx - (xnum * 8);
sy = sprites_offsy - sy - (ynum * 8) / 2;
}
else
{
// hypreact, hypreac2, janjans1, meosism, ryorioh, survarts, sxyreact, sxyreac2, vasara, vasara2
sx = sprites_offsx + sx;
sy = sprites_offsy - sy - (ynum * 8);
}
/* Sprite code masking */
if (xnum == 2 && ynum == 4) // needed by hypreact
{
code &= ~7;
}
draw_sprites_tiles(bitmap, cliprect, code, flipx, flipy, gfx, shadow, attr, sx, sy, xnum, ynum);
} /* sprite type */
} /* single-sprites */
} /* sprites list */
}
/***************************************************************************
Screen Drawing
***************************************************************************/
uint32_t ssv_state::screen_update_eaglshot(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
return screen_update(screen, bitmap, cliprect);
}
uint32_t ssv_state::screen_update_gdfs(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
screen_update(screen, bitmap, cliprect);
// draw zooming sprites
m_gdfs_st0020->update_screen(screen, bitmap, cliprect, false);
m_gdfs_tmap->set_scrollx(0, m_gdfs_tmapscroll[0x0c/2]);
m_gdfs_tmap->set_scrolly(0, m_gdfs_tmapscroll[0x10/2]);
m_gdfs_tmap->draw(screen, bitmap, cliprect, 0, 0);
return 0;
}
void ssv_state::enable_video(int enable)
{
m_enable_video = enable;
}
uint32_t ssv_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
rectangle clip;
// Shadow
if (m_scroll[0x76/2] & 0x0080)
{
// 4 bit shadows (mslider, stmblade)
m_shadow_pen_shift = 15-4;
}
else
{
// 2 bit shadows
m_shadow_pen_shift = 15-2;
}
m_shadow_pen_mask = (1 << m_shadow_pen_shift) - 1;
/* The background color is the first one in the palette */
bitmap.fill(0, cliprect);
// used by twineag2 and ultrax
clip.min_x = (cliprect.max_x / 2 + m_scroll[0x62/2]) * 2 - m_scroll[0x64/2] * 2 + 2;
clip.max_x = (cliprect.max_x / 2 + m_scroll[0x62/2]) * 2 - m_scroll[0x62/2] * 2 + 1;
clip.min_y = (cliprect.max_y + m_scroll[0x6a/2]) - m_scroll[0x6c/2] + 1;
clip.max_y = (cliprect.max_y + m_scroll[0x6a/2]) - m_scroll[0x6a/2] ;
// printf("%04x %04x %04x %04x\n",clip.min_x, clip.max_x, clip.min_y, clip.max_y);
clip &= cliprect;
if (!m_enable_video)
return 0;
draw_layer(bitmap, clip, 0); // "background layer"
draw_sprites(bitmap, clip); // sprites list
return 0;
}