diff options
Diffstat (limited to 'src/emu/emumem.h')
-rw-r--r-- | src/emu/emumem.h | 222 |
1 files changed, 153 insertions, 69 deletions
diff --git a/src/emu/emumem.h b/src/emu/emumem.h index 995a09a1606..4554bea5ed6 100644 --- a/src/emu/emumem.h +++ b/src/emu/emumem.h @@ -438,6 +438,16 @@ template<> struct handler_entry_size<3> { using uX = u64; }; // =====================-> Address segmentation for the search tree +constexpr int handler_entry_dispatch_level(int highbits) +{ + return (highbits > 48) ? 3 : (highbits > 32) ? 2 : (highbits > 14) ? 1 : 0; +} + +constexpr int handler_entry_dispatch_level_to_lowbits(int level, int width, int ashift) +{ + return level == 3 ? 48 : level == 2 ? 32 : level == 1 ? 14 : width + ashift; +} + constexpr int handler_entry_dispatch_lowbits(int highbits, int width, int ashift) { return (highbits > 48) ? 48 : @@ -451,7 +461,7 @@ constexpr int handler_entry_dispatch_lowbits(int highbits, int width, int ashift // ======================> memory_units_descritor forwards declaration -template<int Width, int AddrShift, int Endian> class memory_units_descriptor; +template<int Width, int AddrShift, endianness_t Endian> class memory_units_descriptor; @@ -463,7 +473,7 @@ class handler_entry { DISABLE_COPYING(handler_entry); - template<int Width, int AddrShift, endianness_t Endian> friend class address_space_specific; + template<int Level, int Width, int AddrShift, endianness_t Endian> friend class address_space_specific; public: // Typing flags @@ -535,9 +545,9 @@ protected: // Provides the populate/read/get_ptr/lookup API -template<int Width, int AddrShift, int Endian> class handler_entry_read_passthrough; +template<int Width, int AddrShift, endianness_t Endian> class handler_entry_read_passthrough; -template<int Width, int AddrShift, int Endian> class handler_entry_read : public handler_entry +template<int Width, int AddrShift, endianness_t Endian> class handler_entry_read : public handler_entry { public: using uX = typename emu::detail::handler_entry_size<Width>::uX; @@ -553,7 +563,7 @@ public: handler_entry_read(address_space *space, u32 flags) : handler_entry(space, flags) {} ~handler_entry_read() {} - virtual uX read(offs_t offset, uX mem_mask) = 0; + virtual uX read(offs_t offset, uX mem_mask) const = 0; virtual void *get_ptr(offs_t offset) const; virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift, Endian> *&handler) const; @@ -599,16 +609,16 @@ public: virtual void detach(const std::unordered_set<handler_entry *> &handlers); // Return the internal structures of the root dispatch - virtual void get_dispatch(handler_entry_read<Width, AddrShift, Endian> *const *&dispatch, u8 &shift) const; + virtual const handler_entry_read<Width, AddrShift, Endian> *const *get_dispatch() const; }; // =====================-> The parent class of all write handlers // Provides the populate/write/get_ptr/lookup API -template<int Width, int AddrShift, int Endian> class handler_entry_write_passthrough; +template<int Width, int AddrShift, endianness_t Endian> class handler_entry_write_passthrough; -template<int Width, int AddrShift, int Endian> class handler_entry_write : public handler_entry +template<int Width, int AddrShift, endianness_t Endian> class handler_entry_write : public handler_entry { public: using uX = typename emu::detail::handler_entry_size<Width>::uX; @@ -624,7 +634,7 @@ public: handler_entry_write(address_space *space, u32 flags) : handler_entry(space, flags) {} virtual ~handler_entry_write() {} - virtual void write(offs_t offset, uX data, uX mem_mask) = 0; + virtual void write(offs_t offset, uX data, uX mem_mask) const = 0; virtual void *get_ptr(offs_t offset) const; virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift, Endian> *&handler) const; @@ -671,14 +681,14 @@ public: virtual void detach(const std::unordered_set<handler_entry *> &handlers); // Return the internal structures of the root dispatch - virtual void get_dispatch(handler_entry_write<Width, AddrShift, Endian> *const *&dispatch, u8 &shift) const; + virtual const handler_entry_write<Width, AddrShift, Endian> *const *get_dispatch() const; }; // =====================-> Passthrough handler management structure class memory_passthrough_handler { - template<int Width, int AddrShift, int Endian> friend class handler_entry_read_passthrough; - template<int Width, int AddrShift, int Endian> friend class handler_entry_write_passthrough; + template<int Width, int AddrShift, endianness_t Endian> friend class handler_entry_read_passthrough; + template<int Width, int AddrShift, endianness_t Endian> friend class handler_entry_write_passthrough; public: memory_passthrough_handler(address_space &space) : m_space(space) {} @@ -695,8 +705,8 @@ private: // =====================-> Forward declaration for address_space -template<int Width, int AddrShift, int Endian> class handler_entry_read_unmapped; -template<int Width, int AddrShift, int Endian> class handler_entry_write_unmapped; +template<int Width, int AddrShift, endianness_t Endian> class handler_entry_read_unmapped; +template<int Width, int AddrShift, endianness_t Endian> class handler_entry_write_unmapped; // ======================> address offset -> byte offset @@ -705,7 +715,7 @@ constexpr offs_t memory_offset_to_byte(offs_t offset, int AddrShift) { return Ad // ======================> generic read/write decomposition routines // generic direct read -template<int Width, int AddrShift, int Endian, int TargetWidth, bool Aligned, typename T> typename emu::detail::handler_entry_size<TargetWidth>::uX memory_read_generic(T rop, offs_t address, typename emu::detail::handler_entry_size<TargetWidth>::uX mask) +template<int Width, int AddrShift, endianness_t Endian, int TargetWidth, bool Aligned, typename T> typename emu::detail::handler_entry_size<TargetWidth>::uX memory_read_generic(T rop, offs_t address, typename emu::detail::handler_entry_size<TargetWidth>::uX mask) { using TargetType = typename emu::detail::handler_entry_size<TargetWidth>::uX; using NativeType = typename emu::detail::handler_entry_size<Width>::uX; @@ -839,7 +849,7 @@ template<int Width, int AddrShift, int Endian, int TargetWidth, bool Aligned, ty } // generic direct write -template<int Width, int AddrShift, int Endian, int TargetWidth, bool Aligned, typename T> void memory_write_generic(T wop, offs_t address, typename emu::detail::handler_entry_size<TargetWidth>::uX data, typename emu::detail::handler_entry_size<TargetWidth>::uX mask) +template<int Width, int AddrShift, endianness_t Endian, int TargetWidth, bool Aligned, typename T> void memory_write_generic(T wop, offs_t address, typename emu::detail::handler_entry_size<TargetWidth>::uX data, typename emu::detail::handler_entry_size<TargetWidth>::uX mask) { using NativeType = typename emu::detail::handler_entry_size<Width>::uX; @@ -964,15 +974,17 @@ template<int Width, int AddrShift, int Endian, int TargetWidth, bool Aligned, ty // ======================> Direct dispatching -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX dispatch_read(offs_t mask, u8 shift, offs_t offset, typename emu::detail::handler_entry_size<Width>::uX mem_mask, handler_entry_read<Width, AddrShift, Endian> *const *dispatch) +template<int Level, int Width, int AddrShift, endianness_t Endian> typename emu::detail::handler_entry_size<Width>::uX dispatch_read(offs_t mask, offs_t offset, typename emu::detail::handler_entry_size<Width>::uX mem_mask, const handler_entry_read<Width, AddrShift, Endian> *const *dispatch) { - return dispatch[(offset >> shift) & mask]->read(offset, mem_mask); + static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LowBits]->read(offset, mem_mask); } -template<int Width, int AddrShift, int Endian> void dispatch_write(offs_t mask, u8 shift, offs_t offset, typename emu::detail::handler_entry_size<Width>::uX data, typename emu::detail::handler_entry_size<Width>::uX mem_mask, handler_entry_write<Width, AddrShift, Endian> *const *dispatch) +template<int Level, int Width, int AddrShift, endianness_t Endian> void dispatch_write(offs_t mask, offs_t offset, typename emu::detail::handler_entry_size<Width>::uX data, typename emu::detail::handler_entry_size<Width>::uX mem_mask, const handler_entry_write<Width, AddrShift, Endian> *const *dispatch) { - return dispatch[(offset >> shift) & mask]->write(offset, data, mem_mask); + static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LowBits]->write(offset, data, mem_mask); } @@ -980,21 +992,29 @@ template<int Width, int AddrShift, int Endian> void dispatch_write(offs_t mask, // memory_access_specific does uncached but faster accesses by shortcutting the address_space virtual call -template<int Width, int AddrShift, int Endian> class memory_access_specific +namespace emu { namespace detail { + +template<int Level, int Width, int AddrShift, endianness_t Endian> class memory_access_specific { + friend class ::address_space; + using NativeType = typename emu::detail::handler_entry_size<Width>::uX; static constexpr u32 NATIVE_BYTES = 1 << Width; static constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? (1 << (Width + AddrShift)) - 1 : 0; public: // construction/destruction - memory_access_specific(address_space &space, - handler_entry_read <Width, AddrShift, Endian> *const *dispatch_read, u8 shift_read, - handler_entry_write<Width, AddrShift, Endian> *const *dispatch_write, u8 shift_write); - + memory_access_specific() + : m_space(nullptr), + m_addrmask(0), + m_dispatch_read(nullptr), + m_dispatch_write(nullptr) + { + } - // getters - address_space &space() const { return m_space; } + inline address_space &space() const { + return *m_space; + } u8 read_byte(offs_t address) { return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); } u16 read_word(offs_t address) { return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } @@ -1025,22 +1045,22 @@ public: void write_qword_unaligned(offs_t address, u64 data, u64 mask) { memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } private: - address_space & m_space; + address_space * m_space; offs_t m_addrmask; // address mask - handler_entry_read<Width, AddrShift, Endian> *const *m_dispatch_read; - handler_entry_write<Width, AddrShift, Endian> *const *m_dispatch_write; - u8 m_shift_read; - u8 m_shift_write; + const handler_entry_read<Width, AddrShift, Endian> *const *m_dispatch_read; + const handler_entry_write<Width, AddrShift, Endian> *const *m_dispatch_write; NativeType read_native(offs_t address, NativeType mask = ~NativeType(0)) { - return dispatch_read<Width, AddrShift, Endian>(offs_t(-1), m_shift_read, address & m_addrmask, mask, m_dispatch_read); + return dispatch_read<Level, Width, AddrShift, Endian>(m_addrmask, address, mask, m_dispatch_read);; } void write_native(offs_t address, NativeType data, NativeType mask = ~NativeType(0)) { - dispatch_write<Width, AddrShift, Endian>(offs_t(-1), m_shift_write, address & m_addrmask, data, mask, m_dispatch_write); + dispatch_write<Level, Width, AddrShift, Endian>(m_addrmask, address, data, mask, m_dispatch_write);; } + + void set(address_space *space, std::pair<const void *, const void *> rw); }; @@ -1048,23 +1068,32 @@ private: // ======================> memory_access_cache // memory_access_cache contains state data for cached access -template<int Width, int AddrShift, int Endian> class memory_access_cache +template<int Width, int AddrShift, endianness_t Endian> class memory_access_cache { + friend class ::address_space; + using NativeType = typename emu::detail::handler_entry_size<Width>::uX; static constexpr u32 NATIVE_BYTES = 1 << Width; static constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? (1 << (Width + AddrShift)) - 1 : 0; public: // construction/destruction - memory_access_cache(address_space &space, - handler_entry_read <Width, AddrShift, Endian> *root_read, - handler_entry_write<Width, AddrShift, Endian> *root_write); + memory_access_cache() + : m_space(nullptr), + m_addrmask(0), + 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(nullptr), + m_root_write(nullptr) + { + } ~memory_access_cache(); - // getters - address_space &space() const { return m_space; } - // see if an address is within bounds, update it if not void check_address_r(offs_t address) { if(address >= m_addrstart_r && address <= m_addrend_r) @@ -1080,6 +1109,10 @@ public: // accessor methods + inline address_space &space() const { + return *m_space; + } + void *read_ptr(offs_t address) { address &= m_addrmask; check_address_r(address); @@ -1115,7 +1148,7 @@ public: void write_qword_unaligned(offs_t address, u64 data, u64 mask) { memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } private: - address_space & m_space; + address_space * m_space; int m_notifier_id; // id to remove the notifier on destruction @@ -1124,7 +1157,7 @@ private: offs_t m_addrend_r; // maximum valid address for reading offs_t m_addrstart_w; // minimum valid address for writing offs_t m_addrend_w; // maximum valid address for writing - handler_entry_read<Width, AddrShift, Endian> *m_cache_r; // read cache + handler_entry_read <Width, AddrShift, Endian> *m_cache_r; // read cache handler_entry_write<Width, AddrShift, Endian> *m_cache_w; // write cache handler_entry_read <Width, AddrShift, Endian> *m_root_read; // decode tree roots @@ -1132,9 +1165,23 @@ private: NativeType read_native(offs_t address, NativeType mask = ~NativeType(0)); void write_native(offs_t address, NativeType data, NativeType mask = ~NativeType(0)); + + void set(address_space *space, std::pair<void *, void *> rw); +}; +}} + + +// ======================> memory_access cache/specific type dispatcher + +template<int HighBits, int Width, int AddrShift, endianness_t Endian> struct memory_access { + static constexpr int Level = emu::detail::handler_entry_dispatch_level(HighBits); + + using cache = emu::detail::memory_access_cache<Width, AddrShift, Endian>; + using specific = emu::detail::memory_access_specific<Level, Width, AddrShift, Endian>; }; + // ======================> address_space_config // describes an address space and provides basic functions to map addresses to bytes @@ -1190,9 +1237,8 @@ class address_space { friend class memory_bank; friend class memory_block; - template<int Width, int AddrShift, int Endian> friend class handler_entry_read_unmapped; - template<int Width, int AddrShift, int Endian> friend class handler_entry_write_unmapped; - template<int Width, int AddrShift, int Endian> friend class memory_access_cache; + template<int Width, int AddrShift, endianness_t Endian> friend class handler_entry_read_unmapped; + template<int Width, int AddrShift, endianness_t Endian> friend class handler_entry_write_unmapped; struct notifier_t { std::function<void (read_or_write)> m_notifier; @@ -1212,7 +1258,7 @@ public: int spacenum() const { return m_spacenum; } address_map *map() const { return m_map.get(); } - template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrShift, Endian> *cache() { + template<int Width, int AddrShift, endianness_t Endian> void cache(emu::detail::memory_access_cache<Width, AddrShift, Endian> &v) { if(AddrShift != m_config.addr_shift()) fatalerror("Requesting cache() with address shift %d while the config says %d\n", AddrShift, m_config.addr_shift()); if(8 << Width != m_config.data_width()) @@ -1221,19 +1267,21 @@ public: fatalerror("Requesting cache() with endianness %s while the config says %s\n", endianness_names[Endian], endianness_names[m_config.endianness()]); - return static_cast<memory_access_cache<Width, AddrShift, Endian> *>(create_cache()); + v.set(this, get_cache_info()); } - template<int Width, int AddrShift, int Endian> memory_access_specific<Width, AddrShift, Endian> *specific() { + template<int Level, int Width, int AddrShift, endianness_t Endian> void specific(emu::detail::memory_access_specific<Level, Width, AddrShift, Endian> &v) { + if(Level != emu::detail::handler_entry_dispatch_level(m_config.addr_width())) + fatalerror("Requesting specific() with wrong level, bad address witdh (the config says %d\n", m_config.addr_width()); if(AddrShift != m_config.addr_shift()) - fatalerror("Requesting cache() with address shift %d while the config says %d\n", AddrShift, m_config.addr_shift()); + fatalerror("Requesting specific() with address shift %d while the config says %d\n", AddrShift, m_config.addr_shift()); if(8 << Width != m_config.data_width()) - fatalerror("Requesting cache() with data width %d while the config says %d\n", 8 << Width, m_config.data_width()); + fatalerror("Requesting specific() with data width %d while the config says %d\n", 8 << Width, m_config.data_width()); if(Endian != m_config.endianness()) - fatalerror("Requesting cache() with endianness %s while the config says %s\n", + fatalerror("Requesting spefific() with endianness %s while the config says %s\n", endianness_names[Endian], endianness_names[m_config.endianness()]); - return static_cast<memory_access_specific<Width, AddrShift, Endian> *>(create_specific()); + v.set(this, get_specific_info()); } int add_change_notifier(std::function<void (read_or_write)> n); @@ -1551,13 +1599,13 @@ public: void allocate_memory(); void locate_memory(); - template<int Width, int AddrShift, int Endian> handler_entry_read_unmapped <Width, AddrShift, Endian> *get_unmap_r() const { return static_cast<handler_entry_read_unmapped <Width, AddrShift, Endian> *>(m_unmap_r); } - template<int Width, int AddrShift, int Endian> handler_entry_write_unmapped<Width, AddrShift, Endian> *get_unmap_w() const { return static_cast<handler_entry_write_unmapped<Width, AddrShift, Endian> *>(m_unmap_w); } + template<int Width, int AddrShift, endianness_t Endian> handler_entry_read_unmapped <Width, AddrShift, Endian> *get_unmap_r() const { return static_cast<handler_entry_read_unmapped <Width, AddrShift, Endian> *>(m_unmap_r); } + template<int Width, int AddrShift, endianness_t Endian> handler_entry_write_unmapped<Width, AddrShift, Endian> *get_unmap_w() const { return static_cast<handler_entry_write_unmapped<Width, AddrShift, Endian> *>(m_unmap_w); } protected: // internal helpers - virtual void *create_cache() = 0; - virtual void *create_specific() = 0; + virtual std::pair<void *, void *> get_cache_info() = 0; + virtual std::pair<const void *, const void *> get_specific_info() = 0; void populate_map_entry(const address_map_entry &entry, read_or_write readorwrite); virtual void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) = 0; @@ -1797,7 +1845,7 @@ private: class memory_manager { friend class address_space; - template<int Width, int AddrShift, endianness_t Endian> friend class address_space_specific; + template<int Level, int Width, int AddrShift, endianness_t Endian> friend class address_space_specific; friend memory_region::memory_region(running_machine &machine, const char *name, u32 length, u8 width, endianness_t endian); public: // construction/destruction @@ -1913,14 +1961,19 @@ private: #define QWORD_ALIGNED(a) (((a) & 7) == 0) -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX memory_access_cache<Width, AddrShift, Endian>::read_native(offs_t address, typename emu::detail::handler_entry_size<Width>::uX mask) +template<int Width, int AddrShift, endianness_t Endian> +typename emu::detail::handler_entry_size<Width>::uX +emu::detail::memory_access_cache<Width, AddrShift, Endian>:: +read_native(offs_t address, typename emu::detail::handler_entry_size<Width>::uX mask) { address &= m_addrmask; check_address_r(address); return m_cache_r->read(address, mask); } -template<int Width, int AddrShift, int Endian> void memory_access_cache<Width, AddrShift, Endian>::write_native(offs_t address, typename emu::detail::handler_entry_size<Width>::uX data, typename emu::detail::handler_entry_size<Width>::uX mask) +template<int Width, int AddrShift, endianness_t Endian> +void emu::detail::memory_access_cache<Width, AddrShift, Endian>:: +write_native(offs_t address, typename emu::detail::handler_entry_size<Width>::uX data, typename emu::detail::handler_entry_size<Width>::uX mask) { address &= m_addrmask; check_address_w(address); @@ -1932,16 +1985,47 @@ void memory_passthrough_handler::remove() m_space.remove_passthrough(m_handlers); } -template<int Width, int AddrShift, int Endian> memory_access_specific<Width, AddrShift, Endian>::memory_access_specific(address_space &space, - handler_entry_read <Width, AddrShift, Endian> *const *dispatch_read, u8 shift_read, - handler_entry_write<Width, AddrShift, Endian> *const *dispatch_write, u8 shift_write) - : m_space(space), - m_addrmask(space.addrmask()), - m_dispatch_read(dispatch_read), - m_dispatch_write(dispatch_write), - m_shift_read(shift_read), - m_shift_write(shift_write) + +template<int Level, int Width, int AddrShift, endianness_t Endian> +void emu::detail::memory_access_specific<Level, Width, AddrShift, Endian>:: +set(address_space *space, std::pair<const void *, const void *> rw) +{ + m_space = space; + m_addrmask = space->addrmask(); + m_dispatch_read = (const handler_entry_read <Width, AddrShift, Endian> *const *)(rw.first); + m_dispatch_write = (const handler_entry_write<Width, AddrShift, Endian> *const *)(rw.second); +} + + +template<int Width, int AddrShift, endianness_t Endian> +void emu::detail::memory_access_cache<Width, AddrShift, Endian>:: +set(address_space *space, std::pair<void *, void *> rw) +{ + m_space = space; + m_addrmask = space->addrmask(); + + 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; + } + }); + m_root_read = (handler_entry_read <Width, AddrShift, Endian> *)(rw.first); + m_root_write = (handler_entry_write<Width, AddrShift, Endian> *)(rw.second); +} + +template<int Width, int AddrShift, endianness_t Endian> +emu::detail::memory_access_cache<Width, AddrShift, Endian>:: +~memory_access_cache() { + if(m_space) + m_space->remove_change_notifier(m_notifier_id); } #endif /* MAME_EMU_EMUMEM_H */ |