mirror of
https://github.com/holub/mame
synced 2025-10-06 17:08:28 +03:00

Moved graphics decoding to a new device interface class: device_gfx_interface. The gfxdecode device is now a device that simply inherits this interface and does nothing else. Devices that draw tilemaps or sprites using gfx_elements should in time be updated to use this interface rather than connect to a machine-global gfxdecode device. Updated toaplan_scu.c as an example (also fixed off-by-one sprite alignment in twincobr and rallybik while I was at it). gfx_elements are normally created in interface_post_start(), making it possible to dynamically create or modify the graphics decoding info during device_start() if you need to. On the other hand, if you need the gfx_elements during device_start(), you can directly call decode_gfx() to create them early. This interface also provides a standard and init-order-safe way to connect to a palette device (similarly to how device_video_interface helps devices connect to a screen), so it's handy for any device that does palettized drawing even if it doesn't use gfx_elements. Updated k053250.c as an example of this usage. gfxdecode info entries can now reference shared RAM regions by tag as well as ROM regions, automatically handle endianness, and have some other new capabilities. Updated nemesis.c and pgm.c to showcase the new features. Removed validate_display() (it was just a commented out stub already) since its only function, checking that drivers don't have an ind16 screen without a palette, is now done by screen_device::device_validity_check(). Updated obsolete comments about GFXLAYOUT_RAW (cps1.c hasn't used raw gfx for years, and "to save memory" is no longer a good reason to use it)
381 lines
11 KiB
C
381 lines
11 KiB
C
/***************************************************************************
|
|
|
|
digfx.c
|
|
|
|
Device graphics interfaces.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "validity.h"
|
|
|
|
|
|
//**************************************************************************
|
|
// DEVICE GFX INTERFACE
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// device_gfx_interface - constructor
|
|
//-------------------------------------------------
|
|
|
|
device_gfx_interface::device_gfx_interface(const machine_config &mconfig, device_t &device,
|
|
const gfx_decode_entry *gfxinfo, const char *palette_tag)
|
|
: device_interface(device),
|
|
m_gfxdecodeinfo(gfxinfo),
|
|
m_palette_tag(palette_tag),
|
|
m_palette_is_sibling(palette_tag == NULL),
|
|
m_decoded(false)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// ~device_gfx_interface - destructor
|
|
//-------------------------------------------------
|
|
|
|
device_gfx_interface::~device_gfx_interface()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// static_set_info: configuration helper to set
|
|
// the gfxdecode info used by the device
|
|
//-------------------------------------------------
|
|
|
|
void device_gfx_interface::static_set_info(device_t &device, const gfx_decode_entry *gfxinfo)
|
|
{
|
|
device_gfx_interface *gfx;
|
|
if (!device.interface(gfx))
|
|
throw emu_fatalerror("MCFG_GFX_INFO called on device '%s' with no gfx interface\n", device.tag());
|
|
|
|
gfx->m_gfxdecodeinfo = gfxinfo;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// static_set_palette: configuration helper to
|
|
// set the palette used by the device
|
|
//-------------------------------------------------
|
|
|
|
void device_gfx_interface::static_set_palette(device_t &device, const char *tag)
|
|
{
|
|
device_gfx_interface *gfx;
|
|
if (!device.interface(gfx))
|
|
throw emu_fatalerror("MCFG_GFX_PALETTE called on device '%s' with no gfx interface\n", device.tag());
|
|
|
|
gfx->m_palette_tag = tag;
|
|
gfx->m_palette_is_sibling = true;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// interface_pre_start - make sure all our input
|
|
// devices are started
|
|
//-------------------------------------------------
|
|
|
|
void device_gfx_interface::interface_pre_start()
|
|
{
|
|
if (m_palette_tag == NULL)
|
|
fatalerror("No palette specified for device '%s'\n", device().tag());
|
|
|
|
// find our palette device, either as a sibling device or subdevice
|
|
if (m_palette_is_sibling)
|
|
m_palette = device().siblingdevice<palette_device>(m_palette_tag);
|
|
else
|
|
m_palette = device().subdevice<palette_device>(m_palette_tag);
|
|
|
|
if (m_palette == NULL)
|
|
fatalerror("Device '%s' specifies nonexistent %sdevice '%s' as palette\n",
|
|
device().tag(),
|
|
(m_palette_is_sibling ? "sibling " : "sub"),
|
|
m_palette_tag);
|
|
|
|
// if palette device isn't started, wait for it
|
|
if (!m_palette->started())
|
|
throw device_missing_dependencies();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// interface_post_start - decode gfx, if we
|
|
// haven't done so already
|
|
//-------------------------------------------------
|
|
|
|
void device_gfx_interface::interface_post_start()
|
|
{
|
|
if (!m_decoded)
|
|
decode_gfx(m_gfxdecodeinfo);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// decode_gfx - parse gfx decode info and
|
|
// create gfx elements
|
|
//-------------------------------------------------
|
|
|
|
void device_gfx_interface::decode_gfx(const gfx_decode_entry *gfxdecodeinfo)
|
|
{
|
|
// skip if nothing to do
|
|
if (gfxdecodeinfo == NULL)
|
|
return;
|
|
|
|
// local variables to hold mutable copies of gfx layout data
|
|
gfx_layout glcopy;
|
|
dynamic_array<UINT32> extxoffs(0);
|
|
dynamic_array<UINT32> extyoffs(0);
|
|
|
|
// loop over all elements
|
|
for (int curgfx = 0; curgfx < MAX_GFX_ELEMENTS && gfxdecodeinfo[curgfx].gfxlayout != NULL; curgfx++)
|
|
{
|
|
const gfx_decode_entry &gfx = gfxdecodeinfo[curgfx];
|
|
|
|
// extract the scale factors and xormask
|
|
UINT32 xscale = GFXENTRY_GETXSCALE(gfx.flags);
|
|
UINT32 yscale = GFXENTRY_GETYSCALE(gfx.flags);
|
|
UINT32 xormask = GFXENTRY_ISREVERSE(gfx.flags) ? 7 : 0;
|
|
|
|
// resolve the region
|
|
UINT32 region_length;
|
|
const UINT8 *region_base;
|
|
UINT8 region_width;
|
|
endianness_t region_endianness;
|
|
|
|
if (gfx.memory_region != NULL)
|
|
{
|
|
device_t &basedevice = (GFXENTRY_ISDEVICE(gfx.flags)) ? device() : *device().owner();
|
|
if (GFXENTRY_ISRAM(gfx.flags))
|
|
{
|
|
memory_share *share = basedevice.memshare(gfx.memory_region);
|
|
assert(share != NULL);
|
|
region_length = 8 * share->bytes();
|
|
region_base = reinterpret_cast<UINT8 *>(share->ptr());
|
|
region_width = share->width() / 8;
|
|
region_endianness = share->endianness();
|
|
}
|
|
else
|
|
{
|
|
memory_region *region = basedevice.memregion(gfx.memory_region);
|
|
assert(region != NULL);
|
|
region_length = 8 * region->bytes();
|
|
region_base = region->base();
|
|
// FIXME
|
|
region_width = 1;
|
|
region_endianness = ENDIANNESS_NATIVE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
region_length = 0;
|
|
region_base = NULL;
|
|
region_width = 1;
|
|
region_endianness = ENDIANNESS_NATIVE;
|
|
}
|
|
|
|
if (region_endianness != ENDIANNESS_NATIVE)
|
|
{
|
|
switch (region_width)
|
|
{
|
|
case 2:
|
|
xormask |= 0x08;
|
|
break;
|
|
case 4:
|
|
xormask |= 0x18;
|
|
break;
|
|
case 8:
|
|
xormask |= 0x38;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// copy the layout into our temporary variable
|
|
memcpy(&glcopy, gfx.gfxlayout, sizeof(gfx_layout));
|
|
|
|
// copy the X and Y offsets into our temporary arrays
|
|
extxoffs.resize(glcopy.width * xscale);
|
|
extyoffs.resize(glcopy.height * yscale);
|
|
memcpy(&extxoffs[0], glcopy.xoffset, glcopy.width * sizeof(UINT32));
|
|
memcpy(&extyoffs[0], glcopy.yoffset, glcopy.height * sizeof(UINT32));
|
|
|
|
// if there are extended offsets, copy them over top
|
|
if (glcopy.extxoffs != NULL)
|
|
memcpy(&extxoffs[0], glcopy.extxoffs, glcopy.width * sizeof(UINT32));
|
|
if (glcopy.extyoffs != NULL)
|
|
memcpy(&extyoffs[0], glcopy.extyoffs, glcopy.height * sizeof(UINT32));
|
|
|
|
// always use the extended offsets here
|
|
glcopy.extxoffs = extxoffs;
|
|
glcopy.extyoffs = extyoffs;
|
|
|
|
// expand X and Y by the scale factors
|
|
if (xscale > 1)
|
|
{
|
|
glcopy.width *= xscale;
|
|
for (int j = glcopy.width - 1; j >= 0; j--)
|
|
extxoffs[j] = extxoffs[j / xscale];
|
|
}
|
|
if (yscale > 1)
|
|
{
|
|
glcopy.height *= yscale;
|
|
for (int j = glcopy.height - 1; j >= 0; j--)
|
|
extyoffs[j] = extyoffs[j / yscale];
|
|
}
|
|
|
|
// if the character count is a region fraction, compute the effective total
|
|
if (IS_FRAC(glcopy.total))
|
|
{
|
|
assert(region_length != 0);
|
|
glcopy.total = region_length / glcopy.charincrement * FRAC_NUM(glcopy.total) / FRAC_DEN(glcopy.total);
|
|
}
|
|
|
|
// for non-raw graphics, decode the X and Y offsets
|
|
if (glcopy.planeoffset[0] != GFX_RAW)
|
|
{
|
|
// loop over all the planes, converting fractions
|
|
for (int j = 0; j < glcopy.planes; j++)
|
|
{
|
|
UINT32 value1 = glcopy.planeoffset[j];
|
|
if (IS_FRAC(value1))
|
|
{
|
|
assert(region_length != 0);
|
|
glcopy.planeoffset[j] = FRAC_OFFSET(value1) + region_length * FRAC_NUM(value1) / FRAC_DEN(value1);
|
|
}
|
|
}
|
|
|
|
// loop over all the X/Y offsets, converting fractions
|
|
for (int j = 0; j < glcopy.width; j++)
|
|
{
|
|
UINT32 value2 = extxoffs[j];
|
|
if (IS_FRAC(value2))
|
|
{
|
|
assert(region_length != 0);
|
|
extxoffs[j] = FRAC_OFFSET(value2) + region_length * FRAC_NUM(value2) / FRAC_DEN(value2);
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < glcopy.height; j++)
|
|
{
|
|
UINT32 value3 = extyoffs[j];
|
|
if (IS_FRAC(value3))
|
|
{
|
|
assert(region_length != 0);
|
|
extyoffs[j] = FRAC_OFFSET(value3) + region_length * FRAC_NUM(value3) / FRAC_DEN(value3);
|
|
}
|
|
}
|
|
}
|
|
|
|
// otherwise, just use the line modulo
|
|
else
|
|
{
|
|
int base = gfx.start;
|
|
int end = region_length/8;
|
|
int linemod = glcopy.yoffset[0];
|
|
while (glcopy.total > 0)
|
|
{
|
|
int elementbase = base + (glcopy.total - 1) * glcopy.charincrement / 8;
|
|
int lastpixelbase = elementbase + glcopy.height * linemod / 8 - 1;
|
|
if (lastpixelbase < end)
|
|
break;
|
|
glcopy.total--;
|
|
}
|
|
}
|
|
|
|
// allocate the graphics
|
|
m_gfx[curgfx].reset(global_alloc(gfx_element(m_palette, glcopy, (region_base != NULL) ? region_base + gfx.start : NULL, xormask, gfx.total_color_codes, gfx.color_codes_start)));
|
|
}
|
|
|
|
m_decoded = true;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// interface_validity_check - validate graphics
|
|
// decoding configuration
|
|
//-------------------------------------------------
|
|
|
|
void device_gfx_interface::interface_validity_check(validity_checker &valid) const
|
|
{
|
|
// validate palette tag
|
|
if (m_palette_tag == NULL)
|
|
mame_printf_error("No palette specified for device '%s'\n", device().tag());
|
|
else
|
|
{
|
|
palette_device *palette;
|
|
if (m_palette_is_sibling)
|
|
palette = device().siblingdevice<palette_device>(m_palette_tag);
|
|
else
|
|
palette = device().subdevice<palette_device>(m_palette_tag);
|
|
|
|
if (palette == NULL)
|
|
mame_printf_error("Device '%s' specifies nonexistent %sdevice '%s' as palette\n",
|
|
device().tag(),
|
|
(m_palette_is_sibling ? "sibling " : "sub"),
|
|
m_palette_tag);
|
|
}
|
|
|
|
if (!m_gfxdecodeinfo)
|
|
return;
|
|
|
|
// validate graphics decoding entries
|
|
for (int gfxnum = 0; gfxnum < MAX_GFX_ELEMENTS && m_gfxdecodeinfo[gfxnum].gfxlayout != NULL; gfxnum++)
|
|
{
|
|
const gfx_decode_entry &gfx = m_gfxdecodeinfo[gfxnum];
|
|
const gfx_layout &layout = *gfx.gfxlayout;
|
|
|
|
// currently we are unable to validate RAM-based entries
|
|
const char *region = gfx.memory_region;
|
|
if (region != NULL && GFXENTRY_ISROM(gfx.flags))
|
|
{
|
|
// resolve the region
|
|
astring gfxregion;
|
|
if (GFXENTRY_ISDEVICE(gfx.flags))
|
|
device().subtag(gfxregion, region);
|
|
else
|
|
device().owner()->subtag(gfxregion, region);
|
|
|
|
UINT32 region_length = valid.region_length(gfxregion);
|
|
if (region_length == 0)
|
|
mame_printf_error("gfx[%d] references nonexistent region '%s'\n", gfxnum, gfxregion.cstr());
|
|
|
|
// if we have a valid region, and we're not using auto-sizing, check the decode against the region length
|
|
else if (!IS_FRAC(layout.total))
|
|
{
|
|
// determine which plane is at the largest offset
|
|
int start = 0;
|
|
for (int plane = 0; plane < layout.planes; plane++)
|
|
if (layout.planeoffset[plane] > start)
|
|
start = layout.planeoffset[plane];
|
|
start &= ~(layout.charincrement - 1);
|
|
|
|
// determine the total length based on this info
|
|
int len = layout.total * layout.charincrement;
|
|
|
|
// do we have enough space in the region to cover the whole decode?
|
|
int avail = region_length - (gfx.start & ~(layout.charincrement / 8 - 1));
|
|
|
|
// if not, this is an error
|
|
if ((start + len) / 8 > avail)
|
|
mame_printf_error("gfx[%d] extends past allocated memory of region '%s'\n", gfxnum, region);
|
|
}
|
|
}
|
|
|
|
int xscale = GFXENTRY_GETXSCALE(gfx.flags);
|
|
int yscale = GFXENTRY_GETYSCALE(gfx.flags);
|
|
|
|
// verify raw decode, which can only be full-region and have no scaling
|
|
if (layout.planeoffset[0] == GFX_RAW)
|
|
{
|
|
if (layout.total != RGN_FRAC(1,1))
|
|
mame_printf_error("gfx[%d] with unsupported layout total\n", gfxnum);
|
|
if (xscale != 1 || yscale != 1)
|
|
mame_printf_error("gfx[%d] with unsupported xscale/yscale\n", gfxnum);
|
|
}
|
|
|
|
// verify traditional decode doesn't have too many planes
|
|
else
|
|
{
|
|
if (layout.planes > MAX_GFX_PLANES)
|
|
mame_printf_error("gfx[%d] with invalid planes\n", gfxnum);
|
|
}
|
|
}
|
|
}
|