From dd355e4d68d77fa47a07ee26170a21f3d517ee22 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Fri, 3 Feb 2023 18:02:45 +1100 Subject: docs: Added an introduction to the input system for developers. emu/ioport.cpp: Removed a long-outdated comment that is now rather misleading. --- docs/source/techspecs/index.rst | 1 + docs/source/techspecs/inputsystem.rst | 458 ++++++++++++++++++++++++++++++++++ src/emu/ioport.cpp | 82 ------ 3 files changed, 459 insertions(+), 82 deletions(-) create mode 100644 docs/source/techspecs/inputsystem.rst diff --git a/docs/source/techspecs/index.rst b/docs/source/techspecs/index.rst index 45fe45a6121..43f57bb6fed 100644 --- a/docs/source/techspecs/index.rst +++ b/docs/source/techspecs/index.rst @@ -11,6 +11,7 @@ MAME’s source or working on scripts that run within the MAME framework. layout_files layout_script object_finders + inputsystem device_memory_interface device_rom_interface device_disasm_interface diff --git a/docs/source/techspecs/inputsystem.rst b/docs/source/techspecs/inputsystem.rst new file mode 100644 index 00000000000..9145dba2fa2 --- /dev/null +++ b/docs/source/techspecs/inputsystem.rst @@ -0,0 +1,458 @@ +.. _inputsystem: + +Input System +============ + +.. contents:: + :local: + :depth: 2 + + +.. _inputsystem-intro: + +Introduction +------------ + +The variety of systems MAME emulates, as well as the variation in host +systems and peripherals, necessitates a flexible, configurable input +system. + +Note that the input system is concerned with low-level user input. +High-level user interaction, involving things like text input and +pointing devices, is handled separately. + + +.. _inputsystem-components: + +Components +---------- + +From the emulated system’s point of view, the input system has the +following conceptual components. + +Input device +~~~~~~~~~~~~ + +Input devices supply input values. An input device typically +corresponds to a physical device in the host system, for example a +keyboard, mouse or game controller. However, there isn’t always a +one-to-one correspondence between input devices and physical devices. +For example the SDL keyboard provider module aggregates all keyboards +into a single input device, and the Win32 lightgun provider module can +present two input devices using input from a single mouse. + +Input devices are identified by their device class (keyboard, mouse, +joystick or lightgun) and device number within the class. Input +provider modules can also supply an implementation-dependent identifier +to allow the user to configure stable device numbering. + +Note that input devices are unrelated to emulated devices (``device_t`` +implementations) despite the similar name. + +Input device item +~~~~~~~~~~~~~~~~~ + +Also known as a **control**, and input device item corresponds to a +input source that produces a single value. This usually corresponds to +a physical control or sensor, for example a joystick axis, a button or +an accelerometer. + +MAME supports three kinds of controls: **switches**, **absolute axes** +and **relative axes**: + +* Switches produce the value 0 when inactive (released or off) or 1 when + active (pressed or on). +* Absolute axes produce a value normalised to the range -65,536 to + 65,536 with zero corresponding to the neutral position. +* Relative axes produce a value corresponding to the movement since the + previous input update. Mouse-like devices scale values to + approximately 512 per nominal 100 DPI pixel. + +Negative axis values should correspond to directions up, to the left, +away from the player, or anti-clockwise. For single-ended axes (e.g. +pedals or displacement-sensitive triggers and buttons), only zero and +the negative portion of the range should be used. + +Switches are used to represent controls that naturally have two distinct +states, like buttons and toggle switches. + +Absolute axes are used to represent controls with a definite range +and/or neutral position. Examples include steering wheels with limit +stops, joystick axes, and displacement-sensitive triggers. + +Relative axes are used to represent controls with an effectively +infinite range. Examples include mouse/trackball axes, incremental +encoder dials, and gyroscopes. + +Accelerometers and force sensing joystick axes should be represented as +absolute axes, even though the range is theoretically open-ended. In +practice, there is a limit to the range the transducers can report, +which is usually substantially larger than needed for normal operation. + +Input device items are identified by their associated device’s class and +device number along with an **input item ID**. MAME supplies item IDs +for common types of controls. Additional controls or controls that do +not correspond to a common type are dynamically assigned item IDs. MAME +supports hundreds to items per input device. + +I/O port field +~~~~~~~~~~~~~~ + +An I/O port field represents an input source in an emulated device or +system. Most types of I/O port fields can be assigned one or more +combinations of controls, allowing the user to control the input to +the emulated system. + +Similarly to input device items, there are multiple types of I/O port +fields: + +* **Digital fields** function as switches that produce one of two + distinct values. They are used for keyboard keys, eight-way joystick + direction switches, toggle switches, photointerruptors and other + emulated inputs that function as two-position switches. +* **Absolute analog fields** have a range with defined minimum, maximum + and neutral positions. They are used for analog joystick axes, + displacement-sensitive pedals, paddle knobs, and other emulated inputs + with a defined range. +* **Relative analog fields** have a range with with defined + minimum, maximum and starting positions. On each update, the value + accumulates and wraps when it passes either end of the range. + Functionally, this is like the output of an up/down counter connected + to an incremental encoder. They are used for mouse/trackball axes, + steering wheels without limit stops, and other emulated inputs that + have no range limits. +* DIP switch, configuration and adjuster fields allow the user to set + the value through MAME’s user interface. +* Additional special field types are used to produce fixed or + programmatically generated values. + +A digital field appears to the user as a single assignable input, which +accepts switch values. + +An analog field appears to the user as three assignable inputs: an +**axis input**, which accepts axis values; and an **increment input** +and a **decrement input** which accept switch values. + +Input manager +~~~~~~~~~~~~~ + +The input manager has several responsibilities: + +* Tracking the available input devices in the system. +* Reading input values. +* Converting between internal identifier values, configuration token + strings and display strings. + +In practice, emulated devices and systems rarely interact with the input +manager directly. The most common reason to access the input manager is +implementing special debug controls, which should be disabled in release +builds. Plugins that respond to input need to call the input manager to +raed inputs. + +I/O port manager +~~~~~~~~~~~~~~~~ + +The I/O port manager’s primary responsibilities include: + +* Managing assignments of controls to I/O port fields and user interface + actions. +* Reading input values via the input manager and updating I/O port field + values. + +Like the input manager, the I/O port manager is largely transparent to +emulated devices and systems. You just need to set up your I/O ports +and fields, and the I/O port manager handles the rest. + + +.. _inputsystem-structures: + +Structures and data types +------------------------- + +The following data types are used for dealing with input. + +Input code +~~~~~~~~~~ + +An input code specifies an input device item and how it should be +interpreted. It is a tuple consisting of the following values: **device +class**, **device number**, **item class**, **item modifier** and **item +ID**: + +* The device class, device number and item ID together identify the + input device item to read. +* The item class specifies the type of output value desired: switch, + absolute axis or relative axis. Axis values can be converted to + switch values by specifying an appropriate modifier. +* The modifier specifies how a value should be interpreted. Valid + options depend on the type of input device item and the specified + item class. + +If the specified input item is a switch, it can only be read using the +switch class, and no modifiers are supported. Attempting to read a +switch as an absolute or relative axis always returns zero. + +If the specified input item is an absolute axis, it can be read as an +absolute axis or as a switch: + +* Reading an absolute axis item as an absolute axis returns the current + state of the control, potentially transformed if a modifier is + specified. Supported modifiers are **reverse** to reverse the range + of the control, **positive** to map the positive range of the control + onto the output (zero corresponding to -65,536 and 65,536 + corresponding to 65,536), and **negative** to map the negative range + of the control onto the output (zero corresponding to -65,536 and + -65,536 corresponding to 65,536). +* Reading an absolute axis item as a switch returns zero or 1 depending + on whether the control is past a threshold in the direction specified + by the modifier. Use the **negative** modifier to return 1 when the + control is beyond the threshold in the negative direction (up or + left), or the **positive** modifier to return 1 when the control is + beyond the threshold in the positive direction (down or right). There + are two special pairs of modifiers, **left**/**right** and + **up**/**down** that are only applicable to the primary X/Y axes of + joystick devices. The user can specify a *joystick map* to control + how these modifiers interpret joystick movement. +* Attempting to read an absolute axis item as a relative axis always + returns zero. + +If the specified input item is a relative axis, it can be read as a +relative axis or as a switch: + +* Reading a relative axis item as a relative axis returns the change in + value since the last input update. The only supported modifier is + **reverse**, which negates the value, reversing the direction. +* Reading a relative axis as a switch returns 1 if the control moved in + the direction specified by the modifier since the last input update. + Use the **negative**/**left**/**up** modifiers to return 1 when the + control has been moved in the negative direction (up or left), or the + **positive**/**right**/**down** modifiers to return 1 when the control + has moved in the positive direction (down or right). +* Attempting to read a relative axis item as an absolute axis always + returns zero. + +There are also special input codes used for specifying how multiple +controls are to be combined in an input sequence. + +The most common place you’ll encounter input codes in device and system +driver code is when specifying initial assignments for I/O port fields +that don’t have default assignments supplied by the core. The +``PORT_CODE`` macro is used for this purpose. + +MAME provides macros and helper functions for producing commonly used +input codes, including standard keyboard keys and +mouse/joystick/lightgun axes and buttons. + +Input sequence +~~~~~~~~~~~~~~ + +An input sequence specifies a combination controls that can be assigned +to an input. The name refers to the fact that it is implemented as a +sequence container with input codes as elements. It is somewhat +misleading, as input sequences are interpreted using instantaneous +control values. Input sequences are interpreted differently for switch +and axis input. + +Input sequences for switch input must only contain input codes with the +item class set to switch along with the special **or** and **not** input +codes. The input sequence is interpreted using sum-of-products logic. +A **not** code causes the value returned by the immediately following +code to be inverted. The conjunction of values returned by successive +codes is evaluated until an **or** code is encountered. If the current +value is 1 when an **or** code is encountered it is returned, otherwise +evaluation continues. + +Input sequences for axis input can contain input codes with the item +class set to switch, absolute axis or relative axis along with the +special **or** and **not** codes. It’s helpful to think of the input +sequence as containing one or more groups of input codes separated by +**or** codes: + +* A **not** code causes the value returned by an immediately following + switch code to be inverted. It has no effect on absolute or relative + axis codes. +* Within a group, the conjunction of the values returned by switch codes + is evaluated. If it is zero, the group is ignored. +* Within a group, multiple axis values of the same type are summed. + Values returned by absolute axis codes are summed, and values returned + by relative axis codes are summed. +* If any absolute axis code in a group returns a non-zero value, the sum + of relative axes in the group is ignored. Any non-zero absolute axis + value takes precedence over relative axis values. +* The same logic is applied when combining group values: group values + produced from the same axis type are summed, and values produced from + absolute axes take precedence over values produced from relative axes. +* After the group values are summed, if the value was produced from + absolute axes it is clamped to the range -65,536 to 65,536 (values + produced from relative axes are not clamped). + +Emulation code rarely needs to deal with input sequences directly, as +they’re handled internally between the I/O port manager and input +manager. The input manager also converts input sequences to and from +the token strings stored in configuration files and produces text for +displaying input sequences to users. + +Plugins with controls or hotkeys need to use input sequences to allow +configuration. Utility classes are provided to allow input sequences to +be entered by the user in a consistent way, and the input manager can be +used for conversions to and from configuration and display strings. It +is very rare to need to directly manipulate input sequences. + + +.. _inputsystem-providermodules: + +Input provider modules +---------------------- + +Input provider modules are part of the OS-dependent layer (OSD), and are +not directly exposed to emulation and user interface code. Input +provider modules are responsible for detecting available host input +devices, setting up input devices for the input manager, and providing +callbacks to read the current state of input device items. + +The user is given a choice of input modules to use. One input provider +module is used for each of the four input device classes (keyboard, +mouse, joystick and lightgun). The available modules depend on the host +operating system and OSD implementation. Different modules may use +different APIs, support different kinds of devices, or present devices +in different ways. + + +.. _inputsystem-playerpositions: + +Player positions +---------------- + +MAME uses a concept called *player positions* to help manage input +assignments. The number of player positions supported depends on the +I/O port field type: + +* Ten player positions are supported for common game inputs, including + joystick, pedal, paddle, dial, trackball, lightgun and mouse. +* Four player positions are supported for mahjong and hanafuda inputs. +* One player position is supported for gambling system inputs. +* Other inputs do not use player positions. This includes coin slots, + arcade start buttons, tilt switches, service switches and + keyboard/keypad keys. + +The user can configure default input assignments per player position for +supported I/O port field types which are saved in the file +**default.cfg**. These assignments are used for all systems unless the +device/system driver supplies its own default assignments, or the user +configures system-specific input assignments. + +In order to facilitate development of reusable emulated devices with +inputs, particularly slot devices, the I/O port manager automatically +renumbers player positions when setting up the emulated system: + +* The I/O port manager starts at player position 1 and begins + iterating the emulated device tree in depth first order, starting from + the root device. +* If a device has I/O port fields that support player positions, they + are renumbered to start from the I/O port manager’s current player + position. +* Before advancing to the next device, the I/O port manager sets its + current player position to the last seen player position plus one. + +For a simple example, consider what happens when you run a Sega Mega +Drive console with two game pads connected: + +* The I/O port manager starts at player position 1 at the root device. +* The first device encountered with I/O port fields that support player + positions is the first game pad. The inputs are renumbered to start + at player position 1. This has no visible effect, as the I/O port + fields are initially numbered starting at player position 1. +* Before moving to the next device, the I/O port manager sets its + current player position to 2 (the last player position seen plus one). +* The next device encountered with I/O port fields that support player + positions is the second game pad. The inputs are renumbered to start + at player position 2. This avoids I/O port field type conflicts with + the first game pad. +* Before moving to the next device, the I/O port manager sets its + current player position to 3 (the last player position seen plus one). +* No more devices with I/O port fields that support player positions are + encountered. + + +.. _inputsystem-updatingfields: + +Updating I/O port fields +------------------------ + +The I/O port manager updates I/O port fields once for each video frame +produced by the first emulated screen in the system. How a field is +updated depends on whether it is a digital or analog field. + +Updating digital fields +~~~~~~~~~~~~~~~~~~~~~~~ + +Updating digital I/O port fields is simple: + +* The I/O port manager reads the current value for the field’s assigned + input sequence (via the input manager). +* If the value is zero, the field’s default value is set. +* If the value is non-zero, the binary complement of the field’s default + value is set. + +Updating absolute analog fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Updating absolute analog I/O port fields is more complex due to the need +to support a variety of control setups: + +* The I/O port manager reads the current value for the field’s assigned + axis input sequence (via the input manager). +* If the current value changed since the last update and the input + device item that produced the current value was an absolute axis, the + field’s value is set to the current value scaled to the correct range, + and no further processing is performed. +* If the current value is non-zero and the input device device item that + produced the current value was a relative axis, the current value is + added to the field’s value, scaled by the field’s sensitivity setting. +* The I/O port manager reads the current value for the field’s assigned + increment input sequence (via the input manager); if this value is + non-zero, the field’s increment/decrement speed setting value is added + to its value, scaled by its sensitivity setting. +* The I/O port manager reads the current value for the field’s assigned + decrement input sequence (via the input manager); if this value is + non-zero, the field’s increment/decrement speed setting value is + subtracted from its value, scaled by its sensitivity setting. +* If the current axis input, increment input and decrement input values + are all zero, but either or both of the increment input and decrement + input values were non-zero the last time the field’s value changed in + response to user input, the field’s auto-centring speed setting value + is added to or subtracted from its value to move it toward its default + value. + +Note that the sensitivity setting value for absolute analog fields +affects the response to relative axis input device items and +increment/decrement inputs, but it does not affect the response to +absolute axis input device items or the auto-centring speed. + +Updating relative analog fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Relative analog I/O port fields also need special handling to cater for +multiple control setups, but they are a little simpler than absolute +analog fields: + +* The I/O port manager reads the current value for the field’s assigned + axis input sequence (via the input manager). +* If the current value is non-zero and the input device item that + produced the current value was an absolute axis, the current value is + added to the field’s value, scaled by the field’s sensitivity setting, + and no further processing is performed. +* If the current value is non-zero and the input device device item that + produced the current value was a relative axis, the current value is + added to the field’s value, scaled by the field’s sensitivity setting. +* The I/O port manager reads the current value for the field’s assigned + increment input sequence (via the input manager); if this value is + non-zero, the field’s increment/decrement speed setting value is added + to its value, scaled by its sensitivity setting. +* The I/O port manager reads the current value for the field’s assigned + decrement input sequence (via the input manager); if this value is + non-zero, the field’s increment/decrement speed setting value is + subtracted from its value, scaled by its sensitivity setting. + +Note that the sensitivity setting value for relative analog fields +affects the response to all user input. diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp index 4afba90d1ff..585cf40ae49 100644 --- a/src/emu/ioport.cpp +++ b/src/emu/ioport.cpp @@ -6,88 +6,6 @@ Input/output port handling. -**************************************************************************** - - Theory of operation - - ------------ - OSD controls - ------------ - - There are three types of controls that the OSD can provide as potential - input devices: digital controls, absolute analog controls, and relative - analog controls. - - Digital controls have only two states: on or off. They are generally - mapped to buttons and digital joystick directions (like a gamepad or a - joystick hat). The OSD layer must return either 0 (off) or 1 (on) for - these types of controls. - - Absolute analog controls are analog in the sense that they return a - range of values depending on how much a given control is moved, but they - are physically bounded. This means that there is a minimum and maximum - limit to how far the control can be moved. They are generally mapped to - analog joystick axes, lightguns, most PC steering wheels, and pedals. - The OSD layer must determine the minimum and maximum range of each - analog device and scale that to a value between -65536 and +65536 - representing the position of the control. -65536 generally refers to - the topmost or leftmost position, while +65536 refers to the bottommost - or rightmost position. Note that pedals are a special case here, the - OSD layer needs to return half axis as full -65536 to + 65536 range. - - Relative analog controls are analog as well, but are not physically - bounded. They can be moved continually in one direction without limit. - They are generally mapped to trackballs and mice. Because they are - unbounded, the OSD layer can only return delta values since the last - read. Because of this, it is difficult to scale appropriately. For - MAME's purposes, when mapping a mouse devices to a relative analog - control, one pixel of movement should correspond to 512 units. Other - analog control types should be scaled to return values of a similar - magnitude. Like absolute analog controls, negative values refer to - upward or leftward movement, while positive values refer to downward - or rightward movement. - - ------------- - Game controls - ------------- - - Similarly, the types of controls used by arcade games fall into the same - three categories: digital, absolute analog, and relative analog. The - tricky part is how to map any arbitrary type of OSD control to an - arbitrary type of game control. - - Digital controls: used for game buttons and standard 4/8-way joysticks, - as well as many other types of game controls. Mapping an OSD digital - control to a game's OSD control is trivial. For OSD analog controls, - the MAME core does not directly support mapping any OSD analog devices - to digital controls. However, the OSD layer is free to enumerate digital - equivalents for analog devices. For example, each analog axis in the - Windows OSD code enumerates to two digital controls, one for the - negative direction (up/left) and one for the position direction - (down/right). When these "digital" inputs are queried, the OSD layer - checks the axis position against the center, adding in a dead zone, - and returns 0 or 1 to indicate its position. - - Absolute analog controls: used for analog joysticks, lightguns, pedals, - and wheel controls. Mapping an OSD absolute analog control to this type - is easy. OSD relative analog controls can be mapped here as well by - accumulating the deltas and bounding the results. OSD digital controls - are mapped to these types of controls in pairs, one for a decrement and - one for an increment, but apart from that, operate the same as the OSD - relative analog controls by accumulating deltas and applying bounds. - The speed of the digital delta is user-configurable per analog input. - In addition, most absolute analog control types have an autocentering - feature that is activated when using the digital increment/decrement - sequences, which returns the control back to the center at a user- - controllable speed if no digital sequences are pressed. - - Relative analog controls: used for trackballs and dial controls. Again, - mapping an OSD relative analog control to this type is straightforward. - OSD absolute analog controls can't map directly to these, but if the OSD - layer provides a digital equivalent for each direction, it can be done. - OSD digital controls map just like they do for absolute analog controls, - except that the accumulated deltas are not bounded, but rather wrap. - ***************************************************************************/ #include "emu.h" -- cgit v1.2.3