diff options
Diffstat (limited to 'docs/source/techspecs')
-rw-r--r-- | docs/source/techspecs/device_memory_interface.rst | 171 | ||||
-rw-r--r-- | docs/source/techspecs/device_rom_interface.rst | 112 | ||||
-rw-r--r-- | docs/source/techspecs/index.rst | 7 | ||||
-rw-r--r-- | docs/source/techspecs/layout_files.rst | 314 | ||||
-rw-r--r-- | docs/source/techspecs/layout_script.rst | 203 | ||||
-rw-r--r-- | docs/source/techspecs/luaengine.rst | 104 | ||||
-rw-r--r-- | docs/source/techspecs/memory.rst | 763 | ||||
-rw-r--r-- | docs/source/techspecs/object_finders.rst | 1039 |
8 files changed, 2159 insertions, 554 deletions
diff --git a/docs/source/techspecs/device_memory_interface.rst b/docs/source/techspecs/device_memory_interface.rst index 4efa6a75234..2eae46f3906 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,136 @@ 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); 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 (``TRANSLATE_(READ\|WRITE\|FETCH)(\|_USER\|_DEBUG)``) +and address is an in/out parameter holding the address to translate on +entry and the translated version on return. Should return ``true`` if +the translation went correctly, or ``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. diff --git a/docs/source/techspecs/device_rom_interface.rst b/docs/source/techspecs/device_rom_interface.rst index 366fa763233..4125bc9536a 100644 --- a/docs/source/techspecs/device_rom_interface.rst +++ b/docs/source/techspecs/device_rom_interface.rst @@ -1,89 +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<AddrWidth, DataWidth=0, AddrShift=0, Endian=ENDIANNESS_LITTLE> +.. code-block:: C++ + + device_rom_interface<AddrWidth, DataWidth=0, AddrShift=0, Endian=ENDIANNESS_LITTLE> -The interface is a template that takes the address bus width of the +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 not 0) and endianness (if not little endian +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 **override_address_width**\ (u8 width) +.. code-block:: C++ -This method allows to override the address bus width. It must be + void override_address_width(u8 width); + +This method allows the address bus width to be overridden. It must be called from within the device before **config_complete** time. -| void **set_rom**\ (const void \*base, u32 size); +.. code-block:: C++ + + void set_rom(const void *base, u32 size); -At any time post- **interface_pre_start**, a memory block can be -setup as the connected rom with that method. It overrides any +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/index.rst b/docs/source/techspecs/index.rst index 0d7ddc8ce41..0435dd486e8 100644 --- a/docs/source/techspecs/index.rst +++ b/docs/source/techspecs/index.rst @@ -1,13 +1,16 @@ 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: naming layout_files + layout_script + object_finders device_memory_interface device_rom_interface device_disasm_interface diff --git a/docs/source/techspecs/layout_files.rst b/docs/source/techspecs/layout_files.rst index a3de7d66390..0a8d39c4bba 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,7 +55,7 @@ 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 ~~~~~~~~~~~ @@ -82,14 +84,18 @@ 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:: +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" /> It’s possible to use different schemes in the horizontal and vertical -directions. For example, these equivalent ``bounds`` elements are also valid:: +directions. For example, these equivalent ``bounds`` elements are also valid: + +.. code-block:: XML <bounds x="455" top="120" width="12" bottom="128" /> <bounds left="455" yc="124" right="467" height="8" /> @@ -99,7 +105,7 @@ It is an error if ``width`` or ``height`` are negative, if ``right`` is less than ``left``, or if ``bottom`` is less than ``top``. -.. _layout-concepts-colours: +.. _layfile-concepts-colours: Colours ~~~~~~~ @@ -113,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" /> @@ -122,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 ~~~~~~~~~~ @@ -131,7 +139,9 @@ 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~``:: +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" /> @@ -166,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" /> @@ -189,25 +203,24 @@ 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 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`` @@ -219,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 ~~~~~~~~~~~~~~~~~~~~~~ @@ -329,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 ----------------- @@ -342,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"> @@ -355,26 +370,27 @@ 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. + 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 + ``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 ~~~~~~~~ @@ -387,7 +403,7 @@ 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 -:ref:`layout-interact-elemstate` for information on connecting an element to an +: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 and reels) use the state directly to determine @@ -425,9 +441,9 @@ them). All components support a few common features: (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`` @@ -444,9 +460,9 @@ them). All components support a few common features: 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. For - ``image`` components, the colour of the image pixels is multiplied by the + (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. @@ -472,10 +488,14 @@ disk image 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 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. + 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 refers to a file, it must have the same dimensions (in pixels) as the file referred to by the ``file`` @@ -484,7 +504,8 @@ image 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; it is only used in conjunction with bitmap images. + 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 @@ -575,21 +596,27 @@ reel ``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> @@ -601,7 +628,9 @@ An example element for a button that gives visual feedback when clicked:: </element> An example of an element that draws a seven-segment LED display using external -segment images:: +segment images: + +.. code-block:: XML <element name="digit_a" defstate="0"> <image file="a_off.png" /> @@ -616,7 +645,9 @@ segment images:: </element> An example of a bar graph that grows vertically and changes colour from green, -through yellow, to red as the state increases:: +through yellow, to red as the state increases: + +.. code-block:: XML <element name="pedal"> <rect> @@ -630,7 +661,9 @@ through yellow, to red as the state increases:: 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:: +neutral position: + +.. code-block:: XML <element name="wheel"> <rect> @@ -646,7 +679,7 @@ neutral position:: </element> -.. _layout-parts-views: +.. _layfile-parts-views: Views ~~~~~ @@ -671,7 +704,9 @@ 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"> @@ -686,7 +721,7 @@ 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, + 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 @@ -694,18 +729,18 @@ bounds output window or screen. param Defines or reassigns a value parameter in the view’s scope. See - :ref:`layout-concepts-params` for details. + :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. Within - a view, elements are drawn in the order they appear in the layout file, from - front to back. See below for more details. + 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. See :ref:`layout-interact-clickable` for details. See - :ref:`layout-interact-elemstate` for details on supplying a state value to + 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 @@ -719,14 +754,14 @@ screen 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:`layout-interact-clickable` for details. + 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:`layout-parts-collections`). The name of the collection is + 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. @@ -735,9 +770,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 @@ -746,7 +787,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 @@ -771,13 +812,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> @@ -788,16 +831,16 @@ 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. 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:`layout-interact-itemanim` for details. +:ref:`layfile-interact-itemanim` for details. -.. _layout-parts-collections: +.. _layfile-parts-collections: Collections ~~~~~~~~~~~ @@ -816,7 +859,9 @@ 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:: +to be hidden by the user: + +.. code-block:: XML <view name="LED Displays, CRT and Keypad"> <collection name="LED Displays"> @@ -831,13 +876,14 @@ to be hidden by the user:: 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:`layout-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.) +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 ~~~~~~~~~~~~~~~ @@ -854,20 +900,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. @@ -875,12 +925,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 +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 --> @@ -900,7 +952,9 @@ To demonstrate how bounds calculation works, consider this example:: 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:: +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 --> @@ -931,20 +985,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 +: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.) -.. _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 @@ -962,12 +1016,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" /> @@ -978,7 +1034,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" /> @@ -989,7 +1047,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" /> @@ -1008,7 +1068,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" /> @@ -1038,7 +1100,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" /> @@ -1072,7 +1136,7 @@ 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-interact: +.. _layfile-interact: Interactivity ------------- @@ -1086,23 +1150,23 @@ Clickable items 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, - simple counter and reel elements. See :ref:`layout-parts-elements` for + simple counter and reel 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:`layout-parts-elements` for details. + :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:`layout-parts-elements` for details. + :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. -.. _layout-interact-clickable: +.. _layfile-interact-clickable: Clickable items ~~~~~~~~~~~~~~~ @@ -1112,12 +1176,14 @@ If a view item (``element`` or ``screen`` element) has ``inputtag`` and emulated system, clicking the element will activate the switch. The switch will remain active as long as the mouse 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:`layout-interact-itemanim`). +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:: +should activate. This sample shows instantiation of clickable buttons: + +.. code-block:: XML <element ref="btn_3" inputtag="X2" inputmask="0x10"> <bounds x="2.30" y="4.325" width="1.0" height="1.0" /> @@ -1134,21 +1200,23 @@ and only activates the first clickable item whose area includes the location of the mouse pointer. -.. _layout-interact-elemstate: +.. _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:`layout-parts-elements` for details on how an element’s state affects its +: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:: +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> @@ -1179,7 +1247,7 @@ in the value being shifted four bits to the right). This is useful for obtaining the value of analog or positional inputs. -.. _layout-interact-itemanim: +.. _layfile-interact-itemanim: View item animation ~~~~~~~~~~~~~~~~~~~ @@ -1212,7 +1280,7 @@ 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:`layout-interact-elemstate`). +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 @@ -1238,7 +1306,9 @@ 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:: +position: + +.. code-block:: XML <repeat count="5"> <param name="x" start="10" increment="9" /> @@ -1260,7 +1330,9 @@ position:: 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:: +their positions: + +.. code-block:: XML <repeat count="4"> <param name="y" start="1" increment="3" /> @@ -1273,7 +1345,7 @@ their positions:: </repeat> -.. _layout-errors: +.. _layfile-errors: Error handling -------------- @@ -1289,7 +1361,7 @@ Error handling screens are considered unviable and not available to the user. -.. _layout-autogen: +.. _layfile-autogen: Automatically-generated views ----------------------------- @@ -1332,7 +1404,7 @@ The following views will be automatically generated: will be displayed at physical aspect ratio, with rotation applied. -.. _layout-complay: +.. _layfile-complay: Using complay.py ---------------- @@ -1362,12 +1434,12 @@ 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** -.. _layout-examples: +.. _layfile-examples: Example layout files -------------------- diff --git a/docs/source/techspecs/layout_script.rst b/docs/source/techspecs/layout_script.rst new file mode 100644 index 00000000000..968a8fe81fb --- /dev/null +++ b/docs/source/techspecs/layout_script.rst @@ -0,0 +1,203 @@ +.. _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 layout plugin to be enabled. For example, to run +BWB Double Take with the Lua script in the layout enabled, you might use this +command:: + + mame64 -plugins -plugin layout v4dbltak + +If 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. + + +.. _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. + +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 layout file. + +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 I/O ports 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 view items 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. +* 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. diff --git a/docs/source/techspecs/luaengine.rst b/docs/source/techspecs/luaengine.rst index 6abde32f54c..4b9c3f0e555 100644 --- a/docs/source/techspecs/luaengine.rst +++ b/docs/source/techspecs/luaengine.rst @@ -4,41 +4,53 @@ 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. +It is now possible to externally drive MAME via Lua scripts. This feature +initially appeared in version 0.148, when a minimal Lua engine was implemented. +Today, 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. +Internally, MAME makes extensive use of `Sol3 <https://github.com/ThePhD/sol2>`_ +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. +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 most of the objects +support runtime you can introspection. Features -------- -The API is not yet complete, but this is a partial list of capabilities currently available to LUA scripts: +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 metadata (app version, current emulated system, 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) +- device introspection (device tree listing, memory and register enumeration) +- screen introspection (screens listing, screen details, frame 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) +- register and state control (state 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. +MAME supports external scripting via Lua (>= 5.3) scripts, either written on the +interactive console or loaded as a file. To reach the console, enable the +console plugin (e.g. run MAME with ``-plugin console``) and you will be greeted +by a ``[MAME]>`` prompt where you can enter 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 load a whole script at once, store it in a plain text file and pass it using +``-autoboot_script``. Please note that script loading may be delayed (a few +seconds by default), but you can override the default with the +``-autoboot_delay`` option. -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). +To control the execution of your code, you can use a loop-based or 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 (e.g. at each frame rendering). Walkthrough ----------- @@ -48,12 +60,18 @@ Let's first run MAME in a terminal to reach the LUA console: :: $ mame -console YOUR_ROM - _/ _/ _/_/ _/ _/ _/_/_/_/ - _/_/ _/_/ _/ _/ _/_/ _/_/ _/ - _/ _/ _/ _/_/_/_/ _/ _/ _/ _/_/_/ - _/ _/ _/ _/ _/ _/ _/ - _/ _/ _/ _/ _/ _/ _/_/_/_/ - mame v0.217 + /| /| /| /| /| _______ + / | / | / | / | / | / / + / |/ | / | / |/ | / ____/ + / | / | / | / /_ + / |/ | / |/ __/ + / /| /| /| |/ /| /| /____ + / / | / | / | / | / | / + / _/ |/ / / |___/ |/ /_______/ + / / + / _/ + + mame 0.226 Copyright (C) Nicola Salmoria and the MAME team Lua 5.3 @@ -68,35 +86,38 @@ 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. +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.217 + mame 0.226 -We now start exploring screen related methods. First, let's enumerate available screens: +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 + [MAME]> for tag, screen in pairs(manager:machine().screens) do print(tag) 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: +``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]> -- keep a reference to the main screen in a variable [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: +We have several methods to draw a HUD on the screen composed of lines, boxes and +text: :: @@ -108,19 +129,22 @@ We have several methods to draw on the screen a HUD composed of lines, boxes and [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: +This will draw some useless art on the screen. However, when resuming 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. +All colors are specified in ARGB format (eight bits per channel), 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 + [MAME]> for tag, device in pairs(manager:machine().devices) do print(tag) end :audiocpu :maincpu :saveram @@ -134,7 +158,7 @@ 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 + [MAME]> for k, v in pairs(cpu.state) do print(k) end D5 SP A4 @@ -151,8 +175,8 @@ On some of them, you can also inspect and manipulate memory and state: :: [MAME]> -- inspect memory - [MAME]> for k,v in pairs(cpu.spaces) do print(k) end + [MAME]> for name, space in pairs(cpu.spaces) do print(name) end program [MAME]> mem = cpu.spaces["program"] - [MAME]> print(mem:read_i8(0xC000)) + [MAME]> print(mem:read_i8(0xc000)) 41 diff --git a/docs/source/techspecs/memory.rst b/docs/source/techspecs/memory.rst index d3de2579f6e..65b4ad93cb1 100644 --- a/docs/source/techspecs/memory.rst +++ b/docs/source/techspecs/memory.rst @@ -1,6 +1,9 @@ Emulated system memory and address spaces management ==================================================== +.. contents:: :local: + + 1. Overview ----------- @@ -9,11 +12,11 @@ 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 +* 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 +``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. @@ -27,7 +30,7 @@ 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 +called address width and an Endianness. In addition an address shift allows for buses that have an atomic granularity different than a byte. @@ -43,7 +46,7 @@ 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 -programatically. +programmatically. 2.3 Shares, banks and regions @@ -61,140 +64,210 @@ 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~ -| 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; -| }; + +.. 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 +shared between an emulated CPU and a driver. As such one has easy access to its contents from the driver class. -| 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), +.. 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 memory_share -object. +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; -| memory_share_creator<uNN> m_share; -| -| [device constructor] m_share(\*this, "name", size, endianness), + [device constructor] m_share(*this, "name", size, endianness), -A memory share can be created if it doesn't exist in a memory map +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() -method to get the memory_share object and the bytes(), endianness(), -bitwidth() and bytewidth() methods for share information. +retrieved. That class behaves like a pointer but also has the +``target()``, ``length()``, ``bytes()``, ``endianness()``, +``bitwidth()`` and ``bytewidth()`` methods for share information. -| memory_share \*memshare(string tag) const; +.. code-block:: C++ -The memshare device method retrieves a memory share by name. Beware + 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~ -| 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; -| }; + +.. 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 -allows to set a relationship between an entry number and a base -pointer. configure_entries does the same for multiple consecutive -entries spanning a memory zone. Alternatively set_base sets the base -for entry 0 and selects it. - -set_entry allows to dynamically and efficiently select the current -active entry, entry() gets that selection back, and base() gets the -assotiated base pointer. - -| 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), +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. Alternatively ``set_base`` sets the base for +entry 0 and selects it. + +``set_entry`` allows to dynamically and efficiently select the current +active entry, ``entry()`` gets that selection back, 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. -| memory_bank_creator m_bank; -| -| [device constructor] m_bank(\*this, "name"), +.. code-block:: C++ + + memory_bank_creator m_bank; + + [device constructor] m_bank(*this, "name"), -A memory share can be created if it doesn't exist in a memory map +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. -| memory_bank \*membank(string tag) const; +.. code-block:: C++ -The memshare device method retrieves a memory share by name. Beware + 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -| class memory_bank { -| 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 readonly data like roms or the result of + +.. code-block:: C++ + + class memory_bank { + 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. - -| 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), +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. -| memory_region \*memregion(string tag) const; +.. code-block:: C++ -The memshare device method retrieves a memory share by name. Beware -that the lookup can be expensive, prefer finders instead. + 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. 4. Address maps API @@ -211,16 +284,18 @@ happen when a specific range is accessed. The general syntax for entries uses method chaining: -| map(start, end).handler(...).handler_qualifier(...).range_qualifier(); +.. code-block:: C++ + + map(start, end).handler(...).handler_qualifier(...).range_qualifier(); -The values start and end define the range, the handler() block defines -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, byte -selection...). +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 map follows a "last one wins" principle, where the last one is -selected when multiple handlers match a given address. +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 @@ -229,21 +304,25 @@ selected when multiple handlers match a given address. 4.2.1 Global masking '''''''''''''''''''' -| map.global_mask(offs_t mask); +.. code-block:: C++ + + map.global_mask(offs_t mask); -Allows to indicates a mask to be applied to all addresses when -accessing the space that map is installed in. +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 '''''''''''''''''''''''''''''''''''''''''''' -| map.unmap_value_low(); -| map.unmap_value_high(); -| map.unmap_value(u8 value); +.. code-block:: C++ -Sets the value to return on reads to an unmapped or nopped-out -address. Low means 0, high ~0. + 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 @@ -252,133 +331,151 @@ address. Low means 0, high ~0. 4.3.1 Method on the current device '''''''''''''''''''''''''''''''''' -| (...).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) +.. 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 -less wide than the bus itself (for instance a 8-bits device on a -32-bits bus). +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 a 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 drives 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. +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 '''''''''''''''''''''''''''''''''' -| (...).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)) +.. 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 a finder -(required_device or optional_device) or its tag, to read, write or -both for the current entry. +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 ''''''''''''''''''''' -| (...).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") +.. 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 6 available for methods. One can either use FUNC() 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. +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 '''''''''''''''''''''''''' -| (...).rom() -| (...).writeonly() -| (...).ram() +.. code-block:: C++ + + (...).rom() + (...).writeonly() + (...).ram() Selects the range to access a memory zone as read-only, write-only or -read/write respectively. Specific handle qualifiers allow to tell -where this memory zone should be. There are two cases when no -qualifier is acceptable: +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. +* ``ram()`` gives an anonymous RAM zone not accessible outside of the + address space. -* rom() when the memory map is used in an AS_PROGRAM +* ``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. -| (...).rom().region("name", offset) +.. code-block:: C++ + + (...).rom().region("name", offset) -The region qualifier allows to make a read-only zone point to the -contents of a given region at a given offset. +The ``region`` qualifier causes a read-only zone point to the contents +of a given region at a given offset. -| (...).rom().share("name") -| (...).writeonly.share("name") -| (...).ram().share("name") +.. code-block:: C++ -The share qualifier allow to make the zone point to a shared memory -region defined by its name. If the region is present in multiple -spaces the size, the bus width and if the bus is more than byte-wide -the endianness must match. + (...).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 ''''''''''''''''' -| (...).bankr("name") -| (...).bankw("name") -| (...).bankrw("name") +.. code-block:: C++ + + (...).bankr("name") + (...).bankw("name") + (...).bankrw("name") -Sets the range to point at the contents of a bank is read, write or -readwrite mode. +Sets the range to point at the contents of a memory bank in read, write +or read/write mode. 4.3.6 Port access ''''''''''''''''' -| (...).portr("name") -| (...).portw("name") -| (...).portrw("name") +.. code-block:: C++ + + (...).portr("name") + (...).portw("name") + (...).portrw("name") -Sets the range to point at an i/o port. +Sets the range to point at an I/O port. 4.3.7 Dropped access '''''''''''''''''''' -| (...).nopr() -| (...).nopw() -| (...).noprw() +.. code-block:: C++ + + (...).nopr() + (...).nopw() + (...).noprw() Sets the range to drop the access without logging. When reading, the unmap value is returned. @@ -387,9 +484,11 @@ unmap value is returned. 4.3.8 Unmapped access ''''''''''''''''''''' -| (...).unmapr() -| (...).unmapw() -| (...).unmaprw() +.. code-block:: C++ + + (...).unmapr() + (...).unmapw() + (...).unmaprw() Sets the range to drop the access with logging. When reading, the unmap value is returned. @@ -398,8 +497,10 @@ unmap value is returned. 4.3.9 Subdevice mapping ''''''''''''''''''''''' -| (...).m(m_other_device, FUNC(other_device::map_method)) -| (...).m("other-device-tag", FUNC(other_device::map_method)) +.. 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 @@ -416,19 +517,23 @@ or banks. 4.4.1 Mirroring ''''''''''''''' -| (...).mirror(mask) +.. 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 mask 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. +mirror bits are not seen by the handler. 4.4.2 Masking ''''''''''''' -| (...).mask(mask) +.. code-block:: C++ + + (...).mask(mask) Only valid with handlers, the address will be masked with the mask before being passed to the handler. @@ -437,10 +542,12 @@ before being passed to the handler. 4.4.3 Selection ''''''''''''''' -| (...).select(mask) +.. code-block:: C++ + + (...).select(mask) Only valid with handlers, the range will be mirrored as with mirror, -but the mirror address bits are kept in the offset passed to the +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. @@ -449,32 +556,70 @@ bits a voice number. 4.4.4 Sub-unit selection '''''''''''''''''''''''' -| (...).umask16(16-bits mask) -| (...).umask32(32-bits mask) -| (...).umask64(64-bits mask) +.. 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 actual -device with 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. +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 +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 '''''''''''''''''''''''''''''''''''''' -| (...).cselect(16/32/64) +.. 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 allows to tell 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). +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.5 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 @@ -483,121 +628,167 @@ The parameter is that trigger width (would be 16 in the 68000 case). 5.1 General API structure ~~~~~~~~~~~~~~~~~~~~~~~~~ -A series of methods allow to change the bus decoding of an address -space on the fly. They're powerful but have some issues: +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 that be grouped in an address map, which can be less readable +* 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 that 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 puts them all together as method -parameters. To make things a little more readable lots of them are -optional though, the optional ones being written in italics. +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 ~~~~~~~~~~~~~~~~~~~ -| 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)) + +.. 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 12 +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. +Note that as all delegates, they can also wrap lambdas. + +.. code-block:: C++ -| space.install_read_handler(addrstart, addrend, read_delegate, *unitmask*, *cswidth*) -| space.install_read_handler(addrstart, addrend, addrmask, addrmirror, addrselect, read_delegate, *unitmask*, *cswidth*) -| space.install_write_handler(addrstart, addrend, write_delegate, *unitmask*, *cswidth*) -| space.install_write_handler(addrstart, addrend, addrmask, addrmirror, addrselect, write_delegate, *unitmask*, *cswidth*) -| space.install_readwrite_handler(addrstart, addrend, read_delegate, write_delegate, *unitmask*, *cswidth*) -| space.install_readwrite_handler(addrstart, addrend, addrmask, addrmirror, addrselect, read_delegate, write_delegate, *unitmask*, *cswidth*) + space.install_read_handler(addrstart, addrend, read_delegate, unitmask, cswidth) + space.install_read_handler(addrstart, addrend, addrmask, addrmirror, addrselect, read_delegate, unitmask, cswidth) + space.install_write_handler(addrstart, addrend, write_delegate, unitmask, cswidth) + space.install_write_handler(addrstart, addrend, addrmask, addrmirror, addrselect, write_delegate, unitmask, cswidth) + space.install_readwrite_handler(addrstart, addrend, read_delegate, write_delegate, unitmask, cswidth) + space.install_readwrite_handler(addrstart, addrend, addrmask, addrmirror, addrselect, read_delegate, write_delegate, unitmask, cswidth) 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. +read/write case both delegates must be of the same flavor (``smo`` +stuff) to avoid a combinatorial explosion of method types. The +``unitmask`` and ``cswidth`` arguments are optional. 5.3 Direct memory range mapping ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -| space.install_rom(addrstart, addrend, void \*pointer) -| space.install_rom(addrstart, addrend, addrmirror, void \*pointer) -| space.install_writeonly(addrstart, addrend, void \*pointer) -| space.install_writeonly(addrstart, addrend, addrmirror, void \*pointer) -| space.install_ram(addrstart, addrend, void \*pointer) -| space.install_ram(addrstart, addrend, addrmirror, void \*pointer) +.. code-block:: C++ + + space.install_rom(addrstart, addrend, void *pointer) + space.install_rom(addrstart, addrend, addrmirror, void *pointer) + space.install_writeonly(addrstart, addrend, void *pointer) + space.install_writeonly(addrstart, addrend, addrmirror, void *pointer) + space.install_ram(addrstart, addrend, void *pointer) + space.install_ram(addrstart, addrend, addrmirror, void *pointer) Installs a memory block in an address space, with or without mirror. -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. +``_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 ~~~~~~~~~~~~~~~~ -| space.install_read_bank(addrstart, addrend, memory_bank \*bank) -| space.install_read_bank(addrstart, addrend, addrmirror, memory_bank \*bank) -| space.install_write_bank(addrstart, addrend, memory_bank \*bank) -| space.install_write_bank(addrstart, addrend, addrmirror, memory_bank \*bank) -| space.install_readwrite_bank(addrstart, addrend, memory_bank \*bank) -| space.install_readwrite_bank(addrstart, addrend, addrmirror, memory_bank \*bank) - -Install for reading, writing or both an existing memory bank in an + +.. code-block:: C++ + + space.install_read_bank(addrstart, addrend, memory_bank *bank) + space.install_read_bank(addrstart, addrend, addrmirror, memory_bank *bank) + space.install_write_bank(addrstart, addrend, memory_bank *bank) + space.install_write_bank(addrstart, addrend, addrmirror, memory_bank *bank) + space.install_readwrite_bank(addrstart, addrend, memory_bank *bank) + space.install_readwrite_bank(addrstart, addrend, addrmirror, memory_bank *bank) + +Install an existing memory bank for reading, writing or both in an address space. 5.5 Port mapping ~~~~~~~~~~~~~~~~ -| space.install_read_port(addrstart, addrend, const char \*rtag) -| space.install_read_port(addrstart, addrend, addrmirror, const char \*rtag) -| space.install_write_port(addrstart, addrend, const char \*wtag) -| space.install_write_port(addrstart, addrend, addrmirror, 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) -Install read, write or both ports by name. +.. code-block:: C++ + + space.install_read_port(addrstart, addrend, const char *rtag) + space.install_read_port(addrstart, addrend, addrmirror, const char *rtag) + space.install_write_port(addrstart, addrend, const char *wtag) + space.install_write_port(addrstart, addrend, addrmirror, 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) + +Install ports by name for reading, writing or both. 5.6 Dropped accesses ~~~~~~~~~~~~~~~~~~~~ -| space.nop_read(addrstart, addrend, *addrmirror*) -| space.nop_write(addrstart, addrend, *addrmirror*) -| space.nop_readwrite(addrstart, addrend, *addrmirror*) + +.. code-block:: C++ + + space.nop_read(addrstart, addrend, addrmirror) + space.nop_write(addrstart, addrend, addrmirror) + space.nop_readwrite(addrstart, addrend, addrmirror) Drops the accesses for a given range with an optional mirror. 5.7 Unmapped accesses ~~~~~~~~~~~~~~~~~~~~~ -| space.unmap_read(addrstart, addrend, *addrmirror*) -| space.unmap_write(addrstart, addrend, *addrmirror*) -| space.unmap_readwrite(addrstart, addrend, *addrmirror*) -Unmaps the accesses (e.g. logs the access as unmapped) for a given -range with an optional mirror. +.. code-block:: C++ + + space.unmap_read(addrstart, addrend, addrmirror) + space.unmap_write(addrstart, addrend, addrmirror) + space.unmap_readwrite(addrstart, addrend, addrmirror) + +Unmaps the accesses (e.g. logs the access as unmapped) for a given range +with an optional mirror. 5.8 Device map installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -| space.install_device(addrstart, addrend, device, map, *unitmask*, *cswidth*) -Install a device address with an address map in a space. +.. code-block:: C++ + + space.install_device(addrstart, addrend, device, map, unitmask, cswidth) + +Install a device address with an address map in a space. The +``unitmask`` and ``cswidth`` arguments are optional. + +5.9 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. diff --git a/docs/source/techspecs/object_finders.rst b/docs/source/techspecs/object_finders.rst new file mode 100644 index 00000000000..2256d74b677 --- /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, type, 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 +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; + } |