// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** devcb.h Device callback interface helpers. ***************************************************************************/ #pragma once #ifndef __EMU_H__ #error Dont include this file directly; include emu.h instead. #endif #ifndef MAME_EMU_DEVCB_H #define MAME_EMU_DEVCB_H #include #include #include namespace emu { namespace detail { template struct devcb_delegate_initialiser { devcb_delegate_initialiser(char const *tag, Delegate &&delegate) : m_base(nullptr), m_delegate(std::move(delegate)) { } template devcb_delegate_initialiser(T &device, std::enable_if_t::value, Delegate &&> delegate) : m_base(&device), m_delegate(std::move(delegate)) { } template devcb_delegate_initialiser(T &interface, std::enable_if_t::value, Delegate &&> delegate) : m_base(&interface.device()), m_delegate(std::move(delegate)) { } template devcb_delegate_initialiser(device_finder const &finder, Delegate &&delegate) : m_base(&finder.finder_target().first), m_delegate(std::move(delegate)) { } device_t *m_base; Delegate &&m_delegate; }; inline char const *devcb_delegate_get_tag(char const *tag) { return tag; } template inline std::enable_if_t::value, char const *> devcb_delegate_get_tag(T &device) { return DEVICE_SELF; } template inline std::enable_if_t::value, char const *> devcb_delegate_get_tag(T &interface) { return DEVICE_SELF; } template inline char const *devcb_delegate_get_tag(device_finder const &finder) { return finder.finder_tag(); } template struct devcb_tag_desc_creator { static DescType create(char const *tag) { return DescType{ nullptr, tag }; } static DescType create(device_t &device) { return DescType{ &device, DEVICE_SELF }; } static DescType create(device_interface &interface) { return DescType{ &interface.device(), DEVICE_SELF }; } template static DescType create(device_finder const &finder) { std::pair const target(finder.finder_target()); return DescType{ &target.first, target.second }; } }; template struct devcb_line_desc_creator { static DescType create(char const *tag, int inputnum) { return DescType{ nullptr, tag, inputnum }; } static DescType create(device_t &device, int inputnum) { return DescType{ &device, DEVICE_SELF, inputnum }; } static DescType create(device_interface &interface, int inputnum) { return DescType{ &interface.device(), DEVICE_SELF, inputnum }; } template static DescType create(device_finder const &finder, int inputnum) { std::pair const target(finder.finder_target()); return DescType{ &target.first, target.second, inputnum }; } }; } } // namespace emu::detail //************************************************************************** // MACROS //************************************************************************** // wrappers for read callbacks into any tagged device #define DEVCB_READLINE(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), read_line_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) #define DEVCB_READ8(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), read8_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) #define DEVCB_READ16(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), read16_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) #define DEVCB_READ32(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), read32_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) #define DEVCB_READ64(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), read64_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) // wrappers for write callbacks into any tagged device #define DEVCB_WRITELINE(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), write_line_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) #define DEVCB_WRITE8(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), write8_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) #define DEVCB_WRITE16(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), write16_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) #define DEVCB_WRITE32(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), write32_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) #define DEVCB_WRITE64(tag, _class, _func) (emu::detail::devcb_delegate_initialiser((tag), write64_delegate(&_class::_func, #_class "::" #_func, emu::detail::devcb_delegate_get_tag(tag), (_class *)nullptr))) // machine config helpers to add shift, mask, or address space configuration #define MCFG_DEVCB_RSHIFT(_shift) devcb->set_rshift(_shift); #define MCFG_DEVCB_MASK(_mask) devcb->set_mask(_mask); #define MCFG_DEVCB_BIT(_bit) devcb->set_rshift(-(_bit)).set_mask(1ULL << (_bit)); #define MCFG_DEVCB_XOR(_xor) devcb->set_xor(_xor); #define MCFG_DEVCB_INVERT devcb->set_xor(~u64(0)); #define MCFG_DEVCB_ADDRESS_SPACE(_device, _spacenum) devcb->set_space(_device, _spacenum); // machine config helpers for chaining callbacks #define MCFG_DEVCB_CHAIN_INPUT(_desc) devcb = &downcast(*devcb).chain_alloc().set_callback(DEVCB_##_desc); #define MCFG_DEVCB_CHAIN_OUTPUT(_desc) devcb = &downcast(*devcb).chain_alloc().set_callback(DEVCB_##_desc); //************************************************************************** // TYPE DEFINITIONS //************************************************************************** // base delegate type for a read_line/write_line typedef device_delegate read_line_delegate; typedef device_delegate write_line_delegate; // ======================> devcb_base class devcb_base { protected: // enumerate the types of callbacks enum callback_type { CALLBACK_NONE, CALLBACK_LINE, CALLBACK_8, CALLBACK_16, CALLBACK_32, CALLBACK_64, CALLBACK_IOPORT, CALLBACK_MEMBANK, CALLBACK_OUTPUT, CALLBACK_LOG, CALLBACK_CONSTANT, CALLBACK_INPUTLINE, CALLBACK_ASSERTLINE, CALLBACK_CLEARLINE, CALLBACK_HOLDLINE }; // construction/destruction devcb_base(device_t &device, u64 defmask); virtual ~devcb_base(); template struct tag_desc { device_t *m_base; char const *m_tag; }; template struct line_desc { device_t *m_base; char const *m_tag; int m_inputnum; }; public: // getters bool isnull() const { return (m_type == CALLBACK_NONE); } // additional configuration devcb_base &set_space(const char *device, int space = 0) { m_space_tag = device; m_space_num = space; return *this; } devcb_base &set_rshift(int rshift) { m_rshift = rshift; return *this; } devcb_base &set_mask(u64 mask) { m_mask = mask; return *this; } devcb_base &set_xor(u64 xorval) { m_xor = xorval; return *this; } // construction helper classes struct null_desc { }; using ioport_desc = tag_desc; using membank_desc = tag_desc; using output_desc = tag_desc; struct constant_desc { u64 m_value; }; struct logger_desc { const char *m_string; }; using inputline_desc = line_desc; using assertline_desc = line_desc; using clearline_desc = line_desc; using holdline_desc = line_desc; // shared callback setters devcb_base &set_callback(null_desc null) { reset(CALLBACK_NONE); return *this; } template devcb_base &set_callback(tag_desc desc) { if (desc.m_base) reset(*desc.m_base, Type); else reset(Type); m_target_tag = desc.m_tag; return *this; } devcb_base &set_callback(constant_desc constant) { reset(CALLBACK_CONSTANT); m_target_int = constant.m_value; return *this; } devcb_base &set_callback(logger_desc logger) { reset(CALLBACK_LOG); m_target_tag = logger.m_string; return *this; } void reset() { reset(m_owner, CALLBACK_NONE); } protected: // internal helpers inline u64 shift_mask(u64 value) const { return ((m_rshift < 0) ? (value << -m_rshift) : (value >> m_rshift)) & m_mask; } inline u64 shift_mask_xor(u64 value) const { return (((m_rshift < 0) ? (value << -m_rshift) : (value >> m_rshift)) ^ m_xor) & m_mask; } inline u64 unshift_mask(u64 value) const { return (m_rshift < 0) ? ((value & m_mask) >> -m_rshift) : ((value & m_mask) << m_rshift); } inline u64 unshift_mask_xor(u64 value) const { return (m_rshift < 0) ? (((value ^ m_xor) & m_mask) >> -m_rshift) : (((value ^ m_xor) & m_mask) << m_rshift); } void reset(callback_type type); void reset(device_t &base, callback_type type); virtual void devcb_reset() = 0; void resolve_ioport(); void resolve_membank(); void resolve_output(); void resolve_inputline(); void resolve_space(); // the callback target is going to be one of these union callback_target { void * ptr; device_t * device; ioport_port * ioport; memory_bank * membank; output_manager::output_item *item; }; // configuration device_t & m_owner; // reference to our owning device std::reference_wrapper m_base; // device to resolve relative to callback_type m_type; // type of callback registered const char * m_target_tag; // tag of target object u64 m_target_int; // integer value of target object const char * m_space_tag; // tag of address space device int m_space_num; // address space number of space device // derived state address_space * m_space; // target address space callback_target m_target; // resolved pointer to target object int m_rshift; // right shift to apply to data read u64 m_mask; // mask to apply to data read const u64 m_defmask; // default mask u64 m_xor; // XOR to apply to data read }; // ======================> devcb_read_base class devcb_read_base : public devcb_base { protected: // construction/destruction devcb_read_base(device_t &device, u64 defmask, bool chained = false); template struct set_helper { static constexpr bool valid = false; }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_LINE; static void apply(devcb_read_base &devcb, read_line_delegate &&delegate) { devcb.m_readline = std::move(delegate); } }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_8; static void apply(devcb_read_base &devcb, read8_delegate &&delegate) { devcb.m_read8 = std::move(delegate); } }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_16; static void apply(devcb_read_base &devcb, read16_delegate &&delegate) { devcb.m_read16 = std::move(delegate); } }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_32; static void apply(devcb_read_base &devcb, read32_delegate &&delegate) { devcb.m_read32 = std::move(delegate); } }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_64; static void apply(devcb_read_base &devcb, read64_delegate &&delegate) { devcb.m_read64 = std::move(delegate); } }; public: // callback configuration using devcb_base::set_callback; template std::enable_if_t::valid, devcb_read_base &> set_callback(emu::detail::devcb_delegate_initialiser &&desc) { if (desc.m_base) reset(*desc.m_base, set_helper::type); else reset(set_helper::type); set_helper::apply(*this, std::move(desc.m_delegate)); return *this; } template auto &chain(Params &&... args) { return chain_alloc().set_callback(std::forward(args)...); } devcb_read_base &chain_alloc(); // resolution void resolve(); void resolve_safe(u64 none_constant_value); // validity checking void validity_check(validity_checker &valid) const; protected: // internal helpers virtual void devcb_reset() override; inline u64 read(address_space &space, offs_t offset, u64 mask); private: // adapters u64 read_unresolved_adapter(address_space &space, offs_t offset, u64 mask); u64 read_line_adapter(address_space &space, offs_t offset, u64 mask); u64 read8_adapter(address_space &space, offs_t offset, u64 mask); u64 read16_adapter(address_space &space, offs_t offset, u64 mask); u64 read32_adapter(address_space &space, offs_t offset, u64 mask); u64 read64_adapter(address_space &space, offs_t offset, u64 mask); u64 read_ioport_adapter(address_space &space, offs_t offset, u64 mask); u64 read_logged_adapter(address_space &space, offs_t offset, u64 mask); u64 read_constant_adapter(address_space &space, offs_t offset, u64 mask); // configuration read_line_delegate m_readline; // copy of registered line reader read8_delegate m_read8; // copy of registered 8-bit reader read16_delegate m_read16; // copy of registered 16-bit reader read32_delegate m_read32; // copy of registered 32-bit reader read64_delegate m_read64; // copy of registered 64-bit reader // derived state typedef u64 (devcb_read_base::*adapter_func)(address_space &, offs_t, u64); adapter_func m_adapter; // actual callback to invoke std::unique_ptr m_chain; // next callback for chained input }; // ======================> devcb_write_base class devcb_write_base : public devcb_base { protected: // construction/destruction devcb_write_base(device_t &device, u64 defmask, bool chained = false); template struct set_helper { static constexpr bool valid = false; }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_LINE; static void apply(devcb_write_base &devcb, write_line_delegate &&delegate) { devcb.m_writeline = std::move(delegate); } }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_8; static void apply(devcb_write_base &devcb, write8_delegate &&delegate) { devcb.m_write8 = std::move(delegate); } }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_16; static void apply(devcb_write_base &devcb, write16_delegate &&delegate) { devcb.m_write16 = std::move(delegate); } }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_32; static void apply(devcb_write_base &devcb, write32_delegate &&delegate) { devcb.m_write32 = std::move(delegate); } }; template struct set_helper { static constexpr bool valid = true; static constexpr callback_type type = CALLBACK_64; static void apply(devcb_write_base &devcb, write64_delegate &&delegate) { devcb.m_write64 = std::move(delegate); } }; public: // callback configuration using devcb_base::set_callback; template std::enable_if_t::valid, devcb_write_base &> set_callback(emu::detail::devcb_delegate_initialiser &&desc) { if (desc.m_base) reset(*desc.m_base, set_helper::type); else reset(set_helper::type); set_helper::apply(*this, std::move(desc.m_delegate)); return *this; } template devcb_write_base &set_callback(line_desc desc) { if (desc.m_base) reset(*desc.m_base, Type); else reset(Type); m_target_tag = desc.m_tag; m_target_int = desc.m_inputnum; return *this; } template auto &chain(Params &&... args) { return chain_alloc().set_callback(std::forward(args)...); } devcb_write_base &chain_alloc(); // resolution void resolve(); void resolve_safe(); // validity checking void validity_check(validity_checker &valid) const; protected: // internal helpers virtual void devcb_reset() override; inline void write(address_space &space, offs_t offset, u64 data, u64 mask); private: // adapters void write_unresolved_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_line_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write8_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write16_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write32_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write64_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_ioport_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_membank_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_output_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_logged_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_noop_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_inputline_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_assertline_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_clearline_adapter(address_space &space, offs_t offset, u64 data, u64 mask); void write_holdline_adapter(address_space &space, offs_t offset, u64 data, u64 mask); // configuration write_line_delegate m_writeline; // copy of registered line writer write8_delegate m_write8; // copy of registered 8-bit writer write16_delegate m_write16; // copy of registered 16-bit writer write32_delegate m_write32; // copy of registered 32-bit writer write64_delegate m_write64; // copy of registered 64-bit writer // derived state typedef void (devcb_write_base::*adapter_func)(address_space &, offs_t, u64, u64); adapter_func m_adapter; // actual callback to invoke std::unique_ptr m_chain; // next callback for chained output }; // ======================> devcb_read_line class devcb_read_line : public devcb_read_base { public: devcb_read_line(device_t &device) : devcb_read_base(device, 1) { } int operator()() { return read(*m_space, 0, 0xffU) & 1; } int operator()(address_space &space) { return read((m_space_tag != nullptr) ? *m_space : space, 0, 0xffU) & 1; } }; // ======================> devcb_read8 class devcb_read8 : public devcb_read_base { public: devcb_read8(device_t &device) : devcb_read_base(device, 0xff) { } u8 operator()(offs_t offset = 0, u8 mask = 0xff) { return read(*m_space, offset, mask) & mask; } u8 operator()(address_space &space, offs_t offset = 0, u8 mask = 0xff) { return read((m_space_tag != nullptr) ? *m_space : space, offset, mask) & mask; } }; // ======================> devcb_read16 class devcb_read16 : public devcb_read_base { public: devcb_read16(device_t &device) : devcb_read_base(device, 0xffff) { } u16 operator()(offs_t offset = 0, u16 mask = 0xffff) { return read(*m_space, offset, mask) & mask; } u16 operator()(address_space &space, offs_t offset = 0, u16 mask = 0xffff) { return read((m_space_tag != nullptr) ? *m_space : space, offset, mask) & mask; } }; // ======================> devcb_read32 class devcb_read32 : public devcb_read_base { public: devcb_read32(device_t &device) : devcb_read_base(device, 0xffffffff) { } u32 operator()(offs_t offset = 0, u32 mask = 0xffffffff) { return read(*m_space, offset, mask) & mask; } u32 operator()(address_space &space, offs_t offset = 0, u32 mask = 0xffffffff) { return read((m_space_tag != nullptr) ? *m_space : space, offset, mask) & mask; } }; // ======================> devcb_read64 class devcb_read64 : public devcb_read_base { public: devcb_read64(device_t &device) : devcb_read_base(device, 0xffffffffffffffffU) { } u64 operator()(offs_t offset = 0, u64 mask = 0xffffffffffffffffU) { return read(*m_space, offset, mask) & mask; } u64 operator()(address_space &space, offs_t offset = 0, u64 mask = 0xffffffffffffffffU) { return read((m_space_tag != nullptr) ? *m_space : space, offset, mask) & mask; } }; // ======================> devcb_write_line class devcb_write_line : public devcb_write_base { public: devcb_write_line(device_t &device) : devcb_write_base(device, 1) { } void operator()(int state) { write(*m_space, 0, state & 1, 0xffU); } void operator()(address_space &space, int state) { write((m_space_tag != nullptr) ? *m_space : space, 0, state & 1, 0xffU); } }; // ======================> devcb_write8 class devcb_write8 : public devcb_write_base { public: devcb_write8(device_t &device) : devcb_write_base(device, 0xff) { } void operator()(u8 data, u8 mask = 0xff) { write(*m_space, 0, data, mask); } void operator()(offs_t offset, u8 data, u8 mask = 0xff) { write(*m_space, offset, data, mask); } void operator()(address_space &space, offs_t offset, u8 data, u8 mask = 0xff) { write((m_space_tag != nullptr) ? *m_space : space, offset, data, mask); } }; // ======================> devcb_write16 class devcb_write16 : public devcb_write_base { public: devcb_write16(device_t &device) : devcb_write_base(device, 0xffff) { } void operator()(u16 data, u16 mask = 0xffff) { write(*m_space, 0, data, mask); } void operator()(offs_t offset, u16 data, u16 mask = 0xffff) { write(*m_space, offset, data, mask); } void operator()(address_space &space, offs_t offset, u16 data, u16 mask = 0xffff) { write((m_space_tag != nullptr) ? *m_space : space, offset, data, mask); } }; // ======================> devcb_write32 class devcb_write32 : public devcb_write_base { public: devcb_write32(device_t &device) : devcb_write_base(device, 0xffffffff) { } void operator()(u32 data, u32 mask = 0xffffffff) { write(*m_space, 0, data, mask); } void operator()(offs_t offset, u32 data, u32 mask = 0xffffffff) { write(*m_space, offset, data, mask); } void operator()(address_space &space, offs_t offset, u32 data, u32 mask = 0xffffffff) { write((m_space_tag != nullptr) ? *m_space : space, offset, data, mask); } }; // ======================> devcb_write64 class devcb_write64 : public devcb_write_base { public: devcb_write64(device_t &device) : devcb_write_base(device, 0xffffffffffffffffU) { } void operator()(u64 data, u64 mask = 0xffffffffffffffffU) { write(*m_space, 0, data, mask); } void operator()(offs_t offset, u64 data, u64 mask = 0xffffffffffffffffU) { write(*m_space, offset, data, mask); } void operator()(address_space &space, offs_t offset, u64 data, u64 mask = 0xffffffffffffffffU) { write((m_space_tag != nullptr) ? *m_space : space, offset, data, mask); } }; //************************************************************************** // INLINE FUNCTIONS //************************************************************************** //------------------------------------------------- // read - generic read callback dispatch //------------------------------------------------- inline u64 devcb_read_base::read(address_space &space, offs_t offset, u64 mask) { u64 result = (this->*m_adapter)(space, offset, mask); if (m_chain != nullptr) result |= m_chain->read(space, offset, mask); return result; } //------------------------------------------------- // write - generic write callback dispatch //------------------------------------------------- inline void devcb_write_base::write(address_space &space, offs_t offset, u64 data, u64 mask) { (this->*m_adapter)(space, offset, data, mask); if (m_chain != nullptr) m_chain->write(space, offset, data, mask); } //------------------------------------------------- // wrappers for ioports, constants, and loggers //------------------------------------------------- #define DEVCB_NOOP devcb_base::null_desc{ } template inline devcb_base::ioport_desc DEVCB_IOPORT(Params &&... args) { return emu::detail::devcb_tag_desc_creator::create(std::forward(args)...); } template inline devcb_base::membank_desc DEVCB_MEMBANK(Params &&... args) { return emu::detail::devcb_tag_desc_creator::create(std::forward(args)...); } template inline devcb_base::output_desc DEVCB_OUTPUT(Params &&... args) { return emu::detail::devcb_tag_desc_creator::create(std::forward(args)...); } inline devcb_base::constant_desc DEVCB_CONSTANT(u64 value) { return devcb_base::constant_desc{ value }; } inline devcb_base::logger_desc DEVCB_LOGGER(char const *string) { return devcb_base::logger_desc{ string }; } template inline devcb_base::inputline_desc DEVCB_INPUTLINE(Params &&... args) { return emu::detail::devcb_line_desc_creator::create(std::forward(args)...); } template inline devcb_base::assertline_desc DEVCB_ASSERTLINE(Params &&... args) { return emu::detail::devcb_line_desc_creator::create(std::forward(args)...); } template inline devcb_base::clearline_desc DEVCB_CLEARLINE(Params &&... args) { return emu::detail::devcb_line_desc_creator::create(std::forward(args)...); } template inline devcb_base::holdline_desc DEVCB_HOLDLINE(Params &&... args) { return emu::detail::devcb_line_desc_creator::create(std::forward(args)...); } #define DEVCB_VCC DEVCB_CONSTANT(1) #define DEVCB_GND DEVCB_CONSTANT(0) #endif // MAME_EMU_DEVCB_H