// license:BSD-3-Clause // copyright-holders:Vas Crabb /*************************************************************************** devcb.h Device callback interface helpers. ***************************************************************************/ #ifndef MAME_EMU_DEVCB_H #define MAME_EMU_DEVCB_H #pragma once #include #include #include #include #include #include #include #include //************************************************************************** // DETECT PROBLEMATIC COMPILERS //************************************************************************** #if defined(__GNUC__) && !defined(__clang__) #if __GNUC__ >= 8 #define MAME_DEVCB_GNUC_BROKEN_FRIEND 1 #endif // __GNUC__ >= 8 #endif // defined(__GNUC__) && !defined(__clang__) //************************************************************************** // DELEGATE TYPES //************************************************************************** typedef device_delegate read_line_delegate; typedef device_delegate write_line_delegate; namespace emu { namespace detail { template struct read_line_device_class { }; template struct write_line_device_class { }; template inline read_line_delegate make_delegate(T &&func, char const *name, char const *tag, rw_device_class_t > *obj) { return read_line_delegate(func, name, tag, obj); } template inline write_line_delegate make_delegate(T &&func, char const *name, char const *tag, rw_device_class_t > *obj) { return write_line_delegate(func, name, tag, obj); } } } // namespace emu::detail //************************************************************************** // TYPE DEFINITIONS //************************************************************************** /// \brief Base callback helper /// /// Provides utilities for supporting multiple read/write/transform /// signatures, and the base exclusive-or/mask transform methods. class devcb_base { public: template void set_space(T &&tag, int index) { m_dflt_space_finder.set_tag(std::forward(tag)); m_dflt_space_index = index; } virtual void validity_check(validity_checker &valid) const; protected: // This is in C++17 but not C++14 template struct void_wrapper { using type = void; }; template using void_t = typename void_wrapper::type; // Intermediate is larger of input and output, mask is forced to unsigned template struct intermediate; template struct intermediate= sizeof(U)> > { using type = T; }; template struct intermediate > { using type = U; }; template using intermediate_t = typename intermediate::type; template using mask_t = std::make_unsigned_t >; // Detecting candidates for transform functions template struct is_transform_form1 { static constexpr bool value = false; }; template struct is_transform_form2 { static constexpr bool value = false; }; template struct is_transform_form3 { static constexpr bool value = false; }; template struct is_transform_form4 { static constexpr bool value = false; }; template struct is_transform_form5 { static constexpr bool value = false; }; template struct is_transform_form6 { static constexpr bool value = false; }; template struct is_transform_form1 &)>, Result>::value> > { static constexpr bool value = true; }; template struct is_transform_form2, Result>::value> > { static constexpr bool value = true; }; template struct is_transform_form3 &)>, Result>::value> > { static constexpr bool value = true; }; template struct is_transform_form4, Result>::value> > { static constexpr bool value = true; }; template struct is_transform_form5, Result>::value> > { static constexpr bool value = true; }; template struct is_transform_form6, Result>::value> > { static constexpr bool value = true; }; template struct is_transform { static constexpr bool value = is_transform_form1::value || is_transform_form2::value || is_transform_form3::value || is_transform_form4::value || is_transform_form5::value || is_transform_form6::value; }; // Determining the result type of a transform function template struct transform_result; template struct transform_result::value> > { using type = std::result_of_t &)>; }; template struct transform_result::value> > { using type = std::result_of_t; }; template struct transform_result::value> > { using type = std::result_of_t &)>; }; template struct transform_result::value> > { using type = std::result_of_t; }; template struct transform_result::value> > { using type = std::result_of_t; }; template struct transform_result::value> > { using type = std::result_of_t; }; template using transform_result_t = typename transform_result::type; // Mapping method types to delegate types template struct delegate_type; template struct delegate_type > > > { using type = read8_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read16_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read32_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read64_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read8s_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read16s_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read32s_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read64s_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read8sm_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read16sm_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read32sm_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read64sm_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read8smo_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read16smo_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read32smo_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read64smo_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = read_line_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write8_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write16_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write32_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write64_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write8s_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write16s_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write32s_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write64s_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write8sm_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write16sm_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write32sm_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write64sm_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write8smo_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write16smo_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write32smo_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write64smo_delegate; using device_class = emu::detail::rw_device_class_t >; }; template struct delegate_type > > > { using type = write_line_delegate; using device_class = emu::detail::rw_device_class_t >; }; template using delegate_type_t = typename delegate_type::type; template using delegate_device_class_t = typename delegate_type::device_class; // Invoking transform callbacks template static std::enable_if_t::value, mask_t, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t &mem_mask) { return std::make_unsigned_t >(cb(space, offset, data, mem_mask)); } template static std::enable_if_t::value, mask_t, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t &mem_mask) { return std::make_unsigned_t >(cb(space, data, offset)); } template static std::enable_if_t::value, mask_t, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t &mem_mask) { return std::make_unsigned_t >(cb(offset, data, mem_mask)); } template static std::enable_if_t::value, mask_t, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t &mem_mask) { return std::make_unsigned_t >(cb(offset, data)); } template static std::enable_if_t::value, mask_t, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t &mem_mask) { return std::make_unsigned_t >(cb(space, data)); } template static std::enable_if_t::value, mask_t, Result> > invoke_transform(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t &mem_mask) { return std::make_unsigned_t >(cb(data)); } // Working with devices and interfaces template static std::enable_if_t::value, const char *> get_tag(T &obj) { return obj.tag(); } template static std::enable_if_t::value, const char *> get_tag(T &obj) { return obj.device().tag(); } template static std::enable_if_t, std::add_pointer_t >::value, T &> cast_reference(U &obj) { return downcast(obj); } template static std::enable_if_t, std::add_pointer_t >::value, T &> cast_reference(U &obj) { return dynamic_cast(obj); } /// \brief Base transform helper /// /// Provides member functions for setting exclusive-or, mask and /// shifts. Exclusive-or and mask values are stored; it's assumed /// that the implementation supports lamba transforms to allow /// shifts. template class transform_base { public: Impl &exor(std::make_unsigned_t val) { m_exor ^= val; return static_cast(*this); } Impl &mask(std::make_unsigned_t val) { m_mask = m_inherited_mask ? val : (m_mask & val); m_inherited_mask = false; return static_cast(*this); } Impl &invert() { return exor(~std::make_unsigned_t(0)); } auto rshift(unsigned val) { auto trans(static_cast(*this).transform([val] (offs_t offset, T data, std::make_unsigned_t &mem_mask) { mem_mask >>= val; return data >> val; })); return inherited_mask() ? std::move(trans) : std::move(trans.mask(m_mask >> val)); } auto lshift(unsigned val) { auto trans(static_cast(*this).transform([val] (offs_t offset, T data, std::make_unsigned_t &mem_mask) { mem_mask <<= val; return data << val; })); return inherited_mask() ? std::move(trans) : std::move(trans.mask(m_mask << val)); } auto bit(unsigned val) { return std::move(rshift(val).mask(T(1U))); } constexpr std::make_unsigned_t exor() const { return m_exor & m_mask; } constexpr std::make_unsigned_t mask() const { return m_mask; } protected: constexpr bool need_exor() const { return std::make_unsigned_t(0) != (m_exor & m_mask); } constexpr bool need_mask() const { return std::make_unsigned_t(~std::make_unsigned_t(0)) != m_mask; } constexpr bool inherited_mask() const { return m_inherited_mask; } constexpr transform_base(std::make_unsigned_t mask) : m_mask(mask) { } constexpr transform_base(transform_base const &) = default; transform_base(transform_base &&) = default; transform_base &operator=(transform_base const &) = default; transform_base &operator=(transform_base &&) = default; private: std::make_unsigned_t m_exor = std::make_unsigned_t(0); std::make_unsigned_t m_mask; bool m_inherited_mask = true; }; devcb_base(device_t &owner); ~devcb_base(); device_t &owner() const { return m_owner; } void resolve(); address_space &default_space() { assert(m_dflt_space); return *m_dflt_space; } private: device_t &m_owner; optional_device m_dflt_space_finder; int m_dflt_space_index; address_space *m_dflt_space; }; /// \brief Read callback utilities /// /// Helpers that don't need to be templated on callback type. class devcb_read_base : public devcb_base { protected: // Detecting candidates for read functions template struct is_read_form1 { static constexpr bool value = false; }; template struct is_read_form2 { static constexpr bool value = false; }; template struct is_read_form3 { static constexpr bool value = false; }; template struct is_read_form4 { static constexpr bool value = false; }; template struct is_read_form5 { static constexpr bool value = false; }; template struct is_read_form6 { static constexpr bool value = false; }; template struct is_read_form1, Result>::value> > { static constexpr bool value = true; }; template struct is_read_form2, Result>::value> > { static constexpr bool value = true; }; template struct is_read_form3, Result>::value> > { static constexpr bool value = true; }; template struct is_read_form4, Result>::value> > { static constexpr bool value = true; }; template struct is_read_form5, Result>::value> > { static constexpr bool value = true; }; template struct is_read_form6, Result>::value> > { static constexpr bool value = true; }; template struct is_read { static constexpr bool value = is_read_form1::value || is_read_form2::value || is_read_form3::value || is_read_form4::value || is_read_form5::value || is_read_form6::value; }; // Determining the result type of a read function template struct read_result; template struct read_result::value> > { using type = std::result_of_t)>; }; template struct read_result::value> > { using type = std::result_of_t; }; template struct read_result::value> > { using type = std::result_of_t)>; }; template struct read_result::value> > { using type = std::result_of_t; }; template struct read_result::value> > { using type = std::result_of_t; }; template struct read_result::value> > { using type = std::result_of_t; }; template using read_result_t = typename read_result::type; // Detecting candidates for read delegates template struct is_read_method { static constexpr bool value = false; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; template struct is_read_method > > > { static constexpr bool value = true; }; // Invoking read callbacks template static std::enable_if_t::value, mask_t, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t mem_mask) { return std::make_unsigned_t >(cb(space, offset, mem_mask)); } template static std::enable_if_t::value, mask_t, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t mem_mask) { return std::make_unsigned_t >(cb(space, offset)); } template static std::enable_if_t::value, mask_t, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t mem_mask) { return std::make_unsigned_t >(cb(offset, mem_mask)); } template static std::enable_if_t::value, mask_t, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t mem_mask) { return std::make_unsigned_t >(cb(offset)); } template static std::enable_if_t::value, mask_t, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t mem_mask) { return std::make_unsigned_t >(cb(space)); } template static std::enable_if_t::value, mask_t, Result> > invoke_read(T const &cb, address_space &space, offs_t offset, std::make_unsigned_t mem_mask) { return std::make_unsigned_t >(cb()); } // Delegate characteristics template struct delegate_traits; template struct delegate_traits { static constexpr u8 default_mask = ~u8(0); }; template struct delegate_traits { static constexpr u16 default_mask = ~u16(0); }; template struct delegate_traits { static constexpr u32 default_mask = ~u32(0); }; template struct delegate_traits { static constexpr u64 default_mask = ~u64(0); }; template struct delegate_traits { static constexpr u8 default_mask = ~u8(0); }; template struct delegate_traits { static constexpr u16 default_mask = ~u16(0); }; template struct delegate_traits { static constexpr u32 default_mask = ~u32(0); }; template struct delegate_traits { static constexpr u64 default_mask = ~u64(0); }; template struct delegate_traits { static constexpr u8 default_mask = ~u8(0); }; template struct delegate_traits { static constexpr u16 default_mask = ~u16(0); }; template struct delegate_traits { static constexpr u32 default_mask = ~u32(0); }; template struct delegate_traits { static constexpr u64 default_mask = ~u64(0); }; template struct delegate_traits { static constexpr u8 default_mask = ~u8(0); }; template struct delegate_traits { static constexpr u16 default_mask = ~u16(0); }; template struct delegate_traits { static constexpr u32 default_mask = ~u32(0); }; template struct delegate_traits { static constexpr u64 default_mask = ~u64(0); }; template struct delegate_traits { static constexpr unsigned default_mask = 1U; }; using devcb_base::devcb_base; ~devcb_read_base(); }; /// \brief Write callback utilities /// /// Helpers that don't need to be templated on callback type. class devcb_write_base : public devcb_base { protected: // Detecting candidates for write functions template struct is_write_form1 { static constexpr bool value = false; }; template struct is_write_form2 { static constexpr bool value = false; }; template struct is_write_form3 { static constexpr bool value = false; }; template struct is_write_form4 { static constexpr bool value = false; }; template struct is_write_form5 { static constexpr bool value = false; }; template struct is_write_form6 { static constexpr bool value = false; }; template struct is_write_form1)> > > { static constexpr bool value = true; }; template struct is_write_form2 > > { static constexpr bool value = true; }; template struct is_write_form3)> > > { static constexpr bool value = true; }; template struct is_write_form4 > > { static constexpr bool value = true; }; template struct is_write_form5 > > { static constexpr bool value = true; }; template struct is_write_form6 > > { static constexpr bool value = true; }; template struct is_write { static constexpr bool value = is_write_form1::value || is_write_form2::value || is_write_form3::value || is_write_form4::value || is_write_form5::value || is_write_form6::value; }; // Detecting candidates for write delegates template struct is_write_method { static constexpr bool value = false; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; template struct is_write_method > > > { static constexpr bool value = true; }; // Invoking write callbacks template static std::enable_if_t::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t mem_mask) { return cb(space, offset, data, mem_mask); } template static std::enable_if_t::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t mem_mask) { return cb(space, offset, data); } template static std::enable_if_t::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t mem_mask) { return cb(offset, data, mem_mask); } template static std::enable_if_t::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t mem_mask) { return cb(offset, data); } template static std::enable_if_t::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t mem_mask) { return cb(space, data); } template static std::enable_if_t::value> invoke_write(T const &cb, address_space &space, offs_t &offset, Input data, std::make_unsigned_t mem_mask) { return cb(data); } // Delegate characteristics template struct delegate_traits; template struct delegate_traits { using input_t = u8; static constexpr u8 default_mask = ~u8(0); }; template struct delegate_traits { using input_t = u16; static constexpr u16 default_mask = ~u16(0); }; template struct delegate_traits { using input_t = u32; static constexpr u32 default_mask = ~u32(0); }; template struct delegate_traits { using input_t = u64; static constexpr u64 default_mask = ~u64(0); }; template struct delegate_traits { using input_t = u8; static constexpr u8 default_mask = ~u8(0); }; template struct delegate_traits { using input_t = u16; static constexpr u16 default_mask = ~u16(0); }; template struct delegate_traits { using input_t = u32; static constexpr u32 default_mask = ~u32(0); }; template struct delegate_traits { using input_t = u64; static constexpr u64 default_mask = ~u64(0); }; template struct delegate_traits { using input_t = u8; static constexpr u8 default_mask = ~u8(0); }; template struct delegate_traits { using input_t = u16; static constexpr u16 default_mask = ~u16(0); }; template struct delegate_traits { using input_t = u32; static constexpr u32 default_mask = ~u32(0); }; template struct delegate_traits { using input_t = u64; static constexpr u64 default_mask = ~u64(0); }; template struct delegate_traits { using input_t = u8; static constexpr u8 default_mask = ~u8(0); }; template struct delegate_traits { using input_t = u16; static constexpr u16 default_mask = ~u16(0); }; template struct delegate_traits { using input_t = u32; static constexpr u32 default_mask = ~u32(0); }; template struct delegate_traits { using input_t = u64; static constexpr u64 default_mask = ~u64(0); }; template struct delegate_traits { using input_t = int; static constexpr unsigned default_mask = 1U; }; using devcb_base::devcb_base; ~devcb_write_base(); }; /// \brief Read callback helper /// /// Allows binding a variety of signatures, composing a result from /// multiple callbacks, and chained arbitrary transforms. Transforms /// may modify the offset and mask. template DefaultMask = std::make_unsigned_t(~std::make_unsigned_t(0))> class devcb_read : public devcb_read_base { private: using func_t = std::function)>; class creator { public: using ptr = std::unique_ptr; virtual ~creator() { } virtual void validity_check(validity_checker &valid) const = 0; virtual func_t create() = 0; std::make_unsigned_t mask() const { return m_mask; } protected: creator(std::make_unsigned_t mask) : m_mask(mask) { } std::make_unsigned_t m_mask; }; template class creator_impl : public creator { public: creator_impl(T &&builder) : creator(builder.mask()), m_builder(std::move(builder)) { } virtual void validity_check(validity_checker &valid) const override { m_builder.validity_check(valid); } virtual func_t create() override { func_t result; m_builder.build([&result] (auto &&f) { result = [cb = std::move(f)] (address_space &space, offs_t offset, typename T::input_mask_t mem_mask) { return cb(space, offset, mem_mask); }; }); return result; } private: T m_builder; }; class log_creator : public creator { public: log_creator(device_t &devbase, std::string &&message) : creator(0U), m_devbase(devbase), m_message(std::move(message)) { } virtual void validity_check(validity_checker &valid) const override { } virtual func_t create() override { return [&devbase = m_devbase, message = std::move(m_message)] (address_space &space, offs_t offset, std::make_unsigned_t mem_mask) { devbase.logerror("%s: %s\n", devbase.machine().describe_context(), message); return Result(0); }; } private: device_t &m_devbase; std::string m_message; }; template class transform_builder; // workaround for MSVC class builder_base { protected: template friend class transform_builder; // workaround for MSVC builder_base(devcb_read &target, bool append) : m_target(target), m_append(append) { } builder_base(builder_base const &) = delete; builder_base(builder_base &&) = default; ~builder_base() { assert(m_consumed); } builder_base &operator=(builder_base const &) = delete; builder_base &operator=(builder_base &&) = default; #ifdef MAME_DEVCB_GNUC_BROKEN_FRIEND public: #endif void consume() { m_consumed = true; } #ifdef MAME_DEVCB_GNUC_BROKEN_FRIEND protected: #endif void built() { assert(!m_built); m_built = true; } template void register_creator() { if (!m_consumed) { if (!m_append) m_target.m_creators.clear(); consume(); m_target.m_creators.emplace_back(std::make_unique >(std::move(static_cast(*this)))); } } devcb_read &m_target; bool const m_append; bool m_consumed = false; bool m_built = false; }; template class transform_builder : public builder_base, public transform_base, Result>, transform_builder > { public: template friend class transform_builder; using input_t = typename Source::output_t; using output_t = mask_t, Result>; using input_mask_t = mask_t; template transform_builder(devcb_read &target, bool append, Source &&src, T &&cb, output_t mask) : builder_base(target, append) , transform_base(mask) , m_src(std::move(src)) , m_cb(std::forward(cb)) { m_src.consume(); } transform_builder(transform_builder &&that) : builder_base(std::move(that)) , transform_base(std::move(that)) , m_src(std::move(that.m_src)) , m_cb(std::move(that.m_cb)) { m_src.consume(); that.consume(); that.built(); } ~transform_builder() { this->template register_creator(); } template std::enable_if_t::value, transform_builder > > transform(T &&cb) { output_t const m(this->mask()); if (this->inherited_mask()) this->mask(std::make_unsigned_t >(~std::make_unsigned_t >(0))); return transform_builder >(this->m_target, this->m_append, std::move(*this), std::forward(cb), m); } void validity_check(validity_checker &valid) const { m_src.validity_check(valid); } template void build(T &&chain) { assert(this->m_consumed); auto wrap([this, c = std::forward(chain)] (auto &&f) mutable { this->build(std::move(c), std::move(f)); }); m_src.build(std::move(wrap)); } private: transform_builder(transform_builder const &) = delete; transform_builder &operator=(transform_builder const &) = delete; transform_builder &operator=(transform_builder &&that) = delete; template void build(T &&chain, U &&f) { assert(this->m_consumed); this->built(); chain( [src = std::forward(f), cb = std::move(this->m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t &offset, input_mask_t &mem_mask) { typename Source::input_mask_t source_mask(mem_mask); auto const data(src(space, offset, source_mask)); mem_mask = source_mask & mask; return (devcb_read::invoke_transform(cb, space, offset, data, mem_mask) ^ exor) & mask; }); } Source m_src; Func m_cb; }; template class functoid_builder : public builder_base, public transform_base, Result>, functoid_builder > { public: template friend class transform_builder; using output_t = mask_t, Result>; using input_mask_t = std::make_unsigned_t; template functoid_builder(devcb_read &target, bool append, T &&cb) : builder_base(target, append) , transform_base(DefaultMask) , m_cb(std::forward(cb)) { } functoid_builder(functoid_builder &&that) : builder_base(std::move(that)) , transform_base(std::move(that)) , m_cb(std::move(that.m_cb)) { that.consume(); that.built(); } ~functoid_builder() { this->template register_creator(); } template std::enable_if_t::value, transform_builder > > transform(T &&cb) { output_t const m(this->mask()); if (this->inherited_mask()) this->mask(std::make_unsigned_t >(~std::make_unsigned_t >(0))); return transform_builder >(this->m_target, this->m_append, std::move(*this), std::forward(cb), m); } void validity_check(validity_checker &valid) const { } template void build(T &&chain) { assert(this->m_consumed); this->built(); chain( [cb = std::move(this->m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_mask_t mem_mask) { return (devcb_read::invoke_read(cb, space, offset, mem_mask & mask) ^ exor) & mask; }); } private: functoid_builder(functoid_builder const &) = delete; functoid_builder &operator=(functoid_builder const &) = delete; functoid_builder &operator=(functoid_builder &&that) = delete; Func m_cb; }; template class delegate_builder : public builder_base, public transform_base, Result>, delegate_builder > { public: template friend class transform_builder; using output_t = mask_t, Result>; using input_mask_t = std::make_unsigned_t; template delegate_builder(devcb_read &target, bool append, device_t &devbase, char const *tag, T &&func, char const *name) : builder_base(target, append) , transform_base(DefaultMask & delegate_traits::default_mask) , m_devbase(devbase) , m_delegate(std::forward(func), name, tag, std::add_pointer_t >(nullptr)) { } template delegate_builder(devcb_read &target, bool append, device_t &devbase, devcb_read::delegate_device_class_t &obj, T &&func, char const *name) : builder_base(target, append) , transform_base(DefaultMask & delegate_traits::default_mask) , m_devbase(devbase) , m_delegate(std::forward(func), name, devcb_read::get_tag(obj), &obj) { } delegate_builder(delegate_builder &&that) : builder_base(std::move(that)) , transform_base(std::move(that)) , m_devbase(that.m_devbase) , m_delegate(std::move(that.m_delegate)) { that.consume(); that.built(); } ~delegate_builder() { this->template register_creator(); } template std::enable_if_t::value, transform_builder > > transform(T &&cb) { output_t const m(this->mask()); if (this->inherited_mask()) this->mask(delegate_traits::default_mask); return transform_builder >(this->m_target, this->m_append, std::move(*this), std::forward(cb), m); } void validity_check(validity_checker &valid) const { if (!m_devbase.subdevice(m_delegate.device_name())) osd_printf_error("Read callback bound to non-existent object tag %s (%s)\n", m_delegate.device_name(), m_delegate.name()); } template void build(T &&chain) { assert(this->m_consumed); this->built(); m_delegate.bind_relative_to(m_devbase); chain( [cb = std::move(this->m_delegate), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_mask_t mem_mask) { return (devcb_read::invoke_read(cb, space, offset, mem_mask & mask) ^ exor) & mask; }); } private: delegate_builder(delegate_builder const &) = delete; delegate_builder &operator=(delegate_builder const &) = delete; delegate_builder &operator=(delegate_builder &&that) = delete; device_t &m_devbase; Delegate m_delegate; }; class ioport_builder : public builder_base, public transform_base, ioport_builder> { public: template friend class transform_builder; using output_t = mask_t; using input_mask_t = std::make_unsigned_t; ioport_builder(devcb_read &target, bool append, device_t &devbase, std::string &&tag) : builder_base(target, append) , transform_base(DefaultMask) , m_devbase(devbase) , m_tag(std::move(tag)) { } ioport_builder(ioport_builder &&that) : builder_base(std::move(that)) , transform_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } ~ioport_builder() { this->template register_creator(); } template std::enable_if_t::value, transform_builder > > transform(T &&cb) { output_t const m(this->mask()); if (this->inherited_mask()) this->mask(std::make_unsigned_t(~std::make_unsigned_t(0))); return transform_builder >(this->m_target, this->m_append, std::move(*this), std::forward(cb), m); } void validity_check(validity_checker &valid) const { } template void build(T &&chain) { assert(this->m_consumed); this->built(); ioport_port *const ioport(m_devbase.ioport(m_tag.c_str())); if (!ioport) throw emu_fatalerror("Read callback bound to non-existent I/O port %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name()); chain( [&port = *ioport, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_mask_t mem_mask) { return (port.read() ^ exor) & mask; }); } private: ioport_builder(ioport_builder const &) = delete; ioport_builder &operator=(ioport_builder const &) = delete; ioport_builder &operator=(ioport_builder &&that) = delete; device_t &m_devbase; std::string m_tag; }; class binder { public: binder(devcb_read &target) : m_target(target) { } binder(binder const &) = delete; binder(binder &&that) : m_target(that.m_target), m_append(that.m_append), m_used(that.m_used) { that.m_used = true; } binder &operator=(binder const &) = delete; binder &operator=(binder &&) = delete; template std::enable_if_t::value, functoid_builder > > set(T &&cb) { set_used(); return functoid_builder >(m_target, m_append, std::forward(cb)); } template std::enable_if_t::value, delegate_builder > > set(T &&func, char const *name) { set_used(); return delegate_builder >(m_target, m_append, m_target.owner().mconfig().current_device(), DEVICE_SELF, std::forward(func), name); } template std::enable_if_t::value, delegate_builder > > set(char const *tag, T &&func, char const *name) { set_used(); return delegate_builder >(m_target, m_append, m_target.owner().mconfig().current_device(), tag, std::forward(func), name); } template std::enable_if_t::value, delegate_builder > > set(U &obj, T &&func, char const *name) { set_used(); return delegate_builder >(m_target, m_append, m_target.owner(), devcb_read::cast_reference >(obj), std::forward(func), name); } template std::enable_if_t::value, delegate_builder > > set(device_finder &finder, T &&func, char const *name) { set_used(); std::pair const target(finder.finder_target()); return delegate_builder >(m_target, m_append, target.first, target.second, std::forward(func), name); } template std::enable_if_t::value, delegate_builder > > set(device_finder const &finder, T &&func, char const *name) { set_used(); std::pair const target(finder.finder_target()); return delegate_builder >(m_target, m_append, target.first, target.second, std::forward(func), name); } template auto append(Params &&... args) { m_append = true; return set(std::forward(args)...); } template ioport_builder set_ioport(Params &&... args) { set_used(); return ioport_builder(m_target, m_append, m_target.owner().mconfig().current_device(), std::string(std::forward(args)...)); } template ioport_builder set_ioport(ioport_finder &finder) { set_used(); std::pair const target(finder.finder_target()); return ioport_builder(m_target, m_append, target.first, std::string(target.second)); } template ioport_builder set_ioport(ioport_finder const &finder) { set_used(); std::pair const target(finder.finder_target()); return ioport_builder(m_target, m_append, target.first, std::string(target.second)); } template ioport_builder append_ioport(Params &&... args) { m_append = true; return set_ioport(std::forward(args)...); } template void set_log(device_t &devbase, Params &&... args) { set_used(); if (!m_append) m_target.m_creators.clear(); m_target.m_creators.emplace_back(std::make_unique(devbase, std::string(std::forward(args)...))); } template std::enable_if_t >::value> set_log(T &devbase, Params &&... args) { set_log(static_cast(devbase), std::forward(args)...); } template std::enable_if_t >::value> set_log(T &devbase, Params &&... args) { set_log(devbase.device(), std::forward(args)...); } template void set_log(Params &&... args) { set_log(m_target.owner().mconfig().current_device(), std::forward(args)...); } template void append_log(Params &&... args) { m_append = true; set_log(std::forward(args)...); } auto set_constant(Result val) { return set([val] () { return val; }); } auto append_constant(Result val) { return append([val] () { return val; }); } private: void set_used() { assert(!m_used); m_used = true; } devcb_read &m_target; bool m_append = false; bool m_used = false; }; std::vector m_functions; std::vector m_creators; public: devcb_read(device_t &owner); binder bind(); void reset(); virtual void validity_check(validity_checker &valid) const override; void resolve(); void resolve_safe(Result dflt); Result operator()(address_space &space, offs_t offset = 0, std::make_unsigned_t mem_mask = DefaultMask); Result operator()(offs_t offset, std::make_unsigned_t mem_mask = DefaultMask); Result operator()(); bool isnull() const { return m_functions.empty() && m_creators.empty(); } explicit operator bool() const { return !m_functions.empty(); } }; template DefaultMask> devcb_read::devcb_read(device_t &owner) : devcb_read_base(owner) { } template DefaultMask> typename devcb_read::binder devcb_read::bind() { return binder(*this); } template DefaultMask> void devcb_read::reset() { assert(m_functions.empty()); m_creators.clear(); } template DefaultMask> void devcb_read::validity_check(validity_checker &valid) const { assert(m_functions.empty()); devcb_read_base::validity_check(valid); for (typename std::vector::const_iterator i = m_creators.begin(); m_creators.end() != i; ++i) { (*i)->validity_check(valid); std::make_unsigned_t const m((*i)->mask()); for (typename std::vector::const_iterator j = std::next(i); m_creators.end() != j; ++j) { std::make_unsigned_t const n((*j)->mask()); if (m & n) osd_printf_error("Read callback masks %lX and %lX overlap\n", static_cast(m), static_cast(n)); // FIXME: doesn't work with u64 } } } template DefaultMask> void devcb_read::resolve() { assert(m_functions.empty()); devcb_read_base::resolve(); m_functions.reserve(m_creators.size()); for (typename creator::ptr const &c : m_creators) m_functions.emplace_back(c->create()); m_creators.clear(); } template DefaultMask> void devcb_read::resolve_safe(Result dflt) { resolve(); if (m_functions.empty()) m_functions.emplace_back([dflt] (address_space &space, offs_t offset, std::make_unsigned_t mem_mask) { return dflt; }); } template DefaultMask> Result devcb_read::operator()(address_space &space, offs_t offset, std::make_unsigned_t mem_mask) { assert(m_creators.empty() && !m_functions.empty()); typename std::vector::const_iterator it(m_functions.begin()); std::make_unsigned_t result((*it)(space, offset, mem_mask)); while (m_functions.end() != ++it) result |= (*it)(space, offset, mem_mask); return result; } template DefaultMask> Result devcb_read::operator()(offs_t offset, std::make_unsigned_t mem_mask) { return this->operator()(this->default_space(), offset, mem_mask); } template DefaultMask> Result devcb_read::operator()() { return this->operator()(this->default_space(), 0U, DefaultMask); } /// \brief Write callback helper /// /// Allows binding a variety of signatures, sending the value to /// multiple callbacks, and chained arbitrary transforms. Transforms /// may modify the offset and mask. template DefaultMask = std::make_unsigned_t(~std::make_unsigned_t(0))> class devcb_write : public devcb_write_base { private: using func_t = std::function)>; class creator { public: using ptr = std::unique_ptr; virtual ~creator() { } virtual void validity_check(validity_checker &valid) const = 0; virtual func_t create() = 0; }; template class creator_impl : public creator { public: creator_impl(T &&builder) : m_builder(std::move(builder)) { } virtual void validity_check(validity_checker &valid) const override { m_builder.validity_check(valid); } virtual func_t create() override { return [cb = m_builder.build()] (address_space &space, offs_t offset, Input data, std::make_unsigned_t mem_mask) { cb(space, offset, data, mem_mask); }; } private: T m_builder; }; class nop_creator : public creator { public: virtual void validity_check(validity_checker &valid) const override { } virtual func_t create() override { return [] (address_space &space, offs_t offset, Input data, std::make_unsigned_t mem_mask) { }; } }; template class transform_builder; // workaround for MSVC template class first_transform_builder; // workaround for MSVC template class functoid_builder; // workaround for MSVC class builder_base { protected: template friend class transform_builder; // workaround for MSVC template friend class first_transform_builder; // workaround for MSVC template friend class functoid_builder; // workaround for MSVC builder_base(devcb_write &target, bool append) : m_target(target), m_append(append) { } builder_base(builder_base const &) = delete; builder_base(builder_base &&) = default; ~builder_base() { assert(m_consumed); } builder_base &operator=(builder_base const &) = delete; builder_base &operator=(builder_base &&) = default; #ifdef MAME_DEVCB_GNUC_BROKEN_FRIEND public: #endif void consume() { m_consumed = true; } #ifdef MAME_DEVCB_GNUC_BROKEN_FRIEND protected: #endif void built() { assert(!m_built); m_built = true; } template void register_creator() { if (!m_consumed) { if (!m_append) m_target.m_creators.clear(); consume(); m_target.m_creators.emplace_back(std::make_unique >(std::move(static_cast(*this)))); } } devcb_write &m_target; bool const m_append; bool m_consumed = false; bool m_built = false; }; template class transform_builder : public builder_base, public transform_base, typename Source::output_t>, transform_builder > { public: template friend class transform_builder; using input_t = typename Source::output_t; using output_t = mask_t, typename Source::output_t>; template transform_builder(devcb_write &target, bool append, Source &&src, T &&cb, output_t mask) : builder_base(target, append) , transform_base(mask) , m_src(std::move(src)) , m_cb(std::forward(cb)) { m_src.consume(); } transform_builder(transform_builder &&that) : builder_base(std::move(that)) , transform_base(std::move(that)) , m_src(std::move(that.m_src)) , m_cb(std::move(that.m_cb)) { m_src.consume(); that.consume(); that.built(); } ~transform_builder() { this->template register_creator(); } template std::enable_if_t::value, transform_builder > > transform(T &&cb) { output_t const m(this->mask()); if (this->inherited_mask()) this->mask(output_t(~output_t(0))); return transform_builder >(this->m_target, this->m_append, std::move(*this), std::forward(cb), m); } auto build() { assert(this->m_consumed); this->built(); return m_src.build( [cb = std::move(m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t &offset, input_t data, std::make_unsigned_t &mem_mask) { auto const trans(devcb_write::invoke_transform(cb, space, offset, data, mem_mask)); mem_mask &= mask; return (trans ^ exor) & mask; }); } void validity_check(validity_checker &valid) const { m_src.validity_check(valid); } private: transform_builder(transform_builder const &) = delete; transform_builder &operator=(transform_builder const &) = delete; transform_builder &operator=(transform_builder &&that) = delete; template auto build(T &&chain) { assert(this->m_consumed); this->built(); return m_src.build( [f = std::move(chain), cb = std::move(this->m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t &offset, input_t data, std::make_unsigned_t &mem_mask) { auto const trans(devcb_write::invoke_transform(cb, space, offset, data, mem_mask)); output_t out_mask(mem_mask & mask); return f(space, offset, (trans ^ exor) & mask, out_mask); }); } Source m_src; Func m_cb; }; template class first_transform_builder : public builder_base, public transform_base, typename Sink::input_t>, first_transform_builder > { public: template friend class transform_builder; using input_t = typename Sink::input_t; using output_t = mask_t, typename Sink::input_t>; template first_transform_builder(devcb_write &target, bool append, Sink &&sink, T &&cb, std::make_unsigned_t in_exor, std::make_unsigned_t in_mask, std::make_unsigned_t mask) : builder_base(target, append) , transform_base(mask) , m_sink(std::move(sink)) , m_cb(std::forward(cb)) , m_in_exor(in_exor & in_mask) , m_in_mask(in_mask) { m_sink.consume(); } first_transform_builder(first_transform_builder &&that) : builder_base(std::move(that)) , transform_base(std::move(that)) , m_sink(std::move(that.m_sink)) , m_cb(std::move(that.m_cb)) , m_in_exor(that.m_in_exor) , m_in_mask(that.m_in_mask) { m_sink.consume(); that.consume(); that.built(); } ~first_transform_builder() { this->template register_creator(); } void validity_check(validity_checker &valid) const { m_sink.validity_check(valid); } template std::enable_if_t::value, transform_builder > > transform(T &&cb) { output_t const m(this->mask()); if (this->inherited_mask()) this->mask(output_t(~output_t(0))); return transform_builder >(this->m_target, this->m_append, std::move(*this), std::forward(cb), m); } auto build() { assert(this->m_consumed); this->built(); return [sink = m_sink.build(), cb = std::move(this->m_cb), in_exor = m_in_exor, in_mask = m_in_mask, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { data = (data ^ in_exor) & in_mask; mem_mask &= in_mask; auto const trans(devcb_write::invoke_transform(cb, space, offset, data, mem_mask)); mem_mask &= mask; sink(space, offset, (trans ^ exor) & mask, mem_mask); }; } private: first_transform_builder(first_transform_builder const &) = delete; first_transform_builder operator=(first_transform_builder const &) = delete; first_transform_builder operator=(first_transform_builder &&that) = delete; template auto build(T &&chain) { assert(this->m_consumed); this->built(); return [f = std::move(chain), sink = m_sink.build(), cb = std::move(this->m_cb), in_exor = m_in_exor, in_mask = m_in_mask, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { data = (data ^ in_exor) & in_mask; mem_mask &= in_mask; auto const trans_1(devcb_write::invoke_transform(cb, space, offset, data, mem_mask)); output_t out_mask(mem_mask & mask); auto const trans_n(f(space, offset, (trans_1 ^ exor) & mask, out_mask)); sink(space, offset, trans_n, out_mask); }; } Sink m_sink; Func m_cb; std::make_unsigned_t const m_in_exor, m_in_mask; }; template class functoid_builder : public builder_base, public transform_base, functoid_builder > { private: class wrapped_builder : public builder_base { public: template friend class first_transform_builder; using input_t = Input; wrapped_builder(functoid_builder &&that) : builder_base(std::move(that)), m_cb(std::move(that.m_cb)) { that.consume(); that.built(); } wrapped_builder(wrapped_builder &&that) : builder_base(std::move(that)), m_cb(std::move(that.m_cb)) { that.consume(); that.built(); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); return [cb = std::move(this->m_cb)] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { devcb_write::invoke_write(cb, space, offset, data, mem_mask); }; } private: wrapped_builder(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder &&that) = delete; Func m_cb; }; functoid_builder(functoid_builder const &) = delete; functoid_builder &operator=(functoid_builder const &) = delete; functoid_builder &operator=(functoid_builder &&that) = delete; Func m_cb; public: using input_t = Input; template functoid_builder(devcb_write &target, bool append, T &&cb) : builder_base(target, append) , transform_base, functoid_builder>(DefaultMask) , m_cb(std::forward(cb)) { } functoid_builder(functoid_builder &&that) : builder_base(std::move(that)) , transform_base, functoid_builder>(std::move(that)) , m_cb(std::move(that.m_cb)) { that.consume(); that.built(); } ~functoid_builder() { this->template register_creator(); } template std::enable_if_t::value, first_transform_builder > > transform(T &&cb) { return first_transform_builder >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward(cb), this->exor(), this->mask(), DefaultMask); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); return [cb = std::move(this->m_cb), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { devcb_write::invoke_write(cb, space, offset, (data ^ exor) & mask, mem_mask & mask); }; } }; template class delegate_builder : public builder_base, public transform_base::input_t>, delegate_builder > { private: class wrapped_builder : public builder_base { public: template friend class first_transform_builder; using input_t = intermediate_t::input_t>; wrapped_builder(delegate_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_delegate(std::move(that.m_delegate)) { that.consume(); that.built(); } wrapped_builder(wrapped_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_delegate(std::move(that.m_delegate)) { that.consume(); that.built(); } void validity_check(validity_checker &valid) const { if (!m_devbase.subdevice(m_delegate.device_name())) osd_printf_error("Write callback bound to non-existent object tag %s (%s)\n", m_delegate.device_name(), m_delegate.name()); } auto build() { assert(this->m_consumed); this->built(); m_delegate.bind_relative_to(m_devbase); return [cb = std::move(this->m_delegate)] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { devcb_write::invoke_write(cb, space, offset, data, mem_mask); }; } private: wrapped_builder(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder &&that) = delete; device_t &m_devbase; Delegate m_delegate; }; friend class wrapped_builder; // workaround for MSVC delegate_builder(delegate_builder const &) = delete; delegate_builder &operator=(delegate_builder const &) = delete; delegate_builder &operator=(delegate_builder &&that) = delete; device_t &m_devbase; Delegate m_delegate; public: using input_t = intermediate_t::input_t>; template delegate_builder(devcb_write &target, bool append, device_t &devbase, char const *tag, T &&func, char const *name) : builder_base(target, append) , transform_base::input_t>, delegate_builder>(DefaultMask & delegate_traits::default_mask) , m_devbase(devbase) , m_delegate(std::forward(func), name, tag, std::add_pointer_t >(nullptr)) { } template delegate_builder(devcb_write &target, bool append, device_t &devbase, devcb_write::delegate_device_class_t &obj, T &&func, char const *name) : builder_base(target, append) , transform_base::input_t>, delegate_builder>(DefaultMask & delegate_traits::default_mask) , m_devbase(devbase) , m_delegate(std::forward(func), name, devcb_write::get_tag(obj), &obj) { } delegate_builder(delegate_builder &&that) : builder_base(std::move(that)) , transform_base::input_t>, delegate_builder>(std::move(that)) , m_devbase(that.m_devbase) , m_delegate(std::move(that.m_delegate)) { that.consume(); that.built(); } ~delegate_builder() { this->template register_creator(); } template std::enable_if_t::value, first_transform_builder > > transform(T &&cb) { std::make_unsigned_t const in_mask(this->inherited_mask() ? DefaultMask : this->mask()); mask_t::input_t> const out_mask(DefaultMask & delegate_traits::default_mask); return first_transform_builder >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward(cb), this->exor(), in_mask, out_mask); } void validity_check(validity_checker &valid) const { if (!m_devbase.subdevice(m_delegate.device_name())) osd_printf_error("Write callback bound to non-existent object tag %s (%s)\n", m_delegate.device_name(), m_delegate.name()); } auto build() { assert(this->m_consumed); this->built(); m_delegate.bind_relative_to(m_devbase); return [cb = std::move(this->m_delegate), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { devcb_write::invoke_write(cb, space, offset, (data ^ exor) & mask, mem_mask & mask); }; } }; class inputline_builder : public builder_base, public transform_base, inputline_builder> { private: class wrapped_builder : public builder_base { public: template friend class first_transform_builder; using input_t = intermediate_t; wrapped_builder(inputline_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(that.m_tag) , m_exec(that.m_exec) , m_linenum(that.m_linenum) { that.consume(); that.built(); } wrapped_builder(wrapped_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(that.m_tag) , m_exec(that.m_exec) , m_linenum(that.m_linenum) { that.consume(); that.built(); } void validity_check(validity_checker &valid) const { if (!m_exec) { device_t *const device(m_devbase.subdevice(m_tag)); if (!device) osd_printf_error("Write callback bound to non-existent object tag %s\n", m_tag); else if (!dynamic_cast(device)) osd_printf_error("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name()); } } auto build() { assert(this->m_consumed); this->built(); if (!m_exec) { device_t *const device(m_devbase.subdevice(m_tag)); if (!device) throw emu_fatalerror("Write callback bound to non-existent object tag %s\n", m_tag); m_exec = dynamic_cast(device); if (!m_exec) throw emu_fatalerror("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name()); } return [&exec = *m_exec, linenum = m_linenum] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { exec.set_input_line(linenum, data); }; } private: wrapped_builder(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder &&that) = delete; device_t &m_devbase; char const *const m_tag; device_execute_interface *m_exec; int const m_linenum; }; friend class wrapped_builder; // workaround for MSVC inputline_builder(inputline_builder const &) = delete; inputline_builder &operator=(inputline_builder const &) = delete; inputline_builder &operator=(inputline_builder &&that) = delete; device_t &m_devbase; char const *const m_tag; device_execute_interface *m_exec; int const m_linenum; public: using input_t = intermediate_t; inputline_builder(devcb_write &target, bool append, device_t &devbase, char const *tag, int linenum) : builder_base(target, append) , transform_base, inputline_builder>(1U) , m_devbase(devbase) , m_tag(tag) , m_exec(nullptr) , m_linenum(linenum) { } inputline_builder(devcb_write &target, bool append, device_execute_interface &exec, int linenum) : builder_base(target, append) , transform_base, inputline_builder>(1U) , m_devbase(exec.device()) , m_tag(exec.device().tag()) , m_exec(&exec) , m_linenum(linenum) { } inputline_builder(inputline_builder &&that) : builder_base(std::move(that)) , transform_base, inputline_builder>(std::move(that)) , m_devbase(that.m_devbase) , m_tag(that.m_tag) , m_exec(that.m_exec) , m_linenum(that.m_linenum) { that.consume(); that.built(); } ~inputline_builder() { this->template register_creator(); } template std::enable_if_t::value, first_transform_builder > > transform(T &&cb) { std::make_unsigned_t const in_mask(this->inherited_mask() ? DefaultMask : this->mask()); return first_transform_builder >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward(cb), this->exor(), in_mask, 1U); } void validity_check(validity_checker &valid) const { if (!m_exec) { device_t *const device(m_devbase.subdevice(m_tag)); if (!device) osd_printf_error("Write callback bound to non-existent object tag %s\n", m_tag); else if (!dynamic_cast(device)) osd_printf_error("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name()); } } auto build() { assert(this->m_consumed); this->built(); if (!m_exec) { device_t *const device(m_devbase.subdevice(m_tag)); if (!device) throw emu_fatalerror("Write callback bound to non-existent object tag %s\n", m_tag); m_exec = dynamic_cast(device); if (!m_exec) throw emu_fatalerror("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name()); } return [&exec = *m_exec, linenum = m_linenum, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { exec.set_input_line(linenum, (data ^ exor) & mask); }; } }; class latched_inputline_builder : public builder_base, public transform_base, latched_inputline_builder> { private: class wrapped_builder : public builder_base { public: template friend class first_transform_builder; using input_t = Input; wrapped_builder(latched_inputline_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(that.m_tag) , m_exec(that.m_exec) , m_linenum(that.m_linenum) , m_value(that.m_value) { that.consume(); that.built(); } wrapped_builder(wrapped_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(that.m_tag) , m_exec(that.m_exec) , m_linenum(that.m_linenum) , m_value(that.m_value) { that.consume(); that.built(); } void validity_check(validity_checker &valid) const { if (!m_exec) { device_t *const device(m_devbase.subdevice(m_tag)); if (!device) osd_printf_error("Write callback bound to non-existent object tag %s\n", m_tag); else if (!dynamic_cast(device)) osd_printf_error("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name()); } } auto build() { assert(this->m_consumed); this->built(); if (!m_exec) { device_t *const device(m_devbase.subdevice(m_tag)); if (!device) throw emu_fatalerror("Write callback bound to non-existent object tag %s\n", m_tag); m_exec = dynamic_cast(device); if (!m_exec) throw emu_fatalerror("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name()); } return [&exec = *m_exec, linenum = m_linenum, value = m_value] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { if (data) exec.set_input_line(linenum, value); }; } private: wrapped_builder(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder &&that) = delete; device_t &m_devbase; char const *const m_tag; device_execute_interface *m_exec; int const m_linenum; int const m_value; }; friend class wrapped_builder; // workaround for MSVC latched_inputline_builder(latched_inputline_builder const &) = delete; latched_inputline_builder &operator=(latched_inputline_builder const &) = delete; latched_inputline_builder &operator=(latched_inputline_builder &&that) = delete; device_t &m_devbase; char const *const m_tag; device_execute_interface *m_exec; int const m_linenum; int const m_value; public: using input_t = Input; latched_inputline_builder(devcb_write &target, bool append, device_t &devbase, char const *tag, int linenum, int value) : builder_base(target, append) , transform_base, latched_inputline_builder>(DefaultMask) , m_devbase(devbase) , m_tag(tag) , m_exec(nullptr) , m_linenum(linenum) , m_value(value) { } latched_inputline_builder(devcb_write &target, bool append, device_execute_interface &exec, int linenum, int value) : builder_base(target, append) , transform_base, latched_inputline_builder>(DefaultMask) , m_devbase(exec.device()) , m_tag(exec.device().tag()) , m_exec(&exec) , m_linenum(linenum) , m_value(value) { } latched_inputline_builder(latched_inputline_builder &&that) : builder_base(std::move(that)) , transform_base, latched_inputline_builder>(std::move(that)) , m_devbase(that.m_devbase) , m_tag(that.m_tag) , m_exec(that.m_exec) , m_linenum(that.m_linenum) , m_value(that.m_value) { that.consume(); that.built(); } ~latched_inputline_builder() { this->template register_creator(); } template std::enable_if_t::value, first_transform_builder > > transform(T &&cb) { return first_transform_builder >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward(cb), this->exor(), this->mask(), DefaultMask); } void validity_check(validity_checker &valid) const { if (!m_exec) { device_t *const device(m_devbase.subdevice(m_tag)); if (!device) osd_printf_error("Write callback bound to non-existent object tag %s\n", m_tag); else if (!dynamic_cast(device)) osd_printf_error("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name()); } } auto build() { assert(this->m_consumed); this->built(); if (!m_exec) { device_t *const device(m_devbase.subdevice(m_tag)); if (!device) throw emu_fatalerror("Write callback bound to non-existent object tag %s\n", m_tag); m_exec = dynamic_cast(device); if (!m_exec) throw emu_fatalerror("Write callback bound to device %s (%s) that does not implement device_execute_interface\n", device->tag(), device->name()); } return [&exec = *m_exec, linenum = m_linenum, value = m_value, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { if ((data ^ exor) & mask) exec.set_input_line(linenum, value); }; } }; class ioport_builder : public builder_base, public transform_base, ioport_builder> { private: class wrapped_builder : public builder_base { public: template friend class first_transform_builder; using input_t = intermediate_t; wrapped_builder(ioport_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } wrapped_builder(wrapped_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); ioport_port *const ioport(m_devbase.ioport(m_tag.c_str())); if (!ioport) throw emu_fatalerror("Write callback bound to non-existent I/O port %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name()); return [&port = *ioport] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { port.write(data); }; } private: wrapped_builder(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder &&that) = delete; device_t &m_devbase; std::string m_tag; }; friend class wrapped_builder; // workaround for MSVC ioport_builder(ioport_builder const &) = delete; ioport_builder &operator=(ioport_builder const &) = delete; ioport_builder &operator=(ioport_builder &&that) = delete; device_t &m_devbase; std::string m_tag; public: using input_t = intermediate_t; ioport_builder(devcb_write &target, bool append, device_t &devbase, std::string &&tag) : builder_base(target, append) , transform_base, ioport_builder>(DefaultMask) , m_devbase(devbase) , m_tag(std::move(tag)) { } ioport_builder(ioport_builder &&that) : builder_base(std::move(that)) , transform_base, ioport_builder>(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } ~ioport_builder() { this->template register_creator(); } template std::enable_if_t::value, first_transform_builder > > transform(T &&cb) { return first_transform_builder >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward(cb), this->exor(), this->mask(), DefaultMask); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); ioport_port *const ioport(m_devbase.ioport(m_tag.c_str())); if (!ioport) throw emu_fatalerror("Write callback bound to non-existent I/O port %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name()); return [&port = *ioport, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { port.write((data ^ exor) & mask); }; } }; class membank_builder : public builder_base, public transform_base, membank_builder> { private: class wrapped_builder : public builder_base { public: template friend class first_transform_builder; using input_t = intermediate_t; wrapped_builder(membank_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } wrapped_builder(wrapped_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); memory_bank *const bank(m_devbase.membank(m_tag.c_str())); if (!bank) throw emu_fatalerror("Write callback bound to non-existent memory bank %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name()); return [&membank = *bank] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { membank.set_entry(data); }; } private: wrapped_builder(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder &&that) = delete; device_t &m_devbase; std::string m_tag; }; friend class wrapped_builder; // workaround for MSVC membank_builder(membank_builder const &) = delete; membank_builder &operator=(membank_builder const &) = delete; membank_builder &operator=(membank_builder &&that) = delete; device_t &m_devbase; std::string m_tag; public: using input_t = intermediate_t; membank_builder(devcb_write &target, bool append, device_t &devbase, std::string &&tag) : builder_base(target, append) , transform_base, membank_builder>(DefaultMask) , m_devbase(devbase) , m_tag(std::move(tag)) { } membank_builder(membank_builder &&that) : builder_base(std::move(that)) , transform_base, membank_builder>(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } ~membank_builder() { this->template register_creator(); } template std::enable_if_t::value, first_transform_builder > > transform(T &&cb) { return first_transform_builder >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward(cb), this->exor(), this->mask(), DefaultMask); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); memory_bank *const bank(m_devbase.membank(m_tag.c_str())); if (!bank) throw emu_fatalerror("Write callback bound to non-existent memory bank %s of device %s (%s)\n", m_tag.c_str(), m_devbase.tag(), m_devbase.name()); return [&membank = *bank, exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { membank.set_entry((data ^ exor) & mask); }; } }; class output_builder : public builder_base, public transform_base, output_builder> { private: class wrapped_builder : public builder_base { public: template friend class first_transform_builder; using input_t = intermediate_t; wrapped_builder(output_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } wrapped_builder(wrapped_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); return [&item = m_devbase.machine().output().find_or_create_item(m_tag.c_str(), 0)] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { item.set(data); }; } private: wrapped_builder(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder &&that) = delete; device_t &m_devbase; std::string m_tag; }; friend class wrapped_builder; // workaround for MSVC output_builder(output_builder const &) = delete; output_builder &operator=(output_builder const &) = delete; output_builder &operator=(output_builder &&that) = delete; device_t &m_devbase; std::string m_tag; public: using input_t = intermediate_t; output_builder(devcb_write &target, bool append, device_t &devbase, std::string &&tag) : builder_base(target, append) , transform_base, output_builder>(DefaultMask) , m_devbase(devbase) , m_tag(std::move(tag)) { } output_builder(output_builder &&that) : builder_base(std::move(that)) , transform_base, output_builder>(std::move(that)) , m_devbase(that.m_devbase) , m_tag(std::move(that.m_tag)) { that.consume(); that.built(); } ~output_builder() { this->template register_creator(); } template std::enable_if_t::value, first_transform_builder > > transform(T &&cb) { return first_transform_builder >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward(cb), this->exor(), this->mask(), DefaultMask); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); return [&item = m_devbase.machine().output().find_or_create_item(m_tag.c_str(), 0), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { item.set((data ^ exor) & mask); }; } }; class log_builder : public builder_base, public transform_base, log_builder> { private: class wrapped_builder : public builder_base { public: template friend class first_transform_builder; using input_t = Input; wrapped_builder(log_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_message(std::move(that.m_message)) { that.consume(); that.built(); } wrapped_builder(wrapped_builder &&that) : builder_base(std::move(that)) , m_devbase(that.m_devbase) , m_message(std::move(that.m_message)) { that.consume(); that.built(); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); return [&devbase = m_devbase, message = std::move(m_message)] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { if (data) devbase.logerror("%s: %s\n", devbase.machine().describe_context(), message); }; } private: wrapped_builder(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder &&that) = delete; device_t &m_devbase; std::string m_message; }; friend class wrapped_builder; // workaround for MSVC log_builder(log_builder const &) = delete; log_builder &operator=(log_builder const &) = delete; log_builder &operator=(log_builder &&that) = delete; device_t &m_devbase; std::string m_message; public: using input_t = Input; log_builder(devcb_write &target, bool append, device_t &devbase, std::string &&message) : builder_base(target, append) , transform_base, log_builder>(DefaultMask) , m_devbase(devbase) , m_message(std::move(message)) { } log_builder(log_builder &&that) : builder_base(std::move(that)) , transform_base, log_builder>(std::move(that)) , m_devbase(that.m_devbase) , m_message(std::move(that.m_message)) { that.consume(); that.built(); } ~log_builder() { this->template register_creator(); } template std::enable_if_t::value, first_transform_builder > > transform(T &&cb) { return first_transform_builder >(this->m_target, this->m_append, wrapped_builder(std::move(*this)), std::forward(cb), this->exor(), this->mask(), DefaultMask); } void validity_check(validity_checker &valid) const { } auto build() { assert(this->m_consumed); this->built(); return [&devbase = m_devbase, message = std::move(m_message), exor = this->exor(), mask = this->mask()] (address_space &space, offs_t offset, input_t data, std::make_unsigned_t mem_mask) { if ((data ^ exor) & mask) devbase.logerror("%s: %s\n", devbase.machine().describe_context(), message); }; } }; class binder { public: binder(devcb_write &target) : m_target(target) { } binder(binder const &) = delete; binder(binder &&that) : m_target(that.m_target), m_append(that.m_append), m_used(that.m_used) { that.m_used = true; } binder &operator=(binder const &) = delete; binder &operator=(binder &&) = delete; template std::enable_if_t::value, functoid_builder > > set(T &&cb) { set_used(); return functoid_builder >(m_target, m_append, std::forward(cb)); } template std::enable_if_t::value, delegate_builder > > set(T &&func, char const *name) { set_used(); return delegate_builder >(m_target, m_append, m_target.owner().mconfig().current_device(), DEVICE_SELF, std::forward(func), name); } template std::enable_if_t::value, delegate_builder > > set(char const *tag, T &&func, char const *name) { set_used(); return delegate_builder >(m_target, m_append, m_target.owner().mconfig().current_device(), tag, std::forward(func), name); } template std::enable_if_t::value, delegate_builder > > set(U &obj, T &&func, char const *name) { set_used(); return delegate_builder >(m_target, m_append, m_target.owner(), devcb_write::cast_reference >(obj), std::forward(func), name); } template std::enable_if_t::value, delegate_builder > > set(device_finder &finder, T &&func, char const *name) { set_used(); std::pair const target(finder.finder_target()); return delegate_builder >(m_target, m_append, target.first, target.second, std::forward(func), name); } template std::enable_if_t::value, delegate_builder > > set(device_finder const &finder, T &&func, char const *name) { set_used(); std::pair const target(finder.finder_target()); return delegate_builder >(m_target, m_append, target.first, target.second, std::forward(func), name); } template auto append(Params &&... args) { m_append = true; return set(std::forward(args)...); } inputline_builder set_inputline(char const *tag, int linenum) { set_used(); return inputline_builder(m_target, m_append, m_target.owner().mconfig().current_device(), tag, linenum); } latched_inputline_builder set_inputline(char const *tag, int linenum, int value) { set_used(); return latched_inputline_builder(m_target, m_append, m_target.owner().mconfig().current_device(), tag, linenum, value); } inputline_builder set_inputline(device_execute_interface &obj, int linenum) { set_used(); return inputline_builder(m_target, m_append, obj, linenum); } latched_inputline_builder set_inputline(device_execute_interface &obj, int linenum, int value) { set_used(); return latched_inputline_builder(m_target, m_append, obj, linenum, value); } template inputline_builder set_inputline(device_finder const &finder, int linenum) { set_used(); std::pair const target(finder.finder_target()); return inputline_builder(m_target, m_append, target.first, target.second, linenum); } template latched_inputline_builder set_inputline(device_finder const &finder, int linenum, int value) { set_used(); std::pair const target(finder.finder_target()); return latched_inputline_builder(m_target, m_append, target.first, target.second, linenum, value); } template auto append_inputline(Params &&... args) { m_append = true; return set_inputline(std::forward(args)...); } template ioport_builder set_ioport(Params &&... args) { set_used(); return ioport_builder(m_target, m_append, m_target.owner().mconfig().current_device(), std::string(std::forward(args)...)); } template ioport_builder set_ioport(ioport_finder &finder) { set_used(); std::pair const target(finder.finder_target()); return ioport_builder(m_target, m_append, target.first, std::string(target.second)); } template ioport_builder set_ioport(ioport_finder const &finder) { set_used(); std::pair const target(finder.finder_target()); return ioport_builder(m_target, m_append, target.first, std::string(target.second)); } template ioport_builder append_ioport(Params &&... args) { m_append = true; return set_ioport(std::forward(args)...); } template membank_builder set_membank(Params &&... args) { set_used(); return membank_builder(m_target, m_append, m_target.owner().mconfig().current_device(), std::string(std::forward(args)...)); } template membank_builder set_membank(memory_bank_finder &finder) { set_used(); std::pair const target(finder.finder_target()); return membank_builder(m_target, m_append, target.first, std::string(target.second)); } template membank_builder set_membank(memory_bank_finder const &finder) { set_used(); std::pair const target(finder.finder_target()); return membank_builder(m_target, m_append, target.first, std::string(target.second)); } template membank_builder append_membank(Params &&... args) { m_append = true; return set_membank(std::forward(args)...); } template output_builder set_output(Params &&... args) { set_used(); return output_builder(m_target, m_append, m_target.owner().mconfig().current_device(), std::string(std::forward(args)...)); } template output_builder append_output(Params &&... args) { m_append = true; return set_output(std::forward(args)...); } template log_builder set_log(device_t &devbase, Params &&... args) { set_used(); return log_builder(m_target, m_append, devbase, std::string(std::forward(args)...)); } template std::enable_if_t >::value, log_builder> set_log(T &devbase, Params &&... args) { return set_log(static_cast(devbase), std::forward(args)...); } template std::enable_if_t >::value, log_builder> set_log(T &devbase, Params &&... args) { return set_log(devbase.device(), std::forward(args)...); } template log_builder set_log(Params &&... args) { return set_log(m_target.owner().mconfig().current_device(), std::forward(args)...); } template log_builder append_log(Params &&... args) { m_append = true; return set_log(std::forward(args)...); } void set_nop() { set_used(); m_target.m_creators.clear(); m_target.m_creators.emplace_back(std::make_unique()); } private: void set_used() { assert(!m_used); m_used = true; } devcb_write &m_target; bool m_append = false; bool m_used = false; }; std::vector m_functions; std::vector m_creators; public: devcb_write(device_t &owner); binder bind(); void reset(); virtual void validity_check(validity_checker &valid) const override; void resolve(); void resolve_safe(); void operator()(address_space &space, offs_t offset, Input data, std::make_unsigned_t mem_mask = DefaultMask); void operator()(address_space &space, Input data); void operator()(offs_t offset, Input data, std::make_unsigned_t mem_mask = DefaultMask); void operator()(Input data); bool isnull() const { return m_functions.empty() && m_creators.empty(); } explicit operator bool() const { return !m_functions.empty(); } }; template DefaultMask> devcb_write::devcb_write(device_t &owner) : devcb_write_base(owner) { } template DefaultMask> typename devcb_write::binder devcb_write::bind() { return binder(*this); } template DefaultMask> void devcb_write::reset() { assert(m_functions.empty()); m_creators.clear(); } template DefaultMask> void devcb_write::validity_check(validity_checker &valid) const { assert(m_functions.empty()); devcb_write_base::validity_check(valid); for (typename creator::ptr const &c : m_creators) c->validity_check(valid); } template DefaultMask> void devcb_write::resolve() { assert(m_functions.empty()); devcb_write_base::resolve(); m_functions.reserve(m_creators.size()); for (typename creator::ptr const &c : m_creators) m_functions.emplace_back(c->create()); m_creators.clear(); } template DefaultMask> void devcb_write::resolve_safe() { resolve(); if (m_functions.empty()) m_functions.emplace_back([] (address_space &space, offs_t offset, Input data, std::make_unsigned_t mem_mask) { }); } template DefaultMask> void devcb_write::operator()(address_space &space, offs_t offset, Input data, std::make_unsigned_t mem_mask) { assert(m_creators.empty() && !m_functions.empty()); typename std::vector::const_iterator it(m_functions.begin()); (*it)(space, offset, data, mem_mask); while (m_functions.end() != ++it) (*it)(space, offset, data, mem_mask); } template DefaultMask> void devcb_write::operator()(address_space &space, Input data) { this->operator()(space, 0U, data, DefaultMask); } template DefaultMask> void devcb_write::operator()(offs_t offset, Input data, std::make_unsigned_t mem_mask) { this->operator()(this->default_space(), offset, data, mem_mask); } template DefaultMask> void devcb_write::operator()(Input data) { this->operator()(this->default_space(), 0U, data, DefaultMask); } using devcb_read8 = devcb_read; using devcb_read16 = devcb_read; using devcb_read32 = devcb_read; using devcb_read64 = devcb_read; using devcb_read_line = devcb_read; using devcb_write8 = devcb_write; using devcb_write16 = devcb_write; using devcb_write32 = devcb_write; using devcb_write64 = devcb_write; using devcb_write_line = devcb_write; //************************************************************************** // TEMPLATE INSTANTIATIONS //************************************************************************** extern template class devcb_read; extern template class devcb_read; extern template class devcb_read; extern template class devcb_read; extern template class devcb_read; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read8::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read16::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read32::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read64::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read_line::delegate_builder; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl >; extern template class devcb_read8::creator_impl; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl >; extern template class devcb_read16::creator_impl; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl >; extern template class devcb_read32::creator_impl; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl >; extern template class devcb_read64::creator_impl; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl >; extern template class devcb_read_line::creator_impl; extern template class devcb_write; extern template class devcb_write; extern template class devcb_write; extern template class devcb_write; extern template class devcb_write; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write8::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write16::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write32::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write64::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write_line::delegate_builder; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl >; extern template class devcb_write8::creator_impl; extern template class devcb_write8::creator_impl; extern template class devcb_write8::creator_impl; extern template class devcb_write8::creator_impl; extern template class devcb_write8::creator_impl; extern template class devcb_write8::creator_impl; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl >; extern template class devcb_write16::creator_impl; extern template class devcb_write16::creator_impl; extern template class devcb_write16::creator_impl; extern template class devcb_write16::creator_impl; extern template class devcb_write16::creator_impl; extern template class devcb_write16::creator_impl; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl >; extern template class devcb_write32::creator_impl; extern template class devcb_write32::creator_impl; extern template class devcb_write32::creator_impl; extern template class devcb_write32::creator_impl; extern template class devcb_write32::creator_impl; extern template class devcb_write32::creator_impl; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl >; extern template class devcb_write64::creator_impl; extern template class devcb_write64::creator_impl; extern template class devcb_write64::creator_impl; extern template class devcb_write64::creator_impl; extern template class devcb_write64::creator_impl; extern template class devcb_write64::creator_impl; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl >; extern template class devcb_write_line::creator_impl; extern template class devcb_write_line::creator_impl; extern template class devcb_write_line::creator_impl; extern template class devcb_write_line::creator_impl; extern template class devcb_write_line::creator_impl; extern template class devcb_write_line::creator_impl; #endif // MAME_EMU_DEVCB_H