496 lines
13 KiB
C++
496 lines
13 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:David Haywood, Sylvain Glaize, Paul Priest, Olivier Galibert
|
|
/* Super Kaneko Nova System video */
|
|
|
|
#include "emu.h"
|
|
#include "suprnova.h"
|
|
|
|
#include <algorithm>
|
|
|
|
/* 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, u32 startx, u32 starty, int incxx, int incxy, int incyx, int incyy, int wraparound, int columnscroll, u32 *scrollram)
|
|
{
|
|
//bitmap_ind16 *destbitmap = bitmap;
|
|
const bitmap_ind16 &srcbitmap = tmap->pixmap();
|
|
const 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;
|
|
//u8 *pri;
|
|
//const u16 *src;
|
|
//const u8 *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 */
|
|
int sx = cliprect.min_x;
|
|
int sy = cliprect.min_y;
|
|
int ex = cliprect.max_x;
|
|
int ey = cliprect.max_y;
|
|
|
|
/* loop over rows */
|
|
while (sy <= ey)
|
|
{
|
|
/* initialize X counters */
|
|
int x = sx;
|
|
u32 cx = startx;
|
|
u32 cy = starty;
|
|
|
|
/* get dest and priority pointers */
|
|
u16 *const dest = &bitmap.pix(sy);
|
|
u8 *const destflags = &bitmapflags.pix(sy);
|
|
|
|
/* 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..
|
|
{
|
|
const u32 srcx = cx >> 16;
|
|
const u32 srcy = cy >> 16;
|
|
if (columnscroll)
|
|
{
|
|
dest[x] = srcbitmap.pix((srcy - scrollram[srcx & 0x3ff]) & ymask, srcx & xmask);
|
|
destflags[x] = srcbitmapflags.pix((srcy - scrollram[srcx & 0x3ff]) & ymask, srcx & xmask);
|
|
}
|
|
else
|
|
{
|
|
dest[x] = srcbitmap.pix(srcy & ymask, (srcx - scrollram[srcy & 0x3ff]) & xmask);
|
|
destflags[x] = srcbitmapflags.pix(srcy & ymask, (srcx - scrollram[srcy & 0x3ff]) & xmask);
|
|
}
|
|
}
|
|
|
|
/* advance in X */
|
|
cx += incxx;
|
|
cy += incxy;
|
|
x++;
|
|
//pri++;
|
|
}
|
|
|
|
/* advance in Y */
|
|
startx += incyx;
|
|
starty += incyy;
|
|
sy++;
|
|
}
|
|
}
|
|
|
|
|
|
void skns_state::pal_regs_w(offs_t offset, u32 data, u32 mem_mask)
|
|
{
|
|
data = COMBINE_DATA(&m_pal_regs[offset]);
|
|
m_palette_updated = true;
|
|
|
|
switch (offset)
|
|
{
|
|
/* RWRA regs are for SPRITES */
|
|
case (0x00 / 4): // RWRA0
|
|
if (m_use_spc_bright != BIT(data, 0))
|
|
{
|
|
m_use_spc_bright = BIT(data, 0);
|
|
m_spc_changed = true;
|
|
}
|
|
m_alt_enable_sprites = BIT(data, 8);
|
|
break;
|
|
case (0x04 / 4): // RWRA1
|
|
if (m_bright_spc_g != (data & 0xff))
|
|
{
|
|
m_bright_spc_g = data & 0xff;
|
|
m_spc_changed = true;
|
|
}
|
|
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 = true;
|
|
}
|
|
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 = true;
|
|
}
|
|
m_bright_spc_b_trans = (data >> 8) & 0xff;
|
|
break;
|
|
|
|
/* RWRB regs are for BACKGROUND */
|
|
case (0x10 / 4): // RWRB0
|
|
if (m_use_v3_bright != BIT(data, 0))
|
|
{
|
|
m_use_v3_bright = BIT(data, 0);
|
|
m_v3_changed = true;
|
|
}
|
|
m_alt_enable_background = BIT(data, 8);
|
|
break;
|
|
case (0x14 / 4): // RWRB1
|
|
if (m_bright_v3_g != (data & 0xff))
|
|
{
|
|
m_bright_v3_g = data & 0xff;
|
|
m_v3_changed = true;
|
|
}
|
|
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 = true;
|
|
}
|
|
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 = true;
|
|
}
|
|
m_bright_v3_b_trans = (data >> 8) & 0xff;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void skns_state::palette_set_brightness(offs_t offset)
|
|
{
|
|
int b = pal5bit(m_palette_ram[offset] >> 0);
|
|
int g = pal5bit(m_palette_ram[offset] >> 5);
|
|
int r = pal5bit(m_palette_ram[offset] >> 10);
|
|
const int a = pal1bit(m_palette_ram[offset] >> 15);
|
|
|
|
bool use_bright;
|
|
int brightness_b, brightness_g, brightness_r;
|
|
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 * (brightness_b + 1)) >> 8;
|
|
else b = 0;
|
|
if (brightness_g) g = (g * (brightness_g + 1)) >> 8;
|
|
else g = 0;
|
|
if (brightness_r) r = (r * (brightness_r + 1)) >> 8;
|
|
else r = 0;
|
|
}
|
|
|
|
m_palette->set_pen_color(offset, rgb_t(a, r, g, b));
|
|
}
|
|
|
|
void skns_state::palette_ram_w(offs_t offset, u32 data, u32 mem_mask)
|
|
{
|
|
COMBINE_DATA(&m_palette_ram[offset]);
|
|
|
|
palette_set_brightness(offset);
|
|
}
|
|
|
|
|
|
void skns_state::palette_update()
|
|
{
|
|
if (m_palette_updated)
|
|
{
|
|
if (m_spc_changed)
|
|
{
|
|
for (int i = 0; i < (0x40 * 256); i++)
|
|
{
|
|
palette_set_brightness(i);
|
|
}
|
|
m_spc_changed = false;
|
|
}
|
|
if (m_v3_changed)
|
|
{
|
|
for (int i = (0x40 * 256); i < (0x80 * 256); i++)
|
|
{
|
|
palette_set_brightness(i);
|
|
}
|
|
m_v3_changed = false;
|
|
}
|
|
m_palette_updated = false;
|
|
}
|
|
}
|
|
|
|
|
|
template <unsigned Which>
|
|
TILE_GET_INFO_MEMBER(skns_state::get_tile_info)
|
|
{
|
|
const u32 code = ((m_tilemap_ram[Which][tile_index] & 0x001fffff) >> 0);
|
|
const u32 colr = ((m_tilemap_ram[Which][tile_index] & 0x3f000000) >> 24);
|
|
const u8 pri = ((m_tilemap_ram[Which][tile_index] & 0x00e00000) >> 21);
|
|
const u8 depth = BIT(m_v3_regs[0x0c / 4], Which << 3);
|
|
const u32 flags = TILE_FLIPXY(m_tilemap_ram[Which][tile_index] >> 30);
|
|
|
|
tileinfo.set((Which << 1) + depth,
|
|
code,
|
|
colr,
|
|
flags);
|
|
tileinfo.category = pri;
|
|
|
|
//if (pri) popmessage("pri A!! %02x\n", pri);
|
|
}
|
|
|
|
void skns_state::v3_regs_w(offs_t offset, u32 data, u32 mem_mask)
|
|
{
|
|
const u32 old = m_v3_regs[offset];
|
|
data = COMBINE_DATA(&m_v3_regs[offset]);
|
|
|
|
/* if the depth changes we need to dirty the tilemap */
|
|
if (offset == 0x0c / 4)
|
|
{
|
|
if (BIT(old ^ data, 0))
|
|
m_tilemap[0]->mark_all_dirty();
|
|
if (BIT(old ^ data, 8))
|
|
m_tilemap[1]->mark_all_dirty();
|
|
}
|
|
}
|
|
|
|
|
|
void skns_state::video_start()
|
|
{
|
|
m_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(skns_state::get_tile_info<0>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 64);
|
|
m_tilemap[0]->set_transparent_pen(0);
|
|
|
|
m_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(skns_state::get_tile_info<1>)), TILEMAP_SCAN_ROWS, 16, 16, 64, 64);
|
|
m_tilemap[1]->set_transparent_pen(0);
|
|
|
|
m_tilemap_bitmap_lower.allocate(512, 512);
|
|
m_tilemap_bitmapflags_lower.allocate(512, 512);
|
|
|
|
m_tilemap_bitmap_higher.allocate(512, 512);
|
|
m_tilemap_bitmapflags_higher.allocate(512, 512);
|
|
|
|
m_gfxdecode->gfx(1)->set_granularity(256);
|
|
m_gfxdecode->gfx(3)->set_granularity(256);
|
|
|
|
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_use_spc_bright = m_use_v3_bright = true;
|
|
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 = false;
|
|
m_alt_enable_background = m_alt_enable_sprites = true;
|
|
}
|
|
|
|
void skns_state::draw_tilemap(bitmap_ind16 &bitmap, bitmap_ind8 &bitmap_flags, const rectangle &cliprect, int which)
|
|
{
|
|
const u32 reg_offs = (which ? 0x34 : 0x10) / 4;
|
|
if (!BIT(m_v3_regs[reg_offs], 0))
|
|
return;
|
|
|
|
const bool nowrap = BIT(m_v3_regs[reg_offs], 2);
|
|
|
|
const u32 startx = m_v3_regs[reg_offs + (0x0c / 4)];
|
|
const u32 starty = m_v3_regs[reg_offs + (0x10 / 4)];
|
|
const int incxx = util::sext(m_v3_regs[reg_offs + (0x14 / 4)] & 0x7ffff, 19);
|
|
const int incxy = m_v3_regs[reg_offs + (0x18 / 4)];
|
|
const int incyx = m_v3_regs[reg_offs + (0x1c / 4)];
|
|
const int incyy = util::sext(m_v3_regs[reg_offs + (0x20 / 4)] & 0x7ffff, 19); // level 3 boss in sengekis
|
|
|
|
const bool columnscroll = BIT(m_v3_regs[0x0c / 4], which ? 9 : 1); // selects column scroll or rowscroll
|
|
|
|
draw_roz(bitmap, bitmap_flags,
|
|
cliprect, m_tilemap[which],
|
|
startx << 8, starty << 8,
|
|
incxx << 8, incxy << 8, incyx << 8, incyy << 8,
|
|
!nowrap, columnscroll,
|
|
&m_v3slc_ram[which << 10]);
|
|
}
|
|
|
|
u32 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);
|
|
|
|
{
|
|
const int tilemap_pri_a = BIT(m_v3_regs[0x10 / 4], 1);
|
|
const int tilemap_pri_b = BIT(m_v3_regs[0x34 / 4], 1);
|
|
|
|
//popmessage("pri %d %d\n", tilemap_pri_a, tilemap_pri_b);
|
|
|
|
/*if (!tilemap_pri_b) { */
|
|
if (m_alt_enable_background)
|
|
{
|
|
draw_tilemap(m_tilemap_bitmap_lower, m_tilemap_bitmapflags_lower, cliprect, 1);
|
|
draw_tilemap(m_tilemap_bitmap_higher, m_tilemap_bitmapflags_higher, cliprect, 0);
|
|
}
|
|
|
|
{
|
|
pen_t const *const clut = &m_palette->pen(0);
|
|
|
|
for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
|
|
{
|
|
u16 const *const src = &m_tilemap_bitmap_lower.pix(y);
|
|
u8 const *const srcflags = &m_tilemap_bitmapflags_lower.pix(y);
|
|
|
|
u16 const *const src2 = &m_tilemap_bitmap_higher.pix(y);
|
|
u8 const *const src2flags = &m_tilemap_bitmapflags_higher.pix(y);
|
|
|
|
u16 const *const src3 = &m_spritegen->bitmap().pix(y);
|
|
|
|
u32 *const dst = &bitmap.pix(y);
|
|
|
|
for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
|
|
{
|
|
u16 pendata = src[x] & 0x7fff;
|
|
u16 pendata2 = src2[x] & 0x7fff;
|
|
u16 pendata3 = m_alt_enable_sprites ? src3[x] & 0x3fff : 0;
|
|
|
|
u16 pri = ((srcflags[x] & 0x07) << 1) | tilemap_pri_b;
|
|
u16 pri2 = ((src2flags[x] & 0x07) << 1) | tilemap_pri_a;
|
|
u16 pri3 = ((src3[x] & 0xc000) >> 12) + 3;
|
|
|
|
const bool pendraw = pendata & 0xff;
|
|
const bool pen2draw = pendata2 & 0xff;
|
|
const bool pen3draw = pendata3 & 0xff;
|
|
|
|
// 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
|
|
|
|
u16 bgpendata;
|
|
u16 bgpri;
|
|
if (pri <= pri2) // <= is good for last level of cyvern.. < seem better for galpanis kaneko logo
|
|
{
|
|
if (pen2draw)
|
|
{
|
|
bgpendata = pendata2 & 0x7fff;
|
|
bgpri = pri2;
|
|
}
|
|
else if (pendraw)
|
|
{
|
|
bgpendata = pendata & 0x7fff;
|
|
bgpri = pri;
|
|
}
|
|
else
|
|
{
|
|
bgpendata = pendata2 & 0x7fff;
|
|
bgpri = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pendraw)
|
|
{
|
|
bgpendata = pendata & 0x7fff;
|
|
bgpri = pri;
|
|
}
|
|
else if (pen2draw)
|
|
{
|
|
bgpendata = pendata2 & 0x7fff;
|
|
bgpri = pri2;
|
|
}
|
|
else
|
|
{
|
|
bgpendata = 0;
|
|
bgpri = 0;
|
|
}
|
|
}
|
|
|
|
// if the sprites are higher than the bg pixel
|
|
u32 coldat = 0;
|
|
if (pri3 > bgpri)
|
|
{
|
|
if (pen3draw)
|
|
{
|
|
coldat = clut[pendata3];
|
|
|
|
if (BIT(coldat, 31))
|
|
{
|
|
const u32 srccolour = clut[bgpendata & 0x7fff];
|
|
const u32 dstcolour = clut[pendata3 & 0x3fff];
|
|
|
|
int r = (srccolour & 0x000000ff) >> 0;
|
|
int g = (srccolour & 0x0000ff00) >> 8;
|
|
int b = (srccolour & 0x00ff0000) >> 16;
|
|
|
|
int r2 = (dstcolour & 0x000000ff) >> 0;
|
|
int g2 = (dstcolour & 0x0000ff00) >> 8;
|
|
int 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 = std::min(r + r2, 255);
|
|
g = std::min(g + g2, 255);
|
|
b = std::min(b + b2, 255);
|
|
|
|
coldat = (r << 0) | (g << 8) | (b << 16);
|
|
}
|
|
else
|
|
{
|
|
coldat = clut[pendata3];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
coldat = clut[bgpendata];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
coldat = clut[bgpendata];
|
|
}
|
|
dst[x] = coldat | (0xffu << 24u);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void skns_state::screen_vblank(int state)
|
|
{
|
|
if (state)
|
|
{
|
|
m_spritegen->draw_sprites(m_screen->visible_area(), m_spriteram, m_spriteram.bytes(), m_spc_regs); // TODO: not all 0x4000 of the sprite RAM area can be displayed on real hardware
|
|
}
|
|
}
|