summaryrefslogtreecommitdiffstatshomepage
path: root/docs/source/techspecs/object_finders.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/source/techspecs/object_finders.rst')
-rw-r--r--docs/source/techspecs/object_finders.rst249
1 files changed, 236 insertions, 13 deletions
diff --git a/docs/source/techspecs/object_finders.rst b/docs/source/techspecs/object_finders.rst
index 3fd8be922fd..1acb475a7fa 100644
--- a/docs/source/techspecs/object_finders.rst
+++ b/docs/source/techspecs/object_finders.rst
@@ -73,7 +73,9 @@ its own child devices, inputs and ROM region. The code samples here are based
on the Apple II Parallel Printer Interface card, but a lot of things have been
removed for clarity.
-Object finders are declared as members of the device class::
+Object finders are declared as members of the device class:
+
+.. code-block:: C++
class a2bus_parprn_device : public device_t, public device_a2bus_card_interface
{
@@ -98,7 +100,9 @@ Object finders are declared as members of the device class::
We want to find a ``centronics_device``, an ``output_latch_device``, an I/O
port, and an 8-bit memory region.
-In the constructor, we set the initial target for the object finders::
+In the constructor, we set the initial target for the object finders:
+
+.. code-block:: C++
a2bus_parprn_device::a2bus_parprn_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
device_t(mconfig, A2BUS_PARPRN, tag, owner, clock),
@@ -121,7 +125,9 @@ ensure the tag string remains valid until after validation and/or object
resolution is complete.
The memory region and I/O port come from the ROM definition and input
-definition, respectively::
+definition, respectively:
+
+.. code-block:: C++
namespace {
@@ -164,7 +170,9 @@ Note that the tags ``"prom"`` and ``"CFG"`` match the tags passed to the object
finders on construction.
Child devices are instantiated in the device’s machine configuration member
-function::
+function:
+
+.. code-block:: C++
void a2bus_parprn_device::device_add_mconfig(machine_config &config)
{
@@ -183,7 +191,9 @@ its base device must be the same as the device being configured (the ``this``
pointer of the machine configuration member function).
After the emulated machine has been started, the object finders can be used in
-much the same way as pointers::
+much the same way as pointers:
+
+.. code-block:: C++
void a2bus_parprn_device::write_c0nx(u8 offset, u8 data)
{
@@ -209,7 +219,9 @@ Connections between devices
Devices need to be connected together within a system. For example the Sun SBus
device needs access to the host CPU and address space. Here’s how we declare
-the object finders in the device class (with all distractions removed)::
+the object finders in the device class (with all distractions removed):
+
+.. code-block:: C++
DECLARE_DEVICE_TYPE(SBUS, sbus_device)
@@ -346,7 +358,9 @@ finder array types are provided. The object finder array type names have
| memory_share_creator | memory_share_array_creator |
+------------------------+------------------------------+
-A common case for an object array finder is a key matrix::
+A common case for an object array finder is a key matrix:
+
+.. code-block:: C++
class keyboard_base : public device_t, public device_mac_keyboard_interface
{
@@ -383,7 +397,9 @@ underlying object finder type. It supports indexing, iterators, and range-based
``for`` loops.
Because an index offset is specified, the tags don’t need to use zero-based
-indices. It’s common to use one-based indexing like this::
+indices. It’s common to use one-based indexing like this:
+
+.. code-block:: C++
class dooyong_state : public driver_device
{
@@ -404,7 +420,9 @@ This causes ``m_bg`` to find devices with tags ``bg1`` and ``bg2``, while
into the object finder arrays are still zero-based like any other C array.
It’s also possible to other format conversions, like hexadecimal (``%x`` and
-``%X``) or character (``%c``)::
+``%X``) or character (``%c``):
+
+.. code-block:: C++
class eurit_state : public driver_device
{
@@ -423,7 +441,9 @@ In this case, the key matrix ports use tags ``KEYA``, ``KEYB``, ``KEYC``,
``KEYD`` and ``KEYE``.
When the tags don’t follow a simple ascending sequence, you can supply a
-brace-enclosed initialiser list of tags::
+brace-enclosed initialiser list of tags:
+
+.. code-block:: C++
class seabattl_state : public driver_device
{
@@ -440,20 +460,223 @@ brace-enclosed initialiser list of tags::
If the underlying object finders require additional constructor arguments,
supply them after the tag format and index offset (the same values will be used
-for all elements of the array)::
+for all elements of the array):
+
+.. code-block:: C++
class dreamwld_state : public driver_device
{
public:
- dreamwld_state(const machine_config &mconfig, device_type type, const char *tag) :
+ dreamwld_state(machine_config const &mconfig, device_type type, char const *tag) :
driver_device(mconfig, type, tag),
m_vram(*this, "vram_%u", 0U, 0x2000U, ENDIANNESS_BIG)
{
}
private:
- memory_share_array_creator<u16, 2> m_vram;
+ memory_share_array_creator<u16, 2> m_vram;
};
This finds or creates memory shares with tags ``vram_0`` and ``vram_1``, each of
of which is 8 KiB organised as 4,096 big-Endian 16-bit words.
+
+
+Optional object finders
+-----------------------
+
+Optional object finders don’t raise an error if the target object isn’t found.
+This is useful in two situations: ``driver_device`` implementations (state
+classes) representing a family of systems where some components aren’t present
+in all configurations, and devices that can optionally use a resource. Optional
+object finders provide additional member functions for testing whether the
+target object was found.
+
+Optional system components
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Often a class is used to represent a family of related systems. If a component
+isn’t present in all configurations, it may be convenient to use an optional
+object finder to access it. We’ll use the Sega X-board device as an example:
+
+.. code-block:: C++
+
+ class segaxbd_state : public device_t
+ {
+ protected:
+ segaxbd_state(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, type, tag, owner, clock),
+ m_soundcpu(*this, "soundcpu"),
+ m_soundcpu2(*this, "soundcpu2"),
+ m_segaic16vid(*this, "segaic16vid"),
+ m_pc_0(0),
+ m_lastsurv_mux(0),
+ m_adc_ports(*this, "ADC%u", 0),
+ m_mux_ports(*this, "MUX%u", 0)
+ {
+ }
+
+ optional_device<z80_device> m_soundcpu;
+ optional_device<z80_device> m_soundcpu2;
+ required_device<mb3773_device> m_watchdog;
+ required_device<segaic16_video_device> m_segaic16vid;
+ bool m_adc_reverse[8];
+ u8 m_pc_0;
+ u8 m_lastsurv_mux;
+ optional_ioport_array<8> m_adc_ports;
+ optional_ioport_array<4> m_mux_ports;
+ };
+
+The ``optional_device`` and ``optional_ioport_array`` members are declared and
+constructed in the usual way. Before accessing the target object, we call an
+object finder’s ``found()`` member function to check whether it’s present in the
+system (the explicit cast-to-Boolean operator can be used for the same purpose):
+
+.. code-block:: C++
+
+ void segaxbd_state::pc_0_w(u8 data)
+ {
+ m_pc_0 = data;
+
+ m_watchdog->write_line_ck(BIT(data, 6));
+
+ m_segaic16vid->set_display_enable(data & 0x20);
+
+ if (m_soundcpu.found())
+ m_soundcpu->set_input_line(INPUT_LINE_RESET, (data & 0x01) ? CLEAR_LINE : ASSERT_LINE);
+ if (m_soundcpu2.found())
+ m_soundcpu2->set_input_line(INPUT_LINE_RESET, (data & 0x01) ? CLEAR_LINE : ASSERT_LINE);
+ }
+
+Optional I/O ports provide a convenience member function called ``read_safe``
+that reads the port value if present, or returns the supplied default value
+otherwise:
+
+.. code-block:: C++
+
+ u8 segaxbd_state::analog_r()
+ {
+ int const which = (m_pc_0 >> 2) & 7;
+ u8 value = m_adc_ports[which].read_safe(0x10);
+
+ if (m_adc_reverse[which])
+ value = 255 - value;
+
+ return value;
+ }
+
+ uint8_t segaxbd_state::lastsurv_port_r()
+ {
+ return m_mux_ports[m_lastsurv_mux].read_safe(0xff);
+ }
+
+The ADC ports return 0x10 (16 decimal) if they are not present, while the
+multiplexed digital ports return 0xff (255 decimal) if they are not present.
+Note that ``read_safe`` is a member of the ``optional_ioport`` itself, and not
+a member of the target ``ioport_port`` object (the ``optional_ioport`` is not
+dereferenced when using it).
+
+There are some disadvantages to using optional object finders:
+
+* There’s no way to distinguish between the target not being present, and the
+ target not being found due to mismatched tags, making it more error-prone.
+* Checking whether the target is present may use CPU branch prediction
+ resources, potentially hurting performance if it happens very frequently.
+
+Consider whether optional object finders are the best solution, or whether
+creating a derived class for the system with additional components is more
+appropriate.
+
+Optional resources
+~~~~~~~~~~~~~~~~~~
+
+Some devices can optionally use certain resources. If the host system doesn’t
+supply them, the device will still function, although some functionality may not
+be available. For example, the Virtual Boy cartridge slot responds to three
+address spaces, called EXP, CHIP and ROM. If the host system will never use one
+or more of them, it doesn’t need to supply a place for the cartridge to install
+the corresponding handlers. (For example a copier may only use the ROM space.)
+
+Let’s look at how this is implemented. The Virtual Boy cartridge slot device
+declares ``optional_address_space`` members for the three address spaces,
+``offs_t`` members for the base addresses in these spaces, and inline member
+functions for configuring them:
+
+.. code-block:: C++
+
+ class vboy_cart_slot_device :
+ public device_t,
+ public device_image_interface,
+ public device_single_card_slot_interface<device_vboy_cart_interface>
+ {
+ public:
+ vboy_cart_slot_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0U);
+
+ template <typename T> void set_exp(T &&tag, int no, offs_t base)
+ {
+ m_exp_space.set_tag(std::forward<T>(tag), no);
+ m_exp_base = base;
+ }
+ template <typename T> void set_chip(T &&tag, int no, offs_t base)
+ {
+ m_chip_space.set_tag(std::forward<T>(tag), no);
+ m_chip_base = base;
+ }
+ template <typename T> void set_rom(T &&tag, int no, offs_t base)
+ {
+ m_rom_space.set_tag(std::forward<T>(tag), no);
+ m_rom_base = base;
+ }
+
+ protected:
+ virtual void device_start() override;
+
+ private:
+ optional_address_space m_exp_space;
+ optional_address_space m_chip_space;
+ optional_address_space m_rom_space;
+ offs_t m_exp_base;
+ offs_t m_chip_base;
+ offs_t m_rom_base;
+
+ device_vboy_cart_interface *m_cart;
+ };
+
+ DECLARE_DEVICE_TYPE(VBOY_CART_SLOT, vboy_cart_slot_device)
+
+The object finders are constructed with dummy values for the tags and space
+numbers (``finder_base::DUMMY_TAG`` and -1):
+
+.. code-block:: C++
+
+ vboy_cart_slot_device::vboy_cart_slot_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, VBOY_CART_SLOT, tag, owner, clock),
+ device_image_interface(mconfig, *this),
+ device_single_card_slot_interface<device_vboy_cart_interface>(mconfig, *this),
+ m_exp_space(*this, finder_base::DUMMY_TAG, -1, 32),
+ m_chip_space(*this, finder_base::DUMMY_TAG, -1, 32),
+ m_rom_space(*this, finder_base::DUMMY_TAG, -1, 32),
+ m_exp_base(0U),
+ m_chip_base(0U),
+ m_rom_base(0U),
+ m_cart(nullptr)
+ {
+ }
+
+To help detect configuration errors, we’ll check for cases where address spaces
+have been configured but aren’t present:
+
+.. code-block:: C++
+
+ void vboy_cart_slot_device::device_start()
+ {
+ if (!m_exp_space && ((m_exp_space.finder_tag() != finder_base::DUMMY_TAG) || (m_exp_space.spacenum() >= 0)))
+ throw emu_fatalerror("%s: Address space %d of device %s not found (EXP)\n", tag(), m_exp_space.spacenum(), m_exp_space.finder_tag());
+
+ if (!m_chip_space && ((m_chip_space.finder_tag() != finder_base::DUMMY_TAG) || (m_chip_space.spacenum() >= 0)))
+ throw emu_fatalerror("%s: Address space %d of device %s not found (CHIP)\n", tag(), m_chip_space.spacenum(), m_chip_space.finder_tag());
+
+ if (!m_rom_space && ((m_rom_space.finder_tag() != finder_base::DUMMY_TAG) || (m_rom_space.spacenum() >= 0)))
+ throw emu_fatalerror("%s: Address space %d of device %s not found (ROM)\n", tag(), m_rom_space.spacenum(), m_rom_space.finder_tag());
+
+ m_cart = get_card_device();
+ }