From e008c7b1b1042d644be2a1e177ffa15d53e942c5 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Wed, 9 Dec 2020 01:01:22 +1100 Subject: [PATCH] -Lua engine cleanup, input edition: * Modernised and cleaned up Lua bindings for input classes. * Exposed the input_sequence_poller class to Lua and updated the autofire and cheat plugins to use it, rather than continuing to pretend it's part of the input manager. * Exposed more of the natural keyboard manager, including the ability to enable/disable individual keyboard and keypad devices like you can from the keyboard mode menu. * Exposed a few more things on ioport_port and input_device. -plugins/cheat: Fixed menu item not updating visually when disabling a cheat with UI Left. -plugins/cheatfind: Fixed not finding the first screen after screen enumerator was exposed as an object rather than using a table. -bwidow.cpp, pacman.cpp: Minor cleanup to recent changes. --- docs/source/techspecs/index.rst | 1 + docs/source/techspecs/layout_files.rst | 161 +++--- docs/source/techspecs/layout_script.rst | 203 ++++++++ plugins/autofire/autofire_menu.lua | 13 +- plugins/cheat/init.lua | 18 +- plugins/cheatfind/init.lua | 2 +- src/frontend/mame/luaengine.cpp | 2 - src/frontend/mame/luaengine.h | 7 +- src/frontend/mame/luaengine.ipp | 85 +++- src/frontend/mame/luaengine_input.cpp | 635 ++++++++++++++++-------- src/frontend/mame/luaengine_render.cpp | 82 +-- src/mame/drivers/bwidow.cpp | 4 +- src/mame/drivers/pacman.cpp | 9 +- 13 files changed, 816 insertions(+), 406 deletions(-) create mode 100644 docs/source/techspecs/layout_script.rst diff --git a/docs/source/techspecs/index.rst b/docs/source/techspecs/index.rst index f2e9e2e713c..0435dd486e8 100644 --- a/docs/source/techspecs/index.rst +++ b/docs/source/techspecs/index.rst @@ -9,6 +9,7 @@ MAME’s source or working on scripts that run within the MAME framework. naming layout_files + layout_script object_finders device_memory_interface device_rom_interface diff --git a/docs/source/techspecs/layout_files.rst b/docs/source/techspecs/layout_files.rst index 7581d7f332e..0a8d39c4bba 100644 --- a/docs/source/techspecs/layout_files.rst +++ b/docs/source/techspecs/layout_files.rst @@ -1,10 +1,12 @@ +.. _layfile: + MAME Layout Files ================= .. contents:: :local: -.. _layout-intro: +.. _layfile-intro: Introduction ------------ @@ -18,12 +20,12 @@ screens, built and linked into the MAME binary, or provided externally. MAME layout files are an XML application, using the ``.lay`` filename extension. -.. _layout-concepts: +.. _layfile-concepts: Core concepts ------------- -.. _layout-concepts-numbers: +.. _layfile-concepts-numbers: Numbers ~~~~~~~ @@ -53,7 +55,7 @@ found, the number will be interpreted as an integer. Numbers are parsed using the "C" locale for portability. -.. _layout-concepts-coordinates: +.. _layfile-concepts-coordinates: Coordinates ~~~~~~~~~~~ @@ -103,7 +105,7 @@ It is an error if ``width`` or ``height`` are negative, if ``right`` is less than ``left``, or if ``bottom`` is less than ``top``. -.. _layout-concepts-colours: +.. _layfile-concepts-colours: Colours ~~~~~~~ @@ -128,7 +130,7 @@ is an error if any channel value falls outside the range of 0.0 to 1.0 (inclusive). -.. _layout-concepts-params: +.. _layfile-concepts-params: Parameters ~~~~~~~~~~ @@ -183,7 +185,7 @@ Here’s an example assigning the value “4” to the value parameter “firstd Generator parameters are assigned using a ``param`` element with ``name`` and ``start`` attributes, and ``increment``, ``lshift`` and/or ``rshift`` attributes. Generator parameters may only appear inside ``repeat`` elements -(see :ref:`layout-parts-repeats` for details). A generator parameter must not +(see :ref:`layfile-parts-repeats` for details). A generator parameter must not be reassigned in the same scope (an identically named parameter may be defined in a child scope). Here are some example generator parameters: @@ -230,7 +232,7 @@ innermost scope. It is not possible to define or reassign parameters in a containing scope. -.. _layout-concepts-predef-params: +.. _layfile-concepts-predef-params: Pre-defined parameters ~~~~~~~~~~~~~~~~~~~~~~ @@ -340,7 +342,7 @@ end of configuration. Values are not updated and layouts are not recomputed if the system reconfigures the screen while running. -.. _layout-parts: +.. _layfile-parts: Parts of a layout ----------------- @@ -368,26 +370,27 @@ and groups that appear after them. The following elements are allowed inside the top-level ``mamelayout`` element: param - Defines or reassigns a value parameter. See :ref:`layout-concepts-params` + Defines or reassigns a value parameter. See :ref:`layfile-concepts-params` for details. element Defines an element – one of the basic objects that can be arranged in a - view. See :ref:`layout-parts-elements` for details. + view. See :ref:`layfile-parts-elements` for details. group Defines a reusable group of elements/screens that may be referenced from - views or other groups. See :ref:`layout-parts-groups` for details. + views or other groups. See :ref:`layfile-parts-groups` for details. repeat A repeating group of elements – may contain ``param``, ``element``, - ``group``, and ``repeat`` elements. See :ref:`layout-parts-repeats` for + ``group``, and ``repeat`` elements. See :ref:`layfile-parts-repeats` for details. view An arrangement of elements and/or screens that can be displayed on an output - device (a host screen/window). See :ref:`layout-parts-views` for details. + device (a host screen/window). See :ref:`layfile-parts-views` for details. script - Allows lua script to be supplied for enhanced interactive layouts. + Allows Lua script to be supplied for enhanced interactive layouts. See + :ref:`layscript` for details. -.. _layout-parts-elements: +.. _layfile-parts-elements: Elements ~~~~~~~~ @@ -400,7 +403,7 @@ multiple times within a view. An element’s appearance depends on its *state*. The state is an integer which usually comes from an I/O port field or an emulated output (see -:ref:`layout-interact-elemstate` for information on connecting an element to an +:ref:`layfile-interact-elemstate` for information on connecting an element to an emulated I/O port or output). Any component of an element may be restricted to only drawing when the element’s state is a particular value. Some components (e.g. multi-segment displays and reels) use the state directly to determine @@ -438,9 +441,9 @@ them). All components support a few common features: (The component will always be drawn if neither ``state`` nor ``statemask`` attributes are present, or if the ``statemask`` attribute’s value is zero.) * Each component may have a ``bounds`` child element specifying its position and - size (see :ref:`layout-concepts-coordinates`). If no such element is present, - the bounds default to a unit square (width and height of 1.0) with the top - left corner at (0,0). + size (see :ref:`layfile-concepts-coordinates`). If no such element is + present, the bounds default to a unit square (width and height of 1.0) with + the top left corner at (0,0). A component’s position and/or size may be animated according to the element’s state by supplying multiple ``bounds`` child elements with ``state`` @@ -457,9 +460,9 @@ them). All components support a few common features: values of two ``bounds`` child elements, the position/size will be interpolated linearly. * Each component may have a ``color`` child element specifying an RGBA colour - (see :ref:`layout-concepts-colours` for details). This can be used to control - the colour of geometric, algorithmically drawn, or textual components. For - ``image`` components, the colour of the image pixels is multiplied by the + (see :ref:`layfile-concepts-colours` for details). This can be used to + control the colour of geometric, algorithmically drawn, or textual components. + For ``image`` components, the colour of the image pixels is multiplied by the specified colour. If no such element is present, the colour defaults to opaque white. @@ -485,10 +488,14 @@ disk image Draws an image loaded from a PNG, JPEG, Windows DIB (BMP) or SVG file. The name of the file to load (including the file name extension) is supplied - using the required ``file`` attribute. Additionally, an optional - ``alphafile`` attribute may be used to specify the name of a PNG file - (including the file name extension) to load into the alpha channel of the - image. + using the ``file`` attribute. Additionally, an optional ``alphafile`` + attribute may be used to specify the name of a PNG file (including the file + name extension) to load into the alpha channel of the image. + + Alternatively, image data may be supplied in the layout file itself using a + ``data`` child element. This can be useful for supplying simple, + human-readable SVG graphics. A ``file`` attribute or ``data`` child element + must be supplied; it is an error if neither or both are supplied. If the ``alphafile`` attribute refers refers to a file, it must have the same dimensions (in pixels) as the file referred to by the ``file`` @@ -497,7 +504,8 @@ image alpha channel, with full intensity (white in a greyscale image) corresponding to fully opaque, and black corresponding to fully transparent. The ``alphafile`` attribute will be ignored if the ``file`` attribute refers - to an SVG image; it is only used in conjunction with bitmap images. + to an SVG image or the ``data`` child element contains SVG data; it is only + used in conjunction with bitmap images. The image file(s) should be placed in the same directory/archive as the layout file. Image file formats are detected by examining the content of @@ -671,7 +679,7 @@ neutral position: -.. _layout-parts-views: +.. _layfile-parts-views: Views ~~~~~ @@ -713,7 +721,7 @@ The following child elements are allowed inside a ``view`` element: bounds Sets the origin and size of the view’s internal coordinate system if - present. See :ref:`layout-concepts-coordinates` for details. If absent, + present. See :ref:`layfile-concepts-coordinates` for details. If absent, the bounds of the view are computed as the union of the bounds of all screens and elements within the view. It only makes sense to have one ``bounds`` as a direct child of a view element. Any content outside the @@ -721,18 +729,18 @@ bounds output window or screen. param Defines or reassigns a value parameter in the view’s scope. See - :ref:`layout-concepts-params` for details. + :ref:`layfile-concepts-params` for details. element - Adds an element to the view (see :ref:`layout-parts-elements`). The name of - the element to add is specified using the required ``ref`` attribute. It is - an error if no element with this name is defined in the layout file. Within - a view, elements are drawn in the order they appear in the layout file, from - front to back. See below for more details. + Adds an element to the view (see :ref:`layfile-parts-elements`). The name + of the element to add is specified using the required ``ref`` attribute. It + is an error if no element with this name is defined in the layout file. + Within a view, elements are drawn in the order they appear in the layout + file, from front to back. See below for more details. May optionally be connected to an emulated I/O port using ``inputtag`` and ``inputmask`` attributes, and/or an emulated output using a ``name`` - attribute. See :ref:`layout-interact-clickable` for details. See - :ref:`layout-interact-elemstate` for details on supplying a state value to + attribute. See :ref:`layfile-interact-clickable` for details. See + :ref:`layfile-interact-elemstate` for details on supplying a state value to the instantiated element. screen Adds an emulated screen image to the view. The screen must be identified @@ -746,14 +754,14 @@ screen May optionally be connected to an emulated I/O port using ``inputtag`` and ``inputmask`` attributes, and/or an emulated output using a ``name`` - attribute. See :ref:`layout-interact-clickable` for details. + attribute. See :ref:`layfile-interact-clickable` for details. collection Adds screens and/or items in a collection that can be shown or hidden by the - user (see :ref:`layout-parts-collections`). The name of the collection is + user (see :ref:`layfile-parts-collections`). The name of the collection is specified using the required ``name`` attribute.. There is a limit of 32 collections per view. group - Adds the content of the group to the view (see :ref:`layout-parts-groups`). + Adds the content of the group to the view (see :ref:`layfile-parts-groups`). The name of the group to add is specified using the required ``ref`` attribute. It is an error if no group with this name is defined in the layout file. See below for more details on positioning. @@ -762,9 +770,15 @@ repeat attribute. The ``count`` attribute must be a positive integer. A ``repeat`` element in a view may contain ``element``, ``screen``, ``group``, and further ``repeat`` elements, which function the same way they do when - placed in a view directly. See :ref:`layout-parts-repeats` for discussion + placed in a view directly. See :ref:`layfile-parts-repeats` for discussion on using ``repeat`` elements. +Screens (``screen`` elements) and layout elements (``element`` elements) may +have an ``id`` attribute. If present, the ``id`` attribute must not be empty, +and must be unique within a view, including screens and elements instantiated +via reusable groups and repeating blocks. Screens and layout elements with +``id`` attributes can be looked up by Lua scripts (see :ref:`layscript`). + Screens (``screen`` elements), layout elements (``element`` elements) and groups (``group`` elements) may have their orientation altered using an ``orientation`` child element. For screens, the orientation modifiers are applied in addition @@ -798,8 +812,8 @@ layout elements is alpha blending. Screens (``screen`` elements), layout elements (``element`` elements) and groups (``group`` elements) may be positioned and sized using a ``bounds`` child -element (see :ref:`layout-concepts-coordinates` for details). In the absence of -a ``bounds`` child element, screens’ and layout elements’ bounds default to a +element (see :ref:`layfile-concepts-coordinates` for details). In the absence +of a ``bounds`` child element, screens’ and layout elements’ bounds default to a unit square (origin at 0,0 and height and width both equal to 1). In the absence of a ``bounds`` child element, groups are expanded with no translation/scaling (note that groups may position screens/elements outside @@ -817,16 +831,16 @@ screen, an individual layout element, and two element groups: Screens (``screen`` elements), layout elements (``element`` elements) and groups (``group`` elements) may have a ``color`` child element (see -:ref:`layout-concepts-colours`) specifying a modifier colour. The component +:ref:`layfile-concepts-colours`) specifying a modifier colour. The component colours of the screen or layout element(s) are multiplied by this colour. Screens (``screen`` elements) and layout elements (``element`` elements) may have their colour and position/size animated by supplying multiple ``color`` and/or ``bounds`` child elements with ``state`` attributes. See -:ref:`layout-interact-itemanim` for details. +:ref:`layfile-interact-itemanim` for details. -.. _layout-parts-collections: +.. _layfile-parts-collections: Collections ~~~~~~~~~~~ @@ -862,13 +876,14 @@ to be hidden by the user: A collection creates a nested parameter scope. Any ``param`` elements inside the collection element set parameters in the local scope for the collection. -See :ref:`layout-concepts-params` for more detail on parameters. (Note that the -collection’s name and default visibility are not part of its content, and any -parameter references in the ``name`` and ``visible`` attributes themselves will -be substituted using parameter values from the collection’s parent’s scope.) +See :ref:`layfile-concepts-params` for more detail on parameters. (Note that +the collection’s name and default visibility are not part of its content, and +any parameter references in the ``name`` and ``visible`` attributes themselves +will be substituted using parameter values from the collection’s parent’s +scope.) -.. _layout-parts-groups: +.. _layfile-parts-groups: Reusable groups ~~~~~~~~~~~~~~~ @@ -902,7 +917,7 @@ instantiate – in this example, destination bounds are supplied: Group definition elements allow all the same child elements as views. Positioning and orienting screens, layout elements and nested groups works the -same way as for views. See :ref:`layout-parts-views` for details. A group may +same way as for views. See :ref:`layfile-parts-views` for details. A group may instantiate other groups, but recursive loops are not permitted. It is an error if a group directly or indirectly instantiates itself. @@ -910,7 +925,7 @@ Groups have their own internal coordinate systems. If a group definition element has no ``bounds`` element as a direct child, its bounds are computed as the union of the bounds of all the screens, layout elements and/or nested groups it instantiates. A ``bounds`` child element may be used to explicitly specify -group bounds (see :ref:`layout-concepts-coordinates` for details). Note that +group bounds (see :ref:`layfile-concepts-coordinates` for details). Note that groups’ bounds are only used for the purpose of calculating the coordinate transform when instantiating a group. A group may position screens and/or elements outside its bounds, and they will not be cropped. @@ -970,20 +985,20 @@ the group is instantiated (*not* its lexical parent, the top-level ``mamelayout`` element). Any ``param`` elements inside the group definition element set parameters in the local scope for the group instantiation. Local parameters do not persist across multiple instantiations. See -:ref:`layout-concepts-params` for more detail on parameters. (Note that the +:ref:`layfile-concepts-params` for more detail on parameters. (Note that the group’s name is not part of its content, and any parameter references in the ``name`` attribute itself will be substituted at the point where the group definition appears in the top-level ``mamelayout`` element’s scope.) -.. _layout-parts-repeats: +.. _layfile-parts-repeats: Repeating blocks ~~~~~~~~~~~~~~~~ Repeating blocks provide a concise way to generate or arrange large numbers of similar elements. Repeating blocks are generally used in conjunction with -generator parameters (see :ref:`layout-concepts-params`). Repeating blocks may +generator parameters (see :ref:`layfile-concepts-params`). Repeating blocks may be nested for more complex arrangements. Repeating blocks are created with ``repeat`` elements. Each ``repeat`` element @@ -1001,8 +1016,8 @@ elements allowed inside a ``repeat`` element depend on where it appears: A repeating block effectively repeats its contents the number of times specified by its ``count`` attribute. See the relevant sections for details on how the -child elements are used (:ref:`layout-parts`, :ref:`layout-parts-groups`, and -:ref:`layout-parts-views`). A repeating block creates a nested parameter scope +child elements are used (:ref:`layfile-parts`, :ref:`layfile-parts-groups`, and +:ref:`layfile-parts-views`). A repeating block creates a nested parameter scope inside the parameter scope of its lexical (DOM) parent element. Generating white number labels from zero to eleven named ``label_0``, @@ -1121,7 +1136,7 @@ tiles on each iteration. Rows are connected to I/O ports ``board:IN.7`` at the top to ``board.IN.0`` at the bottom. -.. _layout-interact: +.. _layfile-interact: Interactivity ------------- @@ -1135,23 +1150,23 @@ Clickable items State-dependent components Some components will be drawn differently depending on the containing element’s state. These include the dot matrix, multi-segment LED display, - simple counter and reel elements. See :ref:`layout-parts-elements` for + simple counter and reel elements. See :ref:`layfile-parts-elements` for details. Conditionally-drawn components Components may be conditionally drawn or hidden depending on the containing element’s state by supplying ``state`` and/or ``statemask`` attributes. See - :ref:`layout-parts-elements` for details. + :ref:`layfile-parts-elements` for details. Component parameter animation Components’ colour and position/size within their containing element may be animated according the element’s state by providing multiple ``color`` and/or ``bounds`` elements with ``state`` attributes. See - :ref:`layout-parts-elements` for details. + :ref:`layfile-parts-elements` for details. Item parameter animation Items’ colour and position/size within their containing view may be animated according to their animation state. -.. _layout-interact-clickable: +.. _layfile-interact-clickable: Clickable items ~~~~~~~~~~~~~~~ @@ -1161,7 +1176,7 @@ If a view item (``element`` or ``screen`` element) has ``inputtag`` and emulated system, clicking the element will activate the switch. The switch will remain active as long as the mouse button is held down and the pointer is within the item’s current bounds. (Note that the bounds may change depending on -the item’s animation state, see :ref:`layout-interact-itemanim`). +the item’s animation state, see :ref:`layfile-interact-itemanim`). The ``inputtag`` attribute specifies the tag path of an I/O port relative to the device that caused the layout file to be loaded. The ``inputmask`` attribute @@ -1185,14 +1200,14 @@ and only activates the first clickable item whose area includes the location of the mouse pointer. -.. _layout-interact-elemstate: +.. _layfile-interact-elemstate: Element state ~~~~~~~~~~~~~ A view item that instantiates an element (``element`` element) may supply a state value to the element from an emulated I/O port or output. See -:ref:`layout-parts-elements` for details on how an element’s state affects its +:ref:`layfile-parts-elements` for details on how an element’s state affects its appearance. If the ``element`` element has a ``name`` attribute, the element state value @@ -1232,7 +1247,7 @@ in the value being shifted four bits to the right). This is useful for obtaining the value of analog or positional inputs. -.. _layout-interact-itemanim: +.. _layfile-interact-itemanim: View item animation ~~~~~~~~~~~~~~~~~~~ @@ -1265,7 +1280,7 @@ An item’s animation state may be bound to an emulated output or input port by supplying an ``animate`` child element. If present, the ``animate`` element must have either an ``inputtag`` attribute or a ``name`` attribute (but not both). If the ``animate`` child element is not present, the item’s animation -state is the same as its element state (see :ref:`layout-interact-elemstate`). +state is the same as its element state (see :ref:`layfile-interact-elemstate`). If the ``animate`` child element is present and has an ``inputtag`` attribute, the item’s animation state will be taken from the value of the @@ -1330,7 +1345,7 @@ their positions: -.. _layout-errors: +.. _layfile-errors: Error handling -------------- @@ -1346,7 +1361,7 @@ Error handling screens are considered unviable and not available to the user. -.. _layout-autogen: +.. _layfile-autogen: Automatically-generated views ----------------------------- @@ -1389,7 +1404,7 @@ The following views will be automatically generated: will be displayed at physical aspect ratio, with rotation applied. -.. _layout-complay: +.. _layfile-complay: Using complay.py ---------------- @@ -1424,7 +1439,7 @@ file to check and no output file name or base variable name. For example: **python scripts/build/complay.py artwork/dino/default.lay** -.. _layout-examples: +.. _layfile-examples: Example layout files -------------------- diff --git a/docs/source/techspecs/layout_script.rst b/docs/source/techspecs/layout_script.rst new file mode 100644 index 00000000000..1428fce77a7 --- /dev/null +++ b/docs/source/techspecs/layout_script.rst @@ -0,0 +1,203 @@ +.. _layscript: + +MAME Layout Scripting +===================== + +.. contents:: :local: + + +.. _layscript-intro: + +Introduction +------------ + +MAME layout files can embed Lua script to provide enhanced functionality. +Although there’s a lot you can do with conditionally drawn components and +parameter animation, some things can only be done with scripting. MAME uses an +event-based model. Scripts can supply function that will be called after +certain events, or when certain data is required. + +Layout scripting requires the layout plugin to be enabled. For example, to run +BWB Touble Take with the Lua script in the layout enabled, you might use this +command:: + + mame64 -plugins -plugin layout v4dbltak + +If you may want to add the settings to enable the layout plugin to an INI file +to save having to enable it every time you start a system. + + +.. _layscript-examples: + +Practical examples +------------------ + +Before diving into the technical details of how it works, we’ll start with some +complete examples using Lua script to enhance layouts. + +Espial: joystick split across ports +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Take a look at the player input definitions for Espial: + +.. code-block:: C++ + + PORT_START("IN1") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 ) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_COCKTAIL + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_COCKTAIL + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY PORT_COCKTAIL + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_COCKTAIL + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_COCKTAIL + + PORT_START("IN2") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN ) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_COIN1 ) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNKNOWN ) + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_8WAY + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_8WAY + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_COCKTAIL + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON1 ) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_8WAY + +There are two joysticks, one used for both players on an upright cabinet or the +first player on a cocktail cabinet, and one used for the second player on a +cocktail cabinet. Notice that the switches for the first joystick are split +across the two I/O ports. + +There’s no layout file syntax to build element state using bits from multiple +I/O ports. It’s also inconvenient if each joystick needs to be defined as a +separate element because the bits for the switches aren’t arranged the same +way. + +We can overcome these limitations using a script to read the player inputs and +set the items’ element state: + +.. code-block:: XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +The layout has a ``script`` element containing the Lua script. This is called +as a function by the layout plugin when the layout file is loaded. The layout +views have been built at this point, but the emulated system has not finished +starting. In particular, it’s not safe to access inputs and outputs at this +time. The key variable in the script environment is ``file``, which gives the +script access its layout file. + +We supply a function to be called after tags in the layout file have been +resolved. At this point, the emulated system will have completed starting. +This function does the following tasks + +* Looks up the two I/O ports used for player input. I/O ports can be looked up + by tag relative to the device that caused the layout file to be loaded. +* Looks up the two view items used to display joystick state. Views can be + looked up by name (i.e. value of the ``name`` attribute), and items within a + view can be looked up by ID (i.e. the value of the ``id`` attribute). +* Supplies a function to be called before view items are added to the render + target. +* Hides the warning that reminds the user to enable the layout plugin by setting + the element state for the item to 0 (the text component is only drawn when + the element state is 1). + +The function called before view items are added to the render target reads the +player inputs, and shuffle the bits into the order needed by the joystick +element. diff --git a/plugins/autofire/autofire_menu.lua b/plugins/autofire/autofire_menu.lua index a880d1c5008..4744de1b2ac 100644 --- a/plugins/autofire/autofire_menu.lua +++ b/plugins/autofire/autofire_menu.lua @@ -108,26 +108,27 @@ end -- Borrowed from the cheat plugin local function poll_for_hotkey() local input = manager:machine():input() + local poller = input:sequence_poller() manager:machine():popmessage(_('Press button for hotkey or wait to leave unchanged')) manager:machine():video():frame_update(true) - input:seq_poll_start('switch') + poller:start('switch') local time = os.clock() local clearmsg = true - while (not input:seq_poll()) and (input:seq_poll_modified() or (os.clock() < time + 1)) do - if input:seq_poll_modified() then - if not input:seq_poll_valid() then + while (not poller:poll()) and (poller.modified or (os.clock() < time + 1)) do + if poller.modified then + if not poller.valid then manager:machine():popmessage(_("Invalid sequence entered")) clearmsg = false break end - manager:machine():popmessage(input:seq_name(input:seq_poll_sequence())) + manager:machine():popmessage(input:seq_name(poller.sequence)) manager:machine():video():frame_update(true) end end if clearmsg then manager:machine():popmessage() end - return input:seq_poll_valid() and input:seq_poll_final() or nil + return poller.valid and poller.sequence or nil end local function handle_configure_menu(index, event) diff --git a/plugins/cheat/init.lua b/plugins/cheat/init.lua index 3fa625fb4d3..970e7f3a848 100644 --- a/plugins/cheat/init.lua +++ b/plugins/cheat/init.lua @@ -610,24 +610,25 @@ function cheat.startplugin() local function hkcbfunc(cheat) local input = manager:machine():input() + local poller = input:sequence_poller() manager:machine():popmessage(_("Press button for hotkey or wait to clear")) manager:machine():video():frame_update(true) - input:seq_poll_start("switch") + poller:start("switch") local time = os.clock() local clearmsg = true - while (not input:seq_poll()) and (input.seq_poll_modified() or (os.clock() < time + 1)) do - if input:seq_poll_modified() then - if not input:seq_poll_valid() then + while (not poller:poll()) and (poller.modified or (os.clock() < time + 1)) do + if poller.modified then + if not poller.valid then manager:machine():popmessage(_("Invalid sequence entered")) clearmsg = false break end - manager:machine():popmessage(input:seq_name(input:seq_poll_sequence())) + manager:machine():popmessage(input:seq_name(poller.sequence)) manager:machine():video():frame_update(true) end end - if input:seq_poll_valid() then - cheat.hotkeys = {pressed = false, keys = input:seq_poll_final()} + if poller.valid then + cheat.hotkeys = { pressed = false, keys = poller.sequence } else cheat.hotkeys = nil end @@ -748,7 +749,8 @@ function cheat.startplugin() return chg else if not cheat.is_oneshot then - return cheat:set_enabled(false) + local state, chg = cheat:set_enabled(false) + return chg end return false end diff --git a/plugins/cheatfind/init.lua b/plugins/cheatfind/init.lua index 83e00e4d51a..e6a359f6564 100644 --- a/plugins/cheatfind/init.lua +++ b/plugins/cheatfind/init.lua @@ -1044,7 +1044,7 @@ function cheatfind.startplugin() end emu.register_menu(menu_callback, menu_populate, _("Cheat Finder")) emu.register_frame_done(function () - local tag, screen = next(manager:machine().screens) + local screen = manager:machine().screens:at(1) local height = mame_manager:ui():get_line_height() for num, watch in ipairs(watches) do screen:draw_text("left", num * height, string.format(watch.format, watch.addr, watch.func())) diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index e4da280e221..f069497bf6d 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -378,8 +378,6 @@ bool lua_engine::menu_callback(const std::string &menu, int index, const std::st void lua_engine::set_machine(running_machine *machine) { - if (!machine || (machine != m_machine)) - m_seq_poll.reset(); m_machine = machine; } diff --git a/src/frontend/mame/luaengine.h b/src/frontend/mame/luaengine.h index 14d47fbf89c..bfcbaf04a50 100644 --- a/src/frontend/mame/luaengine.h +++ b/src/frontend/mame/luaengine.h @@ -12,8 +12,6 @@ #pragma once -#include "iptseqpoll.h" - #include #include #include @@ -35,10 +33,10 @@ class lua_engine public: // helper structures template struct devenum; - template struct immutable_container_helper; - + template struct simple_list_wrapper; template struct tag_object_ptr_map; template using standard_tag_object_ptr_map = tag_object_ptr_map > >; + template struct immutable_container_helper; // construction/destruction lua_engine(); @@ -146,7 +144,6 @@ private: lua_State *m_lua_state; std::unique_ptr m_sol_state; running_machine *m_machine; - std::unique_ptr m_seq_poll; std::vector m_menu; diff --git a/src/frontend/mame/luaengine.ipp b/src/frontend/mame/luaengine.ipp index 9e5dddc8065..4cd4112bee5 100644 --- a/src/frontend/mame/luaengine.ipp +++ b/src/frontend/mame/luaengine.ipp @@ -18,6 +18,15 @@ +template +struct lua_engine::simple_list_wrapper +{ + simple_list_wrapper(simple_list const &l) : list(l) { } + + simple_list const &list; +}; + + template struct lua_engine::tag_object_ptr_map { @@ -75,15 +84,83 @@ template <> struct is_container : std::false_type { }; sol::buffer *sol_lua_get(sol::types, lua_State *L, int index, sol::stack::record &tracking); int sol_lua_push(sol::types, lua_State *L, buffer *value); -// lua_engine::devenum customisation + +// these things should be treated as containers template struct is_container > : std::true_type { }; +template struct is_container > : std::true_type { }; +template struct is_container > : std::true_type { }; + + template struct usertype_container >; -// tag_object_ptr_map is_container -template struct is_container > : std::true_type { }; +template +struct usertype_container > : lua_engine::immutable_container_helper, simple_list const, typename simple_list::auto_iterator> +{ +private: + static int next_pairs(lua_State *L) + { + typename usertype_container::indexed_iterator &i(stack::unqualified_get >(L, 1)); + if (i.src.end() == i.it) + return stack::push(L, lua_nil); + int result; + result = stack::push(L, i.ix + 1); + result += stack::push_reference(L, *i.it); + ++i; + return result; + } + +public: + static int at(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + std::ptrdiff_t const index(stack::unqualified_get(L, 2)); + if ((0 >= index) || (self.list.count() < index)) + return stack::push(L, lua_nil); + else + return stack::push_reference(L, *self.list.find(index - 1)); + } + + static int get(lua_State *L) { return at(L); } + static int index_get(lua_State *L) { return at(L); } + + static int index_of(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + T &target(stack::unqualified_get(L, 2)); + int const found(self.list.indexof(target)); + if (0 > found) + return stack::push(L, lua_nil); + else + return stack::push(L, found + 1); + } + + static int size(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + return stack::push(L, self.list.count()); + } + + static int empty(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + return stack::push(L, self.list.empty()); + } + + static int next(lua_State *L) { return stack::push(L, next_pairs); } + static int pairs(lua_State *L) { return ipairs(L); } + + static int ipairs(lua_State *L) + { + lua_engine::simple_list_wrapper &self(usertype_container::get_self(L)); + stack::push(L, next_pairs); + stack::push >(L, self.list, self.list.begin()); + stack::push(L, lua_nil); + return 3; + } +}; + -// tag_object_ptr_map usertype_container template struct usertype_container > : lua_engine::immutable_container_helper, T const, typename T::const_iterator> { diff --git a/src/frontend/mame/luaengine_input.cpp b/src/frontend/mame/luaengine_input.cpp index e135678a840..327f58e8a4f 100644 --- a/src/frontend/mame/luaengine_input.cpp +++ b/src/frontend/mame/luaengine_input.cpp @@ -11,11 +11,140 @@ #include "emu.h" #include "luaengine.ipp" +#include "iptseqpoll.h" + #include "inputdev.h" #include "natkeyboard.h" #include "render.h" #include "uiinput.h" +#include + + +namespace { + +struct natkbd_kbd_dev +{ + natkbd_kbd_dev(natural_keyboard &m, std::size_t i) : manager(m), index(i) { } + + natural_keyboard &manager; + std::size_t index; +}; + + +struct natkbd_kbd_list +{ + natkbd_kbd_list(natural_keyboard &m) : manager(m) { } + + natural_keyboard &manager; +}; + +} // anonymous namespace + + +namespace sol { + +template <> struct is_container : std::true_type { }; + + +template <> +struct usertype_container +{ +private: + static natkbd_kbd_list &get_self(lua_State *L) + { + auto p(sol::stack::unqualified_check_get(L, 1)); + if (!p) + luaL_error(L, "sol: 'self' is not of type 'natkbd_kbd_list' (pass 'self' as first argument with ':' or call on proper type)"); + if (!*p) + luaL_error(L, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a 'natkbd_kbd_list' type"); + return **p; + } + + template + static int next_pairs(lua_State *L) + { + natkbd_kbd_dev &i(stack::unqualified_get >(L, 1)); + if (i.manager.keyboard_count() <= i.index) + return stack::push(L, lua_nil); + int result; + if constexpr (Indexed) + result = stack::push(L, i.index + 1); + else + result = stack::push(L, i.manager.keyboard_device(i.index).tag()); + result += stack::push(L, i); + ++i.index; + return result; + } + + template + static int start_pairs(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + stack::push(L, next_pairs); + stack::push >(L, self.manager, 0); + stack::push(L, lua_nil); + return 3; + } + +public: + static int at(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + std::ptrdiff_t const index(stack::unqualified_get(L, 2)); + if ((0 < index) && (self.manager.keyboard_count() >= index)) + return stack::push(L, natkbd_kbd_dev(self.manager, index - 1)); + else + return stack::push(L, lua_nil); + } + + static int get(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + char const *const tag(stack::unqualified_get(L)); + for (std::size_t i = 0; self.manager.keyboard_count() > i; ++i) + { + if (!std::strcmp(self.manager.keyboard_device(i).tag(), tag)) + return stack::push(L, natkbd_kbd_dev(self.manager, i)); + } + return stack::push(L, lua_nil); + } + + static int index_get(lua_State *L) + { + return get(L); + } + + static int size(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + return stack::push(L, self.manager.keyboard_count()); + } + + static int empty(lua_State *L) + { + natkbd_kbd_list &self(get_self(L)); + return stack::push(L, !self.manager.keyboard_count()); + } + + // produce errors for unsupported operations + static int set(lua_State *L) { return luaL_error(L, "sol: cannot call 'set(key, value)' on type 'natkbd_kbd_list': container is not modifiable"); } + static int index_set(lua_State *L) { return luaL_error(L, "sol: cannot call 'container[key] = value' on type 'natkbd_kbd_list': container is not modifiable"); } + static int add(lua_State *L) { return luaL_error(L, "sol: cannot call 'add' on type 'natkbd_kbd_list': container is not modifiable"); } + static int insert(lua_State *L) { return luaL_error(L, "sol: cannot call 'insert' on type 'natkbd_kbd_list': container is not modifiable"); } + static int find(lua_State *L) { return luaL_error(L, "sol: cannot call 'find' on type 'natkbd_kbd_list': no supported comparison operator for the value type"); } + static int index_of(lua_State *L) { return luaL_error(L, "sol: cannot call 'index_of' on type 'natkbd_kbd_list': no supported comparison operator for the value type"); } + static int clear(lua_State *L) { return luaL_error(L, "sol: cannot call 'clear' on type 'natkbd_kbd_list': container is not modifiable"); } + static int erase(lua_State *L) { return luaL_error(L, "sol: cannot call 'erase' on type 'natkbd_kbd_list': container is not modifiable"); } + + // support for iteration with pairs and ipairs + static int next(lua_State *L) { return stack::push(L, next_pairs); } + static int pairs(lua_State *L) { return start_pairs(L); } + static int ipairs(lua_State *L) { return start_pairs(L); } +}; + +} // namespace sol + //------------------------------------------------- // initialize_input - register input user types @@ -41,86 +170,123 @@ void lua_engine::initialize_input(sol::table &emu) * ioport:type_seq(type, player, seqtype) - get input sequence for ioport type/player * * ioport.ports[] - ioports table (k=tag, v=ioport_port) + * ioport.natkeyboard - get natural keyboard manager */ - auto ioport_manager_type = sol().registry().new_usertype("ioport", "new", sol::no_constructor); - ioport_manager_type.set("count_players", &ioport_manager::count_players); - ioport_manager_type.set("natkeyboard", &ioport_manager::natkeyboard); - ioport_manager_type.set("type_group", [](ioport_manager &im, ioport_type type, int player) { - return im.type_group(type, player); - }); - ioport_manager_type.set("ports", sol::property([this](ioport_manager &im) { - sol::table port_table = sol().create_table(); - for (auto &port : im.ports()) - port_table[port.second->tag()] = port.second.get(); - return port_table; - })); - ioport_manager_type.set("type_seq", [](ioport_manager &m, ioport_type type, int player, input_seq_type seqtype) { - return m.type_seq(type, player, seqtype); - }); + auto ioport_manager_type = sol().registry().new_usertype("ioport", sol::no_constructor); + ioport_manager_type["count_players"] = &ioport_manager::count_players; + ioport_manager_type["type_group"] = &ioport_manager::type_group; + ioport_manager_type["type_seq"] = &ioport_manager::type_seq; + ioport_manager_type["ports"] = sol::property([] (ioport_manager &im) { return tag_object_ptr_map(im.ports()); }); + ioport_manager_type["natkeyboard"] = sol::property(&ioport_manager::natkeyboard); /* natural_keyboard library * - * manager:machine():ioport():natkeyboard() + * manager:machine():ioport().natkeyboard * - * natkeyboard:paste() - paste clipboard data - * natkeyboard:post() - post data to natural keyboard - * natkeyboard:post_coded() - post data to natural keyboard + * natkeyboard:post(text) - post data to natural keyboard + * natkeyboard:post_coded(text) - post data to natural keyboard + * natkeyboard:paste() - paste host clipboard text + * natkeyboard:dump() - returns human-readable description of character mappings * * natkeyboard.empty - is the natural keyboard buffer empty? - * natkeyboard.in_use - is the natural keyboard in use? + * natkeyboard.full - is the natural keyboard buffer full? + * natkeyboard.can_post - does the system support posting characters via natural keyboard? + * natkeyboard.is_posting - is a post operation currently in progress? + * natkeyboard.in_use - is natural keyboard mode enabled (read/write)? + * natkeyboard.keyboards[] - get keyboard devices in system (k=tag, v=natkbd_kbd_dev) */ - auto natkeyboard_type = sol().registry().new_usertype("natkeyboard", "new", sol::no_constructor); - natkeyboard_type.set("empty", sol::property(&natural_keyboard::empty)); - natkeyboard_type.set("in_use", sol::property(&natural_keyboard::in_use, &natural_keyboard::set_in_use)); - natkeyboard_type.set("paste", &natural_keyboard::paste); - natkeyboard_type.set("post", [](natural_keyboard &nat, const std::string &text) { nat.post_utf8(text); }); - natkeyboard_type.set("post_coded", [](natural_keyboard &nat, const std::string &text) { nat.post_coded(text); }); + auto natkeyboard_type = sol().registry().new_usertype("natkeyboard", sol::no_constructor); + natkeyboard_type["post"] = [] (natural_keyboard &nat, std::string const &text) { nat.post_utf8(text); }; + natkeyboard_type["post_coded"] = [] (natural_keyboard &nat, std::string const &text) { nat.post_coded(text); }; + natkeyboard_type["paste"] = &natural_keyboard::paste; + natkeyboard_type["dump"] = static_cast(&natural_keyboard::dump); + natkeyboard_type["empty"] = sol::property(&natural_keyboard::empty); + natkeyboard_type["full"] = sol::property(&natural_keyboard::full); + natkeyboard_type["can_post"] = sol::property(&natural_keyboard::can_post); + natkeyboard_type["is_posting"] = sol::property(&natural_keyboard::is_posting); + natkeyboard_type["in_use"] = sol::property(&natural_keyboard::in_use, &natural_keyboard::set_in_use); + natkeyboard_type["keyboards"] = sol::property([] (natural_keyboard &nat) { return natkbd_kbd_list(nat); }); + + +/* natkbd_kbd_dev library + * + * manager:machine():ioport().natkeyboard.keyboards[tag] + * + * keyboard.device - underlying device that the inputs belong to + * keyboard.tag - absolute tag of the device + * keyboard.basetag - last component of the device tag ("root" for root device) + * keyboard.name - device type full name + * keyboard.shortname - device type short name + * keyboard.is_keypad - does the device have keypad inputs but no keyboard inputs? + * keyboard.enabled - are the device's keyboard/keypad inputs enabled (read/write)? + */ + + auto natkbddev_type = sol().registry().new_usertype("natkeyboard_device", sol::no_constructor); + natkbddev_type["device"] = sol::property([] (natkbd_kbd_dev const &kbd) -> device_t & { return kbd.manager.keyboard_device(kbd.index); }); + natkbddev_type["tag"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).tag(); }); + natkbddev_type["basetag"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).basetag(); }); + natkbddev_type["name"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).name(); }); + natkbddev_type["shortname"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_device(kbd.index).shortname(); }); + natkbddev_type["is_keypad"] = sol::property([] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_is_keypad(kbd.index); }); + natkbddev_type["enabled"] = sol::property( + [] (natkbd_kbd_dev const &kbd) { return kbd.manager.keyboard_enabled(kbd.index); }, + [] (natkbd_kbd_dev &kbd, bool enable) + { + if (enable) + kbd.manager.enable_keyboard(kbd.index); + else + kbd.manager.disable_keyboard(kbd.index); + }); /* ioport_port library * * manager:machine():ioport().ports[port_tag] * - * port:tag() - get port tag - * port:active() - get port status - * port:live() - get port ioport_port_live (TODO: not usable from lua as of now) * port:read() - get port value * port:write(val, mask) - set port to value & mask (output fields only, for other fields use field:set_value(val)) * port:field(mask) - get ioport_field for port and mask * + * port.device - get device that the port belongs to + * port.tag - get port tag + * port.active - get port status + * port.live - get port ioport_port_live (TODO: not usable from lua as of now) * port.fields[] - get ioport_field table (k=name, v=ioport_field) */ auto ioport_port_type = sol().registry().new_usertype("ioport_port", "new", sol::no_constructor); - ioport_port_type.set("tag", &ioport_port::tag); - ioport_port_type.set("active", &ioport_port::active); - ioport_port_type.set("live", &ioport_port::live); - ioport_port_type.set("read", &ioport_port::read); - ioport_port_type.set("write", &ioport_port::write); - ioport_port_type.set("field", &ioport_port::field); - ioport_port_type.set("fields", sol::property([this](ioport_port &p){ - sol::table f_table = sol().create_table(); - // parse twice for custom and default names, default has priority - for(ioport_field &field : p.fields()) + ioport_port_type["read"] = &ioport_port::read; + ioport_port_type["write"] = &ioport_port::write; + ioport_port_type["field"] = &ioport_port::field; + ioport_port_type["device"] = sol::property(&ioport_port::device); + ioport_port_type["tag"] = sol::property(&ioport_port::tag); + ioport_port_type["active"] = sol::property(&ioport_port::active); + ioport_port_type["live"] = sol::property(&ioport_port::live); + ioport_port_type["fields"] = sol::property( + [this] (ioport_port &p) { - if (field.type_class() != INPUT_CLASS_INTERNAL) - f_table[field.name()] = &field; - } - for(ioport_field &field : p.fields()) - { - if (field.type_class() != INPUT_CLASS_INTERNAL) + sol::table f_table = sol().create_table(); + // parse twice for custom and default names, default has priority + for (ioport_field &field : p.fields()) { - if(field.specific_name()) - f_table[field.specific_name()] = &field; - else - f_table[field.manager().type_name(field.type(), field.player())] = &field; + if (field.type_class() != INPUT_CLASS_INTERNAL) + f_table[field.name()] = &field; } - } - return f_table; - })); + for (ioport_field &field : p.fields()) + { + if (field.type_class() != INPUT_CLASS_INTERNAL) + { + if (field.specific_name()) + f_table[field.specific_name()] = &field; + else + f_table[field.manager().type_name(field.type(), field.player())] = &field; + } + } + return f_table; + }); /* ioport_field library @@ -165,92 +331,110 @@ void lua_engine::initialize_input(sol::table &emu) * field.settings[] - ioport_setting table (k=value, v=name) */ - auto ioport_field_type = sol().registry().new_usertype("ioport_field", "new", sol::no_constructor); - ioport_field_type.set("set_value", &ioport_field::set_value); - ioport_field_type.set("set_input_seq", [](ioport_field &f, const std::string &seq_type_string, const input_seq &seq) { + auto ioport_field_type = sol().registry().new_usertype("ioport_field", sol::no_constructor); + ioport_field_type["set_value"] = &ioport_field::set_value; + ioport_field_type["set_input_seq"] = + [] (ioport_field &f, std::string const &seq_type_string, const input_seq &seq) + { input_seq_type seq_type = s_seq_type_parser(seq_type_string); ioport_field::user_settings settings; f.get_user_settings(settings); settings.seq[seq_type] = seq; f.set_user_settings(settings); - }); - ioport_field_type.set("input_seq", [](ioport_field &f, const std::string &seq_type_string) { + }; + ioport_field_type["input_seq"] = + [] (ioport_field &f, std::string const &seq_type_string) + { input_seq_type seq_type = s_seq_type_parser(seq_type_string); return f.seq(seq_type); - }); - ioport_field_type.set("set_default_input_seq", [](ioport_field &f, const std::string &seq_type_string, const input_seq &seq) { + }; + ioport_field_type["set_default_input_seq"] = + [] (ioport_field &f, std::string const &seq_type_string, input_seq const &seq) + { input_seq_type seq_type = s_seq_type_parser(seq_type_string); f.set_defseq(seq_type, seq); - }); - ioport_field_type.set("default_input_seq", [](ioport_field &f, const std::string &seq_type_string) { + }; + ioport_field_type["default_input_seq"] = + [] (ioport_field &f, const std::string &seq_type_string) + { input_seq_type seq_type = s_seq_type_parser(seq_type_string); return f.defseq(seq_type); - }); - ioport_field_type.set("keyboard_codes", [this](ioport_field &f, int which) { + }; + ioport_field_type["keyboard_codes"] = + [this] (ioport_field &f, int which) + { sol::table result = sol().create_table(); int index = 1; for (char32_t code : f.keyboard_codes(which)) result[index++] = code; return result; - }); - ioport_field_type.set("device", sol::property(&ioport_field::device)); - ioport_field_type.set("port", sol::property(&ioport_field::port)); - ioport_field_type.set("name", sol::property(&ioport_field::name)); - ioport_field_type.set("default_name", sol::property([](ioport_field &f) { - return f.specific_name() ? f.specific_name() : f.manager().type_name(f.type(), f.player()); - })); - ioport_field_type.set("player", sol::property(&ioport_field::player, &ioport_field::set_player)); - ioport_field_type.set("mask", sol::property(&ioport_field::mask)); - ioport_field_type.set("defvalue", sol::property(&ioport_field::defvalue)); - ioport_field_type.set("sensitivity", sol::property(&ioport_field::sensitivity)); - ioport_field_type.set("way", sol::property(&ioport_field::way)); - ioport_field_type.set("type_class", sol::property([](ioport_field &f) { - switch (f.type_class()) + }; + ioport_field_type["device"] = sol::property(&ioport_field::device); + ioport_field_type["port"] = sol::property(&ioport_field::port); + ioport_field_type["name"] = sol::property(&ioport_field::name); + ioport_field_type["default_name"] = sol::property( + [] (ioport_field &f) { - case INPUT_CLASS_KEYBOARD: return "keyboard"; - case INPUT_CLASS_CONTROLLER: return "controller"; - case INPUT_CLASS_CONFIG: return "config"; - case INPUT_CLASS_DIPSWITCH: return "dipswitch"; - case INPUT_CLASS_MISC: return "misc"; - default: break; - } - throw false; - })); - ioport_field_type.set("is_analog", sol::property(&ioport_field::is_analog)); - ioport_field_type.set("is_digital_joystick", sol::property(&ioport_field::is_digital_joystick)); - ioport_field_type.set("enabled", sol::property(&ioport_field::enabled)); - ioport_field_type.set("optional", sol::property(&ioport_field::optional)); - ioport_field_type.set("cocktail", sol::property(&ioport_field::cocktail)); - ioport_field_type.set("toggle", sol::property(&ioport_field::toggle)); - ioport_field_type.set("rotated", sol::property(&ioport_field::rotated)); - ioport_field_type.set("analog_reverse", sol::property(&ioport_field::analog_reverse)); - ioport_field_type.set("analog_reset", sol::property(&ioport_field::analog_reset)); - ioport_field_type.set("analog_wraps", sol::property(&ioport_field::analog_wraps)); - ioport_field_type.set("analog_invert", sol::property(&ioport_field::analog_invert)); - ioport_field_type.set("impulse", sol::property(&ioport_field::impulse)); - ioport_field_type.set("type", sol::property(&ioport_field::type)); - ioport_field_type.set("live", sol::property(&ioport_field::live)); - ioport_field_type.set("crosshair_scale", sol::property(&ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale)); - ioport_field_type.set("crosshair_offset", sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset)); - ioport_field_type.set("user_value", sol::property( - [](ioport_field &f) { - ioport_field::user_settings settings; - f.get_user_settings(settings); - return settings.value; - }, - [](ioport_field &f, ioport_value val) { - ioport_field::user_settings settings; - f.get_user_settings(settings); - settings.value = val; - f.set_user_settings(settings); - })); - ioport_field_type.set("settings", sol::property([this](ioport_field &f) { - sol::table result = sol().create_table(); - for (ioport_setting &setting : f.settings()) - if (setting.enabled()) - result[setting.value()] = setting.name(); - return result; - })); + return f.specific_name() ? f.specific_name() : f.manager().type_name(f.type(), f.player()); + }); + ioport_field_type["player"] = sol::property(&ioport_field::player, &ioport_field::set_player); + ioport_field_type["mask"] = sol::property(&ioport_field::mask); + ioport_field_type["defvalue"] = sol::property(&ioport_field::defvalue); + ioport_field_type["sensitivity"] = sol::property(&ioport_field::sensitivity); + ioport_field_type["way"] = sol::property(&ioport_field::way); + ioport_field_type["type_class"] = sol::property( + [] (ioport_field &f) + { + switch (f.type_class()) + { + case INPUT_CLASS_KEYBOARD: return "keyboard"; + case INPUT_CLASS_CONTROLLER: return "controller"; + case INPUT_CLASS_CONFIG: return "config"; + case INPUT_CLASS_DIPSWITCH: return "dipswitch"; + case INPUT_CLASS_MISC: return "misc"; + default: break; + } + throw false; + }); + ioport_field_type["is_analog"] = sol::property(&ioport_field::is_analog); + ioport_field_type["is_digital_joystick"] = sol::property(&ioport_field::is_digital_joystick); + ioport_field_type["enabled"] = sol::property(&ioport_field::enabled); + ioport_field_type["optional"] = sol::property(&ioport_field::optional); + ioport_field_type["cocktail"] = sol::property(&ioport_field::cocktail); + ioport_field_type["toggle"] = sol::property(&ioport_field::toggle); + ioport_field_type["rotated"] = sol::property(&ioport_field::rotated); + ioport_field_type["analog_reverse"] = sol::property(&ioport_field::analog_reverse); + ioport_field_type["analog_reset"] = sol::property(&ioport_field::analog_reset); + ioport_field_type["analog_wraps"] = sol::property(&ioport_field::analog_wraps); + ioport_field_type["analog_invert"] = sol::property(&ioport_field::analog_invert); + ioport_field_type["impulse"] = sol::property(&ioport_field::impulse); + ioport_field_type["type"] = sol::property(&ioport_field::type); + ioport_field_type["live"] = sol::property(&ioport_field::live); + ioport_field_type["crosshair_scale"] = sol::property(&ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale); + ioport_field_type["crosshair_offset"] = sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset); + ioport_field_type["user_value"] = sol::property( + [] (ioport_field &f) + { + ioport_field::user_settings settings; + f.get_user_settings(settings); + return settings.value; + }, + [] (ioport_field &f, ioport_value val) + { + ioport_field::user_settings settings; + f.get_user_settings(settings); + settings.value = val; + f.set_user_settings(settings); + }); + ioport_field_type["settings"] = sol::property( + [this] (ioport_field &f) + { + sol::table result = sol().create_table(); + for (ioport_setting &setting : f.settings()) + if (setting.enabled()) + result[setting.value()] = setting.name(); + return result; + }); /* ioport_field_live library @@ -260,44 +444,79 @@ void lua_engine::initialize_input(sol::table &emu) * live.name */ - sol().registry().new_usertype("ioport_field_live", "new", sol::no_constructor, - "name", &ioport_field_live::name); + auto ioport_field_live_type = sol().registry().new_usertype("ioport_field_live", sol::no_constructor); + ioport_field_live_type["name"] = &ioport_field_live::name; /* input_manager library * * manager:machine():input() * - * input:code_from_token(token) - get input_code for KEYCODE_* string token + * input:code_value(code) - * input:code_pressed(code) - get pressed state for input_code - * input:code_to_token(code) - get KEYCODE_* string token for code + * input:code_pressed_once(code) - * input:code_name(code) - get code friendly name - * input:seq_from_tokens(tokens) - get input_seq for multiple space separated KEYCODE_* string tokens + * input:code_to_token(code) - get KEYCODE_* string token for code + * input:code_from_token(token) - get input_code for KEYCODE_* string token * input:seq_pressed(seq) - get pressed state for input_seq - * input:seq_to_tokens(seq) - get KEYCODE_* string tokens for seq - * input:seq_name(seq) - get seq friendly name * input:seq_clean(seq) - clean the seq and remove invalid elements - * input:seq_poll_start(class, [opt] start_seq) - start polling for input_item_class passed as string - * (switch/abs[olute]/rel[ative]/max[imum]) - * input:seq_poll() - poll once, returns true if input was fetched - * input:seq_poll_final() - get final input_seq - * input.device_classes - returns device classes + * input:seq_name(seq) - get seq friendly name + * input:seq_to_tokens(seq) - get KEYCODE_* string tokens for seq + * input:seq_from_tokens(tokens) - get input_seq for multiple space separated KEYCODE_* string tokens + * input:sequence_poller() - get an input sequence poller + * + * input.device_classes[] - returns device classes (k=name, v=input_device_class) */ - auto input_type = sol().registry().new_usertype("input", "new", sol::no_constructor); - input_type.set("code_from_token", [](input_manager &input, const char *token) { return input.code_from_token(token); }); - input_type.set("code_pressed", [](input_manager &input, const input_code &code) { return input.code_pressed(code); }); - input_type.set("code_to_token", [](input_manager &input, const input_code &code) { return input.code_to_token(code); }); - input_type.set("code_name", [](input_manager &input, const input_code &code) { return input.code_name(code); }); - input_type.set("seq_from_tokens", [](input_manager &input, const char *tokens) { input_seq seq; input.seq_from_tokens(seq, tokens); return seq; }); - input_type.set("seq_pressed", [](input_manager &input, const input_seq &seq) { return input.seq_pressed(seq); }); - input_type.set("seq_to_tokens", [](input_manager &input, const input_seq &seq) { return input.seq_to_tokens(seq); }); - input_type.set("seq_name", [](input_manager &input, const input_seq &seq) { return input.seq_name(seq); }); - input_type.set("seq_clean", [](input_manager &input, const input_seq &seq) { return input.seq_clean(seq); }); - input_type.set("seq_poll_start", [this](input_manager &input, const char *cls_string, sol::object seq) { - if (!m_seq_poll) - m_seq_poll.reset(new input_sequence_poller(input)); + auto input_type = sol().registry().new_usertype("input", sol::no_constructor); + input_type["code_value"] = &input_manager::code_value; + input_type["code_pressed"] = &input_manager::code_pressed; + input_type["code_pressed_once"] = &input_manager::code_pressed_once; + input_type["code_name"] = &input_manager::code_name; + input_type["code_to_token"] = &input_manager::code_to_token; + input_type["code_from_token"] = &input_manager::code_from_token; + input_type["seq_pressed"] = &input_manager::seq_pressed; + input_type["seq_clean"] = &input_manager::seq_clean; + input_type["seq_name"] = &input_manager::seq_name; + input_type["seq_to_tokens"] = &input_manager::seq_to_tokens; + input_type["seq_from_tokens"] = + [] (input_manager &input, const char *tokens) + { + input_seq seq; + input.seq_from_tokens(seq, tokens); + return seq; + }; + input_type["sequence_poller"] = [] (input_manager &input) { return input_sequence_poller(input); }; + input_type["device_classes"] = sol::property( + [this] (input_manager &input) + { + sol::table result = sol().create_table(); + for (input_device_class devclass_id = DEVICE_CLASS_FIRST_VALID; devclass_id <= DEVICE_CLASS_LAST_VALID; devclass_id++) + { + input_class &devclass = input.device_class(devclass_id); + result[devclass.name()] = &devclass; + } + return result; + }); + +/* input_sequence_poller library + * + * manager:machine():input():seq_poll() + * + * poller:start(class, [opt] start_seq) - start polling for input_item_class passed as string + * (switch/abs[olute]/rel[ative]/max[imum]) + * poller:poll() - poll once, returns true if input was fetched + * + * poller.sequence - get current input_seq + * poller.valid - true if input sequence is valid + * poller.modified - true if input sequence was modified + */ + + auto seqpoll_type = sol().registry().new_usertype("input_seq_poller", sol::no_constructor); + seqpoll_type["start"] = + [this] (input_sequence_poller &poller, char const *cls_string, sol::object seq) + { input_item_class cls; if (!strcmp(cls_string, "switch")) cls = ITEM_CLASS_SWITCH; @@ -311,44 +530,14 @@ void lua_engine::initialize_input(sol::table &emu) cls = ITEM_CLASS_INVALID; if (seq.is>()) - m_seq_poll->start(cls, seq.as>()); + poller.start(cls, seq.as()); else - m_seq_poll->start(cls); - }); - input_type.set("seq_poll", [this](input_manager &input) -> sol::object { - if (!m_seq_poll) - return sol::make_object(sol(), sol::lua_nil); - return sol::make_object(sol(), m_seq_poll->poll()); - }); - input_type.set("seq_poll_final", [this](input_manager &input) -> sol::object { - if (!m_seq_poll) - return sol::make_object(sol(), sol::lua_nil); - return sol::make_object(sol(), m_seq_poll->valid() ? m_seq_poll->sequence() : input_seq()); - }); - input_type.set("seq_poll_modified", [this](input_manager &input) -> sol::object { - if (!m_seq_poll) - return sol::make_object(sol(), sol::lua_nil); - return sol::make_object(sol(), m_seq_poll->modified()); - }); - input_type.set("seq_poll_valid", [this](input_manager &input) -> sol::object { - if (!m_seq_poll) - return sol::make_object(sol(), sol::lua_nil); - return sol::make_object(sol(), m_seq_poll->valid()); - }); - input_type.set("seq_poll_sequence", [this](input_manager &input) -> sol::object { - if (!m_seq_poll) - return sol::make_object(sol(), sol::lua_nil); - return sol::make_object(sol(), m_seq_poll->sequence()); - }); - input_type.set("device_classes", sol::property([this](input_manager &input) { - sol::table result = sol().create_table(); - for (input_device_class devclass_id = DEVICE_CLASS_FIRST_VALID; devclass_id <= DEVICE_CLASS_LAST_VALID; devclass_id++) - { - input_class &devclass = input.device_class(devclass_id); - result[devclass.name()] = &devclass; - } - return result; - })); + poller.start(cls); + }; + seqpoll_type["poll"] = &input_sequence_poller::poll; + seqpoll_type["sequence"] = sol::property(&input_sequence_poller::sequence); + seqpoll_type["valid"] = sol::property(&input_sequence_poller::valid); + seqpoll_type["modified"] = sol::property(&input_sequence_poller::modified); /* input_class library @@ -362,61 +551,67 @@ void lua_engine::initialize_input(sol::table &emu) */ auto input_class_type = sol().registry().new_usertype("input_class", "new", sol::no_constructor); - input_class_type.set("name", sol::property(&input_class::name)); - input_class_type.set("enabled", sol::property(&input_class::enabled, &input_class::enable)); - input_class_type.set("multi", sol::property(&input_class::multi, &input_class::set_multi)); - input_class_type.set("devices", sol::property([this](input_class &devclass) { - sol::table result = sol().create_table(); - int index = 1; - for (int devindex = 0; devindex <= devclass.maxindex(); devindex++) + input_class_type["name"] = sol::property(&input_class::name); + input_class_type["enabled"] = sol::property(&input_class::enabled, &input_class::enable); + input_class_type["multi"] = sol::property(&input_class::multi, &input_class::set_multi); + input_class_type["devices"] = sol::property( + [this] (input_class &devclass) { - input_device *dev = devclass.device(devindex); - if (dev) - result[index++] = dev; - } - return result; - })); + sol::table result = sol().create_table(); + int index = 1; + for (int devindex = 0; devindex <= devclass.maxindex(); devindex++) + { + input_device *dev = devclass.device(devindex); + if (dev) + result[index++] = dev; + } + return result; + }); /* input_device library * * manager:machine():input().device_classes[devclass].devices[index] - * device.name - * device.id - * device.devindex - * device.items[] + * + * device.name - + * device.id - + * device.devindex - + * device.items[] - */ auto input_device_type = sol().registry().new_usertype("input_device", "new", sol::no_constructor); - input_device_type.set("name", sol::property(&input_device::name)); - input_device_type.set("id", sol::property(&input_device::id)); - input_device_type.set("devindex", sol::property(&input_device::devindex)); - input_device_type.set("items", sol::property([this](input_device &dev) { - sol::table result = sol().create_table(); - for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++) + input_device_type["name"] = sol::property(&input_device::name); + input_device_type["id"] = sol::property(&input_device::id); + input_device_type["devindex"] = sol::property(&input_device::devindex); + input_device_type["items"] = sol::property( + [this] (input_device &dev) { - input_device_item *item = dev.item(id); - if (item) - result[id] = dev.item(id); - } - return result; - })); + sol::table result = sol().create_table(); + for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++) + { + input_device_item *item = dev.item(id); + if (item) + result[id] = dev.item(id); + } + return result; + }); /* input_device_item library * * manager:machine():input().device_classes[devclass].devices[index].items[item_id] - * item.name - * item.token - * item:code() + * + * item.name - + * item.code - + * item.token - + * item.current - */ auto input_device_item_type = sol().registry().new_usertype("input_device_item", "new", sol::no_constructor); - input_device_item_type.set("name", sol::property(&input_device_item::name)); - input_device_item_type.set("token", sol::property(&input_device_item::token)); - input_device_item_type.set("code", [](input_device_item &item) { - return input_code(item.device().devclass(), item.device().devindex(), item.itemclass(), ITEM_MODIFIER_NONE, item.itemid()); - }); + input_device_item_type["name"] = sol::property(&input_device_item::name); + input_device_item_type["code"] = sol::property(&input_device_item::code); + input_device_item_type["token"] = sol::property(&input_device_item::token); + input_device_item_type["current"] = sol::property(&input_device_item::current); /* ui_input_manager library diff --git a/src/frontend/mame/luaengine_render.cpp b/src/frontend/mame/luaengine_render.cpp index 5df401c9b17..9fff8f4171b 100644 --- a/src/frontend/mame/luaengine_render.cpp +++ b/src/frontend/mame/luaengine_render.cpp @@ -33,21 +33,13 @@ struct layout_view_items layout_view &view; }; - -struct render_manager_targets -{ - render_manager_targets(render_manager &m) : targets(m.targets()) { } - - simple_list const &targets; -}; - } // anonymous namespace + namespace sol { template <> struct is_container : std::true_type { }; template <> struct is_container : std::true_type { }; -template <> struct is_container : std::true_type { }; template <> @@ -239,76 +231,6 @@ public: } }; - -template <> -struct usertype_container : lua_engine::immutable_container_helper const, simple_list::auto_iterator> -{ -private: - using target_list = simple_list; - - static int next_pairs(lua_State *L) - { - indexed_iterator &i(stack::unqualified_get >(L, 1)); - if (i.src.end() == i.it) - return stack::push(L, lua_nil); - int result; - result = stack::push(L, i.ix + 1); - result += stack::push_reference(L, *i.it); - ++i.it; - ++i.ix; - return result; - } - -public: - static int at(lua_State *L) - { - render_manager_targets &self(get_self(L)); - std::ptrdiff_t const index(stack::unqualified_get(L, 2)); - if ((0 >= index) || (self.targets.count() < index)) - return stack::push(L, lua_nil); - else - return stack::push_reference(L, *self.targets.find(index - 1)); - } - - static int get(lua_State *L) { return at(L); } - static int index_get(lua_State *L) { return at(L); } - - static int index_of(lua_State *L) - { - render_manager_targets &self(get_self(L)); - render_target &target(stack::unqualified_get(L, 2)); - int const found(self.targets.indexof(target)); - if (0 > found) - return stack::push(L, lua_nil); - else - return stack::push(L, found + 1); - } - - static int size(lua_State *L) - { - render_manager_targets &self(get_self(L)); - return stack::push(L, self.targets.count()); - } - - static int empty(lua_State *L) - { - render_manager_targets &self(get_self(L)); - return stack::push(L, self.targets.empty()); - } - - static int next(lua_State *L) { return stack::push(L, next_pairs); } - static int pairs(lua_State *L) { return ipairs(L); } - - static int ipairs(lua_State *L) - { - render_manager_targets &self(get_self(L)); - stack::push(L, next_pairs); - stack::push >(L, self.targets, self.targets.begin()); - stack::push(L, lua_nil); - return 3; - } -}; - } // namespace sol @@ -619,6 +541,6 @@ void lua_engine::initialize_render(sol::table &emu) render_type["max_update_rate"] = &render_manager::max_update_rate; render_type["ui_target"] = &render_manager::ui_target; render_type["ui_container"] = &render_manager::ui_container; - render_type["targets"] = sol::property([] (render_manager &m) { return render_manager_targets(m); }); + render_type["targets"] = sol::property([] (render_manager &m) { return simple_list_wrapper(m.targets()); }); } diff --git a/src/mame/drivers/bwidow.cpp b/src/mame/drivers/bwidow.cpp index 3b222bbbf52..241442aa3d1 100644 --- a/src/mame/drivers/bwidow.cpp +++ b/src/mame/drivers/bwidow.cpp @@ -460,8 +460,8 @@ void bwidow_state::spacduel_map(address_map &map) map(0x0e00, 0x0e00).w(FUNC(bwidow_state::irq_ack_w)); // interrupt acknowledge map(0x0e80, 0x0e80).w(FUNC(bwidow_state::earom_control_w)); map(0x0f00, 0x0f3f).w(FUNC(bwidow_state::earom_write)); - map(0x1000, 0x13ff).rw("pokey1", FUNC(pokey_device::read), FUNC(pokey_device::write)); - map(0x1400, 0x17ff).rw("pokey2", FUNC(pokey_device::read), FUNC(pokey_device::write)); + map(0x1000, 0x10ff).mirror(0x0300).rw("pokey1", FUNC(pokey_device::read), FUNC(pokey_device::write)); + map(0x1400, 0x14ff).mirror(0x0300).rw("pokey2", FUNC(pokey_device::read), FUNC(pokey_device::write)); map(0x2000, 0x27ff).ram(); // vector RAM map(0x2800, 0x3fff).rom(); // vector ROM map(0x4000, 0xffff).rom(); diff --git a/src/mame/drivers/pacman.cpp b/src/mame/drivers/pacman.cpp index 9dfb5342944..5639870ff5d 100644 --- a/src/mame/drivers/pacman.cpp +++ b/src/mame/drivers/pacman.cpp @@ -1843,7 +1843,7 @@ static INPUT_PORTS_START( birdiy ) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_4WAY PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_4WAY PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_4WAY - PORT_DIPNAME( 0x10, 0x10, "Test mode?" ) /* Some kind of test / debug mode */ + PORT_DIPNAME( 0x10, 0x10, "Test mode?" ) // Some kind of test/debug mode PORT_DIPSETTING( 0x10, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN1 ) @@ -1855,8 +1855,7 @@ static INPUT_PORTS_START( birdiy ) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_4WAY PORT_COCKTAIL PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_4WAY PORT_COCKTAIL PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_4WAY PORT_COCKTAIL - PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) - + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START1 ) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START2 ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) @@ -1874,8 +1873,8 @@ static INPUT_PORTS_START( birdiy ) PORT_DIPSETTING( 0x0c, "4" ) PORT_DIPNAME( 0x10, 0x00, DEF_STR( Cabinet ) ) PORT_DIPLOCATION("SW:5") PORT_DIPSETTING( 0x00, DEF_STR( Upright ) ) - PORT_DIPSETTING( 0x10, DEF_STR( Cocktail ) ) - PORT_DIPNAME( 0x20, 0x20, "Skip Screen" ) PORT_DIPLOCATION("SW:7") /* End level after the first worm fed to your baby */ + PORT_DIPSETTING( 0x10, DEF_STR( Cocktail ) ) + PORT_DIPNAME( 0x20, 0x20, "Skip Screen" ) PORT_DIPLOCATION("SW:7") // End level after the first worm fed to your baby PORT_DIPSETTING( 0x20, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW:7")