summaryrefslogtreecommitdiffstatshomepage
path: root/docs/source/techspecs
diff options
context:
space:
mode:
Diffstat (limited to 'docs/source/techspecs')
-rw-r--r--docs/source/techspecs/audio_effects.rst147
-rw-r--r--docs/source/techspecs/cpu_device.rst229
-rw-r--r--docs/source/techspecs/device_disasm_interface.rst10
-rw-r--r--docs/source/techspecs/device_memory_interface.rst174
-rw-r--r--docs/source/techspecs/device_rom_interface.rst123
-rw-r--r--docs/source/techspecs/device_sound_interface.rst286
-rw-r--r--docs/source/techspecs/floppy.rst8
-rw-r--r--docs/source/techspecs/index.rst33
-rw-r--r--docs/source/techspecs/inputsystem.rst460
-rw-r--r--docs/source/techspecs/layout_files.rst950
-rw-r--r--docs/source/techspecs/layout_script.rst786
-rw-r--r--docs/source/techspecs/luaengine.rst158
-rw-r--r--docs/source/techspecs/m6502.rst14
-rw-r--r--docs/source/techspecs/memory.rst964
-rw-r--r--docs/source/techspecs/naming.rst95
-rw-r--r--docs/source/techspecs/object_finders.rst1039
-rw-r--r--docs/source/techspecs/osd_audio.rst348
-rw-r--r--docs/source/techspecs/poly_manager.rst1084
-rw-r--r--docs/source/techspecs/uml_instructions.rst1582
19 files changed, 7945 insertions, 545 deletions
diff --git a/docs/source/techspecs/audio_effects.rst b/docs/source/techspecs/audio_effects.rst
new file mode 100644
index 00000000000..12228b97444
--- /dev/null
+++ b/docs/source/techspecs/audio_effects.rst
@@ -0,0 +1,147 @@
+Audio effects
+=============
+
+.. contents:: :local:
+
+
+1. Generalities
+---------------
+
+The audio effects are effects that are applied to the sound between
+the speaker devices and the actual sound output. In the current
+implementation the effect chain is fixed (but not the effect
+parameters), and the parameters are stored in the cfg files. They are
+honestly not really designed for extensibility yet, if ever.
+
+Adding an effect requires working on four parts:
+
+* audio_effects/aeffects.* for effect object creation and "publishing"
+* audio_effects/youreffect.* for the effect implementation
+* frontend/mame/ui/audioeffects.cpp to be able to instantiate the effect configuration menu
+* frontend/mame/ui/audioyoureffect.* to implement the effect configuration menu
+
+2. audio_effects/aeffects.*
+---------------------------
+
+The audio_effect class in the aeffect sources provides three things:
+
+* an enum value to designate the effect type and which must match its
+ position in the chain (iow, the effect chain follows the enum order),
+ in the .h
+* the effect name in the audio_effect::effect_names array in the .cpp
+* the creation of a correct effect object in audio_effect::create in the .cpp
+
+
+
+3. audio_effects/youreffect.*
+-----------------------------
+
+This is where you implement the effect. It takes the shape of an
+audio_effect_youreffect class which derives from audio_effect.
+
+The methods to implement are:
+
+.. code-block:: C++
+
+ audio_effect_youreffect(u32 sample_rate, audio_effect *def);
+
+ virtual int type() const override;
+ virtual void config_load(util::xml::data_node const *ef_node) override;
+ virtual void config_save(util::xml::data_node *ef_node) const override;
+ virtual void default_changed() override;
+ virtual u32 history_size() const; // optional
+
+The constructor must pass the parameters to ``audio_effect`` and
+initialize the effect parameters. ``type`` must return the enum value
+for the effect. ``config_load`` and ``config_save`` should load or
+save the effect parameters from/to the cfg file xml tree.
+``default_changed`` is called when the parameters in ``m_default`` are
+changed and the parameters may need to be updated. ``history_size``
+allows to tell how many samples should still be available of the
+previous input frame. Note that this number must not depend on the
+parameters and only on the sample rate.
+
+An effect has a number of parameters that can come from three sources:
+
+* fixed default value
+* equivalent effect object from the default effect chain
+* user setting through the UI
+
+The first two are recognized through the value of ``m_default`` which
+gets the value of ``def`` in the constructor. When it's nullptr, the
+value to use when not set by the user is the fixed one, otherwise it's
+the one in ``m_default``.
+
+At minimum an effect should have a parameter allowing to bypass it.
+
+Managing a parameter uses four methods:
+
+* ``type param() const;`` returns the current parameter value
+* ``void set_param(type value);`` sets the current parameter value and marks it as set by the user
+* ``bool isset_param() const;`` returns true is the parameter was set by the user
+* ``void reset_param();`` resets the parameter to the default value (from m_default or fixed) and marks it as not set by the user
+
+``config_save`` must only save the user-set parameters.
+``config_load`` must retrieve the parameters that are present and mark
+them as set by the user and reset all the others.
+
+Finally the actual implementation goes into the ``apply`` method:
+
+.. code-block:: C++
+
+ virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) override;
+
+That method takes two buffers with the same number of channels and has
+to apply the effect to ``src`` to produce ``dest``. The
+``output_buffer_flat`` is non-interleaved with independant per-channel
+buffers.
+
+To make bypassing easier, the ``copy(src, dest)`` method of
+audio_effect allows to copy the samples from ``src`` to ``dest``
+without changing them.
+
+The effect application part should looks like:
+
+.. code-block:: C++
+
+ u32 samples = src.available_samples();
+ dest.prepare_space(samples);
+ u32 channels = src.channels();
+
+ // generate channels * samples results and put them in dest
+
+ dest.commit(samples);
+
+To get pointers to the buffers, one uses:
+
+.. code-block:: C++
+
+ const sample_t *source = src.ptrs(channel, source_index); // source_index must be in [-history_size()..samples-1]
+ sample_t *destination = dest.ptrw(channel, destination_index); // destination_index must be in [0..samples-1]
+
+The samples pointed by source and destination are contiguous. The
+number of channels will not change from one apply call to another, the
+number of samples will vary though. Also the call happens in a
+different thread than the main thread and also in a different thread
+than the parameter setting calls are made from.
+
+
+
+
+4. frontend/mame/ui/audioeffects.cpp
+------------------------------------
+
+Here it suffices to add a creation of the menu
+menu_audio_effect_youreffect in menu_audio_effects::handle. The menu
+effect will pick the effect names from audio_effect (in aeffect.*).
+
+
+5. frontend/mame/ui/audioyoureffect.*
+-------------------------------------
+
+This is used to implement the configuration menu for the effect. It's
+a little complicated because it needs to generate the list of
+parameters and their values, set left or right arrow flags depending
+on the possible modifications, dim them (FLAG_INVERT) when not set by
+the user, and manage the arrows and clear keys to change them. Just
+copy an existing one and change it as needed.
diff --git a/docs/source/techspecs/cpu_device.rst b/docs/source/techspecs/cpu_device.rst
new file mode 100644
index 00000000000..c21a20a4fe7
--- /dev/null
+++ b/docs/source/techspecs/cpu_device.rst
@@ -0,0 +1,229 @@
+CPU devices
+===========
+
+.. contents:: :local:
+
+
+1. Overview
+-----------
+
+CPU devices derivatives are used, unsurprisingly, to implement the
+emulation of CPUs, MCUs and SOCs. A CPU device is first a combination
+of ``device_execute_interface``, ``device_memory_interface``,
+``device_state_interface`` and ``device_disasm_interface``. Refer to
+the associated documentations when they exist.
+
+Two more functionalities are specific to CPU devices which are the DRC
+and the interruptibility support.
+
+
+2. DRC
+------
+
+TODO.
+
+
+3. Interruptibility
+-------------------
+
+3.1 Definition
+~~~~~~~~~~~~~~
+
+An interruptible CPU is defined as a core which is able to suspend the
+execution of one instruction at any time, exit execute_run, then at
+the next call of ``execute_run`` keep going from where it was. This
+includes being able to abort an issued memory access, quit
+execute_run, then upon the next call of execute_run reissue the exact
+same access.
+
+
+3.2 Implementation requirements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Memory accesses must be done with ``read_interruptible`` or
+``write_interruptible`` on a ``memory_access_specific`` or a
+``memory_access_cache``. The access must be done as bus width and bus
+alignment.
+
+After each access the core must test whether ``icount <= 0``. This
+test should be done after ``icount`` is decremented of the time taken
+by the access itself, to limit the number of tests. When ``icount``
+reaches 0 or less it means that the instruction emulation needs to be
+suspended.
+
+To know whether the access needs to be re-issued,
+``access_to_be_redone()`` needs to be called. If it returns true then
+the time taken by the access needs to be credited back, since it
+hasn't yet happened, and the access will need to be re-issued. The
+call to ``access_to_be_redone()`` clears the reissue flag. If you
+need to check the flag without clearing it use
+``access_to_be_redone_noclear()``.
+
+The core needs to do enough bookkeeping to eventually restart the
+instruction execution just before the access or just after the test,
+depending on the need of reissue.
+
+Finally, to indicate to the rest of the infrastructure the support, it
+must override cpu_is_interruptible() to return true.
+
+
+3.3 Example implementation with generators
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To ensure decent performance, the current implementations (h8, 6502
+and 68000) use a python generator to generate two versions of each
+instruction interpreter, one for the normal emulation, and one for
+restarting the instruction.
+
+The restarted version looks like that (for a 4-cycles per access cpu):
+
+.. code-block:: C++
+
+ void device::execute_inst_restarted()
+ {
+ switch(m_inst_substate) {
+ case 0:
+ [...]
+
+ m_address = [...];
+ m_mask = [...];
+ [[fallthrough]];
+ case 42:
+ m_result = specific.read_interruptible(m_address, m_mask);
+ m_icount -= 4;
+ if(m_icount <= 0) {
+ if(access_to_be_redone()) {
+ m_icount += 4;
+ m_inst_substate = 42;
+ } else
+ m_inst_substate = 43;
+ return;
+ }
+ [[fallthrough]];
+ case 43:
+ [...] = m_result;
+ [...]
+ }
+ m_inst_substate = 0;
+ return;
+ }
+
+The non-restarted version is the same thing with the switch and the
+final ``m_inst_substate`` clearing removed.
+
+.. code-block:: C++
+
+ void device::execute_inst_non_restarted()
+ {
+ [...]
+ m_address = [...];
+ m_mask = [...];
+ m_result = specific.read_interruptible(m_address, m_mask);
+ m_icount -= 4;
+ if(m_icount <= 0) {
+ if(access_to_be_redone()) {
+ m_icount += 4;
+ m_inst_substate = 42;
+ } else
+ m_inst_substate = 43;
+ return;
+ }
+ [...] = m_result;
+ [...]
+ return;
+ }
+
+The main loop then looks like this:
+
+.. code-block:: C++
+
+ void device::execute_run()
+ {
+ if(m_inst_substate)
+ call appropriate restarted instruction handler
+ while(m_icount > 0) {
+ debugger_instruction_hook(m_pc);
+ call appropriate non-restarted instruction handler
+ }
+ }
+
+The idea is thus that ``m_inst_substate`` indicates where in an
+instruction one is, but only when an interruption happens. It
+otherwise stays at 0 and is essentially never looked at. Having two
+versions of the interpretation allows to remove the overhead of the
+switch and the end-of-instruction substate clearing.
+
+It is not a requirement to use a generator-based that method, but a
+different one which does not have unacceptable performance
+implications has not yet been found.
+
+3.4 Bus contention cpu_device interface
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The main way to setup bus contention is through the memory maps.
+Lower-level access can be obtained through some methods on cpu_device
+though.
+
+.. code-block:: C++
+
+ bool cpu_device::access_before_time(u64 access_time, u64 current_time) noexcept;
+
+The method ``access_before_time`` allows to try to run an access at a
+given time in cpu cycles. It takes the current time
+(``total_cycles()``) and the expected time for the access. If there
+aren't enough cycles to reach that time the remaining cycles are eaten
+and the method returns true to tell not to do the access and call the
+method again eventually. Otherwise enough cycles are eaten to reach
+the access time and false is returned to tell to do the access.
+
+
+.. code-block:: C++
+
+ bool cpu_device::access_before_delay(u32 cycles, const void *tag) noexcept;
+
+The method ``access_before_delay`` allows to try to run an access
+after a given delay. The tag is an opaque, non-nullptr value used to
+characterize the source of the delay, so that the delay is not applied
+multiple times. Similarly to the previous method cycles are eaten and
+true is returned to abort the access, false to execute it.
+
+.. code-block:: C++
+
+ void cpu_device::access_after_delay(u32 cycles) noexcept;
+
+The method ``access_after_delay`` allows to add a delay after an
+access is done. There is no abort possible, hence no return boolean.
+
+.. code-block:: C++
+
+ void cpu_device::defer_access() noexcept;
+
+The method ``defer_access`` tells the cpu that we need to wait for an
+external event. It marks the access as to be redone, and eats all the
+remaining cycles of the timeslice. The idea is then that the access
+will be retried after time advances up to the next global system
+synchronisation event (sync, timer timeout or set_input_line). This
+is the method to use when for instance waiting on a magic latch for
+data expected from scsi transfers, which happen on timer timeouts.
+
+.. code-block:: C++
+
+ void cpu_device::retry_access() noexcept;
+
+The method ``retry_access`` tells the cpu that the access will need to
+be retried, and nothing else. This can easily reach a situation of
+livelock, so be careful. It is used for instance to simulate a wait
+line (for the z80 for instance) which is controlled through
+set_input_line. The idea is that the device setting wait does the
+set_input_line and a retry_access. The cpu core, as long as the wait
+line is set just eats cycles. Then, when the line is cleared the core
+will retry the access.
+
+
+3.5 Interaction with DRC
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+At this point, interruptibility and DRC are entirely incompatible. We
+do not have a method to quit the generated code before or after an
+access. It's theorically possible but definitely non-trivial.
+
diff --git a/docs/source/techspecs/device_disasm_interface.rst b/docs/source/techspecs/device_disasm_interface.rst
index beacaf8e6df..a2ab4fa7eec 100644
--- a/docs/source/techspecs/device_disasm_interface.rst
+++ b/docs/source/techspecs/device_disasm_interface.rst
@@ -168,7 +168,7 @@ A CPU core derives from **device_disasm_interface** through
That method must return a pointer to a newly allocated disassembler
object. The caller takes ownership and handles the lifetime.
-THis method will be called at most one in the lifetime of the cpu
+This method will be called at most one in the lifetime of the cpu
object.
4. Disassembler configuration and communication
@@ -184,7 +184,7 @@ model name), feel free to use a parameter. Otherwise derive the
class.
Dynamic configuration must be done by first defining a nested public
-struct called "config" in the disassembler, with virtual destructor
+struct called ``config`` in the disassembler, with virtual destructor
and pure virtual methods to pull the required information. A pointer
to that struct should be passed to the disassembler constructor. The
cpu core should then add a derivation from that config struct and
@@ -195,9 +195,9 @@ the config class to give the information.
----------------
There currently is no way for the debugger GUI to add per-core
-configuration. It is needed for in particular the s2650 and the
-saturn cores. It should go through the cpu core class itself, since
-it's pulled from the config struct.
+configuration. In particular, it is needed for the s2650 and Saturn
+cores. It should go through the cpu core class itself, since it's
+pulled from the config struct.
There is support missing in unidasm for per-cpu configuration. That's
needed for a lot of things, see the unidasm source code for the
diff --git a/docs/source/techspecs/device_memory_interface.rst b/docs/source/techspecs/device_memory_interface.rst
index 4efa6a75234..1f8e2b1c37b 100644
--- a/docs/source/techspecs/device_memory_interface.rst
+++ b/docs/source/techspecs/device_memory_interface.rst
@@ -1,18 +1,22 @@
The device_memory_interface
===========================
+.. contents:: :local:
+
+
1. Capabilities
---------------
The device memory interface provides devices with the capability of
creating address spaces, to which address maps can be associated.
-It's used for any device that provides a (logically) address/data bus
-other devices can be connected to. It's mainly, but not only, cpus.
+It’s used for any device that provides a (logical) address/data bus
+that other devices can be connected to. That’s mainly, but not solely,
+CPUs.
The interface allows for an unlimited set of address spaces, numbered
-with small positive values. The IDs should stay small because they
-index vectors to keep the lookup fast. Spaces number 0-3 have an
-associated constant name:
+with small, non-negative values. The IDs index vectors, so they should
+stay small to keep the lookup fast. Spaces numbered 0-3 have associated
+constant name:
+----+---------------+
| ID | Name |
@@ -26,91 +30,139 @@ associated constant name:
| 3 | AS_OPCODES |
+----+---------------+
-Spaces 0 and 3, e.g. AS_PROGRAM and AS_OPCODE, are special for the
-debugger and some CPUs. AS_PROGRAM is use by the debugger and the
-cpus as the space from with the cpu reads its instructions for the
-disassembler. When present, AS_OPCODE is used by the debugger and
-some cpus to read the opcode part of the instruction. What opcode
-means is device-dependant, for instance for the z80 it's the initial
-byte(s) which are read with the M1 signal asserted. For the 68000 is
-means every instruction word plus the PC-relative accesses. The main,
-but not only, use of AS_OPCODE is to implement hardware decrypting
-instructions separately from the data.
+Spaces 0 and 3, i.e. ``AS_PROGRAM`` and ``AS_OPCODES``, are special for
+the debugger and some CPUs. ``AS_PROGRAM`` is use by the debugger and
+the CPUs as the space from which the CPU reads its instructions for the
+disassembler. When present, ``AS_OPCODES`` is used by the debugger and
+some CPUs to read the opcode part of the instruction. What opcode means
+is device-dependant, for instance for the Z80 it's the initial byte(s)
+which are read with the M1 signal asserted, while for the 68000 is means
+every instruction word plus PC-relative accesses. The main, but not
+only, use of ``AS_OPCODES`` is to implement hardware decryption of
+instructions separately from data.
+
2. Setup
--------
-| std::vector<std::pair<int, const address_space_config \*>>\ **memory_space_config**\ (int spacenum) const
+.. code-block:: C++
+
+ std::vector<std::pair<int, const address_space_config *>> memory_space_config() const;
The device must override that method to provide a vector of pairs
-comprising of a space number and its associated
-**address_space_config** describing its configuration. Some examples
-to look up when needed:
+comprising of a space number and an associated ``address_space_config``
+describing its configuration. Some examples to look up when needed:
-* Standard two-space vector: v60_device
-* Conditional AS_OPCODE: z80_device
-* Inherit config and add a space: m6801_device
-* Inherit config and patch a space: tmpz84c011_device
+* Standard two-space vector:
+ `v60_device <https://git.redump.net/mame/tree/src/devices/cpu/v60/v60.cpp?h=mame0226>`_
+* Conditional ``AS_OPCODES``:
+ `z80_device <https://git.redump.net/mame/tree/src/devices/cpu/z80/z80.cpp?h=mame0226>`_
+* Inherit configuration and add a space:
+ `hd647180x_device <https://git.redump.net/mame/tree/src/devices/cpu/z180/hd647180x.cpp?h=mame0226>`_
+* Inherit configuration and modify a space:
+ `tmpz84c011_device <https://git.redump.net/mame/tree/src/devices/cpu/z80/tmpz84c011.cpp?h=mame0226>`_
+.. code-block:: C++
-| bool **has_configured_map**\ () const
-| bool **has_configured_map**\ (int index) const
+ bool has_configured_map(int index = 0) const;
+
+The ``has_configured_map`` method allows to test whether an
+``address_map`` has been associated with a given space in the
+``memory_space_config`` method. That allows optional memory spaces to
+be implemented, such as ``AS_OPCODES`` in certain CPU cores.
-The **has_configured_map** method allows to test in the
-**memory_space_config** method whether an **address_map** has been
-associated with a given space. That allows to implement optional
-memory spaces, such as AS_OPCODES in certain cpu cores. The
-parameterless version tests for space 0.
3. Associating maps to spaces
-----------------------------
-Associating maps to spaces is done at the machine config level, after the device declaration.
+Associating maps to spaces is done at the machine configuration level,
+after the device is instantiated.
+
+.. code-block:: C++
+
+ void set_addrmap(int spacenum, T &obj, Ret (U::*func)(Params...));
+ void set_addrmap(int spacenum, Ret (T::*func)(Params...));
+ void set_addrmap(int spacenum, address_map_constructor map);
+
+These function associate a map with a given space. Address maps
+associated with non-existent spaces are ignored (no warning given). The
+first form takes a reference to an object and a method to call on that
+object. The second form takes a method to call on the current device
+being configured. The third form takes an ``address_map_constructor``
+to copy. In each case, the function must be callable with reference to
+an ``address_map`` object as an argument.
-| **MCFG_DEVICE_ADDRESS_MAP**\ (_space, _map)
-| **MCFG_DEVICE_PROGRAM_MAP**\ (_map)
-| **MCFG_DEVICE_DATA_MAP**\ (_map)
-| **MCFG_DEVICE_IO_MAP**\ (_map)
-| **MCFG_DEVICE_OPCODES_MAP**\ (_map)
+To remove a previously configured address map, call ``set_addrmap`` with
+a default-constructed ``address_map_constructor`` (useful for removing a
+map for an optional space in a derived machine configuration).
-The generic macro and the four specific ones associate a map to a
-given space. Address maps associated to non-existing spaces are
-ignored (no warning given). devcpu.h defines MCFG_CPU_*_MAP aliases
-to the specific macros.
+As an example, here’s the address map configuration for the main CPU in
+the Hana Yayoi and Hana Fubuki machines, with all distractions removed:
-| **MCFG_DEVICE_REMOVE_ADDRESS_MAP**\ (_space)
+.. code-block:: C++
-That macro removes a memory map associated to a given space. Useful
-when removing a map for an optional space in a machine config
-derivative.
+ class hnayayoi_state : public driver_device
+ {
+ public:
+ void hnayayoi(machine_config &config);
+ void hnfubuki(machine_config &config);
+
+ private:
+ required_device<cpu_device> m_maincpu;
+
+ void hnayayoi_map(address_map &map);
+ void hnayayoi_io_map(address_map &map);
+ void hnfubuki_map(address_map &map);
+ };
+
+ void hnayayoi_state::hnayayoi(machine_config &config)
+ {
+ Z80(config, m_maincpu, 20000000/4);
+ m_maincpu->set_addrmap(AS_PROGRAM, &hnayayoi_state::hnayayoi_map);
+ m_maincpu->set_addrmap(AS_IO, &hnayayoi_state::hnayayoi_io_map);
+ }
+
+ void hnayayoi_state::hnfubuki(machine_config &config)
+ {
+ hnayayoi(config);
+
+ m_maincpu->set_addrmap(AS_PROGRAM, &hnayayoi_state::hnfubuki_map);
+ m_maincpu->set_addrmap(AS_IO, address_map_constructor());
+ }
4. Accessing the spaces
-----------------------
-| address_space &\ **space**\ () const
-| address_space &\ **space**\ (int index) const
+.. code-block:: C++
+
+ address_space &space(int index = 0) const;
-Returns a given address space post-initialization. The parameterless
-version tests for AS_PROGRAM/AS_0. Aborts if the space doesn't exist.
+Returns the specified address space post-initialization. The specified
+address space must exist.
-| bool **has_space**\ () const
-| bool **has_space**\ (int index) const
+.. code-block:: C++
-Indicates whether a given space actually exists. The parameterless
-version tests for AS_PROGRAM/AS_0.
+ bool has_space(int index = 0) const;
+
+Indicates whether a given space actually exists.
5. MMU support for disassembler
-------------------------------
-| bool **translate**\ (int spacenum, int intention, offs_t &address)
+.. code-block:: C++
+
+ bool translate(int spacenum, int intention, offs_t &address, address_space *&target_space);
Does a logical to physical address translation through the device's
-MMU. spacenum gives the space number, intention the type of the
-future access (TRANSLATE_(READ\|WRITE\|FETCH)(\|_USER\|_DEBUG)) and
-address is an inout parameter with the address to translate and its
-translated version. Should return true if the translation went
-correctly, false if the address is unmapped.
-
-Note that for some historical reason the device itself must override
-the virtual method **memory_translate** with the same signature.
+MMU. spacenum gives the space number, intention for the type of the
+future access (``TR_(READ\|WRITE\|FETCH)``), address is an in/out
+parameter holding the address to translate on entry and the translated
+version on return, and finally target_space is the actual space the
+access would end up in, which may be in a different device. Should
+return ``true`` if the translation went correctly, or ``false`` if the
+address is unmapped. The call must not change the state of the
+device.
+
+Note that for some historical reason, the device itself must override
+the virtual method ``memory_translate`` with the same signature.
diff --git a/docs/source/techspecs/device_rom_interface.rst b/docs/source/techspecs/device_rom_interface.rst
index 576800e634a..4125bc9536a 100644
--- a/docs/source/techspecs/device_rom_interface.rst
+++ b/docs/source/techspecs/device_rom_interface.rst
@@ -1,92 +1,113 @@
The device_rom_interface
========================
+.. contents:: :local:
+
+
1. Capabilities
---------------
-This interface is designed for devices which expect to have a rom
-connected to them on a dedicated bus. It's mostly designed for sound
+This interface is designed for devices that expect to have a ROM
+connected to them on a dedicated bus. It’s mostly designed for sound
chips. Other devices types may be interested but other considerations
-may make it impratical (graphics decode caching for instance). The
-interface provides the capability of either connecting a ROM_REGION,
-connecting an ADDRESS_MAP or dynamically setting up a block of memory
-as rom. In the region/block cases, banking is automatically handled.
+may make it impractical (graphics decode caching, for instance). The
+interface provides the capability to connect a ROM region, connect an
+address map, or dynamically set up a block of memory as ROM. In the
+region/memory block cases, banking is handled automatically.
+
2. Setup
--------
-| **device_rom_interface**\ (const machine_config &mconfig, device_t &device, u8 addrwidth, endianness_t endian = ENDIANNESS_LITTLE, u8 datawidth = 8)
+.. code-block:: C++
+
+ device_rom_interface<AddrWidth, DataWidth=0, AddrShift=0, Endian=ENDIANNESS_LITTLE>
-The constructor of the interface wants, in addition to the standard
-parameters, the address bus width of the dedicated bus. In addition
-the endianness (if not little endian or byte-sized bus) and data bus
-width (if not byte) can be provided.
+The interface is a template that takes the address width of the
+dedicated bus as a parameter. In addition the data bus width (if not
+byte), address shift (if non-zero) and Endianness (if not little Endian
+or byte-sized bus) can be provided. Data bus width is 0 for byte, 1
+for word, etc.
-| **MCFG_DEVICE_ADDRESS_MAP**\ (AS_0, map)
+.. code-block:: C++
-Use that method at machine config time to provide an address map for
-the bus to connect to. It has priority over a rom region if one is
+ void set_map(map);
+
+Use that method at machine configuration time to provide an address map
+for the bus to connect to. It has priority over a ROM region if one is
also present.
-| **MCFG_DEVICE_ROM**\ (tag)
+.. code-block:: C++
+
+ void set_device_rom_tag(tag);
+
+Used to specify a ROM region to use if a device address map is not
+given. Defaults to ``DEVICE_SELF``, i.e. the device’s tag.
-Used to select a rom region to use if a device address map is not
-given. Defaults to DEVICE_SELF, e.g. the device tag.
+.. code-block:: C++
-| **ROM_REGION**\ (length, tag, flags)
+ ROM_REGION(length, tag, flags)
-If a rom region with a tag as given with **MCFG_DEVICE_ROM** if
+If a ROM region with the tag specified using ``set_device_rom_tag`` if
present, or identical to the device tag otherwise, is provided in the
-rom description for the system, it will be automatically picked up as
-the connected rom. An address map has priority over the region if
-present in the machine config.
+ROM definitions for the system, it will be automatically picked up as
+the connected ROM. An address map has priority over the region if
+present in the machine configuration.
-| void **set_rom_endianness**\ (endianness_t endian)
-| void **set_rom_data_width**\ (u8 width)
-| void **set_rom_addr_width**\ (u8 width)
+.. code-block:: C++
-These methods, intended for generic devices with indefinite hardware
-specifications, override the endianness, data bus width and address
-bus width assigned through the constructor. They must be called from
-within the device before **config_complete** time.
+ void override_address_width(u8 width);
-| void **set_rom**\ (const void \*base, u32 size);
+This method allows the address bus width to be overridden. It must be
+called from within the device before **config_complete** time.
-At any time post- **interface_pre_start**, a memory block can be
-setup as the connected rom with that method. It overrides any
+.. code-block:: C++
+
+ void set_rom(const void *base, u32 size);
+
+At any time post-\ ``interface_pre_start``, a memory block can be
+set up as the connected ROM with that method. It overrides any
previous setup that may have been provided. It can be done multiple
times.
-3. Rom access
+
+3. ROM access
-------------
-| u8 **read_byte**\ (offs_t byteaddress)
-| u16 **read_word**\ (offs_t byteaddress)
-| u32 **read_dword**\ (offs_t byteaddress)
-| u64 **read_qword**\ (offs_t byteaddress)
+.. code-block:: C++
+
+ u8 read_byte(offs_t addr);
+ u16 read_word(offs_t addr);
+ u32 read_dword(offs_t addr);
+ u64 read_qword(offs_t addr);
-These methods provide read access to the connected rom. Out-of-bounds
-access results in standard unmapped read logerror messages.
+These methods provide read access to the connected ROM. Out-of-bounds
+access results in standard unmapped read ``logerror`` messages.
-4. Rom banking
+
+4. ROM banking
--------------
-If the rom region or the memory block in set_rom is larger than the
-address bus, banking is automatically setup.
+If the ROM region or the memory block in ``set_rom`` is larger than the
+address bus can access, banking is automatically set up.
+
+.. code-block:: C++
-| void **set_rom_bank**\ (int bank)
+ void set_rom_bank(int bank);
That method selects the current bank number.
+
5. Caveats
----------
Using that interface makes the device derive from
-**device_memory_interface**. If the device wants to actually use the
-memory interface for itself, remember that AS_0/AS_PROGRAM is used by
-the rom interface, and don't forget to upcall **memory_space_config**.
-
-For devices which have outputs that can be used to address ROMs but
-only to forward the data to another device for processing, it may be
-helpful to disable the interface when it is not required. This can be
-done by overriding **memory_space_config** to return an empty vector.
+``device_memory_interface``. If the device wants to actually use the
+memory interface for itself, remember that space zero (0, or
+``AS_PROGRAM``) is used by the ROM interface, and don’t forget to call
+the base ``memory_space_config`` method.
+
+For devices which have outputs that can be used to address ROMs but only
+to forward the data to another device for processing, it may be helpful
+to disable the interface when it is not required. This can be done by
+overriding ``memory_space_config`` to return an empty vector.
diff --git a/docs/source/techspecs/device_sound_interface.rst b/docs/source/techspecs/device_sound_interface.rst
new file mode 100644
index 00000000000..859d2497477
--- /dev/null
+++ b/docs/source/techspecs/device_sound_interface.rst
@@ -0,0 +1,286 @@
+The device_sound_interface
+==========================
+
+.. contents:: :local:
+
+
+1. The sound system
+-------------------
+
+The device sound interface is the entry point for devices that handle
+sound input and/or output. The sound system is built on the concept
+of *streams* which connect devices together with resampling and mixing
+applied transparently as needed. Microphones (audio input) and
+speakers (audio output) are specific known devices which use the same
+interface.
+
+2. Devices using device_sound_interface
+---------------------------------------
+
+2.1 Initialisation
+~~~~~~~~~~~~~~~~~~
+
+Sound streams must be created in the device_start (or
+interface_pre_start) method.
+
+.. code-block:: C++
+
+ sound_stream *stream_alloc(int inputs, int outputs, int sample_rate, sound_stream_flags flags = STREAM_DEFAULT_FLAGS);
+
+A stream is created with ``stream_alloc``. It takes the number of
+input and output channels, the sample rate and optionally flags.
+
+The sample rate can be SAMPLE_RATE_INPUT_ADAPTIVE,
+SAMPLE_RATE_OUTPUT_ADAPTIVE or SAMPLE_RATE_ADAPTIVE. In that case the
+chosen sample rate is the highest one among the inputs, outputs or
+both respectively. In case of loop, the chosen sample rate is the
+configured global sample rate.
+
+The only available non-default flag is STREAM_SYNCHRONOUS. When set,
+the sound generation method will be called for every sample
+individually. It's necessary for DSPs that run a program on every
+sample. but on the other hand it's expensive, so only to be used when
+required.
+
+Devices can create multiple streams. It's rare though. Some Yamaha
+chips should but don't. Inputs and outputs are numbered from 0 and
+arrange all streams in the order they are created.
+
+
+2.2 Sound input/output
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ virtual void sound_stream_update(sound_stream &stream);
+
+This method is required to be implemented to consume the input samples
+and/or compute the output ones. The stream to update for is passed as
+the parameter. See the streams section, specifically sample access,
+to see how to write the method.
+
+
+2.3 Stream information
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ int inputs() const;
+ int outputs() const;
+ std::pair<sound_stream *, int> input_to_stream_input(int inputnum) const;
+ std::pair<sound_stream *, int> output_to_stream_output(int outputnum) const;
+
+The method ``inputs`` returns the total number of inputs in the
+streams created by the device. The method ``outputs`` similarly
+counts the outputs. The other two methods allow to grab the stream
+and channel number for the device corresponding to the global input or
+output number.
+
+
+2.4 Gain management
+~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ float input_gain(int inputnum) const;
+ float output_gain(int outputnum) const;
+ void set_input_gain(int inputnum, float gain);
+ void set_output_gain(int outputnum, float gain);
+ void set_route_gain(int source_channel, device_sound_interface *target, int target_channel, float gain);
+
+ float user_output_gain() const;
+ float user_output_gain(int outputnum) const;
+ void set_user_output_gain(float gain);
+ void set_user_output_gain(int outputnum, float gain);
+
+Those methods allow to set the gain on every step of the routes
+between streams. All gains are multipliers, with default value 1.0.
+The steps are, from samples output in ``sound_stream_update`` to
+samples read in the next device's ``sound_stream_update``:
+
+* Per-channel output gain
+* Per-channel user output gain
+* Per-device user output gain
+* Per-route gain
+* Per-channel input gain
+
+The user gains must not be set from the driver, they're for use by the
+user interface (the sliders) and are saved in the game configuration.
+The other gains are for driver/device use, and are saved in save
+states.
+
+
+2.5 Routing setup
+~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ device_sound_interface &add_route(u32 output, const device_finder<T, R> &target, double gain, u32 channel = 0)
+ device_sound_interface &add_route(u32 output, const char *target, double gain, u32 channel = 0);
+ device_sound_interface &add_route(u32 output, device_sound_interface &target, double gain, u32 channel = 0);
+
+ device_sound_interface &reset_routes();
+
+Routes between devices, e.g. between streams, are set at configuration
+time. The method ``add_route`` must be called on the source device
+and gives the channel on the source device, the target device, the
+gain, and optionally the channel on the target device. The constant
+``ALL_OUTPUTS`` can be used to add a route from every channel of the
+source to a given channel of the destination.
+
+The method ``reset_routes`` is used to remove all the routes setup on
+a given source device.
+
+
+.. code-block:: C++
+
+ u32 get_sound_requested_inputs() const;
+ u32 get_sound_requested_outputs() const;
+ u64 get_sound_requested_inputs_mask() const;
+ u64 get_sound_requested_outputs_mask() const;
+
+Those methods are useful for devices which want to behave differently
+depending on what routes are set up on them. You get either the max
+number of requested channel plus one (which is the number of channels
+when all channels are routed, but is more useful when there are gaps)
+or a mask of use for channels 0-63. Note that ``ALL_OUTPUTS`` does
+not register any specific output or output count.
+
+
+
+3. Streams
+----------
+
+3.1 Generalities
+~~~~~~~~~~~~~~~~
+
+Streams are endpoints associated with devices and, when connected
+together, ensure the transmission of audio data between them. A
+stream has a number of inputs (which can be zero) and outputs (same)
+and one sample rate which is common to all inputs and outputs. The
+connections are set up at the machine configuration level and the sound
+system ensures mixing and resampling is done transparently.
+
+Samples in streams are encoded as sample_t. In the current
+implementation, this is a float. Nominal values are between -1 and 1,
+but clamping at the device level is not recommended (unless that's
+what happens in hardware of course) because the gain values, volume
+and effects can easily avoid saturation.
+
+They are implemented in the class ``sound_stream``.
+
+
+3.2 Characteristics
+~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ device_t &device() const;
+ bool input_adaptive() const;
+ bool output_adaptive() const;
+ bool synchronous() const;
+ u32 input_count() const;
+ u32 output_count() const;
+ u32 sample_rate() const;
+ attotime sample_period() const;
+
+
+3.3 Sample access
+~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ s32 samples() const;
+
+ void put(s32 output, s32 index, sample_t sample);
+ void put_clamp(s32 output, s32 index, sample_t sample, sample_t clamp = 1.0);
+ void put_int(s32 output, s32 index, s32 sample, s32 max);
+ void put_int_clamp(s32 output, s32 index, s32 sample, s32 maxclamp);
+ void add(s32 output, s32 index, sample_t sample);
+ void add_int(s32 output, s32 index, s32 sample, s32 max);
+ void fill(s32 output, sample_t value, s32 start, s32 count);
+ void fill(s32 output, sample_t value, s32 start);
+ void fill(s32 output, sample_t value);
+ void copy(s32 output, s32 input, s32 start, s32 count);
+ void copy(s32 output, s32 input, s32 start);
+ void copy(s32 output, s32 input);
+ sample_t get(s32 input, s32 index) const;
+ sample_t get_output(s32 output, s32 index) const;
+
+Those are the methods used to implement ``sound_stream_update``.
+First ``samples`` tells how many samples to consume and/or generate.
+The to-generate samples, if any, are pre-cleared (e.g. set to zero).
+
+Input samples are retrieved with ``get``, where ``input`` is the
+stream channel number and ``index`` the sample number.
+
+Generated samples are written with the put variants. ``put`` sets a
+sample_t in channel ``output`` at position ``index``. ``put_clamp``
+does the same but first clamps the value to +/-``clamp``. ``put_int``
+does it with an integer ``sample`` but pre-divides it by ``max``.
+``put_int_clamp`` does the same but also pre-clamps within
+-``maxclamp`` and ``maxclamp``-1, which is the normal range for a
+2-complement value.
+
+``add`` and ``add_int`` are similar but add the value of the sample to
+what's there instead of replacing. ``get_output`` gets the currently
+stored output value.
+
+``fill`` sets a range of an output channel to a given ``value``.
+``start`` tells where to start (default index 0), ``count`` how many
+(default up to the end of the buffer).
+
+``copy`` does the same as fill but gets its value from the indentical
+position in an input channel.
+
+Note that clamping should not be used unless it actually happens in
+hardware. Between gains and effects there is a fair chance saturation
+can be avoided later in the chain.
+
+
+
+3.4 Gain management
+~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ float user_output_gain() const;
+ void set_user_output_gain(float gain);
+ float user_output_gain(s32 output) const;
+ void set_user_output_gain(s32 output, float gain);
+
+ float input_gain(s32 input) const;
+ void set_input_gain(s32 input, float gain);
+ void apply_input_gain(s32 input, float gain);
+ float output_gain(s32 output) const;
+ void set_output_gain(s32 output, float gain);
+ void apply_output_gain(s32 output, float gain);
+
+
+This is similar to the device gain control, with a twist: apply
+multiplies the current gain by the given value.
+
+
+3.5 Misc. actions
+~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ void set_sample_rate(u32 sample_rate);
+ void update();
+
+The method ``set_sample_rate`` allows to change the sample rate of the
+stream. The method ``update`` triggers a call of
+``sound_stream_update`` on the stream and the ones it depends on to
+reach the current time in terms of samples.
+
+
+4. Devices using device_mixer_interface
+---------------------------------------
+
+The device mixer interface is used for devices that want to relay
+sound in the device tree without acting on it. It's very useful on
+for instance slot devices connectors, where the slot device may have
+an audio connection with the main system. They are routed like every
+other sound device, create the streams automatically and copy input to
+output. Nothing needs to be done in the device.
diff --git a/docs/source/techspecs/floppy.rst b/docs/source/techspecs/floppy.rst
index 609e5c53013..39060d4eddf 100644
--- a/docs/source/techspecs/floppy.rst
+++ b/docs/source/techspecs/floppy.rst
@@ -23,7 +23,7 @@ The new floppy subsystem aims at emulating the behaviour of floppies and floppy
A floppy disk is a disc that stores magnetic orientations on their surface disposed in a series on concentric circles called tracks or cylinders [1]_. Its main characteristics are its size (goes from a diameter of around 2.8" to 8") , its number of writable sides (1 or 2) and its magnetic resistivity. The magnetic resistivity indicates how close magnetic orientation changes can happen and the information kept. That's one third of what defines the term "density" that is so often used for floppies (the other two are floppy drive head size and bit-level encoding).
-The magnetic orientations are always binary, e.g. they're one way or the opposite, there's no intermediate state. Their direction can either be tengentially to the track, e.g in the direction or opposite to the rotation, or in the case of perpendicular recording the direction is perpendicular to the disc surface (hence the name). Perpendicular recording allows for closer orientation changes by writing the magnetic information more deeply, but arrived late in the technology lifetime. 2.88Mb disks and the floppy children (Zip drives, etc) used perpendicular recording. For simulation purposes the direction is not important, only the fact that only two orientations are possible is. Two more states are possible though: a portion of a track can be demagnetized (no orientation) or damaged (no orientation and can't be written to).
+The magnetic orientations are always binary, e.g. they're one way or the opposite, there's no intermediate state. Their direction can either be tangentially to the track, i.e. in the direction of or opposite to the rotation, or in the case of perpendicular recording the direction is perpendicular to the disc surface (hence the name). Perpendicular recording allows for closer orientation changes by writing the magnetic information more deeply, but arrived late in the technology lifetime. 2.88Mb disks and the floppy children (Zip drives, etc.) used perpendicular recording. For simulation purposes the direction is not important, only the fact that only two orientations are possible is. Two more states are possible though: a portion of a track can be demagnetized (no orientation) or damaged (no orientation and can't be written to).
A specific position in the disk rotation triggers an index pulse. That position can be detected through a hole in the surface (very visible in 5.25" and 3" floppies for instance) or through a specific position of the rotating center (3.5" floppies, perhaps others). This index pulse is used to designate the beginning of the track, but is not used by every system. Older 8" floppies have multiple index holes used to mark the beginning of sectors (called hard sectoring) but one of them is positioned differently to be recognized as the track start, and the others are at fixed positions relative to the origin one.
@@ -70,7 +70,7 @@ In every cell there may or may not be a magnetic orientation transition, e.g. a
Of course protections play with that to make formats not reproducible by the system controller, either breaking the three-zeroes rule or playing with the cells durations/sizes.
-Bit endocing is then the art of transforming raw data into a cell 0/1 configuration that respects the two constraints.
+Bit encoding is then the art of transforming raw data into a cell 0/1 configuration that respects the two constraints.
2.3.1.2. FM encoding
````````````````````
@@ -108,9 +108,9 @@ Other encodings exist, like M2FM, but they're very rare and system-specific.
2.3.1.6. Reading back encoded data
``````````````````````````````````
-Writing encoded data is easy, you only need a clock at the appropriate frequency and send or not a pulse on the clock edges. Reading back the data is where the fun is. Cells are a logical construct and not a physical measurable entity. Rotational speeds very around the defined one (+/- 2% is not rare) and local perturbations (air turbulence, surface distance...) make the instant speed very variable in general. So to extract the cell values stream the controller must dynamically synchronize with the pulse train that the floppy head picks up. The principle is simple: a cell-sized duration window is build within which the presence of at least one pulse indicates the cell is a '1', and the absence of any a '0'. After reaching the end of the window the starting time is moved appropriately to try to keep the observed pulse at the exact middle of the window. This allows to correct the phase on every '1' cell, making the synchronization work if the rotational speed is not too off. Subsequent generations of controllers used a Phase-Locked Loop (PLL) which vary both phase and window duration to adapt better to wrong rotational speeds, with usually a tolerance of +/- 15%.
+Writing encoded data is easy: you only need a clock at the appropriate frequency and send or not a pulse on the clock edges. Reading back the data is where the fun is. Cells are a logical construct and not a physical measurable entity. Rotational speeds very around the defined one (±2% is not rare), and local perturbations (air turbulence, surface distance…) make the instantaneous speed very variable in general. So to extract the cell values stream, the controller must dynamically synchronize with the pulse train that the floppy head picks up. The principle is simple: a cell-sized duration window is built within which the presence of at least one pulse indicates the cell is a '1', and the absence of any a '0'. After reaching the end of the window, the starting time is moved appropriately to try to keep the observed pulse at the exact middle of the window. This allows the phase to be corrected on every '1' cell, making the synchronization work if the rotational speed is not too off. Subsequent generations of controllers used Phase-Locked Loops (PLLs) which vary both phase and window duration to adapt better to inaccuarate rotational speeds, usually with a tolerance of ±15%.
-Once the cell data stream is extracted decoding depends on the encoding. In the FM and MFM case the only question is to recognize data bits from clock bits, while in GCR the start position of the first group should be found. That second level of synchronization is handled at a higher level using patterns not found in a normal stream.
+Once the cell data stream is extracted, decoding depends on the encoding. In the FM and MFM case the only question is to recognize data bits from clock bits, while in GCR the start position of the first group should be found. That second level of synchronization is handled at a higher level using patterns not found in a normal stream.
2.3.2. Sector-level organization
diff --git a/docs/source/techspecs/index.rst b/docs/source/techspecs/index.rst
index 67775ae3a74..72c678b4ca6 100644
--- a/docs/source/techspecs/index.rst
+++ b/docs/source/techspecs/index.rst
@@ -1,16 +1,27 @@
Technical Specifications
-------------------------
+========================
-This section covers technical specifications useful to programmers working on MAME's source or working on LUA scripts that run within the MAME framework.
+This section covers technical specifications useful to programmers working on
+MAME’s source or working on scripts that run within the MAME framework.
.. toctree::
- :titlesonly:
+ :titlesonly:
- layout_files
- device_memory_interface
- device_rom_interface
- device_disasm_interface
- floppy
- nscsi
- luaengine
- m6502
+ naming
+ layout_files
+ layout_script
+ object_finders
+ inputsystem
+ device_memory_interface
+ device_rom_interface
+ device_disasm_interface
+ device_sound_interface
+ memory
+ cpu_device
+ floppy
+ nscsi
+ m6502
+ uml_instructions
+ poly_manager
+ audio_effects
+ osd_audio
diff --git a/docs/source/techspecs/inputsystem.rst b/docs/source/techspecs/inputsystem.rst
new file mode 100644
index 00000000000..7a027b0ff22
--- /dev/null
+++ b/docs/source/techspecs/inputsystem.rst
@@ -0,0 +1,460 @@
+.. _inputsystem:
+
+Input System
+============
+
+.. contents::
+ :local:
+ :depth: 2
+
+
+.. _inputsystem-intro:
+
+Introduction
+------------
+
+The variety of systems MAME emulates, as well as the variation in host
+systems and peripherals, necessitates a flexible, configurable input
+system.
+
+Note that the input system is concerned with low-level user input.
+High-level user interaction, involving things like text input and
+pointing devices, is handled separately.
+
+
+.. _inputsystem-components:
+
+Components
+----------
+
+From the emulated system’s point of view, the input system has the
+following conceptual components.
+
+Input device
+~~~~~~~~~~~~
+
+Input devices supply input values. An input device typically
+corresponds to a physical device in the host system, for example a
+keyboard, mouse or game controller. However, there isn’t always a
+one-to-one correspondence between input devices and physical devices.
+For example the SDL keyboard provider module aggregates all keyboards
+into a single input device, and the Win32 lightgun provider module can
+present two input devices using input from a single mouse.
+
+Input devices are identified by their device class (keyboard, mouse,
+joystick or lightgun) and device number within the class. Input
+provider modules can also supply an implementation-dependent identifier
+to allow the user to configure stable device numbering.
+
+Note that input devices are unrelated to emulated devices (``device_t``
+implementations) despite the similar name.
+
+Input device item
+~~~~~~~~~~~~~~~~~
+
+Also known as a **control**, and input device item corresponds to a
+input source that produces a single value. This usually corresponds to
+a physical control or sensor, for example a joystick axis, a button or
+an accelerometer.
+
+MAME supports three kinds of controls: **switches**, **absolute axes**
+and **relative axes**:
+
+* Switches produce the value 0 when inactive (released or off) or 1 when
+ active (pressed or on).
+* Absolute axes produce a value normalised to the range -65,536 to
+ 65,536 with zero corresponding to the neutral position.
+* Relative axes produce a value corresponding to the movement since the
+ previous input update. Mouse-like devices scale values to
+ approximately 512 per nominal 100 DPI pixel.
+
+Negative axis values should correspond to directions up, to the left,
+away from the player, or anti-clockwise. For single-ended axes (e.g.
+pedals or displacement-sensitive triggers and buttons), only zero and
+the negative portion of the range should be used.
+
+Switches are used to represent controls that naturally have two distinct
+states, like buttons and toggle switches.
+
+Absolute axes are used to represent controls with a definite range
+and/or neutral position. Examples include steering wheels with limit
+stops, joystick axes, and displacement-sensitive triggers.
+
+Relative axes are used to represent controls with an effectively
+infinite range. Examples include mouse/trackball axes, incremental
+encoder dials, and gyroscopes.
+
+Accelerometers and force sensing joystick axes should be represented as
+absolute axes, even though the range is theoretically open-ended. In
+practice, there is a limit to the range the transducers can report,
+which is usually substantially larger than needed for normal operation.
+
+Input device items are identified by their associated device’s class and
+device number along with an **input item ID**. MAME supplies item IDs
+for common types of controls. Additional controls or controls that do
+not correspond to a common type are dynamically assigned item IDs. MAME
+supports hundreds to items per input device.
+
+I/O port field
+~~~~~~~~~~~~~~
+
+An I/O port field represents an input source in an emulated device or
+system. Most types of I/O port fields can be assigned one or more
+combinations of controls, allowing the user to control the input to
+the emulated system.
+
+Similarly to input device items, there are multiple types of I/O port
+fields:
+
+* **Digital fields** function as switches that produce one of two
+ distinct values. They are used for keyboard keys, eight-way joystick
+ direction switches, toggle switches, photointerruptors and other
+ emulated inputs that function as two-position switches.
+* **Absolute analog fields** have a range with defined minimum, maximum
+ and neutral positions. They are used for analog joystick axes,
+ displacement-sensitive pedals, paddle knobs, and other emulated inputs
+ with a defined range.
+* **Relative analog fields** have a range with defined minimum, maximum
+ and starting positions. On each update, the value accumulates and
+ wraps when it passes either end of the range. Functionally, this is
+ like the output of an up/down counter connected to an incremental
+ encoder. They are used for mouse/trackball axes, steering wheels
+ without limit stops, and other emulated inputs that have no range
+ limits.
+* DIP switch, configuration and adjuster fields allow the user to set
+ the value through MAME’s user interface.
+* Additional special field types are used to produce fixed or
+ programmatically generated values.
+
+A digital field appears to the user as a single assignable input, which
+accepts switch values.
+
+An analog field appears to the user as three assignable inputs: an
+**axis input**, which accepts axis values; and an **increment input**
+and a **decrement input** which accept switch values.
+
+Input manager
+~~~~~~~~~~~~~
+
+The input manager has several responsibilities:
+
+* Tracking the available input devices in the system.
+* Reading input values.
+* Converting between internal identifier values, configuration token
+ strings and display strings.
+
+In practice, emulated devices and systems rarely interact with the input
+manager directly. The most common reason to access the input manager is
+implementing special debug controls, which should be disabled in release
+builds. Plugins that respond to input need to call the input manager to
+read inputs.
+
+I/O port manager
+~~~~~~~~~~~~~~~~
+
+The I/O port manager’s primary responsibilities include:
+
+* Managing assignments of controls to I/O port fields and user interface
+ actions.
+* Reading input values via the input manager and updating I/O port field
+ values.
+
+Like the input manager, the I/O port manager is largely transparent to
+emulated devices and systems. You just need to set up your I/O ports
+and fields, and the I/O port manager handles the rest.
+
+
+.. _inputsystem-structures:
+
+Structures and data types
+-------------------------
+
+The following data types are used for dealing with input.
+
+Input code
+~~~~~~~~~~
+
+An input code specifies an input device item and how it should be
+interpreted. It is a tuple consisting of the following values: **device
+class**, **device number**, **item class**, **item modifier** and **item
+ID**:
+
+* The device class, device number and item ID together identify the
+ input device item to read.
+* The item class specifies the type of output value desired: switch,
+ absolute axis or relative axis. Axis values can be converted to
+ switch values by specifying an appropriate modifier.
+* The modifier specifies how a value should be interpreted. Valid
+ options depend on the type of input device item and the specified
+ item class.
+
+If the specified input item is a switch, it can only be read using the
+switch class, and no modifiers are supported. Attempting to read a
+switch as an absolute or relative axis always returns zero.
+
+If the specified input item is an absolute axis, it can be read as an
+absolute axis or as a switch:
+
+* Reading an absolute axis item as an absolute axis returns the current
+ state of the control, potentially transformed if a modifier is
+ specified. Supported modifiers are **reverse** to reverse the range
+ of the control, **positive** to map the positive range of the control
+ onto the output (zero corresponding to -65,536 and 65,536
+ corresponding to 65,536), and **negative** to map the negative range
+ of the control onto the output (zero corresponding to -65,536 and
+ -65,536 corresponding to 65,536).
+* Reading an absolute axis item as a switch returns zero or 1 depending
+ on whether the control is past a threshold in the direction specified
+ by the modifier. Use the **negative** modifier to return 1 when the
+ control is beyond the threshold in the negative direction (up or
+ left), or the **positive** modifier to return 1 when the control is
+ beyond the threshold in the positive direction (down or right). There
+ are two special pairs of modifiers, **left**/**right** and
+ **up**/**down** that are only applicable to the primary X/Y axes of
+ joystick devices. The user can specify a *joystick map* to control
+ how these modifiers interpret joystick movement.
+* Attempting to read an absolute axis item as a relative axis always
+ returns zero.
+
+If the specified input item is a relative axis, it can be read as a
+relative axis or as a switch:
+
+* Reading a relative axis item as a relative axis returns the change in
+ value since the last input update. The only supported modifier is
+ **reverse**, which negates the value, reversing the direction.
+* Reading a relative axis as a switch returns 1 if the control moved in
+ the direction specified by the modifier since the last input update.
+ Use the **negative**/**left**/**up** modifiers to return 1 when the
+ control has been moved in the negative direction (up or left), or the
+ **positive**/**right**/**down** modifiers to return 1 when the control
+ has moved in the positive direction (down or right).
+* Attempting to read a relative axis item as an absolute axis always
+ returns zero.
+
+There are also special input codes used for specifying how multiple
+controls are to be combined in an input sequence.
+
+The most common place you’ll encounter input codes in device and system
+driver code is when specifying initial assignments for I/O port fields
+that don’t have default assignments supplied by the core. The
+``PORT_CODE`` macro is used for this purpose.
+
+MAME provides macros and helper functions for producing commonly used
+input codes, including standard keyboard keys and
+mouse/joystick/lightgun axes and buttons.
+
+Input sequence
+~~~~~~~~~~~~~~
+
+An input sequence specifies a combination controls that can be assigned
+to an input. The name refers to the fact that it is implemented as a
+sequence container with input codes as elements. It is somewhat
+misleading, as input sequences are interpreted using instantaneous
+control values. Input sequences are interpreted differently for switch
+and axis input.
+
+Input sequences for switch input must only contain input codes with the
+item class set to switch along with the special **or** and **not** input
+codes. The input sequence is interpreted using sum-of-products logic.
+A **not** code causes the value returned by the immediately following
+code to be inverted. The conjunction of values returned by successive
+codes is evaluated until an **or** code is encountered. If the current
+value is 1 when an **or** code is encountered it is returned, otherwise
+evaluation continues.
+
+Input sequences for axis input can contain input codes with the item
+class set to switch, absolute axis or relative axis along with the
+special **or** and **not** codes. It’s helpful to think of the input
+sequence as containing one or more groups of input codes separated by
+**or** codes:
+
+* A **not** code causes the value returned by an immediately following
+ switch code to be inverted. It has no effect on absolute or relative
+ axis codes.
+* Within a group, the conjunction of the values returned by switch codes
+ is evaluated. If it is zero, the group is ignored.
+* Within a group, multiple axis values of the same type are summed.
+ Values returned by absolute axis codes are summed, and values returned
+ by relative axis codes are summed.
+* If any absolute axis code in a group returns a non-zero value, the sum
+ of relative axes in the group is ignored. Any non-zero absolute axis
+ value takes precedence over relative axis values.
+* The same logic is applied when combining group values: group values
+ produced from the same axis type are summed, and values produced from
+ absolute axes take precedence over values produced from relative axes.
+* After the group values are summed, if the value was produced from
+ absolute axes it is clamped to the range -65,536 to 65,536 (values
+ produced from relative axes are not clamped).
+
+Emulation code rarely needs to deal with input sequences directly, as
+they’re handled internally between the I/O port manager and input
+manager. The input manager also converts input sequences to and from
+the token strings stored in configuration files and produces text for
+displaying input sequences to users.
+
+Plugins with controls or hotkeys need to use input sequences to allow
+configuration. Utility classes are provided to allow input sequences to
+be entered by the user in a consistent way, and the input manager can be
+used for conversions to and from configuration and display strings. It
+is very rare to need to directly manipulate input sequences.
+
+
+.. _inputsystem-providermodules:
+
+Input provider modules
+----------------------
+
+Input provider modules are part of the OS-dependent layer (OSD), and are
+not directly exposed to emulation and user interface code. Input
+provider modules are responsible for detecting available host input
+devices, setting up input devices for the input manager, and providing
+callbacks to read the current state of input device items. Input
+provider modules may also provide additional default input assignments
+suitable for host input devices that are present.
+
+The user is given a choice of input modules to use. One input provider
+module is used for each of the four input device classes (keyboard,
+mouse, joystick and lightgun). The available modules depend on the host
+operating system and OSD implementation. Different modules may use
+different APIs, support different kinds of devices, or present devices
+in different ways.
+
+
+.. _inputsystem-playerpositions:
+
+Player positions
+----------------
+
+MAME uses a concept called *player positions* to help manage input
+assignments. The number of player positions supported depends on the
+I/O port field type:
+
+* Ten player positions are supported for common game inputs, including
+ joystick, pedal, paddle, dial, trackball, lightgun and mouse.
+* Four player positions are supported for mahjong and hanafuda inputs.
+* One player position is supported for gambling system inputs.
+* Other inputs do not use player positions. This includes coin slots,
+ arcade start buttons, tilt switches, service switches and
+ keyboard/keypad keys.
+
+The user can configure default input assignments per player position for
+supported I/O port field types which are saved in the file
+**default.cfg**. These assignments are used for all systems unless the
+device/system driver supplies its own default assignments, or the user
+configures system-specific input assignments.
+
+In order to facilitate development of reusable emulated devices with
+inputs, particularly slot devices, the I/O port manager automatically
+renumbers player positions when setting up the emulated system:
+
+* The I/O port manager starts at player position 1 and begins
+ iterating the emulated device tree in depth first order, starting from
+ the root device.
+* If a device has I/O port fields that support player positions, they
+ are renumbered to start from the I/O port manager’s current player
+ position.
+* Before advancing to the next device, the I/O port manager sets its
+ current player position to the last seen player position plus one.
+
+For a simple example, consider what happens when you run a Sega Mega
+Drive console with two game pads connected:
+
+* The I/O port manager starts at player position 1 at the root device.
+* The first device encountered with I/O port fields that support player
+ positions is the first game pad. The inputs are renumbered to start
+ at player position 1. This has no visible effect, as the I/O port
+ fields are initially numbered starting at player position 1.
+* Before moving to the next device, the I/O port manager sets its
+ current player position to 2 (the last player position seen plus one).
+* The next device encountered with I/O port fields that support player
+ positions is the second game pad. The inputs are renumbered to start
+ at player position 2. This avoids I/O port field type conflicts with
+ the first game pad.
+* Before moving to the next device, the I/O port manager sets its
+ current player position to 3 (the last player position seen plus one).
+* No more devices with I/O port fields that support player positions are
+ encountered.
+
+
+.. _inputsystem-updatingfields:
+
+Updating I/O port fields
+------------------------
+
+The I/O port manager updates I/O port fields once for each video frame
+produced by the first emulated screen in the system. How a field is
+updated depends on whether it is a digital or analog field.
+
+Updating digital fields
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Updating digital I/O port fields is simple:
+
+* The I/O port manager reads the current value for the field’s assigned
+ input sequence (via the input manager).
+* If the value is zero, the field’s default value is set.
+* If the value is non-zero, the binary complement of the field’s default
+ value is set.
+
+Updating absolute analog fields
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Updating absolute analog I/O port fields is more complex due to the need
+to support a variety of control setups:
+
+* The I/O port manager reads the current value for the field’s assigned
+ axis input sequence (via the input manager).
+* If the current value changed since the last update and the input
+ device item that produced the current value was an absolute axis, the
+ field’s value is set to the current value scaled to the correct range,
+ and no further processing is performed.
+* If the current value is non-zero and the input device item that
+ produced the current value was a relative axis, the current value is
+ added to the field’s value, scaled by the field’s sensitivity setting.
+* The I/O port manager reads the current value for the field’s assigned
+ increment input sequence (via the input manager); if this value is
+ non-zero, the field’s increment/decrement speed setting value is added
+ to its value, scaled by its sensitivity setting.
+* The I/O port manager reads the current value for the field’s assigned
+ decrement input sequence (via the input manager); if this value is
+ non-zero, the field’s increment/decrement speed setting value is
+ subtracted from its value, scaled by its sensitivity setting.
+* If the current axis input, increment input and decrement input values
+ are all zero, but either or both of the increment input and decrement
+ input values were non-zero the last time the field’s value changed in
+ response to user input, the field’s auto-centring speed setting value
+ is added to or subtracted from its value to move it toward its default
+ value.
+
+Note that the sensitivity setting value for absolute analog fields
+affects the response to relative axis input device items and
+increment/decrement inputs, but it does not affect the response to
+absolute axis input device items or the auto-centring speed.
+
+Updating relative analog fields
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Relative analog I/O port fields also need special handling to cater for
+multiple control setups, but they are a little simpler than absolute
+analog fields:
+
+* The I/O port manager reads the current value for the field’s assigned
+ axis input sequence (via the input manager).
+* If the current value is non-zero and the input device item that
+ produced the current value was an absolute axis, the current value is
+ added to the field’s value, scaled by the field’s sensitivity setting,
+ and no further processing is performed.
+* If the current value is non-zero and the input device item that
+ produced the current value was a relative axis, the current value is
+ added to the field’s value, scaled by the field’s sensitivity setting.
+* The I/O port manager reads the current value for the field’s assigned
+ increment input sequence (via the input manager); if this value is
+ non-zero, the field’s increment/decrement speed setting value is added
+ to its value, scaled by its sensitivity setting.
+* The I/O port manager reads the current value for the field’s assigned
+ decrement input sequence (via the input manager); if this value is
+ non-zero, the field’s increment/decrement speed setting value is
+ subtracted from its value, scaled by its sensitivity setting.
+
+Note that the sensitivity setting value for relative analog fields
+affects the response to all user input.
diff --git a/docs/source/techspecs/layout_files.rst b/docs/source/techspecs/layout_files.rst
index 52b944ec246..a014756a32b 100644
--- a/docs/source/techspecs/layout_files.rst
+++ b/docs/source/techspecs/layout_files.rst
@@ -1,10 +1,12 @@
+.. _layfile:
+
MAME Layout Files
=================
.. contents:: :local:
-.. _layout-intro:
+.. _layfile-intro:
Introduction
------------
@@ -18,12 +20,12 @@ screens, built and linked into the MAME binary, or provided externally. MAME
layout files are an XML application, using the ``.lay`` filename extension.
-.. _layout-concepts:
+.. _layfile-concepts:
Core concepts
-------------
-.. _layout-concepts-numbers:
+.. _layfile-concepts-numbers:
Numbers
~~~~~~~
@@ -53,18 +55,18 @@ found, the number will be interpreted as an integer.
Numbers are parsed using the "C" locale for portability.
-.. _layout-concepts-coordinates:
+.. _layfile-concepts-coordinates:
Coordinates
~~~~~~~~~~~
Layout coordinates are internally represented as IEEE754 32-bit binary
-floating-point numbers (also known as "single precision"). Coordinates increase
+floating-point numbers (also known as “single precision”). Coordinates increase
in the rightward and downward directions. The origin (0,0) has no particular
significance, and you may freely use negative coordinates in layouts.
Coordinates are supplied as floating-point numbers.
-MAME assumes that view coordinates have the same aspect ratio as pixel on the
+MAME assumes that view coordinates have the same aspect ratio as pixels on the
output device (host screen or window). Assuming square pixels and no rotation,
this means equal distances in X and Y axes correspond to equal horizontal and
vertical distances in the rendered output.
@@ -73,29 +75,43 @@ Views, groups and elements all have their own internal coordinate systems. When
an element or group is referenced from a view or another group, its coordinates
are scaled as necessary to fit the specified bounds.
-Objects are positioned and sized using ``bounds`` elements. A bounds element
-may specify the position of the top left corner and the size using ``x``, ``y``,
-``width`` and ``height`` attributes, or it may specify the coordinates of the
-edges with the ``left``, ``top``, ``right`` and ``bottom`` attributes. These
-two ``bounds`` elements are equivalent::
+Objects are positioned and sized using ``bounds`` elements. The horizontal
+position and size may be specified in three ways: left edge and width using
+``x`` and ``width`` attributes, horizontal centre and width using ``xc`` and
+``width`` attributes, or left and right edges using ``left`` and ``right``
+attributes. Similarly, the vertical position and size may be specified in terms
+of the top edge and height using ``y`` and ``height`` attributes, vertical
+centre and height using ``yc`` and ``height`` attributes, or top and bottom
+edges using ``top`` and ``bottom`` attributes.
+
+These three ``bounds`` elements are equivalent:
+
+.. code-block:: XML
+
+ <bounds x="455" y="120" width="12" height="8" />
+ <bounds xc="461" yc="124" width="12" height="8" />
+ <bounds left="455" top="120" right="467" bottom="128" />
- <bounds x="455" y="120" width="11" height="7" />
- <bounds left="455" top="120" right="466" bottom="127" />
+It’s possible to use different schemes in the horizontal and vertical
+directions. For example, these equivalent ``bounds`` elements are also valid:
-Either the ``x`` or ``left`` attribute must be present to distinguish between
-the two schemes. The ``width`` and ``height`` or ``right`` and ``bottom``
-default to 1.0 if not supplied. It is an error if ``width`` or ``height`` are
-negative, if ``right`` is less than ``left``, or if ``bottom`` is less than
-``top``.
+.. code-block:: XML
+ <bounds x="455" top="120" width="12" bottom="128" />
+ <bounds left="455" yc="124" right="467" height="8" />
-.. _layout-concepts-colours:
+The ``width``/``height`` or ``right``/``bottom`` default to 1.0 if not supplied.
+It is an error if ``width`` or ``height`` are negative, if ``right`` is less
+than ``left``, or if ``bottom`` is less than ``top``.
+
+
+.. _layfile-concepts-colours:
Colours
~~~~~~~
Colours are specified in RGBA space. MAME is not aware of colour profiles and
-gamuts, so colours will typically be interpreted as sRGB with your system's
+gamuts, so colours will typically be interpreted as sRGB with your system’s
target gamma (usually 2.2). Channel values are specified as floating-point
numbers. Red, green and blue channel values range from 0.0 (off) to 1.0 (full
intensity). Alpha ranges from 0.0 (fully transparent) to 1.0 (opaque). Colour
@@ -103,7 +119,9 @@ channel values are not pre-multiplied by the alpha value.
Component and view item colour is specified using ``color`` elements.
Meaningful attributes are ``red``, ``green``, ``blue`` and ``alpha``. This
-example ``color`` element specifies all channel values::
+example ``color`` element specifies all channel values:
+
+.. code-block:: XML
<color red="0.85" green="0.4" blue="0.3" alpha="1.0" />
@@ -112,7 +130,7 @@ is an error if any channel value falls outside the range of 0.0 to 1.0
(inclusive).
-.. _layout-concepts-params:
+.. _layfile-concepts-params:
Parameters
~~~~~~~~~~
@@ -120,8 +138,10 @@ Parameters
Parameters are named variables that can be used in most attributes. To use
a parameter in an attribute, surround its name with tilde (~) characters. If a
parameter is not defined, no substitution occurs. Here is an examples showing
-two instances of parameter use -- the values of the ``digitno`` and ``x``
-parameters will be substituted for ``~digitno~`` and ``~x~``::
+two instances of parameter use – the values of the ``digitno`` and ``x``
+parameters will be substituted for ``~digitno~`` and ``~x~``:
+
+.. code-block:: XML
<element name="digit~digitno~" ref="digit">
<bounds x="~x~" y="80" width="25" height="40" />
@@ -137,7 +157,7 @@ scope level corresponds to the top-level ``mamelayout`` element. Each
Internally a parameter can hold a string, integer, or floating-point number, but
this is mostly transparent. Integers are stored as 64-bit signed
twos-complement values, and floating-point numbers are stored as IEEE754 64-bit
-binary floating-point numbers (also known as "double precision"). Integers are
+binary floating-point numbers (also known as “double precision”). Integers are
substituted in decimal notation, and floating point numbers are substituted in
default format, which may be decimal fixed-point or scientific notation
depending on the value). There is no way to override the default formatting of
@@ -156,16 +176,20 @@ Value parameters are assigned using a ``param`` element with ``name`` and
``view`` elements other ``group`` definition elements). A value parameter may
be reassigned at any point.
-Here's an example assigning the value "4" to the value parameter "firstdigit"::
+Here’s an example assigning the value “4” to the value parameter “firstdigit”:
+
+.. code-block:: XML
<param name="firstdigit" value="4" />
Generator parameters are assigned using a ``param`` element with ``name`` and
``start`` attributes, and ``increment``, ``lshift`` and/or ``rshift``
attributes. Generator parameters may only appear inside ``repeat`` elements
-(see :ref:`layout-parts-repeats` for details). A generator parameter must not
+(see :ref:`layfile-parts-repeats` for details). A generator parameter must not
be reassigned in the same scope (an identically named parameter may be defined
-in a child scope). Here are some example generator parameters::
+in a child scope). Here are some example generator parameters:
+
+.. code-block:: XML
<param name="nybble" start="3" increment="-1" />
<param name="switchpos" start="74" increment="156" />
@@ -176,28 +200,27 @@ in a child scope). Here are some example generator parameters::
* The ``mask`` parameter generates values 2048, 128, 8...
The ``increment`` attribute must be an integer or floating-point number to be
-added to the parameter's value. The ``lshift`` and ``rshift`` attributes must
-be non-negative integers specifying numbers of bits to shift the parameter's
+added to the parameter’s value. The ``lshift`` and ``rshift`` attributes must
+be non-negative integers specifying numbers of bits to shift the parameter’s
value to the left or right. The increment and shift are applied at the end of
-the repeating block before the next iteration starts. If both an increment and
-shift are supplied, the increment is applied before the shift.
+the repeating block before the next iteration starts. The parameter’s value
+will be interpreted as an integer or floating-point number before the increment
+and/or shift are applied. If both an increment and shift are supplied, the
+increment is applied before the shift.
If the ``increment`` attribute is present and is a floating-point number, the
-parameter's value will be interpreted as an integer or floating-point number and
-converted to a floating-point number before the increment is added. If the
-``increment`` attribute is present and is an integer, the parameter's value will
-be interpreted as an integer or floating number before the increment is added.
-The increment will be converted to a floating-point number before the addition
-if the parameter's value is a floating-point number.
+parameter’s value will be converted to a floating-point number if necessary
+before the increment is added. If the ``increment`` attribute is present and is
+an integer while the parameter’s value is a floating-point number, the increment
+will be converted to a floating-point number before the addition.
If the ``lshift`` and/or ``rshift`` attributes are present and not equal, the
-parameter's value will be interpreted as an integer or floating-point number,
-converted to an integer as necessary, and shifted accordingly. Shifting to the
-left is defined as shifting towards the most significant bit. If both
-``lshift`` and ``rshift`` are supplied, they are netted off before being
-applied. This means you cannot, for example, use equal ``lshift`` and
-``rshift`` attributes to clear bits at one end of a parameter's value after the
-first iteration.
+parameter’s value will be converted to an integer if necessary, and shifted
+accordingly. Shifting to the left is defined as shifting towards the most
+significant bit. If both ``lshift`` and ``rshift`` are supplied, they are
+netted off before being applied. This means you cannot, for example, use equal
+``lshift`` and ``rshift`` attributes to clear bits at one end of a parameter’s
+value after the first iteration.
It is an error if a ``param`` element has neither ``value`` nor ``start``
attributes, and it is an error if a ``param`` element has both a ``value``
@@ -209,7 +232,7 @@ innermost scope. It is not possible to define or reassign parameters in a
containing scope.
-.. _layout-concepts-predef-params:
+.. _layfile-concepts-predef-params:
Pre-defined parameters
~~~~~~~~~~~~~~~~~~~~~~
@@ -245,21 +268,21 @@ scr0physicalyaspect
fraction. Note that this is the vertical component *before* rotation is
applied. This parameter is an integer defined at layout (global) scope.
scr0nativexaspect
- The horizontal part of the pixel aspect ratio of the first screen's visible
+ The horizontal part of the pixel aspect ratio of the first screen’s visible
area (if present). The pixel aspect ratio is provided as a reduced improper
fraction. Note that this is the horizontal component *before* rotation is
applied. This parameter is an integer defined at layout (global) scope.
scr0nativeyaspect
- The vertical part of the pixel aspect ratio of the first screen's visible
+ The vertical part of the pixel aspect ratio of the first screen’s visible
area (if present). The pixel aspect ratio is provided as a reduced improper
fraction. Note that this is the vertical component *before* rotation is
applied. This parameter is an integer defined at layout (global) scope.
scr0width
- The width of the first screen's visible area (if present) in emulated
+ The width of the first screen’s visible area (if present) in emulated
pixels. Note that this is the width *before* rotation is applied. This
parameter is an integer defined at layout (global) scope.
scr0height
- The height of the first screen's visible area (if present) in emulated
+ The height of the first screen’s visible area (if present) in emulated
pixels. Note that this is the height *before* rotation is applied. This
parameter is an integer defined at layout (global) scope.
scr1physicalxaspect
@@ -269,18 +292,18 @@ scr1physicalyaspect
The vertical part of the physical aspect ratio of the second screen (if
present). This parameter is an integer defined at layout (global) scope.
scr1nativexaspect
- The horizontal part of the pixel aspect ratio of the second screen's visible
+ The horizontal part of the pixel aspect ratio of the second screen’s visible
area (if present). This parameter is an integer defined at layout (global)
scope.
scr1nativeyaspect
- The vertical part of the pixel aspect ratio of the second screen's visible
+ The vertical part of the pixel aspect ratio of the second screen’s visible
area (if present). This parameter is an integer defined at layout (global)
scope.
scr1width
- The width of the second screen's visible area (if present) in emulated
+ The width of the second screen’s visible area (if present) in emulated
pixels. This parameter is an integer defined at layout (global) scope.
scr1height
- The height of the second screen's visible area (if present) in emulated
+ The height of the second screen’s visible area (if present) in emulated
pixels. This parameter is an integer defined at layout (global) scope.
scr\ *N*\ physicalxaspect
The horizontal part of the physical aspect ratio of the (zero-based) *N*\ th
@@ -292,18 +315,18 @@ scr\ *N*\ physicalyaspect
(global) scope.
scr\ *N*\ nativexaspect
The horizontal part of the pixel aspect ratio of the (zero-based) *N*\ th
- screen's visible area (if present). This parameter is an integer defined at
+ screen’s visible area (if present). This parameter is an integer defined at
layout (global) scope.
scr\ *N*\ nativeyaspect
The vertical part of the pixel aspect ratio of the (zero-based) *N*\ th
- screen's visible area (if present). This parameter is an integer defined at
+ screen’s visible area (if present). This parameter is an integer defined at
layout (global) scope.
scr\ *N*\ width
- The width of the (zero-based) *N*\ th screen's visible area (if present) in
+ The width of the (zero-based) *N*\ th screen’s visible area (if present) in
emulated pixels. This parameter is an integer defined at layout (global)
scope.
scr\ *N*\ height
- The height of the (zero-based) *N*\ th screen's visible area (if present) in
+ The height of the (zero-based) *N*\ th screen’s visible area (if present) in
emulated pixels. This parameter is an integer defined at layout (global)
scope.
viewname
@@ -319,7 +342,7 @@ end of configuration. Values are not updated and layouts are not recomputed if
the system reconfigures the screen while running.
-.. _layout-parts:
+.. _layfile-parts:
Parts of a layout
-----------------
@@ -332,7 +355,9 @@ are supported.
The top-level element of a MAME layout file must be a ``mamelayout`` element
with a ``version`` attribute. The ``version`` attribute must be an integer.
Currently MAME only supports version 2, and will not load any other version.
-This is an example opening tag for a top-level ``mamelayout`` element::
+This is an example opening tag for a top-level ``mamelayout`` element:
+
+.. code-block:: XML
<mamelayout version="2">
@@ -345,47 +370,48 @@ and groups that appear after them.
The following elements are allowed inside the top-level ``mamelayout`` element:
param
- Defines or reassigns a value parameter. See :ref:`layout-concepts-params`
+ Defines or reassigns a value parameter. See :ref:`layfile-concepts-params`
for details.
element
- Defines an element -- one of the basic objects that can be arranged in a
- view. See :ref:`layout-parts-elements` for details.
+ Defines an element – one of the basic objects that can be arranged in a
+ view. See :ref:`layfile-parts-elements` for details.
group
Defines a reusable group of elements/screens that may be referenced from
- views or other groups. See :ref:`layout-parts-groups` for details.
+ views or other groups. See :ref:`layfile-parts-groups` for details.
repeat
- A repeating group of elements -- may contain ``param``, ``element``,
- ``group``, and ``repeat`` elements. See :ref:`layout-parts-repeats` for
+ A repeating group of elements – may contain ``param``, ``element``,
+ ``group``, and ``repeat`` elements. See :ref:`layfile-parts-repeats` for
details.
view
An arrangement of elements and/or screens that can be displayed on an output
- device (a host screen/window). See :ref:`layout-parts-views` for details.
+ device (a host screen/window). See :ref:`layfile-parts-views` for details.
script
- Allows lua script to be supplied for enhanced interactive layouts.
+ Allows Lua script to be supplied for enhanced interactive layouts. See
+ :ref:`layscript` for details.
-.. _layout-parts-elements:
+.. _layfile-parts-elements:
Elements
~~~~~~~~
Elements are one of the basic visual objects that may be arranged, along with
-screens, to make up a view. Elements may be built up one or more *components*,
-but an element is treated as as single surface when building the scene graph
+screens, to make up a view. Elements may be built up of one or more *components*,
+but an element is treated as a single surface when building the scene graph
and rendering. An element may be used in multiple views, and may be used
multiple times within a view.
-An element's appearance depends on its *state*. The state is an integer which
-usually comes from an I/O port field or an emulated output (see the discussion
-of :ref:`layout-parts-views` for information on connecting an element to an I/O
-port or output). Any component of an element may be restricted to only drawing
-when the element's state is a particular value. Some components (e.g.
-multi-segment displays and reels) use the state directly to determine their
+An element’s appearance depends on its *state*. The state is an integer which
+usually comes from an I/O port field or an emulated output (see
+:ref:`layfile-interact-elemstate` for information on connecting an element to an
+emulated I/O port or output). Any component of an element may be restricted to
+only drawing when the element’s state is a particular value. Some components
+(e.g. multi-segment displays) use the state directly to determine their
appearance.
Each element has its own internal coordinate system. The bounds of the
-element's coordinate system are computed as the union of the bounds of the
-individual components it's composed of.
+element’s coordinate system are computed as the union of the bounds of the
+individual components it’s composed of.
Every element must have a ``name`` attribute specifying its name. Elements are
referred to by name when instantiated in groups or views. It is an error for a
@@ -395,22 +421,63 @@ attribute, to be used if not connected to an emulated output or I/O port. If
present, the ``defstate`` attribute must be a non-negative integer.
Child elements of the ``element`` element instantiate components, which are
-drawn in reading order from first to last (components draw on top of components
-that come before them). All components support a few common features:
-
-* Each component may have a ``state`` attribute. If present, the component will
- only be drawn when the element's state matches its value (if absent, the
- component will always be drawn). If present, the ``state`` attribute must be
- a non-negative integer.
+drawn into the element texture in reading order from first to last using alpha
+blending (components draw over and may obscure components that come before
+them). All components support a few common features:
+
+* Components may be conditionally drawn depending on the element’s state by
+ supplying ``state`` and/or ``statemask`` attributes. If present, these
+ attributes must be non-negative integers. If only the ``state`` attribute is
+ present, the component will only be drawn when the element’s state matches its
+ value. If only the ``statemask`` attribute is present, the component will
+ only be drawn when all the bits that are set in its value are set in the
+ element’s state.
+
+ If both the ``state`` and ``statemask`` attributes are present, the component
+ will only be drawn when the bits in the element’s state corresponding to the
+ bits that are set in the ``statemask`` attribute’s value match the value of the
+ corresponding bits in the ``state`` attribute’s value.
+
+ (The component will always be drawn if neither ``state`` nor ``statemask``
+ attributes are present, or if the ``statemask`` attribute’s value is zero.)
* Each component may have a ``bounds`` child element specifying its position and
- size (see :ref:`layout-concepts-coordinates`). If no such element is present,
- the bounds default to a unit square (width and height of 1.0) with the top
- left corner at (0,0).
+ size (see :ref:`layfile-concepts-coordinates`). If no such element is
+ present, the bounds default to a unit square (width and height of 1.0) with
+ the top left corner at (0,0).
+
+ A component’s position and/or size may be animated according to the element’s
+ state by supplying multiple ``bounds`` child elements with ``state``
+ attributes. The ``state`` attribute of each ``bounds`` child element must be
+ a non-negative integer. The ``state`` attributes must not be equal for any
+ two ``bounds`` elements within a component.
+
+ If the element’s state is lower than the ``state`` value of any ``bounds``
+ child element, the position/size specified by the ``bounds`` child element
+ with the lowest ``state`` value will be used. If the element’s state is
+ higher than the ``state`` value of any ``bounds`` child element, the
+ position/size specified by the ``bounds`` child element with the highest
+ ``state`` value will be used. If the element’s state is between the ``state``
+ values of two ``bounds`` child elements, the position/size will be
+ interpolated linearly.
* Each component may have a ``color`` child element specifying an RGBA colour
- (see :ref:`layout-concepts-colours` for details). This can be used to control
- the colour of geometric, algorithmically drawn, or textual components. It is
- ignored for ``image`` components. If no such element is present, the colour
- defaults to opaque white.
+ (see :ref:`layfile-concepts-colours` for details). This can be used to
+ control the colour of geometric, algorithmically drawn, or textual components.
+ For ``image`` components, the colour of the image pixels is multiplied by the
+ specified colour. If no such element is present, the colour defaults to
+ opaque white.
+
+ A component’s color may be animated according to the element’s state by
+ supplying multiple ``color`` child elements with ``state`` attributes. The
+ ``state`` attributes must not be equal for any two ``color`` elements within a
+ component.
+
+ If the element’s state is lower than the ``state`` value of any ``color``
+ child element, the colour specified by the ``color`` child element with the
+ lowest ``state`` value will be used. If the element’s state is higher than
+ the ``state`` value of any ``color`` child element, the colour specified by
+ the ``color`` child element with the highest ``state`` value will be used. If
+ the element’s state is between the ``state`` values of two ``color`` child
+ elements, the RGBA colour components will be interpolated linearly.
The following components are supported:
@@ -419,18 +486,30 @@ rect
disk
Draws a uniform colour ellipse fitted to its bounds.
image
- Draws an image loaded from a PNG or JPEG file. The name of the file to load
- (including the file name extension) is supplied with the required ``file``
- attribute. Additionally, an optional ``alphafile`` attribute may be used to
- specify the name of a PNG file (including the file name extension) to load
- into the alpha channel of the image. The image file(s) should be placed in
- the same directory/archive as the layout file. If the ``alphafile``
- attribute refers refers to a file, it must have the same dimensions as the
- file referred to by the ``file`` attribute, and must have a bit depth no
- greater than eight bits per channel per pixel. The intensity from this
- image (brightness) is copied to the alpha channel, with full intensity (white
- in a greyscale image) corresponding to fully opaque, and black corresponding
- to fully transparent.
+ Draws an image loaded from a PNG, JPEG, Windows DIB (BMP) or SVG file. The
+ name of the file to load (including the file name extension) is supplied
+ using the ``file`` attribute. Additionally, an optional ``alphafile``
+ attribute may be used to specify the name of a PNG file (including the file
+ name extension) to load into the alpha channel of the image.
+
+ Alternatively, image data may be supplied in the layout file itself using a
+ ``data`` child element. This can be useful for supplying simple,
+ human-readable SVG graphics. A ``file`` attribute or ``data`` child element
+ must be supplied; it is an error if neither or both are supplied.
+
+ If the ``alphafile`` attribute refers to a file, it must have the same
+ dimensions (in pixels) as the file referred to by the ``file`` attribute,
+ and must have a bit depth no greater than eight bits per channel per pixel.
+ The intensity from this image (brightness) is copied to the alpha channel,
+ with full intensity (white in a greyscale image) corresponding to fully
+ opaque, and black corresponding to fully transparent. The ``alphafile``
+ attribute will be ignored if the ``file`` attribute refers to an SVG image
+ or the ``data`` child element contains SVG data; it is only used in
+ conjunction with bitmap images.
+
+ The image file(s) should be placed in the same directory/archive as the
+ layout file. Image file formats are detected by examining the content of
+ the files, file name extensions are ignored.
text
Draws text in using the UI font in the specified colour. The text to draw
must be supplied using a ``string`` attribute. An ``align`` attribute may
@@ -438,39 +517,16 @@ text
be an integer, where 0 (zero) means centred, 1 (one) means left-aligned, and
2 (two) means right-aligned. If the ``align`` attribute is absent, the text
will be centred.
-dotmatrix
- Draws an eight-pixel horizontal segment of a dot matrix display, using
- circular pixels in the specified colour. The bits of the element's state
- determine which pixels are lit, with the least significant bit corresponding
- to the leftmost pixel. Unlit pixels are drawn at low intensity (0x20/0xff).
-dotmatrix5dot
- Draws a five-pixel horizontal segment of a dot matrix display, using
- circular pixels in the specified colour. The bits of the element's state
- determine which pixels are lit, with the least significant bit corresponding
- to the leftmost pixel. Unlit pixels are drawn at low intensity (0x20/0xff).
-dotmatrixdot
- Draws a single element of a dot matrix display as a circular pixels in the
- specified colour. The least significant bit of the element's state
- determines whether the pixel is lit. An unlit pixel is drawn at low
- intensity (0x20/0xff).
led7seg
Draws a standard seven-segment (plus decimal point) digital LED/fluorescent
- display in the specified colour. The low eight bits of the element's state
+ display in the specified colour. The low eight bits of the element’s state
control which segments are lit. Starting from the least significant bit,
the bits correspond to the top segment, the upper right-hand segment,
continuing clockwise to the upper left segment, the middle bar, and the
decimal point. Unlit segments are drawn at low intensity (0x20/0xff).
-led8seg_gts1
- Draws an eight-segment digital fluorescent display of the type used in
- Gottlieb System 1 pinball machines (actually a Futaba part). Compared to
- standard seven-segment displays, these displays have no decimal point, the
- horizontal middle bar is broken in the centre, and there is a broken
- vertical middle bar controlled by the bit that would control the decimal
- point in a standard seven-segment display. Unlit segments are drawn at low
- intensity (0x20/0xff).
led14seg
Draws a standard fourteen-segment alphanumeric LED/fluorescent display in
- the specified colour. The low fourteen bits of the element's state control
+ the specified colour. The low fourteen bits of the element’s state control
which segments are lit. Starting from the least significant bit, the bits
correspond to the top segment, the upper right-hand segment, continuing
clockwise to the upper left segment, the left-hand and right-hand halves of
@@ -480,13 +536,13 @@ led14seg
led14segsc
Draws a standard fourteen-segment alphanumeric LED/fluorescent display with
decimal point/comma in the specified colour. The low sixteen bits of the
- element's state control which segments are lit. The low fourteen bits
+ element’s state control which segments are lit. The low fourteen bits
correspond to the same segments as in the ``led14seg`` component. Two
additional bits correspond to the decimal point and comma tail. Unlit
segments are drawn at low intensity (0x20/0xff).
led16seg
Draws a standard sixteen-segment alphanumeric LED/fluorescent display in the
- specified colour. The low sixteen bits of the element's state control which
+ specified colour. The low sixteen bits of the element’s state control which
segments are lit. Starting from the least significant bit, the bits
correspond to the left-hand half of the top bar, the right-hand half of the
top bar, continuing clockwise to the upper left segment, the left-hand and
@@ -496,12 +552,12 @@ led16seg
led16segsc
Draws a standard sixteen-segment alphanumeric LED/fluorescent display with
decimal point/comma in the specified colour. The low eighteen bits of the
- element's state control which segments are lit. The low sixteen bits
+ element’s state control which segments are lit. The low sixteen bits
correspond to the same segments as in the ``led16seg`` component. Two
additional bits correspond to the decimal point and comma tail. Unlit
segments are drawn at low intensity (0x20/0xff).
simplecounter
- Displays the numeric value of the element's state using the system font in
+ Displays the numeric value of the element’s state using the system font in
the specified colour. The value is formatted in decimal notation. A
``digits`` attribute may be supplied to specify the minimum number of digits
to display. If present, the ``digits`` attribute must be a positive
@@ -512,27 +568,28 @@ simplecounter
to set text alignment. If present, the ``align`` attribute must be an
integer, where 0 (zero) means centred, 1 (one) means left-aligned, and 2
(two) means right-aligned; if absent, the text will be centred.
-reel
- Used for drawing slot machine reels. Supported attributes include
- ``symbollist``, ``stateoffset``, ``numsymbolsvisible``, ``reelreversed``,
- and ``beltreel``.
-An example element that draws a static left-aligned text string::
+An example element that draws a static left-aligned text string:
+
+.. code-block:: XML
<element name="label_reset_cpu">
<text string="CPU" align="1"><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
-
An example element that displays a circular LED where the intensity depends on
-the state of an active-high output::
+the state of an active-high output:
+
+.. code-block:: XML
<element name="led" defstate="0">
- <rect state="0"><color red="0.43" green="0.35" blue="0.39" /></rect>
- <rect state="1"><color red="1.0" green="0.18" blue="0.20" /></rect>
+ <disk state="0"><color red="0.43" green="0.35" blue="0.39" /></disk>
+ <disk state="1"><color red="1.0" green="0.18" blue="0.20" /></disk>
</element>
-An example element for a button that gives visual feedback when clicked::
+An example element for a button that gives visual feedback when clicked:
+
+.. code-block:: XML
<element name="btn_rst">
<rect state="0"><bounds x="0.0" y="0.0" width="1.0" height="1.0" /><color red="0.2" green="0.2" blue="0.2" /></rect>
@@ -543,8 +600,59 @@ An example element for a button that gives visual feedback when clicked::
<text string="RESET"><bounds x="0.1" y="0.4" width="0.8" height="0.2" /><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
+An example of an element that draws a seven-segment LED display using external
+segment images:
+
+.. code-block:: XML
+
+ <element name="digit_a" defstate="0">
+ <image file="a_off.png" />
+ <image file="a_a.png" statemask="0x01" />
+ <image file="a_b.png" statemask="0x02" />
+ <image file="a_c.png" statemask="0x04" />
+ <image file="a_d.png" statemask="0x08" />
+ <image file="a_e.png" statemask="0x10" />
+ <image file="a_f.png" statemask="0x20" />
+ <image file="a_g.png" statemask="0x40" />
+ <image file="a_dp.png" statemask="0x80" />
+ </element>
+
+An example of a bar graph that grows vertically and changes colour from green,
+through yellow, to red as the state increases:
+
+.. code-block:: XML
+
+ <element name="pedal">
+ <rect>
+ <bounds state="0x000" left="0.0" top="0.9" right="1.0" bottom="1.0" />
+ <bounds state="0x610" left="0.0" top="0.0" right="1.0" bottom="1.0" />
+ <color state="0x000" red="0.0" green="1.0" blue="0.0" />
+ <color state="0x184" red="1.0" green="1.0" blue="0.0" />
+ <color state="0x610" red="1.0" green="0.0" blue="0.0" />
+ </rect>
+ </element>
+
+An example of a bar graph that grows horizontally to the left or right and
+changes colour from green, through yellow, to red as the state changes from the
+neutral position:
+
+.. code-block:: XML
+
+ <element name="wheel">
+ <rect>
+ <bounds state="0x800" left="0.475" top="0.0" right="0.525" bottom="1.0" />
+ <bounds state="0x280" left="0.0" top="0.0" right="0.525" bottom="1.0" />
+ <bounds state="0xd80" left="0.475" top="0.0" right="1.0" bottom="1.0" />
+ <color state="0x800" red="0.0" green="1.0" blue="0.0" />
+ <color state="0x3e0" red="1.0" green="1.0" blue="0.0" />
+ <color state="0x280" red="1.0" green="0.0" blue="0.0" />
+ <color state="0xc20" red="1.0" green="1.0" blue="0.0" />
+ <color state="0xd80" red="1.0" green="0.0" blue="0.0" />
+ </rect>
+ </element>
+
-.. _layout-parts-views:
+.. _layfile-parts-views:
Views
~~~~~
@@ -558,18 +666,20 @@ load views from the layout file. This is particularly useful for systems where
a screen is optional, for example computer systems with front panel controls and
an optional serial terminal.
-Views are identified by name in MAME's user interface and in command-line
+Views are identified by name in MAME’s user interface and in command-line
options. For layouts files associated with devices other than the root driver
-device, view names are prefixed with the device's tag (with the initial colon
-omitted) -- for example a view called "Keyboard LEDs" loaded for the device
-``:tty:ie15`` will be called "tty:ie15 Keyboard LEDs" in MAME's user interface.
+device, view names are prefixed with the device’s tag (with the initial colon
+omitted) – for example a view called “Keyboard LEDs” loaded for the device
+``:tty:ie15`` will be called “tty:ie15 Keyboard LEDs” in MAME’s user interface.
Views are listed in the order they are loaded. Within a layout file, views are
loaded in the order they appear, from top to bottom.
Views are created with ``view`` elements inside the top-level ``mamelayout``
element. Each ``view`` element must have a ``name`` attribute, supplying its
human-readable name for use in the user interface and command-line options.
-This is an example of a valid opening tag for a ``view`` element::
+This is an example of a valid opening tag for a ``view`` element:
+
+.. code-block:: XML
<view name="Control panel">
@@ -580,27 +690,36 @@ element. This means a view can reference elements and groups that appear after
it in the file, and parameters from the enclosing scope will have their final
values from the end of the ``mamelayout`` element.
+A ``view`` element may have a ``showpointers`` attribute to set whether mouse
+and pen pointers should be shown for the view. If present, the value must be
+either ``yes`` or ``no``. If the ``showpointers`` attribute is not present, pen
+and mouse pointers are shown for views that contain items bound to I/O ports.
+
The following child elements are allowed inside a ``view`` element:
bounds
- Sets the origin and size of the view's internal coordinate system if
- present. See :ref:`layout-concepts-coordinates` for details. If absent,
+ Sets the origin and size of the view’s internal coordinate system if
+ present. See :ref:`layfile-concepts-coordinates` for details. If absent,
the bounds of the view are computed as the union of the bounds of all
screens and elements within the view. It only makes sense to have one
``bounds`` as a direct child of a view element. Any content outside the
- view's bounds is cropped, and the view is scaled proportionally to fit the
+ view’s bounds is cropped, and the view is scaled proportionally to fit the
output window or screen.
param
- Defines or reassigns a value parameter in the view's scope. See
- :ref:`layout-concepts-params` for details.
+ Defines or reassigns a value parameter in the view’s scope. See
+ :ref:`layfile-concepts-params` for details.
element
- Adds an element to the view (see :ref:`layout-parts-elements`). The name of
- the element to add is specified using the required ``ref`` attribute. It is
- an error if no element with this name is defined in the layout file. May
- optionally be connected to an emulated I/O port using ``inputtag`` and
+ Adds an element to the view (see :ref:`layfile-parts-elements`). The name
+ of the element to add is specified using the required ``ref`` attribute. It
+ is an error if no element with this name is defined in the layout file.
+ Within a view, elements are drawn in the order they appear in the layout
+ file, from front to back. See below for more details.
+
+ May optionally be connected to an emulated I/O port using ``inputtag`` and
``inputmask`` attributes, and/or an emulated output using a ``name``
- attribute. Within a layer, elements are drawn in the order they appear in
- the layout file, from front to back. See below for more details.
+ attribute. See :ref:`layfile-interact-clickable` for details. See
+ :ref:`layfile-interact-elemstate` for details on supplying a state value to
+ the instantiated element.
screen
Adds an emulated screen image to the view. The screen must be identified
using either an ``index`` attribute or a ``tag`` attribute (it is an error
@@ -610,8 +729,17 @@ screen
zero (0). If present, the ``tag`` attribute must be the tag path to the
screen relative to the device that causes the layout to be loaded. Screens
are drawn in the order they appear in the layout file, from front to back.
+
+ May optionally be connected to an emulated I/O port using ``inputtag`` and
+ ``inputmask`` attributes, and/or an emulated output using a ``name``
+ attribute. See :ref:`layfile-interact-clickable` for details.
+collection
+ Adds screens and/or items in a collection that can be shown or hidden by the
+ user (see :ref:`layfile-parts-collections`). The name of the collection is
+ specified using the required ``name`` attribute. There is a limit of 32
+ collections per view.
group
- Adds the content of the group to the view (see :ref:`layout-parts-groups`).
+ Adds the content of the group to the view (see :ref:`layfile-parts-groups`).
The name of the group to add is specified using the required ``ref``
attribute. It is an error if no group with this name is defined in the
layout file. See below for more details on positioning.
@@ -620,9 +748,15 @@ repeat
attribute. The ``count`` attribute must be a positive integer. A
``repeat`` element in a view may contain ``element``, ``screen``, ``group``,
and further ``repeat`` elements, which function the same way they do when
- placed in a view directly. See :ref:`layout-parts-repeats` for discussion
+ placed in a view directly. See :ref:`layfile-parts-repeats` for discussion
on using ``repeat`` elements.
+Screens (``screen`` elements) and layout elements (``element`` elements) may
+have an ``id`` attribute. If present, the ``id`` attribute must not be empty,
+and must be unique within a view, including screens and elements instantiated
+via reusable groups and repeating blocks. Screens and layout elements with
+``id`` attributes can be looked up by Lua scripts (see :ref:`layscript`).
+
Screens (``screen`` elements), layout elements (``element`` elements) and groups
(``group`` elements) may have their orientation altered using an ``orientation``
child element. For screens, the orientation modifiers are applied in addition
@@ -631,7 +765,7 @@ The ``orientation`` element supports the following attributes, all of which are
optional:
rotate
- If present, applies clockwise rotation in ninety degree implements. Must be
+ If present, applies clockwise rotation in ninety degree increments. Must be
an integer equal to 0, 90, 180 or 270.
swapxy
Allows the screen, element or group to be mirrored along a line at
@@ -656,13 +790,15 @@ layout elements is alpha blending.
Screens (``screen`` elements), layout elements (``element`` elements) and groups
(``group`` elements) may be positioned and sized using a ``bounds`` child
-element (see :ref:`layout-concepts-coordinates` for details). In the absence of
-a ``bounds`` child element, screens' and layout elements' bounds default to a
+element (see :ref:`layfile-concepts-coordinates` for details). In the absence
+of a ``bounds`` child element, screens’ and layout elements’ bounds default to a
unit square (origin at 0,0 and height and width both equal to 1). In the
absence of a ``bounds`` child element, groups are expanded with no
translation/scaling (note that groups may position screens/elements outside
their bounds). This example shows a view instantiating and positioning a
-screen, an individual layout element, and two element groups::
+screen, an individual layout element, and two element groups:
+
+.. code-block:: XML
<view name="LED Displays, Terminal and Keypad">
<screen index="0"><bounds x="0" y="132" width="320" height="240" /></screen>
@@ -673,57 +809,101 @@ screen, an individual layout element, and two element groups::
Screens (``screen`` elements), layout elements (``element`` elements) and groups
(``group`` elements) may have a ``color`` child element (see
-:ref:`layout-concepts-colours`) specifying a modifier colour. The component
+:ref:`layfile-concepts-colours`) specifying a modifier colour. The component
colours of the screen or layout element(s) are multiplied by this colour.
-If an ``element`` element has ``inputtag`` and ``inputmask`` attributes,
-clicking it is equivalent to pressing a key/button mapped to the corresponding
-input(s). The ``inputtag`` specifies the tag path of an I/O port relative to
-the device that caused the layout file to be loaded. The ``inputmask``
-attribute must be an integer specifying the bits of the I/O port that the
-element should activate. This sample shows instantiation of clickable buttons::
-
- <element ref="btn_3" inputtag="X2" inputmask="0x10">
- <bounds x="2.30" y="4.325" width="1.0" height="1.0" />
- </element>
- <element ref="btn_0" inputtag="X0" inputmask="0x20">
- <bounds x="0.725" y="5.375" width="1.0" height="1.0" />
- </element>
- <element ref="btn_rst" inputtag="RESET" inputmask="0x01">
- <bounds x="1.775" y="5.375" width="1.0" height="1.0" />
- </element>
-
-If an ``element`` element has a ``name`` attribute, it will take its state from
-the value of the correspondingly named emulated output. Note that output names
-are global, which can become an issue when a machine uses multiple instances of
-the same type of device. See :ref:`layout-parts-elements` for details on how an
-element's state affects its appearance. This example shows how digital displays
-may be connected to emulated outputs::
+Screens (``screen`` elements) and layout elements (``element`` elements) may
+have their colour and position/size animated by supplying multiple ``color``
+and/or ``bounds`` child elements with ``state`` attributes. See
+:ref:`layfile-interact-itemanim` for details.
+
+Layout elements (``element`` elements) may be configured to show only part of
+the element’s width or height using ``xscroll`` and/or ``yscroll`` child
+elements. This can be used for devices like slot machine reels. The
+``xscroll`` and ``yscroll`` elements support the same attributes:
+
+size
+ The size of the horizontal or vertical scroll window, as a proportion of the
+ element’s width or height, respectively. Must be in the range 0.01 to 1.0,
+ inclusive, if present (1% of the width/height to the full width/height). By
+ default, the entire width and height of the element is shown.
+wrap
+ Whether the element should wrap horizontally or vertically. Must be either
+ ``yes`` or ``no`` if present. By default, items do not wrap horizontally or
+ vertically.
+inputtag
+ If present, the horizontal or vertical scroll position will be taken from
+ the value of the corresponding I/O port. Specifies the tag path of an I/O
+ port relative to the device that caused the layout file to be loaded. The
+ raw value from the input port is used, active-low switch values are not
+ normalised.
+name
+ If present, the horizontal or vertical scroll position will be taken from
+ the correspondingly named output.
+mask
+ If present, the horizontal or vertical scroll position will be masked with
+ the value and shifted to the right to remove trailing zeroes (for example a
+ mask of 0x05 will result in no shift, while a mask of 0x68 will result in
+ the value being shifted three bits to the right). Note that this applies to
+ output values (specified with the ``name`` attribute) as well as input port
+ values (specified with the ``inputtag`` attribute). Must be an integer
+ value if present. If not present, it is equivalent to all 32 bits being
+ set.
+min
+ Minimum horizontal or vertical scroll position value. When the horizontal
+ or vertical scroll position has this value, the left or top edge or the
+ scroll window will be aligned with the left or top edge of the element.
+ Must be an integer value if present. Defaults to zero.
+max
+ Maximum horizontal or vertical scroll position value. Must be an integer
+ value if present. Defaults to the ``mask`` value shifted to the right to
+ remove trailing zeroes.
+
+
+.. _layfile-parts-collections:
+
+Collections
+~~~~~~~~~~~
- <element name="digit6" ref="digit"><bounds x="16" y="16" width="48" height="80" /></element>
- <element name="digit5" ref="digit"><bounds x="64" y="16" width="48" height="80" /></element>
- <element name="digit4" ref="digit"><bounds x="112" y="16" width="48" height="80" /></element>
- <element name="digit3" ref="digit"><bounds x="160" y="16" width="48" height="80" /></element>
- <element name="digit2" ref="digit"><bounds x="208" y="16" width="48" height="80" /></element>
- <element name="digit1" ref="digit"><bounds x="256" y="16" width="48" height="80" /></element>
+Collections of screens and/or layout elements can be shown or hidden by the user
+as desired. For example, a single view could include both displays and a
+clickable keypad, and allow the user to hide the keypad leaving only the
+displays visible. Collections are created using ``collection`` elements inside
+``view``, ``group`` and other ``collection`` elements.
+
+A collection element must have a ``name`` attribute providing the display name
+for the collection. Collection names must be unique within a view. The initial
+visibility of a collection may be specified by providing a ``visible``
+attribute. Set the ``visible`` attribute to ``yes`` if the collection should be
+initially visible, or ``no`` if it should be initially hidden. Collections are
+initially visible by default.
+
+Here is an example demonstrating the use of collections to allow parts of a view
+to be hidden by the user:
+
+.. code-block:: XML
+
+ <view name="LED Displays, CRT and Keypad">
+ <collection name="LED Displays">
+ <group ref="displays"><bounds x="240" y="0" width="320" height="47" /></group>
+ </collection>
+ <collection name="Keypad">
+ <group ref="keypad"><bounds x="650" y="57" width="148" height="140" /></group>
+ </collection>
+ <screen tag="screen"><bounds x="0" y="57" width="640" height="480" /></screen>
+ </view>
-If an element instantiating a layout element has ``inputtag`` and ``inputmask``
-attributes but lacks a ``name`` attribute, it will take its state from the value
-of the corresponding I/O port, masked with the ``inputmask`` value and XORed
-with the I/O port default field value. The latter is useful for inputs that are
-active-low. If the result is non-zero, the state is 1, otherwise it's 0. This
-is often used to allow clickable buttons and toggle switches to provide visible
-feedback. By using ``inputraw="1"``, it's possible to obtain the raw data from
-the I/O port, masked with the ``inputmask`` value and shifted to the right to
-remove trailing zeroes (for example a mask of 0x05 will result in no shift, while
-a mask of 0xb0 will result in the value being shifted four bits to the right).
-When handling mouse input, MAME treats all layout elements as being rectangular,
-and only activates the frontmost element whose area includes the location of the
-mouse pointer.
+A collection creates a nested parameter scope. Any ``param`` elements inside
+the collection element set parameters in the local scope for the collection.
+See :ref:`layfile-concepts-params` for more detail on parameters. (Note that
+the collection’s name and default visibility are not part of its content, and
+any parameter references in the ``name`` and ``visible`` attributes themselves
+will be substituted using parameter values from the collection’s parent’s
+scope.)
-.. _layout-parts-groups:
+.. _layfile-parts-groups:
Reusable groups
~~~~~~~~~~~~~~~
@@ -740,20 +920,24 @@ identifier. It is an error if a layout file contains multiple group definitions
with identical ``name`` attributes. The value of the ``name`` attribute is used
when instantiating the group from a view or another group. This is an example
opening tag for a group definition element inside the top-level ``mamelayout``
-element::
+element:
+
+.. code-block:: XML
<group name="panel">
This group may then be instantiated in a view or another group element using a
group reference element, optionally supplying destination bounds, orientation,
and/or modifier colour. The ``ref`` attribute identifies the group to
-instantiate -- in this example, destination bounds are supplied::
+instantiate – in this example, destination bounds are supplied:
+
+.. code-block:: XML
<group ref="panel"><bounds x="87" y="58" width="23" height="23.5" /></group>
Group definition elements allow all the same child elements as views.
Positioning and orienting screens, layout elements and nested groups works the
-same way as for views. See :ref:`layout-parts-views` for details. A group may
+same way as for views. See :ref:`layfile-parts-views` for details. A group may
instantiate other groups, but recursive loops are not permitted. It is an error
if a group directly or indirectly instantiates itself.
@@ -761,12 +945,14 @@ Groups have their own internal coordinate systems. If a group definition
element has no ``bounds`` element as a direct child, its bounds are computed as
the union of the bounds of all the screens, layout elements and/or nested groups
it instantiates. A ``bounds`` child element may be used to explicitly specify
-group bounds (see :ref:`layout-concepts-coordinates` for details). Note that
-groups' bounds are only used for the purpose of calculating the coordinate
+group bounds (see :ref:`layfile-concepts-coordinates` for details). Note that
+groups’ bounds are only used for the purpose of calculating the coordinate
transform when instantiating a group. A group may position screens and/or
elements outside its bounds, and they will not be cropped.
-To demonstrate how bounds calculation works, consider this example::
+To demonstrate how bounds calculation works, consider this example:
+
+.. code-block:: XML
<group name="autobounds">
<!-- bounds automatically calculated with origin at (5,10), width 30, and height 15 -->
@@ -785,8 +971,10 @@ To demonstrate how bounds calculation works, consider this example::
</view>
This is relatively straightforward, as all elements inherently fall within the
-group's automatically computed bounds. Now consider what happens if a group
-positions elements outside its explicit bounds::
+group’s automatically computed bounds. Now consider what happens if a group
+positions elements outside its explicit bounds:
+
+.. code-block:: XML
<group name="periphery">
<!-- elements are above the top edge and to the right of the right edge of the bounds -->
@@ -805,8 +993,8 @@ positions elements outside its explicit bounds::
<group ref="periphery"><bounds x="5" y="5" width="30" height="25" /></group>
</view>
-The group's elements are translated and scaled as necessary to distort the
-group's internal bounds to the destination bounds in the view. The group's
+The group’s elements are translated and scaled as necessary to distort the
+group’s internal bounds to the destination bounds in the view. The group’s
content is not restricted to its bounds. The view considers the bounds of the
actual layout elements when computing its bounds, not the destination bounds
specified for the group.
@@ -817,20 +1005,20 @@ the group is instantiated (*not* its lexical parent, the top-level
``mamelayout`` element). Any ``param`` elements inside the group definition
element set parameters in the local scope for the group instantiation. Local
parameters do not persist across multiple instantiations. See
-:ref:`layout-concepts-params` for more detail on parameters. (Note that the
-group's name is not part of its content, and any parameter references in the
+:ref:`layfile-concepts-params` for more detail on parameters. (Note that the
+group’s name is not part of its content, and any parameter references in the
``name`` attribute itself will be substituted at the point where the group
-definition appears in the top-level ``mamelayout`` element's scope.)
+definition appears in the top-level ``mamelayout`` element’s scope.)
-.. _layout-parts-repeats:
+.. _layfile-parts-repeats:
Repeating blocks
~~~~~~~~~~~~~~~~
Repeating blocks provide a concise way to generate or arrange large numbers of
similar elements. Repeating blocks are generally used in conjunction with
-generator parameters (see :ref:`layout-concepts-params`). Repeating blocks may
+generator parameters (see :ref:`layfile-concepts-params`). Repeating blocks may
be nested for more complex arrangements.
Repeating blocks are created with ``repeat`` elements. Each ``repeat`` element
@@ -848,12 +1036,14 @@ elements allowed inside a ``repeat`` element depend on where it appears:
A repeating block effectively repeats its contents the number of times specified
by its ``count`` attribute. See the relevant sections for details on how the
-child elements are used (:ref:`layout-parts`, :ref:`layout-parts-groups`, and
-:ref:`layout-parts-views`). A repeating block creates a nested parameter scope
+child elements are used (:ref:`layfile-parts`, :ref:`layfile-parts-groups`, and
+:ref:`layfile-parts-views`). A repeating block creates a nested parameter scope
inside the parameter scope of its lexical (DOM) parent element.
Generating white number labels from zero to eleven named ``label_0``,
-``label_1``, and so on (inside the top-level ``mamelayout`` element)::
+``label_1``, and so on (inside the top-level ``mamelayout`` element):
+
+.. code-block:: XML
<repeat count="12">
<param name="labelnum" start="0" increment="1" />
@@ -864,7 +1054,9 @@ Generating white number labels from zero to eleven named ``label_0``,
A horizontal row of forty digital displays, with five units space between them,
controlled by outputs ``digit0`` to ``digit39`` (inside a ``group`` or ``view``
-element)::
+element):
+
+.. code-block:: XML
<repeat count="40">
<param name="i" start="0" increment="1" />
@@ -875,7 +1067,9 @@ element)::
</repeat>
Eight five-by-seven dot matrix displays in a row, with pixels controlled by
-outputs ``Dot_000`` to ``Dot_764`` (inside a ``group`` or ``view`` element)::
+outputs ``Dot_000`` to ``Dot_764`` (inside a ``group`` or ``view`` element):
+
+.. code-block:: XML
<repeat count="8"> <!-- 8 digits -->
<param name="digitno" start="1" increment="1" />
@@ -894,7 +1088,9 @@ outputs ``Dot_000`` to ``Dot_764`` (inside a ``group`` or ``view`` element)::
</repeat>
Two horizontally separated, clickable, four-by-four keypads (inside a ``group``
-or ``view`` element)::
+or ``view`` element):
+
+.. code-block:: XML
<repeat count="2">
<param name="group" start="0" increment="4" />
@@ -924,7 +1120,9 @@ takes its initial value from the correspondingly named parameter in the
enclosing scope, but does not modify it.
Generating a chequerboard pattern with alternating alpha values 0.4 and 0.2
-(inside a ``group`` or ``view`` element)::
+(inside a ``group`` or ``view`` element):
+
+.. code-block:: XML
<repeat count="4">
<param name="pairy" start="3" increment="20" />
@@ -958,7 +1156,220 @@ tiles on each iteration. Rows are connected to I/O ports ``board:IN.7`` at the
top to ``board.IN.0`` at the bottom.
-.. _layout-errors:
+.. _layfile-interact:
+
+Interactivity
+-------------
+
+Interactive views are supported by allowing items to be bound to emulated
+outputs and I/O ports. Five kinds of interactivity are supported:
+
+Clickable items
+ If an item in a view is bound to an I/O port switch field, clicking the
+ item will activate the emulated switch.
+State-dependent components
+ Some components will be drawn differently depending on the containing
+ element’s state. These include the dot matrix, multi-segment LED display
+ and simple counter elements. See :ref:`layfile-parts-elements` for details.
+Conditionally-drawn components
+ Components may be conditionally drawn or hidden depending on the containing
+ element’s state by supplying ``state`` and/or ``statemask`` attributes. See
+ :ref:`layfile-parts-elements` for details.
+Component parameter animation
+ Components’ colour and position/size within their containing element may be
+ animated according the element’s state by providing multiple ``color``
+ and/or ``bounds`` elements with ``state`` attributes. See
+ :ref:`layfile-parts-elements` for details.
+Item parameter animation
+ Items’ colour and position/size within their containing view may be animated
+ according to their animation state.
+
+
+.. _layfile-interact-clickable:
+
+Clickable items
+~~~~~~~~~~~~~~~
+
+If a view item (``element`` or ``screen`` element) has ``inputtag`` and
+``inputmask`` attribute values that correspond to a digital switch field in the
+emulated system, clicking the element will activate the switch. The switch
+will remain active as long as the primary button is held down and the pointer is
+within the item’s current bounds. (Note that the bounds may change depending on
+the item’s animation state, see :ref:`layfile-interact-itemanim`).
+
+The ``inputtag`` attribute specifies the tag path of an I/O port relative to the
+device that caused the layout file to be loaded. The ``inputmask`` attribute
+must be an integer specifying the bits of the I/O port field that the item
+should activate. This sample shows instantiation of clickable buttons:
+
+The ``clickthrough`` attribute controls whether clicks can pass through the view
+item to other view items drawn above it. The ``clickthrough`` attribute must be
+``yes`` or ``no`` if present. The default is ``no`` (clicks do not pass
+through) for view items with ``inputtag`` and ``inputmask`` attributes, and
+``yes`` (clicks pass through) for other view items.
+
+.. code-block:: XML
+
+ <element ref="btn_3" inputtag="X2" inputmask="0x10">
+ <bounds x="2.30" y="4.325" width="1.0" height="1.0" />
+ </element>
+ <element ref="btn_0" inputtag="X0" inputmask="0x20">
+ <bounds x="0.725" y="5.375" width="1.0" height="1.0" />
+ </element>
+ <element ref="btn_rst" inputtag="RESET" inputmask="0x01">
+ <bounds x="1.775" y="5.375" width="1.0" height="1.0" />
+ </element>
+
+When handling pointer input, MAME treats all layout elements as being
+rectangular.
+
+
+.. _layfile-interact-elemstate:
+
+Element state
+~~~~~~~~~~~~~
+
+A view item that instantiates an element (``element`` element) may supply a
+state value to the element from an emulated I/O port or output. See
+:ref:`layfile-parts-elements` for details on how an element’s state affects its
+appearance.
+
+If the ``element`` element has a ``name`` attribute, the element state value
+will be taken from the value of the correspondingly named emulated output. Note
+that output names are global, which can become an issue when a machine uses
+multiple instances of the same type of device. This example shows how digital
+displays may be connected to emulated outputs:
+
+.. code-block:: XML
+
+ <element name="digit6" ref="digit"><bounds x="16" y="16" width="48" height="80" /></element>
+ <element name="digit5" ref="digit"><bounds x="64" y="16" width="48" height="80" /></element>
+ <element name="digit4" ref="digit"><bounds x="112" y="16" width="48" height="80" /></element>
+ <element name="digit3" ref="digit"><bounds x="160" y="16" width="48" height="80" /></element>
+ <element name="digit2" ref="digit"><bounds x="208" y="16" width="48" height="80" /></element>
+ <element name="digit1" ref="digit"><bounds x="256" y="16" width="48" height="80" /></element>
+
+If the ``element`` element has ``inputtag`` and ``inputmask`` attributes but
+lacks a ``name`` attribute, the element state value will be taken from the value
+of the corresponding I/O port, masked with the ``inputmask`` value. The
+``inputtag`` attribute specifies the tag path of an I/O port relative to the
+device that caused the layout file to be loaded. The ``inputmask`` attribute
+must be an integer specifying the bits of the I/O port field to use.
+
+If the ``element`` element has no ``inputraw`` attribute, or if the value of the
+``inputraw`` attribute is ``no``, the I/O port’s value is masked with the
+``inputmask`` value and XORed with the I/O port default field value. If the
+result is non-zero, the element state is 1, otherwise it’s 0. This is often
+used or provide visual feedback for clickable buttons, as values for active-high
+and active-low switches are normalised.
+
+If the ``element`` element has an ``inputraw`` attribute with the value ``yes``,
+the element state will be taken from the I/O port’s value masked with the
+``inputmask`` value and shifted to the right to remove trailing zeroes (for
+example a mask of 0x05 will result in no shift, while a mask of 0xb0 will result
+in the value being shifted four bits to the right). This is useful for
+obtaining the value of analog or positional inputs.
+
+
+.. _layfile-interact-itemanim:
+
+View item animation
+~~~~~~~~~~~~~~~~~~~
+
+Items’ colour and position/size within their containing view may be animated.
+This is achieved by supplying multiple ``color`` and/or ``bounds`` child
+elements with ``state`` attributes. The ``state`` attribute of each ``color``
+or ``bounds`` child element must be a non-negative integer. Within a view
+item, no two ``color`` elements may have equal state ``state`` attributes, and
+no two ``bounds`` elements may have equal ``state`` attributes.
+
+If the item’s animation state is lower than the ``state`` value of any
+``bounds`` child element, the position/size specified by the ``bounds`` child
+element with the lowest ``state`` value will be used. If the item’s
+animation state is higher than the ``state`` value of any ``bounds`` child
+element, the position/size specified by the ``bounds`` child element with the
+highest ``state`` value will be used. If the item’s animation state is between
+the ``state`` values of two ``bounds`` child elements, the position/size will be
+interpolated linearly.
+
+If the item’s animation state is lower than the ``state`` value of any ``color``
+child element, the colour specified by the ``color`` child element with the
+lowest ``state`` value will be used. If the item’s animation state is higher
+than the ``state`` value of any ``color`` child element, the colour specified by
+the ``color`` child element with the highest ``state`` value will be used. If
+the item’s animation state is between the ``state`` values of two ``color``
+child elements, the RGBA colour components will be interpolated linearly.
+
+An item’s animation state may be bound to an emulated output or input port by
+supplying an ``animate`` child element. If present, the ``animate`` element
+must have either an ``inputtag`` attribute or a ``name`` attribute (but not
+both). If the ``animate`` child element is not present, the item’s animation
+state is the same as its element state (see :ref:`layfile-interact-elemstate`).
+
+If the ``animate`` child element is present and has an ``inputtag``
+attribute, the item’s animation state will be taken from the value of the
+corresponding I/O port. The ``inputtag`` attribute specifies the tag path of an
+I/O port relative to the device that caused the layout file to be loaded. The
+raw value from the input port is used, active-low switch values are not
+normalised.
+
+If the ``animate`` child element is present and has a ``name`` attribute, the
+item’s animation state will be taken from the value of the correspondingly named
+emulated output. Note that output names are global, which can become an issue
+when a machine uses multiple instances of the same type of device.
+
+If the ``animate`` child element has a ``mask`` attribute, the item’s animation
+state will be masked with the ``mask`` value and shifted to the right to remove
+trailing zeroes (for example a mask of 0x05 will result in no shift, while a
+mask of 0xb0 will result in the value being shifted four bits to the right).
+Note that the ``mask`` attribute applies to output values (specified with the
+``name`` attribute) as well as input port values (specified with the
+``inputtag`` attribute). If the ``mask`` attribute is present, it must be an
+integer value. If the ``mask`` attribute is not present, it is equivalent to
+all 32 bits being set.
+
+This example shows elements with independent element state and animation state,
+using the animation state taken from emulated outputs to control their
+position:
+
+.. code-block:: XML
+
+ <repeat count="5">
+ <param name="x" start="10" increment="9" />
+ <param name="i" start="0" increment="1" />
+ <param name="mask" start="0x01" lshift="1" />
+
+ <element name="cg_sol~i~" ref="cosmo">
+ <animate name="cg_count~i~" />
+ <bounds state="0" x="~x~" y="10" width="6" height="7" />
+ <bounds state="255" x="~x~" y="48.5" width="6" height="7" />
+ </element>
+
+ <element ref="nothing" inputtag="FAKE1" inputmask="~mask~">
+ <animate name="cg_count~i~" />
+ <bounds state="0" x="~x~" y="10" width="6" height="7" />
+ <bounds state="255" x="~x~" y="48.5" width="6" height="7" />
+ </element>
+ </repeat>
+
+This example shows elements with independent element state and animation state,
+using the animation state taken from an emulated positional input to control
+their positions:
+
+.. code-block:: XML
+
+ <repeat count="4">
+ <param name="y" start="1" increment="3" />
+ <param name="n" start="0" increment="1" />
+ <element ref="ledr" name="~n~.7">
+ <animate inputtag="IN.1" mask="0x0f" />
+ <bounds state="0" x="0" y="~y~" width="1" height="1" />
+ <bounds state="11" x="16.5" y="~y~" width="1" height="1" />
+ </element>
+ </repeat>
+
+
+.. _layfile-errors:
Error handling
--------------
@@ -974,7 +1385,7 @@ Error handling
screens are considered unviable and not available to the user.
-.. _layout-autogen:
+.. _layfile-autogen:
Automatically-generated views
-----------------------------
@@ -984,24 +1395,24 @@ layouts, MAME automatically generates views based on the machine configuration.
The following views will be automatically generated:
* If the system has no screens and no viable views were found in the internal
- and external layouts, MAME will load a view that shows the message "No screens
- attached to the system".
+ and external layouts, MAME will load a view that shows the message “No screens
+ attached to the system”.
* For each emulated screen, MAME will generate a view showing the screen at its
physical aspect ratio with rotation applied.
-* For each emulated screen where the configured pixel aspect ratio doesn't match
+* For each emulated screen where the configured pixel aspect ratio doesn’t match
the physical aspect ratio, MAME will generate a view showing the screen at an
aspect ratio that produces square pixels, with rotation applied.
* If the system has a single emulated screen, MAME will generate a view showing
two copies of the screen image above each other with a small gap between them.
The upper copy will be rotated by 180 degrees. This view can be used in a
- "cocktail table" cabinet for simultaneous two-player games, or alternating
- play games that don't automatically rotate the display for the second player.
+ “cocktail table” cabinet for simultaneous two-player games, or alternating
+ play games that don’t automatically rotate the display for the second player.
The screen will be displayed at its physical aspect ratio, with rotation
applied.
* If the system has exactly two emulated screens, MAME will generate a view
showing the second screen above the first screen with a small gap between
them. The second screen will be rotated by 180 degrees. This view can be
- used to play a dual-screen two-player game on a "cocktail table" cabinet with
+ used to play a dual-screen two-player game on a “cocktail table” cabinet with
a single screen. The screens will be displayed at their physical aspect
ratios, with rotation applied.
* If the system has exactly two emulated screens and no view in the internal or
@@ -1017,23 +1428,23 @@ The following views will be automatically generated:
will be displayed at physical aspect ratio, with rotation applied.
-.. _layout-complay:
+.. _layfile-complay:
Using complay.py
----------------
The MAME source contains a Python script called ``complay.py``, found in the
-``scripts/build`` subdirectory. This script is used as part of MAME's build
+``scripts/build`` subdirectory. This script is used as part of MAME’s build
process to reduce the size of data for internal layouts and convert it to a form
that can be built into the executable. However, it can also detect many common
layout file format errors, and generally provides better error messages than
-MAME does when loading a layout file. Note that it doesn't actually run the
-whole layout engine, so it can't detect errors like undefined element references
+MAME does when loading a layout file. Note that it doesn’t actually run the
+whole layout engine, so it can’t detect errors like undefined element references
when parameters are used, or recursively nested groups. The ``complay.py``
script is compatible with both Python 2.7 and Python 3 interpreters.
-The ``complay.py`` script takes three parameters -- an input file name, an
-output file name, and a base name for variables in the output:
+The ``complay.py`` script takes three parameters – an input file name, an output
+file name, and a base name for variables in the output:
**python scripts/build/complay.py** *<input>* [*<output>* [*<varname>*]]
@@ -1047,6 +1458,49 @@ in case of an I/O error. If an output file name is specified, the file will be
created/overwritten on success or removed on failure.
To check a layout file for common errors, run the script with the path to the
-file no check and no output file name or base variable name. For example:
+file to check and no output file name or base variable name. For example:
**python scripts/build/complay.py artwork/dino/default.lay**
+
+
+.. _layfile-examples:
+
+Example layout files
+--------------------
+
+These layout files demonstrate various artwork system features. They are all
+internal layouts included in MAME.
+
+`sstrangr.lay <https://git.redump.net/mame/tree/src/mame/layout/sstrangr.lay?h=mame0261>`_
+ A simple case of using translucent colour overlays to visually separate and
+ highlight elements on a black and white screen.
+`seawolf.lay <https://git.redump.net/mame/tree/src/mame/layout/seawolf.lay?h=mame0261>`_
+ This system uses lamps for key gameplay elements. Blending modes are used
+ for the translucent colour overlay placed over the monitor, and the lamps
+ reflected in front of the monitor. Also uses collections to allow parts of
+ the layout to be disabled selectively.
+`armora.lay <https://git.redump.net/mame/tree/src/mame/layout/armora.lay?h=mame0261>`_
+ This game’s monitor is viewed directly through a translucent colour overlay
+ rather than being reflected from inside the cabinet. This means the overlay
+ reflects ambient light as well as affecting the colour of the video image.
+ The shapes on the overlay are drawn using embedded SVG images.
+`tranz330.lay <https://git.redump.net/mame/tree/src/mame/layout/tranz330.lay?h=mame0261>`_
+ A multi-segment alphanumeric display and keypad. The keys are clickable,
+ and provide visual feedback when pressed.
+`esq2by16.lay <https://git.redump.net/mame/tree/src/mame/layout/esq2by16.lay?h=mame0261>`_
+ Builds up a multi-line dot matrix character display. Repeats are used to
+ avoid repetition for the rows in a character, characters in a line, and
+ lines in a page. Group colors allow a single element to be used for all
+ four display colours.
+`cgang.lay <https://git.redump.net/mame/tree/src/mame/layout/cgang.lay?h=mame0261>`_
+ Animates the position of element items to simulate an electromechanical
+ shooting gallery game. Also demonstrates effective use of components to
+ build up complex graphics.
+`minspace.lay <https://git.redump.net/mame/tree/src/mame/layout/minspace.lay?h=mame0261>`_
+ Shows the position of a slider control with LEDs on it.
+`md6802.lay <https://git.redump.net/mame/tree/src/mame/layout/md6802.lay?h=mame0261>`_
+ Effectively using groups as a procedural programming language to build up an
+ image of a trainer board.
+`beena.lay <https://git.redump.net/mame/tree/src/mame/layout/beena.lay?h=mame0261>`_
+ Using event-based scripting to dynamically position elements and draw elemnt
+ content programmatically.
diff --git a/docs/source/techspecs/layout_script.rst b/docs/source/techspecs/layout_script.rst
new file mode 100644
index 00000000000..f3ff505c9df
--- /dev/null
+++ b/docs/source/techspecs/layout_script.rst
@@ -0,0 +1,786 @@
+.. _layscript:
+
+MAME Layout Scripting
+=====================
+
+.. contents:: :local:
+
+
+.. _layscript-intro:
+
+Introduction
+------------
+
+MAME layout files can embed Lua script to provide enhanced functionality.
+Although there’s a lot you can do with conditionally drawn components and
+parameter animation, some things can only be done with scripting. MAME uses an
+event-based model. Scripts can supply functions that will be called after
+certain events, or when certain data is required.
+
+Layout scripting requires the :ref:`layout plugin <plugins-layout>` to be
+enabled. For example, to run BWB Double Take with the Lua script in the layout
+enabled, you might use this command::
+
+ mame -plugins -plugin layout v4dbltak
+
+You may want to add the settings to enable the layout plugin to an INI file to
+save having to enable it every time you start a system. See :ref:`plugins` for
+more information about using plugins with MAME.
+
+
+.. _layscript-examples:
+
+Practical examples
+------------------
+
+Before diving into the technical details of how it works, we’ll start with some
+example layout files using Lua script for enhancement. It’s assumed that you’re
+familiar with MAME’s artwork system and have a basic understanding of Lua
+scripting. For details on MAME’s layout file, see :ref:`layfile`; for detailed
+descriptions of MAME’s Lua interface, see :ref:`luascript`.
+
+.. _layscript-examples-espial:
+
+Espial: joystick split across ports
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Take a look at the player input definitions for Espial:
+
+.. code-block:: C++
+
+ PORT_START("IN1")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_COCKTAIL
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_COCKTAIL
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY PORT_COCKTAIL
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_COCKTAIL
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL
+
+ PORT_START("IN2")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_COIN1 )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNKNOWN )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON1 )
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY
+
+There are two joysticks, one used for both players on an upright cabinet or the
+first player on a cocktail cabinet, and one used for the second player on a
+cocktail cabinet. Notice that the switches for the first joystick are split
+across the two I/O ports.
+
+There’s no layout file syntax to build the element state using bits from
+multiple I/O ports. It’s also inconvenient if each joystick needs to be defined
+as a separate element because the bits for the switches aren’t arranged the same
+way.
+
+We can overcome these limitations using a script to read the player inputs and
+set the items’ element state:
+
+.. code-block:: XML
+
+ <?xml version="1.0"?>
+ <mamelayout version="2">
+
+ <!-- element for drawing a joystick -->
+ <!-- up = 1 (bit 0), down = 2 (bit 1), left = 4 (bit 2), right = 8 (bit 3) -->
+ <element name="stick" defstate="0">
+ <image state="0x0" file="stick_c.svg" />
+ <image state="0x1" file="stick_u.svg" />
+ <image state="0x9" file="stick_ur.svg" />
+ <image state="0x8" file="stick_r.svg" />
+ <image state="0xa" file="stick_dr.svg" />
+ <image state="0x2" file="stick_d.svg" />
+ <image state="0x6" file="stick_dl.svg" />
+ <image state="0x4" file="stick_l.svg" />
+ <image state="0x5" file="stick_ul.svg" />
+ </element>
+
+ <!-- we'll warn the user if the layout plugin isn't enabled -->
+ <!-- draw only when state is 1, and set the default state to 1 so warning is visible initially -->
+ <element name="warning" defstate="1">
+ <text state="1" string="This view requires the layout plugin." />
+ </element>
+
+ <!-- view showing the screen and joysticks on a cocktail cabinet -->
+ <view name="Joystick Display">
+ <!-- draw the screen with correct aspect ratio -->
+ <screen index="0">
+ <bounds x="0" y="0" width="4" height="3" />
+ </screen>
+
+ <!-- first joystick, id attribute allows script to find item -->
+ <!-- no bindings, state will be set by the script -->
+ <element id="joy_p1" ref="stick">
+ <!-- position below the screen -->
+ <bounds xc="2" yc="3.35" width="0.5" height="0.5" />
+ </element>
+
+ <!-- second joystick, id attribute allows script to find item -->
+ <!-- no bindings, state will be set by the script -->
+ <element id="joy_p2" ref="stick">
+ <!-- screen is flipped for second player, so rotate by 180 degrees -->
+ <orientation rotate="180" />
+ <!-- position above the screen -->
+ <bounds xc="2" yc="-0.35" width="0.5" height="0.5" />
+ </element>
+
+ <!-- warning text item also has id attribute so the script can find it -->
+ <element id="warning" ref="warning">
+ <!-- position over the screen near the bottom -->
+ <bounds x="0.2" y="2.6" width="3.6" height="0.2" />
+ </element>
+ </view>
+
+ <!-- the content of the script element will be called as a function by the layout plugin -->
+ <!-- use CDATA block to avoid the need to escape angle brackets and ampersands -->
+ <script><![CDATA[
+ -- file is the layout file object
+ -- set a function to call after resolving tags
+ file:set_resolve_tags_callback(
+ function ()
+ -- file.device is the device that caused the layout to be loaded
+ -- in this case, it's the root machine driver for espial
+ -- look up the two I/O ports we need to be able to read
+ local in1 = file.device:ioport("IN1")
+ local in2 = file.device:ioport("IN2")
+
+ -- look up the view items for showing the joystick state
+ local p1_stick = file.views["Joystick Display"].items["joy_p1"]
+ local p2_stick = file.views["Joystick Display"].items["joy_p2"]
+
+ -- set a function to call before adding the view items to the render target
+ file.views["Joystick Display"]:set_prepare_items_callback(
+ function ()
+ -- read the two player input I/O ports
+ local in1_val = in1:read()
+ local in2_val = in2:read()
+
+ -- set element state for first joystick
+ p1_stick:set_state(
+ ((in2_val & 0x10) >> 4) | -- shift up from IN2 bit 4 to bit 0
+ ((in1_val & 0x20) >> 4) | -- shift down from IN1 bit 5 to bit 1
+ ((in2_val & 0x80) >> 5) | -- shift left from IN2 bit 7 to bit 2
+ (in2_val & 0x08)) -- right is in IN2 bit 3
+
+ -- set element state for second joystick
+ p2_stick:set_state(
+ ((in1_val & 0x10) >> 4) | -- shift up from IN1 bit 4 to bit 0
+ ((in1_val & 0x40) >> 5) | -- shift down from IN1 bit 6 to bit 1
+ (in1_val & 0x04) | -- left is in IN1 bit 2
+ (in1_val & 0x08)) -- right is in IN1 bit 3
+ end)
+
+ -- hide the warning, since if we got here the script is running
+ file.views["Joystick Display"].items["warning"]:set_state(0)
+ end)
+ ]]></script>
+
+ </mamelayout>
+
+The layout has a ``script`` element containing the Lua script. This is called
+as a function by the layout plugin when the layout file is loaded. The layout
+views have been built at this point, but the emulated system has not finished
+starting. In particular, it’s not safe to access inputs and outputs at this
+time. The key variable in the script environment is ``file``, which gives the
+script access to its :ref:`layout file <luascript-ref-renderlayfile>`.
+
+We supply a function to be called after tags in the layout file have been
+resolved. At this point, the emulated system will have completed starting.
+This function does the following tasks:
+
+* Looks up the two :ref:`I/O ports <luascript-ref-ioport>` used for player
+ input. I/O ports can be looked up by tag relative to the device that caused
+ the layout file to be loaded.
+* Looks up the two :ref:`view items <luascript-ref-renderlayitem>` used to
+ display joystick state. Views can be looked up by name (i.e. value of the
+ ``name`` attribute), and items within a view can be looked up by ID (i.e. the
+ value of the ``id`` attribute).
+* Supplies a function to be called before view items are added to the render
+ target when drawing a frame.
+* Hides the warning that reminds the user to enable the layout plugin by setting
+ the element state for the item to 0 (the text component is only drawn when
+ the element state is 1).
+
+The function called before view items are added to the render target reads the
+player inputs, and shuffles the bits into the order needed by the joystick
+element.
+
+.. _layscript-examples-starwars:
+
+Star Wars: animation on two axes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We’ll make a layout that shows the position of the flight yoke for Atari Star
+Wars. The input ports are straightforward – each analog axis produces a value
+in the range from 0x00 (0) to 0xff (255), inclusive:
+
+.. code-block:: C++
+
+ PORT_START("STICKY")
+ PORT_BIT( 0xff, 0x80, IPT_AD_STICK_Y ) PORT_SENSITIVITY(70) PORT_KEYDELTA(30)
+
+ PORT_START("STICKX")
+ PORT_BIT( 0xff, 0x80, IPT_AD_STICK_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(30)
+
+Here’s our layout file:
+
+.. code-block:: XML
+
+ <?xml version="1.0"?>
+ <mamelayout version="2">
+
+ <!-- a square with a white outline 1% of its width -->
+ <element name="outline">
+ <rect><bounds x="0.00" y="0.00" width="1.00" height="0.01" /></rect>
+ <rect><bounds x="0.00" y="0.99" width="1.00" height="0.01" /></rect>
+ <rect><bounds x="0.00" y="0.00" width="0.01" height="1.00" /></rect>
+ <rect><bounds x="0.99" y="0.00" width="0.01" height="1.00" /></rect>
+ </element>
+
+ <!-- a rectangle with a vertical line 10% of its width down the middle -->
+ <element name="line">
+ <!-- use a transparent rectangle to force element dimensions -->
+ <rect>
+ <bounds x="0" y="0" width="0.1" height="1" />
+ <color alpha="0" />
+ </rect>
+ <!-- this is the visible white line -->
+ <rect><bounds x="0.045" y="0" width="0.01" height="1" /></rect>
+ </element>
+
+ <!-- an outlined square inset by 20% with lines 10% of the element width/height -->
+ <element name="box">
+ <!-- use a transparent rectangle to force element dimensions -->
+ <rect>
+ <bounds x="0" y="0" width="0.1" height="0.1" />
+ <color alpha="0" />
+ </rect>
+ <!-- draw the outline of a square -->
+ <rect><bounds x="0.02" y="0.02" width="0.06" height="0.01" /></rect>
+ <rect><bounds x="0.02" y="0.07" width="0.06" height="0.01" /></rect>
+ <rect><bounds x="0.02" y="0.02" width="0.01" height="0.06" /></rect>
+ <rect><bounds x="0.07" y="0.02" width="0.01" height="0.06" /></rect>
+ </element>
+
+ <!-- we'll warn the user if the layout plugin isn't enabled -->
+ <!-- draw only when state is 1, and set the default state to 1 so warning is visible initially -->
+ <element name="warning" defstate="1">
+ <text state="1" string="This view requires the layout plugin." />
+ </element>
+
+ <!-- view showing the screen and flight yoke position -->
+ <view name="Analog Control Display">
+ <!-- draw the screen with correct aspect ratio -->
+ <screen index="0">
+ <bounds x="0" y="0" width="4" height="3" />
+ </screen>
+
+ <!-- draw the white outlined square to the right of the screen near the bottom -->
+ <!-- the script uses the size of this item to determine movement ranges -->
+ <element id="outline" ref="outline">
+ <bounds x="4.1" y="1.9" width="1.0" height="1.0" />
+ </element>
+
+ <!-- vertical line for displaying X axis input -->
+ <element id="vertical" ref="line">
+ <!-- element draws a vertical line, no need to rotate it -->
+ <orientation rotate="0" />
+ <!-- centre it in the square horizontally, using the full height -->
+ <bounds x="4.55" y="1.9" width="0.1" height="1" />
+ </element>
+
+ <!-- horizontal line for displaying Y axis input -->
+ <element id="horizontal" ref="line">
+ <!-- rotate the element by 90 degrees to get a horizontal line -->
+ <orientation rotate="90" />
+ <!-- centre it in the square vertically, using the full width -->
+ <bounds x="4.1" y="2.35" width="1" height="0.1" />
+ </element>
+
+ <!-- draw a small box at the intersection of the vertical and horizontal lines -->
+ <element id="box" ref="box">
+ <bounds x="4.55" y="2.35" width="0.1" height="0.1" />
+ </element>
+
+ <!-- draw the warning text over the screen near the bottom -->
+ <element id="warning" ref="warning">
+ <bounds x="0.2" y="2.6" width="3.6" height="0.2" />
+ </element>
+ </view>
+
+ <!-- the content of the script element will be called as a function by the layout plugin -->
+ <!-- use CDATA block to avoid the need to escape angle brackets and ampersands -->
+ <script><![CDATA[
+ -- file is the layout file object
+ -- set a function to call after resolving tags
+ file:set_resolve_tags_callback(
+ function ()
+ -- file.device is the device that caused the layout to be loaded
+ -- in this case, it's the root machine driver for starwars
+ -- find the analog axis inputs
+ local x_input = file.device:ioport("STICKX")
+ local y_input = file.device:ioport("STICKY")
+
+ -- find the outline item
+ local outline_item = file.views["Analog Control Display"].items["outline"]
+
+ -- variables for keeping state across callbacks
+ local outline_bounds -- bounds of the outlined square
+ local width, height -- width and height for animated items
+ local x_scale, y_scale -- ratios of axis units to render coordinates
+ local x_pos, y_pos -- display positions for the animated items
+
+ -- set a function to call when view dimensions have been recalculated
+ -- this can happen when when the window is resized or scaling options are changed
+ file.views["Analog Control Display"]:set_recomputed_callback(
+ function ()
+ -- get the bounds of the outlined square
+ outline_bounds = outline_item.bounds
+ -- animated items use 10% of the width/height of the square
+ width = outline_bounds.width * 0.1
+ height = outline_bounds.height * 0.1
+ -- calculate ratios of axis units to render coordinates
+ -- animated items leave 90% of the width/height for the movement range
+ -- the end of the range of each axis is at 0xff
+ x_scale = outline_bounds.width * 0.9 / 0xff
+ y_scale = outline_bounds.height * 0.9 / 0xff
+ end)
+
+ -- set a function to call before adding the view items to the render target
+ file.views["Analog Control Display"]:set_prepare_items_callback(
+ function ()
+ -- read analog axes, reverse Y axis as zero is at the bottom
+ local x = x_input:read() & 0xff
+ local y = 0xff - (y_input:read() & 0xff)
+ -- convert the input values to layout coordinates
+ -- use the top left corner of the outlined square as the origin
+ x_pos = outline_bounds.x0 + (x * x_scale)
+ y_pos = outline_bounds.y0 + (y * y_scale)
+ end)
+
+ -- set a function to supply the bounds for the vertical line
+ file.views["Analog Control Display"].items["vertical"]:set_bounds_callback(
+ function ()
+ -- create a new render bounds object (starts as a unit square)
+ local result = emu.render_bounds()
+ -- set left, top, width and height
+ result:set_wh(
+ x_pos, -- calculated X position for animated items
+ outline_bounds.y0, -- top of outlined square
+ width, -- 10% of width of outlined square
+ outline_bounds.height) -- full height of outlined square
+ return result
+ end)
+
+ -- set a function to supply the bounds for the horizontal line
+ file.views["Analog Control Display"].items["horizontal"]:set_bounds_callback(
+ function ()
+ -- create a new render bounds object (starts as a unit square)
+ local result = emu.render_bounds()
+ -- set left, top, width and height
+ result:set_wh(
+ outline_bounds.x0, -- left of outlined square
+ y_pos, -- calculated Y position for animated items
+ outline_bounds.width, -- full width of outlined square
+ height) -- 10% of height of outlined square
+ return result
+ end)
+
+ -- set a function to supply the bounds for the box at the intersection of the lines
+ file.views["Analog Control Display"].items["box"]:set_bounds_callback(
+ function ()
+ -- create a new render bounds object (starts as a unit square)
+ local result = emu.render_bounds()
+ -- set left, top, width and height
+ result:set_wh(
+ x_pos, -- calculated X position for animated items
+ y_pos, -- calculated Y position for animated items
+ width, -- 10% of width of outlined square
+ height) -- 10% of height of outlined square
+ return result
+ end)
+
+ -- hide the warning, since if we got here the script is running
+ file.views["Analog Control Display"].items["warning"]:set_state(0)
+ end)
+ ]]></script>
+
+ </mamelayout>
+
+The layout has a ``script`` element containing the Lua script, to be called as a
+function by the layout plugin when the layout file is loaded. This happens
+after the layout views have been build, but before the emulated system has
+finished starting. The :ref:`layout file <luascript-ref-renderlayfile>` object
+is supplied to the script in the ``file`` variable.
+
+We supply a function to be called after tags in the layout file have been
+resolved. This function does the following:
+
+* Looks up the analog axis :ref:`inputs <luascript-ref-ioport>`.
+* Looks up the :ref:`view item <luascript-ref-renderlayitem>` that draws the
+ outline of area where the yoke position is displayed.
+* Declares some variables to hold calculated values across function calls.
+* Supplies a function to be called when the view’s dimensions have been
+ recomputed.
+* Supplies a function to be called before adding view items to the render
+ container when drawing a frame.
+* Supplies functions that will supply the bounds for the animated items.
+* Hides the warning that reminds the user to enable the layout plugin by setting
+ the element state for the item to 0 (the text component is only drawn when
+ the element state is 1).
+
+The view is looked up by name (value of its ``name`` attribute), and items
+within the view are looked up by ID (values of their ``id`` attributes).
+
+Layout view dimensions are recomputed in response to several events, including
+the window being resized, entering/leaving full screen mode, toggling visibility
+of item collections, and changing the zoom to screen area setting. When this
+happens, we need to update our size and animation scale factors. We get the
+bounds of the square where the yoke position is displayed, calculate the size
+for the animated items, and calculate the ratios of axis units to render target
+coordinates in each direction. It’s more efficient to do these calculations
+only when the results may change.
+
+Before view items are added to the render target, we read the analog axis inputs
+and convert the values to coordinates positions for the animated items. The Y
+axis input uses larger values to aim higher, so we need to reverse the value by
+subtracting it from 0xff (255). We add in the coordinates of the top left
+corner of the square where we’re displaying the yoke position. We do this once
+each time the layout is drawn for efficiency, since we can use the values for
+all three animated items.
+
+Finally, we supply bounds for the animated items when required. These functions
+need to return ``render_bounds`` objects giving the position and size of the
+items in render target coordinates.
+
+(Since the vertical and horizontal line elements each only move on a single
+axis, it would be possible to animate them using the layout file format’s item
+animation features. Only the box at the intersection of the line actually
+requires scripting. It’s done entirely using scripting here for illustrative
+purposes.)
+
+
+.. _layscript-environment:
+
+The layout script environment
+-----------------------------
+
+The Lua environment is provided by the layout plugin. It’s fairly minimal, only
+providing what’s needed:
+
+* ``file`` giving the script’s :ref:`layout file <luascript-ref-renderlayfile>`
+ object. Has a ``device`` property for obtaining the :ref:`device
+ <luascript-ref-device>` that caused the layout file to be loaded, and a
+ ``views`` property for obtaining the layout’s :ref:`views
+ <luascript-ref-renderlayview>` (indexed by name).
+* ``machine`` giving MAME’s current :ref:`running machine
+ <luascript-ref-machine>`.
+* ``emu.device_enumerator``, ``emu.palette_enumerator``,
+ ``emu.screen_enumerator``, ``emu.cassette_enumerator``,
+ ``emu.image_enumerator`` and ``emu.slot_enumerator`` functions for obtaining
+ specific device interfaces.
+* ``emu.attotime``, ``emu.render_bounds`` and ``emu.render_color`` functions for
+ creating :ref:`attotime <luascript-ref-attotime>`, :ref:`bounds
+ <luascript-ref-renderbounds>` and :ref:`colour <luascript-ref-rendercolor>`
+ objects.
+* ``emu.bitmap_ind8``, ``emu.bitmap_ind16``, ``emu.bitmap_ind32``,
+ ``emu.bitmap_ind64``, ``emu.bitmap_yuy16``, ``emu.bitmap_rgb32`` and
+ ``emu.bitmap_argb32`` objects for creating
+ :ref:`bitmaps <luascript-ref-bitmap>`.
+* ``emu.print_verbose``, ``emu.print_error``, ``emu.print_warning``,
+ ``emu.print_info`` and ``emu.print_debug`` functions for diagnostic output.
+* Standard Lua ``tonumber``, ``tostring``, ``pairs`` and ``ipairs`` functions,
+ and ``math``, ``table`` and ``string`` objects for manipulating numbers,
+ strings, tables and other containers.
+* Standard Lua ``print`` function for text output to the console.
+
+
+.. _layscript-events:
+
+Layout events
+-------------
+
+MAME layout scripting uses an event-based model. Scripts can supply functions
+to be called after events occur, or when data is needed. There are three levels
+of events: layout file events, layout view events, and layout view item events.
+
+.. _layscript-events-file:
+
+Layout file events
+~~~~~~~~~~~~~~~~~~
+
+Layout file events apply to the file as a whole, and not to an individual view.
+
+Resolve tags
+ ``file:set_resolve_tags_callback(cb)``
+
+ Called after the emulated system has finished starting, input and output
+ tags in the layout have been resolved, and default item callbacks have been
+ set up. This is a good time to look up inputs and set up view item event
+ handlers.
+
+ The callback function has no return value and takes no parameters. Call
+ with ``nil`` as the argument to remove the event handler.
+
+.. _layscript-events-view:
+
+Layout view events
+~~~~~~~~~~~~~~~~~~
+
+Layout view events apply to an individual view.
+
+Prepare items
+ ``view:set_prepare_items_callback(cb)``
+
+ Called before the view’s items are added to the render target in preparation
+ for drawing a video frame.
+
+ The callback function has no return value and takes no parameters. Call
+ with ``nil`` as the argument to remove the event handler.
+Preload
+ ``view:set_preload_callback(cb)``
+
+ Called after pre-loading visible view elements. This can happen when the
+ view is selected for the first time in a session, or when the user toggles
+ visibility of an element collection on. Be aware that this can be called
+ multiple times in a session and avoid repeating expensive tasks.
+
+ The callback function has no return value and takes no parameters. Call
+ with ``nil`` as the argument to remove the event handler.
+Dimensions recomputed
+ ``view:set_recomputed_callback(cb)``
+
+ Called after view dimensions are recomputed. This happens in several
+ situations, including the window being resized, entering or leaving full
+ screen mode, toggling visibility of item collections, and changes to the
+ rotation and zoom to screen area settings. If you’re animating the position
+ of view items, this is a good time to calculate positions and scale factors.
+
+ The callback function has no return value and takes no parameters. Call
+ with ``nil`` as the argument to remove the event handler.
+Pointer updated
+ ``view:set_pointer_updated_callback(cb)``
+
+ Called when a pointer enters, moves or changes button state over the view.
+
+ The callback function is passed nine arguments:
+
+ * The pointer type as a string. This will be ``mouse``, ``pen``, ``touch``
+ or ``unknown``, and will not change for the lifetime of a pointer.
+ * The pointer ID. This will be a non-negative integer that will not change
+ for the lifetime of a pointer. Pointer ID values are recycled
+ aggressively.
+ * The device ID. This will be a non-negative integer that can be used to
+ group pointers for recognising multi-touch gestures.
+ * The horizontal position of the pointer in layout coordinates.
+ * The vertical position of the pointer in layout coordinates.
+ * A bit mask representing the currently pressed buttons. The primary button
+ is the least significant bit.
+ * A bit mask representing the buttons that were pressed in this update. The
+ primary button is the least significant bit.
+ * A bit mask representing the buttons that were released in this update.
+ The primary button is the least significant bit.
+ * The click count. This is positive for multi-click actions, or negative if
+ a click is turned into a hold or drag. This only applies to the primary
+ button.
+
+ The callback function has no return value. Call with ``nil`` as the
+ argument to remove the event handler.
+Pointer left
+ ``view:set_pointer_left_callback(cb)``
+
+ Called when a pointer leaves the view normally. After receiving this event,
+ the pointer ID may be reused for a new pointer.
+
+ The callback function is passed seven arguments:
+
+ * The pointer type as a string. This will be ``mouse``, ``pen``, ``touch``
+ or ``unknown``, and will not change for the lifetime of a pointer.
+ * The pointer ID. This will be a non-negative integer that will not change
+ for the lifetime of a pointer. Pointer ID values are recycled
+ aggressively.
+ * The device ID. This will be a non-negative integer that can be used to
+ group pointers for recognising multi-touch gestures.
+ * The horizontal position of the pointer in layout coordinates.
+ * The vertical position of the pointer in layout coordinates.
+ * A bit mask representing the buttons that were released in this update.
+ The primary button is the least significant bit.
+ * The click count. This is positive for multi-click actions, or negative if
+ a click is turned into a hold or drag. This only applies to the primary
+ button.
+
+ The callback function has no return value. Call with ``nil`` as the
+ argument to remove the event handler.
+Pointer aborted
+ ``view:set_pointer_aborted_callback(cb)``
+
+ Called when a pointer leaves the view abnormally. After receiving this
+ event, the pointer ID may be reused for a new pointer.
+
+ The callback function is passed seven arguments:
+
+ * The pointer type as a string. This will be ``mouse``, ``pen``, ``touch``
+ or ``unknown``, and will not change for the lifetime of a pointer.
+ * The pointer ID. This will be a non-negative integer that will not change
+ for the lifetime of a pointer. Pointer ID values are recycled
+ aggressively.
+ * The device ID. This will be a non-negative integer that can be used to
+ group pointers for recognising multi-touch gestures.
+ * The horizontal position of the pointer in layout coordinates.
+ * The vertical position of the pointer in layout coordinates.
+ * A bit mask representing the buttons that were released in this update.
+ The primary button is the least significant bit.
+ * The click count. This is positive for multi-click actions, or negative if
+ a click is turned into a hold or drag. This only applies to the primary
+ button.
+
+ The callback function has no return value. Call with ``nil`` as the
+ argument to remove the event handler.
+Forget pointers
+ ``view:set_forget_pointers_callback(cb)``
+
+ Called when the view should stop processing pointer input. This can happen
+ in a number of situations, including:
+
+ * The user activated a menu.
+ * The view configuration will change.
+ * The view will be deactivated.
+
+ The callback function has no return value and takes no parameters. Call
+ with ``nil`` as the argument to remove the event handler.
+
+.. _layscript-events-item:
+
+Layout view item events
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Layout view item callbacks apply to individual items within a view. They are
+used to override items’ default element state, animation state, bounds and
+colour behaviour.
+
+Get element state
+ ``item:set_element_state_callback(cb)``
+
+ Set callback for getting the item’s element state. This controls how the
+ item’s element is drawn, for components that change appearance depending on
+ state, conditionally-drawn components, and component bounds/colour
+ animation. Do not attempt to access the item’s ``element_state`` property
+ from the callback, as it will result in infinite recursion.
+
+ The callback function must return an integer, and takes no parameters. Call
+ with ``nil`` as the argument to restore the default element state
+ handler (based on the item’s XML attributes).
+Get animation state
+ ``item:set_animation_state_callback(cb)``
+
+ Set callback for getting the item’s animation state. This is used for item
+ bounds/colour animation. Do not attempt to access the item’s
+ ``animation_state`` property from the callback, as it will result in
+ infinite recursion.
+
+ The callback function must return an integer, and takes no parameters. Call
+ with ``nil`` as the argument to restore the default animation state handler
+ (based on the item’s XML attributes and ``animate`` child element).
+Get item bounds
+ ``item:set_bounds_callback(cb)``
+
+ Set callback for getting the item’s bounds (position and size). Do not
+ attempt to access the item’s ``bounds`` property from the callback, as it
+ will result in infinite recursion.
+
+ The callback function must return a render bounds object representing the
+ item’s bounds in render target coordinates (usually created by calling
+ ``emu.render_bounds``), and takes no parameters. Call with ``nil`` as the
+ argument to restore the default bounds handler (based on the item’s
+ animation state and ``bounds`` child elements).
+Get item colour
+ ``item:set_color_callback(cb)``
+
+ Set callback for getting the item’s colour (the element texture’s colours
+ multiplied by this colour). Do not attempt to access the item’s ``color``
+ property from the callback, as it will result in infinite recursion.
+
+ The callback function must return a render colour object representing the
+ ARGB colour (usually created by calling ``emu.render_color``), and takes no
+ parameters. Call with ``nil`` as the argument to restore the default colour
+ handler (based on the item’s animation state and ``color`` child elements).
+Get item horizontal scroll window size
+ ``item:set_scroll_size_x_callback(cb)``
+
+ Set callback for getting the item’s horizontal scroll window size. This
+ allows the script to control how much of the element is displayed by the
+ item. Do not attempt to access the item’s ``scroll_size_x`` property from
+ the callback, as it will result in infinite recursion.
+
+ The callback function must return a floating-point number representing the
+ horizontal window size as a proportion of the associated element’s width,
+ and takes no parameters. A value of 1.0 will display the entire width of
+ the element; smaller values will display proportionally smaller parts of the
+ element. Call with ``nil`` as the argument to restore the default
+ horizontal scroll window size handler (based on the ``xscroll`` child
+ element).
+Get item vertical scroll window size
+ ``item:set_scroll_size_y_callback(cb)``
+
+ Set callback for getting the item’s vertical scroll window size. This
+ allows the script to control how much of the element is displayed by the
+ item. Do not attempt to access the item’s ``scroll_size_y`` property from
+ the callback, as it will result in infinite recursion.
+
+ The callback function must return a floating-point number representing the
+ vertical window size as a proportion of the associated element’s height, and
+ takes no parameters. A value of 1.0 will display the entire height of the
+ element; smaller values will display proportionally smaller parts of the
+ element. Call with ``nil`` as the argument to restore the default
+ vertical scroll window size handler (based on the ``xscroll`` child
+ element).
+Get item horizontal scroll position
+ ``item:set_scroll_pos_x_callback(cb)``
+
+ Set callback for getting the item’s horizontal scroll position. This allows
+ the script to control which part of the element is displayed by the item.
+ Do not attempt to access the item’s ``scroll_pos_x`` property from the
+ callback, as this will result in infinite recursion.
+
+ The callback must return a floating-point number, and takes no parameters.
+ A value of 0.0 aligns the left edge of the element with the left edge of the
+ item; larger values pan right. Call with ``nil`` as the argument to restore
+ the default horizontal scroll position handler (based on bindings in the
+ ``xscroll`` child element).
+Get item vertical scroll position
+ ``item:set_scroll_pos_y_callback(cb)``
+
+ Set callback for getting the item’s vertical scroll position. This allows
+ the script to control which part of the element is displayed by the item.
+ Do not attempt to access the item’s ``scroll_pos_y`` property from the
+ callback, as this will result in infinite recursion.
+
+ The callback must return a floating-point number, and takes no parameters.
+ A value of 0.0 aligns the top edge of the element with the top edge of the
+ item; larger values pan down. Call with ``nil`` as the argument to restore
+ the default vertical scroll position handler (based on bindings in the
+ ``yscroll`` child element).
+
+.. _layscript-events-element:
+
+Layout element events
+~~~~~~~~~~~~~~~~~~~~~
+
+Layout element events apply to an individual visual element definition.
+
+Draw
+ ``element:set_draw_callback(cb)``
+
+ Set callback for additional drawing after the element’s components have been
+ drawn. This gives the script direct control over the final texture when an
+ element item is drawn.
+
+ The callback is passed two arguments: the element state (an integer) and the
+ 32-bit ARGB bitmap at the required size. The callback must not attempt to
+ resize the bitmap. Call with ``nil`` as the argument to remove the event
+ handler.
diff --git a/docs/source/techspecs/luaengine.rst b/docs/source/techspecs/luaengine.rst
deleted file mode 100644
index 26d6da6a253..00000000000
--- a/docs/source/techspecs/luaengine.rst
+++ /dev/null
@@ -1,158 +0,0 @@
-Scripting MAME via LUA
-======================
-
-Introduction
-------------
-
-It is now possible to externally drive MAME via LUA scripts. This feature initially appeared in version 0.148, when a minimal
-``luaengine`` was implemented. Nowadays, the LUA interface is rich enough to let you inspect and manipulate devices state, access CPU
-registers, read and write memory, and draw a custom HUD on screen.
-
-Internally, MAME makes extensive use of ``luabridge`` to implement this feature: the idea is to transparently expose as many of the useful internals as possible.
-
-Finally, a warning: The LUA API is not yet declared stable and may suddenly change without prior notice. However, we expose methods to let you know at runtime which API version you are running against, and you can introspect most of the objects at runtime.
-
-Features
---------
-
-The API is not yet complete, but this is a partial list of capabilities currently available to LUA scripts:
-
-- machine metadata (app version, current rom, rom details)
-- machine control (starting, pausing, resetting, stopping)
-- machine hooks (on frame painting and on user events)
-- devices introspection (device tree listing, memory and register
- enumeration)
-- screens introspection (screens listing, screen details, frames
- counting)
-- screen HUD drawing (text, lines, boxes on multiple screens)
-- memory read/write (8/16/32/64 bits, signed and unsigned)
-- registers and states control (states enumeration, get and set)
-
-Usage
------
-
-MAME supports external scripting via LUA (>= 5.3) scripts, either written on the interactive console or loaded as a file. To reach the
-console, just run MAME with **-console** and you will be greeted by a naked ``>`` prompt where you can input your script.
-
-To load a whole script at once, store it in a plain text file and pass it via **-autoboot_script**. Please note that script loading may be delayed (few seconds by default), but you can override the default with the **-autoboot_delay** argument.
-
-To control the execution of your code, you can use a loop-based or an event-based approach. The former is not encouraged as it is
-resource-intensive and makes control flow unnecessarily complex. Instead, we suggest to register custom hooks to be invoked on specific
-events (eg. at each frame rendering).
-
-Walkthrough
------------
-
-Let's first run MAME in a terminal to reach the LUA console:
-
-::
-
- $ mame -console YOUR_ROM
- _/ _/ _/_/ _/ _/ _/_/_/_/
- _/_/ _/_/ _/ _/ _/_/ _/_/ _/
- _/ _/ _/ _/_/_/_/ _/ _/ _/ _/_/_/
- _/ _/ _/ _/ _/ _/ _/
- _/ _/ _/ _/ _/ _/ _/_/_/_/
- mame v0.205
- Copyright (C) Nicola Salmoria and the MAME team
-
- Lua 5.3
- Copyright (C) Lua.org, PUC-Rio
-
- [MAME]>
-
-At this point, your game is probably running in demo mode, let's pause it:
-
-::
-
- [MAME]> emu.pause()
- [MAME]>
-
-Even without textual feedback on the console, you'll notice the game is
-now paused. In general, commands are quiet and only print back error
-messages.
-
-You can check at runtime which version of MAME you are running, with:
-
-::
-
- [MAME]> print(emu.app_name() .. " " .. emu.app_version())
- mame 0.205
-
-We now start exploring screen related methods. First, let's enumerate available screens:
-
-::
-
- [MAME]> for i,v in pairs(manager:machine().screens) do print(i) end
- :screen
-
-**manager:machine()** is the root object of your currently running machine: we will be using this often. **screens** is a table with all
-available screens; most machines only have one main screen. In our case, the main and only screen is tagged as **:screen**, and we can further inspect it:
-
-::
-
- [MAME]> -- let's define a shorthand for the main screen
- [MAME]> s = manager:machine().screens[":screen"]
- [MAME]> print(s:width() .. "x" .. s:height())
- 320x224
-
-We have several methods to draw on the screen a HUD composed of lines, boxes and text:
-
-::
-
- [MAME]> -- we define a HUD-drawing function, and then call it
- [MAME]> function draw_hud()
- [MAME]>> s:draw_text(40, 40, "foo"); -- (x0, y0, msg)
- [MAME]>> s:draw_box(20, 20, 80, 80, 0, 0xff00ffff); -- (x0, y0, x1, y1, fill-color, line-color)
- [MAME]>> s:draw_line(20, 20, 80, 80, 0xff00ffff); -- (x0, y0, x1, y1, line-color)
- [MAME]>> end
- [MAME]> draw_hud();
-
-This will draw some useless art on the screen. However, when unpausing the game, your HUD needs to be refreshed otherwise it will just disappear. In order to do this, you have to register your hook to be called on every frame repaint:
-
-::
-
- [MAME]> emu.register_frame_done(draw_hud, "frame")
-
-All colors are expected in ARGB format (32b unsigned), while screen origin (0,0) normally corresponds to the top-left corner.
-
-Similarly to screens, you can inspect all the devices attached to a machine:
-
-::
-
- [MAME]> for k,v in pairs(manager:machine().devices) do print(k) end
- :audiocpu
- :maincpu
- :saveram
- :screen
- :palette
- [...]
-
-On some of them, you can also inspect and manipulate memory and state:
-
-::
-
- [MAME]> cpu = manager:machine().devices[":maincpu"]
- [MAME]> -- enumerate, read and write state registers
- [MAME]> for k,v in pairs(cpu.state) do print(k) end
- D5
- SP
- A4
- A3
- D0
- PC
- [...]
- [MAME]> print(cpu.state["D0"].value)
- 303
- [MAME]> cpu.state["D0"].value = 255
- [MAME]> print(cpu.state["D0"].value)
- 255
-
-::
-
- [MAME]> -- inspect memory
- [MAME]> for k,v in pairs(cpu.spaces) do print(k) end
- program
- [MAME]> mem = cpu.spaces["program"]
- [MAME]> print(mem:read_i8(0xC000))
- 41
diff --git a/docs/source/techspecs/m6502.rst b/docs/source/techspecs/m6502.rst
index c4cb45f5cea..f5d55fc9af4 100644
--- a/docs/source/techspecs/m6502.rst
+++ b/docs/source/techspecs/m6502.rst
@@ -18,7 +18,7 @@ Point 1 has been ensured through bisimulation with the gate-level simulation per
The 6502 family
---------------
-The MOS 6502 family has been large and productive. A large number of variants exist, varying on bus sizes, I/O, and even opcodes. Some offshots (g65c816, hu6280) even exist that live elsewhere in the mame tree. The final class hierarchy is this:
+The MOS 6502 family has been large and productive. A large number of variants exist, varying on bus sizes, I/O, and even opcodes. Some offshoots (g65c816, hu6280) even exist that live elsewhere in the mame tree. The final class hierarchy is this:
::
@@ -26,7 +26,7 @@ The MOS 6502 family has been large and productive. A large number of variants ex
|
+------+--------+--+--+-------+-------+
| | | | | |
- 6510 deco16 6504 6509 n2a03 65c02
+ 6510 deco16 6504 6509 rp2a03 65c02
| |
+-----+-----+ r65c02
| | | |
@@ -38,7 +38,7 @@ The MOS 6502 family has been large and productive. A large number of variants ex
-The 6510 adds an up to 8 bits I/O port, with the 6510t, 7501 and 8502 being software-identical variants with different pin count (hence I/O count), die process (NMOS, HNMOS, etc) and clock support.
+The 6510 adds an up to 8 bits I/O port, with the 6510t, 7501 and 8502 being software-identical variants with different pin count (hence I/O count), die process (NMOS, HNMOS, etc.) and clock support.
The deco16 is a Deco variant with a small number of not really understood additional instructions and some I/O.
@@ -46,7 +46,7 @@ The 6504 is a pin and address-bus reduced version.
The 6509 adds internal support for paging.
-The n2a03 is the NES variant with the D flag disabled and sound functionality integrated.
+The rp2a03 is the NES variant with the D flag disabled and sound functionality integrated.
The 65c02 is the very first cmos variant with some additional instructions, some fixes, and most of the undocumented instructions turned into nops. The R (Rockwell, but eventually produced by WDC too among others) variant adds a number of bitwise instructions and also stp and wai. The SC variant, used by the Lynx portable console, looks identical to the R variant. The 'S' probably indicates a static-ram-cell process allowing full DC-to-max clock control.
@@ -92,7 +92,7 @@ At a minimum, the class must include a constructor and an enum picking up the co
If the CPU has its own dispatch table, the class must also include the declaration (but not definition) of **disasm_entries**, **do_exec_full** and **do_exec_partial**, the declaration and definition of **disasm_disassemble** (identical for all classes but refers to the class-specific **disasm_entries** array) and include the .inc file (which provides the missing definitions). Support for the generation must also be added to CPU.mak.
-If the CPU has in addition its own opcodes, their declaration must be done through a macro, see f.i. m65c02. The .inc file will provide the definitions.
+If the CPU has in addition its own opcodes, their declaration must be done through a macro, see e.g. m65c02. The .inc file will provide the definitions.
Dispatch tables
@@ -365,7 +365,7 @@ A negative icount means that the CPU won't be able to do anything for some time
Multi-dispatch variants
-----------------------
-Some variants currently in the process of being supported change instruction set depending on an internal flag, either switching to a 16-bits mode or changing some register accesses to memory accesses. This is handled by having multiple dispatch tables for the CPU, the d<CPU>.lst not being 257 entries anymore but 256*n+1. The variable **inst_state_base** must select which instruction table to use at a given time. It must be a multiple of 256, and is in fact simply OR-ed to the first instruction byte to get the dispatch table index (aka inst_state).
+Some variants currently in the process of being supported change instruction set depending on an internal flag, either switching to a 16-bit mode or changing some register accesses to memory accesses. This is handled by having multiple dispatch tables for the CPU, the d<CPU>.lst not being 257 entries anymore but 256*n+1. The variable **inst_state_base** must select which instruction table to use at a given time. It must be a multiple of 256, and is in fact simply OR-ed to the first instruction byte to get the dispatch table index (aka inst_state).
Current TO-DO:
--------------
@@ -374,6 +374,6 @@ Current TO-DO:
- Integrate the I/O subsystems in the 4510
-- Possibly integrate the sound subsytem in the n2a03
+- Possibly integrate the sound subsytem in the rp2a03
- Add decent hookups for the Apple 3 madness
diff --git a/docs/source/techspecs/memory.rst b/docs/source/techspecs/memory.rst
new file mode 100644
index 00000000000..9f0a31758c7
--- /dev/null
+++ b/docs/source/techspecs/memory.rst
@@ -0,0 +1,964 @@
+Emulated system memory and address spaces management
+====================================================
+
+.. contents:: :local:
+
+
+1. Overview
+-----------
+
+The memory subsystem (emumem and addrmap) combines multiple functions
+useful for system emulation:
+
+* address bus decoding and dispatching with caching
+* static descriptions of an address map
+* RAM allocation and registration for state saving
+* interaction with memory regions to access ROM
+
+Devices create address spaces, e.g. decodable buses, through the
+``device_memory_interface``. The machine configuration sets up address
+maps to put in the address spaces, then the device can do read and
+writes through the bus.
+
+2. Basic concepts
+-----------------
+
+2.1 Address spaces
+~~~~~~~~~~~~~~~~~~
+
+An address space, implemented in the class **address_space**,
+represents an addressable bus with potentially multiple sub-devices
+connected requiring a decode. It has a number of data lines (8, 16,
+32 or 64) called data width, a number of address lines (1 to 32)
+called address width and an Endianness. In addition an address shift
+allows for buses that have an atomic granularity different than a
+byte.
+
+Address space objects provide a series of methods for read and write
+access, and a second series of methods for dynamically changing the
+decode.
+
+
+2.2 Address maps
+~~~~~~~~~~~~~~~~
+
+An address map is a static description of the decode expected when
+using a bus. It connects to memory, other devices and methods, and is
+installed, usually at startup, in an address space. That description
+is stored in an **address_map** structure which is filled
+programmatically.
+
+
+2.3 Shares, banks and regions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Memory shares are allocated memory zones that can be put in multiple
+places in the same or different address spaces, and can also be
+directly accessed from devices.
+
+Memory banks are zones that indirect memory access, giving the
+possibility to dynamically and efficiently change where a zone
+actually points to.
+
+Memory regions are read-only memory zones in which ROMs are loaded.
+
+All of these have names allowing to access them.
+
+2.4 Views
+~~~~~~~~~
+
+Views are a way to multiplex different submaps over a memory range
+with fast switching. It is to be used when multiple devices map at
+the same addresses and are switched in externally. They must be
+created as an object of the device and then setup either statically in
+a memory map or dynamically through ``install_*`` calls.
+
+Switchable submaps, aka variants, are named through an integer. An
+internal indirection through a map ensures that any integer value can
+be used.
+
+
+3. Memory objects
+-----------------
+
+3.1 Shares - memory_share
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ class memory_share {
+ const std::string &name() const;
+ void *ptr() const;
+ size_t bytes() const;
+ endianness_t endianness() const;
+ u8 bitwidth() const;
+ u8 bytewidth() const;
+ };
+
+A memory share is a named allocated memory zone that is automatically
+saved in save states and can be mapped in address spaces. It is the
+standard container for memory that is shared between spaces, but also
+shared between an emulated CPU and a driver. As such one has easy
+access to its contents from the driver class.
+
+.. code-block:: C++
+
+ required_shared_ptr<uNN> m_share_ptr;
+ optional_shared_ptr<uNN> m_share_ptr;
+ required_shared_ptr_array<uNN, count> m_share_ptr_array;
+ optional_shared_ptr_array<uNN, count> m_share_ptr_array;
+
+ [device constructor] m_share_ptr(*this, "name"),
+ [device constructor] m_share_ptr_array(*this, "name%u", 0U),
+
+At the device level, a pointer to the memory zone can easily be
+retrieved by building one of these four finders. Note that like for
+every finder calling ``target()`` on the finder gives you the base
+pointer of the ``memory_share`` object.
+
+.. code-block:: C++
+
+ memory_share_creator<uNN> m_share;
+
+ [device constructor] m_share(*this, "name", size, endianness),
+
+A memory share can be created if it doesn’t exist in a memory map
+through that creator class. If it already exists it is just
+retrieved. That class behaves like a pointer but also has the
+``target()``, ``length()``, ``bytes()``, ``endianness()``,
+``bitwidth()`` and ``bytewidth()`` methods for share information. The
+desired size is specified in bytes.
+
+.. code-block:: C++
+
+ memory_share *memshare(string tag) const;
+
+The ``memshare`` device method retrieves a memory share by name. Beware
+that the lookup can be expensive, prefer finders instead.
+
+3.2 Banks - memory_bank
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ class memory_bank {
+ const std::string &tag() const;
+ int entry() const;
+ void set_entry(int entrynum);
+ void configure_entry(int entrynum, void *base);
+ void configure_entries(int startentry, int numentry, void *base, offs_t stride);
+ void set_base(void *base);
+ void *base() const;
+ };
+
+A memory bank is a named memory zone indirection that can be mapped in
+address spaces. It points to ``nullptr`` when created.
+``configure_entry`` associates an entry number and a base pointer.
+``configure_entries`` does the same for multiple consecutive entries
+spanning a memory zone.
+
+``set_base`` sets the base address for the active entry. If there are
+no entries, entry 0 (zero) is automatically created and selected. Use
+of ``set_base`` should be avoided in favour of pre-configured entries
+unless there are an impractically large number of possible base
+addresses.
+
+``set_entry`` dynamically and efficiently selects the active entry,
+``entry()`` returns the active entry number, and ``base()`` gets the
+associated base pointer.
+
+.. code-block:: C++
+
+ required_memory_bank m_bank;
+ optional_memory_bank m_bank;
+ required_memory_bank_array<count> m_bank_array;
+ optional_memory_bank_array<count> m_bank_array;
+
+ [device constructor] m_bank(*this, "name"),
+ [device constructor] m_bank_array(*this, "name%u", 0U),
+
+At the device level, a pointer to the memory bank object can easily be
+retrieved by building one of these four finders.
+
+.. code-block:: C++
+
+ memory_bank_creator m_bank;
+
+ [device constructor] m_bank(*this, "name"),
+
+A memory bank can be created if it doesn’t exist in a memory map
+through that creator class. If it already exists it is just
+retrieved.
+
+.. code-block:: C++
+
+ memory_bank *membank(string tag) const;
+
+The ``membank`` device method retrieves a memory bank by name. Beware
+that the lookup can be expensive, prefer finders instead.
+
+
+3.3 Regions - memory_region
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ class memory_region {
+ u8 *base();
+ u8 *end();
+ u32 bytes() const;
+ const std::string &name() const;
+ endianness_t endianness() const;
+ u8 bitwidth() const;
+ u8 bytewidth() const;
+ u8 &as_u8(offs_t offset = 0);
+ u16 &as_u16(offs_t offset = 0);
+ u32 &as_u32(offs_t offset = 0);
+ u64 &as_u64(offs_t offset = 0);
+ }
+
+A region is used to store read-only data like ROMs or the result of
+fixed decryptions. Their contents are not saved, which is why they
+should not being written to from the emulated system. They don’t
+really have an intrinsic width (``base()`` returns an ``u8 *`` always),
+which is historical and pretty much unfixable at this point. The
+``as_*`` methods allow for accessing them at a given width.
+
+.. code-block:: C++
+
+ required_memory_region m_region;
+ optional_memory_region m_region;
+ required_memory_region_array<count> m_region_array;
+ optional_memory_region_array<count> m_region_array;
+
+ [device constructor] m_region(*this, "name"),
+ [device constructor] m_region_array(*this, "name%u", 0U),
+
+At the device level, a pointer to the memory region object can easily be
+retrieved by building one of these four finders.
+
+.. code-block:: C++
+
+ memory_region *memregion(string tag) const;
+
+The ``memregion`` device method retrieves a memory region by name.
+Beware that the lookup can be expensive, prefer finders instead.
+
+
+3.4 Views - memory_view
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ class memory_view {
+ memory_view(device_t &device, std::string name);
+ memory_view_entry &operator[](int slot);
+
+ void select(int entry);
+ void disable();
+
+ const std::string &name() const;
+ }
+
+A view allows to switch part of a memory map between multiple
+possibilities, or even disable it entirely to see what was there
+before. It is created as an object of the device.
+
+.. code-block:: C++
+
+ memory_view m_view;
+
+ [device constructor] m_view(*this, "name"),
+
+It is then setup through the address map API or dynamically. At
+runtime, a numbered variant can be selected using the ``select`` method,
+or the view can be disabled using the ``disable`` method. A disabled
+view can be re-enabled at any time.
+
+
+.. _3.5:
+
+3.5 Bus contention handling
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some specific CPUs have been upgraded to be interruptible which allows
+to add bus contention and wait states capabitilites. Being
+interruptible means, in practice, that an instruction can be
+interrupted at any time and the execute_run method of the core exited.
+Other devices can then run, then eventually controls returns to the
+core and the instruction continues from the point it was started.
+Importantly, this can be triggered from a handler and even be used to
+interrupt just before the access that is currently done
+(e.g. continuation will redo the access).
+
+The CPUs supporting that declare their capability by overriding the
+method ``cpu_is_interruptible`` to return true.
+
+Three intermediate contention handlers can be added to accesses:
+
+* ``before_delay``: wait a number of cycles before doing the access.
+* ``after_delay``: wait a number of cycles after doing the access.
+* ``before_time``: wait for a given time before doing the access.
+
+For the delay handlers, a method or lambda is called which returns the
+number of cycles to wait (as a u32).
+
+The ``before_time`` is special. First, the time is compared to the
+current value of cpu->total_cycles(). That value is the number of
+cycles elapsed since the last reset of the cpu. It is passed as a
+parameter to the method as a u64 and must return the earliest time as
+a u64 when the access can be done, which can be equal to the passed-in
+time. From there two things can happen: either the running cpu has
+enough cycles left to consume to reach that time. In that case, the
+necessary number of cycles is consumed, and the access is done.
+Otherwise, when there isn't enough, the remaining cycles are consumed,
+the access aborted, scheduling happens, and eventually the access is
+redone. In that case the method is called again with the new current
+time, and must return the (probably same) earliest time again. This
+will happen until enough cycles to consume are available to directly
+do the access.
+
+This approach allows to for instance handle consecutive DMAs. A first
+DMA grabs the bus for a transfer. This shows up as the method
+answering for the earliest time for access the time of the end of the
+dma. If no timer happens until that time the access will then happen
+just after the dma finishes. But if a timer elapses before that and
+as a consequence another dma is queued while the first is running, the
+cycle will be aborted for lack of remaining time, and the method will
+eventually be called again. It will then give the time of when the
+second dma will finish, and all will be well.
+
+It can also allow to reduce said earlier time when circumstances
+require it. For instance a PIO latch that waits up to 64 cycles that
+data arrives can indicate that current time + 64 as a target (which
+will trigger a bus error for instance) but if a timer elapses and
+fills the latch meanwhile the method will be called again and that
+time can just return the current time to let the access pass though.
+Beware that if the timer elapsing did not fill the latch then the
+method must return the time it returned previously, e.g. the initial
+access time + 64, otherwise irrelevant timers happening or simply
+scheduling quantum effects will delay the timeout, possibly to
+infinity if the quantum is small enough.
+
+Contention handlers on the same address are taken into account in the
+``before_time``, ``before_delay`` then ``after_delay`` order.
+Contention handlers of the same type on the same address at
+last-one-wins. Installing any non-contention handler on a range where
+a contention handler was removes it.
+
+
+4. Address maps API
+-------------------
+
+4.1 General API structure
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An address map is a method of a device which fills an **address_map**
+structure, usually called **map**, passed by reference. The method
+then can set some global configuration through specific methods and
+then provide address range-oriented entries which indicate what should
+happen when a specific range is accessed.
+
+The general syntax for entries uses method chaining:
+
+.. code-block:: C++
+
+ map(start, end).handler(...).handler_qualifier(...).range_qualifier().contention();
+
+The values start and end define the range, the handler() block
+determines how the access is handled, the handler_qualifier() block
+specifies some aspects of the handler (memory sharing for instance)
+and the range_qualifier() block refines the range (mirroring, masking,
+lane selection, etc.). The contention methods handle bus contention
+and wait states for cpus supporting them.
+
+The map follows a “last one wins” principle, where the handler specified
+last is selected when multiple handlers match a given address.
+
+
+4.2 Global configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+4.2.1 Global masking
+''''''''''''''''''''
+
+.. code-block:: C++
+
+ map.global_mask(offs_t mask);
+
+Specifies a mask to be applied to all addresses when accessing the space
+that map is installed in.
+
+
+4.2.2 Returned value on unmapped/nop-ed read
+''''''''''''''''''''''''''''''''''''''''''''
+
+.. code-block:: C++
+
+ map.unmap_value_low();
+ map.unmap_value_high();
+ map.unmap_value(u8 value);
+
+Sets the value to return on reads to an unmapped or nopped-out address.
+Low means 0, high ~0.
+
+
+4.3 Handler setting
+~~~~~~~~~~~~~~~~~~~
+
+4.3.1 Method on the current device
+''''''''''''''''''''''''''''''''''
+
+.. code-block:: C++
+
+ (...).r(FUNC(my_device::read_method))
+ (...).w(FUNC(my_device::write_method))
+ (...).rw(FUNC(my_device::read_method), FUNC(my_device::write_method))
+
+ uNN my_device::read_method(address_space &space, offs_t offset, uNN mem_mask)
+ uNN my_device::read_method(address_space &space, offs_t offset)
+ uNN my_device::read_method(address_space &space)
+ uNN my_device::read_method(offs_t offset, uNN mem_mask)
+ uNN my_device::read_method(offs_t offset)
+ uNN my_device::read_method()
+
+ void my_device::write_method(address_space &space, offs_t offset, uNN data, uNN mem_mask)
+ void my_device::write_method(address_space &space, offs_t offset, uNN data)
+ void my_device::write_method(address_space &space, uNN data)
+ void my_device::write_method(offs_t offset, uNN data, uNN mem_mask)
+ void my_device::write_method(offs_t offset, uNN data)
+ void my_device::write_method(uNN data)
+
+Sets a method of the current device or driver to read, write or both
+for the current entry. The prototype of the method can take multiple
+forms making some elements optional. ``uNN`` represents ``u8``,
+``u16``, ``u32`` or ``u64`` depending on the data width of the handler.
+The handler can be narrower than the bus itself (for instance an 8-bit
+device on a 32-bit bus).
+
+The offset passed in is built from the access address. It starts at
+zero at the start of the range, and increments for each ``uNN`` unit.
+An ``u8`` handler will get an offset in bytes, an ``u32`` one in double
+words. The ``mem_mask`` has its bits set where the accessors actually
+drive the bit. It’s usually built in byte units, but in some cases of
+I/O chips ports with per-bit direction registers the resolution can be
+at the bit level.
+
+
+4.3.2 Method on a different device
+''''''''''''''''''''''''''''''''''
+
+.. code-block:: C++
+
+ (...).r(m_other_device, FUNC(other_device::read_method))
+ (...).r("other-device-tag", FUNC(other_device::read_method))
+ (...).w(m_other_device, FUNC(other_device::write_method))
+ (...).w("other-device-tag", FUNC(other_device::write_method))
+ (...).rw(m_other_device, FUNC(other_device::read_method), FUNC(other_device::write_method))
+ (...).rw("other-device-tag", FUNC(other_device::read_method), FUNC(other_device::write_method))
+
+Sets a method of another device, designated by an object finder
+(usually ``required_device`` or ``optional_device``) or its tag, to
+read, write or both for the current entry.
+
+
+4.3.3 Lambda function
+'''''''''''''''''''''
+
+.. code-block:: C++
+
+ (...).lr{8,16,32,64}(NAME([...](address_space &space, offs_t offset, uNN mem_mask) -> uNN { ... }))
+ (...).lr{8,16,32,64}([...](address_space &space, offs_t offset, uNN mem_mask) -> uNN { ... }, "name")
+ (...).lw{8,16,32,64}(NAME([...](address_space &space, offs_t offset, uNN data, uNN mem_mask) -> void { ... }))
+ (...).lw{8,16,32,64}([...](address_space &space, offs_t offset, uNN data, uNN mem_mask) -> void { ... }, "name")
+ (...).lrw{8,16,32,64}(NAME(read), NAME(write))
+ (...).lrw{8,16,32,64}(read, "name_r", write, "name_w")
+
+Sets a lambda called on read, write or both. The lambda prototype can
+be any of the six available for methods. One can either use ``NAME()``
+over the whole lambda, or provide a name after the lambda definition.
+The number is the data width of the access, e.g. the NN.
+
+
+4.3.4 Direct memory access
+''''''''''''''''''''''''''
+
+.. code-block:: C++
+
+ (...).rom()
+ (...).writeonly()
+ (...).ram()
+
+Selects the range to access a memory zone as read-only, write-only or
+read/write respectively. Specific handler qualifiers specify the
+location of this memory zone. There are two cases when no qualifier is
+acceptable:
+
+* ``ram()`` gives an anonymous RAM zone not accessible outside of the
+ address space.
+
+* ``rom()`` when the memory map is used in an ``AS_PROGRAM``
+ space of a (CPU) device which names is also the name of a region.
+ Then the memory zone points to that region at the offset
+ corresponding to the start of the zone.
+
+.. code-block:: C++
+
+ (...).rom().region("name", offset)
+
+The ``region`` qualifier causes a read-only zone point to the contents
+of a given region at a given offset.
+
+.. code-block:: C++
+
+ (...).rom().share("name")
+ (...).writeonly.share("name")
+ (...).ram().share("name")
+
+The ``share`` qualifier causes the zone point to a shared memory region
+identified by its name. If the share is present in multiple spaces, the
+size, bus width, and, if the bus is more than byte-wide, the Endianness
+must match.
+
+
+4.3.5 Bank access
+'''''''''''''''''
+
+.. code-block:: C++
+
+ (...).bankr("name")
+ (...).bankw("name")
+ (...).bankrw("name")
+
+Sets the range to point at the contents of a memory bank in read, write
+or read/write mode.
+
+
+4.3.6 Port access
+'''''''''''''''''
+
+.. code-block:: C++
+
+ (...).portr("name")
+ (...).portw("name")
+ (...).portrw("name")
+
+Sets the range to point at an I/O port.
+
+
+4.3.7 Dropped access
+''''''''''''''''''''
+
+.. code-block:: C++
+
+ (...).nopr()
+ (...).nopw()
+ (...).noprw()
+
+Sets the range to drop the access without logging. When reading, the
+unmap value is returned.
+
+
+4.3.8 Unmapped access
+'''''''''''''''''''''
+
+.. code-block:: C++
+
+ (...).unmapr()
+ (...).unmapw()
+ (...).unmaprw()
+
+Sets the range to drop the access with logging. When reading, the
+unmap value is returned.
+
+
+4.3.9 Subdevice mapping
+'''''''''''''''''''''''
+
+.. code-block:: C++
+
+ (...).m(m_other_device, FUNC(other_device::map_method))
+ (...).m("other-device-tag", FUNC(other_device::map_method))
+
+Includes a device-defined submap. The start of the range indicates
+where the address zero of the submap ends up, and the end of the range
+clips the submap if needed. Note that range qualifiers (defined
+later) apply.
+
+Currently, only handlers are allowed in submaps and not memory zones
+or banks.
+
+
+4.4 Range qualifiers
+~~~~~~~~~~~~~~~~~~~~
+
+4.4.1 Mirroring
+'''''''''''''''
+
+.. code-block:: C++
+
+ (...).mirror(mask)
+
+Duplicate the range on the addresses reachable by setting any of the 1
+bits present in mask. For instance, a range 0-0x1f with mirror 0x300
+will be present on 0-0x1f, 0x100-0x11f, 0x200-0x21f and 0x300-0x31f.
+The addresses passed in to the handler stay in the 0-0x1f range, the
+mirror bits are not seen by the handler.
+
+
+4.4.2 Masking
+'''''''''''''
+
+.. code-block:: C++
+
+ (...).mask(mask)
+
+Only valid with handlers, the address will be masked with the mask
+before being passed to the handler.
+
+
+4.4.3 Selection
+'''''''''''''''
+
+.. code-block:: C++
+
+ (...).select(mask)
+
+Only valid with handlers, the range will be mirrored as with mirror,
+but the mirror address bits are preserved in the offset passed to the
+handler when it is called. This is useful for devices like sound
+chips where the low bits of the address select a function and the high
+bits a voice number.
+
+
+4.4.4 Sub-unit selection
+''''''''''''''''''''''''
+
+.. code-block:: C++
+
+ (...).umask16(16-bits mask)
+ (...).umask32(32-bits mask)
+ (...).umask64(64-bits mask)
+
+Only valid with handlers and submaps, selects which data lines of the
+bus are actually connected to the handler or the device. The mask value
+should be a multiple of a byte, e.g. the mask is a series of 00 and ff.
+The offset will be adjusted accordingly, so that a difference of 1 means
+the next handled unit in the access.
+
+If the mask is narrower than the bus width, the mask is replicated in
+the upper lines.
+
+
+4.4.5 Chip select handling on sub-unit
+''''''''''''''''''''''''''''''''''''''
+
+.. code-block:: C++
+
+ (...).cselect(16/32/64)
+
+When a device is connected to part of the bus, like a byte on a
+16-bits bus, the target handler is only activated when that part is
+actually accessed. In some cases, very often byte access on a 68000
+16-bits bus, the actual hardware only checks the word address and not
+if the correct byte is accessed. ``cswidth`` tells the memory system to
+trigger the handler if a wider part of the bus is accessed. The
+parameter is that trigger width (would be 16 in the 68000 case).
+
+
+4.4.6 User flags
+''''''''''''''''
+
+.. code-block:: C++
+
+ (...).flags(16-bits mask)
+
+This parameter allows to set user-defined flags on the handler which
+can then be retrieved by an accessing device to change their
+behaviour. An example of use the i960 which marks burstable zones
+that way (they have a specific hardware-level support).
+
+
+4.5 Contention
+~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ (...).before_time(method).(...)
+ (...).before_delay(method).(...)
+ (...).after_delay(method).(...)
+
+These three methods allow to add the contention methods to a handler.
+See section `3.5`_. Multiple methods can be handler to one handler.
+
+
+4.6 View setup
+~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ map(start, end).view(m_view);
+ m_view[0](start1, end1).[...];
+
+A view is setup in a address map with the view method. The only
+qualifier accepted is mirror. The “disabled” version of the view will
+include what was in the range prior to the view setup.
+
+The different variants are setup by indexing the view with the variant
+number and setting up an entry in the usual way. The entries within a
+variant must of course stay within the range. There are no other
+additional constraints. The contents of a variant, by default, are
+what was there before, i.e. the contents of the disabled view, and
+setting it up allows part or all of it to be overridden.
+
+Variants can only be setup once the view itself has been setup with
+the ``view`` method.
+
+A view can only be put in one address map and in only one position.
+If multiple views have identical or similar contents, remember that
+setting up a map is nothing more than a method call, and creating a
+second method to setup a view is perfectly reasonable. A view is of
+type ``memory_view`` and an indexed entry (e.g. a variant to setup) is
+of type ``memory_view::memory_view_entry &``.
+
+A view can be installed in another view, but don’t forget that a view
+can be installed only once. A view can also be part of “what was there
+before”.
+
+
+
+5. Address space dynamic mapping API
+------------------------------------
+
+5.1 General API structure
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A series of methods allow the bus decoding of an address space to be
+changed on-the-fly. They’re powerful but have some issues:
+
+* changing the mappings repeatedly can be slow
+* the address space state is not saved in the saved states, so it has to
+ be rebuilt after state load
+* they can be hidden anywhere rather than be grouped in an address map,
+ which can be less readable
+
+The methods, rather than decomposing the information in handler, handler
+qualifier and range qualifier, put them all together as method
+parameters. To make things a little more readable, lots of them are
+optional.
+
+
+5.2 Handler mapping
+~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ uNN my_device::read_method(address_space &space, offs_t offset, uNN mem_mask)
+ uNN my_device::read_method_m(address_space &space, offs_t offset)
+ uNN my_device::read_method_mo(address_space &space)
+ uNN my_device::read_method_s(offs_t offset, uNN mem_mask)
+ uNN my_device::read_method_sm(offs_t offset)
+ uNN my_device::read_method_smo()
+
+ void my_device::write_method(address_space &space, offs_t offset, uNN data, uNN mem_mask)
+ void my_device::write_method_m(address_space &space, offs_t offset, uNN data)
+ void my_device::write_method_mo(address_space &space, uNN data)
+ void my_device::write_method_s(offs_t offset, uNN data, uNN mem_mask)
+ void my_device::write_method_sm(offs_t offset, uNN data)
+ void my_device::write_method_smo(uNN data)
+
+ readNN_delegate (device, FUNC(read_method))
+ readNNm_delegate (device, FUNC(read_method_m))
+ readNNmo_delegate (device, FUNC(read_method_mo))
+ readNNs_delegate (device, FUNC(read_method_s))
+ readNNsm_delegate (device, FUNC(read_method_sm))
+ readNNsmo_delegate(device, FUNC(read_method_smo))
+
+ writeNN_delegate (device, FUNC(write_method))
+ writeNNm_delegate (device, FUNC(write_method_m))
+ writeNNmo_delegate (device, FUNC(write_method_mo))
+ writeNNs_delegate (device, FUNC(write_method_s))
+ writeNNsm_delegate (device, FUNC(write_method_sm))
+ writeNNsmo_delegate(device, FUNC(write_method_smo))
+
+To be added to a map, a method call and the device it is called onto
+have to be wrapped in the appropriate delegate type. There are twelve
+types, for read and for write and for all six possible prototypes.
+Note that as all delegates, they can also wrap lambdas.
+
+.. code-block:: C++
+
+ space.install_read_handler(addrstart, addrend, read_delegate, unitmask, cswidth, flags)
+ space.install_read_handler(addrstart, addrend, addrmask, addrmirror, addrselect, read_delegate, unitmask, cswidth, flags)
+ space.install_write_handler(addrstart, addrend, write_delegate, unitmask, cswidth, flags)
+ space.install_write_handler(addrstart, addrend, addrmask, addrmirror, addrselect, write_delegate, unitmask, cswidth, flags)
+ space.install_readwrite_handler(addrstart, addrend, read_delegate, write_delegate, unitmask, cswidth, flags)
+ space.install_readwrite_handler(addrstart, addrend, addrmask, addrmirror, addrselect, read_delegate, write_delegate, unitmask, cswidth, flags)
+
+These six methods allow to install delegate-wrapped handlers in a live
+address space. Either plain or with mask, mirror and select. In the
+read/write case both delegates must be of the same flavor (``smo``
+stuff) to avoid a combinatorial explosion of method types. The
+``unitmask``, ``cswidth`` and ``flags`` arguments are optional.
+
+5.3 Direct memory range mapping
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ space.install_rom(addrstart, addrend, void *pointer)
+ space.install_rom(addrstart, addrend, addrmirror, void *pointer)
+ space.install_rom(addrstart, addrend, addrmirror, flags, void *pointer)
+ space.install_writeonly(addrstart, addrend, void *pointer)
+ space.install_writeonly(addrstart, addrend, addrmirror, void *pointer)
+ space.install_writeonly(addrstart, addrend, addrmirror, flags, void *pointer)
+ space.install_ram(addrstart, addrend, void *pointer)
+ space.install_ram(addrstart, addrend, addrmirror, void *pointer)
+ space.install_ram(addrstart, addrend, addrmirror, flags, void *pointer)
+
+Installs a memory block in an address space, with or without mirror
+and flags. ``_rom`` is read-only, ``_ram`` is read/write,
+``_writeonly`` is write-only. The pointer must be non-null, this
+method will not allocate the memory.
+
+5.4 Bank mapping
+~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ space.install_read_bank(addrstart, addrend, memory_bank *bank)
+ space.install_read_bank(addrstart, addrend, addrmirror, memory_bank *bank)
+ space.install_read_bank(addrstart, addrend, addrmirror, flags, memory_bank *bank)
+ space.install_write_bank(addrstart, addrend, memory_bank *bank)
+ space.install_write_bank(addrstart, addrend, addrmirror, memory_bank *bank)
+ space.install_write_bank(addrstart, addrend, addrmirror, flags, memory_bank *bank)
+ space.install_readwrite_bank(addrstart, addrend, memory_bank *bank)
+ space.install_readwrite_bank(addrstart, addrend, addrmirror, memory_bank *bank)
+ space.install_readwrite_bank(addrstart, addrend, addrmirror, flags, memory_bank *bank)
+
+Install an existing memory bank for reading, writing or both in an
+address space.
+
+5.5 Port mapping
+~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ space.install_read_port(addrstart, addrend, const char *rtag)
+ space.install_read_port(addrstart, addrend, addrmirror, const char *rtag)
+ space.install_read_port(addrstart, addrend, addrmirror, flags, const char *rtag)
+ space.install_write_port(addrstart, addrend, const char *wtag)
+ space.install_write_port(addrstart, addrend, addrmirror, const char *wtag)
+ space.install_write_port(addrstart, addrend, addrmirror, flags, const char *wtag)
+ space.install_readwrite_port(addrstart, addrend, const char *rtag, const char *wtag)
+ space.install_readwrite_port(addrstart, addrend, addrmirror, const char *rtag, const char *wtag)
+ space.install_readwrite_port(addrstart, addrend, addrmirror, flags, const char *rtag, const char *wtag)
+
+Install ports by name for reading, writing or both.
+
+5.6 Dropped accesses
+~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ space.nop_read(addrstart, addrend, addrmirror, flags)
+ space.nop_write(addrstart, addrend, addrmirror, flags)
+ space.nop_readwrite(addrstart, addrend, addrmirror, flags)
+
+Drops the accesses for a given range with an optional mirror and flags;
+
+5.7 Unmapped accesses
+~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ space.unmap_read(addrstart, addrend, addrmirror, flags)
+ space.unmap_write(addrstart, addrend, addrmirror, flags)
+ space.unmap_readwrite(addrstart, addrend, addrmirror, flags)
+
+Unmaps the accesses (e.g. logs the access as unmapped) for a given range
+with an optional mirror and flags.
+
+5.8 Device map installation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ space.install_device(addrstart, addrend, device, map, unitmask, cswidth, flags)
+
+Install a device address with an address map in a space. The
+``unitmask``, ``cswidth`` and ``flags`` arguments are optional.
+
+5.9 Contention
+~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ using ws_time_delegate = device_delegate<u64 (offs_t, u64)>;
+ using ws_delay_delegate = device_delegate<u32 (offs_t)>;
+
+ space.install_read_before_time(addrstart, addrend, addrmirror, ws_time_delegate)
+ space.install_write_before_time(addrstart, addrend, addrmirror, ws_time_delegate)
+ space.install_readwrite_before_time(addrstart, addrend, addrmirror, ws_time_delegate)
+
+ space.install_read_before_delay(addrstart, addrend, addrmirror, ws_delay_delegate)
+ space.install_write_before_delay(addrstart, addrend, addrmirror, ws_delay_delegate)
+ space.install_readwrite_before_delay(addrstart, addrend, addrmirror, ws_delay_delegate)
+
+ space.install_read_after_delay(addrstart, addrend, addrmirror, ws_delay_delegate)
+ space.install_write_after_delay(addrstart, addrend, addrmirror, ws_delay_delegate)
+ space.install_readwrite_after_delay(addrstart, addrend, addrmirror, ws_delay_delegate)
+
+Install a contention handler in the decode path. The addrmirror
+parameter is optional.
+
+
+5.10 View installation
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ space.install_view(addrstart, addrend, view)
+ space.install_view(addrstart, addrend, addrmirror, view)
+
+ view[0].install...
+
+Installs a view in a space. This can be only done once and in only
+one space, and the view must not have been setup through the address
+map API before. Once the view is installed, variants can be selected
+by indexing to call a dynamic mapping method on it.
+
+A view can be installed into a variant of another view without issues,
+with only the usual constraint of single installation.
+
+5.11 Taps
+~~~~~~~~~
+
+.. code-block:: C++
+
+ using tap = std::function<void (offs_t offset, uNN &data, uNN mem_mask)
+
+ memory_passthrough_handler mph = space.install_read_tap(addrstart, addrend, name, read_tap, &mph);
+ memory_passthrough_handler mph = space.install_write_tap(addrstart, addrend, name, write_tap, &mph);
+ memory_passthrough_handler mph = space.install_readwrite_tap(addrstart, addrend, name, read_tap, write_tap, &mph);
+
+ mph.remove();
+
+A tap is a method that is be called when a specific range of addresses
+is accessed without overriding the actual access. Taps can change the
+data passed around. A write tap happens before the access, and can
+change the value to be written. A read tap happens after the access,
+and can change the value returned.
+
+Taps must be of the same width and alignement than the bus. Multiple
+taps can act over the same addresses.
+
+The ``memory_passthrough_handler`` object collates a number of taps
+and allow to remove them all in one call. The ``mph`` parameter is
+optional and a new one will be created if absent.
+
+Taps are lost when a new handler is installed at the same addresses
+(under the usual principle of last one wins). If they need to be
+preserved, one should install a change notifier on the address space,
+and remove + reinstall the taps when notified.
+
diff --git a/docs/source/techspecs/naming.rst b/docs/source/techspecs/naming.rst
new file mode 100644
index 00000000000..4846876c0cd
--- /dev/null
+++ b/docs/source/techspecs/naming.rst
@@ -0,0 +1,95 @@
+.. _naming:
+
+MAME Naming Conventions
+=======================
+
+.. contents:: :local:
+
+
+.. _naming-intro:
+
+Introduction
+------------
+
+To promote consistency and readability in MAME source code, we have some
+naming conventions for various elements.
+
+
+.. _naming-transliteration:
+
+Transliteration
+---------------
+
+For better or worse, the most broadly recognised script in the world is
+English Latin. Conveniently, it’s also included in almost all character
+encodings. To make MAME more globally accessible, we require Latin
+transliterations of titles and other metadata from other scripts. Do
+not use translations in metadata – translations are inherently
+subjective and error-prone. Translations may be included in comments if
+they may be helpful.
+
+If general, if an official Latin script name is known, it should be used
+in favour of a naïve transliteration. For titles containing foreign
+loanwords, the conventional Latin spelling should be used for the
+loanwords (the most obvious example of this is the use of “Mahjong” in
+Japanese titles rather than “Maajan”).
+
+Chinese
+ Where the primary audience was Mandarin-speaking, Hanyu Pinyin
+ should be used. In contexts where diacritics are not permitted
+ (e.g. when limited to ASCII), tone numbers should be omitted. When
+ tones are being indicated using diacritics, tone sandhi rules should
+ be applied. Where the primary audience was Cantonese-speaking
+ (primarily Hong Kong and Guandong), Jyutping should be used with
+ tone numbers omitted. If in doubt, use Hanyu Pinyin.
+Greek
+ Use ISO 843:1997 type 2 (TR) rules. Do not use traditional English
+ spellings for Greek names (people or places).
+Japanese
+ Modified Hepburn rules should generally be used. Use an apostrophe
+ between syllabic N and a following vowel (including iotised vowels).
+ Do not use hyphens to transliterate prolonged vowels.
+Korean
+ Use Revised Romanisation of Korean (RR) rules with traditional
+ English spelling for Korean surnames. Do not use ALA-LC rules for
+ word division and use of hyphens.
+Vietnamese
+ When diacritics can’t be used, omit the tones and replace the vowels
+ with single English vowels – do not use VIQR or TELEX conventions
+ (“an chuot nuong” rather than “a(n chuo^.t nu*o*'ng” or “awn chuootj
+ nuowngs”).
+
+
+.. _naming-titles:
+
+Titles and descriptions
+-----------------------
+
+Try to reproduce the original title faithfully where possible. Try to
+preserve the case convention used by the manufacturer/publisher. If no
+official English Latin title is known, use a standard transliteration.
+For software list entries where a transliteration is used for the
+``description`` element, put the title in an ``info`` element with a
+``name="alt_title"`` attribute.
+
+For software items that have multiple titles (for example different
+regional titles with the same installation media), use the most
+widespread English Latin title for the ``description`` element, and put
+the other titles in ``info`` elements with ``name="alt_title"``
+attributes.
+
+If disambiguation is needed, try to be descriptive as possible. For
+example, use the manufacturer’s version number, regional licensee’s
+name, or terse description of hardware differences in preference to
+arbitrary set numbers. Surround the disambiguation text with
+parentheses, preserve original case for names and version text, but
+use lowercase for anything else besides proper nouns and initialisms.
+
+
+.. _naming-cplusplus:
+
+C++ naming conventions
+----------------------
+
+For C++ naming conventions, see the relevant section in the C++
+Coding Guidelines: :ref:`contributing-cxx-naming`
diff --git a/docs/source/techspecs/object_finders.rst b/docs/source/techspecs/object_finders.rst
new file mode 100644
index 00000000000..27fe42fdd50
--- /dev/null
+++ b/docs/source/techspecs/object_finders.rst
@@ -0,0 +1,1039 @@
+Object Finders
+==============
+
+.. contents:: :local:
+
+
+Introduction
+------------
+
+Object finders are an important part of the glue MAME provides to tie the
+devices that make up an emulated system together. Object finders are used to
+specify connections between devices, to efficiently access resources, and to
+check that necessary resources are available on validation.
+
+Object finders search for a target object by tag relative to a base device.
+Some types of object finder require additional parameters.
+
+Most object finders have required and optional versions. The required versions
+will raise an error if the target object is not found. This will prevent a
+device from starting or cause a validation error. The optional versions will
+log a verbose message if the target object is not found, and provide additional
+members for testing whether the target object was found or not.
+
+Object finder classes are declared in the header src/emu/devfind.h and have
+Doxygen format API documentation.
+
+
+Types of object finder
+----------------------
+
+required_device<DeviceClass>, optional_device<DeviceClass>
+ Finds a device. The template argument ``DeviceClass`` should be a class
+ derived from ``device_t`` or ``device_interface``.
+required_memory_region, optional_memory_region
+ Finds a memory region, usually from ROM definitions. The target is the
+ ``memory_region`` object.
+required_memory_bank, optional_memory_bank
+ Finds a memory bank instantiated in an address map. The target is the
+ ``memory_bank`` object.
+memory_bank_creator
+ Finds a memory bank instantiated in an address map, or creates it if it
+ doesn’t exist. The target is the ``memory_bank`` object. There is no
+ optional version, because the target object will always be found or
+ created.
+required_ioport, optional_ioport
+ Finds an I/O port from a device’s input port definitions. The target is the
+ ``ioport_port`` object.
+required_address_space, optional_address_space
+ Finds a device’s address space. The target is the ``address_space`` object.
+required_region_ptr<PointerType>, optional_region_ptr<PointerType>
+ Finds the base pointer of a memory region, usually from ROM definitions.
+ The template argument ``PointerType`` is the target type (usually an
+ unsigned integer type). The target is the first element in the memory
+ region.
+required_shared_ptr<PointerType>, optional_shared_ptr<PointerType>
+ Finds the base pointer of a memory share instantiated in an address map.
+ The template argument ``PointerType`` is the target type (usually an
+ unsigned integer type). The target is the first element in the memory
+ share.
+memory_share_creator<PointerType>
+ Finds the base pointer of a memory share instantiated in an address map, or
+ creates it if it doesn’t exist. The template argument ``PointerType`` is
+ the target type (usually an unsigned integer type). The target is the first
+ element in the memory share. There is no optional version, because the
+ target object will always be found or created.
+
+
+Finding resources
+-----------------
+
+We’ll start with a simple example of a device that uses object finders to access
+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:
+
+.. code-block:: C++
+
+ class a2bus_parprn_device : public device_t, public device_a2bus_card_interface
+ {
+ public:
+ a2bus_parprn_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ virtual void write_c0nx(u8 offset, u8 data) override;
+ virtual u8 read_cnxx(u8 offset) override;
+
+ protected:
+ virtual tiny_rom_entry const *device_rom_region() const override;
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual ioport_constructor device_input_ports() const override;
+
+ private:
+ required_device<centronics_device> m_printer_conn;
+ required_device<output_latch_device> m_printer_out;
+ required_ioport m_input_config;
+ required_region_ptr<u8> m_prom;
+ };
+
+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:
+
+.. 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),
+ device_a2bus_card_interface(mconfig, *this),
+ m_printer_conn(*this, "prn"),
+ m_printer_out(*this, "prn_out"),
+ m_input_config(*this, "CFG"),
+ m_prom(*this, "prom")
+ {
+ }
+
+Each object finder takes a base device and tag as constructor arguments. The
+base device supplied at construction serves two purposes. Most obviously, the
+tag is specified relative to this device. Possibly more importantly, the object
+finder registers itself with this device so that it will be called to perform
+validation and object resolution.
+
+Note that the object finders *do not* copy the tag strings. The caller must
+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:
+
+.. code-block:: C++
+
+ namespace {
+
+ ROM_START(parprn)
+ ROM_REGION(0x100, "prom", 0)
+ ROM_LOAD( "prom.b4", 0x0000, 0x0100, BAD_DUMP CRC(00b742ca) SHA1(c67888354aa013f9cb882eeeed924e292734e717) )
+ ROM_END
+
+ INPUT_PORTS_START(parprn)
+ PORT_START("CFG")
+ PORT_CONFNAME(0x01, 0x00, "Acknowledge latching edge")
+ PORT_CONFSETTING( 0x00, "Falling (/Y-B)")
+ PORT_CONFSETTING( 0x01, "Rising (Y-B)")
+ PORT_CONFNAME(0x06, 0x02, "Printer ready")
+ PORT_CONFSETTING( 0x00, "Always (S5-C-D)")
+ PORT_CONFSETTING( 0x02, "Acknowledge latch (Z-C-D)")
+ PORT_CONFSETTING( 0x04, "ACK (Y-C-D)")
+ PORT_CONFSETTING( 0x06, "/ACK (/Y-C-D)")
+ PORT_CONFNAME(0x08, 0x00, "Strobe polarity")
+ PORT_CONFSETTING( 0x00, "Negative (S5-A-/X, GND-X)")
+ PORT_CONFSETTING( 0x08, "Positive (S5-X, GND-A-/X)")
+ PORT_CONFNAME(0x10, 0x10, "Character width")
+ PORT_CONFSETTING( 0x00, "7-bit")
+ PORT_CONFSETTING( 0x10, "8-bit")
+ INPUT_PORTS_END
+
+ } // anonymous namespace
+
+ tiny_rom_entry const *a2bus_parprn_device::device_rom_region() const
+ {
+ return ROM_NAME(parprn);
+ }
+
+ ioport_constructor a2bus_parprn_device::device_input_ports() const
+ {
+ return INPUT_PORTS_NAME(parprn);
+ }
+
+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:
+
+.. code-block:: C++
+
+ void a2bus_parprn_device::device_add_mconfig(machine_config &config)
+ {
+ CENTRONICS(config, m_printer_conn, centronics_devices, "printer");
+ m_printer_conn->ack_handler().set(FUNC(a2bus_parprn_device::ack_w));
+
+ OUTPUT_LATCH(config, m_printer_out);
+ m_printer_conn->set_output_latch(*m_printer_out);
+ }
+
+Object finders are passed to device types to provide tags when instantiating
+child devices. After instantiating a child device in this way, the object
+finder can be used like a pointer to the device until the end of the machine
+configuration member function. Note that to use an object finder like this,
+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:
+
+.. code-block:: C++
+
+ void a2bus_parprn_device::write_c0nx(u8 offset, u8 data)
+ {
+ ioport_value const cfg(m_input_config->read());
+
+ m_printer_out->write(data & (BIT(cfg, 8) ? 0xffU : 0x7fU));
+ m_printer_conn->write_strobe(BIT(~cfg, 3));
+ }
+
+
+ u8 a2bus_parprn_device::read_cnxx(u8 offset)
+ {
+ offset ^= 0x40U;
+ return m_prom[offset];
+ }
+
+For convenience, object finders that target the base pointer of memory regions
+and shares can be indexed like arrays.
+
+
+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):
+
+.. code-block:: C++
+
+ DECLARE_DEVICE_TYPE(SBUS, sbus_device)
+
+ class sbus_device : public device_t, public device_memory_interface
+ {
+ template <typename T, typename U>
+ sbus_device(
+ machine_config const &mconfig, char const *tag, device_t *owner, u32 clock,
+ T &&cpu_tag,
+ U &&space_tag, int space_num) :
+ sbus_device(mconfig, tag, owner, clock)
+ {
+ set_cpu(std::forward<T>(cpu_tag));
+ set_type1space(std::forward<U>(space_tag), space_num);
+ }
+
+ sbus_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, SBUS, tag, owner, clock),
+ device_memory_interface(mconfig, *this),
+ m_maincpu(*this, finder_base::DUMMY_TAG),
+ m_type1space(*this, finder_base::DUMMY_TAG, -1)
+ {
+ }
+
+ template <typename T> void set_cpu(T &&tag) { m_maincpu.set_tag(std::forward<T>(tag)); }
+ template <typename T> void set_type1space(T &&tag, int num) { m_type1space.set_tag(std::forward<T>(tag), num); }
+
+ protected:
+ required_device<sparc_base_device> m_maincpu;
+ required_address_space m_type1space;
+ };
+
+There are several things to take note of here:
+
+* Object finder members are declared for the things the device needs to access.
+* The device doesn’t know how it will fit into a larger system, the object
+ finders are constructed with dummy arguments.
+* Configuration member functions are provided to set the tag for the host CPU,
+ and the tag and index for the type 1 address space.
+* In addition to the standard device constructor, a constructor with additional
+ parameters for setting the CPU and type 1 address space is provided.
+
+The constant ``finder_base::DUMMY_TAG`` is guaranteed to be invalid and will not
+resolve to an object. This makes it easy to detect incomplete configuration and
+report an error. Address spaces are numbered from zero, so a negative address
+space number is invalid.
+
+The member functions for configuring object finders take a universal reference
+to a tag-like object (templated type with ``&&`` qualifier), as well as any
+other parameters needed by the specific type of object finder. An address space
+finder needs an address space number in addition to a tag-like object.
+
+So what’s a tag-like object? Three things are supported:
+
+* A C string pointer (``char const *``) representing a tag relative to the
+ device being configured. Note that the object finder will not copy the
+ string. The caller must ensure it remains valid until resolution and/or
+ validation is complete.
+* Another object finder. The object finder will take on its current target.
+* For device finders, a reference to an instance of the target device type,
+ setting the target to that device. Note that this will not work if the device
+ is subsequently replaced in the machine configuration. It’s most often used
+ with ``*this``.
+
+The additional constructor that sets initial configuration delegates to the
+standard constructor and then calls the configuration member functions. It’s
+purely for convenience.
+
+When we want to instantiate this device and hook it up, we do this::
+
+ SPARCV7(config, m_maincpu, 20'000'000);
+
+ ADDRESS_MAP_BANK(config, m_type1space);
+
+ SBUS(config, m_sbus, 20'000'000);
+ m_sbus->set_cpu(m_maincpu);
+ m_sbus->set_type1space(m_type1space, 0);
+
+We supply the same object finders to instantiate the CPU and address space
+devices, and to configure the SBus device.
+
+Note that we could also use literal C strings to configure the SBus device, at
+the cost of needing to update the tags in multiple places if they change::
+
+ SBUS(config, m_sbus, 20'000'000);
+ m_sbus->set_cpu("maincpu");
+ m_sbus->set_type1space("type1", 0);
+
+If we want to use the convenience constructor, we just supply additional
+arguments when instantiating the device::
+
+ SBUS(config, m_sbus, 20'000'000, m_maincpu, m_type1space, 0);
+
+
+Object finder arrays
+--------------------
+
+Many systems have multiple similar devices, I/O ports or other resources that
+can be logically organised as an array. To simplify these use cases, object
+finder array types are provided. The object finder array type names have
+``_array`` added to them:
+
++------------------------+------------------------------+
+| required_device | required_device_array |
++------------------------+------------------------------+
+| optional_device | optional_device_array |
++------------------------+------------------------------+
+| required_memory_region | required_memory_region_array |
++------------------------+------------------------------+
+| optional_memory_region | optional_memory_region_array |
++------------------------+------------------------------+
+| required_memory_bank | required_memory_bank_array |
++------------------------+------------------------------+
+| optional_memory_bank | optional_memory_bank_array |
++------------------------+------------------------------+
+| memory_bank_creator | memory_bank_array_creator |
++------------------------+------------------------------+
+| required_ioport | required_ioport_array |
++------------------------+------------------------------+
+| optional_ioport | optional_ioport_array |
++------------------------+------------------------------+
+| required_address_space | required_address_space_array |
++------------------------+------------------------------+
+| optional_address_space | optional_address_space_array |
++------------------------+------------------------------+
+| required_region_ptr | required_region_ptr_array |
++------------------------+------------------------------+
+| optional_region_ptr | optional_region_ptr_array |
++------------------------+------------------------------+
+| required_shared_ptr | required_shared_ptr_array |
++------------------------+------------------------------+
+| optional_shared_ptr | optional_shared_ptr_array |
++------------------------+------------------------------+
+| memory_share_creator | memory_share_array_creator |
++------------------------+------------------------------+
+
+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
+ {
+ protected:
+ keyboard_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, type, tag, owner, clock),
+ device_mac_keyboard_interface(mconfig, *this),
+ m_rows(*this, "ROW%u", 0U)
+ {
+ }
+
+ u8 bus_r()
+ {
+ u8 result(0xffU);
+ for (unsigned i = 0U; m_rows.size() > i; ++i)
+ {
+ if (!BIT(m_row_drive, i))
+ result &= m_rows[i]->read();
+ }
+ return result;
+ }
+
+ required_ioport_array<10> m_rows;
+ };
+
+Constructing an object finder array is similar to constructing an object finder,
+except that rather than just a tag you supply a tag format string and index
+offset. In this case, the tags of the I/O ports in the array will be ``ROW0``,
+``ROW1``, ``ROW2``, … ``ROW9``. Note that the object finder array allocates
+dynamic storage for the tags, which remain valid until destruction.
+
+The object finder array is used in much the same way as a ``std::array`` of the
+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:
+
+.. code-block:: C++
+
+ class dooyong_state : public driver_device
+ {
+ protected:
+ dooyong_state(machine_config const &mconfig, device_type type, char const *tag) :
+ driver_device(mconfig, type, tag),
+ m_bg(*this, "bg%u", 1U),
+ m_fg(*this, "fg%u", 1U)
+ {
+ }
+
+ optional_device_array<dooyong_rom_tilemap_device, 2> m_bg;
+ optional_device_array<dooyong_rom_tilemap_device, 2> m_fg;
+ };
+
+This causes ``m_bg`` to find devices with tags ``bg1`` and ``bg2``, while
+``m_fg`` finds devices with tags ``fg1`` and ``fg2``. Note that the indexes
+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``):
+
+.. code-block:: C++
+
+ class eurit_state : public driver_device
+ {
+ public:
+ eurit_state(machine_config const &mconfig, device_type type, char const *tag) :
+ driver_device(mconfig, type, tag),
+ m_keys(*this, "KEY%c", 'A')
+ {
+ }
+
+ private:
+ required_ioport_array<5> m_keys;
+ };
+
+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:
+
+.. code-block:: C++
+
+ class seabattl_state : public driver_device
+ {
+ public:
+ seabattl_state(machine_config const &mconfig, device_type type, char const *tag) :
+ driver_device(mconfig, type, tag),
+ m_digits(*this, { "sc_thousand", "sc_hundred", "sc_half", "sc_unity", "tm_half", "tm_unity" })
+ {
+ }
+
+ private:
+ required_device_array<dm9368_device, 6> m_digits;
+ };
+
+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):
+
+.. code-block:: C++
+
+ class dreamwld_state : public driver_device
+ {
+ public:
+ 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;
+ };
+
+This finds or creates memory shares with tags ``vram_0`` and ``vram_1``, each 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;
+ }
+
+ u8 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();
+ }
+
+
+Object finder types in more detail
+----------------------------------
+
+All object finders provide configuration functionality:
+
+.. code-block:: C++
+
+ char const *finder_tag() const { return m_tag; }
+ std::pair<device_t &, char const *> finder_target();
+ void set_tag(device_t &base, char const *tag);
+ void set_tag(char const *tag);
+ void set_tag(finder_base const &finder);
+
+The ``finder_tag`` and ``finder_target`` member function provides access to the
+currently configured target. Note that the tag returned by ``finder`` tag is
+relative to the base device. It is not sufficient on its own to identify the
+target.
+
+The ``set_tag`` member functions configure the target of the object finder.
+These members must not be called after the object finder is resolved. The first
+form configures the base device and relative tag. The second form sets the
+relative tag and also implicitly sets the base device to the device that is
+currently being configured. This form must only be called from machine
+configuration functions. The third form sets the base object and relative tag
+to the current target of another object finder.
+
+Note that the ``set_tag`` member functions **do not** copy the relative tag. It
+is the caller’s responsibility to ensure the C string remains valid until the
+object finder is resolved (or reconfigured with a different tag). The base
+device must also be valid at resolution time. This may not be the case if the
+device could be removed or replaced later.
+
+All object finders provide the same interface for accessing the target object:
+
+.. code-block:: C++
+
+ ObjectClass *target() const;
+ operator ObjectClass *() const;
+ ObjectClass *operator->() const;
+
+These members all provide access to the target object. The ``target`` member
+function and cast-to-pointer operator will return ``nullptr`` if the target has
+not been found. The pointer member access operator asserts that the target has
+been found.
+
+Optional object finders additionally provide members for testing whether the
+target object has been found:
+
+.. code-block:: C++
+
+ bool found() const;
+ explicit operator bool() const;
+
+These members return ``true`` if the target was found, on the assumption
+that the target pointer will be non-null if the target was found.
+
+Device finders
+~~~~~~~~~~~~~~
+
+Device finders require one template argument for the expected device class.
+This should derive from either ``device_t`` or ``device_interface``. The target
+device object must either be an instance of this class, an instance of a class
+that derives from it. A warning message is logged if a matching device is found
+but it is not an instance of the expected class.
+
+Device finders provide an additional ``set_tag`` overload:
+
+.. code-block:: C++
+
+ set_tag(DeviceClass &object);
+
+This is equivalent to calling ``set_tag(object, DEVICE_SELF)``. Note that the
+device object must not be removed or replaced before the object finder is
+resolved.
+
+Memory system object finders
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The memory system object finders, ``required_memory_region``,
+``optional_memory_region``, ``required_memory_bank``, ``optional_memory_bank``
+and ``memory_bank_creator``, do not have any special functionality. They are
+often used in place of literal tags when installing memory banks in an address
+space.
+
+Example using memory bank finders in an address map:
+
+.. code-block:: C++
+
+ class qvt70_state : public driver_device
+ {
+ public:
+ qvt70_state(machine_config const &mconfig, device_type type, char const *tag) :
+ driver_device(mconfig, type, tag),
+ m_rombank(*this, "rom"),
+ m_rambank(*this, "ram%d", 0U),
+ { }
+
+ private:
+ required_memory_bank m_rombank;
+ required_memory_bank_array<2> m_rambank;
+
+ void mem_map(address_map &map);
+
+ void rombank_w(u8 data);
+ };
+
+ void qvt70_state::mem_map(address_map &map)
+ {
+ map(0x0000, 0x7fff).bankr(m_rombank);
+ map(0x8000, 0x8000).w(FUNC(qvt70_state::rombank_w));
+ map(0xa000, 0xbfff).ram();
+ map(0xc000, 0xdfff).bankrw(m_rambank[0]);
+ map(0xe000, 0xffff).bankrw(m_rambank[1]);
+ }
+
+Example using a memory bank creator to install a memory bank dynamically:
+
+.. code-block:: C++
+
+ class vegaeo_state : public eolith_state
+ {
+ public:
+ vegaeo_state(machine_config const &mconfig, device_type type, char const *tag) :
+ eolith_state(mconfig, type, tag),
+ m_qs1000_bank(*this, "qs1000_bank")
+ {
+ }
+
+ void init_vegaeo();
+
+ private:
+ memory_bank_creator m_qs1000_bank;
+ };
+
+ void vegaeo_state::init_vegaeo()
+ {
+ // Set up the QS1000 program ROM banking, taking care not to overlap the internal RAM
+ m_qs1000->cpu().space(AS_IO).install_read_bank(0x0100, 0xffff, m_qs1000_bank);
+ m_qs1000_bank->configure_entries(0, 8, memregion("qs1000:cpu")->base() + 0x100, 0x10000);
+
+ init_speedup();
+ }
+
+I/O port finders
+~~~~~~~~~~~~~~~~
+
+Optional I/O port finders provide an additional convenience member function:
+
+.. code-block:: C++
+
+ ioport_value read_safe(ioport_value defval);
+
+This will read the port’s value if the target I/O port was found, or return
+``defval`` otherwise. It is useful in situations where certain input devices
+are not always present.
+
+
+Address space finders
+~~~~~~~~~~~~~~~~~~~~~
+
+Address space finders accept an additional argument for the address space number
+to find. A required data width can optionally be supplied to the constructor.
+
+.. code-block:: C++
+
+ address_space_finder(device_t &base, char const *tag, int spacenum, u8 width = 0);
+ void set_tag(device_t &base, char const *tag, int spacenum);
+ void set_tag(char const *tag, int spacenum);
+ void set_tag(finder_base const &finder, int spacenum);
+ template <bool R> void set_tag(address_space_finder<R> const &finder);
+
+The base device and tag must identify a device that implements
+``device_memory_interface``. The address space number is a zero-based index to
+one of the device’s address spaces.
+
+If the width is non-zero, it must match the target address space’s data width in
+bits. If the target address space exists but has a different data width, a
+warning message will be logged, and it will be treated as not being found. If
+the width is zero (the default argument value), the target address space’s data
+width won’t be checked.
+
+Member functions are also provided to get the configured address space number
+and set the required data width:
+
+.. code-block:: C++
+
+ int spacenum() const;
+ void set_data_width(u8 width);
+
+Memory pointer finders
+~~~~~~~~~~~~~~~~~~~~~~
+
+The memory pointer finders, ``required_region_ptr``, ``optional_region_ptr``,
+``required_shared_ptr``, ``optional_shared_ptr`` and ``memory_share_creator``,
+all require one template argument for the element type of the memory area. This
+should usually be an explicitly-sized unsigned integer type (``u8``, ``u16``,
+``u32`` or ``u64``). The size of this type is compared to the width of the
+memory area. If it doesn’t match, a warning message is logged and the region or
+share is treated as not being found.
+
+The memory pointer finders provide an array access operator, and members for
+accessing the size of the memory area:
+
+.. code-block:: C++
+
+ PointerType &operator[](int index) const;
+ size_t length() const;
+ size_t bytes() const;
+
+The array access operator returns a non-\ ``const`` reference to an element of
+the memory area. The index is in units of the element type; it must be
+non-negative and less than the length of the memory area. The ``length`` member
+returns the number of elements in the memory area. The ``bytes`` member returns
+the size of the memory area in bytes. These members should not be called if the
+target region/share has not been found.
+
+The ``memory_share_creator`` requires additional constructor arguments for the
+size and Endianness of the memory share:
+
+.. code-block:: C++
+
+ memory_share_creator(device_t &base, char const *tag, size_t bytes, endianness_t endianness);
+
+The size is specified in bytes. If an existing memory share is found, it is an
+error if its size does not match the specified size. If the width is wider than
+eight bits and an existing memory share is found, it is an error if its
+Endianness does not match the specified Endianness.
+
+The ``memory_share_creator`` provides additional members for accessing
+properties of the memory share:
+
+.. code-block:: C++
+
+ endianness_t endianness() const;
+ u8 bitwidth() const;
+ u8 bytewidth() const;
+
+These members return the Endianness, width in bits and width in bytes of the
+memory share, respectively. They must not be called if the memory share has not
+been found.
+
+
+Output finders
+--------------
+
+Output finders are used for exposing outputs that can be used by the artwork
+system, or by external programs. A common application using an external program
+is a control panel or cabinet lighting controller.
+
+Output finders are not really object finders, but they’re described here because
+they’re used in a similar way. There are a number of important differences to
+be aware of:
+
+* Output finders always create outputs if they do not exist.
+* Output finders must be manually resolved, they are not automatically resolved.
+* Output finders cannot have their target changed after construction.
+* Output finders are array-like, and support an arbitrary number of dimensions.
+* Output names are global, the base device has no influence. (This will change
+ in the future.)
+
+Output finders take a variable number of template arguments corresponding to the
+number of array dimensions you want. Let’s look at an example that uses zero-,
+one- and two-dimensional output finders:
+
+.. code-block:: C++
+
+ class mmd2_state : public driver_device
+ {
+ public:
+ mmd2_state(machine_config const &mconfig, device_type type, char const *tag) :
+ driver_device(mconfig, type, tag),
+ m_digits(*this, "digit%u", 0U),
+ m_p(*this, "p%u_%u", 0U, 0U),
+ m_led_halt(*this, "led_halt"),
+ m_led_hold(*this, "led_hold")
+ { }
+
+ protected:
+ virtual void machine_start() override;
+
+ private:
+ void round_leds_w(offs_t, u8);
+ void digit_w(u8 data);
+ void status_callback(u8 data);
+
+ u8 m_digit;
+
+ output_finder<9> m_digits;
+ output_finder<3, 8> m_p;
+ output_finder<> m_led_halt;
+ output_finder<> m_led_hold;
+ };
+
+The ``m_led_halt`` and ``m_led_hold`` members are zero-dimensional output
+finders. They find a single output each. The ``m_digits`` member is a
+one-dimensional output finder. It finds nine outputs organised as a
+single-dimensional array. The ``m_p`` member is a two-dimensional output
+finder. It finds 24 outputs organised as three rows of eight columns each.
+Larger numbers of dimensions are supported.
+
+The output finder constructor takes a base device reference, a format string,
+and an index offset for each dimension. In this case, all the offsets are
+zero. The one-dimensional output finder ``m_digits`` will find outputs
+``digit0``, ``digit1``, ``digit2``, … ``digit8``. The two-dimensional output
+finder ``m_p`` will find the outputs ``p0_0``, ``p0_1``, … ``p0_7`` for the
+first row, ``p1_0``, ``p1_1``, … ``p1_7`` for the second row, and ``p2_0``,
+``p2_1``, … ``p2_7`` for the third row.
+
+You must call ``resolve`` on each output finder before it can be used. This
+should be done at start time for the output values to be included in save
+states:
+
+.. code-block:: C++
+
+ void mmd2_state::machine_start()
+ {
+ m_digits.resolve();
+ m_p.resolve();
+ m_led_halt.resolve();
+ m_led_hold.resolve();
+
+ save_item(NAME(m_digit));
+ }
+
+Output finders provide operators allowing them to be assigned from or cast to
+32-bit signed integers. The assignment operator will send a notification if the
+new value is different to the output’s current value.
+
+.. code-block:: C++
+
+ operator s32() const;
+ s32 operator=(s32 value);
+
+To set output values, assign through the output finders, as you would with an
+array of the same rank:
+
+.. code-block:: C++
+
+ void mmd2_state::round_leds_w(offs_t offset, u8 data)
+ {
+ for (u8 i = 0; i < 8; i++)
+ m_p[offset][i] = BIT(~data, i);
+ }
+
+ void mmd2_state::digit_w(u8 data)
+ {
+ if (m_digit < 9)
+ m_digits[m_digit] = data;
+ }
+
+ void mmd2_state::status_callback(u8 data)
+ {
+ m_led_halt = (~data & i8080_cpu_device::STATUS_HLTA) ? 1 : 0;
+ m_led_hold = (data & i8080_cpu_device::STATUS_WO) ? 1 : 0;
+ }
diff --git a/docs/source/techspecs/osd_audio.rst b/docs/source/techspecs/osd_audio.rst
new file mode 100644
index 00000000000..82e03d999ef
--- /dev/null
+++ b/docs/source/techspecs/osd_audio.rst
@@ -0,0 +1,348 @@
+OSD audio support
+=================
+
+Introduction
+------------
+
+The audio support in Mame tries to allow the user to freely map
+between the emulated system audio outputs (called speakers) and the
+host system audio. A part of it is the OSD support, where a
+host-specific module ensures the interface between Mame and the host.
+This is the documentation for that module.
+
+Note: this is currenty output-only, but input should follow.
+
+
+Capabitilies
+------------
+
+The OSD interface is designed to allow three levels of support,
+depending on what the API allows and the amount of effort to expend.
+Those are:
+
+* Level 1: One or more audio targets, only one stream allowed per target
+ (aka exclusive mode)
+* Level 2: One or more audio targets, multiple streams per target
+* Level 3: One or more audio targets, multiple streams per target, user-visible
+ per-stream-channel volume control
+
+In any case we support having the user use an external interface to
+change the target of a stream and, in level 3, change the volumes. By
+support we mean storing the information in the per-game configuration
+and keeping the internal UI in sync.
+
+
+Terminology
+-----------
+
+For this module, we use the terms:
+
+* node: some object we can send audio to. Can be physical, like speakers,
+ or virtual, like an effect system. It should have a unique, user-presentable
+ name for the UI.
+* port: a channel of a node, has a name (non-unique, like "front left") and a
+ 3D position
+* stream: a connection to a node with allows to send audio to it
+
+
+Reference documentation
+-----------------------
+
+Adding a module
+~~~~~~~~~~~~~~~
+
+Adding a module is done by adding a cpp file to src/osd/modules/sound
+which follows this structure,
+
+.. code-block:: C++
+
+ // License/copyright
+ #include "sound_module.h"
+ #include "modules/osdmodules.h"
+
+ #ifdef MODULE_SUPPORT_KEY
+
+ #include "modules/lib/osdobj_common.h"
+
+ // [...]
+ namespace osd {
+ namespace {
+
+ class sound_module_class : public osd_module, public sound_module
+ {
+ sound_module_class() : osd_module(OSD_SOUND_PROVIDER, "module_name"),
+ sound_module()
+ // ...
+ };
+
+ }
+ }
+ #else
+ namespace osd { namespace {
+ MODULE_NOT_SUPPORTED(sound_module_class, OSD_SOUND_PROVIDER, "module_name")
+ }}
+ #endif
+
+ MODULE_DEFINITION(SOUND_MODULE_KEY, osd::sound_module_class)
+
+In that code, four names must be chosen:
+
+* MODULE_SUPPORT_KEY some #define coming from the genie scripts to tell that
+ this particular module can be compiled (like NO_USE_PIPEWIRE or SDLMAME_MACOSX)
+* sound_module_class is the name of the class which makes up the module
+ (like sound_coreaudio)
+* module_name is the name to be used in -sound <xxx> to select that particular
+ module (like coreaudio)
+* SOUND_MODULE_KEY is a symbol that represents the module internally (like
+ SOUND_COREAUDIO)
+
+The file path needs to be added to scripts/src/osd/modules.lua in
+osdmodulesbuild() and the module reference to
+src/osd/modules/lib/osdobj_common.cpp in
+osd_common_t::register_options with the line:
+
+.. code-block:: C++
+
+ REGISTER_MODULE(m_mod_man, SOUND_MODULE_KEY);
+
+This should ensure that the module is reachable through -sound <xxx>
+on the appropriate hosts.
+
+
+Interface
+~~~~~~~~~
+
+The full interface is:
+
+.. code-block:: C++
+
+ virtual bool split_streams_per_source() const override;
+ virtual bool external_per_channel_volume() const override;
+
+ virtual int init(osd_interface &osd, osd_options const &options) override;
+ virtual void exit() override;
+
+ virtual uint32_t get_generation() override;
+ virtual osd::audio_info get_information() override;
+ virtual uint32_t stream_sink_open(uint32_t node, std::string name, uint32_t rate) override;
+ virtual uint32_t stream_source_open(uint32_t node, std::string name, uint32_t rate) override;
+ virtual void stream_set_volumes(uint32_t id, const std::vector<float> &db) override;
+ virtual void stream_close(uint32_t id) override;
+ virtual void stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) override;
+ virtual void stream_source_update(uint32_t id, int16_t *buffer, int samples_this_frame) override;
+
+
+The class sound_module provides defaults for minimum capabilities: one
+stereo target and stream at default sample rate. To support that,
+only *init*, *exit* and *stream_update* need to be implemented.
+*init* is called at startup and *exit* when quitting and can do
+whatever they need to do. *stream_sink_update* will be called on a
+regular basis with a buffer of sample_this_frame * 2 * int16_t with the
+audio to play. From this point in the documentation we'll assume more
+than a single stereo channel is wanted.
+
+
+Capabilities
+~~~~~~~~~~~~
+
+Two methods are used by the module to indicate the level of capability
+of the module:
+
+* split_streams_per_source() should return true when having multiple streams
+ for one target is expected (e.g. Level 2 or 3)
+* external_per_channel_volume() should return true when the streams have
+ per-channel volume control that can be externally controlled (e.g. Level 3)
+
+
+Hardware information and generations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The core runs on the assumption that the host hardware capabilities
+can change at any time (bluetooth devices coming and going, usb
+hot-plugging...) and that the module has some way to keep tabs on what
+is happening, possibly using multi-threading. To keep it
+lightweight-ish, we use the concept of a *generation* which is a
+32-bit number that is incremented by the module every time something
+changes. The core checks the current generation value at least once
+every update (once per frame, usually) and if it changed asks for the
+new state and detects and handles the differences. *generation*
+should be "eventually stable", e.g. it eventually stops changing when
+the user stops changing things all the time. A systematic increment
+every frame would be a bad idea.
+
+.. code-block:: C++
+
+ virtual uint32_t get_generation() override;
+
+That method returns the current generation number. It's called at a
+minimum once per update, which usually means per frame. It whould be
+reasonably lightweight when nothing special happens.
+
+.. code-block: C++
+
+ virtual osd::audio_info get_information() override;
+
+ struct audio_rate_range {
+ uint32_t m_default_rate;
+ uint32_t m_min_rate;
+ uint32_t m_max_rate;
+ };
+
+ struct audio_info {
+ struct node_info {
+ std::string m_name;
+ uint32_t m_id;
+ audio_rate_range m_rate;
+ std::vector<std::string> m_port_names;
+ std::vector<std::array<double, 3>> m_port_positions;
+ uint32_t m_sinks;
+ uint32_t m_sources;
+ };
+
+ struct stream_info {
+ uint32_t m_id;
+ uint32_t m_node;
+ std::vector<float> m_volumes;
+ };
+
+ uint32_t m_generation;
+ uint32_t m_default_sink;
+ uint32_t m_default_source;
+ std::vector<node_info> m_nodes;
+ std::vector<stream_info> m_streams;
+ };
+
+This method must provide all the information about the current state
+of the host and the module. This state is:
+
+* m_generation: The current generation number
+* m_nodes: The vector available nodes (*node_info*)
+
+ * m_name: The name of the node
+ * m_id: The numeric ID of the node
+ * m_rate: The minimum, maximum and preferred sample rate for the node
+ * m_port_names: The vector of port names
+ * m_port_positions: The vector of 3D position of the ports. Refer to
+ src/emu/speaker.h for the "standard" positions
+ * m_sinks: Number of sinks (inputs)
+ * m_sources: Number of sources (outputs)
+
+* m_default_sink: ID of the node that is the current "system default" for
+ audio output, 0 if there's no such concept
+* m_default_source: same for audio input (currently unused)
+* m_streams: The vector of active streams (*stream_info*)
+
+ * m_id: The numeric ID of the stream
+ * m_node: The target node of the stream
+ * m_volumes: empty if *external_per_channel_volume* is false, current volume
+ value per-channel otherwise
+
+IDs, for nodes and streams, are (independant) 32-bit unsigned non-zero
+values associated to respectively nodes and streams. IDs should not
+be reused. A node that goes away then comes back should get a new ID.
+A stream closing does not allow reuse of its ID.
+
+If a node has both sources and sinks, the sources are *monitors* of
+the sinks, e.g. they're loopbacks. They should have the same count in
+such a case.
+
+When external control exists, a module should change the value of
+*stream_info::m_node* when the user changes it, and same for
+*stream_info::m_volumes*. Generation number should be incremented
+when this happens, so that the core knows to look for changes.
+
+Volumes are floats in dB, where 0 means 100% and -96 means no sound.
+audio.h provides osd::db_to_linear and osd::linear_to_db if such a
+conversion is needed.
+
+There is an inherent race condition with this system, because things
+can change at any point after returning for the method. The idea is
+that the information returned must be internally consistent (a stream
+should not point to a node ID that does not exist in the structure,
+same for default sink) and that any external change from that state
+should increment the generation number, but that's it. Through the
+generation system the core will eventually be in sync with reality.
+
+
+Input and output streams
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ virtual uint32_t stream_sink_open(uint32_t node, std::string name, uint32_t rate) override;
+ virtual uint32_t stream_source_open(uint32_t node, std::string name, uint32_t rate) override;
+ virtual void stream_set_volumes(uint32_t id, const std::vector<float> &db) override;
+ virtual void stream_close(uint32_t id) override;
+ virtual void stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) override;
+ virtual void stream_source_update(uint32_t id, int16_t *buffer, int samples_this_frame) override;
+
+Streams are the concept used to send or receive audio from/to the host
+audio system. A stream is first opened through *stream_sink_open* for
+speakers and *stream_source_open* for microphones and targets a
+specific node at a specific sample rate. It is given a name for use
+by the host sound services for user UI purposes (currently the game
+name if split_streams_per_source is false, the
+speaker_device/microphone_device tag if true). The returned ID must
+be a non-zero, never-used-before for streams value in case of success.
+Failures, like when the node went away between the *get_information*
+call and the open one, should be silent and return zero.
+
+*stream_set_volumes* is used only when *external_per_channel_volume*
+is true and is used by the core to set the per-channel volume. The
+call should just be ignored if the stream ID does not exist (or is
+zero). Do not try to apply volumes in the module if the host API
+doesn't provide for it, let the core handle it.
+
+*stream_close* closes a stream, The call should just be ignored if the
+stream ID does not exist (or is zero).
+
+Opening a stream, closing a stream or changing the volume does not
+need to touch the generation number.
+
+*stream_sink_update* is the method used to send data to the node
+through a given stream. It provides a buffer of *samples_this_frame*
+* *node channel count* channel-interleaved int16_t values. The
+lifetime of the data in the buffer or the buffer pointer itself is
+undefined after return from the method call. The call should just be
+ignored if the stream ID does not exist (or is zero).
+
+*stream_source_update* is the equivalent to retrieve data from a node,
+writing to the buffer instead of reading from it. The constraints are
+identical.
+
+When a stream goes away because the target node is lost it should just
+be removed from the information, and the core will pick up the node
+departure and close the stream.
+
+Given the assumed raceness of the interface, all the methods should be
+tolerant of obsolete or zero IDs being used by the core, and that is
+why ID reuse must be avoided. Also the update methods and the
+open/close/volume ones may be called at the same time in different
+threads.
+
+
+Helper class *abuffer*
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: C++
+
+ class abuffer {
+ public:
+ abuffer(uint32_t channels);
+ void get(int16_t *data, uint32_t samples);
+ void push(const int16_t *data, uint32_t samples);
+ uint32_t channels() const;
+ };
+
+The class *abuffer* is a helper provided by *sound_module* to buffer
+audio input or output. It automatically drops data when there is
+an overflow and duplicates the last sample on underflow. It must
+first be initialized with the number of channels, which can be
+retrieved with *channels()* if needed. *push* sends
+*samples* * *channels* 16-bit samples in the buffer. *get* retrieves
+*samples* * *channels* 16-bit samples from the buffer, on a FIFO basis.
+
+It is not protected against multithreading, but uses no class
+variables. So just don't read and write from one specific abuffer
+instance at the same time. The system sound interface mandated
+locking should be enough to ensure that.
diff --git a/docs/source/techspecs/poly_manager.rst b/docs/source/techspecs/poly_manager.rst
new file mode 100644
index 00000000000..d30a57c39c3
--- /dev/null
+++ b/docs/source/techspecs/poly_manager.rst
@@ -0,0 +1,1084 @@
+Software 3D Rendering in MAME
+=============================
+
+.. contents:: :local:
+
+
+Background
+----------
+
+Beginning in the late 1980s, many arcade games began incorporating hardware-rendered
+3D graphics into their video. These 3D graphics are typically rendered from low-level
+primitives into a frame buffer (usually double- or triple-buffered), then perhaps
+combined with traditional tilemaps or sprites, before being presented to the player.
+
+When it comes to emulating 3D games, there are two general approaches. The first
+approach is to leverage modern 3D hardware by mapping the low-level primitives onto
+modern equivalents. For a cross-platform emulator like MAME, this requires having an
+API that is flexible enough to describe the primitives and all their associated
+behaviors with high accuracy. It also requires the emulator to be able to read back
+from the rendered frame buffer (since many games do this) and combine it with other
+elements, in a way that is properly synchronized with background rendering.
+
+The alternative approach is to render the low-level primitives directly in software.
+This has the advantage of being able to achieve pretty much any behavior exhibited by
+the original hardware, but at the cost of speed. In MAME, since all emulation happens
+on one thread, this is particularly painful. However, just as with the 3D hardware
+approach, in theory a software-based approach could be spun off to other threads to
+handle the work, as long as mechanisms were present to synchronize when necessary,
+for example, when reading/writing directly to/from the frame buffer.
+
+For the time being, MAME has opted for the second approach, leveraging a templated
+helper class called **poly_manager** to handle common situations.
+
+
+Concepts
+--------
+
+At its core, **poly_manager** is a mechanism to support multi-threaded rendering of
+low-level 3D primitives. Callers provide **poly_manager** with a set of *vertices* for a
+primitive plus a *render callback*. **poly_manager** breaks the primitive into
+clipped scanline *extents* and distributes the work among a pool of *worker
+threads*. The render callback is then called on the worker thread for each extent,
+where game-specific logic can do whatever needs to happen to render the data.
+
+One key responsibility that **poly_manager** takes care of is ensuring order. Given a
+pool of threads and a number of work items to complete, it is important that—at least
+within a given scanline—all work is performed serially in order. The basic approach is
+to assign each extent to a *bucket* based on the Y coordinate. **poly_manager** then ensures
+that only one worker thread at a time is responsible for processing work in a given bucket.
+
+Vertices in **poly_manager** consist of simple 2D X and Y *coordinates*, plus zero or
+more additional *iterated parameters*. These iterated parameters can be anything: intensity
+values for lighting; RGB(A) colors for Gouraud shading; normalized U, V coordinates for
+texture mapping; 1/Z values for Z buffering; etc. Iterated parameters, regardless of what
+they represent, are interpolated linearly across the primitive in screen space and provided
+as part of the extent to the render callback.
+
+
+ObjectType
+~~~~~~~~~~
+
+When creating a **poly_manager** class, you must provide it a special type that you define,
+known as **ObjectType**.
+
+Because rendering happens asynchronously on worker threads, the idea is that the
+**ObjectType** class will hold a snapshot of all the relevant data needed for rendering.
+This allows the main thread to proceed—potentially modifying some of the relevant state—while
+rendering happens elsewhere.
+
+In theory, we could allocate a new **ObjectType** class for each primitive rendered;
+however, that would be rather inefficient. It is quite common to set up the rendering
+state and then render several primitives using the same state.
+
+For this reason, **poly_manager** maintains an internal array of **ObjectType** objects and
+keeps a copy of the last **ObjectType** used. Before submitting a new primitive, callers
+can see if the rendering state has changed. If it has, it can ask **poly_manager** to allocate
+a new **ObjectType** class and fill it in. When the primitive is submitted for rendering, the
+most recently allocated **ObjectType** instance is implicitly captured and provided to the
+render callbacks.
+
+For more complex scenarios, where data might change even more infrequently, there is a
+**poly_array** template, which can be used to manage data in a similar way. In fact,
+internally **poly_manager** uses the **poly_array** class to manage its **ObjectType**
+allocations. More information on the **poly_array** class is provided later.
+
+
+
+Primitives
+~~~~~~~~~~
+
+**poly_manager** supports several different types of primitives:
+
+* The most commonly-used primitive in **poly_manager** is the *triangle*, which has the
+ nice property that iterated parameters have constant deltas across the full surface.
+ Arbitrary-length *triangle fans* and *triangle strips* are also supported.
+
+* In addition to triangles, **poly_manager** also supports *polygons* with an arbitrary
+ number of vertices. The list of vertices is expected to be in either clockwise or
+ anticlockwise order. **poly_manager** will walk the edges to compute deltas across
+ each extent.
+
+* As a special case, **poly_manager** supports a *tile* primitive, which is a simple quad
+ defined by two vertices, a top-left vertex and a bottom-right vertex. Like triangles,
+ tiles have constant iterated parameter deltas across their surface.
+
+* Finally, **poly_manager** supports a fully custom mechanism where the caller provides
+ a list of extents that are more or less fed directly to the worker threads.
+ This is useful if emulating a system that has unusual primitives or requires highly
+ specific behaviors for its edges.
+
+
+Synchronization
+~~~~~~~~~~~~~~~
+
+One of the key requirements of providing an asynchronous rendering mechanism is
+synchronization. Synchronization in **poly_manager** is super simple: just
+call the ``wait()`` function.
+
+There are several common reasons for issuing a wait:
+
+* At display time, the pixel data must be copied to the screen. If any primitives were
+ queued which touch the portion of the display that is going to be shown, you need to
+ wait for rendering to be complete before copying. Note that this wait may not be
+ strictly necessary in some situations (for example, a triple-buffered system).
+
+* If the emulated system has a mechanism to read back from the framebuffer after
+ rendering, then a wait must be issued prior to the read in order to ensure that
+ asynchronous rendering is complete.
+
+* If the emulated system modifies any state that is not cached in the **ObjectType**
+ or elsewhere (for example, texture memory), then a wait must be issued to ensure
+ that pending primitives which might consume that state have finished their work.
+
+* If the emulated system can use a previous render target as, say, the texture source
+ for a new primitive, then submitting the second primitive must wait until the first
+ completes. **poly_manager** provides no internal mechanism to help detect this, so it
+ is on the caller to determine when or if this is necessary.
+
+Because the wait operation knows after it is done that all rendering is complete,
+**poly_manager** also takes this opportunity to reclaim all memory allocated for its
+internal structures, as well as memory allocated for **ObjectType** structures. Thus it is
+important that you don’t hang onto any **ObjectType** pointers after a wait is called.
+
+
+The poly_manager class
+----------------------
+
+In most applications, **poly_manager** is not used directly, but rather serves as
+the base class for a more complete rendering class. The **poly_manager** class
+itself is a template::
+
+ template<typename BaseType, class ObjectType, int MaxParams, u8 Flags = 0>
+ class poly_manager;
+
+and the template parameters are:
+
+* **BaseType** is the type used internally for coordinates and iterated parameters, and
+ should generally be either ``float`` or ``double``. In theory, a fixed-point integral
+ type could also be used, though the math logic has not been designed for that, so you
+ may encounter problems.
+
+* **ObjectType** is the user-defined per-object data structure described above.
+ Internally, **poly_manager** will manage a **poly_array** of these, and a pointer to
+ the most-recently allocated one at the time a primitive is submitted will be implicitly
+ passed to the render callback for each corresponding extent.
+
+* **MaxParams** is the maximum number of iterated parameters that may be specified in a
+ vertex. Iterated parameters are generic and treated identically, so the mapping of
+ parameter indices is completely up to the contract between the caller and the render
+ callback. It is permitted for **MaxParams** to be 0.
+
+* **Flags** is zero or more of the following flags:
+
+ - POLY_FLAG_NO_WORK_QUEUE — specify this flag to disable asynchronous rendering; this
+ can be useful for debugging. When this option is enabled, all primitives are queued
+ and then processed in order on the calling thread when ``wait()`` is called on the
+ **poly_manager** class.
+
+ - POLY_FLAG_NO_CLIPPING — specify this if you want **poly_manager** to skip its
+ internal clipping. Use this if your render callbacks do their own clipping, or if
+ the caller always handles clipping prior to submitting primitives.
+
+
+Types & Constants
+~~~~~~~~~~~~~~~~~
+
+vertex_t
+++++++++
+
+Within the **poly_manager** class, you’ll find a **vertex_t** type that describes a
+single vertex. All primitive drawing methods accept 2 or more of these **vertex_t**
+objects. The **vertex_t** includes the X and Y coordinates along with an array of
+iterated parameter values at that vertex::
+
+ struct vertex_t
+ {
+ vertex_t() { }
+ vertex_t(BaseType _x, BaseType _y) { x = _x; y = _y; }
+
+ BaseType x, y; // X, Y coordinates
+ std::array<BaseType, MaxParams> p; // iterated parameters
+ };
+
+Note that **vertex_t** itself is defined in terms of the **BaseType** and **MaxParams**
+template values of the owning **poly_manager** class.
+
+All of **poly_manager**’s primitives operate in screen space, where (0,0) represents the
+top-left corner of the top-left pixel, and (0.5,0.5) represents the center of that pixel.
+Left and top pixel values are inclusive, while right and bottom pixel values are exclusive.
+
+Thus, a *tile* rendered from (2,2)-(4,3) will completely cover 2 pixels: (2,2) and (3,2).
+
+When calling a primitive drawing method, the iterated parameter array **p** need not be
+completely filled out. The number of valid iterated parameter values is specified as a
+template parameter to the primitive drawing methods, so only that many parameters need to
+actually be populated in the **vertex_t** structures that are passed in.
+
+
+extent_t
+++++++++
+
+**poly_manager** breaks primitives into extents, which are contiguous horizontal spans
+contained within a single scanline. These extents are then distributed to worker threads,
+who will call the render callback with information on how to render each extent. The
+**extent_t** type describes one such extent, providing the bounding X coordinates along with
+an array of iterated parameter start values and deltas across the span::
+
+ struct extent_t
+ {
+ struct param_t
+ {
+ BaseType start; // parameter value at start
+ BaseType dpdx; // dp/dx relative to start
+ };
+ int16_t startx, stopx; // starting (inclusive)/ending (exclusive) endpoints
+ std::array<param_t, MaxParams> param; // array of parameter start/deltas
+ void *userdata; // custom per-span data
+ };
+
+For each iterated parameter, the **start** value contains the value at the left side of
+the span. The **dpdx** value contains the change of the parameter’s value per X coordinate.
+
+There is also a **userdata** field in the **extent_t** structure, which is not normally used,
+except when performing custom rendering.
+
+
+render_delegate
++++++++++++++++
+
+When rendering a primitive, in addition to the vertices, you must also provide a
+**render_delegate** callback of the form::
+
+ void render(int32_t y, extent_t const &extent, ObjectType const &object, int threadid)
+
+This callback is responsible for the actual rendering. It will be called at a later time,
+likely on a different thread, for each extent. The parameters passed are:
+
+* **y** is the Y coordinate (scanline) of the current extent.
+
+* **extent** is a reference to a **extent_t** structure, described above, which specifies for
+ this extent the start/stop X values along with the start/delta values for each iterated
+ parameter.
+
+* **object** is a reference to the most recently allocated **ObjectType** at the time the
+ primitive was submitted for rendering; in theory it should contain most of not all of the
+ necessary data to perform rendering.
+
+* **threadid** is a unique ID indicating the index of the thread you’re running on; this value
+ is useful if you are keeping any kind of statistics and don’t want to add contention over
+ shared values. In this situation, you can allocate **WORK_MAX_THREADS** instances of your
+ data and update the instance for the **threadid** you are passed. When you want to display
+ the statistics, the main thread can accumulate and reset the data from all threads when it’s
+ safe to do so (e.g., after a wait).
+
+
+Methods
+~~~~~~~
+
+poly_manager
+++++++++++++
+::
+
+ poly_manager(running_machine &machine);
+
+The **poly_manager** constructor takes just one parameter, a reference to the
+**running_machine**. This grants **poly_manager** access to the work queues needed for
+multithreaded running.
+
+wait
+++++
+::
+
+ void wait(char const *debug_reason = "general");
+
+Calling ``wait()`` stalls the calling thread until all outstanding rendering is complete:
+
+* **debug_reason** is an optional parameter specifying the reason for the wait. It is
+ useful if the compile-time constant **TRACK_POLY_WAITS** is enabled, as it will print a
+ summary of wait times and reasons at the end of execution.
+
+**Return value:** none.
+
+object_data
++++++++++++
+::
+
+ objectdata_array &object_data();
+
+This method just returns a reference to the internally-maintained **poly_array** of the
+**ObjectType** you specified when creating **poly_manager**. For most applications, the
+only interesting thing to do with this object is call the ``next()`` method to allocate
+a new object to fill out.
+
+**Return value:** reference to a **poly_array** of **ObjectType**.
+
+register_poly_array
++++++++++++++++++++
+::
+
+ void register_poly_array(poly_array_base &array);
+
+For advanced applications, you may choose to create your own **poly_array** objects to
+manage large chunks of infrequently-changed data, such a palettes. After each ``wait()``,
+**poly_manager** resets all the **poly_array** objects it knows about in order to reclaim all
+outstanding allocated memory. By registering your **poly_array** objects here, you can ensure
+that your arrays will also be reset after an ``wait()`` call.
+
+**Return value:** none.
+
+render_tile
++++++++++++
+::
+
+ template<int ParamCount>
+ uint32_t render_tile(rectangle const &cliprect, render_delegate callback,
+ vertex_t const &v1, vertex_t const &v2);
+
+This method enqueues a single *tile* primitive for rendering:
+
+* **ParamCount** is the number of live values in the iterated parameter array within each
+ **vertex_t** provided; it must be no greater than the **MaxParams** value specified in the
+ **poly_manager** template instantiation.
+
+* **cliprect** is a reference to a clipping rectangle. All pixels and parameter values are
+ clipped to stay within these bounds before being added to the work queues for rendering,
+ unless **POLY_FLAG_NO_CLIPPING** was specified as a flag parameter to **poly_manager**.
+
+* **callback** is the render callback delegate that will be called to render each extent.
+
+* **v1** contains the coordinates and iterated parameters for the top-left corner of the tile.
+
+* **v2** contains the coordinates and iterated parameters for the bottom-right corner of the tile.
+
+**Return value:** the total number of clipped pixels represented by the enqueued extents.
+
+render_triangle
++++++++++++++++
+::
+
+ template<int ParamCount>
+ uint32_t render_triangle(rectangle const &cliprect, render_delegate callback,
+ vertex_t const &v1, vertex_t const &v2, vertex_t const &v3);
+
+This method enqueues a single *triangle* primitive for rendering:
+
+* **ParamCount** is the number of live values in the iterated parameter array within each
+ **vertex_t** provided; it must be no greater than the **MaxParams** value specified in the
+ **poly_manager** template instantiation.
+
+* **cliprect** is a reference to a clipping rectangle. All pixels and parameter values are
+ clipped to stay within these bounds before being added to the work queues for rendering,
+ unless **POLY_FLAG_NO_CLIPPING** was specified as a flag parameter to **poly_manager**.
+
+* **callback** is the render callback delegate that will be called to render each extent.
+
+* **v1**, **v2**, **v3** contain the coordinates and iterated parameters for each vertex
+ of the triangle.
+
+**Return value:** the total number of clipped pixels represented by the enqueued extents.
+
+render_triangle_fan
++++++++++++++++++++
+::
+
+ template<int ParamCount>
+ uint32_t render_triangle_fan(rectangle const &cliprect, render_delegate callback,
+ int numverts, vertex_t const *v);
+
+This method enqueues one or more *triangle* primitives for rendering, specified in fan order:
+
+* **ParamCount** is the number of live values in the iterated parameter array within each
+ **vertex_t** provided; it must be no greater than the **MaxParams** value specified in the
+ **poly_manager** template instantiation.
+
+* **cliprect** is a reference to a clipping rectangle. All pixels and parameter values are
+ clipped to stay within these bounds before being added to the work queues for rendering,
+ unless **POLY_FLAG_NO_CLIPPING** was specified as a flag parameter to **poly_manager**.
+
+* **callback** is the render callback delegate that will be called to render each extent.
+
+* **numverts** is the total number of vertices provided; it must be at least 3.
+
+* **v** is a pointer to an array of **vertex_t** objects containing the coordinates and iterated
+ parameters for all the triangles, in fan order. This means that the first vertex is fixed.
+ So if 5 vertices are provided, indicating 3 triangles, the vertices used will be:
+ (0,1,2) (0,2,3) (0,3,4)
+
+**Return value:** the total number of clipped pixels represented by the enqueued extents.
+
+render_triangle_strip
++++++++++++++++++++++
+::
+
+ template<int ParamCount>
+ uint32_t render_triangle_strip(rectangle const &cliprect, render_delegate callback,
+ int numverts, vertex_t const *v);
+
+This method enqueues one or more *triangle* primitives for rendering, specified in strip order:
+
+* **ParamCount** is the number of live values in the iterated parameter array within each
+ **vertex_t** provided; it must be no greater than the **MaxParams** value specified in the
+ **poly_manager** template instantiation.
+
+* **cliprect** is a reference to a clipping rectangle. All pixels and parameter values are
+ clipped to stay within these bounds before being added to the work queues for rendering,
+ unless **POLY_FLAG_NO_CLIPPING** was specified as a flag parameter to **poly_manager**.
+
+* **callback** is the render callback delegate that will be called to render each extent.
+
+* **numverts** is the total number of vertices provided; it must be at least 3.
+
+* **v** is a pointer to an array of **vertex_t** objects containing the coordinates and iterated
+ parameters for all the triangles, in strip order.
+ So if 5 vertices are provided, indicating 3 triangles, the vertices used will be:
+ (0,1,2) (1,2,3) (2,3,4)
+
+**Return value:** the total number of clipped pixels represented by the enqueued extents.
+
+render_polygon
+++++++++++++++
+::
+
+ template<int NumVerts, int ParamCount>
+ uint32_t render_polygon(rectangle const &cliprect, render_delegate callback, vertex_t const *v);
+
+This method enqueues a single *polygon* primitive for rendering:
+
+* **NumVerts** is the number of vertices in the polygon.
+
+* **ParamCount** is the number of live values in the iterated parameter array within each
+ **vertex_t** provided; it must be no greater than the **MaxParams** value specified in the
+ **poly_manager** template instantiation.
+
+* **cliprect** is a reference to a clipping rectangle. All pixels and parameter values are
+ clipped to stay within these bounds before being added to the work queues for rendering,
+ unless **POLY_FLAG_NO_CLIPPING** was specified as a flag parameter to **poly_manager**.
+
+* **callback** is the render callback delegate that will be called to render each extent.
+
+* **v** is a pointer to an array of **vertex_t** objects containing the coordinates and iterated
+ parameters for the polygon. Vertices are assumed to be in either clockwise or anticlockwise
+ order.
+
+**Return value:** the total number of clipped pixels represented by the enqueued extents.
+
+render_extents
+++++++++++++++
+::
+
+ template<int ParamCount>
+ uint32_t render_extents(rectangle const &cliprect, render_delegate callback,
+ int startscanline, int numscanlines, extent_t const *extents);
+
+This method enqueues custom extents directly:
+
+* **ParamCount** is the number of live values in the iterated parameter array within each
+ **vertex_t** provided; it must be no greater than the **MaxParams** value specified in the
+ **poly_manager** template instantiation.
+
+* **cliprect** is a reference to a clipping rectangle. All pixels and parameter values are
+ clipped to stay within these bounds before being added to the work queues for rendering,
+ unless **POLY_FLAG_NO_CLIPPING** was specified as a flag parameter to **poly_manager**.
+
+* **callback** is the render callback delegate that will be called to render each extent.
+
+* **startscanline** is the Y coordinate of the first extent provided.
+
+* **numscanlines** is the number of extents provided.
+
+* **extents** is a pointer to an array of **extent_t** objects containing the start/stop
+ X coordinates and iterated parameters. The **userdata** field of the source extents is
+ copied to the target as well (this field is otherwise unused for all other types of
+ rendering).
+
+**Return value:** the total number of clipped pixels represented by the enqueued extents.
+
+zclip_if_less
++++++++++++++
+::
+
+ template<int ParamCount>
+ int zclip_if_less(int numverts, vertex_t const *v, vertex_t *outv, BaseType clipval);
+
+This method is a helper method to clip a polygon against a provided Z value. It assumes
+that the first iterated parameter in **vertex_t** represents the Z coordinate. If any edge
+crosses the Z plane represented by **clipval** that edge is clipped.
+
+* **ParamCount** is the number of live values in the iterated parameter array within each
+ **vertex_t** provided; it must be no greater than the **MaxParams** value specified in the
+ **poly_manager** template instantiation.
+
+* **numverts** is the number of vertices in the input array.
+
+* **v** is a pointer to the input array of **vertex_t** objects.
+
+* **outv** is a pointer to the output array of **vertex_t** objects. **v** and **outv**
+ cannot overlap or point to the same memory.
+
+* **clipval** is the value to compare parameter 0 against for clipping.
+
+**Return value:** the number of output vertices written to **outv**.
+Note that by design it is possible for this method to produce more vertices than the
+input array, so callers should ensure there is enough room in the output buffer to
+accommodate this.
+
+
+Example Renderer
+----------------
+
+Here is a complete example of how to create a software 3D renderer using **poly_manager**.
+Our example renderer will only handle flat and Gouraud-shaded triangles with depth (Z)
+buffering.
+
+
+Types
+~~~~~
+
+The first thing we need to define is our *externally-visible* vertex format, which is distinct
+from the internal **vertex_t** that **poly_manager** will define. In theory you could
+use **vertex_t** directly, but the generic nature of **poly_manager**’s iterated parameters
+make it awkward::
+
+ struct example_vertex
+ {
+ float x, y, z; // X,Y,Z coordinates
+ rgb_t color; // color at this vertex
+ };
+
+Next we define the **ObjectType** needed by **poly_manager**. For our simple case, we
+define an **example_object_data** struct that consists of pointers to our rendering buffers,
+plus a couple of fixed values that are consumed in some cases. More complex renderers would
+typically have many more object-wide parameters defined here::
+
+ struct example_object_data
+ {
+ bitmap_rgb32 *dest; // pointer to the rendering bitmap
+ bitmap_ind16 *depth; // pointer to the depth bitmap
+ rgb_t color; // overall color (for clearing and flat shaded case)
+ uint16_t depthval; // fixed depth v alue (for clearing)
+ };
+
+Now it’s time to define our renderer class, which we derive from **poly_manager**. As
+template parameters we specify ``float`` as the base type for our data, since that will
+be enough accuracy for this example, and we also provide our **example_object_data** as
+the **ObjectType** class, plus the maximum number of iterated parameters our renderer
+will ever need (4 in this case)::
+
+ class example_renderer : public poly_manager<float, example_object_data, 4>
+ {
+ public:
+ example_renderer(running_machine &machine, uint32_t width, uint32_t height);
+
+ bitmap_rgb32 *swap_buffers();
+
+ void clear_buffers(rgb_t color, uint16_t depthval);
+ void draw_triangle(example_vertex const *verts);
+
+ private:
+ static uint16_t ooz_to_depthval(float ooz);
+
+ void draw_triangle_flat(example_vertex const *verts);
+ void draw_triangle_gouraud(example_vertex const *verts);
+
+ void render_clear(int32_t y, extent_t const &extent, example_object_data const &object, int threadid);
+ void render_flat(int32_t y, extent_t const &extent, example_object_data const &object, int threadid);
+ void render_gouraud(int32_t y, extent_t const &extent, example_object_data const &object, int threadid);
+
+ int m_draw_buffer;
+ bitmap_rgb32 m_display[2];
+ bitmap_ind16 m_depth;
+ };
+
+
+Constructor
+~~~~~~~~~~~
+
+The constructor for our example renderer just initializes **poly_manager** and allocates
+the rendering and depth buffers::
+
+ example_renderer::example_renderer(running_machine &machine, uint32_t width, uint32_t height) :
+ poly_manager(machine),
+ m_draw_buffer(0)
+ {
+ // allocate two display buffers and a depth buffer
+ m_display[0].allocate(width, height);
+ m_display[1].allocate(width, height);
+ m_depth.allocate(width, height);
+ }
+
+
+swap_buffers
+~~~~~~~~~~~~
+
+The first interesting method in our renderer is ``swap_buffers()``, which returns a pointer to
+the buffer we’ve been drawing to, and sets up the other buffer as the new drawing target. The
+idea is that the display update handler will call this method to get ahold of the bitmap to
+display to the user::
+
+ bitmap_rgb32 *example_renderer::swap_buffers()
+ {
+ // wait for any rendering to complete before returning the buffer
+ wait("swap_buffers");
+
+ // return the current draw buffer and then switch to the other
+ // for future drawing
+ bitmap_rgb32 *result = &m_display[m_draw_buffer];
+ m_draw_buffer ^= 1;
+ return result;
+ }
+
+The most important thing here to note here is the call to **poly_manager**’s ``wait()``, which
+will block the current thread until all rendering is complete. This is important because
+otherwise the caller may receive a bitmap that is still being drawn to, leading to torn
+or corrupt visuals.
+
+
+clear_buffers
+~~~~~~~~~~~~~
+
+One of the most common operations to perform when doing 3D rendering is to initialize or
+clear the display and depth buffers to a known value. This method below leverages
+the *tile* primitive to render a rectangle over the screen by passing in (0,0) and (width,height)
+for the two vertices.
+
+Because the color and depth values to clear the buffer to are constant, they are stored in
+a freshly-allocated **example_object_data** object, along with a pointer to the buffers in
+question. The ``render_tile()`` call is made with a ``<0>`` suffix indicating that there are
+no iterated parameters to worry about::
+
+ void example_renderer::clear_buffers(rgb_t color, uint16_t depthval)
+ {
+ // allocate object data and populate it with information needed
+ example_object_data &object = object_data().next();
+ object.dest = &m_display[m_draw_buffer];
+ object.depth = &m_depth;
+ object.color = color;
+ object.depthval = depthval;
+
+ // top,left coordinate is always (0,0)
+ vertex_t topleft;
+ topleft.x = 0;
+ topleft.y = 0;
+
+ // bottom,right coordinate is (width,height)
+ vertex_t botright;
+ botright.x = m_display[0].width();
+ botright.y = m_display[0].height();
+
+ // render as a tile with 0 iterated parameters
+ render_tile<0>(m_display[0].cliprect(),
+ render_delegate(&example_renderer::render_clear, this),
+ topleft, botright);
+ }
+
+The render callback provided to ``render_tile()`` is also defined (privately) in our class,
+and handles a single span. Note how the rendering parameters are extracted from the
+**example_object_data** struct provided::
+
+ void example_renderer::render_clear(int32_t y, extent_t const &extent, example_object_data const &object, int threadid)
+ {
+ // get pointers to the start of the depth buffer and destination scanlines
+ uint16_t *depth = &object.depth->pix(y);
+ uint32_t *dest = &object.dest->pix(y);
+
+ // loop over the full extent and just store the constant values from the object
+ for (int x = extent.startx; x < extent.stopx; x++)
+ {
+ dest[x] = object.color;
+ depth[x] = object.depthval;
+ }
+ }
+
+Another important point to make is that the X coordinates provided by extent struct are
+inclusive of startx but exclusive of stopx. Clipping is performed ahead of time so that
+the render callback can focus on laying down pixels as quickly as possible with minimal
+overhead.
+
+
+draw_triangle
+~~~~~~~~~~~~~
+
+Next up, we have our actual triangle rendering function, which will draw a single triangle
+given an array of three vertices provided in the external **example_vertex** format::
+
+ void example_renderer::draw_triangle(example_vertex const *verts)
+ {
+ // flat shaded case
+ if (verts[0].color == verts[1].color && verts[0].color == verts[2].color)
+ draw_triangle_flat(verts);
+ else
+ draw_triangle_gouraud(verts);
+ }
+
+Because it is simpler and faster to render a flat shaded triangle, the code checks to see
+if the colors are the same on all three vertices. If they are, we call through to a special
+flat-shaded case, otherwise we process it as a full Gouraud-shaded triangle.
+
+This is a common technique to optimize rendering performance: identify special cases that
+reduce the per-pixel work, and route them to separate render callbacks that are optimized
+for that special case.
+
+
+draw_triangle_flat
+~~~~~~~~~~~~~~~~~~
+
+Here’s the setup code for rendering a flat-shaded triangle::
+
+ void example_renderer::draw_triangle_flat(example_vertex const *verts)
+ {
+ // allocate object data and populate it with information needed
+ example_object_data &object = object_data().next();
+ object.dest = &m_display[m_draw_buffer];
+ object.depth = &m_depth;
+
+ // in this case the color is constant and specified in the object data
+ object.color = verts[0].color;
+
+ // copy X, Y, and 1/Z into poly_manager vertices
+ vertex_t v[3];
+ for (int vertnum = 0; vertnum < 3; vertnum++)
+ {
+ v[vertnum].x = verts[vertnum].x;
+ v[vertnum].y = verts[vertnum].y;
+ v[vertnum].p[0] = 1.0f / verts[vertnum].z;
+ }
+
+ // render the triangle with 1 iterated parameter (1/Z)
+ render_triangle<1>(m_display[0].cliprect(),
+ render_delegate(&example_renderer::render_flat, this),
+ v[0], v[1], v[2]);
+ }
+
+First, we put the fixed color into the **example_object_data** directly, and then fill
+out three **vertex_t** objects with the X and Y coordinates in the usual spot, and 1/Z
+as our one and only iterated parameter. (We use 1/Z here because iterated parameters are
+interpolated linearly in screen space. Z is not linear in screen space, but 1/Z is due to
+perspective correction.)
+
+Our flat-shaded case then calls ``render_trangle`` specifying ``<1>`` iterated parameter to
+interpolate, and pointing to a special-case flat render callback::
+
+ void example_renderer::render_flat(int32_t y, extent_t const &extent, example_object_data const &object, int threadid)
+ {
+ // get pointers to the start of the depth buffer and destination scanlines
+ uint16_t *depth = &object.depth->pix(y);
+ uint32_t *dest = &object.dest->pix(y);
+
+ // get the starting 1/Z value and the delta per X
+ float ooz = extent.param[0].start;
+ float doozdx = extent.param[0].dpdx;
+
+ // iterate over the extent
+ for (int x = extent.startx; x < extent.stopx; x++)
+ {
+ // convert the 1/Z value into an integral depth value
+ uint16_t depthval = ooz_to_depthval(ooz);
+
+ // if closer than the current pixel, copy the color and depth value
+ if (depthval < depth[x])
+ {
+ dest[x] = object.color;
+ depth[x] = depthval;
+ }
+
+ // regardless, update the 1/Z value for the next pixel
+ ooz += doozdx;
+ }
+ }
+
+This render callback is a bit more involved than the clearing case.
+
+First, we have an iterated parameter (1/Z) to deal with, whose starting and X-delta
+values we extract from the extent before the start of the inner loop.
+
+Second, we perform depth buffer testing, using ``ooz_to_depthval()`` as a helper
+to transform the floating-point 1/Z value into a 16-bit integer. We compare this value against
+the current depth buffer value, and only store the pixel/depth value if it’s less.
+
+At the end of each iteration, we advance the 1/Z value by the X-delta in preparation for the
+next pixel.
+
+
+draw_triangle_gouraud
+~~~~~~~~~~~~~~~~~~~~~
+
+Finally we get to the code for the full-on Gouraud-shaded case::
+
+ void example_renderer::draw_triangle_gouraud(example_vertex const *verts)
+ {
+ // allocate object data and populate it with information needed
+ example_object_data &object = object_data().next();
+ object.dest = &m_display[m_draw_buffer];
+ object.depth = &m_depth;
+
+ // copy X, Y, 1/Z, and R,G,B into poly_manager vertices
+ vertex_t v[3];
+ for (int vertnum = 0; vertnum < 3; vertnum++)
+ {
+ v[vertnum].x = verts[vertnum].x;
+ v[vertnum].y = verts[vertnum].y;
+ v[vertnum].p[0] = 1.0f / verts[vertnum].z;
+ v[vertnum].p[1] = verts[vertnum].color.r();
+ v[vertnum].p[2] = verts[vertnum].color.g();
+ v[vertnum].p[3] = verts[vertnum].color.b();
+ }
+
+ // render the triangle with 4 iterated parameters (1/Z, R, G, B)
+ render_triangle<4>(m_display[0].cliprect(),
+ render_delegate(&example_renderer::render_gouraud, this),
+ v[0], v[1], v[2]);
+ }
+
+Here we have 4 iterated parameters: the 1/Z depth value, plus red, green, and blue,
+stored as floating point values. We call ``render_triangle()`` with ``<4>`` as the
+number of iterated parameters to process, and point to the full Gouraud render callback::
+
+ void example_renderer::render_gouraud(int32_t y, extent_t const &extent, example_object_data const &object, int threadid)
+ {
+ // get pointers to the start of the depth buffer and destination scanlines
+ uint16_t *depth = &object.depth->pix(y);
+ uint32_t *dest = &object.dest->pix(y);
+
+ // get the starting 1/Z value and the delta per X
+ float ooz = extent.param[0].start;
+ float doozdx = extent.param[0].dpdx;
+
+ // get the starting R,G,B values and the delta per X as 8.24 fixed-point values
+ uint32_t r = uint32_t(extent.param[1].start * float(1 << 24));
+ uint32_t drdx = uint32_t(extent.param[1].dpdx * float(1 << 24));
+ uint32_t g = uint32_t(extent.param[2].start * float(1 << 24));
+ uint32_t dgdx = uint32_t(extent.param[2].dpdx * float(1 << 24));
+ uint32_t b = uint32_t(extent.param[3].start * float(1 << 24));
+ uint32_t dbdx = uint32_t(extent.param[3].dpdx * float(1 << 24));
+
+ // iterate over the extent
+ for (int x = extent.startx; x < extent.stopx; x++)
+ {
+ // convert the 1/Z value into an integral depth value
+ uint16_t depthval = ooz_to_depthval(ooz);
+
+ // if closer than the current pixel, assemble the color
+ if (depthval < depth[x])
+ {
+ dest[x] = rgb_t(r >> 24, g >> 24, b >> 24);
+ depth[x] = depthval;
+ }
+
+ // regardless, update the 1/Z and R,G,B values for the next pixel
+ ooz += doozdx;
+ r += drdx;
+ g += dgdx;
+ b += dbdx;
+ }
+ }
+
+This follows the same pattern as the flat-shaded callback, except we have 4 iterated parameters
+to step through.
+
+Note that even though the iterated parameters are of ``float`` type, we convert the
+color values to fixed-point integers when iterating over them. This saves us doing 3
+float-to-int conversions each pixel. The original RGB values were 0-255, so interpolation
+can only produce values in the 0-255 range. Thus we can use 24 bits of a 32-bit integer as
+the fraction, which is plenty accurate for this case.
+
+
+Advanced Topic: the poly_array class
+------------------------------------
+
+**poly_array** is a template class that is used to manage a dynamically-sized vector of
+objects whose lifetime starts at allocation and ends when ``reset()`` is called. The
+**poly_manager** class uses several **poly_array** objects internally, including one for
+allocated **ObjectType** data, one for each primitive rendered, and one for holding all
+allocated extents.
+
+**poly_array** has an additional property where after a reset it retains a copy of the most
+recently allocated object. This ensures that callers can always call ``last()`` and get
+a valid object, even immediately after a reset.
+
+The **poly_array** class requires two template parameters::
+
+ template<class ArrayType, int TrackingCount>
+ class poly_array;
+
+These parameters are:
+
+* **ArrayType** is the type of object you wish to allocate and manage.
+
+* **TrackingCount** is the number of objects you wish to preserve after a reset. Typically
+ this value is either 0 (you don’t care to track any objects) or 1 (you only need one
+ object); however, if you are using **poly_array** to manage a shared collection of
+ objects across several independent consumers, it can be higher. See below for an example
+ where this might be handy.
+
+Note that objects allocated by **poly_array** are owned by **poly_array** and will be
+automatically freed upon exit.
+
+**poly_array** is optimized for use in high frequency multi-threaded systems. Therefore,
+one added feature of the class is that it rounds the allocation size of **ArrayType** to
+the nearest cache line boundary, on the assumption that neighboring entries could be
+accessed by different cores simultaneously. Keeping each **ArrayType** object in its
+own cache line ensures no false sharing performance impacts.
+
+Currently, **poly_array** has no mechanism to determine cache line size at runtime, so
+it presumes that 64 bytes is a typical cache line size, which is true for most x64 and ARM
+chips as of 2021. This value can be altered by changing the **CACHE_LINE_SHIFT** constant
+defined at the top of the class.
+
+Objects allocated by **poly_array** are created in 64k chunks. At construction time, one
+chunk’s worth of objects is allocated up front. The chunk size is controlled by the
+**CHUNK_GRANULARITY** constant defined at the top of the class.
+
+As more objects are allocated, if **poly_array** runs out of space, it will dynamically
+allocate more. This will produce discontiguous chunks of objects until the next ``reset()``
+call, at which point **poly_array** will reallocate all the objects into a contiguous
+vector once again.
+
+For the case where **poly_array** is used to manage a shared pool of objects, it can be
+configured to retain multiple most recently allocated items by using a **TrackingCount**
+greater than 1. For example, if **poly_array** is managing objects for two texture units,
+then it can set **TrackingCount** equal to 2, and pass the index of the texture unit in
+calls to ``next()`` and ``last()``. After a reset, **poly_array** will remember the most
+recently allocated object for each of the units independently.
+
+
+Methods
+~~~~~~~
+
+poly_array
+++++++++++
+::
+
+ poly_array();
+
+The **poly_array** constructor requires no parameters and simply pre-allocates one
+chunk of objects in preparation for future allocations.
+
+count
++++++
+::
+
+ u32 count() const;
+
+**Return value:** the number of objects currently allocated.
+
+max
++++
+::
+
+ u32 max() const;
+
+**Return value:** the maximum number of objects ever allocated at one time.
+
+itemsize
+++++++++
+::
+
+ size_t itemsize() const;
+
+**Return value:** the size of an object, rounded up to the nearest cache line boundary.
+
+allocated
++++++++++
+::
+
+ u32 allocated() const;
+
+**Return value:** the number of objects that fit within what’s currently been allocated.
+
+byindex
++++++++
+::
+
+ ArrayType &byindex(u32 index);
+
+Returns a reference to an object in the array by index. Equivalent to [**index**] on a
+normal array:
+
+* **index** is the index of the item you wish to reference.
+
+**Return value:** a reference to the object in question. Since a reference is returned,
+it is your responsibility to ensure that **index** is less than ``count()`` as there
+is no mechanism to return an invalid result.
+
+contiguous
+++++++++++
+::
+
+ ArrayType *contiguous(u32 index, u32 count, u32 &chunk);
+
+Returns a pointer to the base of a contiguous section of **count** items starting at
+**index**. Because **poly_array** dynamically resizes, it may not be possible to access
+all **count** objects contiguously, so the number of actually contiguous items is
+returned in **chunk**:
+
+* **index** is the index of the first item you wish to access contiguously.
+
+* **count** is the number of items you wish to access contiguously.
+
+* **chunk** is a reference to a variable that will be set to the actual number of
+ contiguous items available starting at **index**. If **chunk** is less than **count**,
+ then the caller should process the **chunk** items returned, then call ``countiguous()``
+ again at (**index** + **chunk**) to access the rest.
+
+**Return value:** a pointer to the first item in the contiguous chunk. No range checking
+is performed, so it is your responsibility to ensure that **index** + **count** is less
+than or equal to ``count()``.
+
+indexof
++++++++
+::
+
+ int indexof(ArrayType &item) const;
+
+Returns the index within the array of the given item:
+
+* **item** is a reference to an item in the array.
+
+**Return value:** the index of the item. It should always be the case that::
+
+ array.indexof(array.byindex(index)) == index
+
+reset
++++++
+::
+
+ void reset();
+
+Resets the **poly_array** by semantically deallocating all objects. If previous allocations
+created a discontiguous array, a fresh vector is allocated at this time so that future
+allocations up to the same level will remain contiguous.
+
+Note that the **ArrayType** destructor is *not* called on objects as they are deallocated.
+
+**Return value:** none.
+
+next
+++++
+::
+
+ ArrayType &next(int tracking_index = 0);
+
+Allocates a new object and returns a reference to it. If there is not enough space for
+a new object in the current array, a new discontiguous array is created to hold it:
+
+* **tracking_index** is the tracking index you wish to assign the new item to. In the
+ common case this is 0, but could be non-zero if using a **TrackingCount** greater than 1.
+
+**Return value:** a reference to the object. Note that the placement new operator is
+called on this object, so the default **ArrayType** constructor will be invoked here.
+
+last
+++++
+::
+
+ ArrayType &last(int tracking_index = 0) const;
+
+Returns a reference to the last object allocated:
+
+* **tracking_index** is the tracking index whose object you want. In the
+ common case this is 0, but could be non-zero if using a **TrackingCount** greater than 1.
+ **poly_array** remembers the most recently allocated object independently for each
+ **tracking_index**.
+
+**Return value:** a reference to the last allocated object.
diff --git a/docs/source/techspecs/uml_instructions.rst b/docs/source/techspecs/uml_instructions.rst
new file mode 100644
index 00000000000..5d379dd1c60
--- /dev/null
+++ b/docs/source/techspecs/uml_instructions.rst
@@ -0,0 +1,1582 @@
+.. _umlinst:
+
+UML Instruction Reference
+=========================
+
+.. contents::
+ :local:
+ :depth: 2
+
+
+.. _umlinst-intro:
+
+Introduction
+------------
+
+UML is the instruction set used by MAME’s recompiler framework.
+Front-ends translate code running on the guest CPUs to UML instructions,
+and back-ends convert the UML instructions to a form that can be
+executed or interpreted on the host system.
+
+
+.. _umlinst-flow:
+
+Flow control
+------------
+
+.. _umlinst-comment:
+
+COMMENT
+~~~~~~~
+
+Insert a comment into logged UML code.
+
++--------------------+---------------------------------+
+| Disassembly | Usage |
++====================+=================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| comment string | UML_COMMENT(block, string); |
++--------------------+---------------------------------+
+
+Operands
+^^^^^^^^
+
+string
+ The comment text as a pointer to a NUL-terminated string. This must
+ remain valid until code is generated for the block.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-nop:
+
+NOP
+~~~
+
+No operation.
+
++-----------------+---------------------+
+| Disassembly | Usage |
++=================+=====================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| nop | UML_NOP(block); |
++-----------------+---------------------+
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-label:
+
+LABEL
+~~~~~
+
+Associate a location with a label number local to the current generated
+code block. Label numbers must not be reused within a generated code
+block. The :ref:`JMP <umlinst-jmp>` instruction may be used to transfer
+control to the location associated with a label number.
+
++-------------------+------------------------------+
+| Disassembly | Usage |
++===================+==============================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| label label | UML_LABEL(block, label); |
++-------------------+------------------------------+
+
+Operands
+^^^^^^^^
+
+label (label number)
+ The label number to associate with the current location. A label
+ number must not be used more than once within a generated code
+ block.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-handle:
+
+HANDLE
+~~~~~~
+
+Mark a location as an entry point of a subroutine. Subroutines may be
+called using the :ref:`CALLH <umlinst-callh>` and :ref:`EXH
+<umlinst-exh>` instructions, and also by the `HASHJMP <umlinst-hashjmp>`
+if no location is associated with the specified mode and emulated
+program counter.
+
++--------------------+--------------------------------+
+| Disassembly | Usage |
++====================+================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| handle handle | UML_HANDLE(block, handle); |
++--------------------+--------------------------------+
+
+Operands
+^^^^^^^^
+
+handle (code handle)
+ The code handle to bind to the current location. The handle must
+ already be allocated, and must not have been bound since the last
+ generated code reset (all handles are implicitly unbound when
+ resetting the generated code cache).
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-hash:
+
+HASH
+~~~~
+
+Associate a location with the specified mode and emulated program
+counter values. The :ref:`HASHJMP <umlinst-hashjmp>` instruction may be
+used to transfer control to the location associated with a mode and
+emulated program counter value.
+
+This is usually used to mark the location of the generated code for an
+emulated instruction or sequence of instructions.
+
++---------------------+------------------------------+
+| Disassembly | Usage |
++=====================+==============================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| hash mode,pc | UML_HASH(block, mode, pc); |
++---------------------+------------------------------+
+
+Operands
+^^^^^^^^
+
+mode (32-bit – immediate, map variable)
+ The mode to associate with the current location in the generated
+ code. Must be greater than or equal to zero and less than the
+ number of modes specified when creating the recompiler context.
+pc (32-bit – immediate, map variable)
+ The emulated program counter value to associate with the current
+ location in the generated code.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-jmp:
+
+JMP
+~~~
+
+Jump to the location associated with a label number within the current
+block.
+
++------------------------+-----------------------------------+
+| Disassembly | Usage |
++========================+===================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| jmp label | UML_JMP(block, label); |
+| jmp label,cond | UML_JMPc(block, cond, label); |
++------------------------+-----------------------------------+
+
+Operands
+^^^^^^^^
+
+label (label number)
+ The label number associated with the location to jump to in the
+ current generated code block. The label number must be associated
+ with a location in the generated code block before the block is
+ finalised.
+cond (condition)
+ If supplied, a condition that must be met to jump to the specified
+ label. If the condition is not met, execution will continue with
+ the following instruction.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-callh:
+
+CALLH
+~~~~~
+
+Call the subroutine beginning at the specified code handle.
+
++-------------------------+--------------------------------------+
+| Disassembly | Usage |
++=========================+======================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| callh handle | UML_CALLH(block, handle); |
+| callh handle,cond | UML_CALLHc(block, handle, cond); |
++-------------------------+--------------------------------------+
+
+Operands
+^^^^^^^^
+
+handle (code handle)
+ Handle located at the entry point of the subroutine to call. The
+ handle must already be allocated but does not need to be bound until
+ the instruction is executed. Calling a handle that was unbound at
+ code generation time may produce less efficient code than calling a
+ handle that was already bound.
+cond (condition)
+ If supplied, a condition that must be met for the subroutine to be
+ called. If the condition is not met, the subroutine will not be
+ called.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-exh:
+
+EXH
+~~~
+
+Set the ``EXP`` register and call the subroutine beginning at the
+specified code handle. The ``EXP`` register is a 32-bit special
+function register that may be retrieved with the :ref:`GETEXP
+<umlinst-getexp>` instruction.
+
++-----------------------------+-----------------------------------------+
+| Disassembly | Usage |
++=============================+=========================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| exh handle,arg | UML_EXH(block, handle, arg); |
+| exh handle,arg,cond | UML_EXHc(block, handle, arg, cond); |
++-----------------------------+-----------------------------------------+
+
+Operands
+^^^^^^^^
+
+handle (code handle)
+ Handle located at the entry point of the subroutine to call. The
+ handle must already be allocated but does not need to be bound until
+ the instruction is executed. Calling a handle that was unbound at
+ code generation time may produce less efficient code than calling a
+ handle that was already bound.
+arg (32-bit – memory, integer register, immediate, map variable)
+ Value to store in the ``EXP`` register.
+cond (condition)
+ If supplied, a condition that must be met for the subroutine to be
+ called. If the condition is not met, the subroutine will not be
+ called and the ``EXP`` register will not be modified.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``arg`` operand are truncated to 32 bits.
+
+.. _umlinst-ret:
+
+RET
+~~~
+
+Return from a subroutine, transferring control to the instruction
+following the :ref:`CALLH <umlinst-callh>` or :ref:`EXH <umlinst-exh>`
+instruction used to call the subroutine. This instruction must only be
+used within generated code subroutines. The :ref:`EXIT <umlinst-exit>`
+instruction must be used to exit from the generated code.
+
++------------------+----------------------------+
+| Disassembly | Usage |
++==================+============================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| ret | UML_RET(block); |
+| ret cond | UML_RETc(block, cond); |
++------------------+----------------------------+
+
+Operands
+^^^^^^^^
+
+cond (condition)
+ If supplied, a condition that must be met to return from the
+ subroutine. If the condition is not met, execution will continue
+ with the following instruction.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-hashjmp:
+
+HASHJMP
+~~~~~~~
+
+Unwind all nested generated code subroutine frames and transfer control
+to the location associated with the specified mode and emulated program
+counter values. If no location is associated with the specified mode
+and program counter values, call the subroutine beginning at the
+specified code handle. Note that all nested generated code subroutine
+frames are unwound in either case.
+
+This is usually used to jump to the generated code corresponding to the
+emulated code at a particular address when it is not known to be in the
+current generated code block or when the mode changes.
+
++----------------------------+-----------------------------------------+
+| Disassembly | Usage |
++============================+=========================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| hashjmp mode,pc,handle | UML_HASHJMP(block, mode, pc, handle); |
++----------------------------+-----------------------------------------+
+
+Operands
+^^^^^^^^
+
+mode (32-bit – memory, integer register, immediate, map variable)
+ The mode associated with the location in the generated code to
+ transfer control to. Must be greater than or equal to zero and less
+ than the number of modes specified when creating the recompiler
+ context.
+pc (32-bit – memory, integer register, immediate, map variable)
+ The emulated program counter value associated with the location in
+ the generated code to transfer control to.
+handle (code handle)
+ Handle located at the entry point of the subroutine to call if no
+ location in the generated code is associated with the specified mode
+ and emulated program counter values. The handle must already be
+ allocated but does not need to be bound until the instruction is
+ executed. Calling a handle that was unbound at code generation time
+ may produce less efficient code than calling a handle that was
+ already bound.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+.. _umlinst-exit:
+
+EXIT
+~~~~
+
+Exit from the generated code, returning control to the caller. May be
+used from within any level of nested subroutine calls in the generated
+code.
+
++-----------------------+----------------------------------+
+| Disassembly | Usage |
++=======================+==================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| exit arg, | UML_EXIT(block, arg); |
+| exit arg,,cond | UML_EXITc(block, arg, cond); |
++-----------------------+----------------------------------+
+
+Operands
+^^^^^^^^
+
+arg (32-bit – memory, integer register, immediate, map variable)
+ The value to return to the caller.
+cond (condition)
+ If supplied, a condition that must be met to exit from the generated
+ code. If the condition is not met, execution will continue with the
+ following instruction.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``arg`` operand are truncated to 32 bits.
+
+.. _umlinst-callc:
+
+CALLC
+~~~~~
+
+Call a C function with the signature ``void (*)(void *)``.
+
++---------------------------+-----------------------------------------+
+| Disassembly | Usage |
++===========================+=========================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| callc func,arg | UML_CALLC(block, func, arg); |
+| callc func,arg,cond | UML_CALLCc(block, func, arg, cond); |
++---------------------------+-----------------------------------------+
+
+Operands
+^^^^^^^^
+
+func (C function)
+ Function pointer to the function to call.
+arg (memory)
+ Argument to pass to the function.
+cond (condition)
+ If supplied, a condition that must be met for the function to be
+ called. If the condition is not met, the function will not be
+ called.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+.. _umlinst-debug:
+
+DEBUG
+~~~~~
+
+Call the debugger instruction hook function if appropriate.
+
+If the debugger is active, this should be executed before each emulated
+instruction. Any emulated CPU state kept in UML registers should be
+flushed to memory before executing this instruction and reloaded
+afterwards to ensure the debugger can display and modify values
+correctly.
+
++-----------------+---------------------------+
+| Disassembly | Usage |
++=================+===========================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| debug pc | UML_DEBUG(block, pc); |
++-----------------+---------------------------+
+
+Operands
+^^^^^^^^
+
+pc (32-bit – memory, integer register, immediate, map variable)
+ The emulated program counter value to supply to the debugger
+ instruction hook function.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``pc`` operand are truncated to 32 bits.
+
+.. _umlinst-break:
+
+BREAK
+~~~~~
+
+Break into the host debugger if attached. Has no effect or crashes if
+no host debugger is attached depending on the host system and
+configuration. This is intended as a developer aid and should not be
+left in final code.
+
++-----------------+-----------------------+
+| Disassembly | Usage |
++=================+=======================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| break | UML_BREAK(block); |
++-----------------+-----------------------+
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+
+.. _umlinst-datamove:
+
+Data movement
+-------------
+
+.. _umlinst-mov:
+
+MOV
+~~~
+
+Copy an integer value.
+
++--------------------------+---------------------------------------+
+| Disassembly | Usage |
++==========================+=======================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| mov dst,src | UML_MOV(block, dst, src); |
+| mov dst,src,cond | UML_MOVc(block, cond, dst, src); |
+| dmov dst,src | UML_DMOV(block, dst, src); |
+| dmov dst,src,cond | UML_DMOVc(block, cond, dst, src); |
++--------------------------+---------------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, integer register)
+ The destination where the value will be copied to.
+src (32-bit or 64-bit – memory, integer register, immediate, map variable)
+ The source value to copy.
+cond (condition)
+ If supplied, a condition that must be met to copy the value. If the
+ condition is not met, the instruction will have no effect.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``src`` operand are truncated to the
+ instruction size.
+* Converted to :ref:`NOP <umlinst-nop>` if the ``src`` and ``dst``
+ operands refer to the same memory location or register and the
+ instruction size is no larger than the destination size.
+
+.. _umlinst-fmov:
+
+FMOV
+~~~~
+
+Copy a floating point value. The binary value will be preserved even if
+it is not a valid representation of a floating point number.
+
++--------------------------+----------------------------------------+
+| Disassembly | Usage |
++==========================+========================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| fsmov dst,src | UML_FSMOV(block, dst, src); |
+| fsmov dst,src,cond | UML_FSMOVc(block, cond, dst, src); |
+| fdmov dst,src | UML_FDMOV(block, dst, src); |
+| fdmov dst,src,cond | UML_FDMOVc(block, cond, dst, src); |
++--------------------------+----------------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, floating point register)
+ The destination where the value will be copied to.
+src (32-bit or 64-bit – memory, floating point register)
+ The source value to copy.
+cond (condition)
+ If supplied, a condition that must be met to copy the value. If the
+ condition is not met, the instruction will have no effect.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Converted to :ref:`NOP <umlinst-nop>` if the ``src`` and ``dst``
+ operands refer to the same memory location or register.
+
+.. _umlinst-fcopyi:
+
+FCOPYI
+~~~~~~
+
+Reinterpret an integer value as a floating point value. The binary
+value will be preserved even if it is not a valid representation of a
+floating point number.
+
++---------------------+-----------------------------------+
+| Disassembly | Usage |
++=====================+===================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| fscopyi dst,src | UML_FSCOPYI(block, dst, src); |
+| fdcopyi dst,src | UML_FDCOPYI(block, dst, src); |
++---------------------+-----------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, floating point register)
+ The destination where the value will be copied to.
+src (32-bit or 64-bit – memory, integer register)
+ The source value to copy.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-icopyf:
+
+ICOPYF
+~~~~~~
+
+Reinterpret a floating point value as an integer value. The binary
+value will be preserved even if it is not a valid representation of a
+floating point number.
+
++---------------------+-----------------------------------+
+| Disassembly | Usage |
++=====================+===================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| icopyfs dst,src | UML_ICOPYFS(block, dst, src); |
+| icopyfd dst,src | UML_ICOPYFD(block, dst, src); |
++---------------------+-----------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, integer register)
+ The destination where the value will be copied to.
+src (32-bit or 64-bit – memory, floating point register)
+ The source value to copy.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-load:
+
+LOAD
+~~~~
+
+Load an unsigned integer value from a memory location with variable
+displacement. The value is zero-extended to the size of the
+destination. Host system rules for integer alignment must be followed.
+
++---------------------------------------+------------------------------------------------------+
+| Disassembly | Usage |
++=======================================+======================================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| load dst,base,index,size_scale | UML_LOAD(block, dst, base, index, size, scale); |
+| dload dst,base,index,size_scale | UML_DLOAD(block, dst, base, index, size, scale); |
++---------------------------------------+------------------------------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, integer register)
+ The destination where the value read from memory will be stored.
+base (memory)
+ The base address of the area of memory to read from.
+index (32-bit – memory, integer register, immediate, map variable)
+ The displacement value added to the base address to calculate the
+ address to read from. This value may be scaled by a factor of 1, 2,
+ 4 or 8 depending on the ``scale`` operand. Note that this is always
+ a 32-bit operand interpreted as a signed integer, irrespective of
+ the instruction size.
+size (access size)
+ The size of the value to read. Must be ``SIZE_BYTE`` (8-bit),
+ ``SIZE_WORD`` (16-bit), ``SIZE_DWORD`` (32-bit) or ``SIZE_QWORD``
+ (64-bit). Note that this operand controls the size of the value
+ read from memory while the instruction size sets the size of the
+ ``dst`` operand.
+scale (index scale)
+ The scale factor to apply to the ``index`` operand. Must be
+ ``SCALE_x1``, ``SCALE_x2``, ``SCALE_x4`` or ``SCALE_x8`` to multiply
+ by 1, 2, 4 or 8, respectively (shift left by 0, 1, 2 or 3 bits).
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-loads:
+
+LOADS
+~~~~~
+
+Load a signed integer value from a memory location with variable
+displacement. The value is sign-extended to the size of the
+destination. Host system rules for integer alignment must be followed.
+
++---------------------------------------+-------------------------------------------------------+
+| Disassembly | Usage |
++=======================================+=======================================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| loads dst,base,index,size_scale | UML_LOADS(block, dst, base, index, size, scale); |
+| dloads dst,base,index,size_scale | UML_DLOADS(block, dst, base, index, size, scale); |
++---------------------------------------+-------------------------------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, integer register)
+ The destination where the value read from memory will be stored.
+base (memory)
+ The base address of the area of memory to read from.
+index (32-bit – memory, integer register, immediate, map variable)
+ The displacement value added to the base address to calculate the
+ address to read from. This value may be scaled by a factor of 1, 2,
+ 4 or 8 depending on the ``scale`` operand. Note that this is always
+ a 32-bit operand interpreted as a signed integer, irrespective of
+ the instruction size.
+size (access size)
+ The size of the value to read. Must be ``SIZE_BYTE`` (8-bit),
+ ``SIZE_WORD`` (16-bit), ``SIZE_DWORD`` (32-bit) or ``SIZE_QWORD``
+ (64-bit). Note that this operand controls the size of the value
+ read from memory while the instruction size sets the size of the
+ ``dst`` operand.
+scale (index scale)
+ The scale factor to apply to the ``index`` operand. Must be
+ ``SCALE_x1``, ``SCALE_x2``, ``SCALE_x4`` or ``SCALE_x8`` to multiply
+ by 1, 2, 4 or 8, respectively (shift left by 0, 1, 2 or 3 bits).
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-store:
+
+STORE
+~~~~~
+
+Store an integer value to a location in memory with variable
+displacement. Host system rules for integer alignment must be followed.
+
++---------------------------------------+-------------------------------------------------------+
+| Disassembly | Usage |
++=======================================+=======================================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| store base,index,src,size_scale | UML_STORE(block, base, index, src, size, scale); |
+| dstore base,index,src,size_scale | UML_DSTORE(block, base, index, src, size, scale); |
++---------------------------------------+-------------------------------------------------------+
+
+Operands
+^^^^^^^^
+
+base (memory)
+ The base address of the area of memory to write to.
+index (32-bit – memory, integer register, immediate, map variable)
+ The displacement value added to the base address to calculate the
+ address to write to. This value may be scaled by a factor of 1, 2,
+ 4 or 8 depending on the ``scale`` operand. Note that this is always
+ a 32-bit operand interpreted as a signed integer, irrespective of
+ the instruction size.
+src (32-bit or 64-bit – memory, integer register, immediate, map variable)
+ The value to write to memory.
+size (access size)
+ The size of the value to write. Must be ``SIZE_BYTE`` (8-bit),
+ ``SIZE_WORD`` (16-bit), ``SIZE_DWORD`` (32-bit) or ``SIZE_QWORD``
+ (64-bit). Note that this operand controls the size of the value
+ written to memory while the instruction size sets the size of the
+ ``src`` operand.
+scale (index scale)
+ The scale factor to apply to the ``index`` operand. Must be
+ ``SCALE_x1``, ``SCALE_x2``, ``SCALE_x4`` or ``SCALE_x8`` to multiply
+ by 1, 2, 4 or 8, respectively (shift left by 0, 1, 2 or 3 bits).
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-fload:
+
+FLOAD
+~~~~~
+
+Load a floating point value from a memory location with variable
+displacement. The binary value will be preserved even if it is not a
+valid representation of a floating point number. Host system rules for
+memory access alignment must be followed.
+
++----------------------------+------------------------------------------+
+| Disassembly | Usage |
++============================+==========================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| fsload dst,base,index | UML_FSLOAD(block, dst, base, index); |
+| fdload dst,base,index | UML_FDLOAD(block, dst, base, index); |
++----------------------------+------------------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, floating point register)
+ The destination where the value read from memory will be stored.
+base (memory)
+ The base address of the area of memory to read from.
+index (32-bit – memory, integer register, immediate, map variable)
+ The displacement value added to the base address to calculate the
+ address to read from. This value will be scaled by the instruction
+ size (multiplied by 4 or 8). Note that this is always a 32-bit
+ operand interpreted as a signed integer, irrespective of the
+ instruction size.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-fstore:
+
+FSTORE
+~~~~~~
+
+Store a floating point value to a memory location with variable
+displacement. The binary value will be preserved even if it is not a
+valid representation of a floating point number. Host system rules for
+memory access alignment must be followed.
+
++----------------------------+-------------------------------------------+
+| Disassembly | Usage |
++============================+===========================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| fsstore base,index,src | UML_FSSTORE(block, base, index, src); |
+| fdstore base,index,src | UML_FDSTORE(block, base, index, src); |
++----------------------------+-------------------------------------------+
+
+Operands
+^^^^^^^^
+
+base (memory)
+ The base address of the area of memory to write to.
+index (32-bit – memory, integer register, immediate, map variable)
+ The displacement value added to the base address to calculate the
+ address to write to. This value will be scaled by the instruction
+ size (multiplied by 4 or 8). Note that this is always a 32-bit
+ operand interpreted as a signed integer, irrespective of the
+ instruction size.
+src (32-bit or 64-bit – memory, floating point register)
+ The value to write to memory.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-getexp:
+
+GETEXP
+~~~~~~
+
+Copy the value of the ``EXP`` register. The ``EXP`` register can be set
+using the :ref:`EXH <umlinst-exh>` instruction.
+
++-----------------+-----------------------------+
+| Disassembly | Usage |
++=================+=============================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| getexp dst | UML_GETEXP(block, dst); |
++-----------------+-----------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit – memory, integer register)
+ The destination to copy the value of the ``EXP`` register to. Note
+ that the ``EXP`` register can only hold a 32-bit value.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-mapvar:
+
+MAPVAR
+~~~~~~
+
+Set the value of a map variable starting at the current location in the
+current generated code block.
+
++--------------------------+---------------------------------------+
+| Disassembly | Usage |
++==========================+=======================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| mapvar mapvar,value | UML_MAPVAR(block, mapvar, value); |
++--------------------------+---------------------------------------+
+
+Operands
+^^^^^^^^
+
+mapvar (map variable)
+ The map variable to set the value of.
+value (32-bit – immediate, map variable)
+ The value to set the map variable to. Note that map variables can
+ only hold 32-bit values.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Unchanged. |
++---------------+------------+
+| overflow (V) | Unchanged. |
++---------------+------------+
+| zero (Z) | Unchanged. |
++---------------+------------+
+| sign (S) | Unchanged. |
++---------------+------------+
+| unordered (U) | Unchanged. |
++---------------+------------+
+
+.. _umlinst-recover:
+
+RECOVER
+~~~~~~~
+
+Retrieve the value of a map variable at the location of the call
+instruction in the outermost generated code frame. This instruction
+should only be used from within a generated code subroutine. Results
+are undefined if this instruction is executed from outside any
+generated code subroutines.
+
++------------------------+--------------------------------------+
+| Disassembly | Usage |
++========================+======================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| recover dst,mapvar | UML_RECOVER(block, dst, mapvar); |
++------------------------+--------------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit – memory, integer register)
+ The destination to copy the value of the map variable to. Note that
+ map variables can only hold 32-bit values.
+mapvar (map variable)
+ The map variable to retrieve the value of from the outermost
+ generated code frame.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+
+.. _umlinst-memaccess:
+
+Emulated memory access
+----------------------
+
+.. _umlinst-read:
+
+READ
+~~~~
+
+Read from an emulated address space. The access mask is implied to have
+all bits set.
+
++---------------------------------+-----------------------------------------------+
+| Disassembly | Usage |
++=================================+===============================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| read dst,addr,space_size | UML_READ(block, dst, addr, size, space); |
+| dread dst,addr,space_size | UML_DREAD(block, dst, addr, size, space); |
++---------------------------------+-----------------------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, integer register)
+ The destination where the value read from the emulated address space
+ will be stored.
+addr (32-bit – memory, integer register, immediate, map variable)
+ The address to read from in the emulated address space. Note that
+ this is always a 32-bit operand, irrespective of the instruction
+ size.
+size (access size)
+ The size of the emulated memory access. Must be ``SIZE_BYTE``
+ (8-bit), ``SIZE_WORD`` (16-bit), ``SIZE_DWORD`` (32-bit) or
+ ``SIZE_QWORD`` (64-bit). Note that this operand controls the size
+ of the emulated memory access while the instruction size sets the
+ size of the ``dst`` operand.
+space (address space number)
+ An integer identifying the address space to read from. May be
+ ``SPACE_PROGRAM``, ``SPACE_DATA``, ``SPACE_IO`` or ``SPACE_OPCODES``
+ for one of the common CPU address spaces, or a non-negative integer
+ cast to ``memory_space``.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``addr`` operand are truncated to 32 bits.
+
+.. _umlinst-readm:
+
+READM
+~~~~~
+
+Read from an emulated address space with access mask specified.
+
++--------------------------------------+------------------------------------------------------+
+| Disassembly | Usage |
++======================================+======================================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| readm dst,addr,mask,space_size | UML_READM(block, dst, addr, mask, size, space); |
+| dreadm dst,addr,mask,space_size | UML_DREADM(block, dst, addr, mask, size, space); |
++--------------------------------------+------------------------------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, integer register)
+ The destination where the value read from the emulated address space
+ will be stored.
+addr (32-bit – memory, integer register, immediate, map variable)
+ The address to read from in the emulated address space. Note that
+ this is always a 32-bit operand, irrespective of the instruction
+ size.
+mask (32-bit or 64-bit – memory, integer register, immediate, map variable)
+ The access mask for the emulated memory access.
+size (access size)
+ The size of the emulated memory access. Must be ``SIZE_BYTE``
+ (8-bit), ``SIZE_WORD`` (16-bit), ``SIZE_DWORD`` (32-bit) or
+ ``SIZE_QWORD`` (64-bit). Note that this operand controls the size
+ of the emulated memory access while the instruction size sets the
+ size of the ``dst`` and ``mask`` operands.
+space (address space number)
+ An integer identifying the address space to read from. May be
+ ``SPACE_PROGRAM``, ``SPACE_DATA``, ``SPACE_IO`` or ``SPACE_OPCODES``
+ for one of the common CPU address spaces, or a non-negative integer
+ cast to ``memory_space``.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``addr`` operand are truncated to 32 bits.
+* Immediate values for the ``mask`` operand are truncated to the access
+ size.
+* Converted to :ref:`READ <umlinst-read>` if the ``mask`` operand is an
+ immediate value with all bits set.
+
+.. _umlinst-write:
+
+WRITE
+~~~~~
+
+Write to an emulated address space. The access mask is implied to have
+all bits set.
+
++---------------------------------+------------------------------------------------+
+| Disassembly | Usage |
++=================================+================================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| write addr,src,space_size | UML_WRITE(block, addr, src, size, space); |
+| dwrite addr,src,space_size | UML_DWRITE(block, addr, src, size, space); |
++---------------------------------+------------------------------------------------+
+
+Operands
+^^^^^^^^
+
+addr (32-bit – memory, integer register, immediate, map variable)
+ The address to write to in the emulated address space. Note that
+ this is always a 32-bit operand, irrespective of the instruction
+ size.
+src (32-bit or 64-bit – memory, integer register, immediate, map variable)
+ The value to write to the emulated address space.
+size (access size)
+ The size of the emulated memory access. Must be ``SIZE_BYTE``
+ (8-bit), ``SIZE_WORD`` (16-bit), ``SIZE_DWORD`` (32-bit) or
+ ``SIZE_QWORD`` (64-bit). Note that this operand controls the size
+ of the emulated memory access while the instruction size sets the
+ size of the ``src`` operand.
+space (address space number)
+ An integer identifying the address space to read from. May be
+ ``SPACE_PROGRAM``, ``SPACE_DATA``, ``SPACE_IO`` or ``SPACE_OPCODES``
+ for one of the common CPU address spaces, or a non-negative integer
+ cast to ``memory_space``.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``addr`` operand are truncated to 32 bits.
+* Immediate values for the ``src`` operand are truncated to the access
+ size.
+
+.. _umlinst-writem:
+
+WRITEM
+~~~~~~
+
+Write to an emulated address space with access mask specified.
+
++--------------------------------------+-------------------------------------------------------+
+| Disassembly | Usage |
++======================================+=======================================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| writem addr,src,mask,space_size | UML_WRITEM(block, addr, src, mask, size, space); |
+| dwritem addr,src,mask,space_size | UML_DWRITEM(block, addr, src, mask, size, space); |
++--------------------------------------+-------------------------------------------------------+
+
+Operands
+^^^^^^^^
+
+addr (32-bit – memory, integer register, immediate, map variable)
+ The address to write to in the emulated address space. Note that
+ this is always a 32-bit operand, irrespective of the instruction
+ size.
+src (32-bit or 64-bit – memory, integer register, immediate, map variable)
+ The value to write to the emulated address space.
+mask (32-bit or 64-bit – memory, integer register, immediate, map variable)
+ The access mask for the emulated memory access.
+size (access size)
+ The size of the emulated memory access. Must be ``SIZE_BYTE``
+ (8-bit), ``SIZE_WORD`` (16-bit), ``SIZE_DWORD`` (32-bit) or
+ ``SIZE_QWORD`` (64-bit). Note that this operand controls the size
+ of the emulated memory access while the instruction size sets the
+ size of the ``src`` and ``mask`` operands.
+space (address space number)
+ An integer identifying the address space to read from. May be
+ ``SPACE_PROGRAM``, ``SPACE_DATA``, ``SPACE_IO`` or ``SPACE_OPCODES``
+ for one of the common CPU address spaces, or a non-negative integer
+ cast to ``memory_space``.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``addr`` operand are truncated to 32 bits.
+* Immediate values for the ``src`` and ``mask`` operands are truncated
+ to the access size.
+* Converted to :ref:`WRITE <umlinst-read>` if the ``mask`` operand is an
+ immediate value with all bits set.
+
+.. _umlinst-fread:
+
+FREAD
+~~~~~
+
+Read a floating point value from an emulated address space. The binary
+value will be preserved even if it is not a valid representation of a
+floating point number. The access mask is implied to have all bits set.
+
++---------------------------------+------------------------------------------+
+| Disassembly | Usage |
++=================================+==========================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| fsread dst,addr,space_size | UML_FSREAD(block, dst, addr, space); |
+| fdread dst,addr,space_size | UML_FDREAD(block, dst, addr, space); |
++---------------------------------+------------------------------------------+
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, floating point register)
+ The destination where the value read from the emulated address space
+ will be stored.
+addr (32-bit – memory, integer register, immediate, map variable)
+ The address to read from in the emulated address space. Note that
+ this is always a 32-bit operand, irrespective of the instruction
+ size.
+space (address space number)
+ An integer identifying the address space to read from. May be
+ ``SPACE_PROGRAM``, ``SPACE_DATA``, ``SPACE_IO`` or ``SPACE_OPCODES``
+ for one of the common CPU address spaces, or a non-negative integer
+ cast to ``memory_space``.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``addr`` operand are truncated to 32 bits.
+
+.. _umlinst-fwrite:
+
+FWRITE
+~~~~~~
+
+Write a floating point value to an emulated address space. The binary
+value will be preserved even if it is not a valid representation of a
+floating point number. The access mask is implied to have all bits set.
+
++---------------------------------+-------------------------------------------+
+| Disassembly | Usage |
++=================================+===========================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| fswrite addr,src,space_size | UML_FSWRITE(block, addr, src, space); |
+| fdwrite addr,src,space_size | UML_FDWRITE(block, addr, src, space); |
++---------------------------------+-------------------------------------------+
+
+Operands
+^^^^^^^^
+
+addr (32-bit – memory, integer register, immediate, map variable)
+ The address to write to in the emulated address space. Note that
+ this is always a 32-bit operand, irrespective of the instruction
+ size.
+src (32-bit or 64-bit – memory, floating point register)
+ The value to write to the emulated address space.
+ will be stored.
+space (address space number)
+ An integer identifying the address space to read from. May be
+ ``SPACE_PROGRAM``, ``SPACE_DATA``, ``SPACE_IO`` or ``SPACE_OPCODES``
+ for one of the common CPU address spaces, or a non-negative integer
+ cast to ``memory_space``.
+
+Flags
+^^^^^
+
++---------------+------------+
+| carry (C) | Undefined. |
++---------------+------------+
+| overflow (V) | Undefined. |
++---------------+------------+
+| zero (Z) | Undefined. |
++---------------+------------+
+| sign (S) | Undefined. |
++---------------+------------+
+| unordered (U) | Undefined. |
++---------------+------------+
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Immediate values for the ``addr`` operand are truncated to 32 bits.