From 8dc10e7e10e6e0ffe5cf9f31c33bc1868edd8667 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Mon, 9 Nov 2020 00:57:21 +1100 Subject: docs: Wrote another section of object finder documentation. Also improved formatting of code snippets in a few pages. --- docs/source/techspecs/layout_files.rst | 145 +++++-- docs/source/techspecs/memory.rst | 663 ++++++++++++++++++------------- docs/source/techspecs/object_finders.rst | 249 +++++++++++- src/mame/drivers/atarisy1.cpp | 4 +- src/mame/drivers/dooyong.cpp | 16 +- src/mame/drivers/segaxbd.cpp | 2 +- src/mame/includes/atarisy1.h | 8 +- src/mame/machine/tnzs.cpp | 50 +-- src/mame/video/avgdvg.cpp | 3 +- 9 files changed, 756 insertions(+), 384 deletions(-) diff --git a/docs/source/techspecs/layout_files.rst b/docs/source/techspecs/layout_files.rst index a3de7d66390..69e978713d6 100644 --- a/docs/source/techspecs/layout_files.rst +++ b/docs/source/techspecs/layout_files.rst @@ -82,14 +82,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 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 @@ -113,7 +117,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 @@ -131,7 +137,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 @@ -166,7 +174,9 @@ 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 @@ -175,7 +185,9 @@ Generator parameters are assigned using a ``param`` element with ``name`` and attributes. Generator parameters may only appear inside ``repeat`` elements (see :ref:`layout-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 @@ -189,25 +201,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`` @@ -342,7 +353,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 @@ -575,21 +588,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 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 -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 @@ -601,7 +620,9 @@ An example element for a button that gives visual feedback when clicked:: An example of an element that draws a seven-segment LED display using external -segment images:: +segment images: + +.. code-block:: XML @@ -616,7 +637,9 @@ segment images:: 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 @@ -630,7 +653,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 @@ -671,7 +696,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 @@ -777,7 +804,9 @@ 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 @@ -816,7 +845,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 @@ -854,14 +885,18 @@ 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 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 @@ -880,7 +915,9 @@ 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 @@ -900,7 +937,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 @@ -967,7 +1006,9 @@ child elements are used (:ref:`layout-parts`, :ref:`layout-parts-groups`, and 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 @@ -978,7 +1019,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 @@ -989,7 +1032,9 @@ element):: 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 @@ -1008,7 +1053,9 @@ outputs ``Dot_000`` to ``Dot_764`` (inside a ``group`` or ``view`` element):: Two horizontally separated, clickable, four-by-four keypads (inside a ``group`` -or ``view`` element):: +or ``view`` element): + +.. code-block:: XML @@ -1038,7 +1085,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 @@ -1117,7 +1166,9 @@ the item’s animation state, see :ref:`layout-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 @@ -1148,7 +1199,9 @@ 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 @@ -1238,7 +1291,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 @@ -1260,7 +1315,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 diff --git a/docs/source/techspecs/memory.rst b/docs/source/techspecs/memory.rst index d3de2579f6e..e069c02f05b 100644 --- a/docs/source/techspecs/memory.rst +++ b/docs/source/techspecs/memory.rst @@ -9,11 +9,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 +27,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 +43,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 @@ -66,135 +66,160 @@ All of these have names allowing to access them. 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 m_share_ptr; -| optional_shared_ptr m_share_ptr; -| required_shared_ptr_array m_share_ptr_array; -| optional_shared_ptr_array 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 m_share_ptr; + optional_shared_ptr m_share_ptr; + required_shared_ptr_array m_share_ptr_array; + optional_shared_ptr_array 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. -| memory_share_creator m_share; -| -| [device constructor] m_share(\*this, "name", size, endianness), +.. code-block:: C++ -A memory share can be created if it doesn't exist in a memory map + memory_share_creator m_share; + + [device constructor] m_share(*this, "name", size, endianness), + +A memory share can be created if it doesn’t exist in a memory map through that creator class. If it already exists it is just -retrieved. That class behaves like a pointer but also has the target() -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 m_bank_array; -| optional_memory_bank_array 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 m_bank_array; + optional_memory_bank_array 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 m_region_array; -| optional_memory_region_array 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 m_region_array; + optional_memory_region_array 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. 4. Address maps API @@ -211,16 +236,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++ -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...). + map(start, end).handler(...).handler_qualifier(...).range_qualifier(); -The map follows a "last one wins" principle, where the last one is -selected when multiple handlers match a given address. +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 handler specified +last is selected when multiple handlers match a given address. 4.2 Global configurations @@ -229,21 +256,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 +283,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 causes a read-only zone point to the contents +of a given region at a given offset. -The region qualifier allows to make a read-only zone point to the -contents of a given region at a given offset. +.. code-block:: C++ -| (...).rom().share("name") -| (...).writeonly.share("name") -| (...).ram().share("name") + (...).rom().share("name") + (...).writeonly.share("name") + (...).ram().share("name") -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. +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++ -Sets the range to point at the contents of a bank is read, write or -readwrite mode. + (...).bankr("name") + (...).bankw("name") + (...).bankrw("name") + +Sets the range to point at the contents of a memory bank in read, write +or read/write mode. 4.3.6 Port access ''''''''''''''''' -| (...).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 +436,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 +449,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 +469,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 +494,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 +508,36 @@ 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). 5. Address space dynamic mapping API @@ -483,121 +546,149 @@ 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. diff --git a/docs/source/techspecs/object_finders.rst b/docs/source/techspecs/object_finders.rst index 3fd8be922fd..1acb475a7fa 100644 --- a/docs/source/techspecs/object_finders.rst +++ b/docs/source/techspecs/object_finders.rst @@ -73,7 +73,9 @@ its own child devices, inputs and ROM region. The code samples here are based on the Apple II Parallel Printer Interface card, but a lot of things have been removed for clarity. -Object finders are declared as members of the device class:: +Object finders are declared as members of the device class: + +.. code-block:: C++ class a2bus_parprn_device : public device_t, public device_a2bus_card_interface { @@ -98,7 +100,9 @@ Object finders are declared as members of the device class:: We want to find a ``centronics_device``, an ``output_latch_device``, an I/O port, and an 8-bit memory region. -In the constructor, we set the initial target for the object finders:: +In the constructor, we set the initial target for the object finders: + +.. code-block:: C++ a2bus_parprn_device::a2bus_parprn_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : device_t(mconfig, A2BUS_PARPRN, tag, owner, clock), @@ -121,7 +125,9 @@ ensure the tag string remains valid until after validation and/or object resolution is complete. The memory region and I/O port come from the ROM definition and input -definition, respectively:: +definition, respectively: + +.. code-block:: C++ namespace { @@ -164,7 +170,9 @@ Note that the tags ``"prom"`` and ``"CFG"`` match the tags passed to the object finders on construction. Child devices are instantiated in the device’s machine configuration member -function:: +function: + +.. code-block:: C++ void a2bus_parprn_device::device_add_mconfig(machine_config &config) { @@ -183,7 +191,9 @@ its base device must be the same as the device being configured (the ``this`` pointer of the machine configuration member function). After the emulated machine has been started, the object finders can be used in -much the same way as pointers:: +much the same way as pointers: + +.. code-block:: C++ void a2bus_parprn_device::write_c0nx(u8 offset, u8 data) { @@ -209,7 +219,9 @@ Connections between devices Devices need to be connected together within a system. For example the Sun SBus device needs access to the host CPU and address space. Here’s how we declare -the object finders in the device class (with all distractions removed):: +the object finders in the device class (with all distractions removed): + +.. code-block:: C++ DECLARE_DEVICE_TYPE(SBUS, sbus_device) @@ -346,7 +358,9 @@ finder array types are provided. The object finder array type names have | memory_share_creator | memory_share_array_creator | +------------------------+------------------------------+ -A common case for an object array finder is a key matrix:: +A common case for an object array finder is a key matrix: + +.. code-block:: C++ class keyboard_base : public device_t, public device_mac_keyboard_interface { @@ -383,7 +397,9 @@ underlying object finder type. It supports indexing, iterators, and range-based ``for`` loops. Because an index offset is specified, the tags don’t need to use zero-based -indices. It’s common to use one-based indexing like this:: +indices. It’s common to use one-based indexing like this: + +.. code-block:: C++ class dooyong_state : public driver_device { @@ -404,7 +420,9 @@ This causes ``m_bg`` to find devices with tags ``bg1`` and ``bg2``, while into the object finder arrays are still zero-based like any other C array. It’s also possible to other format conversions, like hexadecimal (``%x`` and -``%X``) or character (``%c``):: +``%X``) or character (``%c``): + +.. code-block:: C++ class eurit_state : public driver_device { @@ -423,7 +441,9 @@ In this case, the key matrix ports use tags ``KEYA``, ``KEYB``, ``KEYC``, ``KEYD`` and ``KEYE``. When the tags don’t follow a simple ascending sequence, you can supply a -brace-enclosed initialiser list of tags:: +brace-enclosed initialiser list of tags: + +.. code-block:: C++ class seabattl_state : public driver_device { @@ -440,20 +460,223 @@ brace-enclosed initialiser list of tags:: If the underlying object finders require additional constructor arguments, supply them after the tag format and index offset (the same values will be used -for all elements of the array):: +for all elements of the array): + +.. code-block:: C++ class dreamwld_state : public driver_device { public: - dreamwld_state(const machine_config &mconfig, device_type type, const char *tag) : + dreamwld_state(machine_config const &mconfig, device_type type, char const *tag) : driver_device(mconfig, type, tag), m_vram(*this, "vram_%u", 0U, 0x2000U, ENDIANNESS_BIG) { } private: - memory_share_array_creator m_vram; + memory_share_array_creator 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 m_soundcpu; + optional_device m_soundcpu2; + required_device m_watchdog; + required_device m_segaic16vid; + bool m_adc_reverse[8]; + u8 m_pc_0; + u8 m_lastsurv_mux; + optional_ioport_array<8> m_adc_ports; + optional_ioport_array<4> m_mux_ports; + }; + +The ``optional_device`` and ``optional_ioport_array`` members are declared and +constructed in the usual way. Before accessing the target object, we call an +object finder’s ``found()`` member function to check whether it’s present in the +system (the explicit cast-to-Boolean operator can be used for the same purpose): + +.. code-block:: C++ + + void segaxbd_state::pc_0_w(u8 data) + { + m_pc_0 = data; + + m_watchdog->write_line_ck(BIT(data, 6)); + + m_segaic16vid->set_display_enable(data & 0x20); + + if (m_soundcpu.found()) + m_soundcpu->set_input_line(INPUT_LINE_RESET, (data & 0x01) ? CLEAR_LINE : ASSERT_LINE); + if (m_soundcpu2.found()) + m_soundcpu2->set_input_line(INPUT_LINE_RESET, (data & 0x01) ? CLEAR_LINE : ASSERT_LINE); + } + +Optional I/O ports provide a convenience member function called ``read_safe`` +that reads the port value if present, or returns the supplied default value +otherwise: + +.. code-block:: C++ + + u8 segaxbd_state::analog_r() + { + int const which = (m_pc_0 >> 2) & 7; + u8 value = m_adc_ports[which].read_safe(0x10); + + if (m_adc_reverse[which]) + value = 255 - value; + + return value; + } + + uint8_t segaxbd_state::lastsurv_port_r() + { + return m_mux_ports[m_lastsurv_mux].read_safe(0xff); + } + +The ADC ports return 0x10 (16 decimal) if they are not present, while the +multiplexed digital ports return 0xff (255 decimal) if they are not present. +Note that ``read_safe`` is a member of the ``optional_ioport`` itself, and not +a member of the target ``ioport_port`` object (the ``optional_ioport`` is not +dereferenced when using it). + +There are some disadvantages to using optional object finders: + +* There’s no way to distinguish between the target not being present, and the + target not being found due to mismatched tags, making it more error-prone. +* Checking whether the target is present may use CPU branch prediction + resources, potentially hurting performance if it happens very frequently. + +Consider whether optional object finders are the best solution, or whether +creating a derived class for the system with additional components is more +appropriate. + +Optional resources +~~~~~~~~~~~~~~~~~~ + +Some devices can optionally use certain resources. If the host system doesn’t +supply them, the device will still function, although some functionality may not +be available. For example, the Virtual Boy cartridge slot responds to three +address spaces, called EXP, CHIP and ROM. If the host system will never use one +or more of them, it doesn’t need to supply a place for the cartridge to install +the corresponding handlers. (For example a copier may only use the ROM space.) + +Let’s look at how this is implemented. The Virtual Boy cartridge slot device +declares ``optional_address_space`` members for the three address spaces, +``offs_t`` members for the base addresses in these spaces, and inline member +functions for configuring them: + +.. code-block:: C++ + + class vboy_cart_slot_device : + public device_t, + public device_image_interface, + public device_single_card_slot_interface + { + public: + vboy_cart_slot_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0U); + + template void set_exp(T &&tag, int no, offs_t base) + { + m_exp_space.set_tag(std::forward(tag), no); + m_exp_base = base; + } + template void set_chip(T &&tag, int no, offs_t base) + { + m_chip_space.set_tag(std::forward(tag), no); + m_chip_base = base; + } + template void set_rom(T &&tag, int no, offs_t base) + { + m_rom_space.set_tag(std::forward(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(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(); + } diff --git a/src/mame/drivers/atarisy1.cpp b/src/mame/drivers/atarisy1.cpp index 3a866a84516..99488c5d651 100644 --- a/src/mame/drivers/atarisy1.cpp +++ b/src/mame/drivers/atarisy1.cpp @@ -250,7 +250,7 @@ uint8_t atarisy1_state::adc_r(offs_t offset) if (!m_adc.found()) return 0xff; - int value = m_adc->data_r(); + const u8 value = m_adc->data_r(); if (!machine().side_effects_disabled()) adc_w(offset, 0); @@ -266,7 +266,7 @@ void atarisy1_state::adc_w(offs_t offset, uint8_t data) m_adc->address_offset_start_w(offset & 7, 0); - /* the A4 bit enables/disables joystick IRQs */ + // the A4 bit enables/disables joystick IRQs m_ajsint->in_w<0>(!BIT(offset, 3)); } diff --git a/src/mame/drivers/dooyong.cpp b/src/mame/drivers/dooyong.cpp index c8fc3ac0278..84b5638cde5 100644 --- a/src/mame/drivers/dooyong.cpp +++ b/src/mame/drivers/dooyong.cpp @@ -318,13 +318,13 @@ protected: void ctrl_w(u8 data) { - /* bit 0 flips screen */ - flip_screen_set(data & 0x01); + // bit 0 flips screen + flip_screen_set(BIT(data, 0)); - /* bit 4 changes tilemaps priority */ - m_bg2_priority = data & 0x10; + // bit 4 changes tilemaps priority + m_bg2_priority = BIT(data, 4); - /* bit 5 used but unknown */ + // bit 5 used but unknown } TIMER_DEVICE_CALLBACK_MEMBER(scanline); @@ -352,7 +352,7 @@ protected: virtual void video_start() override { - /* Register for save/restore */ + // Register for save/restore save_item(NAME(m_bg2_priority)); } @@ -388,8 +388,8 @@ protected: m_screen->register_screen_bitmap(m_bg_bitmap[0]); m_screen->register_screen_bitmap(m_bg_bitmap[1]); - /* Register for save/restore */ - save_item(NAME(m_bg2_priority)); // Not used atm + // Register for save/restore + save_item(NAME(m_bg2_priority)); // Not used ATM } void popbingo_tile_callback(u16 attr, u32 &code, u32 &color) diff --git a/src/mame/drivers/segaxbd.cpp b/src/mame/drivers/segaxbd.cpp index 2c0e024f2ca..92efade11c1 100644 --- a/src/mame/drivers/segaxbd.cpp +++ b/src/mame/drivers/segaxbd.cpp @@ -477,7 +477,7 @@ WRITE_LINE_MEMBER(segaxbd_state::timer_irq_w) uint8_t segaxbd_state::analog_r() { // on the write, latch the selected input port and stash the value - int which = (m_pc_0 >> 2) & 7; + const int which = (m_pc_0 >> 2) & 7; uint8_t value = m_adc_ports[which].read_safe(0x10); // reverse some port values diff --git a/src/mame/includes/atarisy1.h b/src/mame/includes/atarisy1.h index aac95be7055..a90a2743177 100644 --- a/src/mame/includes/atarisy1.h +++ b/src/mame/includes/atarisy1.h @@ -87,7 +87,7 @@ protected: optional_device m_adc; optional_device m_ajsint; - /* playfield parameters */ + // playfield parameters required_device m_playfield_tilemap; required_device m_alpha_tilemap; required_shared_ptr m_xscroll; @@ -97,19 +97,19 @@ protected: uint16_t m_playfield_priority_pens; required_device m_yscroll_reset_timer; - /* INT3 tracking */ + // INT3 tracking int m_next_timer_scanline; required_device m_scanline_timer; required_device m_int3off_timer; uint8_t m_scanline_int_state; - /* speech */ + // speech optional_device m_tms; required_device m_outlatch; optional_device m_via; - /* graphics bank tracking */ + // graphics bank tracking uint8_t m_bank_gfx[3][8]; uint8_t m_bank_color_shift[MAX_GFX_ELEMENTS]; uint8_t m_bankselect; diff --git a/src/mame/machine/tnzs.cpp b/src/mame/machine/tnzs.cpp index af3eb6ed3e8..e1246b052df 100644 --- a/src/mame/machine/tnzs.cpp +++ b/src/mame/machine/tnzs.cpp @@ -64,10 +64,7 @@ void tnzs_mcu_state::mcu_port2_w(uint8_t data) uint8_t tnzs_mcu_state::analog_r(offs_t offset) { - if (m_upd4701.found()) - return m_upd4701->read_xy(offset); - - return 0; + return m_upd4701.found() ? m_upd4701->read_xy(offset) : 0; } void arknoid2_state::mcu_reset() @@ -360,6 +357,7 @@ void tnzs_base_state::machine_start() void arknoid2_state::machine_start() { tnzs_base_state::machine_start(); + save_item(NAME(m_mcu_readcredits)); save_item(NAME(m_insertcoin)); save_item(NAME(m_mcu_initializing)); @@ -393,20 +391,18 @@ void tnzs_base_state::ramrom_bankswitch_w(uint8_t data) { // logerror("%s: writing %02x to bankswitch\n", m_maincpu->pc(),data); - /* bit 4 resets the second CPU */ - if (data & 0x10) - m_subcpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); - else - m_subcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + // bit 4 resets the second CPU + m_subcpu->set_input_line(INPUT_LINE_RESET, BIT(data, 4) ? CLEAR_LINE : ASSERT_LINE); - /* bits 0-2 select RAM/ROM bank */ + // bits 0-2 select RAM/ROM bank m_mainbank->set_bank(data & 0x07); } void arknoid2_state::bankswitch1_w(uint8_t data) { tnzs_base_state::bankswitch1_w(data); - if (data & 0x04) + + if (BIT(data, 2)) mcu_reset(); // never actually written by arknoid2 (though code exists to do it) @@ -417,33 +413,37 @@ void arknoid2_state::bankswitch1_w(uint8_t data) void insectx_state::bankswitch1_w(uint8_t data) { tnzs_base_state::bankswitch1_w(data); - machine().bookkeeping().coin_lockout_w(0, (~data & 0x04)); - machine().bookkeeping().coin_lockout_w(1, (~data & 0x08)); - machine().bookkeeping().coin_counter_w(0, (data & 0x10)); - machine().bookkeeping().coin_counter_w(1, (data & 0x20)); + + machine().bookkeeping().coin_lockout_w(0, BIT(~data, 2)); + machine().bookkeeping().coin_lockout_w(1, BIT(~data, 3)); + machine().bookkeeping().coin_counter_w(0, BIT(data, 4)); + machine().bookkeeping().coin_counter_w(1, BIT(data, 5)); } void tnzsb_state::bankswitch1_w(uint8_t data) // kabukiz_state { tnzs_base_state::bankswitch1_w(data); - machine().bookkeeping().coin_lockout_w(0, (~data & 0x10)); - machine().bookkeeping().coin_lockout_w(1, (~data & 0x20)); - machine().bookkeeping().coin_counter_w(0, (data & 0x04)); - machine().bookkeeping().coin_counter_w(1, (data & 0x08)); + + machine().bookkeeping().coin_lockout_w(0, BIT(~data, 4)); + machine().bookkeeping().coin_lockout_w(1, BIT(~data, 5)); + machine().bookkeeping().coin_counter_w(0, BIT(data, 2)); + machine().bookkeeping().coin_counter_w(1, BIT(data, 3)); } void kageki_state::bankswitch1_w(uint8_t data) { tnzs_base_state::bankswitch1_w(data); - machine().bookkeeping().coin_lockout_global_w((~data & 0x20)); - machine().bookkeeping().coin_counter_w(0, (data & 0x04)); - machine().bookkeeping().coin_counter_w(1, (data & 0x08)); + + machine().bookkeeping().coin_lockout_global_w(BIT(~data, 5)); + machine().bookkeeping().coin_counter_w(0, BIT(data, 2)); + machine().bookkeeping().coin_counter_w(1, BIT(data, 3)); } void tnzs_mcu_state::bankswitch1_w(uint8_t data) { tnzs_base_state::bankswitch1_w(data); - if ((data & 0x04) != 0 && m_mcu != nullptr) + + if (BIT(data, 2) && m_mcu) m_mcu->pulse_input_line(INPUT_LINE_RESET, attotime::zero); // written only at startup by plumppop? @@ -458,7 +458,7 @@ void tnzs_base_state::bankswitch1_w(uint8_t data) { // logerror("%s: writing %02x to bankswitch 1\n", m_maincpu->pc(),data); - /* bits 0-1 select ROM bank */ + // bits 0-1 select ROM bank m_bank2 = data & 0x03; m_subbank->set_entry(m_bank2); } @@ -484,7 +484,7 @@ void tnzsb_state::sound_command_w(uint8_t data) m_audiocpu->set_input_line_and_vector(0, HOLD_LINE, 0xff); // Z80 } -/* handler called by the 2203 emulator when the internal timers cause an IRQ */ +// handler called by the 2203 emulator when the internal timers cause an IRQ WRITE_LINE_MEMBER(tnzsb_state::ym2203_irqhandler) { m_audiocpu->set_input_line(INPUT_LINE_NMI, state ? ASSERT_LINE : CLEAR_LINE); diff --git a/src/mame/video/avgdvg.cpp b/src/mame/video/avgdvg.cpp index fe8727d5688..dee459d4e4d 100644 --- a/src/mame/video/avgdvg.cpp +++ b/src/mame/video/avgdvg.cpp @@ -771,7 +771,8 @@ int avg_mhavoc_device::handler_6() // mhavoc_strobe2 if (m_dvy & 0x800) { m_enspkl = 1; - m_spkl_shift = bitswap<4>(m_dvy, 0, 1, 2, 3) | ((machine().rand() & 0x7) << 4); // sparkle lfsr bits 4,5,6 here come from address bus bits 0,1,2 from the alpha cpu, they're not truly random. + // sparkle LFSR bits 4,5,6 here come from alpha CPU address bus bits 0,1,2, they're not truly random. + m_spkl_shift = bitswap<4>(m_dvy, 0, 1, 2, 3) | ((machine().rand() & 0x7) << 4); } else { -- cgit v1.2.3