// license:BSD-3-Clause
// copyright-holders:David Haywood, Sylvain Glaize, Paul Priest, Olivier Galibert
/* Super Kaneko Nova System video */
#include "emu.h"
#include "video/sknsspr.h"
#include "includes/suprnova.h"
/* draws ROZ with linescroll OR columnscroll to 16-bit indexed bitmap */
void skns_state::draw_roz(bitmap_ind16 &bitmap, bitmap_ind8& bitmapflags, const rectangle &cliprect, tilemap_t *tmap, uint32_t startx, uint32_t starty, int incxx, int incxy, int incyx, int incyy, int wraparound, int columnscroll, uint32_t* scrollram)
{
//bitmap_ind16 *destbitmap = bitmap;
bitmap_ind16 &srcbitmap = tmap->pixmap();
bitmap_ind8 &srcbitmapflags = tmap->flagsmap();
const int xmask = srcbitmap.width()-1;
const int ymask = srcbitmap.height()-1;
const int widthshifted = srcbitmap.width() << 16;
const int heightshifted = srcbitmap.height() << 16;
uint32_t cx;
uint32_t cy;
int x;
int sx;
int sy;
int ex;
int ey;
uint16_t *dest;
uint8_t* destflags;
// uint8_t *pri;
//const uint16_t *src;
//const uint8_t *maskptr;
//int destadvance = destbitmap->bpp / 8;
/* pre-advance based on the cliprect */
startx += cliprect.min_x * incxx + cliprect.min_y * incyx;
starty += cliprect.min_x * incxy + cliprect.min_y * incyy;
/* extract start/end points */
sx = cliprect.min_x;
sy = cliprect.min_y;
ex = cliprect.max_x;
ey = cliprect.max_y;
{
/* loop over rows */
while (sy <= ey)
{
/* initialize X counters */
x = sx;
cx = startx;
cy = starty;
/* get dest and priority pointers */
dest = &bitmap.pix16(sy, sx);
destflags = &bitmapflags.pix8(sy, sx);
/* loop over columns */
while (x <= ex)
{
if ((wraparound) || (cx < widthshifted && cy < heightshifted)) // not sure how this will cope with no wraparound, but row/col scroll..
{
if (columnscroll)
{
dest[0] = srcbitmap.pix16(((cy >> 16) - scrollram[(cx>>16)&0x3ff]) & ymask, (cx >> 16) & xmask);
destflags[0] = srcbitmapflags.pix8(((cy >> 16) - scrollram[(cx>>16)&0x3ff]) & ymask, (cx >> 16) & xmask);
}
else
{
dest[0] = srcbitmap.pix16((cy >> 16) & ymask, ((cx >> 16) - scrollram[(cy>>16)&0x3ff]) & xmask);
destflags[0] = srcbitmapflags.pix8((cy >> 16) & ymask, ((cx >> 16) - scrollram[(cy>>16)&0x3ff]) & xmask);
}
}
/* advance in X */
cx += incxx;
cy += incxy;
x++;
dest++;
destflags++;
// pri++;
}
/* advance in Y */
startx += incyx;
starty += incyy;
sy++;
}
}
}
WRITE32_MEMBER(skns_state::pal_regs_w)
{
COMBINE_DATA(&m_pal_regs[offset]);
m_palette_updated =1;
switch ( offset )
{
/* RWRA regs are for SPRITES */
case (0x00/4): // RWRA0
if( m_use_spc_bright != (data&1) ) {
m_use_spc_bright = data&1;
m_spc_changed = 1;
}
m_alt_enable_sprites = (data>>8)&1;
break;
case (0x04/4): // RWRA1
if( m_bright_spc_g != (data&0xff) ) {
m_bright_spc_g = data&0xff;
m_spc_changed = 1;
}
m_bright_spc_g_trans = (data>>8) &0xff;
break;
case (0x08/4): // RWRA2
if( m_bright_spc_r != (data&0xff) ) {
m_bright_spc_r = data&0xff;
m_spc_changed = 1;
}
m_bright_spc_r_trans = (data>>8) &0xff;
break;
case (0x0C/4): // RWRA3
if( m_bright_spc_b != (data&0xff) ) {
m_bright_spc_b = data&0xff;
m_spc_changed = 1;
}
m_bright_spc_b_trans = (data>>8)&0xff;
break;
/* RWRB regs are for BACKGROUND */
case (0x10/4): // RWRB0
if( m_use_v3_bright != (data&1) ) {
m_use_v3_bright = data&1;
m_v3_changed = 1;
}
m_alt_enable_background = (data>>8)&1;
break;
case (0x14/4): // RWRB1
if( m_bright_v3_g != (data&0xff) ) {
m_bright_v3_g = data&0xff;
m_v3_changed = 1;
}
m_bright_v3_g_trans = (data>>8)&0xff;
break;
case (0x18/4): // RWRB2
if( m_bright_v3_r != (data&0xff) ) {
m_bright_v3_r = data&0xff;
m_v3_changed = 1;
}
m_bright_v3_r_trans = (data>>8)&0xff;
break;
case (0x1C/4): // RWRB3
if( m_bright_v3_b != (data&0xff) ) {
m_bright_v3_b = data&0xff;
m_v3_changed = 1;
}
m_bright_v3_b_trans = (data>>8)&0xff;
break;
}
}
WRITE32_MEMBER(skns_state::palette_ram_w)
{
int r,g,b;
int brightness_r, brightness_g, brightness_b/*, alpha*/;
int use_bright;
COMBINE_DATA(&m_palette_ram[offset]);
b = ((m_palette_ram[offset] >> 0 ) & 0x1f);
g = ((m_palette_ram[offset] >> 5 ) & 0x1f);
r = ((m_palette_ram[offset] >> 10 ) & 0x1f);
//alpha = ((m_palette_ram[offset] >> 15 ) & 0x1);
if(offset<(0x40*256)) { // 1st half is for Sprites
use_bright = m_use_spc_bright;
brightness_b = m_bright_spc_b;
brightness_g = m_bright_spc_g;
brightness_r = m_bright_spc_r;
} else { // V3 bg's
use_bright = m_use_v3_bright;
brightness_b = m_bright_v3_b;
brightness_g = m_bright_v3_g;
brightness_r = m_bright_v3_r;
}
if(use_bright) {
if(brightness_b) b = ((b<<3) * (brightness_b+1))>>8;
else b = 0;
if(brightness_g) g = ((g<<3) * (brightness_g+1))>>8;
else g = 0;
if(brightness_r) r = ((r<<3) * (brightness_r+1))>>8;
else r = 0;
} else {
b <<= 3;
g <<= 3;
r <<= 3;
}
m_palette->set_pen_color(offset,rgb_t(r,g,b));
}
void skns_state::palette_set_rgb_brightness (int offset, uint8_t brightness_r, uint8_t brightness_g, uint8_t brightness_b)
{
int use_bright, r, g, b/*, alpha*/;
b = ((m_palette_ram[offset] >> 0 ) & 0x1f);
g = ((m_palette_ram[offset] >> 5 ) & 0x1f);
r = ((m_palette_ram[offset] >> 10 ) & 0x1f);
//alpha = ((m_palette_ram[offset] >> 15 ) & 0x1);
if(offset<(0x40*256)) { // 1st half is for Sprites
use_bright = m_use_spc_bright;
} else { // V3 bg's
use_bright = m_use_v3_bright;
}
if(use_bright) {
if(brightness_b) b = ((b<<3) * (brightness_b+1))>>8;
else b = 0;
if(brightness_g) g = ((g<<3) * (brightness_g+1))>>8;
else g = 0;
if(brightness_r) r = ((r<<3) * (brightness_r+1))>>8;
else r = 0;
} else {
b <<= 3;
g <<= 3;
r <<= 3;
}
m_palette->set_pen_color(offset,rgb_t(r,g,b));
}
void skns_state::palette_update()
{
int i;
if (m_palette_updated)
{
if(m_spc_changed)
for(i=0; i<=((0x40*256)-1); i++)
palette_set_rgb_brightness (i, m_bright_spc_r, m_bright_spc_g, m_bright_spc_b);
if(m_v3_changed)
for(i=(0x40*256); i<=((0x80*256)-1); i++)
palette_set_rgb_brightness (i, m_bright_v3_r, m_bright_v3_g, m_bright_v3_b);
m_palette_updated =0;
}
}
TILE_GET_INFO_MEMBER(skns_state::get_tilemap_A_tile_info)
{
int code = ((m_tilemapA_ram[tile_index] & 0x001fffff) >> 0 );
int colr = ((m_tilemapA_ram[tile_index] & 0x3f000000) >> 24 );
int pri = ((m_tilemapA_ram[tile_index] & 0x00e00000) >> 21 );
int depth = (m_v3_regs[0x0c/4] & 0x0001) << 1;
int flags = 0;
if(m_tilemapA_ram[tile_index] & 0x80000000) flags |= TILE_FLIPX;
if(m_tilemapA_ram[tile_index] & 0x40000000) flags |= TILE_FLIPY;
SET_TILE_INFO_MEMBER(0+depth,
code,
0x40+colr,
flags);
tileinfo.category = pri;
//if (pri) popmessage("pri A!! %02x\n", pri);
}
WRITE32_MEMBER(skns_state::tilemapA_w)
{
COMBINE_DATA(&m_tilemapA_ram[offset]);
m_tilemap_A->mark_tile_dirty(offset);
}
TILE_GET_INFO_MEMBER(skns_state::get_tilemap_B_tile_info)
{
int code = ((m_tilemapB_ram[tile_index] & 0x001fffff) >> 0 );
int colr = ((m_tilemapB_ram[tile_index] & 0x3f000000) >> 24 );
int pri = ((m_tilemapB_ram[tile_index] & 0x00e00000) >> 21 );
int depth = (m_v3_regs[0x0c/4] & 0x0100) >> 7;
int flags = 0;
if(m_tilemapB_ram[tile_index] & 0x80000000) flags |= TILE_FLIPX;
if(m_tilemapB_ram[tile_index] & 0x40000000) flags |= TILE_FLIPY;
SET_TILE_INFO_MEMBER(1+depth,
code,
0x40+colr,
flags);
tileinfo.category = pri;
//if (pri) popmessage("pri B!! %02x\n", pri); // 02 on cyvern
}
WRITE32_MEMBER(skns_state::tilemapB_w)
{
COMBINE_DATA(&m_tilemapB_ram[offset]);
m_tilemap_B->mark_tile_dirty(offset);
}
WRITE32_MEMBER(skns_state::v3_regs_w)
{
COMBINE_DATA(&m_v3_regs[offset]);
/* if the depth changes we need to dirty the tilemap */
if (offset == 0x0c/4)
{
int old_depthA = m_depthA;
int old_depthB = m_depthB;
m_depthA = (m_v3_regs[0x0c/4] & 0x0001) << 1;
m_depthB = (m_v3_regs[0x0c/4] & 0x0100) >> 7;
if (old_depthA != m_depthA) m_tilemap_A->mark_all_dirty();
if (old_depthB != m_depthB) m_tilemap_B->mark_all_dirty();
}
}
void skns_state::video_start()
{
m_tilemap_A = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(skns_state::get_tilemap_A_tile_info)), TILEMAP_SCAN_ROWS, 16,16, 64,64);
m_tilemap_A->set_transparent_pen(0);
m_tilemap_B = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(skns_state::get_tilemap_B_tile_info)), TILEMAP_SCAN_ROWS, 16,16, 64,64);
m_tilemap_B->set_transparent_pen(0);
m_sprite_bitmap.allocate(1024,1024);
m_tilemap_bitmap_lower.allocate(320,240);
m_tilemap_bitmapflags_lower.allocate(320,240);
m_tilemap_bitmap_higher.allocate(320,240);
m_tilemap_bitmapflags_higher.allocate(320,240);
m_gfxdecode->gfx(2)->set_granularity(256);
m_gfxdecode->gfx(3)->set_granularity(256);
save_item(NAME(m_depthA));
save_item(NAME(m_depthB));
save_item(NAME(m_use_spc_bright));
save_item(NAME(m_use_v3_bright));
save_item(NAME(m_bright_spc_b));
save_item(NAME(m_bright_spc_g));
save_item(NAME(m_bright_spc_r));
save_item(NAME(m_bright_spc_b_trans));
save_item(NAME(m_bright_spc_g_trans));
save_item(NAME(m_bright_spc_r_trans));
save_item(NAME(m_bright_v3_b));
save_item(NAME(m_bright_v3_g));
save_item(NAME(m_bright_v3_r));
save_item(NAME(m_bright_v3_b_trans));
save_item(NAME(m_bright_v3_g_trans));
save_item(NAME(m_bright_v3_r_trans));
save_item(NAME(m_spc_changed));
save_item(NAME(m_v3_changed));
save_item(NAME(m_palette_updated));
save_item(NAME(m_alt_enable_background));
save_item(NAME(m_alt_enable_sprites));
}
void skns_state::video_reset()
{
m_depthA = m_depthB = 0;
m_use_spc_bright = m_use_v3_bright = 1;
m_bright_spc_b= m_bright_spc_g = m_bright_spc_r = 0x00;
m_bright_spc_b_trans = m_bright_spc_g_trans = m_bright_spc_r_trans = 0x00;
m_bright_v3_b = m_bright_v3_g = m_bright_v3_r = 0x00;
m_bright_v3_b_trans = m_bright_v3_g_trans = m_bright_v3_r_trans = 0x00;
m_spc_changed = m_v3_changed = m_palette_updated = 0;
m_alt_enable_background = m_alt_enable_sprites = 1;
}
void skns_state::draw_a( bitmap_ind16 &bitmap, bitmap_ind8 &bitmap_flags, const rectangle &cliprect, int tran )
{
int enable_a = (m_v3_regs[0x10/4] >> 0) & 0x0001;
int nowrap_a = (m_v3_regs[0x10/4] >> 0) & 0x0004;
uint32_t startx,starty;
int incxx,incxy,incyx,incyy;
int columnscroll;
//if(nowrap_a) printf("a\n");
if (enable_a && m_alt_enable_background)
{
startx = m_v3_regs[0x1c/4];
incyy = m_v3_regs[0x30/4]&0x7ffff;
if (incyy&0x40000) incyy = incyy-0x80000; // level 3 boss in sengekis
incyx = m_v3_regs[0x2c/4];
starty = m_v3_regs[0x20/4];
incxy = m_v3_regs[0x28/4];
incxx = m_v3_regs[0x24/4]&0x7ffff;
if (incxx&0x40000) incxx = incxx-0x80000;
columnscroll = (m_v3_regs[0x0c/4] >> 1) & 0x0001;
draw_roz(bitmap,bitmap_flags,cliprect, m_tilemap_A, startx << 8,starty << 8, incxx << 8,incxy << 8,incyx << 8,incyy << 8, !nowrap_a, columnscroll, &m_v3slc_ram[0]);
//tilemap_copy_bitmap(bitmap, m_tilemap_bitmap_lower, m_tilemap_bitmapflags_lower);
}
}
void skns_state::draw_b( bitmap_ind16 &bitmap, bitmap_ind8 &bitmap_flags, const rectangle &cliprect, int tran )
{
int enable_b = (m_v3_regs[0x34/4] >> 0) & 0x0001;
int nowrap_b = (m_v3_regs[0x34/4] >> 0) & 0x0004;
uint32_t startx,starty;
int incxx,incxy,incyx,incyy;
int columnscroll;
//if(nowrap_b) printf("b\n");
if (enable_b && m_alt_enable_background)
{
startx = m_v3_regs[0x40/4];
incyy = m_v3_regs[0x54/4]&0x7ffff;
if (incyy&0x40000) incyy = incyy-0x80000;
incyx = m_v3_regs[0x50/4];
starty = m_v3_regs[0x44/4];
incxy = m_v3_regs[0x4c/4];
incxx = m_v3_regs[0x48/4]&0x7ffff;
if (incxx&0x40000) incxx = incxx-0x80000;
columnscroll = (m_v3_regs[0x0c/4] >> 9) & 0x0001; // selects column scroll or rowscroll
draw_roz(bitmap,bitmap_flags, cliprect, m_tilemap_B, startx << 8,starty << 8, incxx << 8,incxy << 8,incyx << 8,incyy << 8, !nowrap_b, columnscroll, &m_v3slc_ram[0x1000/4]);
//popmessage("%08x %08x %08x %08x %08x %08x", startx, starty, incxx, incyy, incxy, incyx);
}
}
uint32_t skns_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
palette_update();
bitmap.fill(m_palette->black_pen(), cliprect);
m_tilemap_bitmap_lower.fill(0);
m_tilemap_bitmapflags_lower.fill(0);
m_tilemap_bitmap_higher.fill(0);
m_tilemap_bitmapflags_higher.fill(0);
{
int supernova_pri_a;
int supernova_pri_b;
int tran = 0;
supernova_pri_a = (m_v3_regs[0x10/4] & 0x0002)>>1;
supernova_pri_b = (m_v3_regs[0x34/4] & 0x0002)>>1;
//popmessage("pri %d %d\n", supernova_pri_a, supernova_pri_b);
/*if (!supernova_pri_b) { */
draw_b(m_tilemap_bitmap_lower, m_tilemap_bitmapflags_lower, cliprect,tran);// tran = 1;
draw_a(m_tilemap_bitmap_higher,m_tilemap_bitmapflags_higher,cliprect,tran);// tran = 1;
{
int x,y;
uint8_t* srcflags, *src2flags;
uint16_t* src, *src2, *src3;
uint32_t* dst;
uint16_t pri, pri2, pri3;
uint16_t bgpri;
const pen_t *clut = &m_palette->pen(0);
// int drawpri;
for (y=cliprect.min_y;y<=cliprect.max_y;y++)
{
src = &m_tilemap_bitmap_lower.pix16(y);
srcflags = &m_tilemap_bitmapflags_lower.pix8(y);
src2 = &m_tilemap_bitmap_higher.pix16(y);
src2flags = &m_tilemap_bitmapflags_higher.pix8(y);
src3 = &m_sprite_bitmap.pix16(y);
dst = &bitmap.pix32(y);
for (x=cliprect.min_x;x<=cliprect.max_x;x++)
{
uint16_t pendata = src[x]&0x7fff;
uint16_t pendata2 = src2[x]&0x7fff;
uint16_t bgpendata;
uint16_t pendata3 = src3[x]&0x3fff;
uint32_t coldat;
pri = ((srcflags[x] & 0x07)<<1) | (supernova_pri_b);
pri2= ((src2flags[x] & 0x07)<<1) | (supernova_pri_a);
pri3 = ((src3[x]&0xc000)>>12)+3;
// work out which layers bg pixel has the higher priority
// note, can the bg layers be blended?? sarukani uses an alpha pen for
// some of them.. and registers indicate it might be possible..
// this priority mixing is almost certainly still incorrect
// bg colour / prioirty handling is now wrong
if (pri<=pri2) // <= is good for last level of cyvern.. < seem better for galpanis kaneko logo
{
if (pendata2&0xff)
{
bgpendata = pendata2&0x7fff;
bgpri = pri2;
}
else if (pendata&0xff)
{
bgpendata = pendata&0x7fff;
bgpri = pri;
}
else
{
bgpendata = pendata2&0x7fff;
bgpri = 0;
}
}
else
{
if (pendata&0xff)
{
bgpendata = pendata&0x7fff;
bgpri = pri;
}
else if (pendata2&0xff)
{
bgpendata = pendata2&0x7fff;
bgpri = pri2;
}
else
{
bgpendata = 0;
bgpri = 0;
}
}
// if the sprites are higher than the bg pixel
if (pri3 > bgpri)
{
if (pendata3&0xff)
{
uint16_t palvalue = m_palette_ram[pendata3];
coldat = clut[pendata3];
if (palvalue&0x8000)
{
uint32_t srccolour = clut[bgpendata&0x7fff];
uint32_t dstcolour = clut[pendata3&0x3fff];
int r,g,b;
int r2,g2,b2;
r = (srccolour & 0x000000ff)>> 0;
g = (srccolour & 0x0000ff00)>> 8;
b = (srccolour & 0x00ff0000)>> 16;
r2 = (dstcolour & 0x000000ff)>> 0;
g2 = (dstcolour & 0x0000ff00)>> 8;
b2 = (dstcolour & 0x00ff0000)>> 16;
r2 = (r2 * m_bright_spc_r_trans) >> 8;
g2 = (g2 * m_bright_spc_g_trans) >> 8;
b2 = (b2 * m_bright_spc_b_trans) >> 8;
r = (r+r2);
if (r>255) r = 255;
g = (g+g2);
if (g>255) g = 255;
b = (b+b2);
if (b>255) b = 255;
dst[x] = (r << 0) | (g << 8) | (b << 16);
}
else
{
coldat = clut[pendata3];
dst[x] = coldat;
}
}
else
{
coldat = clut[bgpendata];
dst[x] = coldat;
}
}
else
{
coldat = clut[bgpendata];
dst[x] = coldat;
}
}
}
}
}
if (m_alt_enable_sprites)
m_spritegen->skns_draw_sprites(m_sprite_bitmap, cliprect, m_spriteram, m_spriteram.bytes(), m_spc_regs ); // TODO : not all 0x4000 of the sprite RAM area can be displayed on real hardware
return 0;
}