MAME Object Finders: Reliable Device References and Optional Hardware
Use MAME object finders to connect driver devices, resolve relative tags, validate required resources, and handle optional hardware safely.
MAME object finders are the typed links that let a driver or device locate other emulated components and resources by tag. They make dependencies visible in the class definition, give MAME a chance to validate required objects, and reduce fragile repeated lookups by string. A missing required sound CPU should be a configuration error, not a null pointer discovered during gameplay. A genuinely optional subdevice should have explicit fallback behavior rather than being disguised as required.
This guide focuses on C++ object finders in current MAME, not Lua inspection of a running device tree or the mapping of host controls to emulated I/O ports. The distinctions matter: a C++ driver finder participates in configuration and validation, while a Lua query or a raw I/O tag serves a different interface.
Tags are relative to a base device
An object finder searches for a target by tag relative to a base device. A tag such as "maincpu" does not always mean the same absolute machine path. On a root driver state it may resolve to the root machine’s CPU. Inside an expansion-card device, the same child tag may identify that card’s CPU. The full path includes parent tags, while the finder stores a base device plus a relative tag.
This is why an unexplained mismatch can occur even when a string appears in a driver file. Start from the finder declaration and constructor: identify the base device, the relative tag, and the place where the target is added to the machine configuration. Then check whether the target exists in every machine variant that uses that state class. Do not copy a tag from another driver and assume its owner or hierarchy is identical.
MAME provides object finders for devices, memory regions, memory banks, I/O ports, address spaces, memory pointers, shared memory, and outputs. The types are not interchangeable. A required_device<T> should point to a class derived from device_t or device_interface; a required address-space finder also needs an address-space number. Use the finder type that represents the resource you actually need.
Choose required or optional deliberately
Required finders express invariants. If the target is absent, MAME reports an error during validation or prevents the device from starting. That is appropriate for a CPU, mandatory screen, or required bus resource. A required finder catches the error near the configuration boundary instead of allowing later code to dereference an invalid pointer.
Optional finders express real variation. A family of related machines may have a second sound CPU only on some board revisions, or a device may use an optional input port. MAME’s optional finder exposes found() and an explicit Boolean conversion so code can test before use. It cannot tell you whether the target is absent by design or absent because someone misspelled the tag. Treat optionality as a documented hardware property, not a way to silence a configuration failure.
For a driver state, the declaration and constructor initialization can look like this excerpt:
required_device<z80_device> m_maincpu;
optional_device<z80_device> m_soundcpu;
// In the state constructor initializer list:
m_maincpu(*this, "maincpu"),
m_soundcpu(*this, "audiocpu")
The first finder states that the main processor must exist. The second allows a machine variant to omit the audio CPU. Use the optional target only after a positive check:
if (m_soundcpu.found())
m_soundcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
The snippet follows the current MAME Object Finders documentation’s device-finder pattern. It is an excerpt, not a complete driver translation unit: the class declaration, device registration, includes, and machine configuration must match the target driver. Avoid calling operator-> on an optional finder before testing it, because that assumes the target exists.
Keep configuration and access linked
When a device finder is also used to instantiate a child in device_add_mconfig, use the same finder where practical. MAME’s current documentation demonstrates passing an object finder to a device-creation macro and then using it to configure a connection. This avoids maintaining two independent strings for the same target.
For example, an address-bus device can retain finders for its host CPU and address space, then have its machine-configuration function add the CPU, address-space device, and bus. The finder can be passed into the creation macro and later used to configure the bus connection. If the target is renamed, there is one declaration to update rather than a construction string in one method and an unrelated runtime lookup in another.
There are legitimate cases where a child device does not know its eventual host when constructed. MAME supports initializing a finder with a guaranteed-invalid dummy tag and setting the target later through a configuration member function. The system should detect the incomplete configuration rather than silently resolving an accidental default. Use this pattern only when the device is intentionally reusable across different host configurations, and validate that every machine variant sets the required target.
The finder retains its tag pointer rather than copying arbitrary strings. String literals are safe for the usual static driver declarations. If a tag is assembled dynamically, its storage must remain valid until finder validation and resolution finish. Do not pass a temporary string and assume the finder owns it.
Use arrays for regular hardware families
Repeated resources such as keyboard rows, tilemap layers, or identical subdevices can use finder arrays. A tag pattern and index offset make the intended set explicit. For example, the current documentation shows an array declared with the pattern "ROW%u" and offset 0U, resolving tags ROW0 through ROW9.
The index offset is part of the hardware naming convention. Some real boards label their first component 1, not 0; an offset of 1U can resolve bg1, bg2, while C++ array indexes remain zero-based. Use a fixed-size array only when the required hardware count is invariant. If machine variants differ, use an optional array or separate derived state classes and make the presence behavior clear.
For non-sequential names, the documentation supports an explicit list of tags. That is preferable to clever arithmetic when labels encode board functions rather than an index. Keep the declaration near the consumer and add a comment only when the name mapping is not obvious from the schematic or device definition.
Validate at the right boundary
Run MAME’s -validate command after changing driver configuration or finder tags, then start each affected machine and variant. Validation can detect missing required references in configurations, but it cannot prove that the chosen component type, address map, interrupt wiring, or hardware behavior is correct. A machine can pass structural validation and still emulate the wrong bus transaction.
Use a focused acceptance matrix:
- Run
mame -validateand preserve errors with the MAME version and build type. - Inspect the machine’s XML or source definition to confirm expected device count, tags, and configuration options.
- Launch the base machine and every clone or optional-hardware variant using the changed state class.
- Confirm required finder failures are fatal and descriptive by using a temporary test configuration, not a production driver edit.
- Verify each optional path both when the target is present and when it is absent; the absent case should follow an intentional behavior.
- Exercise the actual I/O, memory, or timing path that uses the finder and compare against a known reference or board evidence.
Keep logs free of ambiguous fallback behavior. If an optional input port is absent, use a documented safe default. If a required CPU or bus device is absent, fail early. Do not convert every finder to optional merely to make a clone boot; that can hide a driver configuration regression and silently disable hardware behavior.
Make dependencies maintainable
An object finder declaration is a compact architecture record. It names the resource, whether it is mandatory, and how the code expects to use it. That record is strongest when the tag corresponds to a real driver or device configuration and when validation covers the full family of machines that share the implementation.
When adding a new device, record its expected owner, relative tag, interface type, and whether absence is valid. When changing a tag, update every machine configuration, review derived variants, run -validate, and test startup and the code path that consumes the resource. This keeps string-based topology from becoming invisible technical debt.
The payoff is practical: required hardware fails at the boundary, optional hardware is handled deliberately, and a maintainer can understand the machine graph without searching every method for ad hoc name lookups. That is the difference between a driver that merely starts and one whose dependency structure can be checked and preserved.
Related:
- MAME I/O Ports: Mapping Host Controls to Emulated Hardware Inputs
- MAME ROM Regions: Mapping Preserved Chips into Emulated Hardware
Sources: