mame/src/emu/addrmap.cpp
2020-06-19 12:38:41 +02:00

1382 lines
43 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
addrmap.cpp
Macros and helper functions for handling address map definitions.
***************************************************************************/
#include "emu.h"
#include "romload.h"
#include "validity.h"
//**************************************************************************
// PARAMETERS
//**************************************************************************
#define DETECT_OVERLAPPING_MEMORY (0)
/*-------------------------------------------------
core_i64_hex_format - i64 format printf helper
why isn't fatalerror going through the same
channels as logerror exactly?
-------------------------------------------------*/
static char *core_i64_hex_format(u64 value, u8 mindigits)
{
static char buffer[16][64];
// TODO: this can overflow - e.g. when a lot of unmapped writes are logged
static int index;
char *bufbase = &buffer[index++ % 16][0];
char *bufptr = bufbase;
s8 curdigit;
for (curdigit = 15; curdigit >= 0; curdigit--)
{
int nibble = (value >> (curdigit * 4)) & 0xf;
if (nibble != 0 || curdigit < mindigits)
{
mindigits = curdigit;
*bufptr++ = "0123456789ABCDEF"[nibble];
}
}
if (bufptr == bufbase)
*bufptr++ = '0';
*bufptr = 0;
return bufbase;
}
//**************************************************************************
// ADDRESS MAP ENTRY
//**************************************************************************
//-------------------------------------------------
// address_map_entry - constructor
//-------------------------------------------------
address_map_entry::address_map_entry(device_t &device, address_map &map, offs_t start, offs_t end)
: m_next(nullptr)
, m_map(map)
, m_devbase(device)
, m_addrstart(start)
, m_addrend(end)
, m_addrmirror(0)
, m_addrmask(0)
, m_addrselect(0)
, m_mask(0)
, m_cswidth(0)
, m_share(nullptr)
, m_region(nullptr)
, m_rgnoffs(0)
, m_rproto8(device)
, m_rproto16(device)
, m_rproto32(device)
, m_rproto64(device)
, m_wproto8(device)
, m_wproto16(device)
, m_wproto32(device)
, m_wproto64(device)
, m_rproto8m(device)
, m_rproto16m(device)
, m_rproto32m(device)
, m_rproto64m(device)
, m_wproto8m(device)
, m_wproto16m(device)
, m_wproto32m(device)
, m_wproto64m(device)
, m_rproto8s(device)
, m_rproto16s(device)
, m_rproto32s(device)
, m_rproto64s(device)
, m_wproto8s(device)
, m_wproto16s(device)
, m_wproto32s(device)
, m_wproto64s(device)
, m_rproto8sm(device)
, m_rproto16sm(device)
, m_rproto32sm(device)
, m_rproto64sm(device)
, m_wproto8sm(device)
, m_wproto16sm(device)
, m_wproto32sm(device)
, m_wproto64sm(device)
, m_rproto8mo(device)
, m_rproto16mo(device)
, m_rproto32mo(device)
, m_rproto64mo(device)
, m_wproto8mo(device)
, m_wproto16mo(device)
, m_wproto32mo(device)
, m_wproto64mo(device)
, m_rproto8smo(device)
, m_rproto16smo(device)
, m_rproto32smo(device)
, m_rproto64smo(device)
, m_wproto8smo(device)
, m_wproto16smo(device)
, m_wproto32smo(device)
, m_wproto64smo(device)
, m_submap_device(nullptr)
, m_memory(nullptr)
{
}
//-------------------------------------------------
// mask - set the address mask value
//-------------------------------------------------
address_map_entry &address_map_entry::mask(offs_t _mask)
{
m_addrmask = _mask;
if (m_map.m_globalmask != 0)
m_addrmask &= m_map.m_globalmask;
return *this;
}
//-------------------------------------------------
// umask16 - set a 16-bits unitmask value
//-------------------------------------------------
address_map_entry &address_map_entry::umask16(u16 _mask)
{
m_mask = (u64(_mask) << 48) | (u64(_mask) << 32) | (u64(_mask) << 16) | _mask;
return *this;
}
//-------------------------------------------------
// umask32 - set a 32-bits unitmask value
//-------------------------------------------------
address_map_entry &address_map_entry::umask32(u32 _mask)
{
m_mask = (u64(_mask) << 32) | _mask;
return *this;
}
//-------------------------------------------------
// umask64 - set a 64-bits unitmask value
//-------------------------------------------------
address_map_entry &address_map_entry::umask64(u64 _mask)
{
m_mask = _mask;
return *this;
}
//-------------------------------------------------
// m - set up a handler for
// retrieve a submap from a device
//-------------------------------------------------
address_map_entry &address_map_entry::m(const char *tag, address_map_constructor func)
{
m_read.m_type = AMH_DEVICE_SUBMAP;
m_read.m_tag = tag;
m_write.m_type = AMH_DEVICE_SUBMAP;
m_write.m_tag = tag;
m_submap_device = nullptr;
m_submap_delegate = func;
return *this;
}
address_map_entry &address_map_entry::m(device_t *device, address_map_constructor func)
{
m_read.m_type = AMH_DEVICE_SUBMAP;
m_read.m_tag = nullptr;
m_write.m_type = AMH_DEVICE_SUBMAP;
m_write.m_tag = nullptr;
m_submap_device = device;
m_submap_delegate = func;
return *this;
}
//-------------------------------------------------
// r/w/rw - handler setters for
// 8-bit read/write delegates
//-------------------------------------------------
address_map_entry &address_map_entry::r(read8_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE;
m_read.m_bits = 8;
m_read.m_name = func.name();
m_rproto8 = func;
return *this;
}
address_map_entry &address_map_entry::w(write8_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE;
m_write.m_bits = 8;
m_write.m_name = func.name();
m_wproto8 = func;
return *this;
}
address_map_entry &address_map_entry::r(read8m_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_M;
m_read.m_bits = 8;
m_read.m_name = func.name();
m_rproto8m = func;
return *this;
}
address_map_entry &address_map_entry::w(write8m_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_M;
m_write.m_bits = 8;
m_write.m_name = func.name();
m_wproto8m = func;
return *this;
}
address_map_entry &address_map_entry::r(read8s_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_S;
m_read.m_bits = 8;
m_read.m_name = func.name();
m_rproto8s = func;
return *this;
}
address_map_entry &address_map_entry::w(write8s_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_S;
m_write.m_bits = 8;
m_write.m_name = func.name();
m_wproto8s = func;
return *this;
}
address_map_entry &address_map_entry::r(read8sm_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_SM;
m_read.m_bits = 8;
m_read.m_name = func.name();
m_rproto8sm = func;
return *this;
}
address_map_entry &address_map_entry::w(write8sm_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_SM;
m_write.m_bits = 8;
m_write.m_name = func.name();
m_wproto8sm = func;
return *this;
}
address_map_entry &address_map_entry::r(read8mo_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_MO;
m_read.m_bits = 8;
m_read.m_name = func.name();
m_rproto8mo = func;
return *this;
}
address_map_entry &address_map_entry::w(write8mo_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_MO;
m_write.m_bits = 8;
m_write.m_name = func.name();
m_wproto8mo = func;
return *this;
}
address_map_entry &address_map_entry::r(read8smo_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_SMO;
m_read.m_bits = 8;
m_read.m_name = func.name();
m_rproto8smo = func;
return *this;
}
address_map_entry &address_map_entry::w(write8smo_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_SMO;
m_write.m_bits = 8;
m_write.m_name = func.name();
m_wproto8smo = func;
return *this;
}
//-------------------------------------------------
// r/w/rw - handler setters for
// 16-bit read/write delegates
//-------------------------------------------------
address_map_entry &address_map_entry::r(read16_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE;
m_read.m_bits = 16;
m_read.m_name = func.name();
m_rproto16 = func;
return *this;
}
address_map_entry &address_map_entry::w(write16_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE;
m_write.m_bits = 16;
m_write.m_name = func.name();
m_wproto16 = func;
return *this;
}
address_map_entry &address_map_entry::r(read16m_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_M;
m_read.m_bits = 16;
m_read.m_name = func.name();
m_rproto16m = func;
return *this;
}
address_map_entry &address_map_entry::w(write16m_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_M;
m_write.m_bits = 16;
m_write.m_name = func.name();
m_wproto16m = func;
return *this;
}
address_map_entry &address_map_entry::r(read16s_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_S;
m_read.m_bits = 16;
m_read.m_name = func.name();
m_rproto16s = func;
return *this;
}
address_map_entry &address_map_entry::w(write16s_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_S;
m_write.m_bits = 16;
m_write.m_name = func.name();
m_wproto16s = func;
return *this;
}
address_map_entry &address_map_entry::r(read16sm_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_SM;
m_read.m_bits = 16;
m_read.m_name = func.name();
m_rproto16sm = func;
return *this;
}
address_map_entry &address_map_entry::w(write16sm_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_SM;
m_write.m_bits = 16;
m_write.m_name = func.name();
m_wproto16sm = func;
return *this;
}
address_map_entry &address_map_entry::r(read16mo_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_MO;
m_read.m_bits = 16;
m_read.m_name = func.name();
m_rproto16mo = func;
return *this;
}
address_map_entry &address_map_entry::w(write16mo_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_MO;
m_write.m_bits = 16;
m_write.m_name = func.name();
m_wproto16mo = func;
return *this;
}
address_map_entry &address_map_entry::r(read16smo_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_SMO;
m_read.m_bits = 16;
m_read.m_name = func.name();
m_rproto16smo = func;
return *this;
}
address_map_entry &address_map_entry::w(write16smo_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_SMO;
m_write.m_bits = 16;
m_write.m_name = func.name();
m_wproto16smo = func;
return *this;
}
//-------------------------------------------------
// r/w/rw - handler setters for
// 32-bit read/write delegates
//-------------------------------------------------
address_map_entry &address_map_entry::r(read32_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE;
m_read.m_bits = 32;
m_read.m_name = func.name();
m_rproto32 = func;
return *this;
}
address_map_entry &address_map_entry::w(write32_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE;
m_write.m_bits = 32;
m_write.m_name = func.name();
m_wproto32 = func;
return *this;
}
address_map_entry &address_map_entry::r(read32m_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_M;
m_read.m_bits = 32;
m_read.m_name = func.name();
m_rproto32m = func;
return *this;
}
address_map_entry &address_map_entry::w(write32m_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_M;
m_write.m_bits = 32;
m_write.m_name = func.name();
m_wproto32m = func;
return *this;
}
address_map_entry &address_map_entry::r(read32s_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_S;
m_read.m_bits = 32;
m_read.m_name = func.name();
m_rproto32s = func;
return *this;
}
address_map_entry &address_map_entry::w(write32s_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_S;
m_write.m_bits = 32;
m_write.m_name = func.name();
m_wproto32s = func;
return *this;
}
address_map_entry &address_map_entry::r(read32sm_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_SM;
m_read.m_bits = 32;
m_read.m_name = func.name();
m_rproto32sm = func;
return *this;
}
address_map_entry &address_map_entry::w(write32sm_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_SM;
m_write.m_bits = 32;
m_write.m_name = func.name();
m_wproto32sm = func;
return *this;
}
address_map_entry &address_map_entry::r(read32mo_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_MO;
m_read.m_bits = 32;
m_read.m_name = func.name();
m_rproto32mo = func;
return *this;
}
address_map_entry &address_map_entry::w(write32mo_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_MO;
m_write.m_bits = 32;
m_write.m_name = func.name();
m_wproto32mo = func;
return *this;
}
address_map_entry &address_map_entry::r(read32smo_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_SMO;
m_read.m_bits = 32;
m_read.m_name = func.name();
m_rproto32smo = func;
return *this;
}
address_map_entry &address_map_entry::w(write32smo_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_SMO;
m_write.m_bits = 32;
m_write.m_name = func.name();
m_wproto32smo = func;
return *this;
}
//-------------------------------------------------
// r/w/rw - handler setters for
// 64-bit read/write delegates
//-------------------------------------------------
address_map_entry &address_map_entry::r(read64_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE;
m_read.m_bits = 64;
m_read.m_name = func.name();
m_rproto64 = func;
return *this;
}
address_map_entry &address_map_entry::w(write64_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE;
m_write.m_bits = 64;
m_write.m_name = func.name();
m_wproto64 = func;
return *this;
}
address_map_entry &address_map_entry::r(read64m_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_M;
m_read.m_bits = 64;
m_read.m_name = func.name();
m_rproto64m = func;
return *this;
}
address_map_entry &address_map_entry::w(write64m_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_M;
m_write.m_bits = 64;
m_write.m_name = func.name();
m_wproto64m = func;
return *this;
}
address_map_entry &address_map_entry::r(read64s_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_S;
m_read.m_bits = 64;
m_read.m_name = func.name();
m_rproto64s = func;
return *this;
}
address_map_entry &address_map_entry::w(write64s_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_S;
m_write.m_bits = 64;
m_write.m_name = func.name();
m_wproto64s = func;
return *this;
}
address_map_entry &address_map_entry::r(read64sm_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_SM;
m_read.m_bits = 64;
m_read.m_name = func.name();
m_rproto64sm = func;
return *this;
}
address_map_entry &address_map_entry::w(write64sm_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_SM;
m_write.m_bits = 64;
m_write.m_name = func.name();
m_wproto64sm = func;
return *this;
}
address_map_entry &address_map_entry::r(read64mo_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_MO;
m_read.m_bits = 64;
m_read.m_name = func.name();
m_rproto64mo = func;
return *this;
}
address_map_entry &address_map_entry::w(write64mo_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_MO;
m_write.m_bits = 64;
m_write.m_name = func.name();
m_wproto64mo = func;
return *this;
}
address_map_entry &address_map_entry::r(read64smo_delegate func)
{
assert(!func.isnull());
m_read.m_type = AMH_DEVICE_DELEGATE_SMO;
m_read.m_bits = 64;
m_read.m_name = func.name();
m_rproto64smo = func;
return *this;
}
address_map_entry &address_map_entry::w(write64smo_delegate func)
{
assert(!func.isnull());
m_write.m_type = AMH_DEVICE_DELEGATE_SMO;
m_write.m_bits = 64;
m_write.m_name = func.name();
m_wproto64smo = func;
return *this;
}
//-------------------------------------------------
// unitmask_is_appropriate - verify that the
// provided unitmask is valid and expected
//-------------------------------------------------
bool address_map_entry::unitmask_is_appropriate(u8 width, u64 unitmask, const char *string) const
{
#if 0
// if no mask, this must match the default width of the map
if (unitmask == 0)
{
if (m_map.m_databits != width)
{
osd_printf_error("Handler %s is a %d-bit handler but was specified in a %d-bit address map\n", string, width, m_map.m_databits);
return false;
}
return true;
}
// if we have a mask, we must be smaller than the default width of the map
if (m_map.m_databits < width)
{
osd_printf_error("Handler %s is a %d-bit handler and is too wide to be used in a %d-bit address map\n", string, width, m_map.m_databits);
return false;
}
// if map is narrower than 64 bits, check the mask width as well
if (m_map.m_databits < 64 && (unitmask >> m_map.m_databits) != 0)
{
osd_printf_error("Handler %s specified a mask of %016X, too wide to be used in a %d-bit address map\n", string, unitmask, m_map.m_databits);
return false;
}
// the mask must represent whole units of width
u32 basemask = (width == 8) ? 0xff : (width == 16) ? 0xffff : 0xffffffff;
u64 singlemask = basemask;
int count = 0;
while (singlemask != 0)
{
if ((unitmask & singlemask) == singlemask)
count++;
else if ((unitmask & singlemask) != 0)
{
osd_printf_error("Handler %s specified a mask of %016X; needs to be in even chunks of %X\n", string, unitmask, basemask);
return false;
}
singlemask <<= width;
}
#if 0
// the mask must be symmetrical
u64 unitmask_bh = unitmask >> 8 & 0x00ff00ff00ff00ffU;
u64 unitmask_bl = unitmask & 0x00ff00ff00ff00ffU;
u64 unitmask_wh = unitmask >> 16 & 0x0000ffff0000ffffU;
u64 unitmask_wl = unitmask & 0x0000ffff0000ffffU;
u64 unitmask_dh = unitmask >> 32 & 0x00000000ffffffffU;
u64 unitmask_dl = unitmask & 0x00000000ffffffffU;
if ((unitmask_bh != 0 && unitmask_bl != 0 && unitmask_bh != unitmask_bl)
|| (unitmask_wh != 0 && unitmask_wl != 0 && unitmask_wh != unitmask_wl)
|| (unitmask_dh != 0 && unitmask_dl != 0 && unitmask_dh != unitmask_dl))
{
osd_printf_error("Handler %s specified an asymmetrical mask of %016X\n", string, unitmask);
return false;
}
#endif
#endif
return true;
}
//**************************************************************************
// ADDRESS MAP
//**************************************************************************
//-------------------------------------------------
// address_map - constructor
//-------------------------------------------------
address_map::address_map(device_t &device, int spacenum)
: m_spacenum(spacenum),
m_device(&device),
m_unmapval(0),
m_globalmask(0)
{
// get our memory interface
const device_memory_interface *memintf;
if (!m_device->interface(memintf))
throw emu_fatalerror("No memory interface defined for device '%s'\n", m_device->tag());
// and then the configuration for the current address space
const address_space_config *spaceconfig = memintf->space_config(spacenum);
if (spaceconfig == nullptr)
throw emu_fatalerror("No memory address space configuration found for device '%s', space %d\n", m_device->tag(), spacenum);
// append the map provided by the owner
if (!memintf->get_addrmap(spacenum).isnull())
{
m_device = device.owner();
memintf->get_addrmap(spacenum)(*this);
m_device = &device;
}
// construct the internal device map (last so it takes priority)
if (!spaceconfig->m_internal_map.isnull())
spaceconfig->m_internal_map(*this);
}
//-------------------------------------------------
// address_map - constructor in the submap case
//-------------------------------------------------
address_map::address_map(device_t &device, address_map_entry *entry)
: m_spacenum(AS_PROGRAM),
m_device(&device),
m_unmapval(0),
m_globalmask(0)
{
// Retrieve the submap
entry->m_submap_delegate.late_bind(*m_device);
entry->m_submap_delegate(*this);
}
//----------------------------------------------------------
// address_map - constructor dynamic device mapping case
//----------------------------------------------------------
address_map::address_map(const address_space &space, offs_t start, offs_t end, u64 unitmask, int cswidth, device_t &device, address_map_constructor submap_delegate)
: m_spacenum(space.spacenum()),
m_device(&device),
m_unmapval(space.unmap()),
m_globalmask(space.addrmask())
{
(*this)(start, end).m(DEVICE_SELF, submap_delegate).umask64(unitmask).cswidth(cswidth);
}
//-------------------------------------------------
// ~address_map - destructor
//-------------------------------------------------
address_map::~address_map()
{
}
//-------------------------------------------------
// append - append an entry to the end of the
// list
//-------------------------------------------------
void address_map::global_mask(offs_t mask)
{
m_globalmask = mask;
}
//-------------------------------------------------
// add - add a new entry
//-------------------------------------------------
address_map_entry &address_map::operator()(offs_t start, offs_t end)
{
address_map_entry *ptr = global_alloc(address_map_entry(*m_device, *this, start, end));
m_entrylist.append(*ptr);
return *ptr;
}
//-------------------------------------------------
// import_submaps - propagate in the device submaps
//-------------------------------------------------
void address_map::import_submaps(running_machine &machine, device_t &owner, int data_width, endianness_t endian, int addr_shift)
{
address_map_entry *prev = nullptr;
address_map_entry *entry = m_entrylist.first();
u64 base_unitmask = (~u64(0)) >> (64 - data_width);
while (entry)
{
if (entry->m_read.m_type == AMH_DEVICE_SUBMAP)
{
device_t *mapdevice = entry->m_submap_device;
if (!mapdevice)
{
mapdevice = owner.subdevice(entry->m_read.m_tag);
if (mapdevice == nullptr)
throw emu_fatalerror("Attempted to submap a non-existent device '%s' in space %d of device '%s'\n", owner.subtag(entry->m_read.m_tag).c_str(), m_spacenum, m_device->basetag());
}
// Grab the submap
address_map submap(*mapdevice, entry);
// Recursively import if needed
submap.import_submaps(machine, *mapdevice, data_width, endian, addr_shift);
offs_t max_end = entry->m_addrend - entry->m_addrstart;
if(!entry->m_mask || (entry->m_mask & base_unitmask) == base_unitmask)
{
// Easy case, no unitmask at mapping level - Merge in all the map contents in order
while (submap.m_entrylist.count())
{
address_map_entry *subentry = submap.m_entrylist.detach_head();
if (addr_shift > 0)
{
subentry->m_addrstart <<= addr_shift;
subentry->m_addrend = ((subentry->m_addrend + 1) << addr_shift) - 1;
subentry->m_addrmirror <<= addr_shift;
subentry->m_addrmask <<= addr_shift;
subentry->m_addrselect <<= addr_shift;
}
else if (addr_shift < 0)
{
subentry->m_addrstart >>= -addr_shift;
subentry->m_addrend >>= -addr_shift;
subentry->m_addrmirror >>= -addr_shift;
subentry->m_addrmask >>= -addr_shift;
subentry->m_addrselect >>= -addr_shift;
}
if (subentry->m_addrend > max_end)
subentry->m_addrend = max_end;
subentry->m_addrstart += entry->m_addrstart;
subentry->m_addrend += entry->m_addrstart;
subentry->m_addrmirror |= entry->m_addrmirror;
subentry->m_addrmask |= entry->m_addrmask;
subentry->m_addrselect |= entry->m_addrselect;
if (subentry->m_addrstart > entry->m_addrend)
{
delete subentry;
continue;
}
// Insert the entry in the map
m_entrylist.insert_after(*subentry, prev);
prev = subentry;
}
}
else
{
// There is a unitmask, calculate its ratio
int ratio = 0;
for (int i=0; i != data_width; i++)
if ((entry->m_mask >> i) & 1)
ratio ++;
ratio = data_width / ratio;
if (addr_shift > 0)
ratio <<= addr_shift;
else if (addr_shift < 0)
max_end = ((max_end + 1) << -addr_shift) - 1;
max_end = (max_end + 1) / ratio - 1;
// Then merge the contents taking the ratio into account
while (submap.m_entrylist.count())
{
address_map_entry *subentry = submap.m_entrylist.detach_head();
if (subentry->m_mask && (subentry->m_mask != 0xffffffffffffffffU))
{
// Check if the mask can actually fit
int subentry_ratio = 0;
for (int i=0; i != data_width; i++)
if ((subentry->m_mask >> i) & 1)
subentry_ratio ++;
subentry_ratio = data_width / subentry_ratio;
if (ratio * subentry_ratio > data_width / 8)
fatalerror("import_submap: In range %x-%x mask %x mirror %x select %x of device %s, the import unitmask of %s combined with an entry unitmask of %s does not fit in %d bits.\n", subentry->m_addrstart, subentry->m_addrend, subentry->m_addrmask, subentry->m_addrmirror, subentry->m_addrselect, entry->m_read.m_tag, core_i64_hex_format(entry->m_mask, data_width/4), core_i64_hex_format(subentry->m_mask, data_width/4), data_width);
// Regenerate the unitmask
u64 newmask = 0;
int bit_in_subentry = 0;
for (int i=0; i != data_width; i++)
if ((entry->m_mask >> i) & 1)
{
if ((subentry->m_mask >> bit_in_subentry) & 1)
newmask |= u64(1) << i;
bit_in_subentry ++;
}
subentry->m_mask = newmask;
}
else
subentry->m_mask = entry->m_mask;
subentry->m_cswidth = std::max(subentry->m_cswidth, entry->m_cswidth);
if (subentry->m_addrend > max_end)
subentry->m_addrend = max_end;
if (addr_shift < 0)
{
subentry->m_addrstart = ((subentry->m_addrstart * ratio) >> -addr_shift) + entry->m_addrstart;
subentry->m_addrend = (((subentry->m_addrend + 1) * ratio - 1) >> -addr_shift) + entry->m_addrstart;
subentry->m_addrmirror = ((subentry->m_addrmirror / ratio) << -addr_shift) | entry->m_addrmirror;
subentry->m_addrmask = ((subentry->m_addrmask / ratio) << -addr_shift) | entry->m_addrmask;
subentry->m_addrselect = ((subentry->m_addrselect / ratio) << -addr_shift) | entry->m_addrselect;
}
else
{
subentry->m_addrstart = subentry->m_addrstart * ratio + entry->m_addrstart;
subentry->m_addrend = (subentry->m_addrend + 1) * ratio - 1 + entry->m_addrstart;
subentry->m_addrmirror = (subentry->m_addrmirror / ratio) | entry->m_addrmirror;
subentry->m_addrmask = (subentry->m_addrmask / ratio) | entry->m_addrmask;
subentry->m_addrselect = (subentry->m_addrselect / ratio) | entry->m_addrselect;
}
if (subentry->m_addrstart > entry->m_addrend)
{
delete subentry;
continue;
}
// Insert the entry in the map
m_entrylist.insert_after(*subentry, prev);
prev = subentry;
}
}
address_map_entry *to_delete = entry;
entry = entry->next();
m_entrylist.remove(*to_delete);
}
else
{
prev = entry;
entry = entry->next();
}
}
}
//-------------------------------------------------
// map_validity_check - perform validity checks on
// one of the device's address maps
//-------------------------------------------------
void address_map::map_validity_check(validity_checker &valid, int spacenum) const
{
// it's safe to assume here that the device has a memory interface and a config for this space
const address_space_config &spaceconfig = *m_device->memory().space_config(spacenum);
int default_alignunit = spaceconfig.alignment();
bool detected_overlap = DETECT_OVERLAPPING_MEMORY ? false : true;
// if this is an empty map, just ignore it
if (m_entrylist.first() == nullptr)
return;
// validate the global map parameters
if (m_spacenum != spacenum)
osd_printf_error("Space %d has address space %d handlers!\n", spacenum, m_spacenum);
offs_t globalmask = 0xffffffffUL >> (32 - spaceconfig.m_addr_width);
if (m_globalmask != 0)
globalmask = m_globalmask;
// loop over entries and look for errors
for (address_map_entry &entry : m_entrylist)
{
// look for overlapping entries
if (!detected_overlap)
{
for (address_map_entry &scan : m_entrylist)
{
if (&scan == &entry)
break;
if (entry.m_addrstart <= scan.m_addrend && entry.m_addrend >= scan.m_addrstart &&
((entry.m_read.m_type != AMH_NONE && scan.m_read.m_type != AMH_NONE) ||
(entry.m_write.m_type != AMH_NONE && scan.m_write.m_type != AMH_NONE)))
{
osd_printf_warning("%s space has overlapping memory (%X-%X,%d,%d) vs (%X-%X,%d,%d)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_read.m_type, entry.m_write.m_type, scan.m_addrstart, scan.m_addrend, scan.m_read.m_type, scan.m_write.m_type);
detected_overlap = true;
break;
}
}
}
// look for inverted start/end pairs
if (entry.m_addrend < entry.m_addrstart)
osd_printf_error("Wrong %s memory read handler start = %08x > end = %08x\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend);
// look for ranges outside the global mask
if (entry.m_addrstart & ~globalmask)
osd_printf_error("In %s memory range %x-%x, start address is outside of the global address mask %x\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, globalmask);
if (entry.m_addrend & ~globalmask)
osd_printf_error("In %s memory range %x-%x, end address is outside of the global address mask %x\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, globalmask);
if (entry.m_addrmask & ~globalmask)
osd_printf_error("In %s range %x-%x mask %x mirror %x select %x, mask is outside of the global address mask %x, did you mean %x ?\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, globalmask, entry.m_addrmask & globalmask);
if ((entry.m_addrmirror & ~globalmask) && (entry.m_addrmirror | globalmask) != 0xffffffff >> (32 - spaceconfig.m_addr_width))
osd_printf_error("In %s range %x-%x mask %x mirror %x select %x, mirror is outside of the global address mask %x, did you mean %x ?\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, globalmask, entry.m_addrmirror & globalmask);
if (entry.m_addrselect & ~globalmask)
osd_printf_error("In %s range %x-%x mask %x mirror %x select %x, select is outside of the global address mask %x, did you mean %x ?\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, globalmask, entry.m_addrselect & globalmask);
// look for misaligned entries
if (entry.m_read.m_type != AMH_NONE)
{
int alignunit = spaceconfig.byte2addr(entry.m_read.m_bits / 8);
if (!alignunit)
alignunit = default_alignunit;
if ((entry.m_addrstart & (alignunit - 1)) != 0 || (entry.m_addrend & (alignunit - 1)) != (alignunit - 1))
osd_printf_error("Wrong %s memory read handler start = %08x, end = %08x ALIGN = %d\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, alignunit);
}
if (entry.m_write.m_type != AMH_NONE)
{
int alignunit = spaceconfig.byte2addr(entry.m_write.m_bits / 8);
if (!alignunit)
alignunit = default_alignunit;
if ((entry.m_addrstart & (alignunit - 1)) != 0 || (entry.m_addrend & (alignunit - 1)) != (alignunit - 1))
osd_printf_error("Wrong %s memory write handler start = %08x, end = %08x ALIGN = %d\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, alignunit);
}
// verify mask/mirror/select
offs_t set_bits = entry.m_addrstart | entry.m_addrend;
offs_t changing_bits = entry.m_addrstart ^ entry.m_addrend;
changing_bits |= changing_bits >> 1;
changing_bits |= changing_bits >> 2;
changing_bits |= changing_bits >> 4;
changing_bits |= changing_bits >> 8;
changing_bits |= changing_bits >> 16;
if (entry.m_addrmask & ~changing_bits)
osd_printf_error("In %s memory range %x-%x, mask %x is trying to unmask an unchanging address bit (%x)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmask & ~changing_bits);
if (entry.m_addrmirror & changing_bits)
osd_printf_error("In %s memory range %x-%x, mirror %x touches a changing address bit (%x)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_addrmirror & changing_bits);
if (entry.m_addrselect & changing_bits)
osd_printf_error("In %s memory range %x-%x, select %x touches a changing address bit (%x)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrselect, entry.m_addrselect & changing_bits);
if (entry.m_addrmirror & set_bits)
osd_printf_error("In %s memory range %x-%x, mirror %x touches a set address bit (%x)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_addrmirror & set_bits);
if (entry.m_addrselect & set_bits)
osd_printf_error("In %s memory range %x-%x, select %x touches a set address bit (%x)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrselect, entry.m_addrselect & set_bits);
if (entry.m_addrmirror & entry.m_addrselect)
osd_printf_error("In %s memory range %x-%x, mirror %x touches a select bit (%x)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_addrmirror & entry.m_addrselect);
// if this is a program space, auto-assign implicit ROM entries
if (entry.m_read.m_type == AMH_ROM && entry.m_region == nullptr)
{
entry.m_region = m_device->tag();
entry.m_rgnoffs = entry.m_addrstart;
}
// if this entry references a memory region, validate it
if (entry.m_region != nullptr && entry.m_share == nullptr)
{
// address map entries that reference regions but are NOPs are pointless
if (entry.m_read.m_type == AMH_NONE && entry.m_write.m_type == AMH_NONE)
osd_printf_error("%s space references memory region %s, but is noprw()\n", spaceconfig.m_name, entry.m_region);
// make sure we can resolve the full path to the region
bool found = false;
std::string entry_region = entry.m_devbase.subtag(entry.m_region);
// look for the region
for (device_t &dev : device_iterator(m_device->mconfig().root_device()))
{
for (romload::region const &region : romload::entries(dev.rom_region()).get_regions())
{
if (dev.subtag(region.get_tag()) == entry_region)
{
// verify the address range is within the region's bounds
offs_t const length = region.get_length();
if (entry.m_rgnoffs + spaceconfig.addr2byte(entry.m_addrend - entry.m_addrstart + 1) > length)
osd_printf_error("%s space memory map entry %X-%X extends beyond region '%s' size (%X)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_region, length);
device_t *rgndev = dev.subdevice(region.get_tag());
if (rgndev != m_device)
{
int rgnwidth = region.get_width();
bool rgnisbe = region.is_bigendian();
// memory interface sets region width when tags are the same
device_memory_interface *memintf;
if (rgndev != nullptr && rgndev->interface(memintf))
{
const address_space_config *rgncfg = memintf->space_config(0);
if (rgncfg != nullptr)
{
rgnwidth = rgncfg->data_width();
rgnisbe = rgncfg->endianness() == ENDIANNESS_BIG;
}
}
// verify data width
if (rgnwidth != spaceconfig.data_width())
osd_printf_error("%s space is %d-bit but references %d-bit region '%s'\n", spaceconfig.m_name, spaceconfig.data_width(), rgnwidth, dev.subtag(region.get_tag()));
// verify endianness
if (rgnwidth != 8)
{
if (!rgnisbe && spaceconfig.endianness() == ENDIANNESS_BIG)
osd_printf_error("%s space is big-endian but references little-endian region '%s'\n", spaceconfig.m_name, dev.subtag(region.get_tag()));
else if (rgnisbe && spaceconfig.endianness() == ENDIANNESS_LITTLE)
osd_printf_error("%s space is little-endian but references big-endian region '%s'\n", spaceconfig.m_name, dev.subtag(region.get_tag()));
}
}
found = true;
}
}
}
// error if not found
if (!found)
osd_printf_error("%s space memory map entry %X-%X references nonexistent region '%s'\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_region);
}
// make sure all devices exist
if (entry.m_read.m_type == AMH_DEVICE_DELEGATE || entry.m_read.m_type == AMH_DEVICE_DELEGATE_M || entry.m_read.m_type == AMH_DEVICE_DELEGATE_S || entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM || entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO || entry.m_read.m_type == AMH_DEVICE_DELEGATE_SMO)
{
// extract the device tag from the proto-delegate
std::pair<std::reference_wrapper<device_t>, const char *> devtag(entry.m_devbase, nullptr);
switch (entry.m_read.m_bits)
{
case 8:
if (entry.m_read.m_type == AMH_DEVICE_DELEGATE)
devtag = entry.m_rproto8.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_M)
devtag = entry.m_rproto8m.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_S)
devtag = entry.m_rproto8s.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM)
devtag = entry.m_rproto8sm.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO)
devtag = entry.m_rproto8mo.finder_target();
else
devtag = entry.m_rproto8smo.finder_target();
break;
case 16:
if (entry.m_read.m_type == AMH_DEVICE_DELEGATE)
devtag = entry.m_rproto16.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_M)
devtag = entry.m_rproto16m.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_S)
devtag = entry.m_rproto16s.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM)
devtag = entry.m_rproto16sm.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO)
devtag = entry.m_rproto16mo.finder_target();
else
devtag = entry.m_rproto16smo.finder_target();
break;
case 32:
if (entry.m_read.m_type == AMH_DEVICE_DELEGATE)
devtag = entry.m_rproto32.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_M)
devtag = entry.m_rproto32m.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_S)
devtag = entry.m_rproto32s.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM)
devtag = entry.m_rproto32sm.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO)
devtag = entry.m_rproto32mo.finder_target();
else
devtag = entry.m_rproto32smo.finder_target();
break;
case 64:
if (entry.m_read.m_type == AMH_DEVICE_DELEGATE)
devtag = entry.m_rproto64.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_M)
devtag = entry.m_rproto64m.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_S)
devtag = entry.m_rproto64s.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM)
devtag = entry.m_rproto64sm.finder_target();
else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO)
devtag = entry.m_rproto64mo.finder_target();
else
devtag = entry.m_rproto64smo.finder_target();
break;
}
if (devtag.second && !devtag.first.get().subdevice(devtag.second))
osd_printf_error("%s space memory map entry reads from nonexistent device '%s'\n", spaceconfig.m_name, devtag.first.get().subtag(devtag.second).c_str());
#ifndef MAME_DEBUG // assert will catch this earlier
(void)entry.unitmask_is_appropriate(entry.m_read.m_bits, entry.m_mask, entry.m_read.m_name);
#endif
}
if (entry.m_write.m_type == AMH_DEVICE_DELEGATE || entry.m_read.m_type == AMH_DEVICE_DELEGATE_M || entry.m_write.m_type == AMH_DEVICE_DELEGATE_S || entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM || entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO || entry.m_write.m_type == AMH_DEVICE_DELEGATE_SMO)
{
// extract the device tag from the proto-delegate
std::pair<std::reference_wrapper<device_t>, const char *> devtag(entry.m_devbase, nullptr);
switch (entry.m_write.m_bits)
{
case 8:
if (entry.m_write.m_type == AMH_DEVICE_DELEGATE)
devtag = entry.m_wproto8.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_M)
devtag = entry.m_wproto8m.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_S)
devtag = entry.m_wproto8s.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM)
devtag = entry.m_wproto8sm.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_MO)
devtag = entry.m_wproto8mo.finder_target();
else
devtag = entry.m_wproto8smo.finder_target();
break;
case 16:
if (entry.m_write.m_type == AMH_DEVICE_DELEGATE)
devtag = entry.m_wproto16.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_M)
devtag = entry.m_wproto16m.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_S)
devtag = entry.m_wproto16s.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM)
devtag = entry.m_wproto16sm.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_MO)
devtag = entry.m_wproto16mo.finder_target();
else
devtag = entry.m_wproto16smo.finder_target();
break;
case 32:
if (entry.m_write.m_type == AMH_DEVICE_DELEGATE)
devtag = entry.m_wproto32.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_M)
devtag = entry.m_wproto32m.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_S)
devtag = entry.m_wproto32s.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM)
devtag = entry.m_wproto32sm.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_MO)
devtag = entry.m_wproto32mo.finder_target();
else
devtag = entry.m_wproto32smo.finder_target();
break;
case 64:
if (entry.m_write.m_type == AMH_DEVICE_DELEGATE)
devtag = entry.m_wproto64.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_M)
devtag = entry.m_wproto64m.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_S)
devtag = entry.m_wproto64s.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM)
devtag = entry.m_wproto64sm.finder_target();
else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_MO)
devtag = entry.m_wproto64mo.finder_target();
else
devtag = entry.m_wproto64smo.finder_target();
break;
}
if (devtag.second && !devtag.first.get().subdevice(devtag.second))
osd_printf_error("%s space memory map entry writes to nonexistent device '%s'\n", spaceconfig.m_name, devtag.first.get().subtag(devtag.second).c_str());
#ifndef MAME_DEBUG // assert will catch this earlier
(void)entry.unitmask_is_appropriate(entry.m_write.m_bits, entry.m_mask, entry.m_write.m_name);
#endif
}
// make sure ports exist
// if ((entry.m_read.m_type == AMH_PORT && entry.m_read.m_tag != nullptr && portlist.find(entry.m_read.m_tag) == nullptr) ||
// (entry.m_write.m_type == AMH_PORT && entry.m_write.m_tag != nullptr && portlist.find(entry.m_write.m_tag) == nullptr))
// osd_printf_error("%s space memory map entry references nonexistent port tag '%s'\n", spaceconfig.m_name, entry.m_read.m_tag);
// validate bank and share tags
if (entry.m_read.m_type == AMH_BANK)
valid.validate_tag(entry.m_read.m_tag);
if (entry.m_write.m_type == AMH_BANK)
valid.validate_tag(entry.m_write.m_tag);
if (entry.m_share != nullptr)
valid.validate_tag(entry.m_share);
}
}