MAME Output Items: Exporting Cabinet Lamps, Counters, and State
Inspect MAME cabinet outputs through named items, separating them from screens, inputs, save data, physical controls, and Lua integration.
An arcade machine can expose state beyond its rendered pixels: a coin counter advances, a lockout changes, a lamp lights, or a mechanical device moves. MAME’s output system gives machine drivers a path to publish integer-valued output items and gives host-side consumers a way to observe those values. That is a different problem from rendering multiple monitors, binding a physical button, or drawing artwork in a layout.
Treat an output item as a named signal at an emulated-machine boundary. The driver owns the meaning and the update points. The output manager owns item lookup and notification. A frontend, script, cabinet bridge, or external output module may consume values according to its own policy. The output API does not standardize the electrical meaning, polarity, persistence, or update frequency of every signal. Those semantics must be verified in the driver and for the exact machine variant.
The output manager is not the video renderer
MAME’s output manager stores named values and notifies registered callbacks when a value changes. The value type is an integer. Some devices publish binary states, while others use counters or numeric levels. The same output subsystem is also used by UI-facing or externally consumed signals, but a named output is not automatically a rendered screen or an artwork element.
The number of display windows is configured separately. A multi-screen machine can have several emulated screens while exposing zero or more cabinet outputs. Conversely, a one-screen game can publish lamps, counters, or motors. A layout may visualize a signal, but the layout is a consumer and presentation layer; it is not the source of the emulated value.
The same distinction separates outputs from inputs. A coin switch pressed by a player is an input event mapped into an emulated port. A coin meter pulse is an output produced by the driver in response to emulated logic. Conflating them can create feedback loops in a physical cabinet or cause an integration to count host button presses rather than the game’s meter pulses.
Follow the signal from device to consumer
When investigating a signal, start in the machine driver or device implementation. Search for the output name, output finder declarations, or calls that update an output item. Then identify which emulated write or state transition reaches that update. A coin counter may increment on a rising edge, a lamp may use active-low hardware semantics but be exported as an on/off value, and an analog mechanism may publish a position that is not a calibrated physical unit.
MAME’s output implementation distinguishes finding an existing item from creating one. A device output proxy looks up a name relative to a device and can report whether the item exists. The Lua output manager can set an output by name; that operation may create an item if none exists. These are not equivalent operations. A diagnostic script that creates a typo in a name can observe its own new value and falsely suggest that the driver publishes the expected signal.
Use the driver’s own name and device tag. Do not infer a naming convention from another game. Machine revisions can implement a signal differently, and not every output name is guaranteed to exist for every configuration. Feature-detect before reading a proxy, and report a missing signal as missing rather than silently treating zero as proof that the hardware is off.
Observe before controlling
Start with read-only inspection. The core Lua API documents a device-relative output proxy with existence, name, and current-value methods. A schematic observer looks like this:
local device = manager.machine.devices[device_tag]
local signal = device:output(output_name)
if signal:exists() then
print(signal:name(), signal:get())
else
print("driver does not expose this output")
end
Replace device_tag and output_name with values verified in the selected machine’s source or runtime. This snippet observes one device-relative output; it does not enumerate all machine signals, guarantee that a given name exists, or control a physical cabinet. If the Lua binding or MAME version differs, check that build’s API reference before adapting it.
A useful trace records the machine short name, parent and clone relationship, MAME version, output name, device tag, prior value, new value, emulated time, and the input or game action that caused the transition. Capture a controlled sequence: start from reset, trigger one coin, release the switch, and inspect the meter. If a lamp flickers during a frame, log transitions rather than sampling once per second.
Values, edges, and counters need different interpretation
An integer value does not say whether the value is a Boolean, a monotonically increasing counter, a pulse, or an analog quantity. A consumer must not normalize every nonzero value to on/off until the source semantics are known. For a meter, an edge or delta may matter more than the current absolute count. For a solenoid or motor, a sustained nonzero value may need a safety timeout at the physical output layer even though the emulated value remains active.
If an output is edge-triggered, define how the consumer handles initialization and reset. On startup, the first observed value may already be nonzero. Treating that initial read as a new pulse could actuate a real device unexpectedly. A robust adapter distinguishes initial synchronization from subsequent changes and can force outputs to a safe state on pause, exit, or communication failure.
Output callbacks are notifications, not a license to block the emulation thread. Keep callbacks short. Queue physical I/O to a separate bounded worker if the cabinet interface can stall, and preserve the newest state for level signals. For pulse counters, use a queue or counter that cannot silently collapse several events into one. Document what happens on overflow, reset, save-state load, and fast-forward.
Save states and observers
MAME’s output manager participates in save-state registration and post-load processing. That keeps observable output values aligned with the machine state, but an external cabinet controller has its own state. After loading a save, a consumer may see a transition that reflects restoration rather than a new gameplay event. Decide whether the bridge should resynchronize to the restored value or suppress edge actions during the load boundary.
This is particularly important for counters and actuators. A counter output can move backward after loading a state. An external meter cannot necessarily move backward, so a physical integration may need a monotonic accounting policy separate from the emulated counter. A motor output restored to active may require an explicit safety review before reactivation.
Do not treat an output as a durable event log. The output manager exposes current values and notifications. If audit history matters, record it in the consumer with timestamps and source identity. The machine’s current item value is not enough to reconstruct every intermediate edge if an observer was disconnected.
Build a stable cabinet adapter
A cabinet adapter should declare a mapping table rather than discover outputs and actuate everything automatically. For each allowed signal, record the machine short name, exact output name, expected value range, whether it is level or edge based, polarity conversion, reset semantics, and safe failure state. Make the mapping opt-in per machine and keep a read-only diagnostic mode.
Before connecting hardware, use a virtual sink that prints value changes and rejects unknown names. Test an active-low mapping with both values, repeated writes of the same value, save-state load, game reset, pause, and emulator exit. Then validate one physical device at a time with power-limited hardware and an emergency stop. MAME’s machine model should never be trusted to provide electrical protection for a host-connected actuator.
Keep outputs and host configuration separate. Do not edit the driver merely to match a cabinet’s wiring if a documented mapping layer can translate the signal safely. If a driver signal itself is wrong, submit a minimal reproduction with a machine short name, source location, input sequence, expected value, observed value, and MAME build. Do not publish copyrighted game data or private cabinet credentials with the report.
Acceptance criteria
A correct integration can explain where a signal originates, what each integer means, whether transitions or levels are important, and how the consumer behaves on startup, disconnect, reset, save-state load, and shutdown. It reads only names confirmed for the selected machine, contains errors for absent items, and cannot create a false positive by accidentally observing a newly created typo. It keeps host device I/O out of callbacks that must remain responsive.
MAME output items are a small interface with large consequences when connected to real hardware. Verify the driver contract, observe the exact runtime values, and put fail-safe behavior in the external bridge. That produces a reliable cabinet integration without confusing display configuration, input mapping, or rendered artwork with machine outputs.
Related:
- MAME Plugin Lifecycle: Packaging Lua Extensions and Measuring Machine Events
- MAME Multi-Screen Output: Emulated Displays, Layouts, and Host Monitors
Sources: