mirror of
https://github.com/holub/mame
synced 2025-06-05 20:33:45 +03:00
docs: Wrote another section of object finder documentation.
Also improved formatting of code snippets in a few pages.
This commit is contained in:
parent
4410b37e13
commit
8dc10e7e10
@ -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
|
||||
|
||||
<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" />
|
||||
@ -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
|
||||
|
||||
<color red="0.85" green="0.4" blue="0.3" alpha="1.0" />
|
||||
|
||||
@ -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
|
||||
|
||||
<element name="digit~digitno~" ref="digit">
|
||||
<bounds x="~x~" y="80" width="25" height="40" />
|
||||
@ -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
|
||||
|
||||
<param name="firstdigit" value="4" />
|
||||
|
||||
@ -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
|
||||
|
||||
<param name="nybble" start="3" increment="-1" />
|
||||
<param name="switchpos" start="74" increment="156" />
|
||||
@ -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
|
||||
|
||||
<mamelayout version="2">
|
||||
|
||||
@ -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
|
||||
|
||||
<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>
|
||||
</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 +620,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 +637,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 +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
|
||||
|
||||
<element name="wheel">
|
||||
<rect>
|
||||
@ -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
|
||||
|
||||
<view name="Control panel">
|
||||
|
||||
@ -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
|
||||
|
||||
<view name="LED Displays, Terminal and Keypad">
|
||||
<screen index="0"><bounds x="0" y="132" width="320" height="240" /></screen>
|
||||
@ -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
|
||||
|
||||
<view name="LED Displays, CRT and Keypad">
|
||||
<collection name="LED Displays">
|
||||
@ -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
|
||||
|
||||
<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>
|
||||
|
||||
@ -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
|
||||
|
||||
<group name="autobounds">
|
||||
<!-- bounds automatically calculated with origin at (5,10), width 30, and height 15 -->
|
||||
@ -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
|
||||
|
||||
<group name="periphery">
|
||||
<!-- elements are above the top edge and to the right of the right edge of the bounds -->
|
||||
@ -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
|
||||
|
||||
<repeat count="12">
|
||||
<param name="labelnum" start="0" increment="1" />
|
||||
@ -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
|
||||
|
||||
<repeat count="40">
|
||||
<param name="i" start="0" increment="1" />
|
||||
@ -989,7 +1032,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 +1053,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 +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
|
||||
|
||||
<repeat count="4">
|
||||
<param name="pairy" start="3" increment="20" />
|
||||
@ -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
|
||||
|
||||
<element ref="btn_3" inputtag="X2" inputmask="0x10">
|
||||
<bounds x="2.30" y="4.325" width="1.0" height="1.0" />
|
||||
@ -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
|
||||
|
||||
<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>
|
||||
@ -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
|
||||
|
||||
<repeat count="5">
|
||||
<param name="x" start="10" increment="9" />
|
||||
@ -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
|
||||
|
||||
<repeat count="4">
|
||||
<param name="y" start="1" increment="3" />
|
||||
|
@ -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<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.
|
||||
|
||||
| memory_share_creator<uNN> 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<uNN> m_share;
|
||||
|
||||
[device constructor] m_share(*this, "name", size, endianness),
|
||||
|
||||
A memory share can be created if it doesn’t exist in a memory map
|
||||
through that creator class. If it already exists it is just
|
||||
retrieved. That class behaves like a pointer but also has the target()
|
||||
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.
|
||||
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
|
||||
assotiated base pointer.
|
||||
``set_entry`` allows to dynamically and efficiently select the current
|
||||
active entry, ``entry()`` gets that selection back, and ``base()`` gets
|
||||
the associated 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),
|
||||
.. 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++
|
||||
|
||||
A memory share can be created if it doesn't exist in a memory map
|
||||
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
|
||||
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.
|
||||
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),
|
||||
.. 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.
|
||||
|
||||
|
||||
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++
|
||||
|
||||
Allows to indicates a mask to be applied to all addresses when
|
||||
accessing the space that map is installed in.
|
||||
map.global_mask(offs_t mask);
|
||||
|
||||
Specifies a mask to be applied to all addresses when accessing the space
|
||||
that map is installed in.
|
||||
|
||||
|
||||
4.2.2 Returned value on unmapped/nop-ed read
|
||||
''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
| 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++
|
||||
|
||||
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.
|
||||
(...).r(m_other_device, FUNC(other_device::read_method))
|
||||
(...).r("other-device-tag", FUNC(other_device::read_method))
|
||||
(...).w(m_other_device, FUNC(other_device::write_method))
|
||||
(...).w("other-device-tag", FUNC(other_device::write_method))
|
||||
(...).rw(m_other_device, FUNC(other_device::read_method), FUNC(other_device::write_method))
|
||||
(...).rw("other-device-tag", FUNC(other_device::read_method), FUNC(other_device::write_method))
|
||||
|
||||
Sets a method of another device, designated by an object finder
|
||||
(usually ``required_device`` or ``optional_device``) or its tag, to
|
||||
read, write or both for the current entry.
|
||||
|
||||
|
||||
4.3.3 Lambda function
|
||||
'''''''''''''''''''''
|
||||
|
||||
| (...).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
|
||||
* ``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++
|
||||
|
||||
The region qualifier allows to make a read-only zone point to the
|
||||
contents of a given region at a given offset.
|
||||
(...).rom().region("name", offset)
|
||||
|
||||
| (...).rom().share("name")
|
||||
| (...).writeonly.share("name")
|
||||
| (...).ram().share("name")
|
||||
The ``region`` qualifier causes a read-only zone point to the contents
|
||||
of a given region at a given offset.
|
||||
|
||||
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.
|
||||
.. code-block:: C++
|
||||
|
||||
(...).rom().share("name")
|
||||
(...).writeonly.share("name")
|
||||
(...).ram().share("name")
|
||||
|
||||
The ``share`` qualifier causes the zone point to a shared memory region
|
||||
identified by its name. If the share is present in multiple spaces, the
|
||||
size, bus width, and, if the bus is more than byte-wide, the Endianness
|
||||
must match.
|
||||
|
||||
|
||||
4.3.5 Bank access
|
||||
'''''''''''''''''
|
||||
|
||||
| (...).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++
|
||||
|
||||
Sets the range to point at an i/o port.
|
||||
(...).portr("name")
|
||||
(...).portw("name")
|
||||
(...).portrw("name")
|
||||
|
||||
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:
|
||||
* 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
|
||||
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:
|
||||
|
||||
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.
|
||||
* 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 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.
|
||||
|
||||
| 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*)
|
||||
.. 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)
|
||||
|
||||
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.
|
||||
|
@ -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,12 +460,14 @@ 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)
|
||||
{
|
||||
@ -457,3 +479,204 @@ for all elements of the array)::
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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<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();
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -87,7 +87,7 @@ protected:
|
||||
optional_device<adc0808_device> m_adc;
|
||||
optional_device<input_merger_device> m_ajsint;
|
||||
|
||||
/* playfield parameters */
|
||||
// playfield parameters
|
||||
required_device<tilemap_device> m_playfield_tilemap;
|
||||
required_device<tilemap_device> m_alpha_tilemap;
|
||||
required_shared_ptr<uint16_t> m_xscroll;
|
||||
@ -97,19 +97,19 @@ protected:
|
||||
uint16_t m_playfield_priority_pens;
|
||||
required_device<timer_device> m_yscroll_reset_timer;
|
||||
|
||||
/* INT3 tracking */
|
||||
// INT3 tracking
|
||||
int m_next_timer_scanline;
|
||||
required_device<timer_device> m_scanline_timer;
|
||||
required_device<timer_device> m_int3off_timer;
|
||||
uint8_t m_scanline_int_state;
|
||||
|
||||
/* speech */
|
||||
// speech
|
||||
optional_device<tms5220_device> m_tms;
|
||||
|
||||
required_device<ls259_device> m_outlatch;
|
||||
optional_device<via6522_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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user