summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/genie.lua3
-rw-r--r--scripts/src/emu.lua27
-rw-r--r--src/devices/bus/ti99/internal/998board.cpp8
-rw-r--r--src/devices/bus/ti99/internal/998board.h4
-rw-r--r--src/devices/bus/ti99/internal/datamux.cpp10
-rw-r--r--src/devices/bus/ti99/internal/datamux.h2
-rw-r--r--src/devices/bus/ti99/internal/genboard.cpp3
-rw-r--r--src/devices/bus/ti99/internal/genboard.h2
-rw-r--r--src/devices/cpu/tms32031/tms32031.cpp1
-rw-r--r--src/devices/cpu/tms9900/tms9900.cpp25
-rw-r--r--src/devices/cpu/tms9900/tms9900.h4
-rw-r--r--src/devices/cpu/tms9900/tms9980a.cpp12
-rw-r--r--src/devices/cpu/tms9900/tms9995.cpp34
-rw-r--r--src/devices/cpu/tms9900/tms9995.h4
-rw-r--r--src/devices/cpu/v60/v60.cpp2
-rw-r--r--src/devices/cpu/v60/v60.h1
-rw-r--r--src/emu/addrmap.cpp21
-rw-r--r--src/emu/addrmap.h32
-rw-r--r--src/emu/debug/debugbuf.cpp2
-rw-r--r--src/emu/debug/debugcmd.cpp48
-rw-r--r--src/emu/debug/debugcpu.cpp569
-rw-r--r--src/emu/debug/debugcpu.h48
-rw-r--r--src/emu/debug/dvwpoints.cpp8
-rw-r--r--src/emu/dimemory.cpp28
-rw-r--r--src/emu/dimemory.h5
-rw-r--r--src/emu/emumem.cpp4554
-rw-r--r--src/emu/emumem.h604
-rw-r--r--src/emu/emumem_hea.h40
-rw-r--r--src/emu/emumem_hedp.cpp144
-rw-r--r--src/emu/emumem_hedp.h81
-rw-r--r--src/emu/emumem_hedr.h61
-rw-r--r--src/emu/emumem_hedr.ipp479
-rw-r--r--src/emu/emumem_hedr0.cpp171
-rw-r--r--src/emu/emumem_hedr1.cpp192
-rw-r--r--src/emu/emumem_hedr2.cpp192
-rw-r--r--src/emu/emumem_hedr3.cpp193
-rw-r--r--src/emu/emumem_hedw.h61
-rw-r--r--src/emu/emumem_hedw.ipp479
-rw-r--r--src/emu/emumem_hedw0.cpp171
-rw-r--r--src/emu/emumem_hedw1.cpp192
-rw-r--r--src/emu/emumem_hedw2.cpp192
-rw-r--r--src/emu/emumem_hedw3.cpp193
-rw-r--r--src/emu/emumem_hem.cpp189
-rw-r--r--src/emu/emumem_hem.h87
-rw-r--r--src/emu/emumem_hep.cpp97
-rw-r--r--src/emu/emumem_hep.h48
-rw-r--r--src/emu/emumem_het.cpp89
-rw-r--r--src/emu/emumem_het.h50
-rw-r--r--src/emu/emumem_heu.cpp273
-rw-r--r--src/emu/emumem_heu.h90
-rw-r--r--src/emu/emumem_heun.cpp158
-rw-r--r--src/emu/emumem_heun.h68
-rw-r--r--src/emu/emumem_mud.cpp127
-rw-r--r--src/emu/emumem_mud.h41
-rw-r--r--src/frontend/mame/luaengine.cpp30
-rw-r--r--src/lib/util/coretmpl.h8
-rw-r--r--src/mame/drivers/geneve.cpp9
-rw-r--r--src/mame/drivers/m6805evs.cpp5
-rw-r--r--src/mame/drivers/ti99_4p.cpp17
-rw-r--r--src/mame/drivers/ti99_4x.cpp12
-rw-r--r--src/mame/drivers/ti99_8.cpp9
-rw-r--r--src/mame/machine/c117.cpp8
-rw-r--r--src/mame/machine/segaic16.cpp1
63 files changed, 6038 insertions, 4280 deletions
diff --git a/scripts/genie.lua b/scripts/genie.lua
index ed289f73396..51e80e88562 100644
--- a/scripts/genie.lua
+++ b/scripts/genie.lua
@@ -1192,6 +1192,9 @@ configuration { "mingw*" }
"-static",
}
end
+ buildoptions {
+ "-Wa,-mbig-obj",
+ }
linkoptions {
"-Wl,--start-group",
}
diff --git a/scripts/src/emu.lua b/scripts/src/emu.lua
index cc281b6a8c3..c9a8ddeb625 100644
--- a/scripts/src/emu.lua
+++ b/scripts/src/emu.lua
@@ -116,6 +116,33 @@ files {
MAME_DIR .. "src/emu/emucore.h",
MAME_DIR .. "src/emu/emumem.cpp",
MAME_DIR .. "src/emu/emumem.h",
+ MAME_DIR .. "src/emu/emumem_mud.cpp",
+ MAME_DIR .. "src/emu/emumem_mud.h",
+ MAME_DIR .. "src/emu/emumem_hea.h",
+ MAME_DIR .. "src/emu/emumem_hem.cpp",
+ MAME_DIR .. "src/emu/emumem_hem.h",
+ MAME_DIR .. "src/emu/emumem_hedp.cpp",
+ MAME_DIR .. "src/emu/emumem_hedp.h",
+ MAME_DIR .. "src/emu/emumem_heun.cpp",
+ MAME_DIR .. "src/emu/emumem_heun.h",
+ MAME_DIR .. "src/emu/emumem_heu.cpp",
+ MAME_DIR .. "src/emu/emumem_heu.h",
+ MAME_DIR .. "src/emu/emumem_hedr.ipp",
+ MAME_DIR .. "src/emu/emumem_hedr.h",
+ MAME_DIR .. "src/emu/emumem_hedr0.cpp",
+ MAME_DIR .. "src/emu/emumem_hedr1.cpp",
+ MAME_DIR .. "src/emu/emumem_hedr2.cpp",
+ MAME_DIR .. "src/emu/emumem_hedr3.cpp",
+ MAME_DIR .. "src/emu/emumem_hedw.ipp",
+ MAME_DIR .. "src/emu/emumem_hedw.h",
+ MAME_DIR .. "src/emu/emumem_hedw0.cpp",
+ MAME_DIR .. "src/emu/emumem_hedw1.cpp",
+ MAME_DIR .. "src/emu/emumem_hedw2.cpp",
+ MAME_DIR .. "src/emu/emumem_hedw3.cpp",
+ MAME_DIR .. "src/emu/emumem_hep.cpp",
+ MAME_DIR .. "src/emu/emumem_hep.h",
+ MAME_DIR .. "src/emu/emumem_het.cpp",
+ MAME_DIR .. "src/emu/emumem_het.h",
MAME_DIR .. "src/emu/emuopts.cpp",
MAME_DIR .. "src/emu/emuopts.h",
MAME_DIR .. "src/emu/emupal.cpp",
diff --git a/src/devices/bus/ti99/internal/998board.cpp b/src/devices/bus/ti99/internal/998board.cpp
index 91bad557d5b..443f5b9312b 100644
--- a/src/devices/bus/ti99/internal/998board.cpp
+++ b/src/devices/bus/ti99/internal/998board.cpp
@@ -347,7 +347,7 @@ WRITE_LINE_MEMBER( mainboard8_device::dbin_in )
m_dbin_level = (line_state)state;
}
-SETOFFSET_MEMBER( mainboard8_device::setoffset )
+READ8_MEMBER( mainboard8_device::setoffset )
{
LOGMASKED(LOG_ADDRESS, "set %s %04x\n", (m_dbin_level==ASSERT_LINE)? "R" : "W", offset);
@@ -386,6 +386,8 @@ SETOFFSET_MEMBER( mainboard8_device::setoffset )
// AMIGO is the one to control the READY line to the CPU
// MOFETTA does not contribute to READY
m_ready(m_amigo->cpury_out());
+
+ return 0;
}
WRITE_LINE_MEMBER( mainboard8_device::reset_console )
@@ -1945,7 +1947,7 @@ WRITE_LINE_MEMBER( amigo_device::lascs_in )
3. Set the physical address bus with the second 16 bits of the physical
address. Clear the MSAST line. Forward any incoming READY=0 to the CPU.
*/
-SETOFFSET_MEMBER( amigo_device::set_address )
+READ8_MEMBER( amigo_device::set_address )
{
// Check whether the mapper itself is accessed
int mapaddr = (m_crus==ASSERT_LINE)? 0x8810 : 0xf870;
@@ -1991,6 +1993,8 @@ SETOFFSET_MEMBER( amigo_device::set_address )
m_ready_out = m_srdy;
LOGMASKED(LOG_CPURY, "Setting CPURY = %d (LAS)\n", m_ready_out);
}
+
+ return 0;
}
/*
diff --git a/src/devices/bus/ti99/internal/998board.h b/src/devices/bus/ti99/internal/998board.h
index fa161c016db..0f64b551443 100644
--- a/src/devices/bus/ti99/internal/998board.h
+++ b/src/devices/bus/ti99/internal/998board.h
@@ -337,7 +337,7 @@ public:
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
- DECLARE_SETOFFSET_MEMBER( set_address );
+ DECLARE_READ8_MEMBER( set_address );
// Debugger support
int get_physical_address_debug(offs_t offset);
@@ -541,7 +541,7 @@ public:
// Memory space
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
- DECLARE_SETOFFSET_MEMBER( setoffset );
+ DECLARE_READ8_MEMBER( setoffset );
// Memory space for debugger access
DECLARE_READ8_MEMBER( debugger_read );
diff --git a/src/devices/bus/ti99/internal/datamux.cpp b/src/devices/bus/ti99/internal/datamux.cpp
index 5fbcb8f3078..da6e870516a 100644
--- a/src/devices/bus/ti99/internal/datamux.cpp
+++ b/src/devices/bus/ti99/internal/datamux.cpp
@@ -426,21 +426,21 @@ WRITE16_MEMBER( datamux_device::write )
Called when the memory access starts by setting the address bus. From that
point on, we suspend the CPU until all operations are done.
*/
-SETOFFSET_MEMBER( datamux_device::setoffset )
+READ8_MEMBER( datamux_device::setoffset )
{
- m_addr_buf = offset << 1;
+ m_addr_buf = offset;
m_waitcount = 0;
if (TRACE_ADDRESS) logerror("set address %04x\n", m_addr_buf);
if ((m_addr_buf & 0xe000) == 0x0000)
{
- return; // console ROM
+ return 0; // console ROM
}
if ((m_addr_buf & 0xfc00) == 0x8000)
{
- return; // console RAM
+ return 0; // console RAM
}
// Initialize counter
@@ -467,6 +467,8 @@ SETOFFSET_MEMBER( datamux_device::setoffset )
ready_join();
}
else m_waitcount = 0;
+
+ return 0;
}
/*
diff --git a/src/devices/bus/ti99/internal/datamux.h b/src/devices/bus/ti99/internal/datamux.h
index d864ee98c53..b2ec1bff123 100644
--- a/src/devices/bus/ti99/internal/datamux.h
+++ b/src/devices/bus/ti99/internal/datamux.h
@@ -35,7 +35,7 @@ public:
datamux_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ16_MEMBER( read );
DECLARE_WRITE16_MEMBER( write );
- DECLARE_SETOFFSET_MEMBER( setoffset );
+ DECLARE_READ8_MEMBER( setoffset );
DECLARE_WRITE_LINE_MEMBER( clock_in );
DECLARE_WRITE_LINE_MEMBER( dbin_in );
diff --git a/src/devices/bus/ti99/internal/genboard.cpp b/src/devices/bus/ti99/internal/genboard.cpp
index a9ed0e9e1bb..861e0655200 100644
--- a/src/devices/bus/ti99/internal/genboard.cpp
+++ b/src/devices/bus/ti99/internal/genboard.cpp
@@ -907,7 +907,7 @@ WRITE8_MEMBER( geneve_mapper_device::write_to_pfm )
This decoding will later be used in the READ/WRITE member functions. Also,
we initiate wait state creation here.
*/
-SETOFFSET_MEMBER( geneve_mapper_device::setoffset )
+READ8_MEMBER( geneve_mapper_device::setoffset )
{
LOGMASKED(LOG_DETAIL, "setoffset = %04x\n", offset);
m_debug_no_ws = false;
@@ -928,6 +928,7 @@ SETOFFSET_MEMBER( geneve_mapper_device::setoffset )
m_peribox->memen_in(ASSERT_LINE);
m_peribox->setaddress_dbin(space, m_decoded.physaddr, m_read_mode);
}
+ return 0;
}
/*
diff --git a/src/devices/bus/ti99/internal/genboard.h b/src/devices/bus/ti99/internal/genboard.h
index baac4db69de..260f02932b1 100644
--- a/src/devices/bus/ti99/internal/genboard.h
+++ b/src/devices/bus/ti99/internal/genboard.h
@@ -126,7 +126,7 @@ public:
DECLARE_READ8_MEMBER( readm );
DECLARE_WRITE8_MEMBER( writem );
- DECLARE_SETOFFSET_MEMBER( setoffset );
+ DECLARE_READ8_MEMBER( setoffset );
DECLARE_INPUT_CHANGED_MEMBER( settings_changed );
diff --git a/src/devices/cpu/tms32031/tms32031.cpp b/src/devices/cpu/tms32031/tms32031.cpp
index 777097ba85d..fb13fbba9ce 100644
--- a/src/devices/cpu/tms32031/tms32031.cpp
+++ b/src/devices/cpu/tms32031/tms32031.cpp
@@ -745,7 +745,6 @@ void tms3203x_device::execute_set_input(int inputnum, int state)
{
// switch between microcomputer/boot loader and microprocessor modes
m_mcbl_mode = (state == ASSERT_LINE);
- m_cache->force_update();
return;
}
diff --git a/src/devices/cpu/tms9900/tms9900.cpp b/src/devices/cpu/tms9900/tms9900.cpp
index 21760205aff..61ee5644d7f 100644
--- a/src/devices/cpu/tms9900/tms9900.cpp
+++ b/src/devices/cpu/tms9900/tms9900.cpp
@@ -114,6 +114,8 @@
#define NOPRG -1
+constexpr int tms99xx_device::AS_SETOFFSET;
+
/* tms9900 ST register bits. */
enum
{
@@ -179,6 +181,7 @@ enum
tms99xx_device::tms99xx_device(const machine_config &mconfig, device_type type, const char *tag, int data_width, int prg_addr_bits, int cru_addr_bits, device_t *owner, uint32_t clock)
: cpu_device(mconfig, type, tag, owner, clock),
m_program_config("program", ENDIANNESS_BIG, data_width, prg_addr_bits),
+ m_setoffset_config("setoffset", ENDIANNESS_BIG, data_width, prg_addr_bits),
m_io_config("cru", ENDIANNESS_BIG, 8, cru_addr_bits),
m_prgspace(nullptr),
m_cru(nullptr),
@@ -223,6 +226,7 @@ void tms99xx_device::device_start()
// TODO: Restore state save feature
resolve_lines();
m_prgspace = &space(AS_PROGRAM);
+ m_sospace = has_space(AS_SETOFFSET) ? &space(AS_SETOFFSET) : nullptr;
m_cru = &space(AS_IO);
// set our instruction counter
@@ -437,10 +441,17 @@ void tms99xx_device::write_workspace_register_debug(int reg, uint16_t data)
device_memory_interface::space_config_vector tms99xx_device::memory_space_config() const
{
- return space_config_vector {
- std::make_pair(AS_PROGRAM, &m_program_config),
- std::make_pair(AS_IO, &m_io_config)
- };
+ if (has_configured_map(AS_SETOFFSET))
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_SETOFFSET, &m_setoffset_config),
+ std::make_pair(AS_IO, &m_io_config)
+ };
+ else
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_IO, &m_io_config)
+ };
}
/**************************************************************************
@@ -1523,7 +1534,8 @@ void tms99xx_device::mem_read()
if (m_mem_phase==1)
{
if (!m_dbin_line.isnull()) m_dbin_line(ASSERT_LINE);
- m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe);
+ if (m_sospace)
+ m_sospace->read_byte(m_address & m_prgaddr_mask & 0xfffe);
m_check_ready = true;
m_mem_phase = 2;
m_pass = 2;
@@ -1550,7 +1562,8 @@ void tms99xx_device::mem_write()
if (!m_dbin_line.isnull()) m_dbin_line(CLEAR_LINE);
// When writing, the data bus is asserted immediately after the address bus
if (TRACE_ADDRESSBUS) logerror("set address (w) %04x\n", m_address);
- m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe);
+ if (m_sospace)
+ m_sospace->read_byte(m_address & m_prgaddr_mask & 0xfffe);
if (TRACE_MEM) logerror("mem w %04x <- %04x\n", m_address, m_current_value);
m_prgspace->write_word(m_address & m_prgaddr_mask & 0xfffe, m_current_value);
m_check_ready = true;
diff --git a/src/devices/cpu/tms9900/tms9900.h b/src/devices/cpu/tms9900/tms9900.h
index 5af536fb54d..c084c190623 100644
--- a/src/devices/cpu/tms9900/tms9900.h
+++ b/src/devices/cpu/tms9900/tms9900.h
@@ -43,6 +43,8 @@ static const char opname[][5] =
class tms99xx_device : public cpu_device
{
public:
+ static constexpr int AS_SETOFFSET = 4;
+
~tms99xx_device();
// READY input line. When asserted (high), the memory is ready for data exchange.
@@ -93,8 +95,10 @@ protected:
void decode(uint16_t inst);
const address_space_config m_program_config;
+ const address_space_config m_setoffset_config;
const address_space_config m_io_config;
address_space* m_prgspace;
+ address_space* m_sospace;
address_space* m_cru;
virtual uint16_t read_workspace_register_debug(int reg);
diff --git a/src/devices/cpu/tms9900/tms9980a.cpp b/src/devices/cpu/tms9900/tms9980a.cpp
index e63b1b76d5b..aebe26f9575 100644
--- a/src/devices/cpu/tms9900/tms9980a.cpp
+++ b/src/devices/cpu/tms9900/tms9980a.cpp
@@ -183,7 +183,8 @@ void tms9980a_device::mem_read()
case 1:
m_pass = 4; // make the CPU visit this method more than once
if (!m_dbin_line.isnull()) m_dbin_line(ASSERT_LINE);
- m_prgspace->set_address(m_address & m_prgaddr_mask & ~1);
+ if (m_sospace)
+ m_sospace->read_byte(m_address & m_prgaddr_mask & ~1);
if (TRACE_ADDRESSBUS) logerror("tms9980a: set address bus %04x\n", m_address & m_prgaddr_mask & ~1);
m_check_ready = true;
break;
@@ -194,7 +195,8 @@ void tms9980a_device::mem_read()
m_current_value = (value << 8) & 0xff00;
break;
case 3:
- m_prgspace->set_address((m_address & m_prgaddr_mask) | 1);
+ if (m_sospace)
+ m_sospace->read_byte((m_address & m_prgaddr_mask) | 1);
if (TRACE_ADDRESSBUS) logerror("tms9980a: set address bus %04x\n", (m_address & m_prgaddr_mask) | 1);
break;
case 4:
@@ -216,7 +218,8 @@ void tms9980a_device::mem_write()
case 1:
m_pass = 4; // make the CPU visit this method once more
if (!m_dbin_line.isnull()) m_dbin_line(CLEAR_LINE);
- m_prgspace->set_address(m_address & m_prgaddr_mask & ~1);
+ if (m_sospace)
+ m_sospace->read_byte(m_address & m_prgaddr_mask & ~1);
if (TRACE_ADDRESSBUS) logerror("tms9980a: set address bus %04x\n", m_address & m_prgaddr_mask & ~1);
m_prgspace->write_byte(m_address & 0x3ffe & ~1, (m_current_value >> 8)&0xff);
if (TRACE_MEM) logerror("tms9980a: memory write high byte %04x <- %02x\n", m_address & m_prgaddr_mask & ~1, (m_current_value >> 8)&0xff);
@@ -226,7 +229,8 @@ void tms9980a_device::mem_write()
// no action here, just wait for READY
break;
case 3:
- m_prgspace->set_address((m_address & m_prgaddr_mask) | 1);
+ if (m_sospace)
+ m_sospace->read_byte((m_address & m_prgaddr_mask) | 1);
if (TRACE_ADDRESSBUS) logerror("tms9980a: set address bus %04x\n", (m_address & m_prgaddr_mask) | 1);
m_prgspace->write_byte((m_address & m_prgaddr_mask) | 1, m_current_value & 0xff);
if (TRACE_MEM) logerror("tms9980a: memory write low byte %04x <- %02x\n", (m_address & m_prgaddr_mask) | 1, m_current_value & 0xff);
diff --git a/src/devices/cpu/tms9900/tms9995.cpp b/src/devices/cpu/tms9900/tms9995.cpp
index ddc80522f68..26d357a8e61 100644
--- a/src/devices/cpu/tms9900/tms9995.cpp
+++ b/src/devices/cpu/tms9900/tms9995.cpp
@@ -151,6 +151,8 @@ enum
#include "logmacro.h"
+constexpr int tms9995_device::AS_SETOFFSET;
+
/****************************************************************************
Constructor
****************************************************************************/
@@ -167,8 +169,10 @@ tms9995_device::tms9995_device(const machine_config &mconfig, device_type type,
PC(0),
PC_debug(0),
m_program_config("program", ENDIANNESS_BIG, 8, 16),
+ m_setoffset_config("setoffset", ENDIANNESS_BIG, 8, 16),
m_io_config("cru", ENDIANNESS_BIG, 8, 16),
m_prgspace(nullptr),
+ m_sospace(nullptr),
m_cru(nullptr),
m_external_operation(*this),
m_iaq_line(*this),
@@ -193,7 +197,8 @@ void tms9995_device::device_start()
{
// TODO: Restore save state suport
- m_prgspace = &space(AS_PROGRAM); // dimemory.h
+ m_prgspace = &space(AS_PROGRAM);
+ m_sospace = has_space(AS_SETOFFSET) ? &space(AS_SETOFFSET) : nullptr;
m_cru = &space(AS_IO);
// Resolve our external connections
@@ -440,10 +445,17 @@ void tms9995_device::write_workspace_register_debug(int reg, uint16_t data)
device_memory_interface::space_config_vector tms9995_device::memory_space_config() const
{
- return space_config_vector {
- std::make_pair(AS_PROGRAM, &m_program_config),
- std::make_pair(AS_IO, &m_io_config)
- };
+ if (has_configured_map(AS_SETOFFSET))
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_SETOFFSET, &m_setoffset_config),
+ std::make_pair(AS_IO, &m_io_config)
+ };
+ else
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_IO, &m_io_config)
+ };
}
/**************************************************************************
@@ -1838,7 +1850,8 @@ void tms9995_device::mem_read()
m_check_hold = false;
LOGMASKED(LOG_ADDRESSBUS, "set address bus %04x\n", m_address & ~1);
- m_prgspace->set_address(address);
+ if (m_sospace)
+ m_sospace->read_byte(address);
m_request_auto_wait_state = m_auto_wait;
pulse_clock(1);
break;
@@ -1852,7 +1865,8 @@ void tms9995_device::mem_read()
case 3:
// Set address + 1 (unless byte command)
LOGMASKED(LOG_ADDRESSBUS, "set address bus %04x\n", m_address | 1);
- m_prgspace->set_address(m_address | 1);
+ if (m_sospace)
+ m_sospace->read_byte(m_address | 1);
m_request_auto_wait_state = m_auto_wait;
pulse_clock(1);
break;
@@ -1970,7 +1984,8 @@ void tms9995_device::mem_write()
m_check_hold = false;
LOGMASKED(LOG_ADDRESSBUS, "set address bus %04x\n", address);
- m_prgspace->set_address(address);
+ if (m_sospace)
+ m_sospace->read_byte(address);
LOGMASKED(LOG_MEM, "memory write byte %04x <- %02x\n", address, (m_current_value >> 8)&0xff);
m_prgspace->write_byte(address, (m_current_value >> 8)&0xff);
m_request_auto_wait_state = m_auto_wait;
@@ -1983,7 +1998,8 @@ void tms9995_device::mem_write()
case 3:
// Set address + 1 (unless byte command)
LOGMASKED(LOG_ADDRESSBUS, "set address bus %04x\n", m_address | 1);
- m_prgspace->set_address(m_address | 1);
+ if (m_sospace)
+ m_sospace->read_byte(m_address | 1);
LOGMASKED(LOG_MEM, "memory write byte %04x <- %02x\n", m_address | 1, m_current_value & 0xff);
m_prgspace->write_byte(m_address | 1, m_current_value & 0xff);
m_request_auto_wait_state = m_auto_wait;
diff --git a/src/devices/cpu/tms9900/tms9995.h b/src/devices/cpu/tms9900/tms9995.h
index 1dcc4667176..940ec0f43ee 100644
--- a/src/devices/cpu/tms9900/tms9995.h
+++ b/src/devices/cpu/tms9900/tms9995.h
@@ -49,6 +49,8 @@ enum
class tms9995_device : public cpu_device
{
public:
+ static constexpr int AS_SETOFFSET = 4;
+
tms9995_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// READY input line. When asserted (high), the memory is ready for data exchange.
@@ -125,8 +127,10 @@ private:
uint8_t m_onchip_memory[256];
const address_space_config m_program_config;
+ const address_space_config m_setoffset_config;
const address_space_config m_io_config;
address_space* m_prgspace;
+ address_space* m_sospace;
address_space* m_cru;
// Processor states
diff --git a/src/devices/cpu/v60/v60.cpp b/src/devices/cpu/v60/v60.cpp
index 0952d1fb189..9fb8374ae1b 100644
--- a/src/devices/cpu/v60/v60.cpp
+++ b/src/devices/cpu/v60/v60.cpp
@@ -94,7 +94,6 @@ v60_device::v60_device(const machine_config &mconfig, device_type type, const ch
: cpu_device(mconfig, type, tag, owner, clock)
, m_program_config("program", ENDIANNESS_LITTLE, databits, addrbits, 0)
, m_io_config("io", ENDIANNESS_LITTLE, 16, 24, 0)
- , m_fetch_xor(BYTE4_XOR_LE(0))
, m_start_pc(0xfffffff0)
{
m_reg[45] = pir;
@@ -428,7 +427,6 @@ void v60_device::device_start()
m_io = &space(AS_IO);
- save_item(NAME(m_fetch_xor));
save_item(NAME(m_reg));
save_item(NAME(m_irq_line));
save_item(NAME(m_nmi_line));
diff --git a/src/devices/cpu/v60/v60.h b/src/devices/cpu/v60/v60.h
index 14a0ca62615..47917aa1c94 100644
--- a/src/devices/cpu/v60/v60.h
+++ b/src/devices/cpu/v60/v60.h
@@ -151,7 +151,6 @@ private:
address_space_config m_program_config;
address_space_config m_io_config;
- offs_t m_fetch_xor;
offs_t m_start_pc;
uint32_t m_reg[68];
struct {
diff --git a/src/emu/addrmap.cpp b/src/emu/addrmap.cpp
index 544bb51011c..3a3fb07c899 100644
--- a/src/emu/addrmap.cpp
+++ b/src/emu/addrmap.cpp
@@ -295,20 +295,6 @@ address_map_entry &address_map_entry::rw(read64_delegate rfunc, write64_delegate
//-------------------------------------------------
-// set_handler - handler setter for setoffset
-//-------------------------------------------------
-
-address_map_entry &address_map_entry::setoffset(setoffset_delegate func)
-{
- assert(!func.isnull());
- m_setoffsethd.m_type = AMH_DEVICE_DELEGATE;
- m_setoffsethd.m_bits = 0;
- m_setoffsethd.m_name = func.name();
- m_soproto = func;
- return *this;
-}
-
-//-------------------------------------------------
// unitmask_is_appropriate - verify that the
// provided unitmask is valid and expected
//-------------------------------------------------
@@ -797,13 +783,6 @@ void address_map::map_validity_check(validity_checker &valid, int spacenum) cons
(void)entry.unitmask_is_appropriate(entry.m_write.m_bits, entry.m_mask, entry.m_write.m_name);
#endif
}
- if (entry.m_setoffsethd.m_type == AMH_DEVICE_DELEGATE)
- {
- // extract the device tag from the proto-delegate
- const char *devtag = entry.m_soproto.device_name();
- if (entry.m_devbase.subdevice(devtag) == nullptr)
- osd_printf_error("%s space memory map entry references nonexistent device '%s'\n", spaceconfig.m_name, devtag ? devtag : "<unspecified>");
- }
// 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) ||
diff --git a/src/emu/addrmap.h b/src/emu/addrmap.h
index 1654856dd62..90dd8898533 100644
--- a/src/emu/addrmap.h
+++ b/src/emu/addrmap.h
@@ -69,16 +69,9 @@ class address_map_entry
struct is_addrmap_method { static constexpr bool value = std::is_constructible<address_map_constructor, Ret (T::*)(Params...), const char *, T*>::value; };
template <typename T, typename Ret, typename... Params>
- struct is_setoffset_method { static constexpr bool value = std::is_constructible<setoffset_delegate, Ret (T::*)(Params...), const char *, const char *, T *>::value; };
-
- template <typename T, typename Ret, typename... Params>
static std::enable_if_t<is_addrmap_method<T, Ret, Params...>::value, address_map_constructor> make_delegate(Ret (T::*func)(Params...), const char *name, T *obj)
{ return address_map_constructor(func, name, obj); }
- template <typename T, typename Ret, typename... Params>
- static std::enable_if_t<is_setoffset_method<T, Ret, Params...>::value, setoffset_delegate> make_delegate(Ret (T::*func)(Params...), const char *name, const char *tag, T *obj)
- { return setoffset_delegate(func, name, tag, obj); }
-
template <typename T, typename U>
static std::enable_if_t<std::is_convertible<std::add_pointer_t<U>, std::add_pointer_t<T> >::value, T *> make_pointer(U &obj)
{ return &downcast<T &>(obj); }
@@ -167,9 +160,6 @@ public:
return bankrw(target.second);
}
- // set offset handler (only one version, since there is no data width to consider)
- address_map_entry &setoffset(setoffset_delegate func);
-
// type setters
address_map_entry &set_read_type(map_handler_type _type) { m_read.m_type = _type; return *this; }
address_map_entry &set_write_type(map_handler_type _type) { m_write.m_type = _type; return *this; }
@@ -196,10 +186,6 @@ public:
address_map_entry &m(Ret (T::*map)(Params...), const char *map_name)
{ return m(&m_devbase, make_delegate(map, map_name, make_pointer<T>(m_devbase))); }
- template <typename T, typename Ret, typename... Params>
- address_map_entry &setoffset(Ret (T::*so)(Params...), const char *so_name)
- { return setoffset(make_delegate(so, so_name, m_devbase.tag(), make_pointer<T>(m_devbase))); }
-
// device tag -> delegate converter
template <typename T, typename Ret, typename... Params>
@@ -218,10 +204,6 @@ public:
address_map_entry &m(const char *tag, Ret (T::*map)(Params...), const char *map_name)
{ return m(tag, make_delegate(map, map_name, static_cast<T *>(nullptr))); }
- template <typename T, typename Ret, typename... Params>
- address_map_entry &setoffset(const char *tag, Ret (T::*so)(Params...), const char *so_name)
- { return setoffset(make_delegate(so, so_name, tag, static_cast<T *>(nullptr))); }
-
// device reference -> delegate converter
template <typename T, typename U, typename Ret, typename... Params>
@@ -240,10 +222,6 @@ public:
address_map_entry &m(T &obj, Ret (U::*map)(Params...), const char *map_name)
{ return m(make_pointer<device_t>(obj), make_delegate(map, map_name, make_pointer<U>(obj))); }
- template <typename T, typename U, typename Ret, typename... Params>
- address_map_entry &setoffset(T &obj, Ret (U::*so)(Params...), const char *so_name)
- { return w(make_delegate(so, so_name, get_tag(obj), make_pointer<U>(obj))); }
-
// device finder -> delegate converter
template <typename T, bool Reqd, typename U, typename Ret, typename... Params>
@@ -278,14 +256,6 @@ public:
address_map_entry &m(const device_finder<T, Reqd> &finder, Ret (U::*map)(Params...), const char *map_name)
{ const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return m(make_delegate(map, map_name, make_pointer<U>(device))); }
- template <typename T, bool Reqd, typename U, typename Ret, typename... Params>
- address_map_entry &setoffset(device_finder<T, Reqd> &finder, Ret (U::*so)(Params...), const char *so_name)
- { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return setoffset(make_delegate(so, so_name, device.tag(), make_pointer<U>(device))); }
-
- template <typename T, bool Reqd, typename U, typename Ret, typename... Params>
- address_map_entry &setoffset(const device_finder<T, Reqd> &finder, Ret (U::*so)(Params...), const char *so_name)
- { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return setoffset(make_delegate(so, so_name, device.tag(), make_pointer<U>(device))); }
-
// lambda -> delegate converter
template<typename _lr> address_map_entry &lr8(const char *name, _lr &&read) {
@@ -351,7 +321,6 @@ public:
int m_cswidth; // chip select width override
map_handler_data m_read; // data for read handler
map_handler_data m_write; // data for write handler
- map_handler_data m_setoffsethd; // data for setoffset handler
const char * m_share; // tag of a shared memory block
const char * m_region; // tag of region containing the memory backing this entry
offs_t m_rgnoffs; // offset within the region
@@ -366,7 +335,6 @@ public:
write32_delegate m_wproto32; // 32-bit write proto-delegate
write64_delegate m_wproto64; // 64-bit write proto-delegate
- setoffset_delegate m_soproto; // set offset proto-delegate
device_t *m_submap_device;
address_map_constructor m_submap_delegate;
diff --git a/src/emu/debug/debugbuf.cpp b/src/emu/debug/debugbuf.cpp
index 26516c62ed6..b61814ef4e5 100644
--- a/src/emu/debug/debugbuf.cpp
+++ b/src/emu/debug/debugbuf.cpp
@@ -194,7 +194,7 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods()
case -1: m_pc_delta_to_bytes = [](offs_t delta) { return delta << 1; }; break;
case 0: m_pc_delta_to_bytes = [](offs_t delta) { return delta; }; break;
case 3: m_pc_delta_to_bytes = [](offs_t delta) { return delta >> 3; }; break;
- default: throw emu_fatalerror("debug_disasm_buffer::debug_data_buffer::setup_methods: Abnormal address buf shift\n");
+ default: throw emu_fatalerror("debug_disasm_buffer::debug_data_buffer::setup_methods: Abnormal address bus shift\n");
}
// Define the filler
diff --git a/src/emu/debug/debugcmd.cpp b/src/emu/debug/debugcmd.cpp
index f6f3a876235..4a433017724 100644
--- a/src/emu/debug/debugcmd.cpp
+++ b/src/emu/debug/debugcmd.cpp
@@ -1400,7 +1400,7 @@ void debugger_commands::execute_wpset(int ref, const std::vector<std::string> &p
address_space *space;
const char *action = nullptr;
u64 address, length;
- int type;
+ read_or_write type;
int wpnum;
/* CPU is implicit */
@@ -1417,11 +1417,11 @@ void debugger_commands::execute_wpset(int ref, const std::vector<std::string> &p
/* param 3 is the type */
if (params[2] == "r")
- type = WATCHPOINT_READ;
+ type = read_or_write::READ;
else if (params[2] == "w")
- type = WATCHPOINT_WRITE;
+ type = read_or_write::WRITE;
else if (params[2] == "rw" || params[2] == "wr")
- type = WATCHPOINT_READWRITE;
+ type = read_or_write::READWRITE;
else
{
m_console.printf("Invalid watchpoint type: expected r, w, or rw\n");
@@ -1527,20 +1527,20 @@ void debugger_commands::execute_wplist(int ref, const std::vector<std::string> &
/* loop over all CPUs */
for (device_t &device : device_iterator(m_machine.root_device()))
for (int spacenum = 0; spacenum < device.debug()->watchpoint_space_count(); ++spacenum)
- if (device.debug()->watchpoint_first(spacenum) != nullptr)
+ if (!device.debug()->watchpoint_vector(spacenum).empty())
{
static const char *const types[] = { "unkn ", "read ", "write", "r/w " };
m_console.printf("Device '%s' %s space watchpoints:\n", device.tag(),
- device.debug()->watchpoint_first(spacenum)->space().name());
+ device.debug()->watchpoint_vector(spacenum).front()->space().name());
/* loop over the watchpoints */
- for (device_debug::watchpoint *wp = device.debug()->watchpoint_first(spacenum); wp != nullptr; wp = wp->next())
+ for (const auto &wp : device.debug()->watchpoint_vector(spacenum))
{
buffer = string_format("%c%4X @ %0*X-%0*X %s", wp->enabled() ? ' ' : 'D', wp->index(),
wp->space().addrchars(), wp->address(),
wp->space().addrchars(), wp->address() + wp->length() - 1,
- types[wp->type() & 3]);
+ types[int(wp->type())]);
if (std::string(wp->condition()).compare("1") != 0)
buffer.append(string_format(" if %s", wp->condition()));
if (std::string(wp->action()).compare("") != 0)
@@ -3001,9 +3001,6 @@ void debugger_commands::execute_trackmem(int ref, const std::vector<std::string>
// Inform the CPU it's time to start tracking memory writes
cpu->debug()->set_track_mem(turnOn);
- // Use the watchpoint system to catch memory writes
- space->enable_write_watchpoints(true);
-
// Clear out the existing data if requested
if (clear)
space->device().debug()->track_mem_data_clear();
@@ -3127,7 +3124,7 @@ void debugger_commands::execute_map(int ref, const std::vector<std::string> &par
taddress = address & space->addrmask();
if (space->device().memory().translate(space->spacenum(), intention, taddress))
{
- const char *mapname = space->get_handler_string((intention == TRANSLATE_WRITE_DEBUG) ? read_or_write::WRITE : read_or_write::READ, taddress);
+ std::string mapname = space->get_handler_string((intention == TRANSLATE_WRITE_DEBUG) ? read_or_write::WRITE : read_or_write::READ, taddress);
m_console.printf(
"%7s: %0*X logical == %0*X physical -> %s\n",
intnames[intention & 3],
@@ -3158,8 +3155,31 @@ void debugger_commands::execute_memdump(int ref, const std::vector<std::string>
if (file)
{
memory_interface_iterator iter(m_machine.root_device());
- for (device_memory_interface &memory : iter)
- memory.dump(file);
+ for (device_memory_interface &memory : iter) {
+ for (int space = 0; space != memory.max_space_count(); space++)
+ if (memory.has_space(space))
+ {
+ address_space &sp = memory.space(space);
+ bool octal = sp.is_octal();
+ int nc = octal ? (sp.addr_width() + 2) / 3 : (sp.addr_width() + 3) / 4;
+
+ std::vector<memory_entry> entries[2];
+ sp.dump_maps(entries[0], entries[1]);
+ for (int mode = 0; mode < 2; mode ++)
+ {
+ fprintf(file, " device %s space %s %s:\n", memory.device().tag(), sp.name(), mode ? "write" : "read");
+ for (memory_entry &entry : entries[mode])
+ {
+ if (octal)
+ fprintf(file, "%0*o - %0*o", nc, entry.start, nc, entry.end);
+ else
+ fprintf(file, "%0*x - %0*x", nc, entry.start, nc, entry.end);
+ fprintf(file, ": %s\n", entry.entry->name().c_str());
+ }
+ fprintf(file, "\n");
+ }
+ }
+ }
fclose(file);
}
}
diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp
index bbf1170b762..cd130c5e3f0 100644
--- a/src/emu/debug/debugcpu.cpp
+++ b/src/emu/debug/debugcpu.cpp
@@ -1388,6 +1388,20 @@ device_debug::device_debug(device_t &device)
device.interface(m_state);
device.interface(m_disasm);
+ // set up notifiers and clear the passthrough handlers
+ if (m_memory) {
+ int count = m_memory->max_space_count();
+ m_phr.resize(count, nullptr);
+ m_phw.resize(count, nullptr);
+ for (int i=0; i != count; i++)
+ if (m_memory->has_space(i)) {
+ address_space &space = m_memory->space(i);
+ m_notifiers.push_back(space.add_change_notifier([this, &space](read_or_write mode) { reinstall(space, mode); }));
+ }
+ else
+ m_notifiers.push_back(-1);
+ }
+
// set up state-related stuff
if (m_state != nullptr)
{
@@ -1470,6 +1484,53 @@ device_debug::~device_debug()
registerpoint_clear_all();
}
+void device_debug::write_tracking(address_space &space, offs_t address, u64 data)
+{
+ dasm_memory_access const newAccess(space.spacenum(), address, data, history_pc(0));
+ std::pair<std::set<dasm_memory_access>::iterator, bool> trackedAccess = m_track_mem_set.insert(newAccess);
+ if (!trackedAccess.second)
+ trackedAccess.first->m_pc = newAccess.m_pc;
+}
+
+void device_debug::reinstall(address_space &space, read_or_write mode)
+{
+ int id = space.spacenum();
+ if (u32(mode) & u32(read_or_write::READ))
+ {
+ if (m_phr[id])
+ m_phr[id]->remove();
+ if (!m_hotspots.empty())
+ switch (space.data_width())
+ {
+ case 8: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u8 &, u8 ) { hotspot_check(space, address); }, m_phr[id]); break;
+ case 16: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u16 &, u16) { hotspot_check(space, address); }, m_phr[id]); break;
+ case 32: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u32 &, u32) { hotspot_check(space, address); }, m_phr[id]); break;
+ case 64: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u64 &, u64) { hotspot_check(space, address); }, m_phr[id]); break;
+ }
+ }
+ if (u32(mode) & u32(read_or_write::WRITE))
+ {
+ if (m_phw[id])
+ m_phw[id]->remove();
+ if (m_track_mem)
+ switch (space.data_width())
+ {
+ case 8: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u8 &data, u8 ) { write_tracking(space, address, data); }, m_phw[id]); break;
+ case 16: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u16 &data, u16) { write_tracking(space, address, data); }, m_phw[id]); break;
+ case 32: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u32 &data, u32) { write_tracking(space, address, data); }, m_phw[id]); break;
+ case 64: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u64 &data, u64) { write_tracking(space, address, data); }, m_phw[id]); break;
+ }
+ }
+}
+
+void device_debug::reinstall_all(read_or_write mode)
+{
+ int count = m_memory->max_space_count();
+ for (int i=0; i < count; i++)
+ if (m_memory->has_space(i))
+ reinstall(m_memory->space(i), mode);
+}
+
//-------------------------------------------------
// start_hook - the scheduler calls this hook
// before beginning execution for the given device
@@ -1679,42 +1740,6 @@ void device_debug::instruction_hook(offs_t curpc)
//-------------------------------------------------
-// memory_read_hook - the memory system calls
-// this hook when watchpoints are enabled and a
-// memory read happens
-//-------------------------------------------------
-
-void device_debug::memory_read_hook(address_space &space, offs_t address, u64 mem_mask)
-{
- // check watchpoints
- watchpoint_check(space, WATCHPOINT_READ, address, 0, mem_mask);
-
- // check hotspots
- if (!m_hotspots.empty())
- hotspot_check(space, address);
-}
-
-
-//-------------------------------------------------
-// memory_write_hook - the memory system calls
-// this hook when watchpoints are enabled and a
-// memory write happens
-//-------------------------------------------------
-
-void device_debug::memory_write_hook(address_space &space, offs_t address, u64 data, u64 mem_mask)
-{
- if (m_track_mem)
- {
- dasm_memory_access const newAccess(space.spacenum(), address, data, history_pc(0));
- std::pair<std::set<dasm_memory_access>::iterator, bool> trackedAccess = m_track_mem_set.insert(newAccess);
- if (!trackedAccess.second)
- trackedAccess.first->m_pc = newAccess.m_pc;
- }
- watchpoint_check(space, WATCHPOINT_WRITE, address, data, mem_mask);
-}
-
-
-//-------------------------------------------------
// set_instruction_hook - set a hook to be
// called on each instruction for a given device
//-------------------------------------------------
@@ -2015,22 +2040,16 @@ void device_debug::breakpoint_enable_all(bool enable)
// returning its index
//-------------------------------------------------
-int device_debug::watchpoint_set(address_space &space, int type, offs_t address, offs_t length, const char *condition, const char *action)
+int device_debug::watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition, const char *action)
{
if (space.spacenum() >= int(m_wplist.size()))
- m_wplist.resize(space.spacenum()+1, nullptr);
+ m_wplist.resize(space.spacenum()+1);
// allocate a new one
u32 id = m_device.machine().debugger().cpu().get_watchpoint_index();
- watchpoint *wp = auto_alloc(m_device.machine(), watchpoint(this, m_symtable, id, space, type, address, length, condition, action));
-
- // hook it into our list
- wp->m_next = m_wplist[space.spacenum()];
- m_wplist[space.spacenum()] = wp;
+ m_wplist[space.spacenum()].emplace_back(std::make_unique<watchpoint>(this, m_symtable, id, space, type, address, length, condition, action));
- // update the flags and return the index
- watchpoint_update_flags(wp->m_space);
- return wp->m_index;
+ return id;
}
@@ -2042,17 +2061,15 @@ int device_debug::watchpoint_set(address_space &space, int type, offs_t address,
bool device_debug::watchpoint_clear(int index)
{
// scan the list to see if we own this breakpoint
- for (int spacenum = 0; spacenum < int(m_wplist.size()); ++spacenum)
- for (watchpoint **wp = &m_wplist[spacenum]; *wp != nullptr; wp = &(*wp)->m_next)
- if ((*wp)->m_index == index)
+ for (auto &wpl : m_wplist)
+ {
+ for (auto wpi = wpl.begin(); wpi != wpl.end(); wpi++)
+ if ((*wpi)->index() == index)
{
- watchpoint *deleteme = *wp;
- address_space &space = deleteme->m_space;
- *wp = deleteme->m_next;
- auto_free(m_device.machine(), deleteme);
- watchpoint_update_flags(space);
+ wpl.erase(wpi);
return true;
}
+ }
// we don't own it, return false
return false;
@@ -2065,10 +2082,8 @@ bool device_debug::watchpoint_clear(int index)
void device_debug::watchpoint_clear_all()
{
- // clear the head until we run out
- for (int spacenum = 0; spacenum < int(m_wplist.size()); ++spacenum)
- while (m_wplist[spacenum] != nullptr)
- watchpoint_clear(m_wplist[spacenum]->index());
+ for (auto &wpl : m_wplist)
+ wpl.clear();
}
@@ -2080,12 +2095,11 @@ void device_debug::watchpoint_clear_all()
bool device_debug::watchpoint_enable(int index, bool enable)
{
// scan the list to see if we own this watchpoint
- for (int spacenum = 0; spacenum < int(m_wplist.size()); ++spacenum)
- for (watchpoint *wp = m_wplist[spacenum]; wp != nullptr; wp = wp->next())
- if (wp->m_index == index)
+ for (auto &wpl : m_wplist)
+ for (auto &wp : wpl)
+ if (wp->index() == index)
{
- wp->m_enabled = enable;
- watchpoint_update_flags(wp->m_space);
+ wp->setEnabled(enable);
return true;
}
@@ -2102,9 +2116,9 @@ bool device_debug::watchpoint_enable(int index, bool enable)
void device_debug::watchpoint_enable_all(bool enable)
{
// apply the enable to all watchpoints we own
- for (int spacenum = 0; spacenum < int(m_wplist.size()); ++spacenum)
- for (watchpoint *wp = m_wplist[spacenum]; wp != nullptr; wp = wp->next())
- watchpoint_enable(wp->index(), enable);
+ for (auto &wpl : m_wplist)
+ for (auto &wp : wpl)
+ wp->setEnabled(enable);
}
@@ -2218,10 +2232,7 @@ void device_debug::hotspot_track(int numspots, int threshhold)
// fill in the info
m_hotspot_threshhold = threshhold;
}
-
- // update the watchpoint flags to include us
- if (m_memory != nullptr && m_memory->has_space(AS_PROGRAM))
- watchpoint_update_flags(m_memory->space(AS_PROGRAM));
+ reinstall_all(read_or_write::READ);
}
@@ -2596,139 +2607,10 @@ void device_debug::breakpoint_check(offs_t pc)
//-------------------------------------------------
-// watchpoint_update_flags - update the device's
-// watchpoint flags
-//-------------------------------------------------
-
-void device_debug::watchpoint_update_flags(address_space &space)
-{
- // if hotspots are enabled, turn on all reads
- bool enableread = false;
- if (!m_hotspots.empty())
- enableread = true;
-
- // see if there are any enabled breakpoints
- bool enablewrite = false;
- if (space.spacenum() < int(m_wplist.size()))
- for (watchpoint *wp = m_wplist[space.spacenum()]; wp != nullptr; wp = wp->m_next)
- if (wp->m_enabled)
- {
- if (wp->m_type & WATCHPOINT_READ)
- enableread = true;
- if (wp->m_type & WATCHPOINT_WRITE)
- enablewrite = true;
- }
-
- // push the flags out globally
- space.enable_read_watchpoints(enableread);
- space.enable_write_watchpoints(enablewrite);
-}
-
-
-//-------------------------------------------------
// watchpoint_check - check the watchpoints
// for a given CPU and address space
//-------------------------------------------------
-void device_debug::watchpoint_check(address_space& space, int type, offs_t address, u64 value_to_write, u64 mem_mask)
-{
- m_device.machine().debugger().cpu().watchpoint_check(space, type, address, value_to_write, mem_mask, m_wplist);
-}
-
-void debugger_cpu::watchpoint_check(address_space& space, int type, offs_t address, u64 value_to_write, u64 mem_mask, std::vector<device_debug::watchpoint *> &wplist)
-{
- // if we're within debugger code, don't stop
- if (m_within_instruction_hook || m_machine.side_effects_disabled())
- return;
-
- m_within_instruction_hook = true;
-
- // adjust address, size & value_to_write based on mem_mask.
- offs_t size = 0;
- if (mem_mask != 0)
- {
- int bus_size = space.data_width() / 8;
- int address_offset = 0;
-
- while (address_offset < bus_size && (mem_mask & 0xff) == 0)
- {
- address_offset++;
- value_to_write >>= 8;
- mem_mask >>= 8;
- }
-
- while (mem_mask != 0)
- {
- size++;
- mem_mask >>= 8;
- }
-
- // (1<<(size*8))-1 won't work when size is 8; let's just use a lut
- static const u64 masks[] = {
- 0x0U,
- 0xffU,
- 0xffffU,
- 0xffffffU,
- 0xffffffffU,
- 0xffffffffffU,
- 0xffffffffffffU,
- 0xffffffffffffffU,
- 0xffffffffffffffffU};
- value_to_write &= masks[size];
-
- if (space.endianness() == ENDIANNESS_LITTLE)
- address += address_offset;
- else
- address += bus_size - size - address_offset;
- }
-
- // if we are a write watchpoint, stash the value that will be written
- m_wpaddr = address;
- if (type & WATCHPOINT_WRITE)
- m_wpdata = value_to_write;
-
- // see if we match
- if (space.spacenum() < int(wplist.size()))
- for (device_debug::watchpoint *wp = wplist[space.spacenum()]; wp != nullptr; wp = wp->next())
- if (wp->hit(type, address, size))
- {
- // halt in the debugger by default
- m_execution_state = exec_state::STOPPED;
-
- // if we hit, evaluate the action
- if (!wp->action().empty())
- m_machine.debugger().console().execute_command(wp->action(), false);
-
- // print a notification, unless the action made us go again
- if (m_execution_state == exec_state::STOPPED)
- {
- static const char *const sizes[] =
- {
- "0bytes", "byte", "word", "3bytes", "dword", "5bytes", "6bytes", "7bytes", "qword"
- };
- offs_t pc = space.device().state().pcbase();
- std::string buffer;
-
- if (type & WATCHPOINT_WRITE)
- {
- buffer = string_format("Stopped at watchpoint %X writing %s to %08X (PC=%X)", wp->index(), sizes[size], address, pc);
- if (value_to_write >> 32)
- buffer.append(string_format(" (data=%X%08X)", u32(value_to_write >> 32), u32(value_to_write)));
- else
- buffer.append(string_format(" (data=%X)", u32(value_to_write)));
- }
- else
- buffer = string_format("Stopped at watchpoint %X reading %s from %08X (PC=%X)", wp->index(), sizes[size], address, pc);
- m_machine.debugger().console().printf("%s\n", buffer.c_str());
- space.device().debug()->compute_debug_flags();
- }
- break;
- }
-
- m_within_instruction_hook = false;
-}
-
-
//-------------------------------------------------
// hotspot_check - check for hotspots on a
// memory read access
@@ -2773,7 +2655,6 @@ void device_debug::hotspot_check(address_space &space, offs_t address)
}
}
-
//-------------------------------------------------
// get_current_pc - getter callback for a device's
// current instruction pointer
@@ -2917,59 +2798,293 @@ device_debug::watchpoint::watchpoint(device_debug* debugInterface,
symbol_table &symbols,
int index,
address_space &space,
- int type,
+ read_or_write type,
offs_t address,
offs_t length,
const char *condition,
const char *action)
: m_debugInterface(debugInterface),
- m_next(nullptr),
- m_space(space),
- m_index(index),
- m_enabled(true),
- m_type(type),
- m_address(address & space.addrmask()),
- m_length(length),
- m_condition(&symbols, (condition != nullptr) ? condition : "1"),
- m_action((action != nullptr) ? action : "")
+ m_phr(nullptr),
+ m_phw(nullptr),
+ m_space(space),
+ m_index(index),
+ m_enabled(true),
+ m_type(type),
+ m_address(address & space.addrmask()),
+ m_length(length),
+ m_condition(&symbols, (condition != nullptr) ? condition : "1"),
+ m_action((action != nullptr) ? action : "")
+{
+ std::fill(std::begin(m_start_address), std::end(m_start_address), 0);
+ std::fill(std::begin(m_end_address), std::end(m_end_address), 0);
+ std::fill(std::begin(m_masks), std::end(m_masks), 0);
+
+ offs_t ashift = m_space.addr_shift();
+ endianness_t endian = m_space.endianness();
+ offs_t subamask = m_space.alignment() - 1;
+ offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift;
+ offs_t start = m_address;
+ offs_t end = (m_address + m_length - 1) & space.addrmask();
+ if (end < start)
+ end = space.addrmask();
+ offs_t rstart = start & ~subamask;
+ offs_t rend = end | subamask;
+ u64 smask, mmask, emask;
+ smask = mmask = emask = make_bitmask<u64>(m_space.data_width());
+ if (start != rstart)
+ {
+ if (endian == ENDIANNESS_LITTLE)
+ smask &= ~make_bitmask<u64>((start - rstart) * unit_size);
+ else
+ smask &= make_bitmask<u64>((rstart + subamask + 1 - start) * unit_size);
+ }
+ if (end != rend)
+ {
+ if (endian == ENDIANNESS_LITTLE)
+ emask &= make_bitmask<u64>((rend + subamask + 1 - end) * unit_size);
+ else
+ emask &= ~make_bitmask<u64>((end - rend) * unit_size);
+ }
+
+ if (rend == (rstart | subamask) || smask == emask)
+ {
+ m_start_address[0] = rstart;
+ m_end_address[0] = rend;
+ m_masks[0] = smask & emask;
+ }
+ else
+ {
+ int idx = 0;
+ if (smask != mmask)
+ {
+ m_start_address[idx] = rstart;
+ m_end_address[idx] = rstart | subamask;
+ m_masks[idx] = smask;
+ idx++;
+ rstart += subamask + 1;
+ }
+ if (mmask == emask)
+ {
+ m_start_address[idx] = rstart;
+ m_end_address[idx] = rend;
+ m_masks[idx] = emask;
+ }
+ else
+ {
+ m_start_address[idx] = rstart;
+ m_end_address[idx] = rend - subamask - 1;
+ m_masks[idx] = mmask;
+ idx++;
+ m_start_address[idx] = rend - subamask;
+ m_end_address[idx] = rend;
+ m_masks[idx] = emask;
+ }
+ }
+
+ install(read_or_write::READWRITE);
+ m_notifier = m_space.add_change_notifier([this](read_or_write mode) {
+ if (m_enabled)
+ {
+ if (u32(mode) & u32(read_or_write::READ))
+ m_phr->remove();
+ if (u32(mode) & u32(read_or_write::WRITE))
+ m_phw->remove();
+ install(mode);
+ }
+ });
+}
+
+device_debug::watchpoint::~watchpoint()
{
+ m_space.remove_change_notifier(m_notifier);
+}
+
+void device_debug::watchpoint::setEnabled(bool value)
+{
+ if (m_enabled != value)
+ {
+ m_enabled = value;
+ if (m_enabled)
+ install(read_or_write::READWRITE);
+ else
+ {
+ m_phr->remove();
+ m_phw->remove();
+ }
+ }
}
+void device_debug::watchpoint::install(read_or_write mode)
+{
+ std::string name = util::string_format("wp@%x", m_address);
+ switch (m_space.data_width())
+ {
+ case 8:
+ if (u32(m_type) & u32(mode) & u32(read_or_write::READ))
+ m_phr = m_space.install_read_tap(m_start_address[0], m_end_address[0], name,
+ [this](offs_t offset, u8 &data, u8 mem_mask) {
+ triggered(read_or_write::READ, offset, data, mem_mask);
+ }, m_phr);
+ if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE))
+ m_phw = m_space.install_write_tap(m_start_address[0], m_end_address[0], name,
+ [this](offs_t offset, u8 &data, u8 mem_mask) {
+ triggered(read_or_write::WRITE, offset, data, mem_mask);
+ }, m_phw);
+ break;
-//-------------------------------------------------
-// hit - detect a hit
-//-------------------------------------------------
+ case 16:
+ for (int i=0; i != 3; i++)
+ if (m_masks[i])
+ {
+ u16 mask = m_masks[i];
+ if (u32(m_type) & u32(mode) & u32(read_or_write::READ))
+ m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u16 &data, u16 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::READ, offset, data, mem_mask);
+ }, m_phr);
+ if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE))
+ m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u16 &data, u16 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::WRITE, offset, data, mem_mask);
+ }, m_phw);
+ }
+ break;
+
+ case 32:
+ for (int i=0; i != 3; i++)
+ if (m_masks[i])
+ {
+ u32 mask = m_masks[i];
+ if (u32(m_type) & u32(mode) & u32(read_or_write::READ))
+ m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u32 &data, u32 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::READ, offset, data, mem_mask);
+ }, m_phr);
+ if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE))
+ m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u32 &data, u32 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::WRITE, offset, data, mem_mask);
+ }, m_phw);
+ }
+ break;
+
+ case 64:
+ for (int i=0; i != 3; i++)
+ if (m_masks[i])
+ {
+ u64 mask = m_masks[i];
+ if (u32(m_type) & u32(mode) & u32(read_or_write::READ))
+ m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u64 &data, u64 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::READ, offset, data, mem_mask);
+ }, m_phr);
+ if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE))
+ m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name,
+ [this, mask](offs_t offset, u64 &data, u64 mem_mask) {
+ if (mem_mask & mask)
+ triggered(read_or_write::WRITE, offset, data, mem_mask);
+ }, m_phw);
+ }
+ break;
+ }
+}
-bool device_debug::watchpoint::hit(int type, offs_t address, int size)
+void device_debug::watchpoint::triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask)
{
- // don't hit if disabled
- if (!m_enabled)
- return false;
+ auto &machine = m_debugInterface->m_device.machine();
+ auto &debug = machine.debugger();
- // must match the type
- if ((m_type & type) == 0)
- return false;
+ // if we're within debugger code, don't trigger
+ if (debug.cpu().within_instruction_hook() || machine.side_effects_disabled())
+ return;
- // must match our address
- if (address + size <= m_address || address >= m_address + m_length)
- return false;
+ // adjust address, size & value_to_write based on mem_mask.
+ offs_t size = 0;
+ offs_t ashift = m_space.addr_shift();
+ offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift;
+ u64 unit_mask = make_bitmask<u64>(unit_size);
+
+ offs_t address_offset = 0;
+
+ if(!mem_mask)
+ mem_mask = 0xff;
+
+ while (!(mem_mask & unit_mask))
+ {
+ address_offset++;
+ data >>= unit_size;
+ mem_mask >>= unit_size;
+ }
+
+ while (mem_mask)
+ {
+ size++;
+ mem_mask >>= unit_size;
+ }
+
+ data &= make_bitmask<u64>(size * unit_size);
+
+ if (m_space.endianness() == ENDIANNESS_LITTLE)
+ address += address_offset;
+ else
+ address += m_space.alignment() - size - address_offset;
+
+ // stash the value that will be written or has just been read
+ debug.cpu().set_wpinfo(address, data);
+
+ // protect against recursion
+ debug.cpu().set_within_instruction(true);
// must satisfy the condition
if (!m_condition.is_empty())
{
try
{
- return (m_condition.execute() != 0);
+ if (!m_condition.execute() != 0)
+ {
+ debug.cpu().set_within_instruction(false);
+ return;
+ }
}
catch (expression_error &)
{
- return false;
+ debug.cpu().set_within_instruction(false);
+ return;
}
}
- return true;
-}
+ // halt in the debugger by default
+ debug.cpu().set_execution_stopped();
+
+ // evaluate the action
+ if (!m_action.empty())
+ debug.console().execute_command(m_action, false);
+ // print a notification, unless the action made us go again
+ if (debug.cpu().is_stopped())
+ {
+ offs_t pc = m_space.device().state().pcbase();
+ std::string buffer;
+
+ buffer = string_format(type == read_or_write::READ ?
+ "Stopped at watchpoint %X reading %0*X from %08X (PC=%X)" :
+ "Stopped at watchpoint %X writing %0*X to %08X (PC=%X)",
+ m_index,
+ size * unit_size / 4,
+ data,
+ address,
+ pc);
+ debug.console().printf("%s\n", buffer);
+ m_debugInterface->compute_debug_flags();
+ }
+
+ debug.cpu().set_within_instruction(false);
+}
//**************************************************************************
// DEBUG REGISTERPOINT
diff --git a/src/emu/debug/debugcpu.h b/src/emu/debug/debugcpu.h
index 6d54456b913..f3e8f68a408 100644
--- a/src/emu/debug/debugcpu.h
+++ b/src/emu/debug/debugcpu.h
@@ -22,10 +22,6 @@
// CONSTANTS
//**************************************************************************
-constexpr u8 WATCHPOINT_READ = 1;
-constexpr u8 WATCHPOINT_WRITE = 2;
-constexpr u8 WATCHPOINT_READWRITE = WATCHPOINT_READ | WATCHPOINT_WRITE;
-
constexpr int COMMENT_VERSION = 1;
@@ -74,7 +70,6 @@ public:
private:
// internals
bool hit(offs_t pc);
-
const device_debug * m_debugInterface; // the interface we were created from
breakpoint * m_next; // next in the list
int m_index; // user reported index
@@ -96,18 +91,18 @@ public:
symbol_table &symbols,
int index,
address_space &space,
- int type,
+ read_or_write type,
offs_t address,
offs_t length,
const char *condition = nullptr,
const char *action = nullptr);
+ ~watchpoint();
// getters
const device_debug *debugInterface() const { return m_debugInterface; }
- watchpoint *next() const { return m_next; }
address_space &space() const { return m_space; }
int index() const { return m_index; }
- int type() const { return m_type; }
+ read_or_write type() const { return m_type; }
bool enabled() const { return m_enabled; }
offs_t address() const { return m_address; }
offs_t length() const { return m_length; }
@@ -115,22 +110,30 @@ public:
const std::string &action() const { return m_action; }
// setters
- void setEnabled(bool value) { m_enabled = value; }
+ void setEnabled(bool value);
// internals
bool hit(int type, offs_t address, int size);
private:
- const device_debug * m_debugInterface; // the interface we were created from
- watchpoint * m_next; // next in the list
+ device_debug * m_debugInterface; // the interface we were created from
+ memory_passthrough_handler *m_phr; // passthrough handler reference, read access
+ memory_passthrough_handler *m_phw; // passthrough handler reference, write access
address_space & m_space; // address space
int m_index; // user reported index
bool m_enabled; // enabled?
- u8 m_type; // type (read/write)
+ read_or_write m_type; // type (read/write)
offs_t m_address; // start address
offs_t m_length; // length of watch area
parsed_expression m_condition; // condition
std::string m_action; // action
+ int m_notifier; // address map change notifier id
+
+ offs_t m_start_address[3]; // the start addresses of the checks to install
+ offs_t m_end_address[3]; // the end addresses
+ u64 m_masks[3]; // the access masks
+ void install(read_or_write mode);
+ void triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask);
};
// registerpoint class
@@ -179,8 +182,6 @@ public:
void interrupt_hook(int irqline);
void exception_hook(int exception);
void instruction_hook(offs_t curpc);
- void memory_read_hook(address_space &space, offs_t address, u64 mem_mask);
- void memory_write_hook(address_space &space, offs_t address, u64 data, u64 mem_mask);
// hooks into our operations
void set_instruction_hook(debug_instruction_hook_func hook);
@@ -222,8 +223,8 @@ public:
// watchpoints
int watchpoint_space_count() const { return m_wplist.size(); }
- watchpoint *watchpoint_first(int spacenum) const { return m_wplist[spacenum]; }
- int watchpoint_set(address_space &space, int type, offs_t address, offs_t length, const char *condition, const char *action);
+ const std::vector<std::unique_ptr<watchpoint>> &watchpoint_vector(int spacenum) const { return m_wplist[spacenum]; }
+ int watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition, const char *action);
bool watchpoint_clear(int wpnum);
void watchpoint_clear_all();
bool watchpoint_enable(int index, bool enable = true);
@@ -289,9 +290,10 @@ private:
// breakpoint and watchpoint helpers
void breakpoint_update_flags();
void breakpoint_check(offs_t pc);
- void watchpoint_update_flags(address_space &space);
- void watchpoint_check(address_space &space, int type, offs_t address, u64 value_to_write, u64 mem_mask);
void hotspot_check(address_space &space, offs_t address);
+ void reinstall_all(read_or_write mode);
+ void reinstall(address_space &space, read_or_write mode);
+ void write_tracking(address_space &space, offs_t address, u64 data);
// symbol get/set callbacks
static u64 get_current_pc(symbol_table &table);
@@ -332,7 +334,7 @@ private:
// breakpoints and watchpoints
breakpoint * m_bplist; // list of breakpoints
- std::vector<watchpoint *> m_wplist; // watchpoint lists for each address space
+ std::vector<std::vector<std::unique_ptr<watchpoint>>> m_wplist; // watchpoint lists for each address space
registerpoint * m_rplist; // list of registerpoints
// tracing
@@ -373,9 +375,13 @@ private:
address_space * m_space; // space where the access occurred
u32 m_count; // number of hits
};
- std::vector<hotspot_entry> m_hotspots; // hotspot list
+ std::vector<hotspot_entry> m_hotspots; // hotspot list
int m_hotspot_threshhold; // threshhold for the number of hits to print
+ std::vector<memory_passthrough_handler *> m_phr; // passthrough handler reference for each space, read mode
+ std::vector<memory_passthrough_handler *> m_phw; // passthrough handler reference for each space, write mode
+ std::vector<int> m_notifiers; // notifiers for each space
+
// pc tracking
class dasm_pc_tag
{
@@ -564,6 +570,7 @@ public:
void set_memory_modified(bool memory_modified) { m_memory_modified = memory_modified; }
void set_execution_stopped() { m_execution_state = exec_state::STOPPED; }
void set_execution_running() { m_execution_state = exec_state::RUNNING; }
+ void set_wpinfo(offs_t address, u64 data) { m_wpaddr = address; m_wpdata = data; }
// device_debug helpers
// [TODO] [RH]: Look into this more later, can possibly merge these two classes
@@ -575,7 +582,6 @@ public:
void ensure_comments_loaded();
void reset_transient_flags();
void process_source_file();
- void watchpoint_check(address_space& space, int type, offs_t address, u64 value_to_write, u64 mem_mask, std::vector<device_debug::watchpoint *> &wplist);
private:
static const size_t NUM_TEMP_VARIABLES;
diff --git a/src/emu/debug/dvwpoints.cpp b/src/emu/debug/dvwpoints.cpp
index a0e5befe017..2ab1fc6a0bf 100644
--- a/src/emu/debug/dvwpoints.cpp
+++ b/src/emu/debug/dvwpoints.cpp
@@ -79,7 +79,7 @@ static int cTypeAscending(const void* a, const void* b)
{
const device_debug::watchpoint* left = *(device_debug::watchpoint**)a;
const device_debug::watchpoint* right = *(device_debug::watchpoint**)b;
- return left->type() - right->type();
+ return int(left->type()) - int(right->type());
}
static int cTypeDescending(const void* a, const void* b)
@@ -229,8 +229,8 @@ void debug_view_watchpoints::gather_watchpoints()
device_debug &debugInterface = *source.device()->debug();
for (int spacenum = 0; spacenum < debugInterface.watchpoint_space_count(); ++spacenum)
{
- for (device_debug::watchpoint *wp = debugInterface.watchpoint_first(spacenum); wp != nullptr; wp = wp->next())
- m_buffer.push_back(wp);
+ for (const auto &wp : debugInterface.watchpoint_vector(spacenum))
+ m_buffer.push_back(wp.get());
}
}
@@ -330,7 +330,7 @@ void debug_view_watchpoints::view_update()
linebuf.put('-');
util::stream_format(linebuf, "%0*X", wp->space().addrchars(), wp->address() + wp->length() - 1);
pad_ostream_to_length(linebuf, tableBreaks[4]);
- linebuf << types[wp->type() & 3];
+ linebuf << types[int(wp->type())];
pad_ostream_to_length(linebuf, tableBreaks[5]);
if (strcmp(wp->condition(), "1"))
linebuf << wp->condition();
diff --git a/src/emu/dimemory.cpp b/src/emu/dimemory.cpp
index 84b161a5a13..0c1caace023 100644
--- a/src/emu/dimemory.cpp
+++ b/src/emu/dimemory.cpp
@@ -117,34 +117,6 @@ void device_memory_interface::set_addrmap(int spacenum, address_map_constructor
//-------------------------------------------------
-// dump - dump memory tables to the given file in
-// human-readable format
-//-------------------------------------------------
-
-void device_memory_interface::dump(FILE *file) const
-{
- for (auto const &space : m_addrspace)
- if (space) {
- fprintf(file,
- "\n\n"
- "====================================================\n"
- "Device '%s' %s address space read handler dump\n"
- "====================================================\n",
- device().tag(), space->name());
- space->dump_map(file, read_or_write::READ);
-
- fprintf(file,
- "\n\n"
- "====================================================\n"
- "Device '%s' %s address space write handler dump\n"
- "====================================================\n",
- device().tag(), space->name());
- space->dump_map(file, read_or_write::WRITE);
- }
-}
-
-
-//-------------------------------------------------
// memory_translate - translate from logical to
// phyiscal addresses; designed to be overridden
// by the actual device implementation if address
diff --git a/src/emu/dimemory.h b/src/emu/dimemory.h
index ec35228edd8..612ff15ae28 100644
--- a/src/emu/dimemory.h
+++ b/src/emu/dimemory.h
@@ -123,7 +123,7 @@ public:
assert((0 <= spacenum) && (max_space_count() > spacenum));
m_addrspace.resize(std::max<std::size_t>(m_addrspace.size(), spacenum + 1));
assert(!m_addrspace[spacenum]);
- m_addrspace[spacenum] = std::make_unique<Space>(manager, *this, spacenum);
+ m_addrspace[spacenum] = std::make_unique<Space>(manager, *this, spacenum, space_config(spacenum)->addr_width());
}
void prepare_maps() { for (auto const &space : m_addrspace) { if (space) { space->prepare_map(); } } }
void populate_from_maps() { for (auto const &space : m_addrspace) { if (space) { space->populate_from_map(); } } }
@@ -131,9 +131,6 @@ public:
void locate_memory() { for (auto const &space : m_addrspace) { if (space) { space->locate_memory(); } } }
void set_log_unmap(bool log) { for (auto const &space : m_addrspace) { if (space) { space->set_log_unmap(log); } } }
- // diagnostic functions
- void dump(FILE *file) const;
-
protected:
using space_config_vector = std::vector<std::pair<int, const address_space_config *>>;
diff --git a/src/emu/emumem.cpp b/src/emu/emumem.cpp
index c27fb468287..ae3e95a0b3b 100644
--- a/src/emu/emumem.cpp
+++ b/src/emu/emumem.cpp
@@ -6,1054 +6,348 @@
Functions which handle device memory access.
-****************************************************************************
-
- Basic theory of memory handling:
-
- An address with up to 32 bits is passed to a memory handler. First,
- an address mask is applied to the address, removing unused bits.
-
- Next, the address is broken into two halves, an upper half and a
- lower half. The number of bits in each half can be controlled via
- the macros in LEVEL1_BITS and LEVEL2_BITS, but they default to the
- upper 18 bits and the lower 14 bits.
-
- The upper half is then used as an index into a lookup table of bytes.
- If the value pulled from the table is between SUBTABLE_BASE and 255,
- then the lower half of the address is needed to resolve the final
- handler. In this case, the value from the table is combined with the
- lower address bits to form an index into a subtable.
-
- The final result of the lookup is a value from 0 to SUBTABLE_BASE - 1.
- These values correspond to memory handlers. The lower numbered
- handlers (from 0 through STATIC_COUNT - 1) are fixed handlers and refer
- to either memory banks or other special cases. The remaining handlers
- (from STATIC_COUNT through SUBTABLE_BASE - 1) are dynamically
- allocated to driver-specified handlers.
-
- Thus, table entries fall into these categories:
-
- 0 .. STATIC_COUNT - 1 = fixed handlers
- STATIC_COUNT .. SUBTABLE_BASE - 1 = driver-specific handlers
- SUBTABLE_BASE .. TOTAL_MEMORY_BANKS - 1 = need to look up lower bits in subtable
-
- Caveats:
-
- * If your driver executes an opcode which crosses a bank-switched
- boundary, it will pull the wrong data out of memory. Although not
- a common case, you may need to revert to memcpy to work around this.
- See machine/tnzs.c for an example.
-
- To do:
-
- - Add local banks for RAM/ROM to reduce pressure on banking
- - Always mirror everything out to 32 bits so we don't have to mask the address?
- - Add the ability to start with another memory map and modify it
- - Add fourth memory space for encrypted opcodes
- - Automatically mirror program space into data space if no data space
- - Get rid of opcode/data separation by using address spaces?
- - Add support for internal addressing (maybe just accessors - see TMS3202x)
-
-****************************************************************************
-
- Address map fields and restrictions:
-
- AM_RANGE(start, end)
- Specifies a range of consecutive addresses beginning with 'start' and
- ending with 'end' inclusive. An address hits in this bucket if the
- 'address' >= 'start' and 'address' <= 'end'.
-
- AM_MASK(mask)
- Specifies a mask for the addresses in the current bucket. This mask
- is applied after a positive hit in the bucket specified by AM_RANGE
- or AM_SPACE, and is computed before accessing the RAM or calling
- through to the read/write handler. If you use AM_MIRROR, below, there
- should not be any bits in common between mask and mirror. Same for
- select.
-
- AM_MIRROR(mirror)
- Specifies mirror addresses for the given bucket. The current bucket
- is mapped repeatedly according to the mirror mask, once where each
- mirror bit is 0, and once where it is 1. For example, a 'mirror'
- value of 0x14000 would map the bucket at 0x00000, 0x04000, 0x10000,
- and 0x14000.
-
- AM_SELECT(select)
- Mirrors the addresses for the given bucket and pass the corresponding
- address bits to the handler. Very useful for devices with multiple
- slots/channels/etc were each slot is on a series of consecutive
- addresses regrouping all its registers.
-
- AM_ROM
- Specifies that this bucket contains ROM data by attaching an
- internal read handler. If this address space describes the first
- address space for a device, and if there is a region whose name
- matches the device's name, and if the bucket start/end range is
- within the bounds of that region, then this bucket will automatically
- map to the memory contained in that region.
-
- AM_RAM
- AM_READONLY
- AM_WRITEONLY
- Specifies that this bucket contains RAM data by attaching internal
- read and/or write handlers. Memory is automatically allocated to back
- this area. AM_RAM maps both reads and writes, while AM_READONLY only
- maps reads and AM_WRITEONLY only maps writes.
-
- AM_NOP
- AM_READNOP
- AM_WRITENOP
- Specifies that reads and/or writes in this bucket are unmapped, but
- that accesses to them should not be logged. AM_NOP unmaps both reads
- and writes, while AM_READNOP only unmaps reads, and AM_WRITENOP only
- unmaps writes.
-
- AM_UNMAP
- Specifies that both reads and writes in thus bucket are unmapeed,
- and that accesses to them should be logged. There is rarely a need
- for this, as the entire address space is initialized to behave this
- way by default.
-
- AM_READ_BANK(tag)
- AM_WRITE_BANK(tag)
- AM_READWRITE_BANK(tag)
- Specifies that reads and/or writes in this bucket map to a memory
- bank with the provided 'tag'. The actual memory this bank points to
- can be later controlled via the same tag.
-
- AM_READ(read)
- AM_WRITE(write)
- AM_READWRITE(read, write)
- Specifies read and/or write handler callbacks for this bucket. All
- reads and writes in this bucket will trigger a call to the provided
- functions.
-
- AM_DEVREAD(tag, read)
- AM_DEVWRITE(tag, read)
- AM_DEVREADWRITE(tag, read)
- Specifies a device-specific read and/or write handler for this
- bucket, automatically bound to the device specified by the provided
- 'tag'.
-
- AM_READ_PORT(tag)
- AM_WRITE_PORT(tag)
- AM_READWRITE_PORT(tag)
- Specifies that read and/or write accesses in this bucket will map
- to the I/O port with the provided 'tag'. An internal read/write
- handler is set up to handle this mapping.
-
- AM_REGION(class, tag, offs)
- Only useful if used in conjunction with AM_ROM, AM_RAM, or
- AM_READ/WRITE_BANK. By default, memory is allocated to back each
- bucket. By specifying AM_REGION, you can tell the memory system to
- point the base of the memory backing this bucket to a given memory
- 'region' at the specified 'offs' instead of allocating it.
-
- AM_SHARE(tag)
- Similar to AM_REGION, this specifies that the memory backing the
- current bucket is shared with other buckets. The first bucket to
- specify the share 'tag' will use its memory as backing for all
- future buckets that specify AM_SHARE with the same 'tag'.
-
- AM_SETOFFSET(setoffset)
- Specifies a handler for a 'set address' operation. The intended use case
- for this operation is to emulate a split-phase memory access: The caller
- (usually a CPU) sets the address bus lines using set_address. Some
- component may then react, for instance, by asserting a control line
- like WAIT before delivering the data on the data bus. The data bits are
- then sampled on the read operation or delivered on the write operation
- that must be called subsequently.
- It is not checked whether the address of the set_address operation
- matches the address of the subsequent read/write operation.
- The address map translates the address to a bucket and an offset,
- hence the name of the macro. If no handler is specified for a bucket,
- a set_address operation hitting that bucket returns silently.
-
- AM_DEVSETOFFSET(tag, setoffset)
- Specifies a handler for a set_address operation, bound to the device
- specified by 'tag'.
-
-
***************************************************************************/
+#include "emu.h"
#include <list>
#include <map>
-
-#include "emu.h"
#include "emuopts.h"
#include "debug/debugcpu.h"
+#include "emumem_mud.h"
+#include "emumem_hea.h"
+#include "emumem_hem.h"
+#include "emumem_hedp.h"
+#include "emumem_heun.h"
+#include "emumem_heu.h"
+#include "emumem_hedr.h"
+#include "emumem_hedw.h"
+#include "emumem_hep.h"
+#include "emumem_het.h"
+
//**************************************************************************
// DEBUGGING
//**************************************************************************
-#define MEM_DUMP (0)
#define VERBOSE (0)
-#define TEST_HANDLER (0)
#define VPRINTF(x) do { if (VERBOSE) printf x; } while (0)
-/*-------------------------------------------------
- core_i64_hex_format - i64 format printf helper
--------------------------------------------------*/
+#define VALIDATE_REFCOUNTS 1
-static char *core_i64_hex_format(u64 value, u8 mindigits)
+void handler_entry::dump_map(std::vector<memory_entry> &map) const
{
- 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;
+ fatalerror("dump_map called on non-dispatching class\n");
}
-
-
-//**************************************************************************
-// CONSTANTS
-//**************************************************************************
-
-// banking constants
-const int BANK_ENTRY_UNSPECIFIED = -1;
-
-// other address map constants
-const int MEMORY_BLOCK_CHUNK = 65536; // minimum chunk size of allocated memory blocks
-
-// static data access handler constants
-enum
+void handler_entry::reflist::add(const handler_entry *entry)
{
- STATIC_INVALID = 0, // invalid - should never be used
- STATIC_BANK1 = 1, // first memory bank
- STATIC_BANKMAX = 0xfb, // last memory bank
- STATIC_NOP, // NOP - reads = unmapped value; writes = no-op
- STATIC_UNMAP, // unmapped - same as NOP except we log errors
- STATIC_WATCHPOINT, // watchpoint - used internally
- STATIC_COUNT // total number of static handlers
-};
-
-
-
-//**************************************************************************
-// TYPE DEFINITIONS
-//**************************************************************************
-
-
-// ======================> handler_entry
-
-// a handler entry contains information about a memory handler
-class handler_entry
-{
- DISABLE_COPYING(handler_entry);
-
-protected:
- // construction/destruction
- handler_entry(u8 width, endianness_t endianness, u8 **rambaseptr);
- virtual ~handler_entry();
-
-public:
- // getters
- bool populated() const { return m_populated; }
- offs_t addrstart() const { return m_addrstart; }
- offs_t addrend() const { return m_addrend; }
- offs_t addrmask() const { return m_addrmask; }
- offs_t bytemask() const { return m_bytemask; }
- virtual const char *name() const = 0;
- virtual const char *subunit_name(int entry) const = 0;
- void description(char *buffer) const;
-
- virtual void copy(handler_entry *entry);
-
- // return offset within the range referenced by this handler
- offs_t offset(offs_t address) const { return (address - m_addrstart) & m_addrmask; }
-
- // return a pointer to the backing RAM at the given offset
- u8 *ramptr(offs_t offset = 0) const { return *m_rambaseptr + offset; }
-
- // see if we are an exact match to the given parameters
- bool matches_exactly(offs_t addrstart, offs_t addrend, offs_t addrmask) const
- {
- return (m_populated && m_addrstart == addrstart && m_addrend == addrend && m_addrmask == addrmask);
- }
-
- // get the start/end address with the given mirror
- void mirrored_start_end(offs_t address, offs_t &start, offs_t &end) const
- {
- offs_t mirrorbits = (address - m_addrstart) & ~m_addrmask;
- start = m_addrstart | mirrorbits;
- end = m_addrend | mirrorbits;
- }
-
- // configure the handler addresses, and mark as populated
- void configure(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t bytemask)
- {
- if (m_populated && m_subunits)
- reconfigure_subunits(addrstart);
- m_populated = true;
- m_addrstart = addrstart;
- m_addrend = addrend;
- m_addrmask = addrmask;
- m_bytemask = bytemask;
- }
-
- // Re-expand the addrmask after subunit fun
- void expand_bytemask(offs_t previous_mask)
- {
- m_bytemask |= previous_mask;
- }
-
- // reconfigure the subunits on a base address change
- void reconfigure_subunits(offs_t addrstart);
-
- // depopulate an handler
- void deconfigure()
- {
- m_populated = false;
- m_subunits = 0;
+ refcounts[entry]++;
+ if(seen.find(entry) == seen.end()) {
+ seen.insert(entry);
+ todo.insert(entry);
}
+}
- // apply a global mask
- void apply_mask(offs_t addrmask) { m_addrmask &= addrmask; }
-
- void clear_conflicting_subunits(u64 handlermask);
- bool overriden_by_mask(u64 handlermask) const;
-
-protected:
- // Subunit description information
- struct subunit_info
- {
- offs_t m_addrmask; // addrmask for this subunit
- u64 m_datamask; // datamask for this subunit
- u64 m_csmask; // chip select mask for this subunit
- s32 m_offset; // offset to add to the address
- u32 m_multiplier; // multiplier to the pre-split address
- u8 m_size; // size (8, 16 or 32)
- u8 m_shift; // shift of the subunit
- };
-
- // internal helpers
- void configure_subunits(u64 handlermask, int handlerbits, int cswidth, int &start_slot, int &end_slot);
- virtual void remove_subunit(int entry) = 0;
-
- // internal state
- bool m_populated; // populated?
- u8 m_datawidth;
- endianness_t m_endianness;
- offs_t m_addrstart; // start address for handler
- offs_t m_addrend; // end address for handler
- offs_t m_addrmask; // mask against the final address
- offs_t m_bytemask; // mask against the final address, byte resolution
- u8 ** m_rambaseptr; // pointer to the bank base
- u8 m_subunits; // for width stubs, the number of subunits
- subunit_info m_subunit_infos[8]; // for width stubs, the associated subunit info
- u64 m_invsubmask; // inverted mask of the populated subunits
-};
-
-
-// ======================> handler_entry_read
-
-// a read-access-specific extension of handler_entry
-class handler_entry_read : public handler_entry
+void handler_entry::reflist::propagate()
{
-public:
- struct access_handler
- {
- // Constructors mean you can't union them
- read8_delegate r8;
- read16_delegate r16;
- read32_delegate r32;
- read64_delegate r64;
- };
-
- // construction/destruction
- handler_entry_read(u8 width, endianness_t endianness, u8 **rambaseptr)
- : handler_entry(width, endianness, rambaseptr),
- m_ioport(nullptr)
- {
+ while(!todo.empty()) {
+ const handler_entry *entry = *todo.begin();
+ todo.erase(todo.begin());
+ entry->enumerate_references(*this);
}
+}
- virtual void copy(handler_entry *entry) override;
-
- // getters
- virtual const char *name() const override;
- virtual const char *subunit_name(int entry) const override;
-
- // configure delegate callbacks
- void set_delegate(read8_delegate delegate, u64 umask = 0, int cswidth = 8);
- void set_delegate(read16_delegate delegate, u64 umask = 0, int cswidth = 16);
- void set_delegate(read32_delegate delegate, u64 umask = 0, int cswidth = 32);
- void set_delegate(read64_delegate delegate, u64 umask = 0, int cswidth = 64);
-
- // configure I/O port access
- void set_ioport(ioport_port &ioport);
-
- // read via the underlying delegates
- u8 read8(address_space &space, offs_t offset, u8 mask) const { return m_read.r8(space, offset, mask); }
- u16 read16(address_space &space, offs_t offset, u16 mask) const { return m_read.r16(space, offset, mask); }
- u32 read32(address_space &space, offs_t offset, u32 mask) const { return m_read.r32(space, offset, mask); }
- u64 read64(address_space &space, offs_t offset, u64 mask) const { return m_read.r64(space, offset, mask); }
-
-private:
- // stubs for converting between address sizes
- u16 read_stub_16(address_space &space, offs_t offset, u16 mask);
- u32 read_stub_32(address_space &space, offs_t offset, u32 mask);
- u64 read_stub_64(address_space &space, offs_t offset, u64 mask);
-
- // stubs for reading I/O ports
- template<typename UintType>
- UintType read_stub_ioport(address_space &space, offs_t offset, UintType mask) { return m_ioport->read(); }
-
- // internal helper
- virtual void remove_subunit(int entry) override;
-
- // internal state
- access_handler m_read;
- access_handler m_subread[8];
- ioport_port * m_ioport;
-};
-
-
-// ======================> handler_entry_write
-
-// a write-access-specific extension of handler_entry
-class handler_entry_write : public handler_entry
+void handler_entry::reflist::check()
{
-public:
- struct access_handler
- {
- // Constructors mean you can't union them
- write8_delegate w8;
- write16_delegate w16;
- write32_delegate w32;
- write64_delegate w64;
- };
-
- // construction/destruction
- handler_entry_write(u8 width, endianness_t endianness, u8 **rambaseptr)
- : handler_entry(width, endianness, rambaseptr),
- m_ioport(nullptr)
- {
+ bool bad = false;
+ for(const auto &i : refcounts) {
+ if(i.first->get_refcount() != i.second) {
+ fprintf(stderr, "Reference count error on handler \"%s\" stored %u real %u.\n",
+ i.first->name().c_str(), i.first->get_refcount(), i.second);
+ bad = true;
+ }
}
+ if(bad)
+ abort();
+}
- virtual void copy(handler_entry *entry) override;
-
- // getters
- virtual const char *name() const override;
- virtual const char *subunit_name(int entry) const override;
-
- // configure delegate callbacks
- void set_delegate(write8_delegate delegate, u64 umask = 0, int cswidth = 8);
- void set_delegate(write16_delegate delegate, u64 umask = 0, int cswidth = 16);
- void set_delegate(write32_delegate delegate, u64 umask = 0, int cswidth = 32);
- void set_delegate(write64_delegate delegate, u64 umask = 0, int cswidth = 64);
-
- // configure I/O port access
- void set_ioport(ioport_port &ioport);
-
- // write via the underlying delegates
- void write8(address_space &space, offs_t offset, u8 data, u8 mask) const { m_write.w8(space, offset, data, mask); }
- void write16(address_space &space, offs_t offset, u16 data, u16 mask) const { m_write.w16(space, offset, data, mask); }
- void write32(address_space &space, offs_t offset, u32 data, u32 mask) const { m_write.w32(space, offset, data, mask); }
- void write64(address_space &space, offs_t offset, u64 data, u64 mask) const { m_write.w64(space, offset, data, mask); }
-private:
- // stubs for converting between address sizes
- void write_stub_16(address_space &space, offs_t offset, u16 data, u16 mask);
- void write_stub_32(address_space &space, offs_t offset, u32 data, u32 mask);
- void write_stub_64(address_space &space, offs_t offset, u64 data, u64 mask);
-
- // stubs for writing I/O ports
- template<typename UintType>
- void write_stub_ioport(address_space &space, offs_t offset, UintType data, UintType mask) { m_ioport->write(data, mask); }
-
- // internal helper
- virtual void remove_subunit(int entry) override;
-
- // internal state
- access_handler m_write;
- access_handler m_subwrite[8];
- ioport_port * m_ioport;
-};
+// default handler methods
-// ======================> handler_entry_setoffset
-// a setoffset-access-specific extension of handler_entry
-class handler_entry_setoffset : public handler_entry
+void handler_entry::enumerate_references(handler_entry::reflist &refs) const
{
-public:
- // construction/destruction
- handler_entry_setoffset()
- : handler_entry(0, ENDIANNESS_LITTLE, nullptr)
- {
- }
-
- const char *name() const override { return m_setoffset.name(); }
- const char *subunit_name(int entry) const override { return "no subunit"; }
-
- // Call through only if the setoffset handler has been late-bound before
- // (i.e. if it was declared in the address map)
- void setoffset(address_space &space, offs_t offset) const { if (m_setoffset.has_object()) m_setoffset(space, offset); }
-
- // configure delegate callbacks
- void set_delegate(setoffset_delegate delegate, u64 umask = 0, int cswidth = 0) { m_setoffset = delegate; }
-
-private:
- setoffset_delegate m_setoffset;
- // We do not have subunits for setoffset
- // Accordingly, we need not implement unused functions.
- void remove_subunit(int entry) override { }
-};
-
-// ======================> handler_entry_proxy
-
-// A proxy class that contains an handler_entry_read or _write and forwards the setter calls
+}
-template<typename HandlerEntry>
-class handler_entry_proxy
+template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler)
{
-public:
- handler_entry_proxy(std::list<HandlerEntry *> &&_handlers, u64 _umask, int _cswidth) : handlers(std::move(_handlers)), umask(_umask), cswidth(_cswidth) {}
- handler_entry_proxy(const handler_entry_proxy<HandlerEntry> &hep) : handlers(hep.handlers), umask(hep.umask), cswidth(hep.cswidth) {}
-
- // forward delegate callbacks configuration
- template<typename Delegate> void set_delegate(Delegate delegate) const {
- for (const auto & elem : handlers)
- (elem)->set_delegate(delegate, umask, cswidth);
- }
-
- // forward I/O port access configuration
- void set_ioport(ioport_port &ioport) const {
- for (const auto & elem : handlers)
- (elem)->set_ioport(ioport);
- }
-
-private:
- std::list<HandlerEntry *> handlers;
- u64 umask;
- int cswidth;
-};
-
-
-// ======================> address_table
+ fatalerror("populate called on non-dispatching class\n");
+}
-// address_table contains information about read/write accesses within an address space
-class address_table
+template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler)
{
- // address map lookup table definitions
- static const int LEVEL1_BITS = 18; // number of address bits in the level 1 table
- static const int LEVEL2_BITS = 32 - LEVEL1_BITS; // number of address bits in the level 2 table
- static const int SUBTABLE_COUNT = 64; // number of slots reserved for subtables
- static const int SUBTABLE_BASE = TOTAL_MEMORY_BANKS - SUBTABLE_COUNT; // first index of a subtable
- static const int ENTRY_COUNT = SUBTABLE_BASE; // number of legitimate (non-subtable) entries
- static const int SUBTABLE_ALLOC = 8; // number of subtables to allocate at a time
+ fatalerror("populate called on non-dispatching class\n");
+}
- inline int level2_bits() const { return m_large ? LEVEL2_BITS : 0; }
+template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings)
+{
+ fatalerror("populate_mismatched called on non-dispatching class\n");
+}
-public:
- // construction/destruction
- address_table(address_space &space, bool large);
- virtual ~address_table();
+template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings)
+{
+ fatalerror("populate_mismatched called on non-dispatching class\n");
+}
- // getters
- virtual handler_entry &handler(u32 index) const = 0;
- bool watchpoints_enabled() const { return (m_live_lookup == s_watchpoint_table); }
+template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ fatalerror("populate_passthrough called on non-dispatching class\n");
+}
- // address lookups
- u32 lookup_live(offs_t address) const { return m_large ? lookup_live_large(address) : lookup_live_small(address); }
- u32 lookup_live_small(offs_t address) const { return m_live_lookup[address]; }
+template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ fatalerror("populate_passthrough called on non-dispatching class\n");
+}
- u32 lookup_live_large(offs_t address) const
- {
- u32 entry = m_live_lookup[level1_index_large(address)];
- if (entry >= SUBTABLE_BASE)
- entry = m_live_lookup[level2_index_large(entry, address)];
- return entry;
- }
+template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift, Endian> *&handler) const
+{
+ fatalerror("lookup called on non-dispatching class\n");
+}
- u32 lookup_live_nowp(offs_t address) const { return m_large ? lookup_live_large_nowp(address) : lookup_live_small_nowp(address); }
- u32 lookup_live_small_nowp(offs_t address) const { return m_table[address]; }
+template<int Width, int AddrShift, int Endian> void *handler_entry_read<Width, AddrShift, Endian>::get_ptr(offs_t offset) const
+{
+ return nullptr;
+}
- u32 lookup_live_large_nowp(offs_t address) const
- {
- u32 entry = m_table[level1_index_large(address)];
- if (entry >= SUBTABLE_BASE)
- entry = m_table[level2_index_large(entry, address)];
- return entry;
- }
+template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers)
+{
+ fatalerror("detach called on non-dispatching class\n");
+}
- u32 lookup(offs_t address) const
- {
- u32 entry = m_live_lookup[level1_index(address)];
- if (entry >= SUBTABLE_BASE)
- entry = m_live_lookup[level2_index(entry, address)];
- return entry;
- }
- // enable watchpoints by swapping in the watchpoint table
- void enable_watchpoints(bool enable = true) { m_live_lookup = enable ? s_watchpoint_table : &m_table[0]; }
-
- // table mapping helpers
- void map_range(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u16 staticentry);
- std::list<u32> setup_range(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u64 umask);
- u16 derive_range(offs_t address, offs_t &addrstart, offs_t &addrend) const;
-
- // misc helpers
- void mask_all_handlers(offs_t mask);
- const char *handler_name(u16 entry) const;
-
-protected:
- // determine table indexes based on the address
- u32 level1_index_large(offs_t address) const { return address >> LEVEL2_BITS; }
- u32 level2_index_large(u16 l1entry, offs_t address) const { return (1 << LEVEL1_BITS) + ((l1entry - SUBTABLE_BASE) << LEVEL2_BITS) + (address & ((1 << LEVEL2_BITS) - 1)); }
- u32 level1_index(offs_t address) const { return m_large ? level1_index_large(address) : address; }
- u32 level2_index(u16 l1entry, offs_t address) const { return m_large ? level2_index_large(l1entry, address) : 0; }
-
- // table population/depopulation
- void populate_range_mirrored(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 handler);
- void populate_range(offs_t addrstart, offs_t addrend, u16 handler);
-
- // subtable management
- u16 subtable_alloc();
- void subtable_realloc(u16 subentry);
- int subtable_merge();
- void subtable_release(u16 subentry);
- u16 *subtable_open(offs_t l1index);
- void subtable_close(offs_t l1index);
- u16 *subtable_ptr(u16 entry) { return &m_table[level2_index(entry, 0)]; }
-
- // internal state
- std::vector<u16> m_table; // pointer to base of table
- u16 * m_live_lookup; // current lookup
- address_space & m_space; // pointer back to the space
- bool m_large; // large memory model?
-
- // subtable_data is an internal class with information about each subtable
- class subtable_data
- {
- public:
- subtable_data()
- : m_checksum_valid(false),
- m_checksum(0),
- m_usecount(0) { }
-
- bool m_checksum_valid; // is the checksum valid
- u32 m_checksum; // checksum over all the bytes
- u32 m_usecount; // number of times this has been used
- };
- std::vector<subtable_data> m_subtable; // info about each subtable
- u16 m_subtable_alloc; // number of subtables allocated
-
- // static global read-only watchpoint table
- static u16 s_watchpoint_table[1 << LEVEL1_BITS];
+template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler)
+{
+ fatalerror("populate called on non-dispatching class\n");
+}
-private:
- int handler_refcount[SUBTABLE_BASE-STATIC_COUNT];
- u16 handler_next_free[SUBTABLE_BASE-STATIC_COUNT];
- u16 handler_free;
- u16 get_free_handler();
- void verify_reference_counts();
- bool range_simply_masks(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u64 umask);
- std::list<u32> setup_range_solid(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror);
- std::list<u32> setup_range_masked(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u64 umask);
-
- void handler_ref(u16 entry, int count)
- {
- assert(entry < SUBTABLE_BASE);
- if (entry >= STATIC_COUNT)
- handler_refcount[entry - STATIC_COUNT] += count;
- }
+template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler)
+{
+ fatalerror("populate called on non-dispatching class\n");
+}
- void handler_unref(u16 entry)
- {
- assert(entry < SUBTABLE_BASE);
- if (entry >= STATIC_COUNT)
- if (! --handler_refcount[entry - STATIC_COUNT])
- {
- handler(entry).deconfigure();
- handler_next_free[entry - STATIC_COUNT] = handler_free;
- handler_free = entry;
- }
- }
-};
+template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings)
+{
+ fatalerror("populate_mismatched called on non-dispatching class\n");
+}
+template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings)
+{
+ fatalerror("populate_mismatched called on non-dispatching class\n");
+}
-// ======================> address_table_read
+template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ fatalerror("populate_passthrough called on non-dispatching class\n");
+}
-// read access-specific version of an address table
-class address_table_read : public address_table
+template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
{
-public:
- // construction/destruction
- address_table_read(address_space &space, bool large);
- virtual ~address_table_read();
-
- // getters
- virtual handler_entry &handler(u32 index) const override;
- handler_entry_read &handler_read(u32 index) const { assert(index < ARRAY_LENGTH(m_handlers)); return *m_handlers[index]; }
-
- // range getter
- handler_entry_proxy<handler_entry_read> handler_map_range(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u64 umask = 0, int cswidth = 0) {
- std::list<u32> entries = setup_range(addrstart, addrend, addrmask, addrmirror, umask);
- std::list<handler_entry_read *> handlers;
- for (std::list<u32>::const_iterator i = entries.begin(); i != entries.end(); ++i)
- handlers.push_back(&handler_read(*i));
- return handler_entry_proxy<handler_entry_read>(std::move(handlers), umask, cswidth);
- }
+ fatalerror("populate_passthrough called on non-dispatching class\n");
+}
-private:
- // internal unmapped handler
- template<typename UintType>
- UintType unmap_r(address_space &space, offs_t offset, UintType mask)
- {
- if (m_space.log_unmap() && !m_space.m_manager.machine().side_effects_disabled())
- {
- m_space.device().logerror(
- m_space.is_octal()
- ? "%s: unmapped %s memory read from %0*o & %0*o\n"
- : "%s: unmapped %s memory read from %0*X & %0*X\n",
- m_space.m_manager.machine().describe_context(), m_space.name(),
- m_space.addrchars(), m_space.byte_to_address(offset * sizeof(UintType)),
- 2 * sizeof(UintType), mask);
- }
- return m_space.unmap();
- }
+template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift, Endian> *&handler) const
+{
+ fatalerror("lookup called on non-dispatching class\n");
+}
- // internal no-op handler
- template<typename UintType>
- UintType nop_r(address_space &space, offs_t offset, UintType mask)
- {
- return m_space.unmap();
- }
+template<int Width, int AddrShift, int Endian> void *handler_entry_write<Width, AddrShift, Endian>::get_ptr(offs_t offset) const
+{
+ return nullptr;
+}
- // internal watchpoint handler
- template<typename UintType, int AddrShift>
- UintType watchpoint_r(address_space &space, offs_t offset, UintType mask)
- {
- int base_shift = sizeof(UintType) == 1 ? 0 : sizeof(UintType) == 2 ? 1 : sizeof(UintType) == 4 ? 2 : 3;
- offset = offset << (AddrShift + base_shift);
- m_space.device().debug()->memory_read_hook(m_space, offset, mask);
-
- u16 *oldtable = m_live_lookup;
- m_live_lookup = &m_table[0];
- UintType result;
- if (sizeof(UintType) == 1) result = m_space.read_byte(offset);
- if (sizeof(UintType) == 2) result = m_space.read_word(offset, mask);
- if (sizeof(UintType) == 4) result = m_space.read_dword(offset, mask);
- if (sizeof(UintType) == 8) result = m_space.read_qword(offset, mask);
- m_live_lookup = oldtable;
- return result;
- }
+template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers)
+{
+ fatalerror("detach called on non-dispatching class\n");
+}
- // internal state
- std::unique_ptr<handler_entry_read> m_handlers[TOTAL_MEMORY_BANKS]; // array of user-installed handlers
-};
-// ======================> address_table_write
+/*-------------------------------------------------
+ core_i64_hex_format - i64 format printf helper
+-------------------------------------------------*/
-// write access-specific version of an address table
-class address_table_write : public address_table
+static char *core_i64_hex_format(u64 value, u8 mindigits)
{
-public:
- // construction/destruction
- address_table_write(address_space &space, bool large);
- virtual ~address_table_write();
-
- // getters
- virtual handler_entry &handler(u32 index) const override;
- handler_entry_write &handler_write(u32 index) const { assert(index < ARRAY_LENGTH(m_handlers)); return *m_handlers[index]; }
-
- // range getter
- handler_entry_proxy<handler_entry_write> handler_map_range(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u64 umask = 0, int cswidth = 0) {
- std::list<u32> entries = setup_range(addrstart, addrend, addrmask, addrmirror, umask);
- std::list<handler_entry_write *> handlers;
- for (std::list<u32>::const_iterator i = entries.begin(); i != entries.end(); ++i)
- handlers.push_back(&handler_write(*i));
- return handler_entry_proxy<handler_entry_write>(std::move(handlers), umask, cswidth);
- }
+ 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;
-private:
- // internal handlers
- template<typename UintType>
- void unmap_w(address_space &space, offs_t offset, UintType data, UintType mask)
+ for (curdigit = 15; curdigit >= 0; curdigit--)
{
- if (m_space.log_unmap() && !m_space.m_manager.machine().side_effects_disabled())
+ int nibble = (value >> (curdigit * 4)) & 0xf;
+ if (nibble != 0 || curdigit < mindigits)
{
- m_space.device().logerror(
- m_space.is_octal()
- ? "%s: unmapped %s memory write to %0*o = %0*o & %0*o\n"
- : "%s: unmapped %s memory write to %0*X = %0*X & %0*X\n",
- m_space.m_manager.machine().describe_context(), m_space.name(),
- m_space.addrchars(), m_space.byte_to_address(offset * sizeof(UintType)),
- 2 * sizeof(UintType), data,
- 2 * sizeof(UintType), mask);
+ mindigits = curdigit;
+ *bufptr++ = "0123456789ABCDEF"[nibble];
}
}
+ if (bufptr == bufbase)
+ *bufptr++ = '0';
+ *bufptr = 0;
- template<typename UintType>
- void nop_w(address_space &space, offs_t offset, UintType data, UintType mask)
- {
- }
-
- template<typename UintType, int AddrShift>
- void watchpoint_w(address_space &space, offs_t offset, UintType data, UintType mask)
- {
- int base_shift = sizeof(UintType) == 1 ? 0 : sizeof(UintType) == 2 ? 1 : sizeof(UintType) == 4 ? 2 : 3;
- offset = offset << (AddrShift + base_shift);
- m_space.device().debug()->memory_write_hook(m_space, offset, data, mask);
-
- u16 *oldtable = m_live_lookup;
- m_live_lookup = &m_table[0];
- if (sizeof(UintType) == 1) m_space.write_byte(offset, data);
- if (sizeof(UintType) == 2) m_space.write_word(offset, data, mask);
- if (sizeof(UintType) == 4) m_space.write_dword(offset, data, mask);
- if (sizeof(UintType) == 8) m_space.write_qword(offset, data, mask);
- m_live_lookup = oldtable;
- }
-
- // internal state
- std::unique_ptr<handler_entry_write> m_handlers[TOTAL_MEMORY_BANKS]; // array of user-installed handlers
-};
-
-// ======================> address_table_setoffset
-// setoffset access-specific version of an address table
-class address_table_setoffset : public address_table
-{
-public:
- // construction/destruction
- address_table_setoffset(address_space &space, bool large)
- : address_table(space, large)
- {
- // allocate handlers for each entry, prepopulating the bankptrs for banks
- for (auto & elem : m_handlers)
- elem = std::make_unique<handler_entry_setoffset>();
+ return bufbase;
+}
- // Watchpoints and unmap states do not make sense for setoffset
- m_handlers[STATIC_NOP]->set_delegate(setoffset_delegate(FUNC(address_table_setoffset::nop_so), this));
- m_handlers[STATIC_NOP]->configure(0, space.addrmask(), ~0, ~0);
- }
- ~address_table_setoffset()
- {
- }
- handler_entry &handler(u32 index) const override { assert(index < ARRAY_LENGTH(m_handlers)); return *m_handlers[index]; }
- handler_entry_setoffset &handler_setoffset(u32 index) const { assert(index < ARRAY_LENGTH(m_handlers)); return *m_handlers[index]; }
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
- // range getter
- handler_entry_proxy<handler_entry_setoffset> handler_map_range(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u64 umask = 0, int cswidth = 0) {
- std::list<u32> entries = setup_range(addrstart, addrend, addrmask, addrmirror, umask);
- std::list<handler_entry_setoffset *> handlers;
- for (std::list<u32>::const_iterator i = entries.begin(); i != entries.end(); ++i)
- handlers.push_back(&handler_setoffset(*i));
- return handler_entry_proxy<handler_entry_setoffset>(std::move(handlers), umask, cswidth);
- }
+// banking constants
+const int BANK_ENTRY_UNSPECIFIED = -1;
-private:
- // internal handlers
- // Setoffset does not allow for watchpoints, since we assume that a
- // corresponding read/write operation will follow, and the watchpoint will
- // apply for that operation
- // For the same reason it does not make sense to put a warning into the log
- // for unmapped locations, as this will be done by the read/write operation
- void nop_so(address_space &space, offs_t offset)
- {
- }
+// other address map constants
+const int MEMORY_BLOCK_CHUNK = 65536; // minimum chunk size of allocated memory blocks
- // internal state
- std::unique_ptr<handler_entry_setoffset> m_handlers[TOTAL_MEMORY_BANKS]; // array of user-installed handlers
-};
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
// ======================> address_space_specific
// this is a derived class of address_space with specific width, endianness, and table size
-template<typename NativeType, endianness_t Endian, int AddrShift, bool Large>
+template<int Width, int AddrShift, endianness_t Endian>
class address_space_specific : public address_space
{
- typedef address_space_specific<NativeType, Endian, AddrShift, Large> this_type;
+ using uX = typename handler_entry_size<Width>::uX;
+ using NativeType = uX;
+ using this_type = address_space_specific<Width, AddrShift, Endian>;
// constants describing the native size
- static constexpr u32 NATIVE_BYTES = sizeof(NativeType);
+ static constexpr u32 NATIVE_BYTES = 1 << Width;
static constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift);
static constexpr u32 NATIVE_MASK = NATIVE_STEP - 1;
static constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES;
- static constexpr u32 Width = NATIVE_BYTES == 1 ? 0 : NATIVE_BYTES == 2 ? 1 : NATIVE_BYTES == 4 ? 2 : 3;
-
- // helpers to simplify core code
- u32 read_lookup(offs_t address) const { return Large ? m_read.lookup_live_large(address) : m_read.lookup_live_small(address); }
- u32 write_lookup(offs_t address) const { return Large ? m_write.lookup_live_large(address) : m_write.lookup_live_small(address); }
- u32 setoffset_lookup(offs_t address) const { return Large ? m_setoffset.lookup_live_large(address) : m_setoffset.lookup_live_small(address); }
static constexpr offs_t offset_to_byte(offs_t offset) { return AddrShift < 0 ? offset << iabs(AddrShift) : offset >> iabs(AddrShift); }
public:
+ std::string get_handler_string(read_or_write readorwrite, offs_t byteaddress) const override;
+ void dump_maps(std::vector<memory_entry> &read_map, std::vector<memory_entry> &write_map) const override;
+
+ void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) override;
+ void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) override;
+ void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag) override;
+ void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) override;
+ void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag) override;
+ void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0) override;
+ void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) override;
+ void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) override;
+
+ using address_space::install_read_tap;
+ using address_space::install_write_tap;
+ using address_space::install_readwrite_tap;
+
+ virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) override;
+ virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) override;
+ virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapr, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapw, memory_passthrough_handler *mph) override;
+
// construction/destruction
- address_space_specific(memory_manager &manager, device_memory_interface &memory, int spacenum)
- : address_space(manager, memory, spacenum, Large),
- m_read(*this, Large),
- m_write(*this, Large),
- m_setoffset(*this, Large)
- {
-#if (TEST_HANDLER)
- // test code to verify the read/write handlers are touching the correct bits
- // and returning the correct results
-
- // install some dummy RAM for the first 16 bytes with well-known values
- u8 buffer[16];
- for (int index = 0; index < 16; index++)
- buffer[index ^ ((Endian == ENDIANNESS_NATIVE) ? 0 : (data_width()/8 - 1))] = index * 0x11;
- install_ram_generic(0x00, 0x0f, 0x0f, 0, read_or_write::READWRITE, buffer);
- printf("\n\naddress_space(%d, %s, %s)\n", NATIVE_BITS, (Endian == ENDIANNESS_LITTLE) ? "little" : "big", Large ? "large" : "small");
-
- // walk through the first 8 addresses
- for (int address = 0; address < 8; address++)
- {
- // determine expected values
- u64 expected64 = (u64((address + ((Endian == ENDIANNESS_LITTLE) ? 7 : 0)) * 0x11) << 56) |
- (u64((address + ((Endian == ENDIANNESS_LITTLE) ? 6 : 1)) * 0x11) << 48) |
- (u64((address + ((Endian == ENDIANNESS_LITTLE) ? 5 : 2)) * 0x11) << 40) |
- (u64((address + ((Endian == ENDIANNESS_LITTLE) ? 4 : 3)) * 0x11) << 32) |
- (u64((address + ((Endian == ENDIANNESS_LITTLE) ? 3 : 4)) * 0x11) << 24) |
- (u64((address + ((Endian == ENDIANNESS_LITTLE) ? 2 : 5)) * 0x11) << 16) |
- (u64((address + ((Endian == ENDIANNESS_LITTLE) ? 1 : 6)) * 0x11) << 8) |
- (u64((address + ((Endian == ENDIANNESS_LITTLE) ? 0 : 7)) * 0x11) << 0);
- u32 expected32 = (Endian == ENDIANNESS_LITTLE) ? expected64 : (expected64 >> 32);
- u16 expected16 = (Endian == ENDIANNESS_LITTLE) ? expected32 : (expected32 >> 16);
- u8 expected8 = (Endian == ENDIANNESS_LITTLE) ? expected16 : (expected16 >> 8);
-
- u64 result64;
- u32 result32;
- u16 result16;
- u8 result8;
-
- // validate byte accesses
- printf("\nAddress %d\n", address);
- printf(" read_byte = "); printf("%02X\n", result8 = read_byte(address)); assert(result8 == expected8);
-
- // validate word accesses (if aligned)
- if (WORD_ALIGNED(address)) { printf(" read_word = "); printf("%04X\n", result16 = read_word(address)); assert(result16 == expected16); }
- if (WORD_ALIGNED(address)) { printf(" read_word (0xff00) = "); printf("%04X\n", result16 = read_word(address, 0xff00)); assert((result16 & 0xff00) == (expected16 & 0xff00)); }
- if (WORD_ALIGNED(address)) { printf(" (0x00ff) = "); printf("%04X\n", result16 = read_word(address, 0x00ff)); assert((result16 & 0x00ff) == (expected16 & 0x00ff)); }
-
- // validate unaligned word accesses
- printf(" read_word_unaligned = "); printf("%04X\n", result16 = read_word_unaligned(address)); assert(result16 == expected16);
- printf(" read_word_unaligned (0xff00) = "); printf("%04X\n", result16 = read_word_unaligned(address, 0xff00)); assert((result16 & 0xff00) == (expected16 & 0xff00));
- printf(" (0x00ff) = "); printf("%04X\n", result16 = read_word_unaligned(address, 0x00ff)); assert((result16 & 0x00ff) == (expected16 & 0x00ff));
-
- // validate dword acceses (if aligned)
- if (DWORD_ALIGNED(address)) { printf(" read_dword = "); printf("%08X\n", result32 = read_dword(address)); assert(result32 == expected32); }
- if (DWORD_ALIGNED(address)) { printf(" read_dword (0xff000000) = "); printf("%08X\n", result32 = read_dword(address, 0xff000000)); assert((result32 & 0xff000000) == (expected32 & 0xff000000)); }
- if (DWORD_ALIGNED(address)) { printf(" (0x00ff0000) = "); printf("%08X\n", result32 = read_dword(address, 0x00ff0000)); assert((result32 & 0x00ff0000) == (expected32 & 0x00ff0000)); }
- if (DWORD_ALIGNED(address)) { printf(" (0x0000ff00) = "); printf("%08X\n", result32 = read_dword(address, 0x0000ff00)); assert((result32 & 0x0000ff00) == (expected32 & 0x0000ff00)); }
- if (DWORD_ALIGNED(address)) { printf(" (0x000000ff) = "); printf("%08X\n", result32 = read_dword(address, 0x000000ff)); assert((result32 & 0x000000ff) == (expected32 & 0x000000ff)); }
- if (DWORD_ALIGNED(address)) { printf(" (0xffff0000) = "); printf("%08X\n", result32 = read_dword(address, 0xffff0000)); assert((result32 & 0xffff0000) == (expected32 & 0xffff0000)); }
- if (DWORD_ALIGNED(address)) { printf(" (0x0000ffff) = "); printf("%08X\n", result32 = read_dword(address, 0x0000ffff)); assert((result32 & 0x0000ffff) == (expected32 & 0x0000ffff)); }
- if (DWORD_ALIGNED(address)) { printf(" (0xffffff00) = "); printf("%08X\n", result32 = read_dword(address, 0xffffff00)); assert((result32 & 0xffffff00) == (expected32 & 0xffffff00)); }
- if (DWORD_ALIGNED(address)) { printf(" (0x00ffffff) = "); printf("%08X\n", result32 = read_dword(address, 0x00ffffff)); assert((result32 & 0x00ffffff) == (expected32 & 0x00ffffff)); }
-
- // validate unaligned dword accesses
- printf(" read_dword_unaligned = "); printf("%08X\n", result32 = read_dword_unaligned(address)); assert(result32 == expected32);
- printf(" read_dword_unaligned (0xff000000) = "); printf("%08X\n", result32 = read_dword_unaligned(address, 0xff000000)); assert((result32 & 0xff000000) == (expected32 & 0xff000000));
- printf(" (0x00ff0000) = "); printf("%08X\n", result32 = read_dword_unaligned(address, 0x00ff0000)); assert((result32 & 0x00ff0000) == (expected32 & 0x00ff0000));
- printf(" (0x0000ff00) = "); printf("%08X\n", result32 = read_dword_unaligned(address, 0x0000ff00)); assert((result32 & 0x0000ff00) == (expected32 & 0x0000ff00));
- printf(" (0x000000ff) = "); printf("%08X\n", result32 = read_dword_unaligned(address, 0x000000ff)); assert((result32 & 0x000000ff) == (expected32 & 0x000000ff));
- printf(" (0xffff0000) = "); printf("%08X\n", result32 = read_dword_unaligned(address, 0xffff0000)); assert((result32 & 0xffff0000) == (expected32 & 0xffff0000));
- printf(" (0x0000ffff) = "); printf("%08X\n", result32 = read_dword_unaligned(address, 0x0000ffff)); assert((result32 & 0x0000ffff) == (expected32 & 0x0000ffff));
- printf(" (0xffffff00) = "); printf("%08X\n", result32 = read_dword_unaligned(address, 0xffffff00)); assert((result32 & 0xffffff00) == (expected32 & 0xffffff00));
- printf(" (0x00ffffff) = "); printf("%08X\n", result32 = read_dword_unaligned(address, 0x00ffffff)); assert((result32 & 0x00ffffff) == (expected32 & 0x00ffffff));
-
- // validate qword acceses (if aligned)
- if (QWORD_ALIGNED(address)) { printf(" read_qword = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address), 16)); assert(result64 == expected64); }
- if (QWORD_ALIGNED(address)) { printf(" read_qword (0xff00000000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0xff00000000000000U), 16)); assert((result64 & 0xff00000000000000U) == (expected64 & 0xff00000000000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x00ff000000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x00ff000000000000U), 16)); assert((result64 & 0x00ff000000000000U) == (expected64 & 0x00ff000000000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x0000ff0000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x0000ff0000000000U), 16)); assert((result64 & 0x0000ff0000000000U) == (expected64 & 0x0000ff0000000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x000000ff00000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x000000ff00000000U), 16)); assert((result64 & 0x000000ff00000000U) == (expected64 & 0x000000ff00000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x00000000ff000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x00000000ff000000U), 16)); assert((result64 & 0x00000000ff000000U) == (expected64 & 0x00000000ff000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x0000000000ff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x0000000000ff0000U), 16)); assert((result64 & 0x0000000000ff0000U) == (expected64 & 0x0000000000ff0000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x000000000000ff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x000000000000ff00U), 16)); assert((result64 & 0x000000000000ff00U) == (expected64 & 0x000000000000ff00U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x00000000000000ff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x00000000000000ffU), 16)); assert((result64 & 0x00000000000000ffU) == (expected64 & 0x00000000000000ffU)); }
- if (QWORD_ALIGNED(address)) { printf(" (0xffff000000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0xffff000000000000U), 16)); assert((result64 & 0xffff000000000000U) == (expected64 & 0xffff000000000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x0000ffff00000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x0000ffff00000000U), 16)); assert((result64 & 0x0000ffff00000000U) == (expected64 & 0x0000ffff00000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x00000000ffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x00000000ffff0000U), 16)); assert((result64 & 0x00000000ffff0000U) == (expected64 & 0x00000000ffff0000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x000000000000ffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x000000000000ffffU), 16)); assert((result64 & 0x000000000000ffffU) == (expected64 & 0x000000000000ffffU)); }
- if (QWORD_ALIGNED(address)) { printf(" (0xffffff0000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0xffffff0000000000U), 16)); assert((result64 & 0xffffff0000000000U) == (expected64 & 0xffffff0000000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x0000ffffff000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x0000ffffff000000U), 16)); assert((result64 & 0x0000ffffff000000U) == (expected64 & 0x0000ffffff000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x000000ffffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x000000ffffff0000U), 16)); assert((result64 & 0x000000ffffff0000U) == (expected64 & 0x000000ffffff0000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x0000000000ffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x0000000000ffffffU), 16)); assert((result64 & 0x0000000000ffffffU) == (expected64 & 0x0000000000ffffffU)); }
- if (QWORD_ALIGNED(address)) { printf(" (0xffffffff00000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0xffffffff00000000U), 16)); assert((result64 & 0xffffffff00000000U) == (expected64 & 0xffffffff00000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x00ffffffff000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x00ffffffff000000U), 16)); assert((result64 & 0x00ffffffff000000U) == (expected64 & 0x00ffffffff000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x0000ffffffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x0000ffffffff0000U), 16)); assert((result64 & 0x0000ffffffff0000U) == (expected64 & 0x0000ffffffff0000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x000000ffffffff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x000000ffffffff00U), 16)); assert((result64 & 0x000000ffffffff00U) == (expected64 & 0x000000ffffffff00U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x00000000ffffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x00000000ffffffffU), 16)); assert((result64 & 0x00000000ffffffffU) == (expected64 & 0x00000000ffffffffU)); }
- if (QWORD_ALIGNED(address)) { printf(" (0xffffffffff000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0xffffffffff000000U), 16)); assert((result64 & 0xffffffffff000000U) == (expected64 & 0xffffffffff000000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x00ffffffffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x00ffffffffff0000U), 16)); assert((result64 & 0x00ffffffffff0000U) == (expected64 & 0x00ffffffffff0000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x0000ffffffffff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x0000ffffffffff00U), 16)); assert((result64 & 0x0000ffffffffff00U) == (expected64 & 0x0000ffffffffff00U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x000000ffffffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x000000ffffffffffU), 16)); assert((result64 & 0x000000ffffffffffU) == (expected64 & 0x000000ffffffffffU)); }
- if (QWORD_ALIGNED(address)) { printf(" (0xffffffffffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0xffffffffffff0000U), 16)); assert((result64 & 0xffffffffffff0000U) == (expected64 & 0xffffffffffff0000U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x00ffffffffffff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x00ffffffffffff00U), 16)); assert((result64 & 0x00ffffffffffff00U) == (expected64 & 0x00ffffffffffff00U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x0000ffffffffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x0000ffffffffffffU), 16)); assert((result64 & 0x0000ffffffffffffU) == (expected64 & 0x0000ffffffffffffU)); }
- if (QWORD_ALIGNED(address)) { printf(" (0xffffffffffffff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0xffffffffffffff00U), 16)); assert((result64 & 0xffffffffffffff00U) == (expected64 & 0xffffffffffffff00U)); }
- if (QWORD_ALIGNED(address)) { printf(" (0x00ffffffffffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword(address, 0x00ffffffffffffffU), 16)); assert((result64 & 0x00ffffffffffffffU) == (expected64 & 0x00ffffffffffffffU)); }
-
- // validate unaligned qword accesses
- printf(" read_qword_unaligned = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address), 16)); assert(result64 == expected64);
- printf(" read_qword_unaligned (0xff00000000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0xff00000000000000U), 16)); assert((result64 & 0xff00000000000000U) == (expected64 & 0xff00000000000000U));
- printf(" (0x00ff000000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x00ff000000000000U), 16)); assert((result64 & 0x00ff000000000000U) == (expected64 & 0x00ff000000000000U));
- printf(" (0x0000ff0000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x0000ff0000000000U), 16)); assert((result64 & 0x0000ff0000000000U) == (expected64 & 0x0000ff0000000000U));
- printf(" (0x000000ff00000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x000000ff00000000U), 16)); assert((result64 & 0x000000ff00000000U) == (expected64 & 0x000000ff00000000U));
- printf(" (0x00000000ff000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x00000000ff000000U), 16)); assert((result64 & 0x00000000ff000000U) == (expected64 & 0x00000000ff000000U));
- printf(" (0x0000000000ff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x0000000000ff0000U), 16)); assert((result64 & 0x0000000000ff0000U) == (expected64 & 0x0000000000ff0000U));
- printf(" (0x000000000000ff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x000000000000ff00U), 16)); assert((result64 & 0x000000000000ff00U) == (expected64 & 0x000000000000ff00U));
- printf(" (0x00000000000000ff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x00000000000000ffU), 16)); assert((result64 & 0x00000000000000ffU) == (expected64 & 0x00000000000000ffU));
- printf(" (0xffff000000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0xffff000000000000U), 16)); assert((result64 & 0xffff000000000000U) == (expected64 & 0xffff000000000000U));
- printf(" (0x0000ffff00000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x0000ffff00000000U), 16)); assert((result64 & 0x0000ffff00000000U) == (expected64 & 0x0000ffff00000000U));
- printf(" (0x00000000ffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x00000000ffff0000U), 16)); assert((result64 & 0x00000000ffff0000U) == (expected64 & 0x00000000ffff0000U));
- printf(" (0x000000000000ffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x000000000000ffffU), 16)); assert((result64 & 0x000000000000ffffU) == (expected64 & 0x000000000000ffffU));
- printf(" (0xffffff0000000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0xffffff0000000000U), 16)); assert((result64 & 0xffffff0000000000U) == (expected64 & 0xffffff0000000000U));
- printf(" (0x0000ffffff000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x0000ffffff000000U), 16)); assert((result64 & 0x0000ffffff000000U) == (expected64 & 0x0000ffffff000000U));
- printf(" (0x000000ffffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x000000ffffff0000U), 16)); assert((result64 & 0x000000ffffff0000U) == (expected64 & 0x000000ffffff0000U));
- printf(" (0x0000000000ffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x0000000000ffffffU), 16)); assert((result64 & 0x0000000000ffffffU) == (expected64 & 0x0000000000ffffffU));
- printf(" (0xffffffff00000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0xffffffff00000000U), 16)); assert((result64 & 0xffffffff00000000U) == (expected64 & 0xffffffff00000000U));
- printf(" (0x00ffffffff000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x00ffffffff000000U), 16)); assert((result64 & 0x00ffffffff000000U) == (expected64 & 0x00ffffffff000000U));
- printf(" (0x0000ffffffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x0000ffffffff0000U), 16)); assert((result64 & 0x0000ffffffff0000U) == (expected64 & 0x0000ffffffff0000U));
- printf(" (0x000000ffffffff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x000000ffffffff00U), 16)); assert((result64 & 0x000000ffffffff00U) == (expected64 & 0x000000ffffffff00U));
- printf(" (0x00000000ffffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x00000000ffffffffU), 16)); assert((result64 & 0x00000000ffffffffU) == (expected64 & 0x00000000ffffffffU));
- printf(" (0xffffffffff000000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0xffffffffff000000U), 16)); assert((result64 & 0xffffffffff000000U) == (expected64 & 0xffffffffff000000U));
- printf(" (0x00ffffffffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x00ffffffffff0000U), 16)); assert((result64 & 0x00ffffffffff0000U) == (expected64 & 0x00ffffffffff0000U));
- printf(" (0x0000ffffffffff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x0000ffffffffff00U), 16)); assert((result64 & 0x0000ffffffffff00U) == (expected64 & 0x0000ffffffffff00U));
- printf(" (0x000000ffffffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x000000ffffffffffU), 16)); assert((result64 & 0x000000ffffffffffU) == (expected64 & 0x000000ffffffffffU));
- printf(" (0xffffffffffff0000) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0xffffffffffff0000U), 16)); assert((result64 & 0xffffffffffff0000U) == (expected64 & 0xffffffffffff0000U));
- printf(" (0x00ffffffffffff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x00ffffffffffff00U), 16)); assert((result64 & 0x00ffffffffffff00U) == (expected64 & 0x00ffffffffffff00U));
- printf(" (0x0000ffffffffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x0000ffffffffffffU), 16)); assert((result64 & 0x0000ffffffffffffU) == (expected64 & 0x0000ffffffffffffU));
- printf(" (0xffffffffffffff00) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0xffffffffffffff00U), 16)); assert((result64 & 0xffffffffffffff00U) == (expected64 & 0xffffffffffffff00U));
- printf(" (0x00ffffffffffffff) = "); printf("%s\n", core_i64_hex_format(result64 = read_qword_unaligned(address, 0x00ffffffffffffffU), 16)); assert((result64 & 0x00ffffffffffffffU) == (expected64 & 0x00ffffffffffffffU));
+ address_space_specific(memory_manager &manager, device_memory_interface &memory, int spacenum, int address_width)
+ : address_space(manager, memory, spacenum)
+ {
+ m_unmap_r = new handler_entry_read_unmapped <Width, AddrShift, Endian>(this);
+ m_unmap_w = new handler_entry_write_unmapped<Width, AddrShift, Endian>(this);
+ m_nop_r = new handler_entry_read_nop <Width, AddrShift, Endian>(this);
+ m_nop_w = new handler_entry_write_nop<Width, AddrShift, Endian>(this);
+
+ handler_entry::range r{ 0, 0xffffffff >> (32 - address_width) };
+
+ switch (address_width) {
+ case 1: m_root_read = new handler_entry_read_dispatch< std::max(1, Width), Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< std::max(1, Width), Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 2: m_root_read = new handler_entry_read_dispatch< std::max(2, Width), Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< std::max(2, Width), Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 3: m_root_read = new handler_entry_read_dispatch< std::max(3, Width), Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< std::max(3, Width), Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 4: m_root_read = new handler_entry_read_dispatch< 4, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 4, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 5: m_root_read = new handler_entry_read_dispatch< 5, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 5, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 6: m_root_read = new handler_entry_read_dispatch< 6, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 6, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 7: m_root_read = new handler_entry_read_dispatch< 7, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 7, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 8: m_root_read = new handler_entry_read_dispatch< 8, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 8, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 9: m_root_read = new handler_entry_read_dispatch< 9, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 9, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 10: m_root_read = new handler_entry_read_dispatch<10, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<10, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 11: m_root_read = new handler_entry_read_dispatch<11, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<11, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 12: m_root_read = new handler_entry_read_dispatch<12, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<12, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 13: m_root_read = new handler_entry_read_dispatch<13, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<13, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 14: m_root_read = new handler_entry_read_dispatch<14, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<14, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 15: m_root_read = new handler_entry_read_dispatch<15, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<15, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 16: m_root_read = new handler_entry_read_dispatch<16, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<16, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 17: m_root_read = new handler_entry_read_dispatch<17, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<17, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 18: m_root_read = new handler_entry_read_dispatch<18, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<18, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 19: m_root_read = new handler_entry_read_dispatch<19, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<19, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 20: m_root_read = new handler_entry_read_dispatch<20, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<20, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 21: m_root_read = new handler_entry_read_dispatch<21, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<21, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 22: m_root_read = new handler_entry_read_dispatch<22, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<22, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 23: m_root_read = new handler_entry_read_dispatch<23, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<23, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 24: m_root_read = new handler_entry_read_dispatch<24, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<24, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 25: m_root_read = new handler_entry_read_dispatch<25, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<25, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 26: m_root_read = new handler_entry_read_dispatch<26, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<26, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 27: m_root_read = new handler_entry_read_dispatch<27, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<27, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 28: m_root_read = new handler_entry_read_dispatch<28, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<28, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 29: m_root_read = new handler_entry_read_dispatch<29, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<29, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 30: m_root_read = new handler_entry_read_dispatch<30, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<30, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 31: m_root_read = new handler_entry_read_dispatch<31, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<31, Width, AddrShift, Endian>(this, r, nullptr); break;
+ case 32: m_root_read = new handler_entry_read_dispatch<32, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<32, Width, AddrShift, Endian>(this, r, nullptr); break;
+ default: fatalerror("Unhandled address bus width %d\n", address_width);
}
-#endif
}
- // accessors
- virtual address_table_read &read() override { return m_read; }
- virtual address_table_write &write() override { return m_write; }
- virtual address_table_setoffset &setoffset() override { return m_setoffset; }
+ void *create_cache() override {
+ return new memory_access_cache<Width, AddrShift, Endian>(*this, m_root_read, m_root_write);
+ }
+
+ void delayed_ref(handler_entry *e) {
+ e->ref();
+ m_delayed_unrefs.insert(e);
+ }
- // watchpoint control
- virtual void enable_read_watchpoints(bool enable = true) override { m_read.enable_watchpoints(enable); }
- virtual void enable_write_watchpoints(bool enable = true) override { m_write.enable_watchpoints(enable); }
+ void delayed_unref(handler_entry *e) {
+ m_delayed_unrefs.erase(m_delayed_unrefs.find(e));
+ e->unref();
+ }
+
+ void validate_reference_counts() const override {
+ handler_entry::reflist refs;
+ refs.add(m_root_read);
+ refs.add(m_root_write);
+ refs.add(m_unmap_r);
+ refs.add(m_unmap_w);
+ refs.add(m_nop_r);
+ refs.add(m_nop_w);
+ for(handler_entry *e : m_delayed_unrefs)
+ refs.add(e);
+ refs.propagate();
+ refs.check();
+ }
+
+ virtual void remove_passthrough(std::unordered_set<handler_entry *> &handlers) override {
+ m_root_read->detach(handlers);
+ m_root_write->detach(handlers);
+ }
// generate accessor table
virtual void accessors(data_accessors &accessors) const override
@@ -1075,31 +369,15 @@ public:
}
// return a pointer to the read bank, or nullptr if none
- virtual void *get_read_ptr(offs_t address) override
+ virtual void *get_read_ptr(offs_t address) const override
{
- // perform the lookup
- address &= m_addrmask;
- u32 entry = read_lookup(address);
- const handler_entry_read &handler = m_read.handler_read(entry);
-
- // 8-bit case: RAM/ROM
- if (entry > STATIC_BANKMAX)
- return nullptr;
- return handler.ramptr(handler.offset(address));
+ return m_root_read->get_ptr(address);
}
// return a pointer to the write bank, or nullptr if none
- virtual void *get_write_ptr(offs_t address) override
+ virtual void *get_write_ptr(offs_t address) const override
{
- // perform the lookup
- address &= m_addrmask;
- u32 entry = write_lookup(address);
- const handler_entry_write &handler = m_write.handler_write(entry);
-
- // 8-bit case: RAM/ROM
- if (entry > STATIC_BANKMAX)
- return nullptr;
- return handler.ramptr(handler.offset(address));
+ return m_root_write->get_ptr(address);
}
// native read
@@ -1107,21 +385,7 @@ public:
{
g_profiler.start(PROFILER_MEMREAD);
- if (TEST_HANDLER) printf("[r%X,%s]", offset, core_i64_hex_format(mask, sizeof(NativeType) * 2));
-
- // look up the handler
- offs_t address = offset & m_addrmask;
- u32 entry = read_lookup(address);
- const handler_entry_read &handler = m_read.handler_read(entry);
-
- // either read directly from RAM, or call the delegate
- offset = offset_to_byte(handler.offset(address));
- NativeType result;
- if (entry <= STATIC_BANKMAX) result = *reinterpret_cast<NativeType *>(handler.ramptr(offset));
- else if (sizeof(NativeType) == 1) result = handler.read8(*this, offset, mask);
- else if (sizeof(NativeType) == 2) result = handler.read16(*this, offset >> 1, mask);
- else if (sizeof(NativeType) == 4) result = handler.read32(*this, offset >> 2, mask);
- else if (sizeof(NativeType) == 8) result = handler.read64(*this, offset >> 3, mask);
+ uX result = m_root_read->read(offset, mask);
g_profiler.stop();
return result;
@@ -1132,21 +396,7 @@ public:
{
g_profiler.start(PROFILER_MEMREAD);
- if (TEST_HANDLER) printf("[r%X]", offset);
-
- // look up the handler
- offs_t address = offset & m_addrmask;
- u32 entry = read_lookup(address);
- const handler_entry_read &handler = m_read.handler_read(entry);
-
- // either read directly from RAM, or call the delegate
- offset = offset_to_byte(handler.offset(address));
- NativeType result;
- if (entry <= STATIC_BANKMAX) result = *reinterpret_cast<NativeType *>(handler.ramptr(offset));
- else if (sizeof(NativeType) == 1) result = handler.read8(*this, offset, 0xff);
- else if (sizeof(NativeType) == 2) result = handler.read16(*this, offset >> 1, 0xffff);
- else if (sizeof(NativeType) == 4) result = handler.read32(*this, offset >> 2, 0xffffffff);
- else if (sizeof(NativeType) == 8) result = handler.read64(*this, offset >> 3, 0xffffffffffffffffU);
+ uX result = m_root_read->read(offset, uX(0xffffffffffffffffU));
g_profiler.stop();
return result;
@@ -1157,22 +407,7 @@ public:
{
g_profiler.start(PROFILER_MEMWRITE);
- // look up the handler
- offs_t address = offset & m_addrmask;
- u32 entry = write_lookup(address);
- const handler_entry_write &handler = m_write.handler_write(entry);
-
- // either write directly to RAM, or call the delegate
- offset = offset_to_byte(handler.offset(address));
- if (entry <= STATIC_BANKMAX)
- {
- NativeType *dest = reinterpret_cast<NativeType *>(handler.ramptr(offset));
- *dest = (*dest & ~mask) | (data & mask);
- }
- else if (sizeof(NativeType) == 1) handler.write8(*this, offset, data, mask);
- else if (sizeof(NativeType) == 2) handler.write16(*this, offset >> 1, data, mask);
- else if (sizeof(NativeType) == 4) handler.write32(*this, offset >> 2, data, mask);
- else if (sizeof(NativeType) == 8) handler.write64(*this, offset >> 3, data, mask);
+ m_root_write->write(offset, data, mask);
g_profiler.stop();
}
@@ -1182,150 +417,235 @@ public:
{
g_profiler.start(PROFILER_MEMWRITE);
- // look up the handler
- offs_t address = offset & m_addrmask;
- u32 entry = write_lookup(address);
- const handler_entry_write &handler = m_write.handler_write(entry);
-
- // either write directly to RAM, or call the delegate
- offset = offset_to_byte(handler.offset(address));
- if (entry <= STATIC_BANKMAX) *reinterpret_cast<NativeType *>(handler.ramptr(offset)) = data;
- else if (sizeof(NativeType) == 1) handler.write8(*this, offset, data, 0xff);
- else if (sizeof(NativeType) == 2) handler.write16(*this, offset >> 1, data, 0xffff);
- else if (sizeof(NativeType) == 4) handler.write32(*this, offset >> 2, data, 0xffffffff);
- else if (sizeof(NativeType) == 8) handler.write64(*this, offset >> 3, data, 0xffffffffffffffffU);
+ m_root_write->write(offset, data, uX(0xffffffffffffffffU));
g_profiler.stop();
}
+ // virtual access to these functions
+ u8 read_byte(offs_t address) override { address &= m_addrmask; return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); }
+ u16 read_word(offs_t address) override { address &= m_addrmask; return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); }
+ u16 read_word(offs_t address, u16 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u16 read_word_unaligned(offs_t address) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); }
+ u16 read_word_unaligned(offs_t address, u16 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u32 read_dword(offs_t address) override { address &= m_addrmask; return Width == 2 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); }
+ u32 read_dword(offs_t address, u32 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u32 read_dword_unaligned(offs_t address) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); }
+ u32 read_dword_unaligned(offs_t address, u32 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u64 read_qword(offs_t address) override { address &= m_addrmask; return Width == 3 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); }
+ u64 read_qword(offs_t address, u64 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u64 read_qword_unaligned(offs_t address) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); }
+ u64 read_qword_unaligned(offs_t address, u64 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+
+ void write_byte(offs_t address, u8 data) override { address &= m_addrmask; if (Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xff); }
+ void write_word(offs_t address, u16 data) override { address &= m_addrmask; if (Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); }
+ void write_word(offs_t address, u16 data, u16 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_word_unaligned(offs_t address, u16 data) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); }
+ void write_word_unaligned(offs_t address, u16 data, u16 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_dword(offs_t address, u32 data) override { address &= m_addrmask; if (Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); }
+ void write_dword(offs_t address, u32 data, u32 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_dword_unaligned(offs_t address, u32 data) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); }
+ void write_dword_unaligned(offs_t address, u32 data, u32 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_qword(offs_t address, u64 data) override { address &= m_addrmask; if (Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); }
+ void write_qword(offs_t address, u64 data, u64 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_qword_unaligned(offs_t address, u64 data) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); }
+ void write_qword_unaligned(offs_t address, u64 data, u64 mask) override {address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+
+ // static access to these functions
+ static u8 read_byte_static(this_type &space, offs_t address) { address &= space.m_addrmask; return Width == 0 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xff); }
+ static u16 read_word_static(this_type &space, offs_t address) { address &= space.m_addrmask; return Width == 1 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xffff); }
+ static u16 read_word_masked_static(this_type &space, offs_t address, u16 mask) { address &= space.m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, mask); }
+ static u32 read_dword_static(this_type &space, offs_t address) { address &= space.m_addrmask; return Width == 2 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xffffffff); }
+ static u32 read_dword_masked_static(this_type &space, offs_t address, u32 mask) { address &= space.m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, mask); }
+ static u64 read_qword_static(this_type &space, offs_t address) { address &= space.m_addrmask; return Width == 3 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 3, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xffffffffffffffffU); }
+ static u64 read_qword_masked_static(this_type &space, offs_t address, u64 mask) { address &= space.m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, mask); }
+ static void write_byte_static(this_type &space, offs_t address, u8 data) { address &= space.m_addrmask; if (Width == 0) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xff); }
+ static void write_word_static(this_type &space, offs_t address, u16 data) { address &= space.m_addrmask; if (Width == 1) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xffff); }
+ static void write_word_masked_static(this_type &space, offs_t address, u16 data, u16 mask) { address &= space.m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, mask); }
+ static void write_dword_static(this_type &space, offs_t address, u32 data) { address &= space.m_addrmask; if (Width == 2) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xffffffff); }
+ static void write_dword_masked_static(this_type &space, offs_t address, u32 data, u32 mask) { address &= space.m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, mask); }
+ static void write_qword_static(this_type &space, offs_t address, u64 data) { address &= space.m_addrmask; if (Width == 3) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, false>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); }
+ static void write_qword_masked_static(this_type &space, offs_t address, u64 data, u64 mask) { address &= space.m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, mask); }
+
+ handler_entry_read <Width, AddrShift, Endian> *m_root_read;
+ handler_entry_write<Width, AddrShift, Endian> *m_root_write;
+
+ std::unordered_set<handler_entry *> m_delayed_unrefs;
- // Allows to announce a pending read or write operation on this address.
- // The user of the address_space calls a set_address operation which leads
- // to some particular set_offset operation for an entry in the address map.
- void set_address(offs_t address) override
+private:
+ template<int AccessWidth> std::enable_if_t<(Width == AccessWidth)>
+ install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth,
+ typename handler_entry_size<AccessWidth>::READ handler_r)
{
- address &= m_addrmask;
- u32 entry = setoffset_lookup(address);
- const handler_entry_setoffset &handler = m_setoffset.handler_setoffset(entry);
+ VPRINTF(("address_space::install_read_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s)\n",
+ core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
+ core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars),
+ 8 << Width, 8 << AccessWidth,
+ handler_r.name(), core_i64_hex_format(unitmask, data_width() / 4)));
+
+ offs_t nstart, nend, nmask, nmirror;
+ u64 nunitmask;
+ int ncswidth;
+ check_optimize_all("install_read_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
- offs_t offset = handler.offset(address);
- handler.setoffset(*this, offset / sizeof(NativeType));
+ auto hand_r = new handler_entry_read_delegate<Width, AddrShift, Endian>(this, handler_r);
+ hand_r->set_address_info(nstart, nmask);
+ m_root_read->populate(nstart, nend, nmirror, hand_r);
+ invalidate_caches(read_or_write::READ);
}
- // virtual access to these functions
- u8 read_byte(offs_t address) override { return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); }
- u16 read_word(offs_t address) override { return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); }
- u16 read_word(offs_t address, u16 mask) override { return memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u16 read_word_unaligned(offs_t address) override { return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); }
- u16 read_word_unaligned(offs_t address, u16 mask) override { return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u32 read_dword(offs_t address) override { return Width == 2 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); }
- u32 read_dword(offs_t address, u32 mask) override { return memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u32 read_dword_unaligned(offs_t address) override { return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); }
- u32 read_dword_unaligned(offs_t address, u32 mask) override { return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u64 read_qword(offs_t address) override { return Width == 3 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); }
- u64 read_qword(offs_t address, u64 mask) override { return memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u64 read_qword_unaligned(offs_t address) override { return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); }
- u64 read_qword_unaligned(offs_t address, u64 mask) override { return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
-
- void write_byte(offs_t address, u8 data) override { if (Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xff); }
- void write_word(offs_t address, u16 data) override { if (Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); }
- void write_word(offs_t address, u16 data, u16 mask) override { memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
- void write_word_unaligned(offs_t address, u16 data) override { memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); }
- void write_word_unaligned(offs_t address, u16 data, u16 mask) override { memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
- void write_dword(offs_t address, u32 data) override { if (Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); }
- void write_dword(offs_t address, u32 data, u32 mask) override { memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
- void write_dword_unaligned(offs_t address, u32 data) override { memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); }
- void write_dword_unaligned(offs_t address, u32 data, u32 mask) override { memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
- void write_qword(offs_t address, u64 data) override { if (Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); }
- void write_qword(offs_t address, u64 data, u64 mask) override { memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
- void write_qword_unaligned(offs_t address, u64 data) override { memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); }
- void write_qword_unaligned(offs_t address, u64 data, u64 mask) override { memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ template<int AccessWidth> std::enable_if_t<(Width > AccessWidth)>
+ install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth,
+ typename handler_entry_size<AccessWidth>::READ handler_r)
+ {
+ VPRINTF(("address_space::install_read_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s)\n",
+ core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
+ core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars),
+ 8 << Width, 8 << AccessWidth,
+ handler_r.name(), core_i64_hex_format(unitmask, data_width() / 4)));
- // static access to these functions
- static u8 read_byte_static(this_type &space, offs_t address) { return Width == 0 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xff); }
- static u16 read_word_static(this_type &space, offs_t address) { return Width == 1 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xffff); }
- static u16 read_word_masked_static(this_type &space, offs_t address, u16 mask) { return memory_read_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, mask); }
- static u32 read_dword_static(this_type &space, offs_t address) { return Width == 2 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xffffffff); }
- static u32 read_dword_masked_static(this_type &space, offs_t address, u32 mask) { return memory_read_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, mask); }
- static u64 read_qword_static(this_type &space, offs_t address) { return Width == 3 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 3, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xffffffffffffffffU); }
- static u64 read_qword_masked_static(this_type &space, offs_t address, u64 mask) { return memory_read_generic<Width, AddrShift, Endian, 3, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, mask); }
- static void write_byte_static(this_type &space, offs_t address, u8 data) { if (Width == 0) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xff); }
- static void write_word_static(this_type &space, offs_t address, u16 data) { if (Width == 1) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xffff); }
- static void write_word_masked_static(this_type &space, offs_t address, u16 data, u16 mask) { memory_write_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, mask); }
- static void write_dword_static(this_type &space, offs_t address, u32 data) { if (Width == 2) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xffffffff); }
- static void write_dword_masked_static(this_type &space, offs_t address, u32 data, u32 mask) { memory_write_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, mask); }
- static void write_qword_static(this_type &space, offs_t address, u64 data) { if (Width == 3) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, false>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); }
- static void write_qword_masked_static(this_type &space, offs_t address, u64 data, u64 mask) { memory_write_generic<Width, AddrShift, Endian, 3, false>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, mask); }
-
- address_table_read m_read; // memory read lookup table
- address_table_write m_write; // memory write lookup table
- address_table_setoffset m_setoffset; // memory setoffset lookup table
-};
+ offs_t nstart, nend, nmask, nmirror;
+ u64 nunitmask;
+ int ncswidth;
+ check_optimize_all("install_read_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
-typedef address_space_specific<u8, ENDIANNESS_LITTLE, 0, false> address_space_8_8le_small;
-typedef address_space_specific<u8, ENDIANNESS_BIG, 0, false> address_space_8_8be_small;
-typedef address_space_specific<u16, ENDIANNESS_LITTLE, 3, false> address_space_16_1le_small;
-typedef address_space_specific<u16, ENDIANNESS_BIG, 3, false> address_space_16_1be_small;
-typedef address_space_specific<u16, ENDIANNESS_LITTLE, 0, false> address_space_16_8le_small;
-typedef address_space_specific<u16, ENDIANNESS_BIG, 0, false> address_space_16_8be_small;
-typedef address_space_specific<u16, ENDIANNESS_LITTLE, -1, false> address_space_16_16le_small;
-typedef address_space_specific<u16, ENDIANNESS_BIG, -1, false> address_space_16_16be_small;
-typedef address_space_specific<u32, ENDIANNESS_LITTLE, 0, false> address_space_32_8le_small;
-typedef address_space_specific<u32, ENDIANNESS_BIG, 0, false> address_space_32_8be_small;
-typedef address_space_specific<u32, ENDIANNESS_LITTLE, -1, false> address_space_32_16le_small;
-typedef address_space_specific<u32, ENDIANNESS_BIG, -1, false> address_space_32_16be_small;
-typedef address_space_specific<u32, ENDIANNESS_LITTLE, -2, false> address_space_32_32le_small;
-typedef address_space_specific<u32, ENDIANNESS_BIG, -2, false> address_space_32_32be_small;
-typedef address_space_specific<u64, ENDIANNESS_LITTLE, 0, false> address_space_64_8le_small;
-typedef address_space_specific<u64, ENDIANNESS_BIG, 0, false> address_space_64_8be_small;
-typedef address_space_specific<u64, ENDIANNESS_LITTLE, -1, false> address_space_64_16le_small;
-typedef address_space_specific<u64, ENDIANNESS_BIG, -1, false> address_space_64_16be_small;
-typedef address_space_specific<u64, ENDIANNESS_LITTLE, -2, false> address_space_64_32le_small;
-typedef address_space_specific<u64, ENDIANNESS_BIG, -2, false> address_space_64_32be_small;
-typedef address_space_specific<u64, ENDIANNESS_LITTLE, -3, false> address_space_64_64le_small;
-typedef address_space_specific<u64, ENDIANNESS_BIG, -3, false> address_space_64_64be_small;
-
-typedef address_space_specific<u8, ENDIANNESS_LITTLE, 0, true> address_space_8_8le_large;
-typedef address_space_specific<u8, ENDIANNESS_BIG, 0, true> address_space_8_8be_large;
-typedef address_space_specific<u16, ENDIANNESS_LITTLE, 3, true> address_space_16_1le_large;
-typedef address_space_specific<u16, ENDIANNESS_BIG, 3, true> address_space_16_1be_large;
-typedef address_space_specific<u16, ENDIANNESS_LITTLE, 0, true> address_space_16_8le_large;
-typedef address_space_specific<u16, ENDIANNESS_BIG, 0, true> address_space_16_8be_large;
-typedef address_space_specific<u16, ENDIANNESS_LITTLE, -1, true> address_space_16_16le_large;
-typedef address_space_specific<u16, ENDIANNESS_BIG, -1, true> address_space_16_16be_large;
-typedef address_space_specific<u32, ENDIANNESS_LITTLE, 0, true> address_space_32_8le_large;
-typedef address_space_specific<u32, ENDIANNESS_BIG, 0, true> address_space_32_8be_large;
-typedef address_space_specific<u32, ENDIANNESS_LITTLE, -1, true> address_space_32_16le_large;
-typedef address_space_specific<u32, ENDIANNESS_BIG, -1, true> address_space_32_16be_large;
-typedef address_space_specific<u32, ENDIANNESS_LITTLE, -2, true> address_space_32_32le_large;
-typedef address_space_specific<u32, ENDIANNESS_BIG, -2, true> address_space_32_32be_large;
-typedef address_space_specific<u64, ENDIANNESS_LITTLE, 0, true> address_space_64_8le_large;
-typedef address_space_specific<u64, ENDIANNESS_BIG, 0, true> address_space_64_8be_large;
-typedef address_space_specific<u64, ENDIANNESS_LITTLE, -1, true> address_space_64_16le_large;
-typedef address_space_specific<u64, ENDIANNESS_BIG, -1, true> address_space_64_16be_large;
-typedef address_space_specific<u64, ENDIANNESS_LITTLE, -2, true> address_space_64_32le_large;
-typedef address_space_specific<u64, ENDIANNESS_BIG, -2, true> address_space_64_32be_large;
-typedef address_space_specific<u64, ENDIANNESS_LITTLE, -3, true> address_space_64_64le_large;
-typedef address_space_specific<u64, ENDIANNESS_BIG, -3, true> address_space_64_64be_large;
+ auto hand_r = new handler_entry_read_delegate<AccessWidth, -AccessWidth, Endian>(this, handler_r);
+ memory_units_descriptor<Width, AddrShift, Endian> descriptor(AccessWidth, Endian, hand_r, nstart, nend, nmask, nunitmask, ncswidth);
+ hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask());
+ m_root_read->populate_mismatched(nstart, nend, nmirror, descriptor);
+ hand_r->unref();
+ invalidate_caches(read_or_write::READ);
+ }
+ template<int AccessWidth> std::enable_if_t<(Width < AccessWidth)>
+ install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth,
+ typename handler_entry_size<AccessWidth>::READ handler_r)
+ {
+ fatalerror("install_read_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width);
+ }
+ template<int AccessWidth> std::enable_if_t<(Width == AccessWidth)>
+ install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth,
+ typename handler_entry_size<AccessWidth>::WRITE handler_w)
+ {
+ VPRINTF(("address_space::install_write_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s)\n",
+ core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
+ core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars),
+ 8 << Width, 8 << AccessWidth,
+ handler_w.name(), core_i64_hex_format(unitmask, data_width() / 4)));
-//**************************************************************************
-// GLOBAL VARIABLES
-//**************************************************************************
+ offs_t nstart, nend, nmask, nmirror;
+ u64 nunitmask;
+ int ncswidth;
+ check_optimize_all("install_write_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
+
+ auto hand_w = new handler_entry_write_delegate<Width, AddrShift, Endian>(this, handler_w);
+ hand_w->set_address_info(nstart, nmask);
+ m_root_write->populate(nstart, nend, nmirror, hand_w);
+ invalidate_caches(read_or_write::WRITE);
+ }
+
+ template<int AccessWidth> std::enable_if_t<(Width > AccessWidth)>
+ install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth,
+ typename handler_entry_size<AccessWidth>::WRITE handler_w)
+ {
+ VPRINTF(("address_space::install_write_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s)\n",
+ core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
+ core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars),
+ 8 << Width, 8 << AccessWidth,
+ handler_w.name(), core_i64_hex_format(unitmask, data_width() / 4)));
-// global watchpoint table
-u16 address_table::s_watchpoint_table[1 << LEVEL1_BITS];
+ offs_t nstart, nend, nmask, nmirror;
+ u64 nunitmask;
+ int ncswidth;
+ check_optimize_all("install_write_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
+ auto hand_w = new handler_entry_write_delegate<AccessWidth, -AccessWidth, Endian>(this, handler_w);
+ memory_units_descriptor<Width, AddrShift, Endian> descriptor(AccessWidth, Endian, hand_w, nstart, nend, nmask, nunitmask, ncswidth);
+ hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask());
+ m_root_write->populate_mismatched(nstart, nend, nmirror, descriptor);
+ hand_w->unref();
+ invalidate_caches(read_or_write::WRITE);
+ }
-//**************************************************************************
-// FUNCTION PROTOTYPES
-//**************************************************************************
+ template<int AccessWidth> std::enable_if_t<(Width < AccessWidth)>
+ install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth,
+ typename handler_entry_size<AccessWidth>::WRITE handler_w)
+ {
+ fatalerror("install_write_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width);
+ }
+
+
+
+ template<int AccessWidth> std::enable_if_t<(Width == AccessWidth)>
+ install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth,
+ typename handler_entry_size<AccessWidth>::READ handler_r,
+ typename handler_entry_size<AccessWidth>::WRITE handler_w)
+ {
+ VPRINTF(("address_space::install_readwrite_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s, %s)\n",
+ core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
+ core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars),
+ 8 << Width, 8 << AccessWidth,
+ handler_r.name(), handler_w.name(), core_i64_hex_format(unitmask, data_width() / 4)));
+
+ offs_t nstart, nend, nmask, nmirror;
+ u64 nunitmask;
+ int ncswidth;
+ check_optimize_all("install_readwrite_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
+
+ auto hand_r = new handler_entry_read_delegate <Width, AddrShift, Endian>(this, handler_r);
+ hand_r->set_address_info(nstart, nmask);
+ m_root_read ->populate(nstart, nend, nmirror, hand_r);
+
+ auto hand_w = new handler_entry_write_delegate<Width, AddrShift, Endian>(this, handler_w);
+ hand_w->set_address_info(nstart, nmask);
+ m_root_write->populate(nstart, nend, nmirror, hand_w);
+
+ invalidate_caches(read_or_write::READWRITE);
+ }
+
+ template<int AccessWidth> std::enable_if_t<(Width > AccessWidth)>
+ install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth,
+ typename handler_entry_size<AccessWidth>::READ handler_r,
+ typename handler_entry_size<AccessWidth>::WRITE handler_w)
+ {
+ VPRINTF(("address_space::install_readwrite_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s, %s)\n",
+ core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
+ core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars),
+ 8 << Width, 8 << AccessWidth,
+ handler_r.name(), handler_w.name(), core_i64_hex_format(unitmask, data_width() / 4)));
+
+ offs_t nstart, nend, nmask, nmirror;
+ u64 nunitmask;
+ int ncswidth;
+ check_optimize_all("install_readwrite_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
+
+ auto hand_r = new handler_entry_read_delegate <AccessWidth, -AccessWidth, Endian>(this, handler_r);
+ memory_units_descriptor<Width, AddrShift, Endian> descriptor(AccessWidth, Endian, hand_r, nstart, nend, nmask, nunitmask, ncswidth);
+ hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask());
+ m_root_read ->populate_mismatched(nstart, nend, nmirror, descriptor);
+ hand_r->unref();
+
+ auto hand_w = new handler_entry_write_delegate<AccessWidth, -AccessWidth, Endian>(this, handler_w);
+ descriptor.set_subunit_handler(hand_w);
+ hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask());
+ m_root_write->populate_mismatched(nstart, nend, nmirror, descriptor);
+ hand_w->unref();
+
+ invalidate_caches(read_or_write::READWRITE);
+ }
+
-// debugging
-static void generate_memdump(running_machine &machine);
+ template<int AccessWidth> std::enable_if_t<(Width < AccessWidth)>
+ install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth,
+ typename handler_entry_size<AccessWidth>::READ handler_r,
+ typename handler_entry_size<AccessWidth>::WRITE handler_w)
+ {
+ fatalerror("install_readwrite_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width);
+ }
+};
@@ -1339,10 +659,8 @@ static void generate_memdump(running_machine &machine);
memory_manager::memory_manager(running_machine &machine)
: m_machine(machine),
- m_initialized(false),
- m_banknext(STATIC_BANK1)
+ m_initialized(false)
{
- memset(m_bank_ptr, 0, sizeof(m_bank_ptr));
}
//-------------------------------------------------
@@ -1358,214 +676,87 @@ void memory_manager::allocate(device_memory_interface &memory)
if (spaceconfig)
{
// allocate one of the appropriate type
- bool const large(spaceconfig->addr2byte_end(0xffffffffUL >> (32 - spaceconfig->m_addr_width)) >= (1 << 18));
-
- switch (spaceconfig->data_width())
+ switch (spaceconfig->data_width() | (spaceconfig->addr_shift() + 4))
{
- case 8:
- if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_8_8le_large>(*this, spacenum);
- else
- memory.allocate<address_space_8_8le_small>(*this, spacenum);
- }
- else
- {
- if (large)
- memory.allocate<address_space_8_8be_large>(*this, spacenum);
+ case 8|(4-0):
+ if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
+ memory.allocate<address_space_specific<0, 0, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- memory.allocate<address_space_8_8be_small>(*this, spacenum);
- }
- break;
+ memory.allocate<address_space_specific<0, 0, ENDIANNESS_BIG >>(*this, spacenum);
+ break;
- case 16:
- switch (spaceconfig->addr_shift())
- {
- case 3:
+ case 16|(4+3):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_16_1le_large>(*this, spacenum);
- else
- memory.allocate<address_space_16_1le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<1, 3, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_16_1be_large>(*this, spacenum);
- else
- memory.allocate<address_space_16_1be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<1, 3, ENDIANNESS_BIG >>(*this, spacenum);
break;
- case 0:
+ case 16|(4-0):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_16_8le_large>(*this, spacenum);
- else
- memory.allocate<address_space_16_8le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<1, 0, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_16_8be_large>(*this, spacenum);
- else
- memory.allocate<address_space_16_8be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<1, 0, ENDIANNESS_BIG >>(*this, spacenum);
break;
- case -1:
+ case 16|(4-1):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_16_16le_large>(*this, spacenum);
- else
- memory.allocate<address_space_16_16le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<1, -1, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_16_16be_large>(*this, spacenum);
- else
- memory.allocate<address_space_16_16be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<1, -1, ENDIANNESS_BIG >>(*this, spacenum);
break;
- }
- break;
- case 32:
- switch (spaceconfig->addr_shift())
- {
- case 0:
+ case 32|(4-0):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_32_8le_large>(*this, spacenum);
- else
- memory.allocate<address_space_32_8le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<2, 0, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_32_8be_large>(*this, spacenum);
- else
- memory.allocate<address_space_32_8be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<2, 0, ENDIANNESS_BIG >>(*this, spacenum);
break;
- case -1:
+ case 32|(4-1):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_32_16le_large>(*this, spacenum);
- else
- memory.allocate<address_space_32_16le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<2, -1, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_32_16be_large>(*this, spacenum);
- else
- memory.allocate<address_space_32_16be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<2, -1, ENDIANNESS_BIG >>(*this, spacenum);
break;
- case -2:
+ case 32|(4-2):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_32_32le_large>(*this, spacenum);
- else
- memory.allocate<address_space_32_32le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<2, -2, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_32_32be_large>(*this, spacenum);
- else
- memory.allocate<address_space_32_32be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<2, -2, ENDIANNESS_BIG >>(*this, spacenum);
break;
- }
- break;
- case 64:
- switch (spaceconfig->addr_shift())
- {
- case 0:
+ case 64|(4-0):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_64_8le_large>(*this, spacenum);
- else
- memory.allocate<address_space_64_8le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<3, 0, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_64_8be_large>(*this, spacenum);
- else
- memory.allocate<address_space_64_8be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<3, 0, ENDIANNESS_BIG >>(*this, spacenum);
break;
- case -1:
+ case 64|(4-1):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_64_16le_large>(*this, spacenum);
- else
- memory.allocate<address_space_64_16le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<3, -1, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_64_16be_large>(*this, spacenum);
- else
- memory.allocate<address_space_64_16be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<3, -1, ENDIANNESS_BIG >>(*this, spacenum);
break;
- case -2:
+ case 64|(4-2):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_64_32le_large>(*this, spacenum);
- else
- memory.allocate<address_space_64_32le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<3, -2, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_64_32be_large>(*this, spacenum);
- else
- memory.allocate<address_space_64_32be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<3, -2, ENDIANNESS_BIG >>(*this, spacenum);
break;
- case -3:
+ case 64|(4-3):
if (spaceconfig->endianness() == ENDIANNESS_LITTLE)
- {
- if (large)
- memory.allocate<address_space_64_64le_large>(*this, spacenum);
- else
- memory.allocate<address_space_64_64le_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<3, -3, ENDIANNESS_LITTLE>>(*this, spacenum);
else
- {
- if (large)
- memory.allocate<address_space_64_64be_large>(*this, spacenum);
- else
- memory.allocate<address_space_64_64be_small>(*this, spacenum);
- }
+ memory.allocate<address_space_specific<3, -3, ENDIANNESS_BIG >>(*this, spacenum);
break;
- }
- break;
-
- default:
- throw emu_fatalerror("Invalid width %d specified for address_space::allocate", spaceconfig->data_width());
+
+ default:
+ throw emu_fatalerror("Invalid width %d/shift %d specified for address_space::allocate", spaceconfig->data_width(), spaceconfig->addr_shift());
}
}
}
@@ -1609,12 +800,6 @@ void memory_manager::initialize()
for (auto const memory : memories)
memory->set_log_unmap(false);
- // register a callback to reset banks when reloading state
- machine().save().register_postload(save_prepost_delegate(FUNC(memory_manager::bank_reattach), this));
-
- // dump the final memory configuration
- generate_memdump(machine());
-
// we are now initialized
m_initialized = true;
}
@@ -1665,40 +850,36 @@ memory_region *memory_manager::region_containing(const void *memory, offs_t byte
return nullptr;
}
-
-//-------------------------------------------------
-// generate_memdump - internal memory dump
-//-------------------------------------------------
-
-static void generate_memdump(running_machine &machine)
+memory_bank *memory_manager::find(const char *tag) const
{
- if (MEM_DUMP)
- {
- FILE *file = fopen("memdump.log", "w");
- if (file)
- {
- memory_interface_iterator iter(machine.root_device());
- for (device_memory_interface &memory : iter)
- memory.dump(file);
- fclose(file);
- }
- }
+ auto bank = m_banklist.find(tag);
+ if (bank != m_banklist.end())
+ return bank->second.get();
+ return nullptr;
}
-
-//-------------------------------------------------
-// bank_reattach - reconnect banks after a load
-//-------------------------------------------------
-
-void memory_manager::bank_reattach()
+memory_bank *memory_manager::find(address_space &space, offs_t addrstart, offs_t addrend) const
{
- // for each non-anonymous bank, explicitly reset its entry
+ // try to find an exact match
for (auto &bank : m_banklist)
- if (!bank.second->anonymous() && bank.second->entry() != BANK_ENTRY_UNSPECIFIED)
- bank.second->set_entry(bank.second->entry());
-}
+ if (bank.second->anonymous() && bank.second->references_space(space, read_or_write::READWRITE) && bank.second->matches_exactly(addrstart, addrend))
+ return bank.second.get();
+ // not found
+ return nullptr;
+}
+memory_bank *memory_manager::allocate(address_space &space, offs_t addrstart, offs_t addrend, const char *tag)
+{
+ auto bank = std::make_unique<memory_bank>(space, m_banklist.size(), addrstart, addrend, tag);
+ std::string temptag;
+ if (tag == nullptr) {
+ temptag = string_format("anon_%p", bank.get());
+ tag = temptag.c_str();
+ }
+ m_banklist.emplace(tag, std::move(bank));
+ return m_banklist.find(tag)->second.get();
+}
//**************************************************************************
// ADDRESS SPACE
@@ -1708,101 +889,21 @@ void memory_manager::bank_reattach()
// address_space - constructor
//-------------------------------------------------
-address_space::address_space(memory_manager &manager, device_memory_interface &memory, int spacenum, bool large)
+address_space::address_space(memory_manager &manager, device_memory_interface &memory, int spacenum)
: m_config(*memory.space_config(spacenum)),
m_device(memory.device()),
- m_addrmask(0xffffffffUL >> (32 - m_config.m_addr_width)),
- m_logaddrmask(0xffffffffUL >> (32 - m_config.m_logaddr_width)),
+ m_addrmask(make_bitmask<offs_t>(m_config.m_addr_width)),
+ m_logaddrmask(make_bitmask<offs_t>(m_config.m_logaddr_width)),
m_unmap(0),
m_spacenum(spacenum),
m_log_unmap(true),
m_name(memory.space_config(spacenum)->name()),
m_addrchars((m_config.m_addr_width + 3) / 4),
m_logaddrchars((m_config.m_logaddr_width + 3) / 4),
+ m_notifier_id(0),
+ m_in_notification(0),
m_manager(manager)
{
- switch(m_config.data_width()) {
- case 8:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<0, 0, ENDIANNESS_LITTLE>();
- else
- cache_init<0, 0, ENDIANNESS_BIG>();
- break;
- case 16:
- switch(m_config.addr_shift()) {
- case 3:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<1, 3, ENDIANNESS_LITTLE>();
- else
- cache_init<1, 3, ENDIANNESS_BIG>();
- break;
- case 0:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<1, 0, ENDIANNESS_LITTLE>();
- else
- cache_init<1, 0, ENDIANNESS_BIG>();
- break;
- case -1:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<1, -1, ENDIANNESS_LITTLE>();
- else
- cache_init<1, -1, ENDIANNESS_BIG>();
- break;
- }
- break;
- case 32:
- switch(m_config.addr_shift()) {
- case 0:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<2, 0, ENDIANNESS_LITTLE>();
- else
- cache_init<2, 0, ENDIANNESS_BIG>();
- break;
- case -1:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<2, -1, ENDIANNESS_LITTLE>();
- else
- cache_init<2, -1, ENDIANNESS_BIG>();
- break;
- case -2:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<2, -2, ENDIANNESS_LITTLE>();
- else
- cache_init<2, -2, ENDIANNESS_BIG>();
- break;
- }
- break;
- case 64:
- switch(m_config.addr_shift()) {
- case 0:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<3, 0, ENDIANNESS_LITTLE>();
- else
- cache_init<3, 0, ENDIANNESS_BIG>();
- break;
- case -1:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<3, -1, ENDIANNESS_LITTLE>();
- else
- cache_init<3, -1, ENDIANNESS_BIG>();
- break;
- case -2:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<3, -2, ENDIANNESS_LITTLE>();
- else
- cache_init<3, -2, ENDIANNESS_BIG>();
- break;
- case -3:
- if(m_config.endianness() == ENDIANNESS_LITTLE)
- cache_init<3, -3, ENDIANNESS_LITTLE>();
- else
- cache_init<3, -3, ENDIANNESS_BIG>();
- break;
- }
- break;
- }
-
- m_cache = m_cache_alloc(nullptr);
}
@@ -1812,21 +913,12 @@ address_space::address_space(memory_manager &manager, device_memory_interface &m
address_space::~address_space()
{
- m_cache_delete(m_cache);
-}
-
-
-template<int Width, int AddrShift, int Endian> void address_space::cache_init()
-{
- using dt = memory_access_cache<Width, AddrShift, Endian>;
- m_cache_alloc = [this](void *cache) -> void * { (void)cache; return static_cast<void *>(new dt(*this)); };
- m_cache_delete = [](void *cache) { delete static_cast<dt *>(cache); };
- m_cache_invalidate_read_caches = [](void *cache) { static_cast<dt *>(cache)->force_update(); };
- m_cache_invalidate_read_cache_entry = [](void *cache, u16 entry) { static_cast<dt *>(cache)->force_update(entry); };
- m_cache_invalidate_read_cache_range = [](void *cache, offs_t start, offs_t end) { static_cast<dt *>(cache)->remove_intersecting_ranges(start, end); };
+ m_unmap_r->unref();
+ m_unmap_w->unref();
+ m_nop_r->unref();
+ m_nop_w->unref();
}
-
//-------------------------------------------------
// adjust_addresses - adjust addresses for a
// given address space in a standard fashion
@@ -2040,7 +1132,12 @@ void address_space::prepare_map()
// extract global parameters specified by the map
m_unmap = (m_map->m_unmapval == 0) ? 0 : ~0;
if (m_map->m_globalmask != 0)
+ {
+ if (m_map->m_globalmask & ~m_addrmask)
+ fatalerror("Can't set a global address mask of %08x on a %d-bits address width bus.\n", m_map->m_globalmask, addr_width());
+
m_addrmask = m_map->m_globalmask;
+ }
// make a pass over the address map, adjusting for the device and getting memory pointers
for (address_map_entry &entry : m_map->m_entrylist)
@@ -2097,10 +1194,6 @@ void address_space::prepare_map()
entry.m_memory = m_manager.machine().root_device().memregion(fulltag.c_str())->base() + entry.m_rgnoffs;
}
}
-
- // now loop over all the handlers and enforce the address mask
- read().mask_all_handlers(m_addrmask);
- write().mask_all_handlers(m_addrmask);
}
@@ -2121,13 +1214,14 @@ void address_space::populate_from_map(address_map *map)
return;
// install the handlers, using the original, unadjusted memory map
- for (const address_map_entry &entry : map->m_entrylist)
- {
+ for (const address_map_entry &entry : map->m_entrylist) {
// map both read and write halves
populate_map_entry(entry, read_or_write::READ);
populate_map_entry(entry, read_or_write::WRITE);
- populate_map_entry_setoffset(entry);
}
+
+ if(VALIDATE_REFCOUNTS)
+ validate_reference_counts();
}
@@ -2201,16 +1295,6 @@ void address_space::populate_map_entry(const address_map_entry &entry, read_or_w
}
//-------------------------------------------------
-// populate_map_entry_setoffset - special case for setoffset
-//-------------------------------------------------
-
-void address_space::populate_map_entry_setoffset(const address_map_entry &entry)
-{
- install_setoffset_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask,
- entry.m_addrmirror, entry.m_addrselect, setoffset_delegate(entry.m_soproto, entry.m_devbase), entry.m_mask);
-}
-
-//-------------------------------------------------
// allocate_memory - determine all neighboring
// address ranges and allocate memory to back
// them
@@ -2301,10 +1385,6 @@ void address_space::locate_memory()
VPRINTF(("assigned bank '%s' pointer to memory from range %08X-%08X [%p]\n", bank.second->tag(), entry.m_addrstart, entry.m_addrend, entry.m_memory));
break;
}
-
- // if the entry was set ahead of time, override the automatically found pointer
- if (!bank.second->anonymous() && bank.second->entry() != BANK_ENTRY_UNSPECIFIED)
- bank.second->set_entry(bank.second->entry());
}
}
@@ -2367,47 +1447,96 @@ address_map_entry *address_space::block_assign_intersecting(offs_t addrstart, of
}
-//-------------------------------------------------
-// get_handler_string - return a string
-// describing the handler at a particular offset
-//-------------------------------------------------
+memory_passthrough_handler *address_space::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph)
+{
+ fatalerror("Trying to install a 8-bits wide bus read tap in a %d-bits wide bus\n", data_width());
+}
-const char *address_space::get_handler_string(read_or_write readorwrite, offs_t address)
+memory_passthrough_handler *address_space::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph)
{
- if (readorwrite == read_or_write::READ)
- return read().handler_name(read().lookup(address));
- else
- return write().handler_name(write().lookup(address));
+ fatalerror("Trying to install a 16-bits wide bus read tap in a %d-bits wide bus\n", data_width());
}
+memory_passthrough_handler *address_space::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph)
+{
+ fatalerror("Trying to install a 32-bits wide bus read tap in a %d-bits wide bus\n", data_width());
+}
-//-------------------------------------------------
-// dump_map - dump the contents of a single
-// address space
-//-------------------------------------------------
+memory_passthrough_handler *address_space::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph)
+{
+ fatalerror("Trying to install a 64-bits wide bus read tap in a %d-bits wide bus\n", data_width());
+}
+
+memory_passthrough_handler *address_space::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph)
+{
+ fatalerror("Trying to install a 8-bits wide bus write tap in a %d-bits wide bus\n", data_width());
+}
-void address_space::dump_map(FILE *file, read_or_write readorwrite)
+memory_passthrough_handler *address_space::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph)
{
- const address_table &table = (readorwrite == read_or_write::READ) ? static_cast<address_table &>(read()) : static_cast<address_table &>(write());
+ fatalerror("Trying to install a 16-bits wide bus write tap in a %d-bits wide bus\n", data_width());
+}
- // dump generic information
- fprintf(file, " Address bits = %d\n", m_config.m_addr_width);
- fprintf(file, " Data bits = %d\n", m_config.m_data_width);
- fprintf(file, " Address mask = %X\n", m_addrmask);
- fprintf(file, "\n");
+memory_passthrough_handler *address_space::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph)
+{
+ fatalerror("Trying to install a 32-bits wide bus write tap in a %d-bits wide bus\n", data_width());
+}
- // iterate over addresses
- offs_t addrstart, addrend;
- for (offs_t address = 0; address <= m_addrmask; address = addrend)
- {
- u16 entry = table.derive_range(address, addrstart, addrend);
- fprintf(file, "%08X-%08X = %02X: %s [offset=%08X]\n",
- addrstart, addrend, entry, table.handler_name(entry), table.handler(entry).addrstart());
- if (++addrend == 0)
- break;
+memory_passthrough_handler *address_space::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph)
+{
+ fatalerror("Trying to install a 64-bits wide bus write tap in a %d-bits wide bus\n", data_width());
+}
+
+memory_passthrough_handler *address_space::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapr, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapw, memory_passthrough_handler *mph)
+{
+ fatalerror("Trying to install a 8-bits wide bus read/write tap in a %d-bits wide bus\n", data_width());
+}
+
+memory_passthrough_handler *address_space::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapr, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapw, memory_passthrough_handler *mph)
+{
+ fatalerror("Trying to install a 16-bits wide bus read/write tap in a %d-bits wide bus\n", data_width());
+}
+
+memory_passthrough_handler *address_space::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapr, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph)
+{
+ fatalerror("Tryingw to install a 32-bits wide bus read/write tap in a %d-bits wide bus\n", data_width());
+}
+
+memory_passthrough_handler *address_space::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapr, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapw, memory_passthrough_handler *mph)
+{
+ fatalerror("Trying to install a 64-bits wide bus read/write tap in a %d-bits wide bus\n", data_width());
+}
+
+
+
+//-------------------------------------------------
+// get_handler_string - return a string
+// describing the handler at a particular offset
+//-------------------------------------------------
+
+template<int Width, int AddrShift, endianness_t Endian> std::string address_space_specific<Width, AddrShift, Endian>::get_handler_string(read_or_write readorwrite, offs_t address) const
+{
+ if (readorwrite == read_or_write::READ) {
+ offs_t start, end;
+ handler_entry_read<Width, AddrShift, Endian> *handler;
+ m_root_read->lookup(address, start, end, handler);
+ return handler->name();
+ } else {
+ offs_t start, end;
+ handler_entry_write<Width, AddrShift, Endian> *handler;
+ m_root_write->lookup(address, start, end, handler);
+ return handler->name();
}
}
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::dump_maps(std::vector<memory_entry> &read_map, std::vector<memory_entry> &write_map) const
+{
+ read_map.clear();
+ write_map.clear();
+ m_root_read->dump_map(read_map);
+ m_root_write->dump_map(write_map);
+}
+
//**************************************************************************
// DYNAMIC ADDRESS SPACE MAPPING
@@ -2417,7 +1546,7 @@ void address_space::dump_map(FILE *file, read_or_write readorwrite)
// unmap - unmap a section of address space
//-------------------------------------------------
-void address_space::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet)
{
VPRINTF(("address_space::unmap(%s-%s mirror=%s, %s, %s)\n",
core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
@@ -2429,21 +1558,101 @@ void address_space::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrm
check_optimize_mirror("unmap_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror);
// read space
- if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE)
- read().map_range(nstart, nend, nmask, nmirror, quiet ? STATIC_NOP : STATIC_UNMAP);
+ if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE) {
+ auto handler = static_cast<handler_entry_read<Width, AddrShift, Endian> *>(quiet ? m_nop_r : m_unmap_r);
+ handler->ref();
+ m_root_read->populate(nstart, nend, nmirror, handler);
+ }
// write space
- if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE)
- write().map_range(nstart, nend, nmask, nmirror, quiet ? STATIC_NOP : STATIC_UNMAP);
+ if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) {
+ auto handler = static_cast<handler_entry_write<Width, AddrShift, Endian> *>(quiet ? m_nop_w : m_unmap_w);
+ handler->ref();
+ m_root_write->populate(nstart, nend, nmirror, handler);
+ }
+
+ invalidate_caches(readorwrite);
+}
+
+//-------------------------------------------------
+// install_read_tap - install a read tap on the bus
+//-------------------------------------------------
+
+template<int Width, int AddrShift, endianness_t Endian> memory_passthrough_handler *address_space_specific<Width, AddrShift, Endian>::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph)
+{
+ offs_t nstart, nend, nmask, nmirror;
+ check_optimize_mirror("install_read_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror);
+ if(!mph) {
+ m_mphs.emplace_back(std::make_unique<memory_passthrough_handler>(*this));
+ mph = m_mphs.back().get();
+ }
+
+ auto handler = new handler_entry_read_tap<Width, AddrShift, Endian>(this, *mph, name, tap);
+ m_root_read->populate_passthrough(nstart, nend, nmirror, handler);
+ handler->unref();
+
+ invalidate_caches(read_or_write::READ);
+
+ return mph;
+}
+
+//-------------------------------------------------
+// install_write_tap - install a write tap on the bus
+//-------------------------------------------------
+
+template<int Width, int AddrShift, endianness_t Endian> memory_passthrough_handler *address_space_specific<Width, AddrShift, Endian>::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph)
+{
+ offs_t nstart, nend, nmask, nmirror;
+ check_optimize_mirror("install_write_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror);
+ if(!mph) {
+ m_mphs.emplace_back(std::make_unique<memory_passthrough_handler>(*this));
+ mph = m_mphs.back().get();
+ }
+
+ auto handler = new handler_entry_write_tap<Width, AddrShift, Endian>(this, *mph, name, tap);
+ m_root_write->populate_passthrough(nstart, nend, nmirror, handler);
+ handler->unref();
+
+ invalidate_caches(read_or_write::WRITE);
+
+ return mph;
+}
+//-------------------------------------------------
+// install_write_tap - install a read and a write tap on the bus
+//-------------------------------------------------
+
+template<int Width, int AddrShift, endianness_t Endian> memory_passthrough_handler *address_space_specific<Width, AddrShift, Endian>::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapr, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapw, memory_passthrough_handler *mph)
+{
+ offs_t nstart, nend, nmask, nmirror;
+ check_optimize_mirror("install_readwrite_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror);
+ if(!mph) {
+ m_mphs.emplace_back(std::make_unique<memory_passthrough_handler>(*this));
+ mph = m_mphs.back().get();
+ }
+
+ auto rhandler = new handler_entry_read_tap <Width, AddrShift, Endian>(this, *mph, name, tapr);
+ m_root_read ->populate_passthrough(nstart, nend, nmirror, rhandler);
+ rhandler->unref();
+
+ auto whandler = new handler_entry_write_tap<Width, AddrShift, Endian>(this, *mph, name, tapw);
+ m_root_write->populate_passthrough(nstart, nend, nmirror, whandler);
+ whandler->unref();
+
+ invalidate_caches(read_or_write::READWRITE);
+
+ return mph;
}
+
+
+
//-------------------------------------------------
// install_device_delegate - install the memory map
// of a live device into this address space
//-------------------------------------------------
-void address_space::install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &delegate, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &delegate, u64 unitmask, int cswidth)
{
check_address("install_device_delegate", addrstart, addrend);
address_map map(*this, addrstart, addrend, unitmask, cswidth, m_device, delegate);
@@ -2458,7 +1667,7 @@ void address_space::install_device_delegate(offs_t addrstart, offs_t addrend, de
// handler into this address space
//-------------------------------------------------
-void address_space::install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag)
{
VPRINTF(("address_space::install_readwrite_port(%s-%s mirror=%s, read=\"%s\" / write=\"%s\")\n",
core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
@@ -2477,7 +1686,8 @@ void address_space::install_readwrite_port(offs_t addrstart, offs_t addrend, off
throw emu_fatalerror("Attempted to map non-existent port '%s' for read in space %s of device '%s'\n", rtag, m_name, m_device.tag());
// map the range and set the ioport
- read().handler_map_range(nstart, nend, nmask, nmirror).set_ioport(*port);
+ auto hand_r = new handler_entry_read_ioport<Width, AddrShift, Endian>(this, port);
+ m_root_read->populate(nstart, nend, nmirror, hand_r);
}
if (wtag != nullptr)
@@ -2488,11 +1698,11 @@ void address_space::install_readwrite_port(offs_t addrstart, offs_t addrend, off
fatalerror("Attempted to map non-existent port '%s' for write in space %s of device '%s'\n", wtag, m_name, m_device.tag());
// map the range and set the ioport
- write().handler_map_range(nstart, nend, nmask, nmirror).set_ioport(*port);
+ auto hand_w = new handler_entry_write_ioport<Width, AddrShift, Endian>(this, port);
+ m_root_write->populate(nstart, nend, nmirror, hand_w);
}
- // update the memory dump
- generate_memdump(m_manager.machine());
+ invalidate_caches(rtag ? wtag ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE);
}
@@ -2501,7 +1711,7 @@ void address_space::install_readwrite_port(offs_t addrstart, offs_t addrend, off
// mapping to a particular bank
//-------------------------------------------------
-void address_space::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag)
{
VPRINTF(("address_space::install_readwrite_bank(%s-%s mirror=%s, read=\"%s\" / write=\"%s\")\n",
core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
@@ -2516,7 +1726,10 @@ void address_space::install_bank_generic(offs_t addrstart, offs_t addrend, offs_
{
std::string fulltag = device().siblingtag(rtag);
memory_bank &bank = bank_find_or_allocate(fulltag.c_str(), addrstart, addrend, addrmirror, read_or_write::READ);
- read().map_range(nstart, nend, nmask, nmirror, bank.index());
+
+ auto hand_r = new handler_entry_read_memory_bank<Width, AddrShift, Endian>(this, bank);
+ hand_r->set_address_info(nstart, nmask);
+ m_root_read->populate(nstart, nend, nmirror, hand_r);
}
// map the write bank
@@ -2524,15 +1737,17 @@ void address_space::install_bank_generic(offs_t addrstart, offs_t addrend, offs_
{
std::string fulltag = device().siblingtag(wtag);
memory_bank &bank = bank_find_or_allocate(fulltag.c_str(), addrstart, addrend, addrmirror, read_or_write::WRITE);
- write().map_range(nstart, nend, nmask, nmirror, bank.index());
+
+ auto hand_w = new handler_entry_write_memory_bank<Width, AddrShift, Endian>(this, bank);
+ hand_w->set_address_info(nstart, nmask);
+ m_root_write->populate(nstart, nend, nmirror, hand_w);
}
- // update the memory dump
- generate_memdump(m_manager.machine());
+ invalidate_caches(rtag ? wtag ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE);
}
-void address_space::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank)
{
VPRINTF(("address_space::install_readwrite_bank(%s-%s mirror=%s, read=\"%s\" / write=\"%s\")\n",
core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
@@ -2545,17 +1760,20 @@ void address_space::install_bank_generic(offs_t addrstart, offs_t addrend, offs_
// map the read bank
if (rbank != nullptr)
{
- read().map_range(nstart, nend, nmask, nmirror, rbank->index());
+ auto hand_r = new handler_entry_read_memory_bank<Width, AddrShift, Endian>(this, *rbank);
+ hand_r->set_address_info(nstart, nmask);
+ m_root_read->populate(nstart, nend, nmirror, hand_r);
}
// map the write bank
if (wbank != nullptr)
{
- write().map_range(nstart, nend, nmask, nmirror, wbank->index());
+ auto hand_w = new handler_entry_write_memory_bank<Width, AddrShift, Endian>(this, *wbank);
+ hand_w->set_address_info(nstart, nmask);
+ m_root_write->populate(nstart, nend, nmirror, hand_w);
}
- // update the memory dump
- generate_memdump(m_manager.machine());
+ invalidate_caches(rbank ? wbank ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE);
}
@@ -2564,7 +1782,7 @@ void address_space::install_bank_generic(offs_t addrstart, offs_t addrend, offs_
// RAM region into the given address space
//-------------------------------------------------
-void address_space::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr)
{
VPRINTF(("address_space::install_ram_generic(%s-%s mirror=%s, %s, %p)\n",
core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
@@ -2580,7 +1798,6 @@ void address_space::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t
{
// find a bank and map it
memory_bank &bank = bank_find_or_allocate(nullptr, addrstart, addrend, addrmirror, read_or_write::READ);
- read().map_range(nstart, nend, nmask, nmirror, bank.index());
// if we are provided a pointer, set it
if (baseptr != nullptr)
@@ -2603,6 +1820,17 @@ void address_space::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t
bank.set_base(block.get()->data());
m_manager.m_blocklist.push_back(std::move(block));
}
+
+ auto hand_r = new handler_entry_read_memory<Width, AddrShift, Endian>(this);
+ if (bank.base())
+ hand_r->set_base(static_cast<uX *>(bank.base()));
+ else {
+ delayed_ref(hand_r);
+ bank.add_notifier([this, hand_r](void *base) { hand_r->set_base(static_cast<uX *>(base)); delayed_unref(hand_r); });
+ }
+
+ hand_r->set_address_info(nstart, nmask);
+ m_root_read->populate(nstart, nend, nmirror, hand_r);
}
// map for write
@@ -2610,7 +1838,6 @@ void address_space::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t
{
// find a bank and map it
memory_bank &bank = bank_find_or_allocate(nullptr, addrstart, addrend, addrmirror, read_or_write::WRITE);
- write().map_range(nstart, nend, nmask, nmirror, bank.index());
// if we are provided a pointer, set it
if (baseptr != nullptr)
@@ -2633,7 +1860,20 @@ void address_space::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t
bank.set_base(block.get()->data());
m_manager.m_blocklist.push_back(std::move(block));
}
+
+ auto hand_w = new handler_entry_write_memory<Width, AddrShift, Endian>(this);
+ if (bank.base())
+ hand_w->set_base(static_cast<uX *>(bank.base()));
+ else {
+ delayed_ref(hand_w);
+ bank.add_notifier([this, hand_w](void *base) { hand_w->set_base(static_cast<uX *>(base)); delayed_unref(hand_w); });
+ }
+
+ hand_w->set_address_info(nstart, nmask);
+ m_root_write->populate(nstart, nend, nmirror, hand_w);
}
+
+ invalidate_caches(readorwrite);
}
@@ -2642,42 +1882,19 @@ void address_space::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t
// delegate handlers for the space
//-------------------------------------------------
-void address_space::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate handler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate handler, u64 unitmask, int cswidth)
{
- VPRINTF(("address_space::install_read_handler(%s-%s mask=%s mirror=%s, %s, %s)\n",
- core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
- core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars),
- handler.name(), core_i64_hex_format(unitmask, data_width() / 4)));
-
- offs_t nstart, nend, nmask, nmirror;
- u64 nunitmask;
- int ncswidth;
- check_optimize_all("install_read_handler", 8, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
-
- read().handler_map_range(nstart, nend, nmask, nmirror, nunitmask, ncswidth).set_delegate(handler);
- generate_memdump(m_manager.machine());
+ install_read_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler);
}
-void address_space::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate handler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate handler, u64 unitmask, int cswidth)
{
- VPRINTF(("address_space::install_write_handler(%s-%s mask=%s mirror=%s, %s, %s)\n",
- core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
- core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars),
- handler.name(), core_i64_hex_format(unitmask, data_width() / 4)));
-
- offs_t nstart, nend, nmask, nmirror;
- u64 nunitmask;
- int ncswidth;
- check_optimize_all("install_write_handler", 8, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
-
- write().handler_map_range(nstart, nend, nmask, nmirror, nunitmask, ncswidth).set_delegate(handler);
- generate_memdump(m_manager.machine());
+ install_write_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler);
}
-void address_space::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask, int cswidth)
{
- install_read_handler(addrstart, addrend, addrmask, addrmirror, addrselect, rhandler, unitmask, cswidth);
- install_write_handler(addrstart, addrend, addrmask, addrmirror, addrselect, whandler, unitmask, cswidth);
+ install_readwrite_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler);
}
@@ -2686,30 +1903,19 @@ void address_space::install_readwrite_handler(offs_t addrstart, offs_t addrend,
// delegate handlers for the space
//-------------------------------------------------
-void address_space::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate handler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate handler, u64 unitmask, int cswidth)
{
- offs_t nstart, nend, nmask, nmirror;
- u64 nunitmask;
- int ncswidth;
- check_optimize_all("install_read_handler", 16, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
- read().handler_map_range(nstart, nend, nmask, nmirror, nunitmask, ncswidth).set_delegate(handler);
- generate_memdump(m_manager.machine());
+ install_read_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler);
}
-void address_space::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate handler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate handler, u64 unitmask, int cswidth)
{
- offs_t nstart, nend, nmask, nmirror;
- u64 nunitmask;
- int ncswidth;
- check_optimize_all("install_write_handler", 16, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
- write().handler_map_range(nstart, nend, nmask, nmirror, nunitmask, ncswidth).set_delegate(handler);
- generate_memdump(m_manager.machine());
+ install_write_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler);
}
-void address_space::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask, int cswidth)
{
- install_read_handler(addrstart, addrend, addrmask, addrmirror, addrselect, rhandler, unitmask, cswidth);
- install_write_handler(addrstart, addrend, addrmask, addrmirror, addrselect, whandler, unitmask, cswidth);
+ install_readwrite_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler);
}
@@ -2718,30 +1924,19 @@ void address_space::install_readwrite_handler(offs_t addrstart, offs_t addrend,
// delegate handlers for the space
//-------------------------------------------------
-void address_space::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate handler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate handler, u64 unitmask, int cswidth)
{
- offs_t nstart, nend, nmask, nmirror;
- u64 nunitmask;
- int ncswidth;
- check_optimize_all("install_read_handler", 32, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
- read().handler_map_range(nstart, nend, nmask, nmirror, nunitmask, ncswidth).set_delegate(handler);
- generate_memdump(m_manager.machine());
+ install_read_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler);
}
-void address_space::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate handler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate handler, u64 unitmask, int cswidth)
{
- offs_t nstart, nend, nmask, nmirror;
- u64 nunitmask;
- int ncswidth;
- check_optimize_all("install_write_handler", 32, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
- write().handler_map_range(nstart, nend, nmask, nmirror, nunitmask, ncswidth).set_delegate(handler);
- generate_memdump(m_manager.machine());
+ install_write_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler);
}
-void address_space::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask, int cswidth)
{
- install_read_handler(addrstart, addrend, addrmask, addrmirror, addrselect, rhandler, unitmask, cswidth);
- install_write_handler(addrstart, addrend, addrmask, addrmirror, addrselect, whandler, unitmask, cswidth);
+ install_readwrite_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler);
}
@@ -2750,49 +1945,19 @@ void address_space::install_readwrite_handler(offs_t addrstart, offs_t addrend,
// delegate handlers for the space
//-------------------------------------------------
-void address_space::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate handler, u64 unitmask, int cswidth)
-{
- offs_t nstart, nend, nmask, nmirror;
- u64 nunitmask;
- int ncswidth;
- check_optimize_all("install_read_handler", 64, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
- read().handler_map_range(nstart, nend, nmask, nmirror, nunitmask, ncswidth).set_delegate(handler);
- generate_memdump(m_manager.machine());
-}
-
-void address_space::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate handler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate handler, u64 unitmask, int cswidth)
{
- offs_t nstart, nend, nmask, nmirror;
- u64 nunitmask;
- int ncswidth;
- check_optimize_all("install_write_handler", 64, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
- write().handler_map_range(nstart, nend, nmask, nmirror, nunitmask, ncswidth).set_delegate(handler);
- generate_memdump(m_manager.machine());
+ install_read_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler);
}
-void address_space::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate handler, u64 unitmask, int cswidth)
{
- install_read_handler(addrstart, addrend, addrmask, addrmirror, addrselect, rhandler, unitmask, cswidth);
- install_write_handler(addrstart, addrend, addrmask, addrmirror, addrselect, whandler, unitmask, cswidth);
+ install_write_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler);
}
-
-//-----------------------------------------------------------------------
-// install_setoffset_handler - install set_offset delegate handlers for the space
-//-----------------------------------------------------------------------
-
-void address_space::install_setoffset_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, setoffset_delegate handler, u64 unitmask, int cswidth)
+template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask, int cswidth)
{
- VPRINTF(("address_space::install_setoffset_handler(%s-%s mask=%s mirror=%s, %s, %s)\n",
- core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars),
- core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars),
- handler.name(), core_i64_hex_format(unitmask, data_width() / 4)));
-
- offs_t nstart, nend, nmask, nmirror;
- u64 nunitmask;
- int ncswidth;
- check_optimize_all("install_setoffset_handler", 8, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth);
- setoffset().handler_map_range(nstart, nend, nmask, nmirror, nunitmask, ncswidth).set_delegate(handler);
+ install_readwrite_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler);
}
//**************************************************************************
@@ -2869,6 +2034,22 @@ bool address_space::needs_backing_store(const address_map_entry &entry)
return false;
}
+int address_space::add_change_notifier(std::function<void (read_or_write)> n)
+{
+ int id = m_notifier_id++;
+ m_notifiers.emplace_back(notifier_t{ n, id });
+ return id;
+}
+
+void address_space::remove_change_notifier(int id)
+{
+ for(auto i = m_notifiers.begin(); i != m_notifiers.end(); i++)
+ if(i->m_id == id) {
+ m_notifiers.erase(i);
+ return;
+ }
+ fatalerror("Unknown notifier id %d, double remove?\n", id);
+}
//**************************************************************************
@@ -2888,39 +2069,11 @@ memory_bank &address_space::bank_find_or_allocate(const char *tag, offs_t addrst
adjust_addresses(addrstart, addrend, addrmask, addrmirror);
// look up the bank by name, or else by byte range
- memory_bank *membank = nullptr;
- if (tag != nullptr) {
- auto bank = m_manager.banks().find(tag);
- if (bank != m_manager.banks().end())
- membank = bank->second.get();
- }
- else {
- membank = bank_find_anonymous(addrstart, addrend);
- }
+ memory_bank *membank = tag ? m_manager.find(tag) : m_manager.find(*this, addrstart, addrend);
- // if we don't have a bank yet, find a free one
- if (membank == nullptr)
- {
- // handle failure
- int banknum = m_manager.m_banknext++;
- if (banknum > STATIC_BANKMAX)
- {
- if (tag != nullptr)
- throw emu_fatalerror("Unable to allocate new bank '%s'", tag);
- else
- throw emu_fatalerror("Unable to allocate bank for RAM/ROM area %X-%X\n", addrstart, addrend);
- }
-
- // if no tag, create a unique one
- auto bank = std::make_unique<memory_bank>(*this, banknum, addrstart, addrend, tag);
- std::string temptag;
- if (tag == nullptr) {
- temptag = string_format("anon_%p", bank.get());
- tag = temptag.c_str();
- }
- m_manager.m_banklist.emplace(tag, std::move(bank));
- membank = m_manager.m_banklist.find(tag)->second.get();
- }
+ // if we don't have a bank yet, create a new one
+ if (!membank)
+ membank = m_manager.allocate(*this, addrstart, addrend, tag);
// add a reference for this space
membank->add_reference(*this, readorwrite);
@@ -2928,1079 +2081,6 @@ memory_bank &address_space::bank_find_or_allocate(const char *tag, offs_t addrst
}
-//-------------------------------------------------
-// bank_find_anonymous - try to find an anonymous
-// bank matching the given byte range
-//-------------------------------------------------
-memory_bank *address_space::bank_find_anonymous(offs_t addrstart, offs_t addrend) const
-{
- // try to find an exact match
- for (auto &bank : m_manager.banks())
- if (bank.second->anonymous() && bank.second->references_space(*this, read_or_write::READWRITE) && bank.second->matches_exactly(addrstart, addrend))
- return bank.second.get();
-
- // not found
- return nullptr;
-}
-
-//-------------------------------------------------
-// address_space::invalidate_read_caches -- clear
-// the read cache (cache) for a specific entry
-// or all of them
-//-------------------------------------------------
-
-void address_space::invalidate_read_caches()
-{
- m_cache_invalidate_read_caches(m_cache);
-}
-
-void address_space::invalidate_read_caches(u16 entry)
-{
- m_cache_invalidate_read_cache_entry(m_cache, entry);
-}
-
-void address_space::invalidate_read_caches(offs_t start, offs_t end)
-{
- m_cache_invalidate_read_cache_range(m_cache, start, end);
-}
-
-
-//**************************************************************************
-// TABLE MANAGEMENT
-//**************************************************************************
-
-//-------------------------------------------------
-// address_table - constructor
-//-------------------------------------------------
-
-address_table::address_table(address_space &space, bool large)
- : m_table(1 << LEVEL1_BITS),
- m_space(space),
- m_large(large),
- m_subtable(SUBTABLE_COUNT),
- m_subtable_alloc(0)
-{
- m_live_lookup = &m_table[0];
-
- // make our static table all watchpoints
- if (s_watchpoint_table[0] != STATIC_WATCHPOINT)
- for (unsigned int i=0; i != ARRAY_LENGTH(s_watchpoint_table); i++)
- s_watchpoint_table[i] = STATIC_WATCHPOINT;
-
- // initialize everything to unmapped
- for (unsigned int i=0; i != 1 << LEVEL1_BITS; i++)
- m_table[i] = STATIC_UNMAP;
-
- // initialize the handlers freelist
- for (int i=0; i != SUBTABLE_BASE-STATIC_COUNT-1; i++)
- handler_next_free[i] = i+STATIC_COUNT+1;
- handler_next_free[SUBTABLE_BASE-STATIC_COUNT-1] = STATIC_INVALID;
- handler_free = STATIC_COUNT;
-
- // initialize the handlers refcounts
- memset(handler_refcount, 0, sizeof(handler_refcount));
-}
-
-
-//-------------------------------------------------
-// ~address_table - destructor
-//-------------------------------------------------
-
-address_table::~address_table()
-{
-}
-
-
-//-------------------------------------------------
-// map_range - map a specific entry in the address
-// map
-//-------------------------------------------------
-
-void address_table::map_range(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u16 entry)
-{
- // convert addresses to bytes
- m_space.adjust_addresses(addrstart, addrend, addrmask, addrmirror);
-
- // validity checks
- assert_always(addrstart <= addrend, "address_table::map_range called with start greater than end");
- assert_always((addrstart & (m_space.alignment() - 1)) == 0, "address_table::map_range called with misaligned start address");
- assert_always((addrend & (m_space.alignment() - 1)) == (m_space.alignment() - 1), "address_table::map_range called with misaligned end address");
-
- // configure the entry to our parameters (but not for static non-banked cases)
- handler_entry &curentry = handler(entry);
- if (entry <= STATIC_BANKMAX || entry >= STATIC_COUNT)
- curentry.configure(addrstart, addrend, addrmask, m_space.address_to_byte_end(addrmask));
-
- // populate it
- populate_range_mirrored(addrstart, addrend, addrmirror, entry);
-
- // recompute any cache access on this space if it is a read modification
- m_space.invalidate_read_caches(entry);
-
- // verify_reference_counts();
-}
-
-u16 address_table::get_free_handler()
-{
- if (handler_free == STATIC_INVALID)
- throw emu_fatalerror("Out of handler entries in address table");
-
- u16 handler = handler_free;
- handler_free = handler_next_free[handler - STATIC_COUNT];
- return handler;
-}
-
-
-//-------------------------------------------------
-// setup_range - finds an appropriate handler entry
-// and requests to populate the address map with
-// it
-//-------------------------------------------------
-
-std::list<u32> address_table::setup_range(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u64 umask)
-{
- // Careful, you can't shift by 64 or more
- u64 testmask = (1ULL << (m_space.data_width()-1) << 1) - 1;
-
- if ((umask & testmask) == 0 || (umask & testmask) == testmask || range_simply_masks(addrstart, addrend, addrmask, addrmirror, umask))
- return setup_range_solid(addrstart, addrend, addrmask, addrmirror);
- else
- return setup_range_masked(addrstart, addrend, addrmask, addrmirror, umask);
-}
-
-//-------------------------------------------------
-// range_simply_masks - determine if a handler
-// can fully override part of one existing range
-// (speeds up mapping in heavily mirrored cases)
-//-------------------------------------------------
-
-bool address_table::range_simply_masks(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u64 umask)
-{
- // convert addresses to bytes
- m_space.adjust_addresses(addrstart, addrend, addrmask, addrmirror);
-
- // Validity checks
- assert_always(addrstart <= addrend, "address_table::setup_range called with start greater than end");
- assert_always((addrstart & (m_space.alignment() - 1)) == 0, "address_table::setup_range called with misaligned start address");
- assert_always((addrend & (m_space.alignment() - 1)) == (m_space.alignment() - 1), "address_table::setup_range called with misaligned end address");
-
- offs_t range_start, range_end;
- u16 entry = derive_range(addrstart, range_start, range_end);
-
- return range_end >= (addrend | addrmirror) && (entry < STATIC_COUNT || handler(entry).overriden_by_mask(umask));
-}
-
-//-------------------------------------------------
-// setup_range_solid - finds an appropriate handler
-// entry and requests to populate the address map with
-// it. Replace what's there.
-//-------------------------------------------------
-
-std::list<u32> address_table::setup_range_solid(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror)
-{
- std::list<u32> entries;
-
- // Grab a free entry
- u16 entry = get_free_handler();
-
- // Add it in the "to be setup" list
- entries.push_back(entry);
-
- // Configure and map it
- map_range(addrstart, addrend, addrmask, addrmirror, entry);
-
- return entries;
-}
-
-//-------------------------------------------------
-// setup_range_masked - finds an appropriate handler
-// entry and requests to populate the address map with
-// it. Handle non-overlapping subunits.
-//-------------------------------------------------
-
-namespace {
- struct subrange {
- offs_t start, end;
- subrange(offs_t _start, offs_t _end) : start(_start), end(_end) {}
- };
-}
-
-std::list<u32> address_table::setup_range_masked(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, u64 umask)
-{
- std::list<u32> entries;
-
- // convert addresses to bytes
- m_space.adjust_addresses(addrstart, addrend, addrmask, addrmirror);
-
- // Validity checks
- assert_always(addrstart <= addrend, "address_table::setup_range called with start greater than end");
- assert_always((addrstart & (m_space.alignment() - 1)) == 0, "address_table::setup_range called with misaligned start address");
- assert_always((addrend & (m_space.alignment() - 1)) == (m_space.alignment() - 1), "address_table::setup_range called with misaligned end address");
-
- offs_t bytemask = m_space.address_to_byte_end(addrmask);
-
- // Scan the memory to see what has to be done
- std::list<subrange> range_override;
- std::map<u16, std::list<subrange> > range_partial;
-
- offs_t base_mirror = 0;
- do
- {
- offs_t base_address = base_mirror | addrstart;
- offs_t end_address = base_mirror | addrend;
-
- do
- {
- offs_t range_start, range_end;
- u16 entry = derive_range(base_address, range_start, range_end);
- u32 stop_address = std::min(range_end, end_address);
-
- if (entry < STATIC_COUNT || handler(entry).overriden_by_mask(umask))
- range_override.push_back(subrange(base_address, stop_address));
- else
- range_partial[entry].push_back(subrange(base_address, stop_address));
-
- base_address = stop_address + 1;
- }
- while (base_address != end_address + 1);
-
- // Efficient method to go to the next range start given a mirroring mask
- base_mirror = (base_mirror + 1 + ~addrmirror) & addrmirror;
- }
- while (base_mirror);
-
- // Ranges in range_override must be plain replaced by the new handler
- if (!range_override.empty())
- {
- // Grab a free entry
- u16 entry = get_free_handler();
-
- // configure the entry to our parameters
- handler_entry &curentry = handler(entry);
- curentry.configure(addrstart, addrend, addrmask, bytemask);
-
- // Populate it wherever needed
- for (std::list<subrange>::const_iterator i = range_override.begin(); i != range_override.end(); ++i)
- populate_range(i->start, i->end, entry);
-
- // Add it in the "to be setup" list
- entries.push_back(entry);
-
- // recompute any cache access on this space if it is a read modification
- m_space.invalidate_read_caches(entry);
- }
-
- // Ranges in range_partial must be duplicated then partially changed
- if (!range_partial.empty())
- {
- for (std::map<u16, std::list<subrange> >::const_iterator i = range_partial.begin(); i != range_partial.end(); ++i)
- {
- // Theorically, if the handler to change matches the
- // characteristics of ours, we can directly change it. In
- // practice, it's more complex than that because the
- // mirroring is not saved, so we're not sure there aren't
- // mappings on the handler outside of the zones we're
- // supposed to change. So we won't do the obvious
- // optimization at this point.
-
- // Get the original handler
- handler_entry *base_entry = &handler(i->first);
-
- offs_t previous_bytemask = base_entry->bytemask();
-
- // Grab a new handler and copy it there
- u16 entry = get_free_handler();
- handler_entry &curentry = handler(entry);
- curentry.copy(base_entry);
-
- // Clear the colliding entries
- curentry.clear_conflicting_subunits(umask);
-
- // Reconfigure the base addresses
- curentry.configure(addrstart, addrend, addrmask, bytemask);
-
- // Populate it wherever needed
- for (const auto & elem : i->second)
- populate_range(elem.start, elem.end, entry);
-
- curentry.expand_bytemask(previous_bytemask);
-
- // Add it in the "to be setup" list
- entries.push_back(entry);
-
- // recompute any cache access on this space if it is a read modification
- m_space.invalidate_read_caches(entry);
- }
- }
-
- // verify_reference_counts();
- return entries;
-}
-
-
-//-------------------------------------------------
-// verify_reference_counts - check how much of a
-// hash we've made of things
-//-------------------------------------------------
-
-void address_table::verify_reference_counts()
-{
- int actual_refcounts[SUBTABLE_BASE-STATIC_COUNT];
- memset(actual_refcounts, 0, sizeof(actual_refcounts));
-
- bool subtable_seen[TOTAL_MEMORY_BANKS - SUBTABLE_BASE];
- memset(subtable_seen, 0, sizeof(subtable_seen));
-
- for (int level1 = 0; level1 != 1 << LEVEL1_BITS; level1++)
- {
- u16 l1_entry = m_table[level1];
- if (l1_entry >= SUBTABLE_BASE)
- {
- assert(m_large);
- if (subtable_seen[l1_entry - SUBTABLE_BASE])
- continue;
-
- subtable_seen[l1_entry - SUBTABLE_BASE] = true;
- const u16 *subtable = subtable_ptr(l1_entry);
- for (int level2 = 0; level2 != 1 << LEVEL2_BITS; level2++)
- {
- u16 l2_entry = subtable[level2];
- assert(l2_entry < SUBTABLE_BASE);
- if (l2_entry >= STATIC_COUNT)
- actual_refcounts[l2_entry - STATIC_COUNT]++;
- }
- }
- else if (l1_entry >= STATIC_COUNT)
- actual_refcounts[l1_entry - STATIC_COUNT]++;
- }
-
- if (memcmp(actual_refcounts, handler_refcount, sizeof(handler_refcount)))
- {
- osd_printf_error("Refcount failure:\n");
- for(int i = STATIC_COUNT; i != SUBTABLE_BASE; i++)
- osd_printf_error("%02x: %4x .. %4x\n", i, handler_refcount[i-STATIC_COUNT], actual_refcounts[i-STATIC_COUNT]);
- throw emu_fatalerror("memory.c: refcounts are fucked.\n");
- }
-}
-
-
-//-------------------------------------------------
-// populate_range - assign a memory handler to a
-// range of addresses
-//-------------------------------------------------
-
-void address_table::populate_range(offs_t addrstart, offs_t addrend, u16 handlerindex)
-{
- offs_t l2mask = (1 << level2_bits()) - 1;
- offs_t l1start = addrstart >> level2_bits();
- offs_t l2start = addrstart & l2mask;
- offs_t l1stop = addrend >> level2_bits();
- offs_t l2stop = addrend & l2mask;
-
- // sanity check
- if (addrstart > addrend)
- return;
-
- // handle the starting edge if it's not on a block boundary
- if (l2start != 0)
- {
- u16 *subtable = subtable_open(l1start);
-
- // if the start and stop end within the same block, handle that
- if (l1start == l1stop)
- {
- handler_ref(handlerindex, l2stop-l2start+1);
- for (int i = l2start; i <= l2stop; i++)
- {
- handler_unref(subtable[i]);
- subtable[i] = handlerindex;
- }
- subtable_close(l1start);
- return;
- }
-
- // otherwise, fill until the end
- handler_ref(handlerindex, l2mask - l2start + 1);
- for (int i = l2start; i <= l2mask; i++)
- {
- handler_unref(subtable[i]);
- subtable[i] = handlerindex;
- }
- subtable_close(l1start);
- if (l1start != (offs_t)~0)
- l1start++;
- }
-
- // handle the trailing edge if it's not on a block boundary
- if (l2stop != l2mask)
- {
- u16 *subtable = subtable_open(l1stop);
-
- // fill from the beginning
- handler_ref(handlerindex, l2stop+1);
- for (int i = 0; i <= l2stop; i++)
- {
- handler_unref(subtable[i]);
- subtable[i] = handlerindex;
- }
- subtable_close(l1stop);
-
- // if the start and stop end within the same block, handle that
- if (l1start == l1stop)
- return;
- if (l1stop != 0)
- l1stop--;
- }
-
- // now fill in the middle tables
- handler_ref(handlerindex, l1stop - l1start + 1);
- for (offs_t l1index = l1start; l1index <= l1stop; l1index++)
- {
- u16 subindex = m_table[l1index];
-
- // if we have a subtable here, release it
- if (subindex >= SUBTABLE_BASE)
- subtable_release(subindex);
- else
- handler_unref(subindex);
- m_table[l1index] = handlerindex;
- }
-}
-
-
-//-------------------------------------------------
-// populate_range_mirrored - assign a memory
-// handler to a range of addresses including
-// mirrors
-//-------------------------------------------------
-
-void address_table::populate_range_mirrored(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 handlerindex)
-{
- // determine the mirror bits
- offs_t lmirrorbits = 0;
- offs_t lmirrorbit[32];
- for (int bit = 0; bit < level2_bits(); bit++)
- if (addrmirror & (1 << bit))
- lmirrorbit[lmirrorbits++] = 1 << bit;
-
- offs_t hmirrorbits = 0;
- offs_t hmirrorbit[32];
- for (int bit = level2_bits(); bit < 32; bit++)
- if (addrmirror & (1 << bit))
- hmirrorbit[hmirrorbits++] = 1 << bit;
-
- // loop over mirrors in the level 2 table
- u16 prev_entry = STATIC_INVALID;
- int prev_index = 0;
- for (offs_t hmirrorcount = 0; hmirrorcount < (1 << hmirrorbits); hmirrorcount++)
- {
- // compute the base of this mirror
- offs_t hmirrorbase = 0;
- for (int bit = 0; bit < hmirrorbits; bit++)
- if (hmirrorcount & (1 << bit))
- hmirrorbase |= hmirrorbit[bit];
-
- // invalidate any intersecting cached ranges
- for (offs_t lmirrorcount = 0; lmirrorcount < (1 << lmirrorbits); lmirrorcount++)
- {
- // compute the base of this mirror
- offs_t lmirrorbase = hmirrorbase;
- for (int bit = 0; bit < lmirrorbits; bit++)
- if (lmirrorcount & (1 << bit))
- lmirrorbase |= lmirrorbit[bit];
- m_space.invalidate_read_caches(addrstart + lmirrorbase, addrend + lmirrorbase);
- }
-
- // if this is not our first time through, and the level 2 entry matches the previous
- // level 2 entry, just do a quick map and get out; note that this only works for entries
- // which don't span multiple level 1 table entries
- int cur_index = level1_index(addrstart + hmirrorbase);
- if (cur_index == level1_index(addrend + hmirrorbase))
- {
- if (hmirrorcount != 0 && prev_entry == m_table[cur_index])
- {
- VPRINTF(("Quick mapping subtable at %08X to match subtable at %08X\n", cur_index << level2_bits(), prev_index << level2_bits()));
-
- // release the subtable if the old value was a subtable
- if (m_table[cur_index] >= SUBTABLE_BASE)
- subtable_release(m_table[cur_index]);
- else
- handler_unref(m_table[cur_index]);
-
- // reallocate the subtable if the new value is a subtable
- if (m_table[prev_index] >= SUBTABLE_BASE)
- subtable_realloc(m_table[prev_index]);
- else
- handler_ref(m_table[prev_index], 1);
-
- // set the new value and short-circuit the mapping step
- m_table[cur_index] = m_table[prev_index];
- continue;
- }
- prev_index = cur_index;
- prev_entry = m_table[cur_index];
- }
-
- // loop over mirrors in the level 1 table
- for (offs_t lmirrorcount = 0; lmirrorcount < (1 << lmirrorbits); lmirrorcount++)
- {
- // compute the base of this mirror
- offs_t lmirrorbase = hmirrorbase;
- for (int bit = 0; bit < lmirrorbits; bit++)
- if (lmirrorcount & (1 << bit))
- lmirrorbase |= lmirrorbit[bit];
-
- // populate the tables
- populate_range(addrstart + lmirrorbase, addrend + lmirrorbase, handlerindex);
- }
- }
-}
-
-
-//-------------------------------------------------
-// derive_range - look up the entry for a memory
-// range, and then compute the extent of that
-// range based on the lookup tables
-//-------------------------------------------------
-
-u16 address_table::derive_range(offs_t address, offs_t &addrstart, offs_t &addrend) const
-{
- // look up the initial address to get the entry we care about
- u16 l1entry;
- u16 entry = l1entry = m_table[level1_index(address)];
- if (l1entry >= SUBTABLE_BASE)
- entry = m_table[level2_index(l1entry, address)];
-
- // use the addrmask of the entry to set minimum and maximum bounds
- offs_t minscan, maxscan;
- handler(entry).mirrored_start_end(address, minscan, maxscan);
-
- // first scan backwards to find the start address
- u16 curl1entry = l1entry;
- u16 curentry = entry;
- addrstart = address;
- while (1)
- {
- // if we need to scan the subtable, do it
- if (curentry != curl1entry)
- {
- u32 minindex = level2_index(curl1entry, 0);
- u32 index;
-
- // scan backwards from the current address, until the previous entry doesn't match
- for (index = level2_index(curl1entry, addrstart); index > minindex; index--, addrstart -= 1)
- if (m_table[index - 1] != entry)
- break;
-
- // if we didn't hit the beginning, then we're finished scanning
- if (index != minindex)
- break;
- }
-
- // move to the beginning of this L1 entry; stop at the minimum address
- addrstart &= ~((1 << level2_bits()) - 1);
- if (addrstart <= minscan)
- break;
-
- // look up the entry of the byte at the end of the previous L1 entry; if it doesn't match, stop
- curentry = curl1entry = m_table[level1_index(addrstart - 1)];
- if (curl1entry >= SUBTABLE_BASE)
- curentry = m_table[level2_index(curl1entry, addrstart - 1)];
- if (curentry != entry)
- break;
-
- // move into the previous entry and resume searching
- addrstart -= 1;
- }
-
- // then scan forwards to find the end address
- curl1entry = l1entry;
- curentry = entry;
- addrend = address;
- while (1)
- {
- // if we need to scan the subtable, do it
- if (curentry != curl1entry)
- {
- u32 maxindex = level2_index(curl1entry, ~0);
- u32 index;
-
- // scan forwards from the current address, until the next entry doesn't match
- for (index = level2_index(curl1entry, addrend); index < maxindex; index++, addrend += 1)
- if (m_table[index + 1] != entry)
- break;
-
- // if we didn't hit the end, then we're finished scanning
- if (index != maxindex)
- break;
- }
-
- // move to the end of this L1 entry; stop at the maximum address
- addrend |= (1 << level2_bits()) - 1;
- if (addrend >= maxscan)
- break;
-
- // look up the entry of the byte at the start of the next L1 entry; if it doesn't match, stop
- curentry = curl1entry = m_table[level1_index(addrend + 1)];
- if (curl1entry >= SUBTABLE_BASE)
- curentry = m_table[level2_index(curl1entry, addrend + 1)];
- if (curentry != entry)
- break;
-
- // move into the next entry and resume searching
- addrend += 1;
- }
-
- return entry;
-}
-
-
-//-------------------------------------------------
-// mask_all_handlers - apply a mask to all
-// address handlers
-//-------------------------------------------------
-
-void address_table::mask_all_handlers(offs_t mask)
-{
- // we don't loop over map entries because the mask applies to static handlers as well
- for (int entrynum = 0; entrynum < ENTRY_COUNT; entrynum++)
- handler(entrynum).apply_mask(mask);
-}
-
-
-
-//**************************************************************************
-// SUBTABLE MANAGEMENT
-//**************************************************************************
-
-//-------------------------------------------------
-// subtable_alloc - allocate a fresh subtable
-// and set its usecount to 1
-//-------------------------------------------------
-
-u16 address_table::subtable_alloc()
-{
- // loop
- while (1)
- {
- // find a subtable with a usecount of 0
- for (u16 subindex = 0; subindex < SUBTABLE_COUNT; subindex++)
- if (m_subtable[subindex].m_usecount == 0)
- {
- // if this is past our allocation budget, allocate some more
- if (subindex >= m_subtable_alloc)
- {
- m_subtable_alloc += SUBTABLE_ALLOC;
- u32 newsize = (1 << LEVEL1_BITS) + (m_subtable_alloc << level2_bits());
-
- bool was_live = (m_live_lookup == &m_table[0]);
- int oldsize = m_table.size();
- m_table.resize(newsize);
- memset(&m_table[oldsize], 0, (newsize-oldsize)*sizeof(m_table[0]));
- if (was_live)
- m_live_lookup = &m_table[0];
- }
- // bump the usecount and return
- m_subtable[subindex].m_usecount++;
- return subindex + SUBTABLE_BASE;
- }
-
- // merge any subtables we can
- if (!subtable_merge())
- fatalerror("Ran out of subtables!\n");
- }
-}
-
-
-//-------------------------------------------------
-// subtable_realloc - increment the usecount on
-// a subtable
-//-------------------------------------------------
-
-void address_table::subtable_realloc(u16 subentry)
-{
- u16 subindex = subentry - SUBTABLE_BASE;
-
- // sanity check
- if (m_subtable[subindex].m_usecount <= 0)
- fatalerror("Called subtable_realloc on a table with a usecount of 0\n");
-
- // increment the usecount
- m_subtable[subindex].m_usecount++;
-}
-
-
-//-------------------------------------------------
-// subtable_merge - merge any duplicate
-// subtables
-//-------------------------------------------------
-
-int address_table::subtable_merge()
-{
- int merged = 0;
- u16 subindex;
-
- VPRINTF(("Merging subtables....\n"));
-
- // okay, we failed; update all the checksums and merge tables
- for (subindex = 0; subindex < SUBTABLE_COUNT; subindex++)
- if (!m_subtable[subindex].m_checksum_valid && m_subtable[subindex].m_usecount != 0)
- {
- u32 *subtable = reinterpret_cast<u32 *>(subtable_ptr(subindex + SUBTABLE_BASE));
- u32 checksum = 0;
-
- // update the checksum
- for (int l2index = 0; l2index < (1 << level2_bits())/4; l2index++)
- checksum += subtable[l2index];
- m_subtable[subindex].m_checksum = checksum;
- m_subtable[subindex].m_checksum_valid = true;
- }
-
- // see if there's a matching checksum
- for (subindex = 0; subindex < SUBTABLE_COUNT; subindex++)
- if (m_subtable[subindex].m_usecount != 0)
- {
- u16 *subtable = subtable_ptr(subindex + SUBTABLE_BASE);
- u32 checksum = m_subtable[subindex].m_checksum;
- u16 sumindex;
-
- for (sumindex = subindex + 1; sumindex < SUBTABLE_COUNT; sumindex++)
- if (m_subtable[sumindex].m_usecount != 0 &&
- m_subtable[sumindex].m_checksum == checksum &&
- !memcmp(subtable, subtable_ptr(sumindex + SUBTABLE_BASE), 2*(1 << level2_bits())))
- {
- int l1index;
-
- VPRINTF(("Merging subtable %d and %d....\n", subindex, sumindex));
-
- // find all the entries in the L1 tables that pointed to the old one, and point them to the merged table
- for (l1index = 0; l1index <= (0xffffffffUL >> level2_bits()); l1index++)
- if (m_table[l1index] == sumindex + SUBTABLE_BASE)
- {
- subtable_release(sumindex + SUBTABLE_BASE);
- subtable_realloc(subindex + SUBTABLE_BASE);
- m_table[l1index] = subindex + SUBTABLE_BASE;
- merged++;
- }
- }
- }
-
- return merged;
-}
-
-
-//-------------------------------------------------
-// subtable_release - decrement the usecount on
-// a subtable and free it if we're done
-//-------------------------------------------------
-
-void address_table::subtable_release(u16 subentry)
-{
- u16 subindex = subentry - SUBTABLE_BASE;
- // sanity check
- if (m_subtable[subindex].m_usecount <= 0)
- fatalerror("Called subtable_release on a table with a usecount of 0\n");
-
- // decrement the usecount and clear the checksum if we're at 0
- // also unref the subhandlers
- m_subtable[subindex].m_usecount--;
- if (m_subtable[subindex].m_usecount == 0)
- {
- m_subtable[subindex].m_checksum = 0;
- u16 *subtable = subtable_ptr(subentry);
- for (int i = 0; i < (1 << LEVEL2_BITS); i++)
- handler_unref(subtable[i]);
- }
-}
-
-
-//-------------------------------------------------
-// subtable_open - gain access to a subtable for
-// modification
-//-------------------------------------------------
-
-u16 *address_table::subtable_open(offs_t l1index)
-{
- u16 subentry = m_table[l1index];
-
- // if we don't have a subtable yet, allocate a new one
- if (subentry < SUBTABLE_BASE)
- {
- int size = 1 << level2_bits();
- u16 newentry = subtable_alloc();
- handler_ref(subentry, size-1);
- u16 *subptr = subtable_ptr(newentry);
- for (int i=0; i<size; i++)
- subptr[i] = subentry;
- m_table[l1index] = newentry;
- u32 subkey = subentry + (subentry << 8) + (subentry << 16) + (subentry << 24);
- m_subtable[newentry - SUBTABLE_BASE].m_checksum = subkey * (((1 << level2_bits())/4));
- subentry = newentry;
- }
-
- // if we're sharing this subtable, we also need to allocate a fresh copy
- else if (m_subtable[subentry - SUBTABLE_BASE].m_usecount > 1)
- {
- u16 newentry = subtable_alloc();
-
- // allocate may cause some additional merging -- look up the subentry again
- // when we're done; it should still require a split
- subentry = m_table[l1index];
- assert(subentry >= SUBTABLE_BASE);
- assert(m_subtable[subentry - SUBTABLE_BASE].m_usecount > 1);
-
- int size = 1 << level2_bits();
- u16 *src = subtable_ptr(subentry);
- for(int i=0; i != size; i++)
- handler_ref(src[i], 1);
-
- memcpy(subtable_ptr(newentry), src, 2*size);
- subtable_release(subentry);
- m_table[l1index] = newentry;
- m_subtable[newentry - SUBTABLE_BASE].m_checksum = m_subtable[subentry - SUBTABLE_BASE].m_checksum;
- subentry = newentry;
- }
-
- // mark the table dirty
- m_subtable[subentry - SUBTABLE_BASE].m_checksum_valid = false;
-
- // return the pointer to the subtable
- return subtable_ptr(subentry);
-}
-
-
-//-------------------------------------------------
-// subtable_close - stop access to a subtable
-//-------------------------------------------------
-
-void address_table::subtable_close(offs_t l1index)
-{
- // defer any merging until we run out of tables
-}
-
-
-//-------------------------------------------------
-// handler_name - return friendly string
-// description of a handler
-//-------------------------------------------------
-
-const char *address_table::handler_name(u16 entry) const
-{
- // banks have names
- if (entry >= STATIC_BANK1 && entry <= STATIC_BANKMAX)
- for (auto &info : m_space.m_manager.banks())
- if (info.second->index() == entry)
- return info.second->name();
-
- // constant strings for static entries
- if (entry == STATIC_INVALID) return "invalid";
- if (entry == STATIC_NOP) return "nop";
- if (entry == STATIC_UNMAP) return "unmapped";
- if (entry == STATIC_WATCHPOINT) return "watchpoint";
-
- static char desc[4096];
- handler(entry).description(desc);
- if (desc[0])
- return desc;
- return "???";
-}
-
-
-//-------------------------------------------------
-// address_table_read - constructor
-//-------------------------------------------------
-
-address_table_read::address_table_read(address_space &space, bool large)
- : address_table(space, large)
-{
- // allocate handlers for each entry, prepopulating the bankptrs for banks
- for (int entrynum = 0; entrynum < ARRAY_LENGTH(m_handlers); entrynum++)
- {
- u8 **bankptr = (entrynum >= STATIC_BANK1 && entrynum <= STATIC_BANKMAX) ? space.m_manager.bank_pointer_addr(entrynum) : nullptr;
- m_handlers[entrynum] = std::make_unique<handler_entry_read>(space.data_width(), space.endianness(), bankptr);
- }
-
- // we have to allocate different object types based on the data bus width
- switch (space.data_width())
- {
- // 8-bit case
- case 8:
- m_handlers[STATIC_UNMAP]->set_delegate(read8_delegate(FUNC(address_table_read::unmap_r<u8>), this));
- m_handlers[STATIC_NOP]->set_delegate(read8_delegate(FUNC(address_table_read::nop_r<u8>), this));
- switch (space.addr_shift())
- {
- case 0: m_handlers[STATIC_WATCHPOINT]->set_delegate(read8_delegate (&address_table_read::watchpoint_r<u8, 0>, "watchpoint_r", this)); break;
- default: abort();
- }
- break;
-
- // 16-bit case
- case 16:
- m_handlers[STATIC_UNMAP]->set_delegate(read16_delegate(FUNC(address_table_read::unmap_r<u16>), this));
- m_handlers[STATIC_NOP]->set_delegate(read16_delegate(FUNC(address_table_read::nop_r<u16>), this));
- switch (space.addr_shift())
- {
- case -1: m_handlers[STATIC_WATCHPOINT]->set_delegate(read16_delegate(&address_table_read::watchpoint_r<u16, -1>, "watchpoint_r", this)); break;
- case 0: m_handlers[STATIC_WATCHPOINT]->set_delegate(read16_delegate(&address_table_read::watchpoint_r<u16, 0>, "watchpoint_r", this)); break;
- case 3: m_handlers[STATIC_WATCHPOINT]->set_delegate(read16_delegate(&address_table_read::watchpoint_r<u16, 3>, "watchpoint_r", this)); break;
- default: abort();
- }
- break;
-
- // 32-bit case
- case 32:
- m_handlers[STATIC_UNMAP]->set_delegate(read32_delegate(FUNC(address_table_read::unmap_r<u32>), this));
- m_handlers[STATIC_NOP]->set_delegate(read32_delegate(FUNC(address_table_read::nop_r<u32>), this));
- switch (space.addr_shift())
- {
- case -2: m_handlers[STATIC_WATCHPOINT]->set_delegate(read32_delegate(&address_table_read::watchpoint_r<u32, -2>, "watchpoint_r", this)); break;
- case -1: m_handlers[STATIC_WATCHPOINT]->set_delegate(read32_delegate(&address_table_read::watchpoint_r<u32, -1>, "watchpoint_r", this)); break;
- case 0: m_handlers[STATIC_WATCHPOINT]->set_delegate(read32_delegate(&address_table_read::watchpoint_r<u32, 0>, "watchpoint_r", this)); break;
- default: abort();
- }
- break;
-
- // 64-bit case
- case 64:
- m_handlers[STATIC_UNMAP]->set_delegate(read64_delegate(FUNC(address_table_read::unmap_r<u64>), this));
- m_handlers[STATIC_NOP]->set_delegate(read64_delegate(FUNC(address_table_read::nop_r<u64>), this));
- switch (space.addr_shift())
- {
- case -3: m_handlers[STATIC_WATCHPOINT]->set_delegate(read64_delegate(&address_table_read::watchpoint_r<u64, -3>, "watchpoint_r", this)); break;
- case -2: m_handlers[STATIC_WATCHPOINT]->set_delegate(read64_delegate(&address_table_read::watchpoint_r<u64, -2>, "watchpoint_r", this)); break;
- case -1: m_handlers[STATIC_WATCHPOINT]->set_delegate(read64_delegate(&address_table_read::watchpoint_r<u64, -1>, "watchpoint_r", this)); break;
- case 0: m_handlers[STATIC_WATCHPOINT]->set_delegate(read64_delegate(&address_table_read::watchpoint_r<u64, 0>, "watchpoint_r", this)); break;
- default: abort();
- }
- break;
- }
-
- // reset the byte masks on the special handlers to open up the full address space for proper reporting
- m_handlers[STATIC_UNMAP]->configure(0, space.addrmask(), ~0, ~0);
- m_handlers[STATIC_NOP]->configure(0, space.addrmask(), ~0, ~0);
- m_handlers[STATIC_WATCHPOINT]->configure(0, space.addrmask(), ~0, ~0);
-}
-
-
-//-------------------------------------------------
-// address_table_read - destructor
-//-------------------------------------------------
-
-address_table_read::~address_table_read()
-{
-}
-
-
-//-------------------------------------------------
-// handler - return the generic handler entry for
-// this index
-//-------------------------------------------------
-
-handler_entry &address_table_read::handler(u32 index) const
-{
- assert(index < ARRAY_LENGTH(m_handlers));
- return *m_handlers[index];
-}
-
-
-//-------------------------------------------------
-// address_table_write - constructor
-//-------------------------------------------------
-
-address_table_write::address_table_write(address_space &space, bool large)
- : address_table(space, large)
-{
- // allocate handlers for each entry, prepopulating the bankptrs for banks
- for (int entrynum = 0; entrynum < ARRAY_LENGTH(m_handlers); entrynum++)
- {
- u8 **bankptr = (entrynum >= STATIC_BANK1 && entrynum <= STATIC_BANKMAX) ? space.m_manager.bank_pointer_addr(entrynum) : nullptr;
- m_handlers[entrynum] = std::make_unique<handler_entry_write>(space.data_width(), space.endianness(), bankptr);
- }
-
- // we have to allocate different object types based on the data bus width
- switch (space.data_width())
- {
- // 8-bit case
- case 8:
- m_handlers[STATIC_UNMAP]->set_delegate(write8_delegate(FUNC(address_table_write::unmap_w<u8>), this));
- m_handlers[STATIC_NOP]->set_delegate(write8_delegate(FUNC(address_table_write::nop_w<u8>), this));
- switch (space.addr_shift())
- {
- case 0: m_handlers[STATIC_WATCHPOINT]->set_delegate(write8_delegate (&address_table_write::watchpoint_w<u8, 0>, "watchpoint_w", this)); break;
- default: abort();
- }
- break;
-
- // 16-bit case
- case 16:
- m_handlers[STATIC_UNMAP]->set_delegate(write16_delegate(FUNC(address_table_write::unmap_w<u16>), this));
- m_handlers[STATIC_NOP]->set_delegate(write16_delegate(FUNC(address_table_write::nop_w<u16>), this));
- switch (space.addr_shift())
- {
- case -1: m_handlers[STATIC_WATCHPOINT]->set_delegate(write16_delegate(&address_table_write::watchpoint_w<u16, -1>, "watchpoint_w", this)); break;
- case 0: m_handlers[STATIC_WATCHPOINT]->set_delegate(write16_delegate(&address_table_write::watchpoint_w<u16, 0>, "watchpoint_w", this)); break;
- case 3: m_handlers[STATIC_WATCHPOINT]->set_delegate(write16_delegate(&address_table_write::watchpoint_w<u16, 3>, "watchpoint_w", this)); break;
- default: abort();
- }
- break;
-
- // 32-bit case
- case 32:
- m_handlers[STATIC_UNMAP]->set_delegate(write32_delegate(FUNC(address_table_write::unmap_w<u32>), this));
- m_handlers[STATIC_NOP]->set_delegate(write32_delegate(FUNC(address_table_write::nop_w<u32>), this));
- switch (space.addr_shift())
- {
- case -2: m_handlers[STATIC_WATCHPOINT]->set_delegate(write32_delegate(&address_table_write::watchpoint_w<u32, -2>, "watchpoint_w", this)); break;
- case -1: m_handlers[STATIC_WATCHPOINT]->set_delegate(write32_delegate(&address_table_write::watchpoint_w<u32, -1>, "watchpoint_w", this)); break;
- case 0: m_handlers[STATIC_WATCHPOINT]->set_delegate(write32_delegate(&address_table_write::watchpoint_w<u32, 0>, "watchpoint_w", this)); break;
- default: abort();
- }
- break;
-
- // 64-bit case
- case 64:
- m_handlers[STATIC_UNMAP]->set_delegate(write64_delegate(FUNC(address_table_write::unmap_w<u64>), this));
- m_handlers[STATIC_NOP]->set_delegate(write64_delegate(FUNC(address_table_write::nop_w<u64>), this));
- switch (space.addr_shift())
- {
- case -3: m_handlers[STATIC_WATCHPOINT]->set_delegate(write64_delegate(&address_table_write::watchpoint_w<u64, -3>, "watchpoint_w", this)); break;
- case -2: m_handlers[STATIC_WATCHPOINT]->set_delegate(write64_delegate(&address_table_write::watchpoint_w<u64, -2>, "watchpoint_w", this)); break;
- case -1: m_handlers[STATIC_WATCHPOINT]->set_delegate(write64_delegate(&address_table_write::watchpoint_w<u64, -1>, "watchpoint_w", this)); break;
- case 0: m_handlers[STATIC_WATCHPOINT]->set_delegate(write64_delegate(&address_table_write::watchpoint_w<u64, 0>, "watchpoint_w", this)); break;
- default: abort();
- }
- break;
- }
-
- // reset the byte masks on the special handlers to open up the full address space for proper reporting
- m_handlers[STATIC_UNMAP]->configure(0, space.addrmask(), ~0, ~0);
- m_handlers[STATIC_NOP]->configure(0, space.addrmask(), ~0, ~0);
- m_handlers[STATIC_WATCHPOINT]->configure(0, space.addrmask(), ~0, ~0);
-}
-
-
-//-------------------------------------------------
-// address_table_write - destructor
-//-------------------------------------------------
-
-address_table_write::~address_table_write()
-{
-}
-
-
-//-------------------------------------------------
-// handler - return the generic handler entry for
-// this index
-//-------------------------------------------------
-
-handler_entry &address_table_write::handler(u32 index) const
-{
- assert(index < ARRAY_LENGTH(m_handlers));
- return *m_handlers[index];
-}
-
-
-
//**************************************************************************
// CACHE MEMORY RANGES
//**************************************************************************
@@ -4009,14 +2089,32 @@ handler_entry &address_table_write::handler(u32 index) const
// memory_access_cache - constructor
//-------------------------------------------------
-template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrShift, Endian>::memory_access_cache(address_space &space)
+template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrShift, Endian>::memory_access_cache(address_space &space,
+ handler_entry_read <Width, AddrShift, Endian> *root_read,
+ handler_entry_write<Width, AddrShift, Endian> *root_write)
: m_space(space),
- m_ptr(nullptr),
- m_addrmask(space.addrmask()),
- m_addrstart(1),
- m_addrend(0),
- m_entry(STATIC_UNMAP)
-{
+ m_addrmask(space.addrmask()),
+ m_addrstart_r(1),
+ m_addrend_r(0),
+ m_addrstart_w(1),
+ m_addrend_w(0),
+ m_cache_r(nullptr),
+ m_cache_w(nullptr),
+ m_root_read(root_read),
+ m_root_write(root_write)
+{
+ m_notifier_id = space.add_change_notifier([this](read_or_write mode) {
+ if(u32(mode) & u32(read_or_write::READ)) {
+ m_addrend_r = 0;
+ m_addrstart_r = 1;
+ m_cache_r = nullptr;
+ }
+ if(u32(mode) & u32(read_or_write::WRITE)) {
+ m_addrend_w = 0;
+ m_addrstart_w = 1;
+ m_cache_w = nullptr;
+ }
+ });
}
@@ -4026,93 +2124,10 @@ template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrSh
template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrShift, Endian>::~memory_access_cache()
{
+ m_space.remove_change_notifier(m_notifier_id);
}
-//-------------------------------------------------
-// set_cache_region - called by device cores to
-// update the opcode base for the given address
-//-------------------------------------------------
-
-template<int Width, int AddrShift, int Endian> bool memory_access_cache<Width, AddrShift, Endian>::set_cache_region(offs_t address)
-{
- // find or allocate a matching range
- cache_range *range = find_range(address, m_entry);
-
- // if we don't map to a bank, return false
- if (m_entry < STATIC_BANK1 || m_entry > STATIC_BANKMAX)
- {
- // ensure future updates to land here as well until we get back into a bank
- m_addrend = 0;
- m_addrstart = 1;
- return false;
- }
-
- u8 *base = *m_space.m_manager.bank_pointer_addr(m_entry);
-
- // compute the adjusted base
- offs_t maskedbits = address & ~m_space.addrmask();
- const handler_entry_read &handler = m_space.read().handler_read(m_entry);
- m_addrmask = handler.addrmask();
- u32 delta = handler.addrstart() & m_addrmask;
- if(AddrShift < 0)
- delta = delta << iabs(AddrShift);
- else if(AddrShift > 0)
- delta = delta >> iabs(AddrShift);
-
- m_ptr = base - delta;
- m_addrstart = maskedbits | range->m_addrstart;
- m_addrend = maskedbits | range->m_addrend;
- return true;
-}
-
-
-//-------------------------------------------------
-// find_range - find a byte address in a range
-//-------------------------------------------------
-
-template<int Width, int AddrShift, int Endian> typename memory_access_cache<Width, AddrShift, Endian>::cache_range *memory_access_cache<Width, AddrShift, Endian>::find_range(offs_t address, u16 &entry)
-{
- // determine which entry
- address &= m_space.m_addrmask;
- entry = m_space.read().lookup_live_nowp(address);
-
- // scan our table
- for (auto &range : m_rangelist[entry])
- if (address >= range.m_addrstart && address <= range.m_addrend)
- return &range;
-
- // didn't find out; create a new one
- cache_range range;
- m_space.read().derive_range(address, range.m_addrstart, range.m_addrend);
- m_rangelist[entry].push_front(range);
-
- return &m_rangelist[entry].front();
-}
-
-
-//-------------------------------------------------
-// remove_intersecting_ranges - remove all cached
-// ranges that intersect the given address range
-//-------------------------------------------------
-
-template<int Width, int AddrShift, int Endian> void memory_access_cache<Width, AddrShift, Endian>::remove_intersecting_ranges(offs_t addrstart, offs_t addrend)
-{
- // loop over all entries
- for (auto & elem : m_rangelist)
- {
- // loop over all ranges in this entry's list
- for (auto range = elem.begin(); range!=elem.end();)
- {
- // if we intersect, remove
- if (addrstart <= range->m_addrend && addrend >= range->m_addrstart)
- range = elem.erase(range);
- else
- range ++;
- }
- }
-}
-
template class memory_access_cache<0, 0, ENDIANNESS_LITTLE>;
template class memory_access_cache<0, 0, ENDIANNESS_BIG>;
template class memory_access_cache<1, 3, ENDIANNESS_LITTLE>;
@@ -4205,12 +2220,11 @@ memory_block::~memory_block()
memory_bank::memory_bank(address_space &space, int index, offs_t addrstart, offs_t addrend, const char *tag)
: m_machine(space.m_manager.machine()),
- m_baseptr(space.m_manager.bank_pointer_addr(index)),
- m_index(index),
- m_anonymous(tag == nullptr),
- m_addrstart(addrstart),
- m_addrend(addrend),
- m_curentry(BANK_ENTRY_UNSPECIFIED)
+ m_baseptr(nullptr),
+ m_anonymous(tag == nullptr),
+ m_addrstart(addrstart),
+ m_addrend(addrend),
+ m_curentry(0)
{
// generate an internal tag if we don't have one
if (tag == nullptr)
@@ -4220,7 +2234,7 @@ memory_bank::memory_bank(address_space &space, int index, offs_t addrstart, offs
}
else
{
- m_tag.assign(tag);
+ m_tag = tag;
m_name = string_format("Bank '%s'", tag);
}
@@ -4268,19 +2282,6 @@ void memory_bank::add_reference(address_space &space, read_or_write readorwrite)
//-------------------------------------------------
-// invalidate_references - force updates on all
-// referencing address spaces
-//-------------------------------------------------
-
-void memory_bank::invalidate_references()
-{
- // invalidate all the cache references to any referenced address spaces
- for (auto &ref : m_reflist)
- ref->space().invalidate_read_caches();
-}
-
-
-//-------------------------------------------------
// set_base - set the bank base explicitly
//-------------------------------------------------
@@ -4290,13 +2291,27 @@ void memory_bank::set_base(void *base)
if (base == nullptr)
throw emu_fatalerror("memory_bank::set_base called nullptr base");
- // set the base and invalidate any referencing spaces
- *m_baseptr = reinterpret_cast<u8 *>(base);
- invalidate_references();
+ // set the base
+ if(m_entries.empty()) {
+ m_entries.resize(1);
+ m_curentry = 0;
+ }
+ m_baseptr = m_entries[m_curentry] = reinterpret_cast<u8 *>(base);
+ for(auto cb : m_alloc_notifier)
+ cb(base);
+ m_alloc_notifier.clear();
}
//-------------------------------------------------
+// add_notifier - add a function used to notify when the allocation is done
+//-------------------------------------------------
+void memory_bank::add_notifier(std::function<void (void *)> cb)
+{
+ m_alloc_notifier.emplace_back(cb);
+}
+
+//-------------------------------------------------
// set_entry - set the base to a pre-configured
// entry
//-------------------------------------------------
@@ -4306,30 +2321,13 @@ void memory_bank::set_entry(int entrynum)
// validate
if (m_anonymous)
throw emu_fatalerror("memory_bank::set_entry called for anonymous bank");
- if (entrynum < 0 || entrynum >= int(m_entry.size()))
+ if (entrynum < 0 || entrynum >= int(m_entries.size()))
throw emu_fatalerror("memory_bank::set_entry called with out-of-range entry %d", entrynum);
- if (m_entry[entrynum].m_ptr == nullptr)
+ if (m_entries[entrynum] == nullptr)
throw emu_fatalerror("memory_bank::set_entry called for bank '%s' with invalid bank entry %d", m_tag.c_str(), entrynum);
m_curentry = entrynum;
- *m_baseptr = m_entry[entrynum].m_ptr;
-
- // invalidate referencing spaces
- invalidate_references();
-}
-
-
-//-------------------------------------------------
-// expand_entries - expand the allocated array
-// of entries
-//-------------------------------------------------
-
-void memory_bank::expand_entries(int entrynum)
-{
- // allocate a new array and copy from the old one; zero out the new entries
- int old_size = m_entry.size();
- m_entry.resize(entrynum + 1);
- memset(&m_entry[old_size], 0, (entrynum+1-old_size)*sizeof(m_entry[0]));
+ m_baseptr = m_entries[entrynum];
}
@@ -4344,15 +2342,15 @@ void memory_bank::configure_entry(int entrynum, void *base)
throw emu_fatalerror("memory_bank::configure_entry called with out-of-range entry %d", entrynum);
// if we haven't allocated this many entries yet, expand our array
- if (entrynum >= int(m_entry.size()))
- expand_entries(entrynum);
+ if (entrynum >= int(m_entries.size()))
+ m_entries.resize(entrynum+1);
// set the entry
- m_entry[entrynum].m_ptr = reinterpret_cast<u8 *>(base);
+ m_entries[entrynum] = reinterpret_cast<u8 *>(base);
// if the bank base is not configured, and we're the first entry, set us up
- if (*m_baseptr == nullptr && entrynum == 0)
- *m_baseptr = m_entry[entrynum].m_ptr;
+ if (!m_baseptr && !entrynum)
+ m_baseptr = m_entries[0];
}
@@ -4362,9 +2360,14 @@ void memory_bank::configure_entry(int entrynum, void *base)
void memory_bank::configure_entries(int startentry, int numentries, void *base, offs_t stride)
{
- // fill in the requested bank entries (backwards to improve allocation)
- for (int entrynum = startentry + numentries - 1; entrynum >= startentry; entrynum--)
- configure_entry(entrynum, reinterpret_cast<u8 *>(base) + (entrynum - startentry) * stride);
+ if (startentry + numentries >= int(m_entries.size()))
+ m_entries.resize(startentry + numentries+1);
+
+ // fill in the requested bank entries
+ for (int entrynum = 0; entrynum < numentries; entrynum ++)
+ m_entries[entrynum + startentry] = reinterpret_cast<u8 *>(base) + entrynum * stride ;
+ if(!m_baseptr && !startentry)
+ m_baseptr = reinterpret_cast<u8 *>(base);
}
@@ -4386,822 +2389,3 @@ memory_region::memory_region(running_machine &machine, const char *name, u32 len
{
assert(width == 1 || width == 2 || width == 4 || width == 8);
}
-
-
-
-//**************************************************************************
-// HANDLER ENTRY
-//**************************************************************************
-
-//-------------------------------------------------
-// handler_entry - constructor
-//-------------------------------------------------
-
-handler_entry::handler_entry(u8 width, endianness_t endianness, u8 **rambaseptr)
- : m_populated(false),
- m_datawidth(width),
- m_endianness(endianness),
- m_addrstart(0),
- m_addrend(0),
- m_addrmask(~0),
- m_bytemask(~0),
- m_rambaseptr(rambaseptr),
- m_subunits(0),
- m_invsubmask(0)
-{
-}
-
-
-//-------------------------------------------------
-// ~handler_entry - destructor
-//-------------------------------------------------
-
-handler_entry::~handler_entry()
-{
-}
-
-
-//-------------------------------------------------
-// copy - copy another handler_entry, but only
-// if it is populated and constitutes of one or
-// more subunit handlers
-//-------------------------------------------------
-
-void handler_entry::copy(handler_entry *entry)
-{
- assert(entry->m_populated);
- assert(entry->m_subunits);
- assert(!entry->m_rambaseptr);
- assert(!m_populated);
-
- m_populated = true;
- m_datawidth = entry->m_datawidth;
- m_endianness = entry->m_endianness;
- m_addrstart = entry->m_addrstart;
- m_addrend = entry->m_addrend;
- m_addrmask = entry->m_addrmask;
- m_bytemask = entry->m_bytemask;
- m_rambaseptr = nullptr;
- m_subunits = entry->m_subunits;
- memcpy(m_subunit_infos, entry->m_subunit_infos, m_subunits*sizeof(subunit_info));
- m_invsubmask = entry->m_invsubmask;
-}
-
-
-//-------------------------------------------------
-// reconfigure_subunits - reconfigure the subunits
-// to handle a new base address
-//-------------------------------------------------
-void handler_entry::reconfigure_subunits(offs_t addrstart)
-{
- s32 delta = addrstart - m_addrstart;
- for (int i=0; i != m_subunits; i++)
- m_subunit_infos[i].m_offset += delta / (m_subunit_infos[i].m_size / 8);
-}
-
-
-//-------------------------------------------------
-// configure_subunits - configure the subunits
-// and subshift array to represent the provided
-// mask
-//-------------------------------------------------
-
-void handler_entry::configure_subunits(u64 handlermask, int handlerbits, int cswidth, int &start_slot, int &end_slot)
-{
- u64 unitmask = (u64(1) << handlerbits) - 1;
- u64 selectmask = (u64(1) << cswidth) - 1;
- assert(handlermask != 0);
- assert(m_datawidth == 64 || (handlermask >> m_datawidth) == 0);
-
- // compute the maximum possible subunits
- int maxunits = m_datawidth / handlerbits;
- assert(maxunits > 1);
- assert(maxunits <= ARRAY_LENGTH(m_subunit_infos));
-
- int granularity = cswidth / handlerbits;
- assert(granularity > 0);
-
- int shift_xor_mask = m_endianness == ENDIANNESS_LITTLE ? 0 : maxunits - 1;
-
- // walk the handlermask to find out how many we have
- int count = 0;
- for (u64 scanmask = handlermask; scanmask != 0; scanmask >>= handlerbits)
- {
- assert((scanmask & unitmask) == 0 || (scanmask & unitmask) == unitmask);
- if ((scanmask & unitmask) != 0)
- count++;
- }
- if (count == 0 || count > maxunits)
- throw emu_fatalerror("Invalid subunit mask %s for %d-bit handler in %d-bit space", core_i64_hex_format(handlermask, m_datawidth / 4), handlerbits, m_datawidth);
-
- // make sure that the multiplier is a power of 2
- int multiplier = count;
- while ((multiplier & (multiplier-1)) != 0)
- multiplier++;
-
- // fill in the shifts
- int cur_offset = 0;
- start_slot = m_subunits;
- for (int unitnum = 0; unitnum < maxunits; unitnum++)
- {
- u32 shift = (unitnum^shift_xor_mask) * handlerbits;
- if (((handlermask >> shift) & unitmask) != 0)
- {
- m_subunit_infos[m_subunits].m_addrmask = m_bytemask / (maxunits / multiplier);
- m_subunit_infos[m_subunits].m_datamask = unitmask << shift;
- m_subunit_infos[m_subunits].m_csmask = selectmask << (((unitnum^shift_xor_mask) & ~(granularity-1)) * handlerbits);
- m_subunit_infos[m_subunits].m_offset = cur_offset++;
- m_subunit_infos[m_subunits].m_size = handlerbits;
- m_subunit_infos[m_subunits].m_shift = shift;
- m_subunit_infos[m_subunits].m_multiplier = multiplier;
- m_subunits++;
- }
- }
- end_slot = m_subunits;
-
- // compute the inverse mask
- m_invsubmask = 0;
- for (int i = 0; i < m_subunits; i++)
- m_invsubmask |= unitmask << m_subunit_infos[i].m_shift;
- m_invsubmask = ~m_invsubmask;
-}
-
-
-//-------------------------------------------------
-// clear_conflicting_subunits - clear the subunits
-// conflicting with the provided mask
-//-------------------------------------------------
-
-void handler_entry::clear_conflicting_subunits(u64 handlermask)
-{
- // A mask of 0 is in fact an alternative way of saying ~0
- if (!handlermask)
- {
- m_subunits = 0;
- return;
- }
-
- // Start by the end to avoid unnecessary memmoves
- for (int i=m_subunits-1; i>=0; i--)
- if ((handlermask & m_subunit_infos[i].m_datamask) != 0)
- {
- if (i != m_subunits-1)
- memmove (m_subunit_infos+i, m_subunit_infos+i+1, (m_subunits-i-1)*sizeof(m_subunit_infos[0]));
- remove_subunit(i);
- }
-
- // Recompute the inverse mask
- m_invsubmask = 0;
- for (int i = 0; i < m_subunits; i++)
- m_invsubmask |= m_subunit_infos[i].m_datamask;
- m_invsubmask = ~m_invsubmask;
-}
-
-
-//-------------------------------------------------
-// overriden_by_mask - check whether a handler with
-// the provided mask fully overrides everything
-// that's currently present
-//-------------------------------------------------
-
-bool handler_entry::overriden_by_mask(u64 handlermask) const
-{
- // A mask of 0 is in fact an alternative way of saying ~0
- if (!handlermask)
- return true;
-
- // If there are no subunits, it's going to override
- if (!m_subunits)
- return true;
-
- // Check whether a subunit would be left
- for (int i=0; i != m_subunits; i++)
- if ((handlermask & m_subunit_infos[i].m_datamask) == 0)
- return false;
-
- return true;
-}
-
-
-//-------------------------------------------------
-// description - build a printable description
-// of the handler
-//-------------------------------------------------
-
-void handler_entry::description(char *buffer) const
-{
- if (m_subunits)
- {
- for (int i=0; i != m_subunits; i++)
- {
- if (i)
- *buffer++ = ' ';
- buffer += sprintf (buffer, "%d:%d:%x:%d:%x:%s:%s:%s",
- m_subunit_infos[i].m_size,
- m_subunit_infos[i].m_shift,
- m_subunit_infos[i].m_offset,
- m_subunit_infos[i].m_multiplier,
- m_subunit_infos[i].m_addrmask,
- core_i64_hex_format(m_subunit_infos[i].m_datamask, m_datawidth),
- core_i64_hex_format(m_subunit_infos[i].m_csmask, m_datawidth),
- subunit_name(i));
- }
- }
- else
- strcpy (buffer, name());
-}
-
-
-//**************************************************************************
-// HANDLER ENTRY READ
-//**************************************************************************
-
-//-------------------------------------------------
-// copy - copy another handler_entry, but only
-// if it is populated and constitutes of one or
-// more subunit handlers
-//-------------------------------------------------
-
-void handler_entry_read::copy(handler_entry *entry)
-{
- handler_entry::copy(entry);
- handler_entry_read *rentry = static_cast<handler_entry_read *>(entry);
- m_read = rentry->m_read;
- for(int i = 0; i < m_subunits; ++i)
- {
- switch(m_subunit_infos[i].m_size)
- {
- case 8:
- m_subread[i].r8 = rentry->m_subread[i].r8;
- break;
- case 16:
- m_subread[i].r16 = rentry->m_subread[i].r16;
- break;
- case 32:
- m_subread[i].r32 = rentry->m_subread[i].r32;
- break;
- }
- }
-}
-
-//-------------------------------------------------
-// name - return the handler name, from the
-// appropriately-sized delegate
-//-------------------------------------------------
-
-const char *handler_entry_read::name() const
-{
- switch (m_datawidth)
- {
- case 8: return m_read.r8.name();
- case 16: return m_read.r16.name();
- case 32: return m_read.r32.name();
- case 64: return m_read.r64.name();
- }
- return nullptr;
-}
-
-//-------------------------------------------------
-// subunit_name - return the handler name, from the
-// appropriately-sized delegate of a subunit
-//-------------------------------------------------
-
-const char *handler_entry_read::subunit_name(int entry) const
-{
- switch (m_subunit_infos[entry].m_size)
- {
- case 8: return m_subread[entry].r8.name();
- case 16: return m_subread[entry].r16.name();
- case 32: return m_subread[entry].r32.name();
- case 64: return m_subread[entry].r64.name();
- }
- return nullptr;
-}
-
-
-//-------------------------------------------------
-// remove_subunit - delete a subunit specific
-// information and shift up the following ones
-//-------------------------------------------------
-void handler_entry_read::remove_subunit(int entry)
-{
- int moving = m_subunits - entry - 1;
- if (moving)
- {
- memmove(m_subread+entry, m_subread+entry+1, moving*sizeof(m_subread[0]));
- }
- m_subunits--;
-}
-
-
-//-------------------------------------------------
-// set_delegate - set an 8-bit delegate, and
-// configure a stub if necessary
-//-------------------------------------------------
-
-void handler_entry_read::set_delegate(read8_delegate delegate, u64 umask, int cswidth)
-{
- // error if no object
- if (!delegate.has_object())
- throw emu_fatalerror("Attempted to install delegate '%s' without a bound object", delegate.name());
-
- // make sure this is a valid size
- assert(m_datawidth >= 8);
-
- // if mismatched bus width, configure a stub
- if (m_datawidth != 8)
- {
- int start_slot, end_slot;
- configure_subunits(umask, 8, cswidth, start_slot, end_slot);
- for (int i=start_slot; i != end_slot; i++)
- {
- m_subread[i].r8 = delegate;
- }
- if (m_datawidth == 16)
- set_delegate(read16_delegate(&handler_entry_read::read_stub_16, delegate.name(), this));
- else if (m_datawidth == 32)
- set_delegate(read32_delegate(&handler_entry_read::read_stub_32, delegate.name(), this));
- else if (m_datawidth == 64)
- set_delegate(read64_delegate(&handler_entry_read::read_stub_64, delegate.name(), this));
- }
- else
- {
- m_read.r8 = delegate;
- }
-}
-
-
-//-------------------------------------------------
-// set_delegate - set a 16-bit delegate, and
-// configure a stub if necessary
-//-------------------------------------------------
-
-void handler_entry_read::set_delegate(read16_delegate delegate, u64 umask, int cswidth)
-{
- // error if no object
- if (!delegate.has_object())
- throw emu_fatalerror("Attempted to install delegate '%s' without a bound object", delegate.name());
-
- // make sure this is a valid size
- assert(m_datawidth >= 16);
-
- // if mismatched bus width, configure a stub
- if (m_datawidth != 16)
- {
- int start_slot, end_slot;
- configure_subunits(umask, 16, cswidth, start_slot, end_slot);
- for (int i=start_slot; i != end_slot; i++)
- {
- m_subread[i].r16 = delegate;
- }
- if (m_datawidth == 32)
- set_delegate(read32_delegate(&handler_entry_read::read_stub_32, delegate.name(), this));
- else if (m_datawidth == 64)
- set_delegate(read64_delegate(&handler_entry_read::read_stub_64, delegate.name(), this));
- }
- else
- {
- m_read.r16 = delegate;
- }
-}
-
-
-//-------------------------------------------------
-// set_delegate - set a 32-bit delegate, and
-// configure a stub if necessary
-//-------------------------------------------------
-
-void handler_entry_read::set_delegate(read32_delegate delegate, u64 umask, int cswidth)
-{
- // error if no object
- if (!delegate.has_object())
- throw emu_fatalerror("Attempted to install delegate '%s' without a bound object", delegate.name());
-
- // make sure this is a valid size
- assert(m_datawidth >= 32);
-
- // if mismatched bus width, configure a stub
- if (m_datawidth != 32)
- {
- int start_slot, end_slot;
- configure_subunits(umask, 32, cswidth, start_slot, end_slot);
- for (int i=start_slot; i != end_slot; i++)
- {
- m_subread[i].r32 = delegate;
- }
- if (m_datawidth == 64)
- set_delegate(read64_delegate(&handler_entry_read::read_stub_64, delegate.name(), this));
- }
- else
- {
- m_read.r32 = delegate;
- }
-}
-
-
-//-------------------------------------------------
-// set_delegate - set a 64-bit delegate
-//-------------------------------------------------
-
-void handler_entry_read::set_delegate(read64_delegate delegate, u64 umask, int cswidth)
-{
- // error if no object
- if (!delegate.has_object())
- throw emu_fatalerror("Attempted to install delegate '%s' without a bound object", delegate.name());
-
- // make sure this is a valid size
- assert(m_datawidth >= 64);
- m_read.r64 = delegate;
-}
-
-
-//-------------------------------------------------
-// set_ioport - configure an I/O port read stub
-// of the appropriate size
-//-------------------------------------------------
-
-void handler_entry_read::set_ioport(ioport_port &ioport)
-{
- m_ioport = &ioport;
- if (m_datawidth == 8)
- set_delegate(read8_delegate(&handler_entry_read::read_stub_ioport<u8>, ioport.tag(), this));
- else if (m_datawidth == 16)
- set_delegate(read16_delegate(&handler_entry_read::read_stub_ioport<u16>, ioport.tag(), this));
- else if (m_datawidth == 32)
- set_delegate(read32_delegate(&handler_entry_read::read_stub_ioport<u32>, ioport.tag(), this));
- else if (m_datawidth == 64)
- set_delegate(read64_delegate(&handler_entry_read::read_stub_ioport<u64>, ioport.tag(), this));
-}
-
-
-//-------------------------------------------------
-// read_stub_16 - construct a 16-bit read from
-// 8-bit sources
-//-------------------------------------------------
-
-u16 handler_entry_read::read_stub_16(address_space &space, offs_t offset, u16 mask)
-{
- u16 result = space.unmap() & m_invsubmask;
- for (int index = 0; index < m_subunits; index++)
- {
- const subunit_info &si = m_subunit_infos[index];
- if (mask & si.m_csmask)
- {
- offs_t aoffset = offset * si.m_multiplier + si.m_offset;
- u8 val;
- val = m_subread[index].r8(space, aoffset & si.m_addrmask, mask >> si.m_shift);
- result |= val << si.m_shift;
- }
- }
- return result;
-}
-
-
-//-------------------------------------------------
-// read_stub_32 - construct a 32-bit read from
-// 8-bit and 16-bit sources
-//-------------------------------------------------
-
-u32 handler_entry_read::read_stub_32(address_space &space, offs_t offset, u32 mask)
-{
- u32 result = space.unmap() & m_invsubmask;
- for (int index = 0; index < m_subunits; index++)
- {
- const subunit_info &si = m_subunit_infos[index];
- if (mask & si.m_csmask)
- {
- offs_t aoffset = offset * si.m_multiplier + si.m_offset;
- u16 val = 0;
- switch (si.m_size)
- {
- case 8:
- val = m_subread[index].r8(space, aoffset & si.m_addrmask, mask >> si.m_shift);
- break;
- case 16:
- val = m_subread[index].r16(space, aoffset & si.m_addrmask, mask >> si.m_shift);
- break;
- }
- result |= val << si.m_shift;
- }
- }
- return result;
-}
-
-
-//-------------------------------------------------
-// read_stub_64 - construct a 64-bit read from
-// 8-bit, 16-bit and 32-bit sources
-//-------------------------------------------------
-
-u64 handler_entry_read::read_stub_64(address_space &space, offs_t offset, u64 mask)
-{
- u64 result = space.unmap() & m_invsubmask;
- for (int index = 0; index < m_subunits; index++)
- {
- const subunit_info &si = m_subunit_infos[index];
- if (mask & si.m_csmask)
- {
- offs_t aoffset = offset * si.m_multiplier + si.m_offset;
- u32 val = 0;
- switch (si.m_size)
- {
- case 8:
- val = m_subread[index].r8(space, aoffset & si.m_addrmask, mask >> si.m_shift);
- break;
- case 16:
- val = m_subread[index].r16(space, aoffset & si.m_addrmask, mask >> si.m_shift);
- break;
- case 32:
- val = m_subread[index].r32(space, aoffset & si.m_addrmask, mask >> si.m_shift);
- break;
- }
- result |= u64(val) << si.m_shift;
- }
- }
- return result;
-}
-
-
-//**************************************************************************
-// HANDLER ENTRY WRITE
-//**************************************************************************
-
-//-------------------------------------------------
-// copy - copy another handler_entry, but only
-// if it is populated and constitutes of one or
-// more subunit handlers
-//-------------------------------------------------
-
-void handler_entry_write::copy(handler_entry *entry)
-{
- handler_entry::copy(entry);
- handler_entry_write *wentry = static_cast<handler_entry_write *>(entry);
- m_write = wentry->m_write;
- for(int i = 0; i < m_subunits; ++i)
- {
- switch(m_subunit_infos[i].m_size)
- {
- case 8:
- m_subwrite[i].w8 = wentry->m_subwrite[i].w8;
- break;
- case 16:
- m_subwrite[i].w16 = wentry->m_subwrite[i].w16;
- break;
- case 32:
- m_subwrite[i].w32 = wentry->m_subwrite[i].w32;
- break;
- }
- }
-}
-
-//-------------------------------------------------
-// name - return the handler name, from the
-// appropriately-sized delegate
-//-------------------------------------------------
-
-const char *handler_entry_write::name() const
-{
- switch (m_datawidth)
- {
- case 8: return m_write.w8.name();
- case 16: return m_write.w16.name();
- case 32: return m_write.w32.name();
- case 64: return m_write.w64.name();
- }
- return nullptr;
-}
-
-
-//-------------------------------------------------
-// subunit_name - return the handler name, from the
-// appropriately-sized delegate of a subunit
-//-------------------------------------------------
-
-const char *handler_entry_write::subunit_name(int entry) const
-{
- switch (m_subunit_infos[entry].m_size)
- {
- case 8: return m_subwrite[entry].w8.name();
- case 16: return m_subwrite[entry].w16.name();
- case 32: return m_subwrite[entry].w32.name();
- case 64: return m_subwrite[entry].w64.name();
- }
- return nullptr;
-}
-
-
-//-------------------------------------------------
-// remove_subunit - delete a subunit specific
-// information and shift up the following ones
-//-------------------------------------------------
-void handler_entry_write::remove_subunit(int entry)
-{
- int moving = m_subunits - entry - 1;
- if (moving)
- {
- memmove(m_subwrite+entry, m_subwrite+entry+1, moving*sizeof(m_subwrite[0]));
- }
- m_subunits--;
-}
-
-
-//-------------------------------------------------
-// set_delegate - set an 8-bit delegate, and
-// configure a stub if necessary
-//-------------------------------------------------
-
-void handler_entry_write::set_delegate(write8_delegate delegate, u64 umask, int cswidth)
-{
- assert(m_datawidth >= 8);
-
- // if mismatched bus width, configure a stub
- if (m_datawidth != 8)
- {
- int start_slot, end_slot;
- configure_subunits(umask, 8, cswidth, start_slot, end_slot);
- for (int i=start_slot; i != end_slot; i++)
- {
- m_subwrite[i].w8 = delegate;
- }
- if (m_datawidth == 16)
- set_delegate(write16_delegate(&handler_entry_write::write_stub_16, delegate.name(), this));
- else if (m_datawidth == 32)
- set_delegate(write32_delegate(&handler_entry_write::write_stub_32, delegate.name(), this));
- else if (m_datawidth == 64)
- set_delegate(write64_delegate(&handler_entry_write::write_stub_64, delegate.name(), this));
- }
- else
- {
- m_write.w8 = delegate;
- }
-}
-
-
-//-------------------------------------------------
-// set_delegate - set a 16-bit delegate, and
-// configure a stub if necessary
-//-------------------------------------------------
-
-void handler_entry_write::set_delegate(write16_delegate delegate, u64 umask, int cswidth)
-{
- assert(m_datawidth >= 16);
-
- // if mismatched bus width, configure a stub
- if (m_datawidth != 16)
- {
- int start_slot, end_slot;
- configure_subunits(umask, 16, cswidth, start_slot, end_slot);
- for (int i=start_slot; i != end_slot; i++)
- {
- m_subwrite[i].w16 = delegate;
- }
- if (m_datawidth == 32)
- set_delegate(write32_delegate(&handler_entry_write::write_stub_32, delegate.name(), this));
- else if (m_datawidth == 64)
- set_delegate(write64_delegate(&handler_entry_write::write_stub_64, delegate.name(), this));
- }
- else
- {
- m_write.w16 = delegate;
- }
-}
-
-
-//-------------------------------------------------
-// set_delegate - set a 32-bit delegate, and
-// configure a stub if necessary
-//-------------------------------------------------
-
-void handler_entry_write::set_delegate(write32_delegate delegate, u64 umask, int cswidth)
-{
- assert(m_datawidth >= 32);
-
- // if mismatched bus width, configure a stub
- if (m_datawidth != 32)
- {
- int start_slot, end_slot;
- configure_subunits(umask, 32, cswidth, start_slot, end_slot);
- for (int i=start_slot; i != end_slot; i++)
- {
- m_subwrite[i].w32 = delegate;
- }
- if (m_datawidth == 64)
- set_delegate(write64_delegate(&handler_entry_write::write_stub_64, delegate.name(), this));
- }
- else
- {
- m_write.w32 = delegate;
- }
-}
-
-
-//-------------------------------------------------
-// set_delegate - set a 64-bit delegate
-//-------------------------------------------------
-
-void handler_entry_write::set_delegate(write64_delegate delegate, u64 mask, int cswidth)
-{
- assert(m_datawidth >= 64);
- m_write.w64 = delegate;
-}
-
-
-//-------------------------------------------------
-// set_ioport - configure an I/O port read stub
-// of the appropriate size
-//-------------------------------------------------
-
-void handler_entry_write::set_ioport(ioport_port &ioport)
-{
- m_ioport = &ioport;
- if (m_datawidth == 8)
- set_delegate(write8_delegate(&handler_entry_write::write_stub_ioport<u8>, ioport.tag(), this));
- else if (m_datawidth == 16)
- set_delegate(write16_delegate(&handler_entry_write::write_stub_ioport<u16>, ioport.tag(), this));
- else if (m_datawidth == 32)
- set_delegate(write32_delegate(&handler_entry_write::write_stub_ioport<u32>, ioport.tag(), this));
- else if (m_datawidth == 64)
- set_delegate(write64_delegate(&handler_entry_write::write_stub_ioport<u64>, ioport.tag(), this));
-}
-
-
-//-------------------------------------------------
-// write_stub_16 - construct a 16-bit write from
-// 8-bit sources
-//-------------------------------------------------
-
-void handler_entry_write::write_stub_16(address_space &space, offs_t offset, u16 data, u16 mask)
-{
- for (int index = 0; index < m_subunits; index++)
- {
- const subunit_info &si = m_subunit_infos[index];
- if (mask & si.m_csmask)
- {
- offs_t aoffset = offset * si.m_multiplier + si.m_offset;
- u8 adata = data >> si.m_shift;
- u8 amask = mask >> si.m_shift;
- m_subwrite[index].w8(space, aoffset & si.m_addrmask, adata, amask);
- }
- }
-}
-
-
-//-------------------------------------------------
-// write_stub_32 - construct a 32-bit write from
-// 8-bit and 16-bit sources
-//-------------------------------------------------
-
-void handler_entry_write::write_stub_32(address_space &space, offs_t offset, u32 data, u32 mask)
-{
- for (int index = 0; index < m_subunits; index++)
- {
- const subunit_info &si = m_subunit_infos[index];
- if (mask & si.m_csmask)
- {
- offs_t aoffset = offset * si.m_multiplier + si.m_offset;
- u16 adata = data >> si.m_shift;
- u16 amask = mask >> si.m_shift;
- switch (si.m_size)
- {
- case 8:
- m_subwrite[index].w8(space, aoffset & si.m_addrmask, adata, amask);
- break;
- case 16:
- m_subwrite[index].w16(space, aoffset & si.m_addrmask, adata, amask);
- break;
- }
- }
- }
-}
-
-
-//-------------------------------------------------
-// write_stub_64 - construct a 64-bit write from
-// 8-bit, 16-bit and 32-bit sources
-//-------------------------------------------------
-
-void handler_entry_write::write_stub_64(address_space &space, offs_t offset, u64 data, u64 mask)
-{
- for (int index = 0; index < m_subunits; index++)
- {
- const subunit_info &si = m_subunit_infos[index];
- if (mask & si.m_csmask)
- {
- offs_t aoffset = offset * si.m_multiplier + si.m_offset;
- u32 adata = data >> si.m_shift;
- u32 amask = mask >> si.m_shift;
- switch (si.m_size)
- {
- case 8:
- m_subwrite[index].w8(space, aoffset & si.m_addrmask, adata, amask);
- break;
- case 16:
- m_subwrite[index].w16(space, aoffset & si.m_addrmask, adata, amask);
- break;
- case 32:
- m_subwrite[index].w32(space, aoffset & si.m_addrmask, adata, amask);
- break;
- }
- }
- }
-}
diff --git a/src/emu/emumem.h b/src/emu/emumem.h
index 045e4d33a76..e8b286c6e94 100644
--- a/src/emu/emumem.h
+++ b/src/emu/emumem.h
@@ -33,8 +33,6 @@ using u64 = std::uint64_t;
// CONSTANTS
//**************************************************************************
-enum { TOTAL_MEMORY_BANKS = 512 };
-
// address space names for common use
constexpr int AS_PROGRAM = 0; // program address space
constexpr int AS_DATA = 1; // data address space
@@ -55,14 +53,8 @@ enum class read_or_write
// TYPE DEFINITIONS
//**************************************************************************
-// private classes declared in emumem.cpp
-class address_table;
-class address_table_read;
-class address_table_setoffset;
-class address_table_write;
-
// offsets and addresses are 32-bit (for now...)
-typedef u32 offs_t;
+using offs_t = u32;
// address map constructors are delegates that build up an address_map
using address_map_constructor = named_delegate<void (address_map &)>;
@@ -87,20 +79,26 @@ struct data_accessors
void (*write_qword_masked)(address_space &space, offs_t address, u64 data, u64 mask);
};
+// a line in the memory structure dump
+struct memory_entry {
+ offs_t start, end;
+ class handler_entry *entry;
+};
+
// ======================> read_delegate
// declare delegates for each width
-typedef device_delegate<u8 (address_space &, offs_t, u8)> read8_delegate;
-typedef device_delegate<u16 (address_space &, offs_t, u16)> read16_delegate;
-typedef device_delegate<u32 (address_space &, offs_t, u32)> read32_delegate;
-typedef device_delegate<u64 (address_space &, offs_t, u64)> read64_delegate;
+using read8_delegate = device_delegate<u8 (address_space &, offs_t, u8 )>;
+using read16_delegate = device_delegate<u16 (address_space &, offs_t, u16)>;
+using read32_delegate = device_delegate<u32 (address_space &, offs_t, u32)>;
+using read64_delegate = device_delegate<u64 (address_space &, offs_t, u64)>;
// ======================> write_delegate
// declare delegates for each width
-typedef device_delegate<void (address_space &, offs_t, u8, u8)> write8_delegate;
+typedef device_delegate<void (address_space &, offs_t, u8, u8 )> write8_delegate;
typedef device_delegate<void (address_space &, offs_t, u16, u16)> write16_delegate;
typedef device_delegate<void (address_space &, offs_t, u32, u32)> write32_delegate;
typedef device_delegate<void (address_space &, offs_t, u64, u64)> write64_delegate;
@@ -227,18 +225,251 @@ inline write64_delegate make_delegate(T &&func, const char *name, const char *ta
} } // namespace emu::detail
+// =====================-> Width -> types
+
+template<int Width> struct handler_entry_size {};
+template<> struct handler_entry_size<0> { using uX = u8; using READ = read8_delegate; using WRITE = write8_delegate; };
+template<> struct handler_entry_size<1> { using uX = u16; using READ = read16_delegate; using WRITE = write16_delegate; };
+template<> struct handler_entry_size<2> { using uX = u32; using READ = read32_delegate; using WRITE = write32_delegate; };
+template<> struct handler_entry_size<3> { using uX = u64; using READ = read64_delegate; using WRITE = write64_delegate; };
-// ======================> setoffset_delegate
-typedef device_delegate<void (address_space &, offs_t)> setoffset_delegate;
+// ======================> memopry_units_descritor forwards declaration
-// =====================-> Width -> types
-template<int Width> struct handler_entry_size {};
-template<> struct handler_entry_size<0> { typedef u8 uX; typedef read8_delegate READ; typedef write8_delegate WRITE; };
-template<> struct handler_entry_size<1> { typedef u16 uX; typedef read16_delegate READ; typedef write16_delegate WRITE; };
-template<> struct handler_entry_size<2> { typedef u32 uX; typedef read32_delegate READ; typedef write32_delegate WRITE; };
-template<> struct handler_entry_size<3> { typedef u64 uX; typedef read64_delegate READ; typedef write64_delegate WRITE; };
+template<int Width, int AddrShift, int Endian> class memory_units_descriptor;
+
+// =====================-> Address segmentation for the search tree
+
+static inline constexpr int handler_entry_dispatch_lowbits(int highbits, int width, int ashift)
+{
+ if(highbits > 48)
+ return 48;
+ if(highbits > 32)
+ return 32;
+ if(highbits > 14)
+ return 14;
+ return width + ashift;
+}
+
+
+// =====================-> The root class of all handlers
+
+// Handlers the refcounting as part of the interface
+
+class handler_entry
+{
+ DISABLE_COPYING(handler_entry);
+
+public:
+ // Typing flags
+ static constexpr u32 F_DISPATCH = 0x00000001; // handler that forwards the access to other handlers
+ static constexpr u32 F_UNITS = 0x00000002; // handler that merges/splits an access among multiple handlers (unitmask support)
+ static constexpr u32 F_PASSTHROUGH = 0x00000004; // handler that passes through the request to another handler
+
+ // Start/end of range flags
+ static constexpr u8 START = 1;
+ static constexpr u8 END = 2;
+
+ // Intermediary structure for reference count checking
+ class reflist {
+ public:
+ void add(const handler_entry *entry);
+
+ void propagate();
+ void check();
+
+ private:
+ std::unordered_map<const handler_entry *, u32> refcounts;
+ std::unordered_set<const handler_entry *> seen;
+ std::unordered_set<const handler_entry *> todo;
+ };
+
+ handler_entry(address_space *space, u32 flags) { m_space = space; m_refcount = 1; m_flags = flags; }
+ virtual ~handler_entry() {}
+
+ inline void ref(int count = 1) const { m_refcount += count; }
+ inline void unref(int count = 1) const { m_refcount -= count; if(!m_refcount) delete this; }
+ inline u32 flags() const { return m_flags; }
+
+ inline bool is_dispatch() const { return m_flags & F_DISPATCH; }
+ inline bool is_units() const { return m_flags & F_UNITS; }
+ inline bool is_passthrough() const { return m_flags & F_PASSTHROUGH; }
+
+ virtual void dump_map(std::vector<memory_entry> &map) const;
+
+ virtual std::string name() const = 0;
+ virtual void enumerate_references(handler_entry::reflist &refs) const;
+ u32 get_refcount() const { return m_refcount; }
+
+protected:
+ // Address range storage
+ struct range {
+ offs_t start;
+ offs_t end;
+
+ inline void set(offs_t _start, offs_t _end) {
+ start = _start;
+ end = _end;
+ }
+
+ inline void intersect(offs_t _start, offs_t _end) {
+ if(_start > start)
+ start = _start;
+ if(_end < end)
+ end = _end;
+ }
+ };
+
+ address_space *m_space;
+ mutable u32 m_refcount;
+ u32 m_flags;
+};
+
+
+// =====================-> The parent class of all read handlers
+
+// Provides the populate/read/get_ptr/lookup API
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_passthrough;
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read : public handler_entry
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+
+ struct mapping {
+ handler_entry_read<Width, AddrShift, Endian> *original;
+ handler_entry_read<Width, AddrShift, Endian> *patched;
+ u8 ukey;
+ };
+
+ handler_entry_read(address_space *space, u32 flags) : handler_entry(space, flags) {}
+ ~handler_entry_read() {}
+
+ virtual uX read(offs_t offset, uX mem_mask) = 0;
+ virtual void *get_ptr(offs_t offset) const;
+ virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift, Endian> *&handler) const;
+
+ inline void populate(offs_t start, offs_t end, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler) {
+ if(mirror)
+ populate_mirror(start, end, start, end, mirror, handler);
+ else
+ populate_nomirror(start, end, start, end, handler);
+ }
+
+ virtual void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler);
+ virtual void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler);
+
+ inline void populate_mismatched(offs_t start, offs_t end, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor) {
+ std::vector<mapping> mappings;
+ if(mirror)
+ populate_mismatched_mirror(start, end, start, end, mirror, descriptor, mappings);
+ else
+ populate_mismatched_nomirror(start, end, start, end, descriptor, START|END, mappings);
+ }
+
+ virtual void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings);
+ virtual void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings);
+ inline void populate_passthrough(offs_t start, offs_t end, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler) {
+ std::vector<mapping> mappings;
+ if(mirror)
+ populate_passthrough_mirror(start, end, start, end, mirror, handler, mappings);
+ else
+ populate_passthrough_nomirror(start, end, start, end, handler, mappings);
+ }
+
+ virtual void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings);
+ virtual void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings);
+
+ // Remove a set of passthrough handlers, leaving the lower handler in their place
+ virtual void detach(const std::unordered_set<handler_entry *> &handlers);
+};
+
+
+// =====================-> The parent class of all write handlers
+
+// Provides the populate/write/get_ptr/lookup API
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_passthrough;
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write : public handler_entry
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+
+ struct mapping {
+ handler_entry_write<Width, AddrShift, Endian> *original;
+ handler_entry_write<Width, AddrShift, Endian> *patched;
+ u8 ukey;
+ };
+
+ handler_entry_write(address_space *space, u32 flags) : handler_entry(space, flags) {}
+ virtual ~handler_entry_write() {}
+
+ virtual void write(offs_t offset, uX data, uX mem_mask) = 0;
+ virtual void *get_ptr(offs_t offset) const;
+ virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift, Endian> *&handler) const;
+
+ inline void populate(offs_t start, offs_t end, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler) {
+ if(mirror)
+ populate_mirror(start, end, start, end, mirror, handler);
+ else
+ populate_nomirror(start, end, start, end, handler);
+ }
+
+ virtual void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler);
+ virtual void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler);
+
+ inline void populate_mismatched(offs_t start, offs_t end, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor) {
+ std::vector<mapping> mappings;
+ if(mirror)
+ populate_mismatched_mirror(start, end, start, end, mirror, descriptor, mappings);
+ else
+ populate_mismatched_nomirror(start, end, start, end, descriptor, START|END, mappings);
+ }
+
+ virtual void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings);
+ virtual void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings);
+
+ inline void populate_passthrough(offs_t start, offs_t end, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler) {
+ std::vector<mapping> mappings;
+ if(mirror)
+ populate_passthrough_mirror(start, end, start, end, mirror, handler, mappings);
+ else
+ populate_passthrough_nomirror(start, end, start, end, handler, mappings);
+ }
+
+ virtual void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings);
+ virtual void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings);
+
+ // Remove a set of passthrough handlers, leaving the lower handler in their place
+ virtual void detach(const std::unordered_set<handler_entry *> &handlers);
+};
+
+// =====================-> Passthrough handler management structure
+class memory_passthrough_handler
+{
+ template<int Width, int AddrShift, int Endian> friend class handler_entry_read_passthrough;
+ template<int Width, int AddrShift, int Endian> friend class handler_entry_write_passthrough;
+
+public:
+ memory_passthrough_handler(address_space &space) : m_space(space) {}
+
+ inline void remove();
+
+private:
+ address_space &m_space;
+ std::unordered_set<handler_entry *> m_handlers;
+
+ void add_handler(handler_entry *handler) { m_handlers.insert(handler); }
+ void remove_handler(handler_entry *handler) { m_handlers.erase(m_handlers.find(handler)); }
+};
+
+// =====================-> Forward declaration for address_space
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_unmapped;
+template<int Width, int AddrShift, int Endian> class handler_entry_write_unmapped;
// ======================> address offset -> byte offset
@@ -510,86 +741,87 @@ template<int Width, int AddrShift, int Endian, int TargetWidth, bool Aligned, ty
// memory_access_cache contains state data for cached access
template<int Width, int AddrShift, int Endian> class memory_access_cache
{
- friend class address_table;
using NativeType = typename handler_entry_size<Width>::uX;
static constexpr u32 NATIVE_BYTES = 1 << Width;
static constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? (1 << (Width + AddrShift)) - 1 : 0;
public:
- using cache_update_delegate = delegate<offs_t (memory_access_cache<Width, AddrShift, Endian> &, offs_t)>;
-
- // cache_range is an internal class that is part of a list of start/end ranges
- class cache_range
- {
- public:
- // construction
- cache_range(): m_addrstart(0),m_addrend(~0) { }
-
- inline bool operator==(cache_range val) noexcept
- { // return true if _Left and _Right identify the same thread
- return (m_addrstart == val.m_addrstart) && (m_addrend == val.m_addrend);
- }
-
- // internal state
- offs_t m_addrstart; // starting offset of the range
- offs_t m_addrend; // ending offset of the range
- };
-
// construction/destruction
- memory_access_cache(address_space &space);
+ memory_access_cache(address_space &space,
+ handler_entry_read <Width, AddrShift, Endian> *root_read,
+ handler_entry_write<Width, AddrShift, Endian> *root_write);
+
~memory_access_cache();
// getters
address_space &space() const { return m_space; }
- u8 *ptr() const { return m_ptr; }
- // see if an address is within bounds, or attempt to update it if not
- bool address_is_valid(offs_t address) { return EXPECTED(address >= m_addrstart && address <= m_addrend) || set_cache_region(address); }
+ // see if an address is within bounds, update it if not
+ void check_address_r(offs_t address) {
+ if(address >= m_addrstart_r && address <= m_addrend_r)
+ return;
+ m_root_read->lookup(address, m_addrstart_r, m_addrend_r, m_cache_r);
+ }
- // force a recomputation on the next read
- void force_update() { m_addrend = 0; m_addrstart = 1; }
- void force_update(u16 if_match) { if (m_entry == if_match) force_update(); }
+ void check_address_w(offs_t address) {
+ if(address >= m_addrstart_w && address <= m_addrend_w)
+ return;
+ m_root_write->lookup(address, m_addrstart_w, m_addrend_w, m_cache_w);
+ }
// accessor methods
void *read_ptr(offs_t address) {
- if (address_is_valid(address))
- return &m_ptr[memory_offset_to_byte(address & m_addrmask, AddrShift)];
- return nullptr;
+ check_address_r(address);
+ return m_cache_r->get_ptr(address);
}
- u8 read_byte(offs_t address) { return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); }
- u16 read_word(offs_t address) { return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); }
- u16 read_word(offs_t address, u16 mask) { return memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u16 read_word_unaligned(offs_t address) { return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); }
- u16 read_word_unaligned(offs_t address, u16 mask) { return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u32 read_dword(offs_t address) { return Width == 2 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); }
- u32 read_dword(offs_t address, u32 mask) { return memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u32 read_dword_unaligned(offs_t address) { return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); }
- u32 read_dword_unaligned(offs_t address, u32 mask) { return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u64 read_qword(offs_t address) { return Width == 3 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); }
- u64 read_qword(offs_t address, u64 mask) { return memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
- u64 read_qword_unaligned(offs_t address) { return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); }
- u64 read_qword_unaligned(offs_t address, u64 mask) { return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
-
-
- void remove_intersecting_ranges(offs_t start, offs_t end);
+ u8 read_byte(offs_t address) { address &= m_addrmask; return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); }
+ u16 read_word(offs_t address) { address &= m_addrmask; return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); }
+ u16 read_word(offs_t address, u16 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u16 read_word_unaligned(offs_t address) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); }
+ u16 read_word_unaligned(offs_t address, u16 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u32 read_dword(offs_t address) { address &= m_addrmask; return Width == 2 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); }
+ u32 read_dword(offs_t address, u32 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u32 read_dword_unaligned(offs_t address) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); }
+ u32 read_dword_unaligned(offs_t address, u32 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u64 read_qword(offs_t address) { address &= m_addrmask; return Width == 3 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); }
+ u64 read_qword(offs_t address, u64 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+ u64 read_qword_unaligned(offs_t address) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); }
+ u64 read_qword_unaligned(offs_t address, u64 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); }
+
+ void write_byte(offs_t address, u8 data) { address &= m_addrmask; if (Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xff); }
+ void write_word(offs_t address, u16 data) { address &= m_addrmask; if (Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); }
+ void write_word(offs_t address, u16 data, u16 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_word_unaligned(offs_t address, u16 data) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); }
+ void write_word_unaligned(offs_t address, u16 data, u16 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_dword(offs_t address, u32 data) { address &= m_addrmask; if (Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); }
+ void write_dword(offs_t address, u32 data, u32 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_dword_unaligned(offs_t address, u32 data) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); }
+ void write_dword_unaligned(offs_t address, u32 data, u32 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_qword(offs_t address, u64 data) { address &= m_addrmask; if (Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); }
+ void write_qword(offs_t address, u64 data, u64 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
+ void write_qword_unaligned(offs_t address, u64 data) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); }
+ void write_qword_unaligned(offs_t address, u64 data, u64 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); }
private:
- // internal helpers
- bool set_cache_region(offs_t address);
- cache_range *find_range(offs_t address, u16 &entry);
-
- // internal state
address_space & m_space;
- u8 * m_ptr; // cache access data pointer
- offs_t m_addrmask; // address mask
- offs_t m_addrstart; // minimum valid address
- offs_t m_addrend; // maximum valid address
- u16 m_entry; // live entry
- std::list<cache_range> m_rangelist[TOTAL_MEMORY_BANKS]; // list of ranges for each entry
+
+ int m_notifier_id; // id to remove the notifier on destruction
+
+ offs_t m_addrmask; // address mask
+ offs_t m_addrstart_r; // minimum valid address for reading
+ offs_t m_addrend_r; // maximum valid address for reading
+ offs_t m_addrstart_w; // minimum valid address for writing
+ offs_t m_addrend_w; // maximum valid address for writing
+ handler_entry_read<Width, AddrShift, Endian> *m_cache_r; // read cache
+ handler_entry_write<Width, AddrShift, Endian> *m_cache_w; // write cache
+
+ handler_entry_read <Width, AddrShift, Endian> *m_root_read; // decode tree roots
+ handler_entry_write<Width, AddrShift, Endian> *m_root_write;
NativeType read_native(offs_t address, NativeType mask = ~NativeType(0));
+ void write_native(offs_t address, NativeType data, NativeType mask = ~NativeType(0));
};
@@ -642,17 +874,20 @@ public:
// address_space holds live information about an address space
class address_space
{
- friend class address_table;
- friend class address_table_read;
- friend class address_table_write;
- friend class address_table_setoffset;
- template<int Width, int AddrShift, int Endian> friend class memory_access_cache;
friend class memory_bank;
friend class memory_block;
+ template<int Width, int AddrShift, int Endian> friend class handler_entry_read_unmapped;
+ template<int Width, int AddrShift, int Endian> friend class handler_entry_write_unmapped;
+ template<int Width, int AddrShift, int Endian> friend class memory_access_cache;
+
+ struct notifier_t {
+ std::function<void (read_or_write)> m_notifier;
+ int m_id;
+ };
protected:
// construction/destruction
- address_space(memory_manager &manager, device_memory_interface &memory, int spacenum, bool large);
+ address_space(memory_manager &manager, device_memory_interface &memory, int spacenum);
public:
virtual ~address_space();
@@ -663,7 +898,7 @@ public:
int spacenum() const { return m_spacenum; }
address_map *map() const { return m_map.get(); }
- template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrShift, Endian> *cache() const {
+ template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrShift, Endian> *cache() {
if(AddrShift != m_config.addr_shift())
fatalerror("Requesting cache() with address shift %d while the config says %d\n", AddrShift, m_config.addr_shift());
if(8 << Width != m_config.data_width())
@@ -672,9 +907,26 @@ public:
fatalerror("Requesting cache() with endianness %s while the config says %s\n",
endianness_names[Endian], endianness_names[m_config.endianness()]);
- return static_cast<memory_access_cache<Width, AddrShift, Endian> *>(m_cache);
+ return static_cast<memory_access_cache<Width, AddrShift, Endian> *>(create_cache());
+ }
+
+ int add_change_notifier(std::function<void (read_or_write)> n);
+ void remove_change_notifier(int id);
+
+ void invalidate_caches(read_or_write mode) {
+ if(!(u32(mode) & ~m_in_notification)) {
+ u32 old = m_in_notification;
+ m_in_notification |= u32(mode);
+ for(const auto &n : m_notifiers)
+ n.m_notifier(mode);
+ m_in_notification = old;
+ }
}
+ virtual void validate_reference_counts() const = 0;
+
+ virtual void remove_passthrough(std::unordered_set<handler_entry *> &handlers) = 0;
+
int data_width() const { return m_config.data_width(); }
int addr_width() const { return m_config.addr_width(); }
int alignment() const { return m_config.alignment(); }
@@ -689,23 +941,17 @@ public:
u8 logaddrchars() const { return m_logaddrchars; }
// debug helpers
- const char *get_handler_string(read_or_write readorwrite, offs_t byteaddress);
+ virtual std::string get_handler_string(read_or_write readorwrite, offs_t byteaddress) const = 0;
+ virtual void dump_maps(std::vector<memory_entry> &read_map, std::vector<memory_entry> &write_map) const = 0;
bool log_unmap() const { return m_log_unmap; }
void set_log_unmap(bool log) { m_log_unmap = log; }
- void dump_map(FILE *file, read_or_write readorwrite);
-
- // watchpoint enablers
- virtual void enable_read_watchpoints(bool enable = true) = 0;
- virtual void enable_write_watchpoints(bool enable = true) = 0;
// general accessors
virtual void accessors(data_accessors &accessors) const = 0;
- virtual void *get_read_ptr(offs_t address) = 0;
- virtual void *get_write_ptr(offs_t address) = 0;
+ virtual void *get_read_ptr(offs_t address) const = 0;
+ virtual void *get_write_ptr(offs_t address) const = 0;
// read accessors
- template<int AccessWidth> inline typename handler_entry_size<AccessWidth>::uX read_sized(offs_t address, typename handler_entry_size<AccessWidth>::uX mask);
-
virtual u8 read_byte(offs_t address) = 0;
virtual u16 read_word(offs_t address) = 0;
virtual u16 read_word(offs_t address, u16 mask) = 0;
@@ -735,9 +981,6 @@ public:
virtual void write_qword_unaligned(offs_t address, u64 data) = 0;
virtual void write_qword_unaligned(offs_t address, u64 data, u64 mask) = 0;
- // Set address. This will invoke setoffset handlers for the respective entries.
- virtual void set_address(offs_t address) = 0;
-
// address-to-byte conversion helpers
offs_t address_to_byte(offs_t address) const { return m_config.addr2byte(address); }
offs_t address_to_byte_end(offs_t address) const { return m_config.addr2byte_end(address); }
@@ -769,7 +1012,7 @@ public:
// install ports, banks, RAM (with mirror/mask)
void install_read_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag) { install_readwrite_port(addrstart, addrend, addrmirror, rtag, nullptr); }
void install_write_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *wtag) { install_readwrite_port(addrstart, addrend, addrmirror, nullptr, wtag); }
- void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag);
+ virtual void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag) = 0;
void install_read_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *tag) { install_bank_generic(addrstart, addrend, addrmirror, tag, nullptr); }
void install_write_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *tag) { install_bank_generic(addrstart, addrend, addrmirror, nullptr, tag); }
void install_readwrite_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *tag) { install_bank_generic(addrstart, addrend, addrmirror, tag, tag); }
@@ -786,11 +1029,36 @@ public:
install_device_delegate(addrstart, addrend, device, delegate, unitmask, cswidth);
}
- void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0);
+ virtual void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0) = 0;
+
+ // install taps without mirroring
+ memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); }
+ memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); }
+ memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); }
+ memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); }
+ memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); }
+ memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); }
+ memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); }
+ memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); }
+ memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapr, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); }
+ memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapr, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); }
+ memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapr, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); }
+ memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapr, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); }
+
+ // install taps with mirroring
+ virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapr, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapr, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapr, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr);
+ virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapr, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr);
- // install setoffset handler
- void install_setoffset_handler(offs_t addrstart, offs_t addrend, setoffset_delegate sohandler, u64 unitmask = 0, int cswidth = 0) { install_setoffset_handler(addrstart, addrend, 0, 0, 0, sohandler, unitmask, cswidth); }
- void install_setoffset_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, setoffset_delegate sohandler, u64 unitmask = 0, int cswidth = 0);
// install new-style delegate handlers (short form)
void install_read_handler(offs_t addrstart, offs_t addrend, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); }
@@ -807,18 +1075,18 @@ public:
void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); }
// install new-style delegate handlers (with mirror/mask)
- void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0);
- void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0);
- void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0);
- void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0);
- void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0);
- void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0);
- void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0);
- void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0);
- void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0);
- void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0);
- void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0);
- void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0);
+ virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0;
+ virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0;
// setup
void prepare_map();
@@ -826,33 +1094,27 @@ public:
void allocate_memory();
void locate_memory();
- void invalidate_read_caches();
- void invalidate_read_caches(u16 entry);
- void invalidate_read_caches(offs_t start, offs_t end);
+ template<int Width, int AddrShift, int Endian> handler_entry_read_unmapped <Width, AddrShift, Endian> *get_unmap_r() const { return static_cast<handler_entry_read_unmapped <Width, AddrShift, Endian> *>(m_unmap_r); }
+ template<int Width, int AddrShift, int Endian> handler_entry_write_unmapped<Width, AddrShift, Endian> *get_unmap_w() const { return static_cast<handler_entry_write_unmapped<Width, AddrShift, Endian> *>(m_unmap_w); }
-private:
+protected:
// internal helpers
- virtual address_table_read &read() = 0;
- virtual address_table_write &write() = 0;
- virtual address_table_setoffset &setoffset() = 0;
+ virtual void *create_cache() = 0;
void populate_map_entry(const address_map_entry &entry, read_or_write readorwrite);
- void populate_map_entry_setoffset(const address_map_entry &entry);
- void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet);
- void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr);
- void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag);
- void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank);
+ virtual void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) = 0;
+ virtual void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) = 0;
+ virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag, const char *wtag) = 0;
+ virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) = 0;
void adjust_addresses(offs_t &start, offs_t &end, offs_t &mask, offs_t &mirror);
void *find_backing_memory(offs_t addrstart, offs_t addrend);
bool needs_backing_store(const address_map_entry &entry);
memory_bank &bank_find_or_allocate(const char *tag, offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite);
- memory_bank *bank_find_anonymous(offs_t bytestart, offs_t byteend) const;
address_map_entry *block_assign_intersecting(offs_t bytestart, offs_t byteend, u8 *base);
void check_optimize_all(const char *function, int width, offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror, u64 &nunitmask, int &ncswidth);
void check_optimize_mirror(const char *function, offs_t addrstart, offs_t addrend, offs_t addrmirror, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror);
void check_address(const char *function, offs_t addrstart, offs_t addrend);
-protected:
// private state
const address_space_config &m_config; // configuration of this space
device_t & m_device; // reference to the owning device
@@ -862,19 +1124,21 @@ protected:
u64 m_unmap; // unmapped value
int m_spacenum; // address space index
bool m_log_unmap; // log unmapped accesses in this space?
- void * m_cache; // fast cache-access read info
const char * m_name; // friendly name of the address space
u8 m_addrchars; // number of characters to use for physical addresses
u8 m_logaddrchars; // number of characters to use for logical addresses
- std::function<void *(void *)> m_cache_alloc;
- std::function<void (void *)> m_cache_delete;
- std::function<void (void *)> m_cache_invalidate_read_caches;
- std::function<void (void *, u16)> m_cache_invalidate_read_cache_entry;
- std::function<void (void *, offs_t, offs_t)> m_cache_invalidate_read_cache_range;
- template<int Width, int AddrShift, int Endian> void cache_init();
+ handler_entry *m_unmap_r;
+ handler_entry *m_unmap_w;
-private:
+ handler_entry *m_nop_r;
+ handler_entry *m_nop_w;
+
+ std::vector<std::unique_ptr<memory_passthrough_handler>> m_mphs;
+
+ std::vector<notifier_t> m_notifiers; // notifier list for address map change
+ int m_notifier_id; // next notifier id
+ u32 m_in_notification; // notification(s) currently being done
memory_manager & m_manager; // reference to the owning manager
};
@@ -940,12 +1204,7 @@ class memory_bank
// internal state
address_space & m_space; // address space that references us
read_or_write m_readorwrite; // used for read or write?
- };
- // a bank_entry contains a pointer
- struct bank_entry
- {
- u8 * m_ptr;
};
public:
@@ -955,11 +1214,10 @@ public:
// getters
running_machine &machine() const { return m_machine; }
- int index() const { return m_index; }
int entry() const { return m_curentry; }
bool anonymous() const { return m_anonymous; }
offs_t addrstart() const { return m_addrstart; }
- void *base() const { return *m_baseptr; }
+ void *base() const { return m_baseptr; }
const char *tag() const { return m_tag.c_str(); }
const char *name() const { return m_name.c_str(); }
@@ -980,24 +1238,21 @@ public:
void configure_entry(int entrynum, void *base);
void configure_entries(int startentry, int numentries, void *base, offs_t stride);
void set_entry(int entrynum);
+ void add_notifier(std::function<void (void *)> cb);
private:
- // internal helpers
- void invalidate_references();
- void expand_entries(int entrynum);
-
// internal state
running_machine & m_machine; // need the machine to free our memory
- u8 ** m_baseptr; // pointer to our base pointer in the global array
- u16 m_index; // array index for this handler
+ u8 * m_baseptr; // pointer to our current base pointer
+ std::vector<u8 *> m_entries; // the entries
bool m_anonymous; // are we anonymous or explicit?
offs_t m_addrstart; // start offset
offs_t m_addrend; // end offset
int m_curentry; // current entry
- std::vector<bank_entry> m_entry; // array of entries (dynamically allocated)
std::string m_name; // friendly name for this bank
std::string m_tag; // tag for this bank
- std::vector<std::unique_ptr<bank_reference>> m_reflist; // linked list of address spaces referencing this bank
+ std::vector<std::unique_ptr<bank_reference>> m_reflist; // list of address spaces referencing this bank
+ std::vector<std::function<void (void *)>> m_alloc_notifier; // list of notifier targets when allocating
};
@@ -1085,6 +1340,7 @@ private:
class memory_manager
{
friend class address_space;
+ template<int Width, int AddrShift, endianness_t Endian> friend class address_space_specific;
friend memory_region::memory_region(running_machine &machine, const char *name, u32 length, u8 width, endianness_t endian);
public:
// construction/destruction
@@ -1097,32 +1353,26 @@ public:
const std::unordered_map<std::string, std::unique_ptr<memory_region>> &regions() const { return m_regionlist; }
const std::unordered_map<std::string, std::unique_ptr<memory_share>> &shares() const { return m_sharelist; }
- // pointers to a bank pointer (internal usage only)
- u8 **bank_pointer_addr(u8 index) { return &m_bank_ptr[index]; }
-
// regions
memory_region *region_alloc(const char *name, u32 length, u8 width, endianness_t endian);
void region_free(const char *name);
memory_region *region_containing(const void *memory, offs_t bytes) const;
+ memory_bank *find(const char *tag) const;
+ memory_bank *find(address_space &space, offs_t addrstart, offs_t addrend) const;
+ memory_bank *allocate(address_space &space, offs_t addrstart, offs_t addrend, const char *tag = nullptr);
+
private:
- // internal helpers
- void bank_reattach();
void allocate(device_memory_interface &memory);
// internal state
running_machine & m_machine; // reference to the machine
bool m_initialized; // have we completed initialization?
- u8 * m_bank_ptr[TOTAL_MEMORY_BANKS]; // array of bank pointers
-
std::vector<std::unique_ptr<memory_block>> m_blocklist; // head of the list of memory blocks
- std::unordered_map<std::string,std::unique_ptr<memory_bank>> m_banklist; // data gathered for each bank
- u16 m_banknext; // next bank to allocate
-
+ std::unordered_map<std::string, std::unique_ptr<memory_bank>> m_banklist; // data gathered for each bank
std::unordered_map<std::string, std::unique_ptr<memory_share>> m_sharelist; // map for share lookups
-
std::unordered_map<std::string, std::unique_ptr<memory_region>> m_regionlist; // list of memory regions
};
@@ -1151,9 +1401,6 @@ private:
#define DECLARE_READ64_MEMBER(name) u64 name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u64 mem_mask = 0xffffffffffffffffU)
#define DECLARE_WRITE64_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u64 data, ATTR_UNUSED u64 mem_mask = 0xffffffffffffffffU)
-#define SETOFFSET_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset)
-#define DECLARE_SETOFFSET_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset)
-
// device delegate macros
#define READ8_DELEGATE(_class, _member) read8_delegate(FUNC(_class::_member), this)
#define WRITE8_DELEGATE(_class, _member) write8_delegate(FUNC(_class::_member), this)
@@ -1227,17 +1474,22 @@ private:
#define DWORD_ALIGNED(a) (((a) & 3) == 0)
#define QWORD_ALIGNED(a) (((a) & 7) == 0)
-// inline templates
-template<> inline u8 address_space::read_sized<0>(offs_t address, u8 mask) { return read_byte(address); }
-template<> inline u16 address_space::read_sized<1>(offs_t address, u16 mask) { return read_word(address, mask); }
-template<> inline u32 address_space::read_sized<2>(offs_t address, u32 mask) { return read_dword(address, mask); }
-template<> inline u64 address_space::read_sized<3>(offs_t address, u64 mask) { return read_qword(address, mask); }
template<int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX memory_access_cache<Width, AddrShift, Endian>::read_native(offs_t address, typename handler_entry_size<Width>::uX mask)
{
- if (address_is_valid(address))
- return *reinterpret_cast<NativeType *>(&m_ptr[memory_offset_to_byte(address & m_addrmask, AddrShift)]);
- return m_space.read_sized<Width>(address, mask);
+ check_address_r(address);
+ return m_cache_r->read(address, mask);
+}
+
+template<int Width, int AddrShift, int Endian> void memory_access_cache<Width, AddrShift, Endian>::write_native(offs_t address, typename handler_entry_size<Width>::uX data, typename handler_entry_size<Width>::uX mask)
+{
+ check_address_w(address);
+ m_cache_w->write(address, data, mask);
+}
+
+void memory_passthrough_handler::remove()
+{
+ m_space.remove_passthrough(m_handlers);
}
#endif /* MAME_EMU_EMUMEM_H */
diff --git a/src/emu/emumem_hea.h b/src/emu/emumem_hea.h
new file mode 100644
index 00000000000..7de0d7913a0
--- /dev/null
+++ b/src/emu/emumem_hea.h
@@ -0,0 +1,40 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// handler_entry_read_address/handler_entry_write_address
+
+// parent class for final handlers which want an address base and a mask
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_address : public handler_entry_read<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+
+ handler_entry_read_address(address_space *space, u32 flags) : handler_entry_read<Width, AddrShift, Endian>(space, flags) {}
+ ~handler_entry_read_address() = default;
+
+ inline void set_address_info(offs_t base, offs_t mask) {
+ m_address_base = base;
+ m_address_mask = mask;
+ }
+
+protected:
+ offs_t m_address_base, m_address_mask;
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_address : public handler_entry_write<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+
+ handler_entry_write_address(address_space *space, u32 flags) : handler_entry_write<Width, AddrShift, Endian>(space, flags) {}
+ ~handler_entry_write_address() = default;
+
+ inline void set_address_info(offs_t base, offs_t mask) {
+ m_address_base = base;
+ m_address_mask = mask;
+ }
+
+protected:
+ offs_t m_address_base, m_address_mask;
+};
diff --git a/src/emu/emumem_hedp.cpp b/src/emu/emumem_hedp.cpp
new file mode 100644
index 00000000000..330117f2637
--- /dev/null
+++ b/src/emu/emumem_hedp.cpp
@@ -0,0 +1,144 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hea.h"
+#include "emumem_hedp.h"
+
+template<int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX handler_entry_read_delegate<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask)
+{
+ return m_delegate(*inh::m_space, ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift), mem_mask);
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_read_delegate<Width, AddrShift, Endian>::name() const
+{
+ return m_delegate.name();
+}
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_delegate<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask)
+{
+ m_delegate(*inh::m_space, ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift), data, mem_mask);
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_write_delegate<Width, AddrShift, Endian>::name() const
+{
+ return m_delegate.name();
+}
+
+
+
+
+template<int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX handler_entry_read_ioport<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask)
+{
+ return m_port->read();
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_read_ioport<Width, AddrShift, Endian>::name() const
+{
+ return m_port->tag();
+}
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_ioport<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask)
+{
+ m_port->write(data, mem_mask);
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_write_ioport<Width, AddrShift, Endian>::name() const
+{
+ return m_port->tag();
+}
+
+
+
+template class handler_entry_read_delegate<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_delegate<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_delegate<3, -3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_delegate<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_delegate<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_delegate<3, -3, ENDIANNESS_BIG>;
+
+
+template class handler_entry_read_ioport<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_ioport<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_ioport<3, -3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_ioport<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_ioport<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_ioport<3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hedp.h b/src/emu/emumem_hedp.h
new file mode 100644
index 00000000000..e9539bd21d9
--- /dev/null
+++ b/src/emu/emumem_hedp.h
@@ -0,0 +1,81 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// handler_entry_read_delegate/handler_entry_write_delegate
+
+// Executes an access through called a delegate, usually containing a handler or a lambda
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_delegate : public handler_entry_read_address<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using READ = typename handler_entry_size<Width>::READ;
+ using inh = handler_entry_read_address<Width, AddrShift, Endian>;
+
+ handler_entry_read_delegate(address_space *space, READ delegate) : handler_entry_read_address<Width, AddrShift, Endian>(space, 0), m_delegate(delegate) {}
+ ~handler_entry_read_delegate() = default;
+
+ uX read(offs_t offset, uX mem_mask) override;
+
+ std::string name() const override;
+
+private:
+ READ m_delegate;
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_delegate : public handler_entry_write_address<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using WRITE = typename handler_entry_size<Width>::WRITE;
+ using inh = handler_entry_write_address<Width, AddrShift, Endian>;
+
+ handler_entry_write_delegate(address_space *space, WRITE delegate) : handler_entry_write_address<Width, AddrShift, Endian>(space, 0), m_delegate(delegate) {}
+ ~handler_entry_write_delegate() = default;
+
+ void write(offs_t offset, uX data, uX mem_mask) override;
+
+ std::string name() const override;
+
+private:
+ WRITE m_delegate;
+};
+
+
+// handler_entry_read_ioport/handler_entry_write_ioport
+
+// Accesses an ioport
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_ioport : public handler_entry_read<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_read<Width, AddrShift, Endian>;
+
+ handler_entry_read_ioport(address_space *space, ioport_port *port) : handler_entry_read<Width, AddrShift, Endian>(space, 0), m_port(port) {}
+ ~handler_entry_read_ioport() = default;
+
+ uX read(offs_t offset, uX mem_mask) override;
+
+ std::string name() const override;
+
+private:
+ ioport_port *m_port;
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_ioport : public handler_entry_write<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_write<Width, AddrShift, Endian>;
+
+ handler_entry_write_ioport(address_space *space, ioport_port *port) : handler_entry_write<Width, AddrShift, Endian>(space, 0), m_port(port) {}
+ ~handler_entry_write_ioport() = default;
+
+ void write(offs_t offset, uX data, uX mem_mask) override;
+
+ std::string name() const override;
+
+private:
+ ioport_port *m_port;
+};
diff --git a/src/emu/emumem_hedr.h b/src/emu/emumem_hedr.h
new file mode 100644
index 00000000000..87fff01635e
--- /dev/null
+++ b/src/emu/emumem_hedr.h
@@ -0,0 +1,61 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// handler_entry_read_dispatch
+
+// dispatches an access among multiple handlers indexed on part of the address
+
+template<int HighBits, int Width, int AddrShift, int Endian> class handler_entry_read_dispatch : public handler_entry_read<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_read<Width, AddrShift, Endian>;
+ using mapping = typename inh::mapping;
+
+ handler_entry_read_dispatch(address_space *space, const handler_entry::range &init, handler_entry_read<Width, AddrShift, Endian> *handler);
+ ~handler_entry_read_dispatch();
+
+ uX read(offs_t offset, uX mem_mask) override;
+ void *get_ptr(offs_t offset) const override;
+ void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift, Endian> *&handler) const override;
+
+ void dump_map(std::vector<memory_entry> &map) const override;
+
+ std::string name() const override;
+
+ void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler) override;
+ void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler) override;
+ void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) override;
+ void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) override;
+ void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) override;
+ void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) override;
+ void detach(const std::unordered_set<handler_entry *> &handlers) override;
+ void range_cut_before(offs_t address, int start = COUNT);
+ void range_cut_after(offs_t address, int start = -1);
+
+ void enumerate_references(handler_entry::reflist &refs) const override;
+
+protected:
+ static constexpr u32 LowBits = handler_entry_dispatch_lowbits(HighBits, Width, AddrShift);
+ static constexpr u32 BITCOUNT = HighBits > LowBits ? HighBits - LowBits : 0;
+ static constexpr u32 COUNT = 1 << BITCOUNT;
+ static constexpr offs_t BITMASK = make_bitmask<offs_t>(BITCOUNT);
+ static constexpr offs_t LOWMASK = make_bitmask<offs_t>(LowBits);
+ static constexpr offs_t HIGHMASK = make_bitmask<offs_t>(HighBits) ^ LOWMASK;
+ static constexpr offs_t UPMASK = ~make_bitmask<offs_t>(HighBits);
+
+ handler_entry_read<Width, AddrShift, Endian> *m_dispatch[COUNT];
+ handler_entry::range m_ranges[COUNT];
+
+private:
+ void populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler);
+ void populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler);
+
+ void populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings);
+ void populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings);
+ void mismatched_patch(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift, Endian> *&target);
+
+ void populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings);
+ void populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings);
+ void passthrough_patch(handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift, Endian> *&target);
+};
diff --git a/src/emu/emumem_hedr.ipp b/src/emu/emumem_hedr.ipp
new file mode 100644
index 00000000000..5f368b57448
--- /dev/null
+++ b/src/emu/emumem_hedr.ipp
@@ -0,0 +1,479 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_mud.h"
+#include "emumem_hea.h"
+#include "emumem_heu.h"
+#include "emumem_heun.h"
+#include "emumem_hep.h"
+#include "emumem_hedr.h"
+
+template<int HighBits, int Width, int AddrShift, int Endian> handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::handler_entry_read_dispatch(address_space *space, const handler_entry::range &init, handler_entry_read<Width, AddrShift, Endian> *handler) : handler_entry_read<Width, AddrShift, Endian>(space, handler_entry::F_DISPATCH)
+{
+ if (!handler)
+ handler = space->get_unmap_r<Width, AddrShift, Endian>();
+ handler->ref(COUNT);
+ for(unsigned int i=0; i != COUNT; i++) {
+ m_dispatch[i] = handler;
+ m_ranges[i] = init;
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::~handler_entry_read_dispatch()
+{
+ for(unsigned int i=0; i != COUNT; i++)
+ m_dispatch[i]->unref();
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::enumerate_references(handler_entry::reflist &refs) const
+{
+ for(unsigned int i=0; i != COUNT; i++)
+ refs.add(m_dispatch[i]);
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::dump_map(std::vector<memory_entry> &map) const
+{
+ offs_t cur = map.empty() ? 0 : map.back().end + 1;
+ offs_t base = cur & UPMASK;
+ do {
+ offs_t entry = (cur >> LowBits) & BITMASK;
+ if(m_dispatch[entry]->is_dispatch())
+ m_dispatch[entry]->dump_map(map);
+ else
+ map.emplace_back(memory_entry{ m_ranges[entry].start, m_ranges[entry].end, m_dispatch[entry] });
+ cur = map.back().end + 1;
+ } while(cur && !((cur ^ base) & UPMASK));
+
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask)
+{
+ return m_dispatch[(offset >> LowBits) & BITMASK]->read(offset, mem_mask);
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void *handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::get_ptr(offs_t offset) const
+{
+ return m_dispatch[(offset >> LowBits) & BITMASK]->get_ptr(offset);
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> std::string handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::name() const
+{
+ return "dispatch";
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift, Endian> *&handler) const
+{
+ offs_t slot = (address >> LowBits) & BITMASK;
+ auto h = m_dispatch[slot];
+ if(h->is_dispatch())
+ h->lookup(address, start, end, handler);
+ else {
+ start = m_ranges[slot].start;
+ end = m_ranges[slot].end;
+ handler = h;
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::range_cut_before(offs_t address, int start)
+{
+ while(--start >= 0) {
+ if(int(LowBits) > -AddrShift && m_dispatch[start]->is_dispatch()) {
+ static_cast<handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian> *>(m_dispatch[start])->range_cut_before(address);
+ break;
+ }
+ if(m_ranges[start].end <= address)
+ break;
+ m_ranges[start].end = address;
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::range_cut_after(offs_t address, int start)
+{
+ while(++start < COUNT) {
+ if(int(LowBits) > -AddrShift && m_dispatch[start]->is_dispatch()) {
+ static_cast<handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian> *>(m_dispatch[start])->range_cut_after(address);
+ break;
+ }
+ if(m_ranges[start].start >= address)
+ break;
+ m_ranges[start].start = address;
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_nomirror(start, end, ostart, oend, handler);
+ else {
+ auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_nomirror(start, end, ostart, oend, handler);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler)
+{
+ offs_t start_entry = start >> LowBits;
+ offs_t end_entry = end >> LowBits;
+ range_cut_before(ostart-1, start_entry);
+ range_cut_after(oend+1, end_entry);
+
+ if(LowBits <= Width + AddrShift) {
+ handler->ref(end_entry - start_entry);
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ m_dispatch[ent]->unref();
+ m_dispatch[ent] = handler;
+ m_ranges[ent].set(ostart, oend);
+ }
+
+ } else if(start_entry == end_entry) {
+ if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) {
+ m_dispatch[start_entry]->unref();
+ m_dispatch[start_entry] = handler;
+ m_ranges[start_entry].set(ostart, oend);
+ } else
+ populate_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, handler);
+
+ } else {
+ if(start & LOWMASK) {
+ populate_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, handler);
+ start_entry++;
+ if(start_entry <= end_entry)
+ handler->ref();
+ }
+ if((end & LOWMASK) != LOWMASK) {
+ populate_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, handler);
+ end_entry--;
+ if(start_entry <= end_entry)
+ handler->ref();
+ }
+
+ if(start_entry <= end_entry) {
+ handler->ref(end_entry - start_entry);
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ m_dispatch[ent]->unref();
+ m_dispatch[ent] = handler;
+ m_ranges[ent].set(ostart, oend);
+ }
+ }
+ }
+}
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_mirror(start, end, ostart, oend, mirror, handler);
+ else {
+ auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_mirror(start, end, ostart, oend, mirror, handler);
+ }
+}
+
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler)
+{
+ offs_t hmirror = mirror & HIGHMASK;
+ offs_t lmirror = mirror & LOWMASK;
+
+ if(lmirror) {
+ // If lmirror is non-zero, then each mirror instance is a single entry
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ offs_t base_entry = start >> LowBits;
+ start &= LOWMASK;
+ end &= LOWMASK;
+ do {
+ if(offset)
+ handler->ref();
+ populate_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, handler);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ } else {
+ // If lmirror is zero, call the nomirror version as needed
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ do {
+ if(offset)
+ handler->ref();
+ populate_nomirror(start | offset, end | offset, ostart | offset, oend | offset, handler);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::mismatched_patch(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift, Endian> *&target)
+{
+ u8 ukey = descriptor.rkey_to_ukey(rkey);
+ handler_entry_read<Width, AddrShift, Endian> *original = target->is_units() ? target : nullptr;
+ handler_entry_read<Width, AddrShift, Endian> *replacement = nullptr;
+ for(const auto &p : mappings)
+ if(p.ukey == ukey && p.original == original) {
+ replacement = p.patched;
+ break;
+ }
+ if(!replacement) {
+ if(original)
+ replacement = new handler_entry_read_units<Width, AddrShift, Endian>(descriptor, ukey, static_cast<handler_entry_read_units<Width, AddrShift, Endian> *>(original));
+ else
+ replacement = new handler_entry_read_units<Width, AddrShift, Endian>(descriptor, ukey, inh::m_space);
+
+ mappings.emplace_back(mapping{ original, replacement, ukey });
+ } else
+ replacement->ref();
+ target->unref();
+ target = replacement;
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings);
+ else {
+ auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings)
+{
+ offs_t start_entry = start >> LowBits;
+ offs_t end_entry = end >> LowBits;
+ range_cut_before(ostart-1, start_entry);
+ range_cut_after(oend+1, end_entry);
+
+ if(LowBits <= Width + AddrShift) {
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ u8 rkey1 = rkey;
+ if(ent != start_entry)
+ rkey1 &= ~handler_entry::START;
+ if(ent != end_entry)
+ rkey1 &= ~handler_entry::END;
+ mismatched_patch(descriptor, rkey1, mappings, m_dispatch[ent]);
+ m_ranges[ent].set(ostart, oend);
+ }
+
+ } else if(start_entry == end_entry) {
+ if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) {
+ if(m_dispatch[start_entry]->is_dispatch())
+ m_dispatch[start_entry]->populate_mismatched_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey, mappings);
+ else {
+ mismatched_patch(descriptor, rkey, mappings, m_dispatch[start_entry]);
+ m_ranges[start_entry].set(ostart, oend);
+ }
+ } else
+ populate_mismatched_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey, mappings);
+
+ } else {
+ if(start & LOWMASK) {
+ populate_mismatched_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::END, mappings);
+ start_entry++;
+ rkey &= ~handler_entry::START;
+ }
+ if((end & LOWMASK) != LOWMASK) {
+ populate_mismatched_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::START, mappings);
+ end_entry--;
+ rkey &= ~handler_entry::END;
+ }
+
+ if(start_entry <= end_entry) {
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ u8 rkey1 = rkey;
+ if(ent != start_entry)
+ rkey1 &= ~handler_entry::START;
+ if(ent != end_entry)
+ rkey1 &= ~handler_entry::END;
+ if(m_dispatch[ent]->is_dispatch())
+ m_dispatch[ent]->populate_mismatched_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey1, mappings);
+ else {
+ mismatched_patch(descriptor, rkey1, mappings, m_dispatch[ent]);
+ m_ranges[ent].set(ostart, oend);
+ }
+ }
+ }
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_mismatched_mirror(start, end, ostart, oend, mirror, descriptor, mappings);
+ else {
+ auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_mismatched_mirror(start, end, ostart, oend, mirror, descriptor, mappings);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings)
+{
+ offs_t hmirror = mirror & HIGHMASK;
+ offs_t lmirror = mirror & LOWMASK;
+
+ if(lmirror) {
+ // If lmirror is non-zero, then each mirror instance is a single entry
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ offs_t base_entry = start >> LowBits;
+ start &= LOWMASK;
+ end &= LOWMASK;
+ do {
+ populate_mismatched_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, descriptor, mappings);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ } else {
+ // If lmirror is zero, call the nomirror version as needed
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ do {
+ populate_mismatched_nomirror(start | offset, end | offset, ostart | offset, oend | offset, descriptor, handler_entry::START|handler_entry::END, mappings);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ }
+}
+
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::passthrough_patch(handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift, Endian> *&target)
+{
+ handler_entry_read<Width, AddrShift, Endian> *original = target;
+ handler_entry_read<Width, AddrShift, Endian> *replacement = nullptr;
+ for(const auto &p : mappings)
+ if(p.original == original) {
+ replacement = p.patched;
+ break;
+ }
+ if(!replacement) {
+ replacement = handler->instantiate(original);
+ mappings.emplace_back(mapping{ original, replacement });
+ } else
+ replacement->ref();
+ target->unref();
+ target = replacement;
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings);
+ else {
+ auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ offs_t start_entry = (start & HIGHMASK) >> LowBits;
+ offs_t end_entry = (end & HIGHMASK) >> LowBits;
+ range_cut_before(ostart-1, start_entry);
+ range_cut_after(oend+1, end_entry);
+
+ if(LowBits <= Width + AddrShift) {
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ if(m_dispatch[start_entry]->is_dispatch())
+ m_dispatch[start_entry]->populate_passthrough_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings);
+ else {
+ passthrough_patch(handler, mappings, m_dispatch[ent]);
+ m_ranges[ent].intersect(ostart, oend);
+ }
+ }
+
+ } else if(start_entry == end_entry) {
+ if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) {
+ passthrough_patch(handler, mappings, m_dispatch[start_entry]);
+ m_ranges[start_entry].intersect(ostart, oend);
+ } else
+ populate_passthrough_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings);
+
+ } else {
+ if(start & LOWMASK) {
+ populate_passthrough_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, handler, mappings);
+ start_entry++;
+ }
+ if((end & LOWMASK) != LOWMASK) {
+ populate_passthrough_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, handler, mappings);
+ end_entry--;
+ }
+
+ if(start_entry <= end_entry) {
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ if(m_dispatch[ent]->is_dispatch())
+ m_dispatch[ent]->populate_passthrough_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings);
+ else {
+ passthrough_patch(handler, mappings, m_dispatch[ent]);
+ m_ranges[ent].intersect(ostart, oend);
+ }
+ }
+ }
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_passthrough_mirror(start, end, ostart, oend, mirror, handler, mappings);
+ else {
+ auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_passthrough_mirror(start, end, ostart, oend, mirror, handler, mappings);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ offs_t hmirror = mirror & HIGHMASK;
+ offs_t lmirror = mirror & LOWMASK;
+
+ if(lmirror) {
+ // If lmirror is non-zero, then each mirror instance is a single entry
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ offs_t base_entry = start >> LowBits;
+ start &= LOWMASK;
+ end &= LOWMASK;
+ do {
+ populate_passthrough_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, handler, mappings);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ } else {
+ // If lmirror is zero, call the nomirror version as needed
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ do {
+ populate_passthrough_nomirror(start | offset, end | offset, ostart | offset, oend | offset, handler, mappings);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers)
+{
+ for(unsigned int i=0; i != COUNT; i++) {
+ m_dispatch[i]->unref();
+ if(!m_dispatch[i]->is_passthrough())
+ continue;
+ auto np = static_cast<handler_entry_read_passthrough<Width, AddrShift, Endian> *>(m_dispatch[i]);
+
+ if(handlers.find(np) != handlers.end()) {
+ m_dispatch[i] = np->get_subhandler();
+ m_dispatch[i]->ref();
+ np->unref();
+
+ } else
+ np->detach(handlers);
+ }
+}
diff --git a/src/emu/emumem_hedr0.cpp b/src/emu/emumem_hedr0.cpp
new file mode 100644
index 00000000000..b39b88263dc
--- /dev/null
+++ b/src/emu/emumem_hedr0.cpp
@@ -0,0 +1,171 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hedr.ipp"
+
+
+template class handler_entry_read_dispatch< 1, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 1, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 2, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 2, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 3, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 0, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 1, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 1, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 2, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 2, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 3, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 1, 3, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 1, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 1, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 2, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 2, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 3, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 1, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 1, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 1, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 2, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 2, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 3, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 1, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 2, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 2, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 3, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 2, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 2, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 2, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 3, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 2, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 2, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 2, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 3, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 2, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 3, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 3, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 3, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 3, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 3, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 3, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 3, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 3, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 4, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 4, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 5, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 5, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 6, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 6, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 7, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 7, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch< 8, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 8, 3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hedr1.cpp b/src/emu/emumem_hedr1.cpp
new file mode 100644
index 00000000000..5a06dab08c3
--- /dev/null
+++ b/src/emu/emumem_hedr1.cpp
@@ -0,0 +1,192 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hedr.ipp"
+
+template class handler_entry_read_dispatch< 9, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 0, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 1, 3, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 1, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 1, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 2, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 2, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 2, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 3, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 3, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 3, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch< 9, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch< 9, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<10, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<10, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<11, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<11, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<12, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<12, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<13, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<13, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<14, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<14, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<15, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<15, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<16, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<16, 3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hedr2.cpp b/src/emu/emumem_hedr2.cpp
new file mode 100644
index 00000000000..4c80f2423c4
--- /dev/null
+++ b/src/emu/emumem_hedr2.cpp
@@ -0,0 +1,192 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hedr.ipp"
+
+template class handler_entry_read_dispatch<17, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 0, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 1, 3, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 1, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 1, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 2, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 2, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 2, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 3, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 3, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 3, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<17, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<17, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<18, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<18, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<19, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<19, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<20, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<20, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<21, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<21, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<22, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<22, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<23, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<23, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<24, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<24, 3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hedr3.cpp b/src/emu/emumem_hedr3.cpp
new file mode 100644
index 00000000000..97fa6d1c9e3
--- /dev/null
+++ b/src/emu/emumem_hedr3.cpp
@@ -0,0 +1,193 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hedr.ipp"
+
+
+template class handler_entry_read_dispatch<25, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 0, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 1, 3, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 1, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 1, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 2, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 2, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 2, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 3, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 3, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 3, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_read_dispatch<25, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<25, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<26, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<26, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<27, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<27, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<28, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<28, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<29, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<29, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<31, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<31, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<30, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<30, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_read_dispatch<32, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_dispatch<32, 3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hedw.h b/src/emu/emumem_hedw.h
new file mode 100644
index 00000000000..ed51d826f97
--- /dev/null
+++ b/src/emu/emumem_hedw.h
@@ -0,0 +1,61 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// handler_entry_write_dispatch
+
+// dispatches an access among multiple handlers indexed on part of the address
+
+template<int HighBits, int Width, int AddrShift, int Endian> class handler_entry_write_dispatch : public handler_entry_write<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_write<Width, AddrShift, Endian>;
+ using mapping = typename inh::mapping;
+
+ handler_entry_write_dispatch(address_space *space, const handler_entry::range &init, handler_entry_write<Width, AddrShift, Endian> *handler);
+ ~handler_entry_write_dispatch();
+
+ void write(offs_t offset, uX data, uX mem_mask) override;
+ void *get_ptr(offs_t offset) const override;
+ void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift, Endian> *&handler) const override;
+
+ void dump_map(std::vector<memory_entry> &map) const override;
+
+ std::string name() const override;
+
+ void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler) override;
+ void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler) override;
+ void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) override;
+ void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) override;
+ void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) override;
+ void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) override;
+ void detach(const std::unordered_set<handler_entry *> &handlers) override;
+ void range_cut_before(offs_t address, int start = COUNT);
+ void range_cut_after(offs_t address, int start = -1);
+
+ void enumerate_references(handler_entry::reflist &refs) const override;
+
+protected:
+ static constexpr u32 LowBits = handler_entry_dispatch_lowbits(HighBits, Width, AddrShift);
+ static constexpr u32 BITCOUNT = HighBits > LowBits ? HighBits - LowBits : 0;
+ static constexpr u32 COUNT = 1 << BITCOUNT;
+ static constexpr offs_t BITMASK = make_bitmask<offs_t>(BITCOUNT);
+ static constexpr offs_t LOWMASK = make_bitmask<offs_t>(LowBits);
+ static constexpr offs_t HIGHMASK = make_bitmask<offs_t>(HighBits) ^ LOWMASK;
+ static constexpr offs_t UPMASK = ~make_bitmask<offs_t>(HighBits);
+
+ handler_entry_write<Width, AddrShift, Endian> *m_dispatch[COUNT];
+ handler_entry::range m_ranges[COUNT];
+
+private:
+ void populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler);
+ void populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler);
+
+ void populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings);
+ void populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings);
+ void mismatched_patch(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift, Endian> *&target);
+
+ void populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings);
+ void populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings);
+ void passthrough_patch(handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift, Endian> *&target);
+};
diff --git a/src/emu/emumem_hedw.ipp b/src/emu/emumem_hedw.ipp
new file mode 100644
index 00000000000..d50de2262be
--- /dev/null
+++ b/src/emu/emumem_hedw.ipp
@@ -0,0 +1,479 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_mud.h"
+#include "emumem_hea.h"
+#include "emumem_heu.h"
+#include "emumem_heun.h"
+#include "emumem_hep.h"
+#include "emumem_hedw.h"
+
+
+template<int HighBits, int Width, int AddrShift, int Endian> handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::handler_entry_write_dispatch(address_space *space, const handler_entry::range &init, handler_entry_write<Width, AddrShift, Endian> *handler) : handler_entry_write<Width, AddrShift, Endian>(space, handler_entry::F_DISPATCH)
+{
+ if (!handler)
+ handler = space->get_unmap_w<Width, AddrShift, Endian>();
+ handler->ref(COUNT);
+ for(unsigned int i=0; i != COUNT; i++) {
+ m_dispatch[i] = handler;
+ m_ranges[i] = init;
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::~handler_entry_write_dispatch()
+{
+ for(unsigned int i=0; i != COUNT; i++)
+ m_dispatch[i]->unref();
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::enumerate_references(handler_entry::reflist &refs) const
+{
+ for(unsigned int i=0; i != COUNT; i++)
+ refs.add(m_dispatch[i]);
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::dump_map(std::vector<memory_entry> &map) const
+{
+ offs_t cur = map.empty() ? 0 : map.back().end + 1;
+ offs_t base = cur & UPMASK;
+ do {
+ offs_t entry = (cur >> LowBits) & BITMASK;
+ if(m_dispatch[entry]->is_dispatch())
+ m_dispatch[entry]->dump_map(map);
+ else
+ map.emplace_back(memory_entry{ m_ranges[entry].start, m_ranges[entry].end, m_dispatch[entry] });
+ cur = map.back().end + 1;
+ } while(cur && !((cur ^ base) & UPMASK));
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask)
+{
+ m_dispatch[(offset >> LowBits) & BITMASK]->write(offset, data, mem_mask);
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void *handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::get_ptr(offs_t offset) const
+{
+ return m_dispatch[(offset >> LowBits) & BITMASK]->get_ptr(offset);
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> std::string handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::name() const
+{
+ return "dispatch";
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift, Endian> *&handler) const
+{
+ offs_t slot = (address >> LowBits) & BITMASK;
+ auto h = m_dispatch[slot];
+ if(h->is_dispatch())
+ h->lookup(address, start, end, handler);
+ else {
+ start = m_ranges[slot].start;
+ end = m_ranges[slot].end;
+ handler = h;
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::range_cut_before(offs_t address, int start)
+{
+ while(--start >= 0) {
+ if(int(LowBits) > -AddrShift && m_dispatch[start]->is_dispatch()) {
+ static_cast<handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian> *>(m_dispatch[start])->range_cut_before(address);
+ break;
+ }
+ if(m_ranges[start].end <= address)
+ break;
+ m_ranges[start].end = address;
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::range_cut_after(offs_t address, int start)
+{
+ while(++start < COUNT) {
+ if(int(LowBits) > -AddrShift && m_dispatch[start]->is_dispatch()) {
+ static_cast<handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian> *>(m_dispatch[start])->range_cut_after(address);
+ break;
+ }
+ if(m_ranges[start].start >= address)
+ break;
+ m_ranges[start].start = address;
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_nomirror(start, end, ostart, oend, handler);
+ else {
+ auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_nomirror(start, end, ostart, oend, handler);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler)
+{
+ offs_t start_entry = (start & HIGHMASK) >> LowBits;
+ offs_t end_entry = (end & HIGHMASK) >> LowBits;
+ range_cut_before(ostart-1, start_entry);
+ range_cut_after(oend+1, end_entry);
+
+ if(LowBits <= Width + AddrShift) {
+ handler->ref(end_entry - start_entry);
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ m_dispatch[ent]->unref();
+ m_dispatch[ent] = handler;
+ m_ranges[ent].set(ostart, oend);
+ }
+
+ } else if(start_entry == end_entry) {
+ if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) {
+ m_dispatch[start_entry]->unref();
+ m_dispatch[start_entry] = handler;
+ m_ranges[start_entry].set(ostart, oend);
+ } else
+ populate_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, handler);
+
+ } else {
+ if(start & LOWMASK) {
+ populate_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, handler);
+ start_entry++;
+ if(start_entry <= end_entry)
+ handler->ref();
+ }
+ if((end & LOWMASK) != LOWMASK) {
+ populate_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, handler);
+ end_entry--;
+ if(start_entry <= end_entry)
+ handler->ref();
+ }
+
+ if(start_entry <= end_entry) {
+ handler->ref(end_entry - start_entry);
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ m_dispatch[ent]->unref();
+ m_dispatch[ent] = handler;
+ m_ranges[ent].set(ostart, oend);
+ }
+ }
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_mirror(start, end, ostart, oend, mirror, handler);
+ else {
+ auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_mirror(start, end, ostart, oend, mirror, handler);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler)
+{
+ offs_t hmirror = mirror & HIGHMASK;
+ offs_t lmirror = mirror & LOWMASK;
+
+ if(lmirror) {
+ // If lmirror is non-zero, then each mirror instance is a single entry
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ offs_t base_entry = start >> LowBits;
+ start &= LOWMASK;
+ end &= LOWMASK;
+ do {
+ if(offset)
+ handler->ref();
+ populate_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, handler);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ } else {
+ // If lmirror is zero, call the nomirror version as needed
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ do {
+ if(offset)
+ handler->ref();
+ populate_nomirror(start | offset, end | offset, ostart | offset, oend | offset, handler);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::mismatched_patch(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift, Endian> *&target)
+{
+ u8 ukey = descriptor.rkey_to_ukey(rkey);
+ handler_entry_write<Width, AddrShift, Endian> *original = target->is_units() ? target : nullptr;
+ handler_entry_write<Width, AddrShift, Endian> *replacement = nullptr;
+ for(const auto &p : mappings)
+ if(p.ukey == ukey && p.original == original) {
+ replacement = p.patched;
+ break;
+ }
+ if(!replacement) {
+ if(original)
+ replacement = new handler_entry_write_units<Width, AddrShift, Endian>(descriptor, ukey, static_cast<handler_entry_write_units<Width, AddrShift, Endian> *>(original));
+ else
+ replacement = new handler_entry_write_units<Width, AddrShift, Endian>(descriptor, ukey, inh::m_space);
+
+ mappings.emplace_back(mapping{ original, replacement, ukey });
+ } else
+ replacement->ref();
+ target->unref();
+ target = replacement;
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings);
+ else {
+ auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings)
+{
+ offs_t start_entry = (start & HIGHMASK) >> LowBits;
+ offs_t end_entry = (end & HIGHMASK) >> LowBits;
+ range_cut_before(ostart-1, start_entry);
+ range_cut_after(oend+1, end_entry);
+
+ if(LowBits <= Width + AddrShift) {
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ u8 rkey1 = rkey;
+ if(ent != start_entry)
+ rkey1 &= ~handler_entry::START;
+ if(ent != end_entry)
+ rkey1 &= ~handler_entry::END;
+ mismatched_patch(descriptor, rkey1, mappings, m_dispatch[ent]);
+ m_ranges[ent].intersect(ostart, oend);
+ }
+
+ } else if(start_entry == end_entry) {
+ if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) {
+ if(m_dispatch[start_entry]->is_dispatch())
+ m_dispatch[start_entry]->populate_mismatched_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey, mappings);
+ else {
+ mismatched_patch(descriptor, rkey, mappings, m_dispatch[start_entry]);
+ m_ranges[start_entry].intersect(ostart, oend);
+ }
+ } else
+ populate_mismatched_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey, mappings);
+
+ } else {
+ if(start & LOWMASK) {
+ populate_mismatched_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::END, mappings);
+ start_entry++;
+ rkey &= ~handler_entry::START;
+ }
+ if((end & LOWMASK) != LOWMASK) {
+ populate_mismatched_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::START, mappings);
+ end_entry--;
+ rkey &= ~handler_entry::END;
+ }
+
+ if(start_entry <= end_entry) {
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ u8 rkey1 = rkey;
+ if(ent != start_entry)
+ rkey1 &= ~handler_entry::START;
+ if(ent != end_entry)
+ rkey1 &= ~handler_entry::END;
+ if(m_dispatch[ent]->is_dispatch())
+ m_dispatch[ent]->populate_mismatched_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey1, mappings);
+ else {
+ mismatched_patch(descriptor, rkey1, mappings, m_dispatch[ent]);
+ m_ranges[ent].intersect(ostart, oend);
+ }
+ }
+ }
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_mismatched_mirror(start, end, ostart, oend, mirror, descriptor, mappings);
+ else {
+ auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_mismatched_mirror(start, end, ostart, oend, mirror, descriptor, mappings);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings)
+{
+ offs_t hmirror = mirror & HIGHMASK;
+ offs_t lmirror = mirror & LOWMASK;
+
+ if(lmirror) {
+ // If lmirror is non-zero, then each mirror instance is a single entry
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ offs_t base_entry = start >> LowBits;
+ start &= LOWMASK;
+ end &= LOWMASK;
+ do {
+ populate_mismatched_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, descriptor, mappings);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ } else {
+ // If lmirror is zero, call the nomirror version as needed
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ do {
+ populate_mismatched_nomirror(start | offset, end | offset, ostart | offset, oend | offset, descriptor, handler_entry::START|handler_entry::END, mappings);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ }
+}
+
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::passthrough_patch(handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift, Endian> *&target)
+{
+ handler_entry_write<Width, AddrShift, Endian> *original = target;
+ handler_entry_write<Width, AddrShift, Endian> *replacement = nullptr;
+ for(const auto &p : mappings)
+ if(p.original == original) {
+ replacement = p.patched;
+ break;
+ }
+ if(!replacement) {
+ replacement = handler->instantiate(original);
+ mappings.emplace_back(mapping{ original, replacement });
+ } else
+ replacement->ref();
+ target->unref();
+ target = replacement;
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings);
+ else {
+ auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ offs_t start_entry = (start & HIGHMASK) >> LowBits;
+ offs_t end_entry = (end & HIGHMASK) >> LowBits;
+ range_cut_before(ostart-1, start_entry);
+ range_cut_after(oend+1, end_entry);
+
+ if(LowBits <= Width + AddrShift) {
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ passthrough_patch(handler, mappings, m_dispatch[ent]);
+ m_ranges[ent].intersect(ostart, oend);
+ }
+
+ } else if(start_entry == end_entry) {
+ if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) {
+ if(m_dispatch[start_entry]->is_dispatch())
+ m_dispatch[start_entry]->populate_passthrough_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings);
+ else {
+ passthrough_patch(handler, mappings, m_dispatch[start_entry]);
+ m_ranges[start_entry].intersect(ostart, oend);
+ }
+ } else
+ populate_passthrough_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings);
+
+ } else {
+ if(start & LOWMASK) {
+ populate_passthrough_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, handler, mappings);
+ start_entry++;
+ }
+ if((end & LOWMASK) != LOWMASK) {
+ populate_passthrough_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, handler, mappings);
+ end_entry--;
+ }
+
+ if(start_entry <= end_entry) {
+ for(offs_t ent = start_entry; ent <= end_entry; ent++) {
+ if(m_dispatch[ent]->is_dispatch())
+ m_dispatch[ent]->populate_passthrough_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings);
+ else {
+ passthrough_patch(handler, mappings, m_dispatch[ent]);
+ m_ranges[ent].intersect(ostart, oend);
+ }
+ }
+ }
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ auto cur = m_dispatch[entry];
+ if(cur->is_dispatch())
+ cur->populate_passthrough_mirror(start, end, ostart, oend, mirror, handler, mappings);
+ else {
+ auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur);
+ cur->unref();
+ m_dispatch[entry] = subdispatch;
+ subdispatch->populate_passthrough_mirror(start, end, ostart, oend, mirror, handler, mappings);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings)
+{
+ offs_t hmirror = mirror & HIGHMASK;
+ offs_t lmirror = mirror & LOWMASK;
+
+ if(lmirror) {
+ // If lmirror is non-zero, then each mirror instance is a single entry
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ offs_t base_entry = start >> LowBits;
+ start &= LOWMASK;
+ end &= LOWMASK;
+ do {
+ populate_passthrough_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, handler, mappings);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ } else {
+ // If lmirror is zero, call the nomirror version as needed
+ offs_t add = 1 + ~hmirror;
+ offs_t offset = 0;
+ do {
+ populate_passthrough_nomirror(start | offset, end | offset, ostart | offset, oend | offset, handler, mappings);
+ offset = (offset + add) & hmirror;
+ } while(offset);
+ }
+}
+
+template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers)
+{
+ for(unsigned int i=0; i != COUNT; i++) {
+ m_dispatch[i]->unref();
+ if(!m_dispatch[i]->is_passthrough())
+ continue;
+ auto np = static_cast<handler_entry_write_passthrough<Width, AddrShift, Endian> *>(m_dispatch[i]);
+
+ if(handlers.find(np) != handlers.end()) {
+ m_dispatch[i] = np->get_subhandler();
+ m_dispatch[i]->ref();
+ np->unref();
+
+ } else
+ np->detach(handlers);
+ }
+}
diff --git a/src/emu/emumem_hedw0.cpp b/src/emu/emumem_hedw0.cpp
new file mode 100644
index 00000000000..6a1d0b99699
--- /dev/null
+++ b/src/emu/emumem_hedw0.cpp
@@ -0,0 +1,171 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hedw.ipp"
+
+
+template class handler_entry_write_dispatch< 1, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 1, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 2, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 2, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 3, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 0, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 1, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 1, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 2, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 2, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 3, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 1, 3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 1, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 1, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 2, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 2, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 3, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 1, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 1, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 1, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 2, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 2, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 3, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 1, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 2, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 2, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 3, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 2, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 2, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 2, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 3, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 2, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 2, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 2, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 3, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 2, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 3, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 3, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 3, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 3, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 3, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 3, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 3, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 3, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 4, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 4, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 5, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 5, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 6, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 6, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 7, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 7, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch< 8, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 8, 3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hedw1.cpp b/src/emu/emumem_hedw1.cpp
new file mode 100644
index 00000000000..4f62d45de4d
--- /dev/null
+++ b/src/emu/emumem_hedw1.cpp
@@ -0,0 +1,192 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hedw.ipp"
+
+template class handler_entry_write_dispatch< 9, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 0, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 1, 3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 1, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 1, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 2, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 2, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 2, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 3, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 3, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 3, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch< 9, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch< 9, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<10, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<10, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<11, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<11, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<12, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<12, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<13, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<13, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<14, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<14, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<15, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<15, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<16, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<16, 3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hedw2.cpp b/src/emu/emumem_hedw2.cpp
new file mode 100644
index 00000000000..b00eb38311d
--- /dev/null
+++ b/src/emu/emumem_hedw2.cpp
@@ -0,0 +1,192 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hedw.ipp"
+
+template class handler_entry_write_dispatch<17, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 0, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 1, 3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 1, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 1, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 2, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 2, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 2, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 3, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 3, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 3, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<17, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<17, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<18, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<18, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<19, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<19, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<20, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<20, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<21, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<21, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<22, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<22, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<23, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<23, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<24, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<24, 3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hedw3.cpp b/src/emu/emumem_hedw3.cpp
new file mode 100644
index 00000000000..539736655cc
--- /dev/null
+++ b/src/emu/emumem_hedw3.cpp
@@ -0,0 +1,193 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hedw.ipp"
+
+
+template class handler_entry_write_dispatch<25, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 0, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 1, 3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 1, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 1, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 2, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 2, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 2, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 3, 0, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 3, -1, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 3, -2, ENDIANNESS_BIG>;
+
+template class handler_entry_write_dispatch<25, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<25, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<26, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<26, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<27, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<27, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<28, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<28, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<29, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<29, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<31, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<31, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<30, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<30, 3, -3, ENDIANNESS_BIG>;
+template class handler_entry_write_dispatch<32, 3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_dispatch<32, 3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hem.cpp b/src/emu/emumem_hem.cpp
new file mode 100644
index 00000000000..6a897b2c07c
--- /dev/null
+++ b/src/emu/emumem_hem.cpp
@@ -0,0 +1,189 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hea.h"
+#include "emumem_hem.h"
+
+template<int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX handler_entry_read_memory<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask)
+{
+ return m_base[((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)];
+}
+
+template<int Width, int AddrShift, int Endian> void *handler_entry_read_memory<Width, AddrShift, Endian>::get_ptr(offs_t offset) const
+{
+ return m_base + (((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift));
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_read_memory<Width, AddrShift, Endian>::name() const
+{
+ return util::string_format("memory@%x", inh::m_address_base);
+}
+
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_memory<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask)
+{
+ offs_t off = ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift);
+ m_base[off] = (m_base[off] & ~mem_mask) | (data & mem_mask);
+}
+
+template<> void handler_entry_write_memory<0, 0, ENDIANNESS_LITTLE>::write(offs_t offset, u8 data, u8 mem_mask)
+{
+ m_base[(offset - inh::m_address_base) & inh::m_address_mask] = data;
+}
+
+template<> void handler_entry_write_memory<0, 0, ENDIANNESS_BIG>::write(offs_t offset, u8 data, u8 mem_mask)
+{
+ m_base[(offset - inh::m_address_base) & inh::m_address_mask] = data;
+}
+
+template<int Width, int AddrShift, int Endian> void *handler_entry_write_memory<Width, AddrShift, Endian>::get_ptr(offs_t offset) const
+{
+ return m_base + (((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift));
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_write_memory<Width, AddrShift, Endian>::name() const
+{
+ return util::string_format("memory@%x", inh::m_address_base);
+}
+
+
+
+
+
+template<int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX handler_entry_read_memory_bank<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask)
+{
+ return static_cast<uX *>(m_bank.base())[((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)];
+}
+
+template<int Width, int AddrShift, int Endian> void *handler_entry_read_memory_bank<Width, AddrShift, Endian>::get_ptr(offs_t offset) const
+{
+ return static_cast<uX *>(m_bank.base()) + (((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift));
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_read_memory_bank<Width, AddrShift, Endian>::name() const
+{
+ return m_bank.name();
+}
+
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_memory_bank<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask)
+{
+ offs_t off = ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift);
+ static_cast<uX *>(m_bank.base())[off] = (static_cast<uX *>(m_bank.base())[off] & ~mem_mask) | (data & mem_mask);
+}
+
+template<> void handler_entry_write_memory_bank<0, 0, ENDIANNESS_LITTLE>::write(offs_t offset, u8 data, u8 mem_mask)
+{
+ static_cast<uX *>(m_bank.base())[(offset - inh::m_address_base) & inh::m_address_mask] = data;
+}
+
+template<> void handler_entry_write_memory_bank<0, 0, ENDIANNESS_BIG>::write(offs_t offset, u8 data, u8 mem_mask)
+{
+ static_cast<uX *>(m_bank.base())[(offset - inh::m_address_base) & inh::m_address_mask] = data;
+}
+
+template<int Width, int AddrShift, int Endian> void *handler_entry_write_memory_bank<Width, AddrShift, Endian>::get_ptr(offs_t offset) const
+{
+ return static_cast<uX *>(m_bank.base()) + (((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift));
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_write_memory_bank<Width, AddrShift, Endian>::name() const
+{
+ return m_bank.name();
+}
+
+
+
+template class handler_entry_read_memory<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_memory<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory<3, -3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_memory<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_memory<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory<3, -3, ENDIANNESS_BIG>;
+
+
+template class handler_entry_read_memory_bank<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_memory_bank<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_memory_bank<3, -3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_memory_bank<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_memory_bank<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_memory_bank<3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hem.h b/src/emu/emumem_hem.h
new file mode 100644
index 00000000000..554f6957a18
--- /dev/null
+++ b/src/emu/emumem_hem.h
@@ -0,0 +1,87 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// handler_entry_read_memory/handler_entry_write_memory
+
+// Accesses fixed memory (non-banked rom or ram)
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_memory : public handler_entry_read_address<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_read_address<Width, AddrShift, Endian>;
+
+ handler_entry_read_memory(address_space *space) : handler_entry_read_address<Width, AddrShift, Endian>(space, 0) {}
+ ~handler_entry_read_memory() = default;
+
+ uX read(offs_t offset, uX mem_mask) override;
+ void *get_ptr(offs_t offset) const override;
+
+ inline void set_base(uX *base) { m_base = base; }
+
+ std::string name() const override;
+
+private:
+ uX *m_base;
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_memory : public handler_entry_write_address<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_write_address<Width, AddrShift, Endian>;
+
+ handler_entry_write_memory(address_space *space) : handler_entry_write_address<Width, AddrShift, Endian>(space, 0) {}
+ ~handler_entry_write_memory() = default;
+
+ void write(offs_t offset, uX data, uX mem_mask) override;
+ void *get_ptr(offs_t offset) const override;
+
+ inline void set_base(uX *base) { m_base = base; }
+
+ std::string name() const override;
+
+private:
+ uX *m_base;
+};
+
+
+// handler_entry_read_memory_bank/handler_entry_write_memory_bank
+
+// Accesses banked memory, associated to a memory_bank
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_memory_bank : public handler_entry_read_address<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_read_address<Width, AddrShift, Endian>;
+
+ handler_entry_read_memory_bank(address_space *space, memory_bank &bank) : handler_entry_read_address<Width, AddrShift, Endian>(space, 0), m_bank(bank) {}
+ ~handler_entry_read_memory_bank() = default;
+
+ uX read(offs_t offset, uX mem_mask) override;
+ void *get_ptr(offs_t offset) const override;
+
+ std::string name() const override;
+
+private:
+ memory_bank &m_bank;
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_memory_bank : public handler_entry_write_address<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_write_address<Width, AddrShift, Endian>;
+
+ handler_entry_write_memory_bank(address_space *space, memory_bank &bank) : handler_entry_write_address<Width, AddrShift, Endian>(space, 0), m_bank(bank) {}
+ ~handler_entry_write_memory_bank() = default;
+
+ void write(offs_t offset, uX data, uX mem_mask) override;
+ void *get_ptr(offs_t offset) const override;
+
+ std::string name() const override;
+
+private:
+ memory_bank &m_bank;
+};
diff --git a/src/emu/emumem_hep.cpp b/src/emu/emumem_hep.cpp
new file mode 100644
index 00000000000..97bdf759a38
--- /dev/null
+++ b/src/emu/emumem_hep.cpp
@@ -0,0 +1,97 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hep.h"
+
+template<int Width, int AddrShift, int Endian> handler_entry_read_passthrough<Width, AddrShift, Endian>::~handler_entry_read_passthrough()
+{
+ if(m_next) {
+ m_mph.remove_handler(this);
+ m_next->unref();
+ }
+}
+
+template<int Width, int AddrShift, int Endian> void handler_entry_read_passthrough<Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers)
+{
+ if(!m_next->is_passthrough())
+ return;
+ auto np = static_cast<handler_entry_read_passthrough<Width, AddrShift, Endian> *>(m_next);
+
+ if(handlers.find(np) != handlers.end()) {
+ m_next = np->get_subhandler();
+ m_next->ref();
+ np->unref();
+
+ } else
+ np->detach(handlers);
+}
+
+template<int Width, int AddrShift, int Endian> handler_entry_write_passthrough<Width, AddrShift, Endian>::~handler_entry_write_passthrough()
+{
+ if(m_next) {
+ m_mph.remove_handler(this);
+ m_next->unref();
+ }
+}
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_passthrough<Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers)
+{
+ if(!m_next->is_passthrough())
+ return;
+ auto np = static_cast<handler_entry_write_passthrough<Width, AddrShift, Endian> *>(m_next);
+
+ if(handlers.find(np) != handlers.end()) {
+ m_next = np->get_subhandler();
+ m_next->ref();
+ np->unref();
+
+ } else
+ np->detach(handlers);
+}
+
+template class handler_entry_read_passthrough<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_passthrough<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_passthrough<3, -3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_passthrough<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_passthrough<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_passthrough<3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_hep.h b/src/emu/emumem_hep.h
new file mode 100644
index 00000000000..11da1dc2bca
--- /dev/null
+++ b/src/emu/emumem_hep.h
@@ -0,0 +1,48 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// handler_entry_read_passthrough/handler_entry_write_passthrough
+
+// parent class for handlers which want to tap the access and usually pass it on to another handler
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_passthrough : public handler_entry_read<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+
+ handler_entry_read_passthrough(address_space *space, memory_passthrough_handler &mph) : handler_entry_read<Width, AddrShift, Endian>(space, handler_entry::F_PASSTHROUGH), m_mph(mph), m_next(nullptr) {}
+ ~handler_entry_read_passthrough();
+
+ virtual handler_entry_read_passthrough<Width, AddrShift, Endian> *instantiate(handler_entry_read<Width, AddrShift, Endian> *next) const = 0;
+
+ handler_entry_read<Width, AddrShift, Endian> *get_subhandler() const { return m_next; }
+
+ void detach(const std::unordered_set<handler_entry *> &handlers) override;
+
+protected:
+ memory_passthrough_handler &m_mph;
+ handler_entry_read<Width, AddrShift, Endian> *m_next;
+
+ handler_entry_read_passthrough(address_space *space, memory_passthrough_handler &mph, handler_entry_read<Width, AddrShift, Endian> *next) : handler_entry_read<Width, AddrShift, Endian>(space, handler_entry::F_PASSTHROUGH), m_mph(mph), m_next(next) { next->ref(); mph.add_handler(this); }
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_passthrough : public handler_entry_write<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+
+ handler_entry_write_passthrough(address_space *space, memory_passthrough_handler &mph) : handler_entry_write<Width, AddrShift, Endian>(space, handler_entry::F_PASSTHROUGH), m_mph(mph), m_next(nullptr) {}
+ ~handler_entry_write_passthrough();
+
+ virtual handler_entry_write_passthrough<Width, AddrShift, Endian> *instantiate(handler_entry_write<Width, AddrShift, Endian> *next) const = 0;
+
+ handler_entry_write<Width, AddrShift, Endian> *get_subhandler() const { return m_next; }
+
+ void detach(const std::unordered_set<handler_entry *> &handlers) override;
+
+protected:
+ memory_passthrough_handler &m_mph;
+ handler_entry_write<Width, AddrShift, Endian> *m_next;
+
+ handler_entry_write_passthrough(address_space *space, memory_passthrough_handler &mph, handler_entry_write<Width, AddrShift, Endian> *next) : handler_entry_write<Width, AddrShift, Endian>(space, handler_entry::F_PASSTHROUGH), m_mph(mph), m_next(next) { next->ref(); mph.add_handler(this); }
+};
diff --git a/src/emu/emumem_het.cpp b/src/emu/emumem_het.cpp
new file mode 100644
index 00000000000..6870284d61d
--- /dev/null
+++ b/src/emu/emumem_het.cpp
@@ -0,0 +1,89 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hep.h"
+#include "emumem_het.h"
+
+template<int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX handler_entry_read_tap<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask)
+{
+ uX data = inh::m_next->read(offset, mem_mask);
+ m_tap(offset, data, mem_mask);
+ return data;
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_read_tap<Width, AddrShift, Endian>::name() const
+{
+ return '(' + m_name + ") " + inh::m_next->name();
+}
+
+template<int Width, int AddrShift, int Endian> handler_entry_read_tap<Width, AddrShift, Endian> *handler_entry_read_tap<Width, AddrShift, Endian>::instantiate(handler_entry_read<Width, AddrShift, Endian> *next) const
+{
+ return new handler_entry_read_tap<Width, AddrShift, Endian>(inh::m_space, inh::m_mph, next, m_name, m_tap);
+}
+
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_tap<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask)
+{
+ m_tap(offset, data, mem_mask);
+ inh::m_next->write(offset, data, mem_mask);
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_write_tap<Width, AddrShift, Endian>::name() const
+{
+ return '(' + m_name + ") " + inh::m_next->name();
+}
+
+
+template<int Width, int AddrShift, int Endian> handler_entry_write_tap<Width, AddrShift, Endian> *handler_entry_write_tap<Width, AddrShift, Endian>::instantiate(handler_entry_write<Width, AddrShift, Endian> *next) const
+{
+ return new handler_entry_write_tap<Width, AddrShift, Endian>(inh::m_space, inh::m_mph, next, m_name, m_tap);
+}
+
+
+
+template class handler_entry_read_tap<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_tap<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_tap<3, -3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_tap<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_tap<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_tap<3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_het.h b/src/emu/emumem_het.h
new file mode 100644
index 00000000000..785c840d75d
--- /dev/null
+++ b/src/emu/emumem_het.h
@@ -0,0 +1,50 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// handler_entry_read_tap/handler_entry_write_tap
+
+// handler which tap on a bus access and possibly change the data value through a std::function
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_tap : public handler_entry_read_passthrough<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_read_passthrough<Width, AddrShift, Endian>;
+
+ handler_entry_read_tap(address_space *space, memory_passthrough_handler &mph, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_read_passthrough<Width, AddrShift, Endian>(space, mph), m_name(name), m_tap(std::move(tap)) {}
+ ~handler_entry_read_tap() = default;
+
+ uX read(offs_t offset, uX mem_mask) override;
+
+ std::string name() const override;
+
+ handler_entry_read_tap<Width, AddrShift, Endian> *instantiate(handler_entry_read<Width, AddrShift, Endian> *next) const override;
+
+protected:
+ std::string m_name;
+ std::function<void (offs_t offset, uX &data, uX mem_mask)> m_tap;
+
+ handler_entry_read_tap(address_space *space, memory_passthrough_handler &mph, handler_entry_read<Width, AddrShift, Endian> *next, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_read_passthrough<Width, AddrShift, Endian>(space, mph, next), m_name(name), m_tap(tap) {}
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_tap : public handler_entry_write_passthrough<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_write_passthrough<Width, AddrShift, Endian>;
+
+ handler_entry_write_tap(address_space *space, memory_passthrough_handler &mph, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_write_passthrough<Width, AddrShift, Endian>(space, mph), m_name(name), m_tap(std::move(tap)) {}
+ ~handler_entry_write_tap() = default;
+
+ void write(offs_t offset, uX data, uX mem_mask) override;
+
+ std::string name() const override;
+
+ handler_entry_write_tap<Width, AddrShift, Endian> *instantiate(handler_entry_write<Width, AddrShift, Endian> *next) const override;
+
+protected:
+ std::string m_name;
+ std::function<void (offs_t offset, uX &data, uX mem_mask)> m_tap;
+
+ handler_entry_write_tap(address_space *space, memory_passthrough_handler &mph, handler_entry_write<Width, AddrShift, Endian> *next, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_write_passthrough<Width, AddrShift, Endian>(space, mph, next), m_name(name), m_tap(tap) {}
+};
diff --git a/src/emu/emumem_heu.cpp b/src/emu/emumem_heu.cpp
new file mode 100644
index 00000000000..8942ad4418b
--- /dev/null
+++ b/src/emu/emumem_heu.cpp
@@ -0,0 +1,273 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hea.h"
+#include "emumem_mud.h"
+#include "emumem_heu.h"
+
+
+template<int Width, int AddrShift, int Endian> handler_entry_read_units<Width, AddrShift, Endian>::handler_entry_read_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, address_space *space) :
+ handler_entry_read<Width, AddrShift, Endian>(space, inh::F_UNITS),
+ m_subunits(0)
+{
+ const auto &entries = descriptor.get_entries_for_key(ukey);
+ fill(descriptor, entries);
+}
+
+template<int Width, int AddrShift, int Endian> handler_entry_read_units<Width, AddrShift, Endian>::handler_entry_read_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, const handler_entry_read_units *src) :
+ handler_entry_read<Width, AddrShift, Endian>(src->m_space, inh::F_UNITS),
+ m_subunits(0)
+{
+ uX fullmask = 0;
+ const auto &entries = descriptor.get_entries_for_key(ukey);
+ for(const auto &e : entries)
+ fullmask |= e.m_dmask;
+
+ for(u32 i=0; i != src->m_subunits; i++)
+ if(!(src->m_subunit_infos[i].m_dmask & fullmask)) {
+ m_subunit_infos[m_subunits] = src->m_subunit_infos[i];
+ m_subunit_infos[m_subunits].m_handler->ref();
+ m_subunits++;
+ }
+
+ fill(descriptor, entries);
+}
+
+template<int Width, int AddrShift, int Endian> handler_entry_read_units<Width, AddrShift, Endian>::~handler_entry_read_units()
+{
+ for(u32 i=0; i != m_subunits; i++)
+ m_subunit_infos[i].m_handler->unref();
+}
+
+template<int Width, int AddrShift, int Endian> void handler_entry_read_units<Width, AddrShift, Endian>::enumerate_references(handler_entry::reflist &refs) const
+{
+ for(u32 i=0; i != m_subunits; i++)
+ refs.add(m_subunit_infos[i].m_handler);
+}
+
+template<int Width, int AddrShift, int Endian> void handler_entry_read_units<Width, AddrShift, Endian>::fill(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift, Endian>::entry> &entries)
+{
+ handler_entry *handler = descriptor.get_subunit_handler();
+ handler->ref(entries.size());
+ for(const auto &e : entries)
+ m_subunit_infos[m_subunits++] = subunit_info{ handler, e.m_amask, e.m_dmask, e.m_ashift, e.m_offset, e.m_dshift, descriptor.get_subunit_width(), descriptor.get_subunit_endian() };
+ m_unmap = inh::m_space->unmap();
+ for(const auto &e : m_subunit_infos)
+ m_unmap &= ~e.m_dmask;
+}
+
+
+template<int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX handler_entry_read_units<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask)
+{
+ uX result = m_unmap;
+ for (int index = 0; index < m_subunits; index++) {
+ const subunit_info &si = m_subunit_infos[index];
+ if (mem_mask & si.m_amask) {
+ offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset;
+ switch(si.m_width) {
+ case 0:
+ if(si.m_endian == ENDIANNESS_LITTLE)
+ result |= uX(static_cast<handler_entry_read<0, 0, ENDIANNESS_LITTLE> *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift;
+ else
+ result |= uX(static_cast<handler_entry_read<0, 0, ENDIANNESS_BIG > *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift;
+ break;
+ case 1:
+ if(si.m_endian == ENDIANNESS_LITTLE)
+ result |= uX(static_cast<handler_entry_read<1, -1, ENDIANNESS_LITTLE> *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift;
+ else
+ result |= uX(static_cast<handler_entry_read<1, -1, ENDIANNESS_BIG > *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift;
+ break;
+ case 2:
+ if(si.m_endian == ENDIANNESS_LITTLE)
+ result |= uX(static_cast<handler_entry_read<2, -2, ENDIANNESS_LITTLE> *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift;
+ else
+ result |= uX(static_cast<handler_entry_read<2, -2, ENDIANNESS_BIG > *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift;
+ break;
+ default:
+ abort();
+ }
+ }
+ }
+ return result;
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_read_units<Width, AddrShift, Endian>::m2r(typename handler_entry_size<Width>::uX mask)
+{
+ constexpr u32 mbits = 8*sizeof(uX);
+ u32 start, end;
+ for(start = 0; start < mbits && !(mask & (uX(1)<<start)); start += 8);
+ for(end = 8*sizeof(uX) - 1; end < mbits && !(mask & (uX(1)<<end)); end -= 8);
+ if(start >= mbits || end >= mbits)
+ return "???";
+ return util::string_format("%d-%d", end, start);
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_read_units<Width, AddrShift, Endian>::name() const
+{
+ std::string result;
+
+ for (int index = 0; index < m_subunits; index++) {
+ const subunit_info &si = m_subunit_infos[index];
+ if(!result.empty())
+ result += ' ';
+ result += util::string_format("%s:%s:%d:%d:%d:%s", m2r(si.m_amask), m2r(si.m_dmask), si.m_ashift, si.m_offset, si.m_dshift, si.m_handler->name());
+ }
+
+ return result;
+}
+
+
+
+
+template<int Width, int AddrShift, int Endian> handler_entry_write_units<Width, AddrShift, Endian>::handler_entry_write_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, address_space *space) :
+ handler_entry_write<Width, AddrShift, Endian>(space, inh::F_UNITS),
+ m_subunits(0)
+{
+ const auto &entries = descriptor.get_entries_for_key(ukey);
+ fill(descriptor, entries);
+}
+
+template<int Width, int AddrShift, int Endian> handler_entry_write_units<Width, AddrShift, Endian>::handler_entry_write_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, const handler_entry_write_units<Width, AddrShift, Endian> *src) :
+ handler_entry_write<Width, AddrShift, Endian>(src->m_space, inh::F_UNITS),
+ m_subunits(0)
+{
+ uX fullmask = 0;
+ const auto &entries = descriptor.get_entries_for_key(ukey);
+ for(const auto &e : entries)
+ fullmask |= e.m_dmask;
+
+ for(u32 i=0; i != src->m_subunits; i++)
+ if(!(src->m_subunit_infos[i].m_dmask & fullmask)) {
+ m_subunit_infos[m_subunits] = src->m_subunit_infos[i];
+ m_subunit_infos[m_subunits].m_handler->ref();
+ m_subunits++;
+ }
+
+ fill(descriptor, entries);
+}
+
+template<int Width, int AddrShift, int Endian> handler_entry_write_units<Width, AddrShift, Endian>::~handler_entry_write_units()
+{
+ for(u32 i=0; i != m_subunits; i++)
+ m_subunit_infos[i].m_handler->unref();
+}
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_units<Width, AddrShift, Endian>::enumerate_references(handler_entry::reflist &refs) const
+{
+ for(u32 i=0; i != m_subunits; i++)
+ refs.add(m_subunit_infos[i].m_handler);
+}
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_units<Width, AddrShift, Endian>::fill(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift, Endian>::entry> &entries)
+{
+ handler_entry *handler = descriptor.get_subunit_handler();
+ handler->ref(entries.size());
+ for(const auto &e : entries)
+ m_subunit_infos[m_subunits++] = subunit_info{ handler, e.m_amask, e.m_dmask, e.m_ashift, e.m_offset, e.m_dshift, descriptor.get_subunit_width(), descriptor.get_subunit_endian() };
+}
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_units<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask)
+{
+ for (int index = 0; index < m_subunits; index++) {
+ const subunit_info &si = m_subunit_infos[index];
+ if (mem_mask & si.m_amask) {
+ offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset;
+ switch(si.m_width) {
+ case 0:
+ if(si.m_endian == ENDIANNESS_LITTLE)
+ static_cast<handler_entry_write<0, 0, ENDIANNESS_LITTLE> *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift);
+ else
+ static_cast<handler_entry_write<0, 0, ENDIANNESS_BIG > *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift);
+ break;
+ case 1:
+ if(si.m_endian == ENDIANNESS_LITTLE)
+ static_cast<handler_entry_write<1, -1, ENDIANNESS_LITTLE> *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift);
+ else
+ static_cast<handler_entry_write<1, -1, ENDIANNESS_BIG > *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift);
+ break;
+ case 2:
+ if(si.m_endian == ENDIANNESS_LITTLE)
+ static_cast<handler_entry_write<2, -2, ENDIANNESS_LITTLE> *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift);
+ else
+ static_cast<handler_entry_write<2, -2, ENDIANNESS_BIG > *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift);
+ break;
+ default:
+ abort();
+ }
+ }
+ }
+}
+
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_write_units<Width, AddrShift, Endian>::m2r(typename handler_entry_size<Width>::uX mask)
+{
+ constexpr u32 mbits = 8*sizeof(uX);
+ u32 start, end;
+ for(start = 0; start < mbits && !(mask & (uX(1)<<start)); start += 8);
+ for(end = 8*sizeof(uX) - 1; end < mbits && !(mask & (uX(1)<<end)); end -= 8);
+ if(start >= mbits || end >= mbits)
+ return "???";
+ return util::string_format("%d-%d", end, start);
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_write_units<Width, AddrShift, Endian>::name() const
+{
+ std::string result;
+
+ for (int index = 0; index < m_subunits; index++) {
+ const subunit_info &si = m_subunit_infos[index];
+ if(!result.empty())
+ result += ' ';
+ result += util::string_format("%s:%s:%d:%d:%d:%s", m2r(si.m_amask), m2r(si.m_dmask), si.m_ashift, si.m_offset, si.m_dshift, si.m_handler->name());
+ }
+
+ return result;
+}
+
+
+template class handler_entry_read_units<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_units<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_units<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_units<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_units<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_units<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_units<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_units<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_units<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_units<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_units<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_units<3, -3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_units<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_units<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_units<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_units<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_units<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_units<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_units<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_units<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_units<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_units<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_units<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_units<3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_heu.h b/src/emu/emumem_heu.h
new file mode 100644
index 00000000000..c7dcea23287
--- /dev/null
+++ b/src/emu/emumem_heu.h
@@ -0,0 +1,90 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// handler_entry_read_units/handler_entry_write_units
+
+// merges/splits an access among multiple handlers (unitmask support)
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_units : public handler_entry_read<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using READ = typename handler_entry_size<Width>::READ;
+ using inh = handler_entry_read<Width, AddrShift, Endian>;
+
+ handler_entry_read_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, address_space *space);
+ handler_entry_read_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, const handler_entry_read_units *src);
+ ~handler_entry_read_units();
+
+ uX read(offs_t offset, uX mem_mask) override;
+
+ std::string name() const override;
+
+ void enumerate_references(handler_entry::reflist &refs) const override;
+
+private:
+ static constexpr u32 SUBUNIT_COUNT = 1 << Width;
+
+ struct subunit_info
+ {
+ handler_entry *m_handler; // the handler itself, the root to hide the templatization
+
+ uX m_amask; // access mask (for filtering access)
+ uX m_dmask; // data mask (for removal on collisions)
+ s8 m_ashift; // shift to apply to the address (positive = right)
+ u8 m_offset; // offset to add to the address post shift
+ u8 m_dshift; // data shift of the subunit
+
+ u8 m_width; // access width (0..3)
+ u8 m_endian; // endianness
+ };
+
+ subunit_info m_subunit_infos[SUBUNIT_COUNT]; // subunit information
+ uX m_unmap; // "unmapped" value to add to reads
+ u8 m_subunits; // number of subunits
+
+ void fill(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift, Endian>::entry> &entries);
+ static std::string m2r(uX mask);
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_units : public handler_entry_write<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using WRITE = typename handler_entry_size<Width>::WRITE;
+ using inh = handler_entry_write<Width, AddrShift, Endian>;
+
+ handler_entry_write_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, address_space *space);
+ handler_entry_write_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, const handler_entry_write_units<Width, AddrShift, Endian> *src);
+ ~handler_entry_write_units();
+
+ void write(offs_t offset, uX data, uX mem_mask) override;
+
+ std::string name() const override;
+
+ void enumerate_references(handler_entry::reflist &refs) const override;
+
+private:
+ static constexpr u32 SUBUNIT_COUNT = 1 << Width;
+
+ struct subunit_info
+ {
+ handler_entry *m_handler; // the handler itself, the root to hide the templatization
+
+ uX m_amask; // access mask (for filtering access)
+ uX m_dmask; // data mask (for removal on collisions)
+ s8 m_ashift; // shift to apply to the address (positive = right)
+ u8 m_offset; // offset to add to the address post shift
+ u8 m_dshift; // data shift of the subunit
+
+ u8 m_width; // access width (0..3)
+ u8 m_endian; // endianness
+ };
+
+ subunit_info m_subunit_infos[SUBUNIT_COUNT]; // subunit information
+ u8 m_subunits; // number of subunits
+
+ void fill(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift, Endian>::entry> &entries);
+ static std::string m2r(uX mask);
+};
+
diff --git a/src/emu/emumem_heun.cpp b/src/emu/emumem_heun.cpp
new file mode 100644
index 00000000000..1ab59b17c2f
--- /dev/null
+++ b/src/emu/emumem_heun.cpp
@@ -0,0 +1,158 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_hea.h"
+#include "emumem_heun.h"
+
+template<int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX handler_entry_read_unmapped<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask)
+{
+ if (inh::m_space->log_unmap() && !inh::m_space->m_manager.machine().side_effects_disabled())
+ inh::m_space->device().logerror(inh::m_space->is_octal()
+ ? "%s: unmapped %s memory read from %0*o & %0*o\n"
+ : "%s: unmapped %s memory read from %0*X & %0*X\n",
+ inh::m_space->m_manager.machine().describe_context(), inh::m_space->name(),
+ inh::m_space->addrchars(), offset,
+ 2 << Width, mem_mask);
+ return inh::m_space->unmap();
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_read_unmapped<Width, AddrShift, Endian>::name() const
+{
+ return "unmapped";
+}
+
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_unmapped<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask)
+{
+ if (inh::m_space->log_unmap() && !inh::m_space->m_manager.machine().side_effects_disabled())
+ inh::m_space->device().logerror(inh::m_space->is_octal()
+ ? "%s: unmapped %s memory write to %0*o = %0*o & %0*o\n"
+ : "%s: unmapped %s memory write to %0*X = %0*X & %0*X\n",
+ inh::m_space->m_manager.machine().describe_context(), inh::m_space->name(),
+ inh::m_space->addrchars(), offset,
+ 2 << Width, data,
+ 2 << Width, mem_mask);
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_write_unmapped<Width, AddrShift, Endian>::name() const
+{
+ return "unmapped";
+}
+
+
+
+
+template<int Width, int AddrShift, int Endian> typename handler_entry_size<Width>::uX handler_entry_read_nop<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask)
+{
+ return inh::m_space->unmap();
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_read_nop<Width, AddrShift, Endian>::name() const
+{
+ return "nop";
+}
+
+
+template<int Width, int AddrShift, int Endian> void handler_entry_write_nop<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask)
+{
+}
+
+template<int Width, int AddrShift, int Endian> std::string handler_entry_write_nop<Width, AddrShift, Endian>::name() const
+{
+ return "nop";
+}
+
+
+template class handler_entry_read_unmapped<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_unmapped<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_unmapped<3, -3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_unmapped<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_unmapped<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_unmapped<3, -3, ENDIANNESS_BIG>;
+
+
+template class handler_entry_read_nop<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_read_nop<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_read_nop<3, -3, ENDIANNESS_BIG>;
+
+template class handler_entry_write_nop<0, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<0, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<1, 3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<1, 3, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<1, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<1, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<1, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<1, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<2, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<2, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<2, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<2, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<2, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<2, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<3, 0, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<3, 0, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<3, -1, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<3, -1, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<3, -2, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<3, -2, ENDIANNESS_BIG>;
+template class handler_entry_write_nop<3, -3, ENDIANNESS_LITTLE>;
+template class handler_entry_write_nop<3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_heun.h b/src/emu/emumem_heun.h
new file mode 100644
index 00000000000..2bc4a02a2dc
--- /dev/null
+++ b/src/emu/emumem_heun.h
@@ -0,0 +1,68 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// handler_entry_read_unmapped/handler_entry_write_unmapped
+
+// Logs an unmapped access
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_unmapped : public handler_entry_read<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_read<Width, AddrShift, Endian>;
+
+ handler_entry_read_unmapped(address_space *space) : handler_entry_read<Width, AddrShift, Endian>(space, 0) {}
+ ~handler_entry_read_unmapped() = default;
+
+ uX read(offs_t offset, uX mem_mask) override;
+
+ std::string name() const override;
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_unmapped : public handler_entry_write<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_write<Width, AddrShift, Endian>;
+
+ handler_entry_write_unmapped(address_space *space) : handler_entry_write<Width, AddrShift, Endian>(space, 0) {}
+ ~handler_entry_write_unmapped() = default;
+
+ void write(offs_t offset, uX data, uX mem_mask) override;
+
+ std::string name() const override;
+};
+
+
+
+// handler_entry_read_nop/handler_entry_write_nop
+
+// Drops an unmapped access silently
+
+template<int Width, int AddrShift, int Endian> class handler_entry_read_nop : public handler_entry_read<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_read<Width, AddrShift, Endian>;
+
+ handler_entry_read_nop(address_space *space) : handler_entry_read<Width, AddrShift, Endian>(space, 0) {}
+ ~handler_entry_read_nop() = default;
+
+ uX read(offs_t offset, uX mem_mask) override;
+
+ std::string name() const override;
+};
+
+template<int Width, int AddrShift, int Endian> class handler_entry_write_nop : public handler_entry_write<Width, AddrShift, Endian>
+{
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+ using inh = handler_entry_write<Width, AddrShift, Endian>;
+
+ handler_entry_write_nop(address_space *space) : handler_entry_write<Width, AddrShift, Endian>(space, 0) {}
+ ~handler_entry_write_nop() = default;
+
+ void write(offs_t offset, uX data, uX mem_mask) override;
+
+ std::string name() const override;
+};
diff --git a/src/emu/emumem_mud.cpp b/src/emu/emumem_mud.cpp
new file mode 100644
index 00000000000..8279eea5635
--- /dev/null
+++ b/src/emu/emumem_mud.cpp
@@ -0,0 +1,127 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "emu.h"
+#include "emumem_mud.h"
+
+template<typename T> static u8 mask_to_ukey(T mask);
+
+template<> u8 mask_to_ukey<u16>(u16 mask)
+{
+ return
+ (mask & 0xff00 ? 0x02 : 0x00) |
+ (mask & 0x00ff ? 0x01 : 0x00);
+}
+
+template<> u8 mask_to_ukey<u32>(u32 mask)
+{
+ return
+ (mask & 0xff000000 ? 0x08 : 0x00) |
+ (mask & 0x00ff0000 ? 0x04 : 0x00) |
+ (mask & 0x0000ff00 ? 0x02 : 0x00) |
+ (mask & 0x000000ff ? 0x01 : 0x00);
+}
+
+template<> u8 mask_to_ukey<u64>(u64 mask)
+{
+ return
+ (mask & 0xff00000000000000 ? 0x80 : 0x00) |
+ (mask & 0x00ff000000000000 ? 0x40 : 0x00) |
+ (mask & 0x0000ff0000000000 ? 0x20 : 0x00) |
+ (mask & 0x000000ff00000000 ? 0x10 : 0x00) |
+ (mask & 0x00000000ff000000 ? 0x08 : 0x00) |
+ (mask & 0x0000000000ff0000 ? 0x04 : 0x00) |
+ (mask & 0x000000000000ff00 ? 0x02 : 0x00) |
+ (mask & 0x00000000000000ff ? 0x01 : 0x00);
+}
+
+template<int Width, int AddrShift, int Endian> memory_units_descriptor<Width, AddrShift, Endian>::memory_units_descriptor(u8 access_width, u8 access_endian, handler_entry *handler, offs_t addrstart, offs_t addrend, offs_t mask, typename handler_entry_size<Width>::uX unitmask, int cswidth) : m_handler(handler), m_access_width(access_width), m_access_endian(access_endian)
+{
+ constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? make_bitmask<u32>(Width + AddrShift) : 0;
+ u32 bits_per_access = 8 << access_width;
+
+ // Compute the real base addresses
+ m_addrstart = addrstart & ~NATIVE_MASK;
+ m_addrend = addrend & ~NATIVE_MASK;
+
+
+ // Compute the masks and the keys
+ std::array<uX, 4> umasks;
+ umasks.fill(unitmask);
+
+ uX smask = ~make_bitmask<uX>((addrstart - m_addrstart) << (3 - AddrShift));
+ uX emask = make_bitmask<uX>((addrend - m_addrend + 1) << (3 - AddrShift));
+
+ umasks[handler_entry::START] &= smask;
+ umasks[handler_entry::END] &= emask;
+ umasks[handler_entry::START|handler_entry::END] &= smask | emask;
+
+ for(u32 i=0; i<4; i++)
+ m_keymap[i] = mask_to_ukey<uX>(umasks[i]);
+
+
+ // Compute the shift
+ uX dmask = make_bitmask<uX>(bits_per_access);
+ u32 active_count = 0;
+ for(u32 i=0; i != 8 << Width; i += bits_per_access)
+ if(unitmask & (dmask << i))
+ active_count ++;
+ u32 active_count_log = active_count == 1 ? 0 : active_count == 2 ? 1 : active_count == 4 ? 2 : active_count == 8 ? 3 : 0xff;
+ if(active_count_log == 0xff)
+ abort();
+ s8 base_shift = Width - access_width - active_count_log;
+ s8 shift = base_shift + access_width + AddrShift;
+
+
+ // Build the handler characteristics
+ m_handler_start = shift < 0 ? addrstart << -shift : addrstart >> shift;
+ m_handler_mask = shift < 0 ? (mask << -shift) | make_bitmask<offs_t>(-shift) : mask >> shift;
+
+ for(u32 i=0; i<4; i++)
+ if(m_entries_for_key.find(m_keymap[i]) == m_entries_for_key.end())
+ generate(m_keymap[i], umasks[i], cswidth, bits_per_access, base_shift, shift, active_count);
+}
+
+template<int Width, int AddrShift, int Endian> void memory_units_descriptor<Width, AddrShift, Endian>::generate(u8 ukey, typename handler_entry_size<Width>::uX umask, u32 cswidth, u32 bits_per_access, u8 base_shift, s8 shift, u32 active_count)
+{
+ auto &entries = m_entries_for_key[ukey];
+
+ // Compute the selection masks
+ if(!cswidth)
+ cswidth = bits_per_access;
+
+ uX csmask = make_bitmask<uX>(cswidth);
+ uX dmask = make_bitmask<uX>(bits_per_access);
+
+ u32 offset = 0;
+
+ for(u32 i=0; i != 8 << Width; i += bits_per_access) {
+ uX numask = dmask << i;
+ if(umask & numask) {
+ uX amask = csmask << (i & ~(cswidth - 1));
+ entries.emplace_back(entry{ amask, numask, shift, u8(i), u8(Endian == ENDIANNESS_BIG ? active_count - 1 - offset : offset) });
+ offset ++;
+ }
+ }
+}
+
+template class memory_units_descriptor<1, 3, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<1, 3, ENDIANNESS_BIG>;
+template class memory_units_descriptor<1, 0, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<1, 0, ENDIANNESS_BIG>;
+template class memory_units_descriptor<1, -1, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<1, -1, ENDIANNESS_BIG>;
+template class memory_units_descriptor<2, 0, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<2, 0, ENDIANNESS_BIG>;
+template class memory_units_descriptor<2, -1, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<2, -1, ENDIANNESS_BIG>;
+template class memory_units_descriptor<2, -2, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<2, -2, ENDIANNESS_BIG>;
+template class memory_units_descriptor<3, 0, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<3, 0, ENDIANNESS_BIG>;
+template class memory_units_descriptor<3, -1, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<3, -1, ENDIANNESS_BIG>;
+template class memory_units_descriptor<3, -2, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<3, -2, ENDIANNESS_BIG>;
+template class memory_units_descriptor<3, -3, ENDIANNESS_LITTLE>;
+template class memory_units_descriptor<3, -3, ENDIANNESS_BIG>;
diff --git a/src/emu/emumem_mud.h b/src/emu/emumem_mud.h
new file mode 100644
index 00000000000..e1299ed7fcc
--- /dev/null
+++ b/src/emu/emumem_mud.h
@@ -0,0 +1,41 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Descriptors for subunit support
+
+template<int Width, int AddrShift, int Endian> class memory_units_descriptor {
+public:
+ using uX = typename handler_entry_size<Width>::uX;
+
+ struct entry {
+ uX m_amask;
+ uX m_dmask;
+ s8 m_ashift;
+ u8 m_dshift;
+ u8 m_offset;
+ };
+
+ memory_units_descriptor(u8 access_width, u8 access_endian, handler_entry *handler, offs_t addrstart, offs_t addrend, offs_t mask, uX unitmask, int cswidth);
+
+ offs_t get_handler_start() const { return m_handler_start; }
+ offs_t get_handler_mask() const { return m_handler_mask; }
+
+ u8 rkey_to_ukey(u8 rkey) const { return m_keymap[rkey]; }
+ const std::vector<entry> &get_entries_for_key(u8 key) const { return m_entries_for_key.find(key)->second; }
+
+ u8 get_subunit_width() const { return m_access_width; }
+ u8 get_subunit_endian() const { return m_access_endian; }
+
+ void set_subunit_handler(handler_entry *handler) { m_handler = handler; }
+ handler_entry *get_subunit_handler() const { return m_handler; }
+
+private:
+ std::map<u8, std::vector<entry>> m_entries_for_key;
+ offs_t m_addrstart, m_addrend, m_handler_start, m_handler_mask;
+ handler_entry *m_handler;
+ std::array<u8, 4> m_keymap;
+ u8 m_access_width;
+ u8 m_access_endian;
+
+ void generate(u8 ukey, uX umask, u32 cswidth, u32 bits_per_access, u8 base_shift, s8 shift, u32 active_count);
+};
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp
index c4e7bbbbfdd..edc56ee13b9 100644
--- a/src/frontend/mame/luaengine.cpp
+++ b/src/frontend/mame/luaengine.cpp
@@ -1287,42 +1287,40 @@ void lua_engine::initialize()
return table;
},
"wpset", [](device_debug &dev, addr_space &sp, const std::string &type, offs_t addr, offs_t len, const char *cond, const char *act) {
- int wptype = WATCHPOINT_READ;
+ read_or_write wptype = read_or_write::READ;
if(type == "w")
- wptype = WATCHPOINT_WRITE;
+ wptype = read_or_write::WRITE;
else if((type == "rw") || (type == "wr"))
- wptype = WATCHPOINT_READ | WATCHPOINT_WRITE;
+ wptype = read_or_write::READWRITE;
return dev.watchpoint_set(sp.space, wptype, addr, len, cond, act);
},
"wpclr", &device_debug::watchpoint_clear,
"wplist", [this](device_debug &dev, addr_space &sp) {
sol::table table = sol().create_table();
- device_debug::watchpoint *list = dev.watchpoint_first(sp.space.spacenum());
- while(list)
+ for(auto &wpp : dev.watchpoint_vector(sp.space.spacenum()))
{
sol::table wp = sol().create_table();
- wp["enabled"] = list->enabled();
- wp["address"] = list->address();
- wp["length"] = list->length();
- switch(list->type())
+ wp["enabled"] = wpp->enabled();
+ wp["address"] = wpp->address();
+ wp["length"] = wpp->length();
+ switch(wpp->type())
{
- case WATCHPOINT_READ:
+ case read_or_write::READ:
wp["type"] = "r";
break;
- case WATCHPOINT_WRITE:
+ case read_or_write::WRITE:
wp["type"] = "w";
break;
- case WATCHPOINT_READ | WATCHPOINT_WRITE:
+ case read_or_write::READWRITE:
wp["type"] = "rw";
break;
default: // huh?
wp["type"] = "";
break;
}
- wp["condition"] = list->condition();
- wp["action"] = list->action();
- table[list->index()] = wp;
- list = list->next();
+ wp["condition"] = wpp->condition();
+ wp["action"] = wpp->action();
+ table[wpp->index()] = wp;
}
return table;
});
diff --git a/src/lib/util/coretmpl.h b/src/lib/util/coretmpl.h
index 8d317db7596..08c3a6b8678 100644
--- a/src/lib/util/coretmpl.h
+++ b/src/lib/util/coretmpl.h
@@ -32,6 +32,14 @@
#include <utility>
#include <vector>
+// Generate a N-bit mask at compile time. N better be constant.
+// Works even for signed types
+
+template<typename T> constexpr T make_bitmask(unsigned int N)
+{
+ return T((N < (8 * sizeof(T)) ? (std::make_unsigned_t<T>(1) << N) : std::make_unsigned_t<T>(0)) - 1);
+}
+
// ======================> simple_list
diff --git a/src/mame/drivers/geneve.cpp b/src/mame/drivers/geneve.cpp
index 0913d3a5477..f23ef639e10 100644
--- a/src/mame/drivers/geneve.cpp
+++ b/src/mame/drivers/geneve.cpp
@@ -279,6 +279,7 @@ private:
void crumap(address_map &map);
void memmap(address_map &map);
+ void memmap_setoffset(address_map &map);
};
/*
@@ -287,7 +288,12 @@ private:
void geneve_state::memmap(address_map &map)
{
- map(0x0000, 0xffff).rw(GENEVE_MAPPER_TAG, FUNC(bus::ti99::internal::geneve_mapper_device::readm), FUNC(bus::ti99::internal::geneve_mapper_device::writem)).setoffset(GENEVE_MAPPER_TAG, FUNC(bus::ti99::internal::geneve_mapper_device::setoffset));
+ map(0x0000, 0xffff).rw(GENEVE_MAPPER_TAG, FUNC(bus::ti99::internal::geneve_mapper_device::readm), FUNC(bus::ti99::internal::geneve_mapper_device::writem));
+}
+
+void geneve_state::memmap_setoffset(address_map &map)
+{
+ map(0x0000, 0xffff).r(GENEVE_MAPPER_TAG, FUNC(bus::ti99::internal::geneve_mapper_device::setoffset));
}
/*
@@ -717,6 +723,7 @@ MACHINE_CONFIG_START(geneve_state::geneve_common)
// basic machine hardware
// TMS9995 CPU @ 12.0 MHz
MCFG_TMS99xx_ADD("maincpu", TMS9995, 12000000, memmap, crumap)
+ MCFG_DEVICE_ADDRESS_MAP(tms9995_device::AS_SETOFFSET, memmap_setoffset)
MCFG_TMS9995_EXTOP_HANDLER( WRITE8(*this, geneve_state, external_operation) )
MCFG_TMS9995_CLKOUT_HANDLER( WRITELINE(*this, geneve_state, clock_out) )
MCFG_TMS9995_DBIN_HANDLER( WRITELINE(*this, geneve_state, dbin_line) )
diff --git a/src/mame/drivers/m6805evs.cpp b/src/mame/drivers/m6805evs.cpp
index bd1c7164219..1fdf4179c16 100644
--- a/src/mame/drivers/m6805evs.cpp
+++ b/src/mame/drivers/m6805evs.cpp
@@ -83,7 +83,6 @@ private:
void m6805evs_state::mem_map(address_map &map)
{
- map.global_mask(0x1fff);
map.unmap_value_high();
// AM_RANGE(0x0000, 0x001f) I/O registers live here
@@ -104,7 +103,9 @@ void m6805evs_state::machine_reset()
MACHINE_CONFIG_START(m6805evs_state::m6805evs)
/* basic machine hardware */
MCFG_DEVICE_ADD("maincpu", M6805, XTAL(4'000'000))
- MCFG_DEVICE_PROGRAM_MAP(mem_map)
+
+// Needs a 13-bits address bus wide version of the cpu
+// MCFG_DEVICE_PROGRAM_MAP(mem_map)
MACHINE_CONFIG_END
ROM_START(m6805evs)
diff --git a/src/mame/drivers/ti99_4p.cpp b/src/mame/drivers/ti99_4p.cpp
index 1c577218d99..3d9d980d7ac 100644
--- a/src/mame/drivers/ti99_4p.cpp
+++ b/src/mame/drivers/ti99_4p.cpp
@@ -157,7 +157,7 @@ public:
DECLARE_WRITE_LINE_MEMBER( notconnected );
DECLARE_READ8_MEMBER( interrupt_level );
- DECLARE_SETOFFSET_MEMBER( setoffset );
+ DECLARE_READ8_MEMBER( setoffset );
DECLARE_READ16_MEMBER( memread );
DECLARE_WRITE16_MEMBER( memwrite );
DECLARE_WRITE_LINE_MEMBER( dbin_in );
@@ -189,6 +189,7 @@ public:
void ti99_4p_60hz(machine_config &config);
void cru_map(address_map &map);
void memmap(address_map &map);
+ void memmap_setoffset(address_map &map);
private:
void datamux_clock_in(int clock);
@@ -291,7 +292,12 @@ enum
void ti99_4p_state::memmap(address_map &map)
{
- map(0x0000, 0xffff).rw(FUNC(ti99_4p_state::memread), FUNC(ti99_4p_state::memwrite)).setoffset(FUNC(ti99_4p_state::setoffset));
+ map(0x0000, 0xffff).rw(FUNC(ti99_4p_state::memread), FUNC(ti99_4p_state::memwrite));
+}
+
+void ti99_4p_state::memmap_setoffset(address_map &map)
+{
+ map(0x0000, 0xffff).r(FUNC(ti99_4p_state::setoffset));
}
void ti99_4p_state::cru_map(address_map &map)
@@ -427,9 +433,9 @@ int ti99_4p_state::decode_address(int address)
Called when the memory access starts by setting the address bus. From that
point on, we suspend the CPU until all operations are done.
*/
-SETOFFSET_MEMBER( ti99_4p_state::setoffset )
+READ8_MEMBER( ti99_4p_state::setoffset )
{
- m_addr_buf = offset << 1;
+ m_addr_buf = offset;
m_waitcount = 0;
if (TRACE_ADDRESS) logerror("set address %04x\n", m_addr_buf);
@@ -453,6 +459,8 @@ SETOFFSET_MEMBER( ti99_4p_state::setoffset )
}
ready_join();
+
+ return 0;
}
READ16_MEMBER( ti99_4p_state::memread )
@@ -1002,6 +1010,7 @@ MACHINE_CONFIG_START(ti99_4p_state::ti99_4p_60hz)
/* basic machine hardware */
/* TMS9900 CPU @ 3.0 MHz */
MCFG_TMS99xx_ADD("maincpu", TMS9900, 3000000, memmap, cru_map)
+ MCFG_DEVICE_ADDRESS_MAP(tms99xx_device::AS_SETOFFSET, memmap_setoffset)
MCFG_TMS99xx_EXTOP_HANDLER( WRITE8(*this, ti99_4p_state, external_operation) )
MCFG_TMS99xx_INTLEVEL_HANDLER( READ8(*this, ti99_4p_state, interrupt_level) )
MCFG_TMS99xx_CLKOUT_HANDLER( WRITELINE(*this, ti99_4p_state, clock_out) )
diff --git a/src/mame/drivers/ti99_4x.cpp b/src/mame/drivers/ti99_4x.cpp
index b20c87c8fd5..0eb2f7a19ff 100644
--- a/src/mame/drivers/ti99_4x.cpp
+++ b/src/mame/drivers/ti99_4x.cpp
@@ -159,6 +159,7 @@ public:
void ti99_4_60hz(machine_config &config);
void cru_map(address_map &map);
void memmap(address_map &map);
+ void memmap_setoffset(address_map &map);
private:
void set_keyboard_column(int number, int data);
int m_keyboard_column;
@@ -228,7 +229,13 @@ enum
void ti99_4x_state::memmap(address_map &map)
{
map.global_mask(0xffff);
- map(0x0000, 0xffff).rw(TI99_DATAMUX_TAG, FUNC(bus::ti99::internal::datamux_device::read), FUNC(bus::ti99::internal::datamux_device::write)).setoffset(TI99_DATAMUX_TAG, FUNC(bus::ti99::internal::datamux_device::setoffset));
+ map(0x0000, 0xffff).rw(TI99_DATAMUX_TAG, FUNC(bus::ti99::internal::datamux_device::read), FUNC(bus::ti99::internal::datamux_device::write));
+}
+
+void ti99_4x_state::memmap_setoffset(address_map &map)
+{
+ map.global_mask(0xffff);
+ map(0x0000, 0xffff).r(TI99_DATAMUX_TAG, FUNC(bus::ti99::internal::datamux_device::setoffset));
}
/*
@@ -850,6 +857,7 @@ MACHINE_RESET_MEMBER(ti99_4x_state,ti99_4)
MACHINE_CONFIG_START(ti99_4x_state::ti99_4)
// CPU
MCFG_TMS99xx_ADD("maincpu", TMS9900, 3000000, memmap, cru_map)
+ MCFG_DEVICE_ADDRESS_MAP(tms99xx_device::AS_SETOFFSET, memmap_setoffset)
MCFG_TMS99xx_EXTOP_HANDLER( WRITE8(*this, ti99_4x_state, external_operation) )
MCFG_TMS99xx_INTLEVEL_HANDLER( READ8(*this, ti99_4x_state, interrupt_level) )
MCFG_TMS99xx_CLKOUT_HANDLER( WRITELINE(*this, ti99_4x_state, clock_out) )
@@ -968,6 +976,7 @@ MACHINE_RESET_MEMBER(ti99_4x_state,ti99_4a)
MACHINE_CONFIG_START(ti99_4x_state::ti99_4a)
// CPU
MCFG_TMS99xx_ADD("maincpu", TMS9900, 3000000, memmap, cru_map)
+ MCFG_DEVICE_ADDRESS_MAP(tms99xx_device::AS_SETOFFSET, memmap_setoffset)
MCFG_TMS99xx_EXTOP_HANDLER( WRITE8(*this, ti99_4x_state, external_operation) )
MCFG_TMS99xx_INTLEVEL_HANDLER( READ8(*this, ti99_4x_state, interrupt_level) )
MCFG_TMS99xx_CLKOUT_HANDLER( WRITELINE(*this, ti99_4x_state, clock_out) )
@@ -1128,6 +1137,7 @@ MACHINE_RESET_MEMBER(ti99_4x_state, ti99_4ev)
MACHINE_CONFIG_START(ti99_4x_state::ti99_4ev_60hz)
// CPU
MCFG_TMS99xx_ADD("maincpu", TMS9900, 3000000, memmap, cru_map)
+ MCFG_DEVICE_ADDRESS_MAP(tms99xx_device::AS_SETOFFSET, memmap_setoffset)
MCFG_TMS99xx_EXTOP_HANDLER( WRITE8(*this, ti99_4x_state, external_operation) )
MCFG_TMS99xx_INTLEVEL_HANDLER( READ8(*this, ti99_4x_state, interrupt_level) )
MCFG_TMS99xx_CLKOUT_HANDLER( WRITELINE(*this, ti99_4x_state, clock_out) )
diff --git a/src/mame/drivers/ti99_8.cpp b/src/mame/drivers/ti99_8.cpp
index f45dbb0b894..9f2f3ae95c6 100644
--- a/src/mame/drivers/ti99_8.cpp
+++ b/src/mame/drivers/ti99_8.cpp
@@ -267,6 +267,7 @@ public:
void ti99_8_50hz(machine_config &config);
void crumap(address_map &map);
void memmap(address_map &map);
+ void memmap_setoffset(address_map &map);
private:
// Keyboard support
void set_keyboard_column(int number, int data);
@@ -297,7 +298,12 @@ private:
*/
void ti99_8_state::memmap(address_map &map)
{
- map(0x0000, 0xffff).rw(TI998_MAINBOARD_TAG, FUNC(bus::ti99::internal::mainboard8_device::read), FUNC(bus::ti99::internal::mainboard8_device::write)).setoffset(TI998_MAINBOARD_TAG, FUNC(bus::ti99::internal::mainboard8_device::setoffset));
+ map(0x0000, 0xffff).rw(TI998_MAINBOARD_TAG, FUNC(bus::ti99::internal::mainboard8_device::read), FUNC(bus::ti99::internal::mainboard8_device::write));
+}
+
+void ti99_8_state::memmap_setoffset(address_map &map)
+{
+ map(0x0000, 0xffff).r(TI998_MAINBOARD_TAG, FUNC(bus::ti99::internal::mainboard8_device::setoffset));
}
/*
@@ -723,6 +729,7 @@ MACHINE_CONFIG_START(ti99_8_state::ti99_8)
// TMS9995-MP9537 CPU @ 10.7 MHz
// MP9537 mask: This variant of the TMS9995 does not contain on-chip RAM
MCFG_TMS99xx_ADD("maincpu", TMS9995_MP9537, XTAL(10'738'635), memmap, crumap)
+ MCFG_DEVICE_ADDRESS_MAP(tms9995_device::AS_SETOFFSET, memmap_setoffset)
MCFG_TMS9995_EXTOP_HANDLER( WRITE8(*this, ti99_8_state, external_operation) )
MCFG_TMS9995_CLKOUT_HANDLER( WRITELINE(*this, ti99_8_state, clock_out) )
MCFG_TMS9995_DBIN_HANDLER( WRITELINE(*this, ti99_8_state, dbin_line) )
diff --git a/src/mame/machine/c117.cpp b/src/mame/machine/c117.cpp
index ae8ed387190..ff01f311cda 100644
--- a/src/mame/machine/c117.cpp
+++ b/src/mame/machine/c117.cpp
@@ -92,9 +92,6 @@ void namco_c117_device::device_reset()
m_offsets[1][0] = 0x0180 * 0x2000; // bank0 = 0x180(RAM) - evidence: wldcourt
m_offsets[1][7] = 0x03ff * 0x2000; // bank7 = 0x3ff(PRG7)
- m_cpucache[0]->force_update();
- m_cpucache[1]->force_update();
-
m_subres = m_wdog = 0;
m_subres_cb(ASSERT_LINE);
@@ -192,10 +189,7 @@ void namco_c117_device::register_w(int whichcpu, offs_t offset, uint8_t data)
break;
case 14: // FC00 - set initial ROM bank for sub CPU
if (whichcpu == 0)
- {
m_offsets[1][7] = 0x600000 | (data * 0x2000);
- m_cpucache[1]->force_update();
- }
else
unknown_reg = true;
break;
@@ -215,8 +209,6 @@ void namco_c117_device::bankswitch(int whichcpu, int whichbank, int a0, uint8_t
bank = (bank & 0x1fe000) | ((data & 0x03) * 0x200000);
else
bank = (bank & 0x600000) | (data * 0x2000);
-
- m_cpucache[whichcpu]->force_update();
}
void namco_c117_device::kick_watchdog(int whichcpu)
diff --git a/src/mame/machine/segaic16.cpp b/src/mame/machine/segaic16.cpp
index b3690fd06cc..5f80c323933 100644
--- a/src/mame/machine/segaic16.cpp
+++ b/src/mame/machine/segaic16.cpp
@@ -621,6 +621,7 @@ void sega_315_5195_mapper_device::update_mapping()
if (LOG_MEMORY_MAP) osd_printf_debug("----\nRemapping:\n");
// first reset everything back to the beginning
+ m_space->unmap_readwrite(0x000000, 0xffffff);
m_space->install_readwrite_handler(0x000000, 0xffffff, read8_delegate(FUNC(sega_315_5195_mapper_device::read), this), write8_delegate(FUNC(sega_315_5195_mapper_device::write), this), 0x00ff);
// loop over the regions