// license:BSD-3-Clause
// copyright-holders:Luca Elia
/***************************************************************************
CES Blitter, with two layers and double buffering (Xilinx FPGA)
Offset: Bits: Value:
00
02 fedc ba-- ---- ----
---- --9- ---- ---- Layer 1 Buffer To Display
---- ---8 ---- ---- Layer 0 Buffer To Display
---- ---- 7654 3210
04 Width
06 X
08 Height - 1
0A Y
0C Source Address (low)
0E Source Address (mid)
10 fedc ba-- ---- ----
---- --9- ---- ---- Pen Replacement Mode
---- ---8 ---- ----
---- ---- 7--- ---- Layer To Draw To
---- ---- -6-- ---- Draw To Not Displayed Buffer
---- ---- --5- ---- Solid Fill
---- ---- ---4 ---- Enable VBlank IRQ (e.g. level 3) (0 clears the IRQ line too)
---- ---- ---- 3--- Enable Blitter Finished IRQ (e.g. level 2) ""
---- ---- ---- -2-- Enable Scanline IRQ (e.g. level 1) ""
---- ---- ---- --1- Flip Y
---- ---- ---- ---0 Flip X
Addr_Hi Register:
fedc ba98 ---- ---- Solid Fill Pen
---- ---- 7654 3210 Source Address (high)
Color Register:
fedc ba98 ---- ---- Source Pen (Pen Replacement Mode)
---- ---- 7654 3210 Palette (0-f) / Destination Pen (Pen Replacement Mode)
A write to the source address (mid) triggers the blit.
A the end of the blit, an IRQ is issued.
***************************************************************************/
#include "emu.h"
#include "cesblit.h"
#include "screen.h"
/***************************************************************************
GLOBAL VARIABLES
***************************************************************************/
// device type definition
const device_type CESBLIT = device_creator<cesblit_device>;
/***************************************************************************
LIVE DEVICE
***************************************************************************/
//-------------------------------------------------
// cesblit_device - constructor
//-------------------------------------------------
cesblit_device::cesblit_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, CESBLIT, "CES Blitter FPGA", tag, owner, clock, "cesblit", __FILE__),
device_video_interface(mconfig, *this),
device_memory_interface(mconfig, *this),
m_space_config("blitter_space", ENDIANNESS_BIG, 16,23),
m_blit_irq_cb(*this)
{ }
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void cesblit_device::device_start()
{
// resolve callbacks
m_blit_irq_cb.resolve();
// default to rom reading from a region
m_space = &space(AS_PROGRAM);
memory_region *region = memregion(tag());
if (region)
m_space->install_rom(0, region->bytes() - 1, region->base());
// bitmaps
for (int layer = 0; layer < 2; ++layer)
{
for (int buffer = 0; buffer < 2; ++buffer)
{
m_bitmap[layer][buffer].allocate(512, 512);
m_bitmap[layer][buffer].fill(0xff);
}
}
}
//-------------------------------------------------
// device_stop - device-specific stop
//-------------------------------------------------
void cesblit_device::device_stop()
{
for (int layer = 0; layer < 2; ++layer)
{
for (int buffer = 0; buffer < 2; ++buffer)
{
m_bitmap[layer][buffer].reset();
}
}
}
/***************************************************************************
READ/WRITE HANDLERS
***************************************************************************/
READ16_MEMBER(cesblit_device::status_r)
{
return 0x0000; // bit 7 = blitter busy
}
WRITE16_MEMBER(cesblit_device::regs_w)
{
uint16_t olddata = m_regs[offset];
uint16_t newdata = COMBINE_DATA( &m_regs[offset] );
switch (offset)
{
// case 0x00/2: // bit 15: FPGA programming serial in (lsb first)
case 0x10/2:
if (!m_blit_irq_cb.isnull() && !BIT(olddata, 3) && BIT(newdata, 3))
m_blit_irq_cb(CLEAR_LINE);
break;
case 0x0e/2:
do_blit();
if (!m_blit_irq_cb.isnull() && BIT(m_regs[0x10/2], 3))
m_blit_irq_cb(ASSERT_LINE);
break;
}
}
WRITE16_MEMBER(cesblit_device::color_w)
{
COMBINE_DATA( &m_color );
}
WRITE16_MEMBER(cesblit_device::addr_hi_w)
{
COMBINE_DATA( &m_addr_hi );
}
void cesblit_device::do_blit()
{
int buffer = (m_regs[0x02/2] >> 8) & 3; // 1 bit per layer, selects the currently displayed buffer
int sw = m_regs[0x04/2];
int sx = m_regs[0x06/2];
int sh = m_regs[0x08/2] + 1;
int sy = m_regs[0x0a/2];
int addr = (*m_compute_addr)(m_regs[0x0c/2], m_regs[0x0e/2], m_addr_hi);
int mode = m_regs[0x10/2];
int layer = (mode >> 7) & 1; // layer to draw to
buffer = ((mode >> 6) & 1) ^ ((buffer >> layer) & 1); // bit 6 selects whether to use the opposite buffer to that displayed
addr <<= 1;
#ifdef MAME_DEBUG
#if 0
logerror("%s: blit w %03x, h %02x, x %03x, y %02x, src %06x, fill/addr_hi %04x, repl/color %04x, mode %02x\n", machine().describe_context(),
sw,sh,sx,sy, addr, m_addr_hi, m_color, mode
);
#endif
#endif
sx &= 0x1ff;
sw &= 0x1ff; // can be 0: draw nothing (see e.g. fade-in effect in galgame3/diamond derby)
sy &= 0x1ff;
sh &= 0x1ff;
int flipx = mode & 1;
int flipy = mode & 2;
int x0,x1,y0,y1,dx,dy;
if (flipx) { x0 = sw-1; x1 = -1; dx = -1; sx -= sw-1; }
else { x0 = 0; x1 = sw; dx = +1; }
if (flipy) { y0 = sh-1; y1 = -1; dy = -1; sy -= sh-1; }
else { y0 = 0; y1 = sh; dy = +1; }
int color = (m_color & 0x0f) << 8;
// Draw
bitmap_ind16 &bitmap = m_bitmap[layer][buffer];
int x,y;
uint16_t pen;
switch (mode & 0x20)
{
case 0x00: // blit from ROM
if ( mode & 0x200 )
{
// copy from ROM, replacing occurrences of src pen with dst pen
uint8_t dst_pen = (m_color >> 8) & 0xff;
uint8_t src_pen = (m_color >> 0) & 0xff;
for (y = y0; y != y1; y += dy)
{
for (x = x0; x != x1; x += dx)
{
pen = m_space->read_byte(addr);
if (pen == src_pen)
pen = dst_pen;
if (pen != 0xff)
bitmap.pix16((sy + y) & 0x1ff, (sx + x) & 0x1ff) = pen + color;
++addr;
}
}
}
else
{
// copy from ROM as is
for (y = y0; y != y1; y += dy)
{
for (x = x0; x != x1; x += dx)
{
pen = m_space->read_byte(addr);
if (pen != 0xff)
bitmap.pix16((sy + y) & 0x1ff, (sx + x) & 0x1ff) = pen + color;
++addr;
}
}
}
break;
case 0x20: // solid fill
pen = ((m_addr_hi >> 8) & 0xff) + color;
if ((pen & 0xff) == 0xff)
pen = 0xff;
for (y = y0; y != y1; y += dy)
{
for (x = x0; x != x1; x += dx)
{
bitmap.pix16((sy + y) & 0x1ff, (sx + x) & 0x1ff) = pen;
}
}
break;
}
}
uint32_t cesblit_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
int layers_ctrl = -1;
#ifdef MAME_DEBUG
if (machine().input().code_pressed(KEYCODE_Z))
{
int mask = 0;
if (machine().input().code_pressed(KEYCODE_Q)) mask |= 1;
if (machine().input().code_pressed(KEYCODE_W)) mask |= 2;
if (mask != 0) layers_ctrl &= mask;
}
#endif
if (layers_ctrl & 1) copybitmap_trans(bitmap, m_bitmap[0][(m_regs[0x02/2]>>8)&1], 0,0,0,0, cliprect, 0xff);
if (layers_ctrl & 2) copybitmap_trans(bitmap, m_bitmap[1][(m_regs[0x02/2]>>9)&1], 0,0,0,0, cliprect, 0xff);
return 0;
}