259 lines
7.9 KiB
C++
259 lines
7.9 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Andrei I. Holub
|
|
/**********************************************************************
|
|
Spectrum Next Layer2
|
|
**********************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "specnext_layer2.h"
|
|
|
|
#include "screen.h"
|
|
|
|
specnext_layer2_device::specnext_layer2_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
|
|
: device_t(mconfig, SPECNEXT_LAYER2, tag, owner, clock)
|
|
, device_gfx_interface(mconfig, *this)
|
|
{
|
|
}
|
|
|
|
specnext_layer2_device &specnext_layer2_device::set_palette(const char *tag, u16 base_offset, u16 alt_offset)
|
|
{
|
|
device_gfx_interface::set_palette(tag);
|
|
m_palette_base_offset = base_offset,
|
|
m_palette_alt_offset = alt_offset;
|
|
|
|
return *this;
|
|
}
|
|
|
|
void specnext_layer2_device::draw_mix(screen_device &screen, bitmap_rgb32 &bitmap, bitmap_rgb32 &blendprio, const rectangle &cliprect, u8 mixer)
|
|
{
|
|
if (!m_layer2_en)
|
|
return;
|
|
|
|
const rgb_t gt0 = rgbexpand<3,3,3>((m_global_transparent << 1) | 0, 6, 3, 0);
|
|
const rgb_t gt1 = rgbexpand<3,3,3>((m_global_transparent << 1) | 1, 6, 3, 0);
|
|
const rgb_t fb = palette().pen_color(0xa00);
|
|
auto blend_op = [gt0, gt1, fb, mixer](u8 &prio, u32 &target, u32 &priotarget, const rgb_t pen, bool is_prio_color)
|
|
{
|
|
if ((pen == gt0) || (pen == gt1))
|
|
{
|
|
target = fb;
|
|
}
|
|
else
|
|
{
|
|
u32 &to = is_prio_color ? priotarget : target;
|
|
if (!prio)
|
|
{
|
|
to = pen;
|
|
}
|
|
else if (mixer == 0)
|
|
{
|
|
to = rgb_t(target) + pen;
|
|
}
|
|
else if (mixer == 1)
|
|
{
|
|
const u8 five = pal3bit(5);
|
|
const rgb_t t = rgb_t(target);
|
|
to = rgb_t(0, rgb_t::clamp(t.r() + pen.r() - five)
|
|
, rgb_t::clamp(t.g() + pen.g() - five)
|
|
, rgb_t::clamp(t.b() + pen.b() - five));
|
|
}
|
|
prio |= 8 * is_prio_color;
|
|
}
|
|
|
|
};
|
|
|
|
if (BIT(m_resolution, 1))
|
|
draw_16(screen, bitmap, blendprio, cliprect, blend_op);
|
|
else
|
|
draw_256(screen, bitmap, blendprio, cliprect, blend_op);
|
|
}
|
|
|
|
void specnext_layer2_device::copyprio(screen_device &screen, bitmap_rgb32 &bitmap, bitmap_rgb32 &blendprio, const rectangle &cliprect)
|
|
{
|
|
if (!m_layer2_en)
|
|
return;
|
|
|
|
for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
|
|
{
|
|
u8 *prio = &screen.priority().pix(y, cliprect.min_x);
|
|
for (int x = cliprect.min_x; x <= cliprect.max_x; x++, prio++)
|
|
{
|
|
if (prio[0] & 8)
|
|
bitmap.pix(y, x) = blendprio.pix(y, x);
|
|
}
|
|
}
|
|
}
|
|
|
|
void specnext_layer2_device::draw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 pcode, u8 priority_mask)
|
|
{
|
|
if (!m_layer2_en)
|
|
return;
|
|
|
|
const rgb_t gt0 = rgbexpand<3,3,3>((m_global_transparent << 1) | 0, 6, 3, 0);
|
|
const rgb_t gt1 = rgbexpand<3,3,3>((m_global_transparent << 1) | 1, 6, 3, 0);
|
|
auto blend_op = [gt0, gt1, pcode, priority_mask](u8 &prio, u32 &target, u32 &priotarget_ignore, const rgb_t pen, bool is_prio_color)
|
|
{
|
|
if ((prio & priority_mask) && !is_prio_color)
|
|
return;
|
|
|
|
const bool is_transparent = (pen == gt0) || (pen == gt1);
|
|
if (!is_transparent)
|
|
{
|
|
target = pen;
|
|
prio |= is_prio_color ? 8 : pcode;
|
|
}
|
|
};
|
|
|
|
if (BIT(m_resolution, 1))
|
|
draw_16(screen, bitmap, bitmap, cliprect, blend_op);
|
|
else
|
|
draw_256(screen, bitmap, bitmap, cliprect, blend_op);
|
|
}
|
|
|
|
template <typename FunctionClass>
|
|
void specnext_layer2_device::draw_256(screen_device &screen, bitmap_rgb32 &bitmap, bitmap_rgb32 &blendprio, const rectangle &cliprect, FunctionClass blend_op)
|
|
{
|
|
const u8 res = m_resolution + 1;
|
|
const u16 (&info)[5] = LAYER2_INFO[m_resolution];
|
|
|
|
rectangle clip = rectangle{ m_clip_x1 << res, (std::min<u16>(m_clip_x2 + 1, info[0]) << res) - 1, m_clip_y1, std::min<u8>(m_clip_y2, info[1] - 1) };
|
|
const u16 offset_h = m_offset_h - (info[2] << 1);
|
|
const u16 offset_v = m_offset_v - info[2];
|
|
clip.offset(offset_h, offset_v);
|
|
clip &= cliprect;
|
|
|
|
do_draw(
|
|
screen, bitmap, blendprio, clip, info, offset_h, offset_v,
|
|
[this, &blend_op] (u16 pen_base, const u8 *scr, u32 *pix, u8 *prio, u32 *bprio, u16 &hpos, u16 &vpos, bool skip_second)
|
|
{
|
|
const u16 idx = pen_base + ((*scr + (m_palette_offset << 4)) % 0x100);
|
|
const rgb_t pen = palette().pen_color(idx);
|
|
const bool is_prio_color = m_pen_priority[idx];
|
|
if (hpos & 1)
|
|
hpos ^= 1;
|
|
else
|
|
blend_op(prio[0], pix[0], bprio[0], pen, is_prio_color);
|
|
|
|
if (!skip_second)
|
|
blend_op(prio[1], pix[1], bprio[1], pen, is_prio_color);
|
|
});
|
|
}
|
|
|
|
template <typename FunctionClass>
|
|
void specnext_layer2_device::draw_16(screen_device &screen, bitmap_rgb32 &bitmap, bitmap_rgb32 &blendprio, const rectangle &cliprect, FunctionClass blend_op)
|
|
{
|
|
const u16 (&info)[5] = LAYER2_INFO[1];
|
|
|
|
rectangle clip = rectangle{ m_clip_x1 << 2, (std::min<u16>(m_clip_x2 + 1, info[0]) << 2) - 1, m_clip_y1, std::min<u8>(m_clip_y2, info[1] - 1) };
|
|
|
|
const u16 offset_h = m_offset_h - (info[2] << 1);
|
|
const u16 offset_v = m_offset_v - info[2];
|
|
clip.offset(offset_h, offset_v);
|
|
clip &= cliprect;
|
|
|
|
do_draw(
|
|
screen, bitmap, blendprio, clip, info, offset_h, offset_v,
|
|
[this, &blend_op] (u16 pen_base, const u8 *scr, u32 *pix, u8 *prio, u32 *bprio, u16 &hpos, u16 &vpos, bool skip_second)
|
|
{
|
|
if (hpos & 1)
|
|
hpos ^= 1;
|
|
else
|
|
{
|
|
const u16 idx = (pen_base | (m_palette_offset << 4)) + (*scr >> 4);
|
|
const rgb_t pen = palette().pen_color(idx);
|
|
const bool is_prio_color = m_pen_priority[idx];
|
|
blend_op(prio[0], pix[0], bprio[0], pen, is_prio_color);
|
|
}
|
|
|
|
if (!skip_second)
|
|
{
|
|
const u16 idx = (pen_base | (m_palette_offset << 4)) + (*scr & 0x0f);
|
|
const rgb_t pen = palette().pen_color(idx);
|
|
const bool is_prio_color = m_pen_priority[idx];
|
|
blend_op(prio[1], pix[1], bprio[1], pen, is_prio_color);
|
|
}
|
|
});
|
|
}
|
|
|
|
template <typename FunctionClass>
|
|
void specnext_layer2_device::do_draw(screen_device &screen, bitmap_rgb32 &bitmap, bitmap_rgb32 &blendprio, const rectangle &clip, const u16 (&info)[5], u16 offset_h, u16 offset_v, FunctionClass plot_op)
|
|
{
|
|
if (clip.empty())
|
|
return;
|
|
|
|
const u16 pen_base = m_layer2_palette_select ? m_palette_alt_offset : m_palette_base_offset;
|
|
u16 x_min = ((clip.left() - offset_h) >> 1) + m_scroll_x;
|
|
const bool x_scrollover = m_scroll_x >= info[0] && info[3] == 256;
|
|
if (x_scrollover)
|
|
x_min -= info[0]; // scrolls over
|
|
else
|
|
x_min %= info[0]; // wraps around
|
|
|
|
for (u16 vpos = clip.top(); vpos <= clip.bottom(); vpos++)
|
|
{
|
|
u16 y = vpos - offset_v + m_scroll_y;
|
|
if (m_scroll_y >= info[1] && info[4] == 256)
|
|
y -= info[1]; // scrolls over
|
|
else
|
|
y %= info[1]; // wraps around
|
|
|
|
u16 x = x_min;
|
|
const u8 *scr = m_host_ram_ptr + (m_layer2_active_bank << 14) + (y * info[4]) + (x * info[3]);
|
|
u32 *pix = &(bitmap.pix(vpos, clip.left()));
|
|
u8 *prio = &(screen.priority().pix(vpos, clip.left()));
|
|
u32 *bprio = &(blendprio.pix(vpos, clip.left()));
|
|
if (clip.left() & 1)
|
|
{
|
|
pix -= 1;
|
|
prio -= 1;
|
|
bprio -= 1;
|
|
}
|
|
for (u16 hpos = clip.left(); hpos <= clip.right(); hpos += 2, pix += 2, prio += 2, bprio += 2)
|
|
{
|
|
// (hpos & 1) skips first pixel reading and decreases hpos reference.
|
|
plot_op(pen_base, scr, pix, prio, bprio, hpos, vpos, hpos == clip.right() && (~hpos & 1));
|
|
|
|
++x %= info[0];
|
|
if (x == 0 && !x_scrollover)
|
|
scr = m_host_ram_ptr + (m_layer2_active_bank << 14) + (y * info[4]);
|
|
else
|
|
scr += info[3];
|
|
}
|
|
}
|
|
}
|
|
|
|
void specnext_layer2_device::device_add_mconfig(machine_config &config)
|
|
{
|
|
m_offset_h = 0;
|
|
m_offset_v = 0;
|
|
}
|
|
|
|
void specnext_layer2_device::device_start()
|
|
{
|
|
save_item(NAME(m_offset_h));
|
|
save_item(NAME(m_offset_v));
|
|
save_item(NAME(m_global_transparent));
|
|
save_item(NAME(m_layer2_palette_select));
|
|
save_item(NAME(m_pen_priority));
|
|
|
|
save_item(NAME(m_layer2_en));
|
|
save_item(NAME(m_resolution));
|
|
save_item(NAME(m_palette_offset));
|
|
save_item(NAME(m_layer2_active_bank));
|
|
|
|
save_item(NAME(m_scroll_x));
|
|
save_item(NAME(m_scroll_y));
|
|
save_item(NAME(m_clip_x1));
|
|
save_item(NAME(m_clip_x2));
|
|
save_item(NAME(m_clip_y1));
|
|
save_item(NAME(m_clip_y2));
|
|
}
|
|
|
|
void specnext_layer2_device::device_reset()
|
|
{
|
|
memset(m_pen_priority, 0, 512 * 4);
|
|
}
|
|
|
|
// device type definition
|
|
DEFINE_DEVICE_TYPE(SPECNEXT_LAYER2, specnext_layer2_device, "layer2", "Spectrum Next Layer2")
|