// license:BSD-3-Clause // copyright-holders:Aaron Giles, Vas Crabb /// \file /// \brief Object auto-discovery helpers /// \defgroup devfind Object auto-discovery helpers /// \{ #ifndef __EMU_H__ #error Dont include this file directly; include emu.h instead. #endif #ifndef MAME_EMU_DEVFIND_H #define MAME_EMU_DEVFIND_H #pragma once #include #include #include #include #include #include #include //************************************************************************** // TYPE DEFINITIONS //************************************************************************** /// \brief Helper class to find arrays of devices, etc. /// /// Useful when a machine/device has a number of similar subdevices, I/O /// ports, memory regions, etc. Template arguments are the element type /// and number of elements in the array. It's assumed that the element /// can be constructed with a device_t reference and a C string tag. template class object_array_finder { private: template object_array_finder(device_t &base, F const &fmt, unsigned start, std::integer_sequence, Param const &... arg) : m_tag{ util::string_format(fmt, start + V)... } , m_array{ { base, m_tag[V].c_str(), arg... }... } { } template object_array_finder(device_t &base, std::array const &tags, std::integer_sequence, Param const &... arg) : m_array{ { base, tags[V], arg... }... } { } /// \brief Generated tag names /// /// Finder objects do not copy the search tag supplied at /// construction. Tags that are programmatically generated at /// construction are stored here so they persist until resolution /// time (and beyond). std::string const m_tag[Count]; /// \brief The object discovery elements /// /// These are the actual object discovery helpers. Note that this /// member must be initialised after m_tag, as it may depend on /// programmatically generated tags. T m_array[Count]; public: /// \brief Element type for Container concept typedef T value_type; /// \brief Reference to element type for Container concept typedef T &reference; /// \brief Reference to constant element type for Container concept typedef T const &const_reference; /// \brief Iterator for Container concept typedef T *iterator; /// \brief Constant iterator for Container concept typedef T const *const_iterator; /// \brief Iterator difference type for Container concept typedef typename std::iterator_traits::difference_type difference_type; /// \brief Size type for Container concept typedef std::make_unsigned_t size_type; /// \brief Construct with programmatically generated tags /// /// Specify a format string and starting number. A single unsigned /// int format argument is supplied containing the (zero-based) /// element index added to the starting number. For example if /// Count = 2, ("p%u_joy", 1) expands to ("p1_joy", "p2_joy"). The /// relaxed format rules used by util::string_format apply. /// \param [in] base Base device to search from. /// \param [in] fmt Search tag format, should expect single unsigned /// int argument. /// \param [in] start Number to add to element index when /// calculating values for string format argument. /// \param [in] arg Optional additional constructor argument(s) passed /// to all elements. /// \sa util::string_format template object_array_finder(device_t &base, F const &fmt, unsigned start, Param const &... arg) : object_array_finder(base, fmt, start, std::make_integer_sequence(), arg...) { } /// \brief Construct with free-form list of tags /// /// Specify arbitrary tags for objects. Useful when there is no /// particular pattern to the object tags. /// \param [in] base Base device to search from. /// \param [in] tags Tags to search for, e.g. { "player", "dips" }. /// The tags are not copied, it is the caller's responsibility to /// ensure the pointers remain valid until resolution time. /// \param [in] arg Optional additional constructor argument(s) passed /// to all elements. template object_array_finder(device_t &base, std::array const &tags, Param const &... arg) : object_array_finder(base, tags, std::make_integer_sequence(), arg...) { } /// \brief Get iterator to first element /// /// Returns an iterator to the first element in the array. /// \return Iterator to first element. const_iterator begin() const { return m_array; } iterator begin() { return m_array; } /// \brief Get iterator beyond last element /// /// Returns an iterator one past the last element in the array. /// \return Iterator one past last element. const_iterator end() const { return m_array + Count; } iterator end() { return m_array + Count; } /// \brief Get constant iterator to first element /// /// Returns a constant iterator to the first element in the array. /// \return Constant iterator to first element. const_iterator cbegin() const { return m_array; } /// \brief Get constant iterator beyond last element /// /// Returns a constant iterator one past the last element in the /// array. /// \return Constant iterator one past last element. const_iterator cend() const { return m_array + Count; } /// \brief Get array size /// /// Returns number of elements in the array (compile-time constant). /// \return The size of the array. constexpr size_type size() const { return Count; } /// \brief Get maximum array size /// /// Returns maximum number of elements in the array (compile-time /// constant, always equal to the size of the array). /// \return The size of the array. constexpr size_type max_size() const { return Count; } /// \brief Does array have no elements /// /// Returns whether the array has no elements (compile-time /// constant). /// \return True if the array has no elements, false otherwise. constexpr bool empty() const { return !Count; } /// \brief Get first element /// /// Returns a reference to the first element in the array. /// \return Reference to first element. T const &front() const { return m_array[0]; } T &front() { return m_array[0]; } /// \brief Get last element /// /// Returns a reference to the last element in the array. /// \return Reference to last element. T const &back() const { return m_array[Count - 1]; } T &back() { return m_array[Count - 1]; } /// \brief Element accessor (const) /// /// Returns a const reference to the element at the supplied index. /// \param [in] index Index of desired element (zero-based). /// \return Constant reference to element at specified index. T const &operator[](unsigned index) const { assert(index < Count); return m_array[index]; } /// \brief Element accessor (non-const) /// /// Returns a reference to the element at the supplied index. /// \param [in] index Index of desired element (zero-based). /// \return Reference to element at specified index. T &operator[](unsigned index) { assert(index < Count); return m_array[index]; } /// \brief Checked element accessor /// /// Returns a reference to the element at the supplied index if less /// than the size of the array, or throws std::out_of_range /// otherwise. /// \param [in] index Index of desired element (zero-based). /// \return Reference to element at specified index. /// \throw std::out_of_range T const &at(unsigned index) const { if (Count > index) return m_array[index]; else throw std::out_of_range("Index out of range"); } T &at(unsigned index) { if (Count > index) return m_array[index]; else throw std::out_of_range("Index out of range"); } }; /// \brief Base class for object discovery helpers /// /// Abstract non-template base class for object auto-discovery helpers. /// Provides the interface that the device_t uses to manage discovery at /// resolution time. class device_resolver_base { public: /// \brief Destructor /// /// Destruction via base class pointer and dynamic type behaviour /// are allowed. virtual ~device_resolver_base(); /// \brief Get next registered object discovery helper /// /// Implementation of basic single-linked list behaviour. /// \return Pointer to the next registered object discovery helper, /// or nullptr if this is the last. device_resolver_base *next() const noexcept { return m_next; } /// \brief Attempt discovery /// /// Concrete derived classes must implement this member function. /// Should return false if the object is required but not found, or /// true otherwise (the report_missing member function can assist /// in implementing this behaviour). /// \param [in] valid Pass a pointer to the validity checker if this /// is a dry run (i.e. no intention to actually start the device), /// or nullptr otherwise. /// \return False if the object is required but not found, or true /// otherwise. virtual bool findit(validity_checker *valid) = 0; /// \brief Clear temporary binding from configuration /// /// Concrete derived classes must implement this member function. /// Object finders may allow temporary binding to the anticipated /// target during configuration. This needs to be cleared to ensure /// the correct target is found if a device further up the hierarchy /// subsequently removes or replaces devices. virtual void end_configuration() = 0; protected: /// \brief Designated constructor /// /// Construct base object discovery helper and register with device /// to be invoked at resolution time. /// \param [in] base Device to register with for resolution. device_resolver_base(device_t &base); /// \brief Pointer to next registered discovery helper /// /// This is a polymorphic class, so it can't be held in a standard /// list container that requires elements of the same type. Hence /// it implements basic intrusive single-linked list behaviour. device_resolver_base *const m_next; }; /// \brief Base class for object discovery helpers /// /// Abstract non-template base class for object auto-discovery helpers. /// Provides functionality for specifying a target as a base device and /// relative tag. Also supplies helpers for looking up resources. class finder_base : protected device_resolver_base { public: /// \brief Get search tag /// /// Returns the search tag. /// \return The object tag this helper will search for. char const *finder_tag() const { return m_tag; } /// \brief Get search target /// /// Returns the search base device and tag. /// \return a pair consisting of a reference to the device to search /// relative to and the relative tag. std::pair finder_target() const { return std::make_pair(m_base, m_tag); } /// \brief Set search tag /// /// Allows search tag to be changed after construction. Note that /// this must be done before resolution time to take effect. Also /// note that the tag is not copied. /// \param [in] base Updated search base. The tag must be specified /// relative to this device. /// \param [in] tag Updated search tag. This is not copied, it is /// the caller's responsibility to ensure this pointer remains /// valid until resolution time. void set_tag(device_t &base, char const *tag) { assert(!m_resolved); assert(tag); m_base = base; m_tag = tag; } /// \brief Set search tag /// /// Allows search tag to be changed after construction. Note that /// this must be done before resolution time to take effect. Also /// note that the tag is not copied. /// \param [in] tag Updated search tag relative to the current /// device being configured. This is not copied, it is the /// caller's responsibility to ensure this pointer remains valid /// until resolution time. void set_tag(char const *tag); /// \brief Set search tag /// /// Allows search tag to be changed after construction. Note that /// this must be done before resolution time to take effect. /// \param [in] finder Object finder to take the search base and tag /// from. void set_tag(finder_base const &finder) { assert(!m_resolved); std::tie(m_base, m_tag) = finder.finder_target(); } /// \brief Dummy tag always treated as not found static constexpr char DUMMY_TAG[17] = "finder_dummy_tag"; protected: /// \brief Designated constructor /// /// Construct base object discovery helper and register with device /// to be invoked at resolution time. /// \param [in] base Device to register with for resolution. Also /// sets initial base device to search from. The tag must be /// specified relative to this device. /// \param [in] tag Object tag to search for. This is not copied, /// it is the caller's responsibility to ensure this pointer /// remains valid until resolution time. finder_base(device_t &base, char const *tag); /// \brief Find a memory region /// /// Look up memory region and check that its length and width match /// desired values. Returns pointer to the base of the region if a /// matching region is found, or nullptr otherwise. Prints a /// message at warning level if the region is required, a region /// with the requested tag is found, but it doesn't match the /// desired width and length. /// \param [in] width Desired region width in bytes. /// \param [in,out] length On entry, the desired region length in /// width units, or 0U to match any region length. Set to the /// length of the region in width units if a matching region is /// found, or 0U otherwise. /// \param [in] required Whether warning message should be printed /// if a region with matching tag of incorrect width/length is /// found. /// \return Base pointer of the memory region if a matching region /// is found, or nullptr otherwise. void *find_memregion(u8 width, size_t &length, bool required) const; /// \brief Check that memory region exists /// /// Walks ROM regions of all devices starting from the root looking /// for one with matching tag and length in bytes. Prints a warning /// message if the region is required, a region with the requested /// tag is found, but its length does not match. Calls /// report_missing to print an error message if the region is /// not found. Returns true if the region is required but no /// matching region is found, or false otherwise. /// \param [in] required True if the region is required, or false if /// it is optional. /// \return True if the region is optional, or if the region is /// required and a matching region is found, or false otherwise. bool validate_memregion(bool required) const; /// \brief Find a memory share /// /// Look up memory share and check that its width matches desired /// value. Returns pointer to base of memory share if a matching /// share is found, or nullptr otherwise. Prints a message at /// warning level if the memory share is required, a memory share /// with the requested tag is found, but it doesn't match the /// desired width. /// \param [in] width Desired memory share width in bits. /// \param [out] bytes Set to memory share length in bytes if a /// matching memory share is found, otherwise left unchanged. /// \param [in] required Whether warning message should be printed /// if a memory share with matching tag of incorrect width is /// found. /// \return Pointer to base of memory share if a matching memory /// share is found, or nullptr otherwise. void *find_memshare(u8 width, size_t &bytes, bool required) const; /// \brief Find an address space /// /// Look up address space and check that its width matches desired /// value. Returns pointer to address space if a matching space /// is found, or nullptr otherwise. Prints a message at warning /// level if the address space is required, a device with the /// requested tag is found, but it doesn't have a memory interface /// or a space with the designated number. /// \param [in] spacenum Address space number. /// \param [in] width Specific data width, or 0. /// \param [in] required Whether warning message should be printed /// if a device with no memory interface or space of that number /// is found. /// \return Pointer to address space if a matching address space /// is found, or nullptr otherwise. address_space *find_addrspace(int spacenum, u8 width, bool required) const; /// \brief Check that address space exists /// /// Returns true if the space is required but no matching space is /// found, or false otherwise. /// \param [in] spacenum Address space number. /// \param [in] width Specific data width, or 0. /// \param [in] required Whether warning message should be printed /// if a device with no memory interface or space of that number /// is found. /// \return True if the space is optional, or if the space is /// space and a matching space is found, or false otherwise. bool validate_addrspace(int spacenum, u8 width, bool required) const; /// \brief Log if object was not found /// /// Logs a message at error level if the target object is required /// and the search tag is the dummy tag, or the target object is /// required and not found. Logs a message at verbose level if the /// object is optional and not found. Returns true if the object is /// found or not required, and false otherwise. /// \param [in] found Whether the target object has been found. /// \param [in] objname Display name for target object type. /// \param [in] required True if the object is required (validation /// error if not found), or false if optional. /// \return True if found or not required, false otherwise. bool report_missing(bool found, char const *objname, bool required) const; /// \brief Base device to search from std::reference_wrapper m_base; /// \brief Object tag to search for char const *m_tag; /// \brief Set when object resolution completes bool m_resolved; }; /// \brief Base class for object discovery helpers /// /// Abstract template base for auto-discovery of objects of a particular /// type. Provides implicit cast-to-pointer and pointer member access /// operators. Template arguments are the type of object to discover, /// and whether failure to find the object is considered an error. /// Assumes that non-null pointer is found, and null pointer is not /// found. template class object_finder_common_base : public finder_base { public: /// \brief Clear temporary binding from configuration /// /// Object finders may allow temporary binding to the anticipated /// target during configuration. This needs to be cleared to ensure /// the correct target is found if a device further up the hierarchy /// subsequently removes or replaces devices. virtual void end_configuration() override { assert(!m_resolved); m_target = nullptr; } /// \brief Get pointer to target object /// /// \return Pointer to target object if found, or nullptr otherwise. ObjectClass *target() const { return m_target; } /// \brief Cast-to-pointer operator /// /// Allows implicit casting to a pointer to the target object. /// Returns a null pointer if resolution has not been attempted or // object was not found. /// \return Pointer to target object if found, or nullptr otherwise. operator ObjectClass *() const { return m_target; } /// \brief Pointer member access operator /// /// Allows pointer-member-style access to members of the target /// object. Asserts that the target object has been found. /// \return Pointer to target object if found, or nullptr otherwise. ObjectClass *operator->() const { assert(m_target); return m_target; } protected: /// \brief Designated constructor /// /// Construct base, register with base device to be invoked at /// resolution time, and initialise target object pointer to /// nullptr. /// \param [in] base Base device to search from. /// \param [in] tag Object tag to search for. This is not copied, /// it is the caller's responsibility to ensure this pointer /// remains valid until resolution time. object_finder_common_base(device_t &base, const char *tag) : finder_base(base, tag) { } /// \brief Log if object was not found /// /// Logs a message if the target object was not found, and returns /// false if this is problematic. Calls base implementation, /// supplying values for found and required parameters. See base /// implementation for more detail. /// \param [in] objname Display name for target object type. /// \return True if found or not required, false otherwise. bool report_missing(char const *objname) const { return finder_base::report_missing(m_target != nullptr, objname, Required); } /// \brief Pointer to target object /// /// Pointer to target object, or nullptr if resolution has not been /// attempted or the search failed. Concrete derived classes must /// set this in their implementation of the findit member function. ObjectClass *m_target = nullptr; }; /// \brief Base class for object discovery helpers /// /// Allows partial specialisations to add members based on object class /// and/or whether the object is required. Template arguments are the /// type of object to discover, and whether failure to find the object /// is considered an error. template class object_finder_base : public object_finder_common_base { using object_finder_common_base::object_finder_common_base; }; /// \brief Base class for optional object discovery helpers /// /// Adds helpers for to test whether the object was found, and by /// extension whether it's safe to dereference. Template argument is /// the type of object to discover. template class object_finder_base : public object_finder_common_base { public: /// \brief Return whether target has been found /// /// Works on the assumption that the target object pointer will /// be non-null if the target has been found, and null /// otherwise. /// \return True if object has been found, or false otherwise. bool found() const { return this->m_target != nullptr; } /// \brief Cast-to-Boolean operator /// /// Allows truth tests to test whether it's safe to dereference /// the object, similar to pointers and various C++ standard /// library objects. /// \return True if safe to dereference, or false otherwise. explicit operator bool() const { return this->m_target != nullptr; } using object_finder_common_base::object_finder_common_base; }; /// \brief Device finder template /// /// Template arguments are the device class to find, and whether the /// device is required. It is a validation error if a required device /// is not found. If a device with matching tag is found but the class /// does not match, a message is printed at warning level. This class /// is generally not used directly, instead the optional_device and /// required_device helpers are used. /// \sa optional_device required_device template class device_finder : public object_finder_base { public: using object_finder_base::set_tag; /// \brief Device finder constructor /// \param [in] base Base device to search from. /// \param [in] tag Device tag to search for. This is not copied, /// it is the caller's responsibility to ensure this pointer /// remains valid until resolution time. device_finder(device_t &base, char const *tag) : object_finder_base(base, tag) { } /// \brief Set search tag /// /// Allows search tag to be changed after construction. Note that /// this must be done before resolution time to take effect. Note /// that this binds to a particular instance, so the device must not /// be removed or replaced, as it will cause a use-after-free when /// resolving objects. /// \param [in] object Object to refer to. void set_tag(DeviceClass &object) { set_tag(object, DEVICE_SELF); } /// \brief Set target during configuration /// /// During configuration, device_finder instances may be assigned /// a reference to the anticipated target device to avoid the need /// for tempories during configuration. Normal resolution will /// still happen after machine configuration is completed to ensure /// device removal/replacement is handled properly. /// \param [in] device Reference to anticipated target device. /// \return The same reference supplied by the caller. template std::enable_if_t::value, T &> operator=(T &device) { assert(!this->m_resolved); assert(is_expected_tag(device)); this->m_target = &device; return device; } /// \brief Look up target during configuration /// Look up the current target device during configuration, before /// resolution. /// \return Pointer to target device if found, or nullptr otherwise. DeviceClass *lookup() const { return this->m_base.get().template subdevice(this->m_tag); } private: /// \brief Check that device implementation has expected tag /// \param [in] device Reference to device. /// \return True if supplied device matches the configured target /// tag, or false otherwise. template std::enable_if_t::value, bool> is_expected_tag(T const &device) const { return this->m_base.get().subtag(this->m_tag) == device.tag(); } /// \brief Check that device mixin has expected tag /// \param [in] device Reference to interface/mixin. /// \return True if supplied mixin matches the configured target /// tag, or false otherwise. template std::enable_if_t::value, bool> is_expected_tag(T const &interface) const { return this->m_base.get().subtag(this->m_tag) == interface.device().tag(); } /// \brief Find device /// /// Find device of desired type with requested tag. If a device /// with the requested tag is found but the type is incorrect, a /// warning message will be printed. This method is called by the /// base device at resolution time. /// \param [in] valid Pass a pointer to the validity checker if this /// is a dry run (i.e. no intention to actually start the device), /// or nullptr otherwise. /// \return True if the device is optional or if a matching device /// is found, false otherwise. virtual bool findit(validity_checker *valid) override { if (!valid) { assert(!this->m_resolved); this->m_resolved = true; } device_t *const device = this->m_base.get().subdevice(this->m_tag); this->m_target = dynamic_cast(device); if (device && !this->m_target) osd_printf_warning("Device '%s' found but is of incorrect type (actual type is %s)\n", this->m_tag, device->name()); return this->report_missing("device"); } }; /// \brief Optional device finder /// /// Finds device with matching type and tag. If a device with matching /// tag is found but the type does not match, a message is printed at /// warning level. No error is generated if a matching device is not /// found (the target object pointer will be null). If you have a /// number of similar optional devices, consider using /// optional_device_array. /// \sa required_device optional_device_array device_finder template using optional_device = device_finder; /// \brief Required device finder /// /// Finds device with matching type and tag. If a device with matching /// tag is found but the type does not match, a message is printed at /// warning level. A validation error is generated if a matching device /// is not found. If you have a number of similar required devices, /// consider using required_device_array. /// \sa optional_device required_device_array device_finder template using required_device = device_finder; template using device_array_finder = object_array_finder, Count>; template using optional_device_array = device_array_finder; template using required_device_array = device_array_finder; /// \brief Memory region finder template /// /// Template argument is whether the memory region is required. It is a /// validation error if a required memory region is not found. This /// class is generally not used directly, instead the /// optional_memory_region and required_memory_region helpers are used. /// \sa optional_memory_region required_memory_region template class memory_region_finder : public object_finder_base { public: /// \brief Memory region finder constructor /// \param [in] base Base device to search from. /// \param [in] tag Memory region tag to search for. This is not /// copied, it is the caller's responsibility to ensure this /// pointer remains valid until resolution time. memory_region_finder(device_t &base, char const *tag); private: /// \brief Find memory region /// /// Find memory region with requested tag. For a dry run, the /// target object pointer will not be set. This method is called by /// the base device at resolution time. /// \param [in] valid Pass a pointer to the validity checker if this /// is a dry run (i.e. no intention to actually start the device), /// or nullptr otherwise. /// \return True if the memory region is optional or if a matching /// memory region is found, false otherwise. virtual bool findit(validity_checker *valid) override; }; /// \brief Optional memory region finder /// /// Finds memory region with matching tag. No error is generated if a /// matching memory region is not found (the target object pointer will /// be null). If you have a number of similar optional memory regions, /// consider using optional_memory_region_array. /// \sa required_memory_region optional_memory_region_array /// memory_region_finder using optional_memory_region = memory_region_finder; /// \brief Required memory region finder /// /// Finds memory region with matching tag. A validation error is /// generated if a matching memory region is not found. If you have a /// number of similar required memory regions, consider using /// required_memory_region_array. /// \sa optional_memory_region required_memory_region_array /// memory_region_finder using required_memory_region = memory_region_finder; template using memory_region_array_finder = object_array_finder, Count>; template using optional_memory_region_array = memory_region_array_finder; template using required_memory_region_array = memory_region_array_finder; /// \brief Memory bank finder template /// /// Template argument is whether the memory bank is required. It is a /// validation error if a required memory bank is not found. This class /// is generally not used directly, instead the optional_memory_bank and /// required_memory_bank helpers are used. /// \sa optional_memory_bank required_memory_bank template class memory_bank_finder : public object_finder_base { public: /// \brief Memory bank finder constructor /// \param [in] base Base device to search from. /// \param [in] tag Memory bank tag to search for. This is not /// copied, it is the caller's responsibility to ensure this /// pointer remains valid until resolution time. memory_bank_finder(device_t &base, char const *tag); /// \brief Find memory bank /// /// Find memory bank with requested tag. Just returns true for a /// dry run. This method is called by the base device at resolution /// time. /// \param [in] valid Pass a pointer to the validity checker if this /// is a dry run (i.e. no intention to actually start the device), /// or nullptr otherwise. /// \return True if the memory bank is optional, a matching memory /// bank is found or this is a dry run, false otherwise. virtual bool findit(validity_checker *valid) override; }; /// \brief Optional memory bank finder /// /// Finds memory bank with matching tag. No error is generated if a /// matching memory bank is not found (the target object pointer will /// be null). If you have a number of similar optional memory banks, /// consider using optional_memory_bank_array. /// \sa required_memory_bank optional_memory_bank_array /// memory_bank_finder using optional_memory_bank = memory_bank_finder; /// \brief Required memory bank finder /// /// Finds memory bank with matching tag. A validation error is /// generated if a matching memory bank is not found. If you have a /// number of similar required memory banks, consider using /// required_memory_bank_array. /// \sa optional_memory_bank required_memory_bank_array /// memory_bank_finder using required_memory_bank = memory_bank_finder; template using memory_bank_array_finder = object_array_finder, Count>; template using optional_memory_bank_array = memory_bank_array_finder; template using required_memory_bank_array = memory_bank_array_finder; /// \brief Memory bank creator /// /// Creates a memory bank or finds an existing one instantiated via an /// address map. class memory_bank_creator : public finder_base { public: /// \brief Memory bank creator constructor /// \param [in] base Base device to search from. /// \param [in] tag Memory bank tag to search for or create. This /// is not copied, it is the caller's responsibility to ensure /// this pointer remains valid until resolution time. memory_bank_creator(device_t &base, char const *tag) : finder_base(base, tag) { } /// \brief Get pointer to memory bank object /// \return Pointer to found or created bank object. memory_bank *target() const { return m_target; } /// \brief Cast-to-pointer operator /// /// Allows implicit casting to a pointer to the target /// memory bank object. /// \return Pointer to found or created memory bank object. operator memory_bank *() const { return m_target; } /// \brief Pointer member access operator /// /// Allows pointer-member-style access to members of the target /// memory bank object. Asserts that the target has been found. /// \return Pointer to found or created bank object. memory_bank *operator->() const { assert(m_target); return m_target; } protected: virtual bool findit(validity_checker *valid) override; virtual void end_configuration() override; /// \brief Pointer to target object /// /// Pointer to target memory bank object, or nullptr if creation has /// not been attempted yet. memory_bank *m_target = nullptr; }; template using memory_bank_array_creator = object_array_finder; /// \brief Base class for optional I/O port finders /// /// Adds helpers for to test whether the I/O port was found, and to read /// the port value or return a default if the I/O port was not found. template <> class object_finder_base : public object_finder_common_base { public: /// \brief Read I/O port if found or return default value /// /// If the I/O port was found, this reads a value from the I/O port /// and returns it. If the I/O port was not found, the default /// value (supplied as a parameter) is returned. /// \param [in] defval Value to return if I/O port was not found. /// \return Value read from I/O port if found, or supplied default /// value otherwise. ioport_value read_safe(ioport_value defval) { return this->m_target ? this->m_target->read() : defval; } /// \brief Return whether target has been found /// /// Works on the assumption that the target object pointer will /// be non-null if the target has been found, and null /// otherwise. /// \return True if object has been found, or false otherwise. bool found() const { return this->m_target != nullptr; } /// \brief Cast-to-Boolean operator /// /// Allows truth tests to test whether it's safe to dereference /// the object, similar to pointers and various C++ standard /// library objects. /// \return True if safe to dereference, or false otherwise. explicit operator bool() const { return this->m_target != nullptr; } using object_finder_common_base::object_finder_common_base; }; /// \brief I/O port finder template /// /// Template argument is whether the I/O port is required. It is a /// validation error if a required I/O port is not found. This class is /// generally not used directly, instead the optional_ioport and /// required_ioport helpers are used. /// \sa optional_ioport required_ioport template class ioport_finder : public object_finder_base { public: /// \brief I/O port finder constructor /// \param [in] base Base device to search from. /// \param [in] tag I/O port tag to search for. This is not copied, /// it is the caller's responsibility to ensure this pointer /// remains valid until resolution time. ioport_finder(device_t &base, char const *tag); private: /// \brief Find I/O port /// /// Find I/O port with requested tag. Just returns true for a dry /// run. This method is called by the base device at resolution /// time. /// \param [in] valid Pass a pointer to the validity checker if this /// is a dry run (i.e. no intention to actually start the device), /// or nullptr otherwise. /// \return True if the I/O port is optional, a matching I/O port is /// is found or this is a dry run, false otherwise. virtual bool findit(validity_checker *valid) override; }; /// \brief Optional I/O port finder /// /// Finds I/O port with matching tag. No error is generated if a /// matching I/O port is not found (the target object pointer will be /// null). If you have a number of similar optional I/O ports, consider /// using optional_ioport_array. /// \sa required_ioport optional_ioport_array ioport_finder using optional_ioport = ioport_finder; /// \brief Required I/O port finder /// /// Finds I/O port with matching tag. A validation error is generated /// if a matching I/O port is not found. If you have a number of /// similar required I/O ports, consider using required_ioport_array. /// \sa optional_ioport required_ioport_array ioport_finder using required_ioport = ioport_finder; template using ioport_array_finder = object_array_finder, Count>; template using optional_ioport_array = ioport_array_finder; template using required_ioport_array = ioport_array_finder; /// \brief Address space finder template /// /// Template argument is whether the address space is required. It is a /// validation error if a required address space is not found. This /// class is generally not used directly, instead the /// optional_address_space and required_address_space helpers are used. /// \sa optional_address_space required_address_space template class address_space_finder : public object_finder_base { public: /// \brief Address space finder constructor /// \param [in] base Base device to search from. /// \param [in] tag Address space device tag to search for. /// This is not copied, it is the caller's responsibility to /// ensure this pointer remains valid until resolution time. /// \param [in] spacenum Address space number. /// \param [in] width Required data width in bits, or zero if /// any data width is acceptable. address_space_finder(device_t &base, char const *tag, int spacenum, u8 width = 0); /// \brief Set search tag and space number /// /// Allows search tag to be changed after construction. Note that /// this must be done before resolution time to take effect. Also /// note that the tag is not copied. /// \param [in] base Updated search base. The tag must be specified /// relative to this device. /// \param [in] tag Updated search tag. This is not copied, it is /// the caller's responsibility to ensure this pointer remains /// valid until resolution time. /// \param [in] spacenum Address space number. void set_tag(device_t &base, char const *tag, int spacenum) { finder_base::set_tag(base, tag); m_spacenum = spacenum; } /// \brief Set search tag and space number /// /// Allows search tag and address space number to be changed after /// construction. Note that this must be done before resolution /// time to take effect. Also note that the tag is not copied. /// \param [in] tag Updated search tag relative to the current /// device being configured. This is not copied, it is the /// caller's responsibility to ensure this pointer remains valid /// until resolution time. /// \param [in] spacenum Address space number. void set_tag(char const *tag, int spacenum) { finder_base::set_tag(tag); m_spacenum = spacenum; } /// \brief Set search tag and space number /// /// Allows search tag and address space number to be changed after /// construction. Note that this must be done before resolution /// time to take effect. Also note that the tag is not copied. /// \param [in] finder Object finder to take the search base and tag /// from. /// \param [in] spacenum Address space number. void set_tag(finder_base const &finder, int spacenum) { finder_base::set_tag(finder); this->m_spacenum = spacenum; } /// \brief Set search tag and space number /// /// Allows search tag and address space number to be changed after /// construction. Note that this must be done before resolution /// time to take effect. Also note that the tag is not copied. /// \param [in] finder Address space finder to take the search base, /// tag and address space number from. template void set_tag(address_space_finder const &finder) { set_tag(finder, finder.spacenum()); } /// \brief Set data width of space /// /// Allows data width to be specified after construction. Note /// that this must be done before resolution time to take effect. /// \param [in] width Required data width in bits, or zero if any /// data width is acceptable. void set_data_width(u8 width) { assert(!this->m_resolved); this->m_data_width = width; } /// \brief Get space number /// /// Returns the configured address space number. /// \return The space number to be found. int spacenum() const { return m_spacenum; } private: /// \brief Find address space /// /// Find address space with requested tag. For a dry run, the /// target object pointer will not be set. This method is called by /// the base device at resolution time. /// \param [in] valid Pass a pointer to the validity checker if this /// is a dry run (i.e. no intention to actually start the device), /// or nullptr otherwise. /// \return True if the address space is optional, a matching /// address space is found or this is a dry run, false otherwise. virtual bool findit(validity_checker *valid) override; int m_spacenum; u8 m_data_width; }; /// \brief Optional address space finder /// /// Finds address space with matching tag and number. No error is /// generated if a matching address space is not found (the target /// object pointer will be null). /// \sa required_address_space address_space_finder using optional_address_space = address_space_finder; /// \brief Required address space finder /// /// Finds address space with matching tag and number. A validation /// error is generated if a matching address space is not found. /// \sa optional_address_space address_space_finder using required_address_space = address_space_finder; /// \brief Memory region base pointer finder /// /// Template arguments are the element type of the memory region and /// whether the memory region is required. It is a validation error if /// a required memory region is not found. This class is generally not /// used directly, instead the optional_region_ptr and /// required_region_ptr helpers are used. /// \sa optional_region_ptr required_region_ptr template class region_ptr_finder : public object_finder_base { public: /// \brief Memory region base pointer finder constructor /// /// Desired width is implied by sizeof(PointerType). /// \param [in] base Base device to search from. /// \param [in] tag Memory region tag to search for. This is not /// copied, it is the caller's responsibility to ensure this /// pointer remains valid until resolution time. region_ptr_finder(device_t &base, char const *tag) : object_finder_base(base, tag) , m_length(0) { } /// \brief Array access operator /// /// Returns a non-const reference to the element of the memory /// region at the supplied zero-based index. Behaviour is undefined /// for negative element indices. /// \param [in] index Non-negative element index. /// \return Non-const reference to element at requested index. PointerType &operator[](int index) const { assert(index < m_length); return this->m_target[index]; } /// \brief Get length in units of elements /// /// \return Length in units of elements or zero if no matching /// memory region has been found. size_t length() const { return m_length; } /// \brief Get length in units of bytes /// /// \return Length in units of bytes or zero if no matching memory /// region has been found. size_t bytes() const { return m_length * sizeof(PointerType); } private: /// \brief Find memory region base pointer /// /// Find base pointer of memory region with with requested tag, /// width and length. Width of memory region is checked against /// sizeof(PointerType). For a dry run, only the tag and length are /// checked - the width is not checked and the target pointer is not /// set. This method is called by the base device at resolution /// time. /// \param [in] valid Pass a pointer to the validity checker if this /// is a dry run (i.e. no intention to actually start the device), /// or nullptr otherwise. /// \return True if the memory region is optional or a matching /// memory region is found, or false otherwise. virtual bool findit(validity_checker *valid) override { if (valid) return this->validate_memregion(Required); assert(!this->m_resolved); this->m_resolved = true; this->m_target = reinterpret_cast(this->find_memregion(sizeof(PointerType), m_length, Required)); return this->report_missing("memory region"); } /// \brief Region length /// /// Actual length of the region that was found in units of /// elements, or zero if no matching region has been found. size_t m_length; }; /// \brief Optional memory region base pointer finder /// /// Finds base pointer of memory region with matching tag, width and /// length. No error is generated if a matching memory region is not /// found (the target pointer will be null). If you have a number of /// similar optional memory regions, consider using /// optional_region_ptr_array. /// \sa required_region_ptr optional_region_ptr_array region_ptr_finder template using optional_region_ptr = region_ptr_finder; /// \brief Required memory region base pointer finder /// /// Finds base pointer of memory region with matching tag, width and /// length. A validation error is generated if a matching memory region /// is not found. If you have a number of similar required memory /// regions, consider using required_region_ptr_array. /// \sa optional_region_ptr required_region_ptr_array region_ptr_finder template using required_region_ptr = region_ptr_finder; template using region_ptr_array_finder = object_array_finder, Count>; template using optional_region_ptr_array = region_ptr_array_finder; template using required_region_ptr_array = region_ptr_array_finder; /// \brief Memory share base pointer finder /// /// Template arguments are the element type of the memory share and /// whether the memory share is required. It is a validation error if a /// required memory share is not found. This class is generally not /// used directly, instead the optional_shared_ptr and /// required_shared_ptr helpers are used. /// \sa optional_shared_ptr required_shared_ptr template class shared_ptr_finder : public object_finder_base { public: /// \brief Memory share finder constructor /// /// Desired width is implied by sizeof(PointerType). /// \param [in] base Base device to search from. /// \param [in] tag Memory share tag to search for. This is not /// copied, it is the caller's responsibility to ensure this /// pointer remains valid until resolution time. shared_ptr_finder(device_t &base, char const *tag) : object_finder_base(base, tag) , m_bytes(0) { } /// \brief Array access operator /// /// Returns a non-const reference to the element of the memory /// share at the supplied zero-based index. Behaviour is undefined /// for negative element indices. /// \param [in] index Non-negative element index. /// \return Non-const reference to element at requested index. PointerType &operator[](int index) const { return this->m_target[index]; } /// \brief Get length in units of elements /// /// \return Length in units of elements or zero if no matching /// memory region has been found. size_t length() const { return m_bytes / sizeof(PointerType); } /// \brief Get length in bytes /// /// \return Length in bytes or zero if no matching memory share has /// been found. size_t bytes() const { return m_bytes; } /// \brief Get iterator to first element /// /// Returns an iterator to the first element of the memory share. /// \return Iterator to first element. PointerType *begin() const { return this->m_target; } /// \brief Get iterator beyond last element /// /// Returns an iterator one past the last element of the memory /// share. /// \return Iterator one past last element. PointerType *end() const { return this->m_target + length(); } /// \brief Get constant iterator to first element /// /// Returns a constant iterator to the first element of the memory /// share. /// \return Constant iterator to first element. PointerType const *cbegin() const { return this->m_target; } /// \brief Get constant iterator beyond last element /// /// Returns a constant iterator one past the last element of the /// memory share. /// \return Constant iterator one past last element. PointerType const *cend() const { return this->m_target + length(); } private: /// \brief Find memory share base pointer /// /// Find base pointer of memory share with with requested tag. /// Width of memory share is checked against sizeof(PointerType). /// This method is called by the base device at resolution time. /// \param [in] valid Pass a pointer to the validity checker if this /// is a dry run (i.e. no intention to actually start the device), /// or nullptr otherwise. /// \return True if the memory share is optional or a matching /// memory share is found, or false otherwise. virtual bool findit(validity_checker *valid) override { if (valid) return true; assert(!this->m_resolved); this->m_resolved = true; this->m_target = reinterpret_cast(this->find_memshare(sizeof(PointerType)*8, m_bytes, Required)); return this->report_missing("shared pointer"); } /// \brief Memory share length /// /// Actual length of the memory share that was found in bytes, or /// zero if no matching region has been found. size_t m_bytes; }; /// \brief Optional memory share base pointer finder /// /// Finds base pointer of memory share with matching tag and width. No /// error is generated if a matching memory share is not found (the /// target pointer will be null). If you have a number of similar /// optional memory regions, consider using optional_shared_ptr_array. /// \sa required_shared_ptr optional_shared_ptr_array shared_ptr_finder template using optional_shared_ptr = shared_ptr_finder; /// \brief Required memory share base pointer finder /// /// Finds base pointer of memory share with matching tag and width. A /// validation error is generated if a matching memory share is not /// found. If you have a number of similar required memory shares, /// consider using required_shared_ptr_array. /// \sa optional_shared_ptr required_shared_ptr_array shared_ptr_finder template using required_shared_ptr = shared_ptr_finder; template using shared_ptr_array_finder = object_array_finder, Count>; template using optional_shared_ptr_array = shared_ptr_array_finder; template using required_shared_ptr_array = shared_ptr_array_finder; /// \brief Memory share creator template /// /// Creates a memory share or finds an existing one instantiated via an /// address map. Template argument is the element type of the memory /// share. If an existing memory share is found, it is an error if it /// doesn't match the requested width, length and endianness. template class memory_share_creator : public finder_base { public: /// \brief Memory share creator constructor /// /// Desired width is implied by sizeof(PointerType). /// \param [in] base Base device to search from. /// \param [in] tag Memory share tag to search for or create. This /// is not copied, it is the caller's responsibility to ensure /// this pointer remains valid until resolution time. /// \param [in] bytes Desired memory share length in bytes. /// \param [in] endianness Desired endianness of the memory share. memory_share_creator(device_t &base, char const *tag, size_t bytes, endianness_t endianness); /// \brief Get target memory share base pointer /// /// Must not be called before creation is attempted. /// \return Base pointer of found or created memory share. PointerType *target() const { return reinterpret_cast(m_target->ptr()); } /// \brief Cast-to-pointer operator /// /// Allows implicit casting to the base pointer of the target memory /// share. Must not be called before creation is attempted. /// \return Base pointer of found or created memory share. operator PointerType *() const { return target(); } /// \brief Array access operator /// /// Returns a non-const reference to the element of the memory /// share at the supplied zero-based index. Behaviour is undefined /// for negative element indices. Must not be called before /// creation is attempted. /// \param [in] index Non-negative element index. /// \return Non-const reference to element at requested index. PointerType &operator[](int index) const { return target()[index]; } /// \brief Get length in units of elements /// /// Must not be called before creation is attempted. /// \return Length of memory share in units of elements. size_t length() const { return m_target->bytes() / sizeof(PointerType); } /// \brief Get length in bytes /// /// Must not be called before creation is attempted. /// \return Length of memory share in bytes. size_t bytes() const { return m_target->bytes(); } /// \brief Get endianness /// /// Must not be called before creation is attempted. /// \return Endianness of memory share. endianness_t endianness() const { return m_target->endianness(); } /// \brief Get width in bits /// /// Must not be called before creation is attempted. /// \return Memory share width in bits. u8 bitwidth() const { return m_target->bitwidth(); } /// \brief Get width in bytes /// /// Must not be called before creation is attempted. /// \return Memory share width in bytes. u8 bytewidth() const { return m_target->bytewidth(); } /// \brief Get iterator to first element /// /// Returns an iterator to the first element of the memory share. /// Must not be called before creation is attempted. /// \return Iterator to first element. PointerType *begin() const { return target(); } /// \brief Get iterator beyond last element /// /// Returns an iterator one past the last element of the memory /// share. Must not be called before creation is attempted. /// \return Iterator one past last element. PointerType *end() const { return target() + length(); } /// \brief Get constant iterator to first element /// /// Returns a constant iterator to the first element of the memory /// share. Must not be called before creation is attempted. /// \return Constant iterator to first element. PointerType const *cbegin() const { return target(); } /// \brief Get constant iterator beyond last element /// /// Returns a constant iterator one past the last element of the /// memory share. Must not be called before creation is attempted. /// \return Constant iterator one past last element. PointerType const *cend() const { return target() + length(); } protected: virtual bool findit(validity_checker *valid) override; virtual void end_configuration() override; /// \brief Pointer to target object /// /// Pointer to target memory share object, or nullptr if creation /// has not been attempted yet. memory_share *m_target = nullptr; /// \brief Requested memory share width in bits u8 const m_width; /// \brief Requested memory share length in bytes size_t const m_bytes; /// \brief Requested memory share endianness endianness_t const m_endianness; }; template using memory_share_array_creator = object_array_finder, Count>; //************************************************************************** // EXTERNAL TEMPLATE INSTANTIATIONS //************************************************************************** extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class object_finder_base; extern template class memory_region_finder; extern template class memory_region_finder; extern template class memory_bank_finder; extern template class memory_bank_finder; extern template class ioport_finder; extern template class ioport_finder; extern template class address_space_finder; extern template class address_space_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class region_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class shared_ptr_finder; extern template class memory_share_creator; extern template class memory_share_creator; extern template class memory_share_creator; extern template class memory_share_creator; extern template class memory_share_creator; extern template class memory_share_creator; extern template class memory_share_creator; extern template class memory_share_creator; #endif // MAME_EMU_DEVFIND_H /// \}