diff options
Diffstat (limited to 'src/emu/emumem.cpp')
-rw-r--r-- | src/emu/emumem.cpp | 4554 |
1 files changed, 869 insertions, 3685 deletions
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 ⦥ - - // 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; - } - } - } -} |