-emu/ioport.cpp: Made default behaviour better.

* If an input is configured to some combination of controls that are not
  present at all, ignore the setting altogether for the session.
* Fixed relative axes with PORT_RESET not responding to absolute
  controls (MT07685).
* Fixed relative axes not responding to an absolute control if the value
  doesn’t change every frame (eg. holding a stick against the stop).
* Changed the scaling for absolute controls assigned to relative axes to
  make defaults more sane (e.g. arkanoid or spdheat with a joystick).

-frontend: Fixed some localisation issues in Analog Controls menu.

-docs: Added documentation on assigning inputs.
This commit is contained in:
Vas Crabb 2021-11-03 19:22:36 +11:00
parent dce1fd7dfa
commit d29287e092
7 changed files with 225 additions and 45 deletions

View File

@ -3431,11 +3431,10 @@ Debugging Options
**-[no]debug** **-[no]debug**
Activates the integrated debugger. By default, pressing the tilde Activates the integrated debugger. By default, pressing the backtick/tilde
(**~**) key during emulation breaks into the debugger. MAME also (**~**) key during emulation breaks into the debugger. MAME also breaks
breaks into the debugger after the initial soft reset on startup if into the debugger after the initial soft reset on startup if the debugger is
the debugger is active. See :ref:`debugger` for information on using active. See :ref:`debugger` for information on using the debugger.
the debugger.
The default is OFF (**-nodebug**). The default is OFF (**-nodebug**).

View File

@ -17,8 +17,9 @@ issues, developing software to run on vintage systems, creating cheats,
ROM hacking, or just investigating how software works. ROM hacking, or just investigating how software works.
Use the ``-debug`` command line option to start MAME with the debugger Use the ``-debug`` command line option to start MAME with the debugger
activated. By default, pressing the tilde (**~**) during emulation activated. By default, pressing the backtick/tilde (**~**) during
breaks into the debugger. emulation breaks into the debugger (this can by changed by reassigning
the **Break in Debugger** input).
The exact appearance of the debugger depends on your operating system The exact appearance of the debugger depends on your operating system
and the options MAME was built with. All variants of the debugger and the options MAME was built with. All variants of the debugger

View File

@ -30,7 +30,7 @@ and saving/loading save states.
**Tab** **Tab**
Toggles the configuration menu. Toggles the configuration menu.
**~** (tilde key) **`**/**~** (backtick/tilde key)
Toggles the On-Screen Display. Toggles the On-Screen Display.
If you are running with -debug, this key sends a break in emulation. If you are running with -debug, this key sends a break in emulation.

View File

@ -30,9 +30,9 @@ starting point.
`source code is on GitHub <https://github.com/npwoods/bletchmame>`__. `source code is on GitHub <https://github.com/npwoods/bletchmame>`__.
`IV/Play <http://www.mameui.info/>`__ (Microsoft Windows) `IV/Play <http://www.mameui.info/>`__ (Microsoft Windows)
A simple Windows program for launching systems in MAME. Written in C#, the A simple Windows program for launching systems in MAME. Written in C#, the
`souce code is on GitHub <https://github.com/Mataniko/IV-Play>`__. `source code is on GitHub <https://github.com/Mataniko/IV-Play>`__.
`EmuLoader <http://emuloader.mameworld.info/>`__ (Microsoft Windows) `Emu Loader <http://emuloader.mameworld.info/>`__ (Microsoft Windows)
EmuLoader provides a Windows interface for launching systems in multiple Emu Loader provides a Windows interface for launching systems in multiple
emulators, including MAME, Supermodel and DEMUL. Written in Delphi Pascal, emulators, including MAME, Supermodel and DEMUL. Written in Delphi Pascal,
the source code is available the source code is available
`on the download page <https://emuloader.mameworld.info/downloads.htm>`__. `on the download page <https://emuloader.mameworld.info/downloads.htm>`__.

View File

@ -137,6 +137,174 @@ combinations to the **Config Menu**, **Pause** and/or **UI Cancel** inputs to
make it possible to use MAME without a keyboard. make it possible to use MAME without a keyboard.
.. _ui-inptcfg:
Configuring inputs
------------------
MAME needs a flexible input system to support the control schemes of the vast
array of systems it emulates. In MAME, inputs that only have two distinct
states, on and off or active and inactive, are called *digital inputs*, and all
other inputs are called *analog inputs*, even if this is not strictly true.
To assign MAMEs user interface controls or the default inputs for all systems,
select **Input (general)** from the main menu during emulation, or select
**Configure Options** from the system selection menu and then select **General
Inputs**. From there, select a category.
To assign inputs for the currently running machine, select **Input (this
Machine)** from the main menu during emulation. Inputs are grouped by device
and sorted by type. You can move between devices with the next group and
previous group keys/buttons (**[** and **]** on the keyboard by default).
The input assignment menus show the name of the emulated input or user interface
control on the left, and the input (or combination of inputs) assigned to it on
the right.
To adjust the sensitivity, auto-centre speed and inversion settings, or to see
how emulated analog controls react to your inputs, select **Analog Controls**
from the main menu during emulation. (This item only appears on the main menu
for systems with analog controls.)
.. _ui-inptcfg-digital:
Digital input settings
~~~~~~~~~~~~~~~~~~~~~~
Each emulated digital input has a single assignment setting. For flexibility,
MAME can combine host inputs (keys, buttons and joystick axes) using logical
**and**, **not** and **or** operations. This is best illustrated with some
examples:
Kbd 1
In this simple case, pressing the **1** key on the keyboard activates the
emulated input or user interface control.
Kbd Down or Joy 1 Down
Pressing the down arrow on the keyboard or moving the first joystick down
activates the emulated input or user interface control.
Kbd P not Kbd Shift not Kbd Right Shift
Pressing the **P** key on the keyboard while not pressing either **Shift**
key activates the emulated input or user interface control. MAME does not
show the implicit **and** operations.
Kbd P Kbd Shift or Kbd P Kbd Right Shift
Pressing the **P** key while also pressing either of the **Shift** keys
activates the emulated input or user interface control. Once again, the
implicit **and** operations are not shown.
(In technical terms, MAME uses Boolean sum of products logic to combine inputs.)
When a digital input setting is highlighted, the prompt below the menu shows
whether selecting it will set the assignment or append an **or** operation to
it. Press **UI Left/Right** before selecting the setting to switch between
setting or appending an **or** operation. Press **UI Clear** (**Delete** or
**Forward Delete** by default) to clear the setting or restore the default
assignment.
When you select a digital input setting, MAME will wait for you to enter an
input or a combination of inputs for a logical **and** operation:
* Press a key or button or move an analog control once to add it to the **and**
operation.
* Press a key or button or move an analog control twice to add a **not** item to
the **and** operation. Pressing the same key or button or moving the same
analog control additional times toggles the **not** on and off.
* Pressing **UI Cancel** (**Escape** by default) *before* activating any other
inputs clears the setting or restores the default assignment.
* Pressing **UI Cancel** *after* activating another input leaves the setting
unchanged.
* The new setting is shown below the menu. Wait one second after activating an
input to accept the new setting.
Heres how to produce some example settings:
Kbd 1
Press the **1** key on the keyboard once, then wait one second to accept the
setting.
Kbd F12 Kbd Shift Keyboard Alt
Press the **P** key on the keyboard once, press the left **Shift** key once,
press the left **Alt** key once, then wait one second to accept the setting.
Kbd P not Kbd Shift not Kbd Right Shift
Press the **P** key on the keyboard once, press the left **Shift** key
twice, press the right **Shift** key twice, then wait one second to accept
the setting.
.. _ui-inptcfg-analog:
Analog input settings
~~~~~~~~~~~~~~~~~~~~~
Each emulated analog input has three assignment settings:
* Use the *axis setting* to assign an analog axis to control the emulated analog
input. The axis setting uses the name of the input with the suffix “Analog”.
For example the axis setting for the steering wheel in Ridge Racer is called
**Steering Wheel Analog**.
* Use the *increment setting* assign an input (or combination of inputs) to
increase the value of the emulated analog input. The increment setting uses
the name of the input with the suffix “Analog Inc”. For example the increment
setting for the steering wheel in Ridge Racer is called **Steering Wheel
Analog Inc**. This is a digital input setting if an analog axis is
assigned to it, MAME will not increase the emulated input value at a
proportional speed.
* Use the *decrement setting* assign an input (or combination of inputs) to
decrease the value of the emulated analog input. The decrement setting uses
the name of the input with the suffix “Analog Dec”. For example the decrement
setting for the steering wheel in Ridge Racer is called **Steering Wheel
Analog Dec**. This is a digital input setting if an analog axis is
assigned to it, MAME will not decrease the emulated input value at a
proportional speed.
The increment and decrement settings are most useful for controlling an emulated
analog input using digital controls (for example keyboard keys, joystick
buttons, or a directional pad). They are configured in the same way as emulated
digital inputs (:ref:`see above <ui-inptcfg-digital>`). **Its important that
you dont assign the same control to the axis setting as well as the increment
and/or decrement settings for the same emulated input at the same time.** For
example if you assign Ridge Racers **Steering Wheel Analog** setting to the X
axis of the left analog stick on your controller, you *should not* assign either
the **Steering Wheel Analog Inc** or **Steering Wheel Analog Dec** setting to
the X axis of the same analog stick.
To adjust sensitivity, auto-centring speed and inversion settings for emulated
analog inputs, or to see how they respond to your settings, select **Analog
Controls** from the main menu during emulation. Settings for emulated analog
inputs are grouped by device and sorted by type. You can move between devices
with the next group and previous group keys/buttons (**[** and **]** on the
keyboard by default). The state of the emulated analog inputs is shown below
the menu, and reacts in real time. Press the **On Screen Display** key or
button (the backtick/tilde key by default on a US ANSI QWERTY keyboard) to hide
the menu to make it easier to test without changing settings. Press the same
key or button to show the menu again.
Each emulated input has four settings on the **Analog Controls** menu:
* The *increment/decrement speed* setting controls how fast the input value
increases or decreases in response to the controls assigned to the
increment/decrement settings.
* The *auto-centering speed* setting controls how fast the input value returns
to the neutral state when the controls assigned to the increment/decrement
settings are released.
* The **reverse** setting allows the direction of the emulated inputs response
to controls to be inverted. This applies to controls assigned to the axis
setting *and* the increment/decrement settings.
* The *sensitivity* setting adjusts the input values response to the control
assigned to the axis setting.
Use the UI left/right keys or buttons to adjust the highlighted setting.
Selecting a setting or pressing the UI clear key/button (**Forward Delete** by
default) restores its default value.
The units for the increment/decrement speed, auto-centering speed and
sensitivity settings are tied to the driver/device implementation. The
increment/decrement speed and auto-centering speed settings are also tied to the
frame rate of the first emulated screen in the system. The response to controls
assigned to the increment/decrement settings will change if the system changes
the frame rate of this screen.
.. _ui-selmenu: .. _ui-selmenu:
The system and software selection menus The system and software selection menus

View File

@ -296,6 +296,17 @@ const struct
{ INPUT_STRING_None, "None" }, { INPUT_STRING_None, "None" },
}; };
inline bool input_seq_good(running_machine &machine, input_seq const &seq)
{
if (INPUT_CODE_INVALID == seq[0])
return false;
else if (seq.empty())
return true;
else
return input_seq::end_code != machine.input().seq_clean(seq)[0];
}
} // anonymous namespace } // anonymous namespace
@ -2122,25 +2133,18 @@ void ioport_manager::load_config(config_type cfg_type, config_level cfg_level, u
newseq[seqtype].first.reset(); newseq[seqtype].first.reset();
else else
machine().input().seq_from_tokens(newseq[seqtype].first, seqnode->get_value()); machine().input().seq_from_tokens(newseq[seqtype].first, seqnode->get_value());
newseq[seqtype].second = seqnode->get_value(); if (config_type::CONTROLLER != cfg_type)
newseq[seqtype].second = seqnode->get_value();
} }
} }
// load into the appropriate place for the config type/level // load into the appropriate place for the config type/level
if (config_type::SYSTEM == cfg_type) if (config_type::SYSTEM == cfg_type)
{
load_system_config(*portnode, type, player, newseq); load_system_config(*portnode, type, player, newseq);
}
else if ((config_type::CONTROLLER == cfg_type) && (config_level::DEFAULT != cfg_level)) else if ((config_type::CONTROLLER == cfg_type) && (config_level::DEFAULT != cfg_level))
{
for (auto &seq : newseq)
seq.second = "";
load_controller_config(*portnode, type, player, newseq); load_controller_config(*portnode, type, player, newseq);
}
else else
{
load_default_config(type, player, newseq); load_default_config(type, player, newseq);
}
} }
// after applying the controller config, push that back into the backup, since that is // after applying the controller config, push that back into the backup, since that is
@ -2272,7 +2276,7 @@ bool ioport_manager::load_default_config(
{ {
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
{ {
if (newseq[seqtype].first[0] != INPUT_CODE_INVALID) if (input_seq_good(machine(), newseq[seqtype].first))
entry.set_seq(seqtype, newseq[seqtype].first); entry.set_seq(seqtype, newseq[seqtype].first);
entry.set_cfg(seqtype, newseq[seqtype].second); entry.set_cfg(seqtype, newseq[seqtype].second);
} }
@ -2318,7 +2322,7 @@ bool ioport_manager::load_controller_config(
// if a sequence was specified, override the developer-specified default for the field // if a sequence was specified, override the developer-specified default for the field
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
{ {
if (newseq[seqtype].first[0] != INPUT_CODE_INVALID) if (input_seq_good(machine(), newseq[seqtype].first))
field.set_defseq(seqtype, newseq[seqtype].first); field.set_defseq(seqtype, newseq[seqtype].first);
} }
@ -2408,7 +2412,7 @@ void ioport_manager::load_system_config(
// if a sequence was specified, copy it in // if a sequence was specified, copy it in
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
{ {
if (newseq[seqtype].first[0] != INPUT_CODE_INVALID) if (input_seq_good(machine(), newseq[seqtype].first))
field.live().seq[seqtype] = newseq[seqtype].first; field.live().seq[seqtype] = newseq[seqtype].first;
field.live().cfg[seqtype] = newseq[seqtype].second; field.live().cfg[seqtype] = newseq[seqtype].second;
} }
@ -3557,7 +3561,7 @@ void analog_field::frame_update(running_machine &machine)
input_item_class itemclass; input_item_class itemclass;
s32 rawvalue = machine.input().seq_axis_value(m_field.seq(SEQ_TYPE_STANDARD), itemclass); s32 rawvalue = machine.input().seq_axis_value(m_field.seq(SEQ_TYPE_STANDARD), itemclass);
// use programmatically set value if avaiable // use programmatically set value if available
if (m_was_written) if (m_was_written)
{ {
m_was_written = false; m_was_written = false;
@ -3567,7 +3571,23 @@ void analog_field::frame_update(running_machine &machine)
// if we got an absolute input, it overrides everything else // if we got an absolute input, it overrides everything else
if (itemclass == ITEM_CLASS_ABSOLUTE) if (itemclass == ITEM_CLASS_ABSOLUTE)
{ {
if (m_previousanalog != rawvalue) if (!m_absolute && !m_positionalscale)
{
// if port is relative, we use the value to simulate the speed of relative movement
// sensitivity adjustment is allowed for this mode
if (rawvalue)
{
if (m_field.analog_reset())
m_accum = rawvalue / 8;
else
m_accum += rawvalue / 8;
// do not bother with other control types if the analog data is changing
m_lastdigital = false;
return;
}
}
else if (m_previousanalog != rawvalue)
{ {
// only update if analog value changed // only update if analog value changed
m_previousanalog = rawvalue; m_previousanalog = rawvalue;
@ -3580,8 +3600,10 @@ void analog_field::frame_update(running_machine &machine)
// if port is absolute, then just return the absolute data supplied // if port is absolute, then just return the absolute data supplied
m_accum = apply_inverse_sensitivity(rawvalue); m_accum = apply_inverse_sensitivity(rawvalue);
} }
else if (m_positionalscale != 0) else
{ {
assert(m_positionalscale); // only way to get here due to previous if
// if port is positional, we will take the full analog control and divide it // if port is positional, we will take the full analog control and divide it
// into positions, that way as the control is moved full scale, // into positions, that way as the control is moved full scale,
// it moves through all the positions // it moves through all the positions
@ -3591,27 +3613,17 @@ void analog_field::frame_update(running_machine &machine)
rawvalue = std::min(rawvalue, m_maximum); rawvalue = std::min(rawvalue, m_maximum);
m_accum = apply_inverse_sensitivity(rawvalue); m_accum = apply_inverse_sensitivity(rawvalue);
} }
else
// if port is relative, we use the value to simulate the speed of relative movement
// sensitivity adjustment is allowed for this mode
m_accum += rawvalue;
m_lastdigital = false;
// do not bother with other control types if the analog data is changing // do not bother with other control types if the analog data is changing
m_lastdigital = false;
return; return;
} }
else
{
// we still have to update fake relative from joystick control
if (!m_absolute && m_positionalscale == 0)
m_accum += rawvalue;
}
} }
// if we got it from a relative device, use that as the starting delta // if we got it from a relative device, use that as the starting delta
// also note that the last input was not a digital one // also note that the last input was not a digital one
s32 delta = 0; s32 delta = 0;
if (itemclass == ITEM_CLASS_RELATIVE && rawvalue != 0) if (itemclass == ITEM_CLASS_RELATIVE && rawvalue)
{ {
delta = rawvalue; delta = rawvalue;
m_lastdigital = false; m_lastdigital = false;
@ -3663,9 +3675,9 @@ void analog_field::frame_update(running_machine &machine)
s32 center = apply_inverse_sensitivity(m_center); s32 center = apply_inverse_sensitivity(m_center);
if (m_lastdigital && !keypressed) if (m_lastdigital && !keypressed)
{ {
// autocenter from positive values
if (m_accum >= center) if (m_accum >= center)
{ {
// autocenter from positive values
m_accum -= apply_scale(m_centerdelta, m_keyscalepos); m_accum -= apply_scale(m_centerdelta, m_keyscalepos);
if (m_accum < center) if (m_accum < center)
{ {
@ -3673,10 +3685,9 @@ void analog_field::frame_update(running_machine &machine)
m_lastdigital = false; m_lastdigital = false;
} }
} }
// autocenter from negative values
else else
{ {
// autocenter from negative values
m_accum += apply_scale(m_centerdelta, m_keyscaleneg); m_accum += apply_scale(m_centerdelta, m_keyscaleneg);
if (m_accum > center) if (m_accum > center)
{ {

View File

@ -226,6 +226,7 @@ void menu_analog::handle(event const *ev)
{ {
// if selected, reset to default value // if selected, reset to default value
case IPT_UI_SELECT: case IPT_UI_SELECT:
case IPT_UI_CLEAR:
newval = data.defvalue; newval = data.defvalue;
break; break;
@ -360,25 +361,25 @@ void menu_analog::populate(float &customtop, float &custombottom)
{ {
default: default:
case ANALOG_ITEM_KEYSPEED: case ANALOG_ITEM_KEYSPEED:
text = string_format("%s Digital Speed", field->name()); text = string_format(_("%1$s Increment/Decrement Speed"), field->name());
subtext = string_format("%d", settings.delta); subtext = string_format("%d", settings.delta);
data.cur = settings.delta; data.cur = settings.delta;
break; break;
case ANALOG_ITEM_CENTERSPEED: case ANALOG_ITEM_CENTERSPEED:
text = string_format("%s Autocenter Speed", field->name()); text = string_format(_("%1$s Auto-centering Speed"), field->name());
subtext = string_format("%d", settings.centerdelta); subtext = string_format("%d", settings.centerdelta);
data.cur = settings.centerdelta; data.cur = settings.centerdelta;
break; break;
case ANALOG_ITEM_REVERSE: case ANALOG_ITEM_REVERSE:
text = string_format("%s Reverse", field->name()); text = string_format(_("%1$s Reverse"), field->name());
subtext.assign(settings.reverse ? "On" : "Off"); subtext.assign(settings.reverse ? "On" : "Off");
data.cur = settings.reverse; data.cur = settings.reverse;
break; break;
case ANALOG_ITEM_SENSITIVITY: case ANALOG_ITEM_SENSITIVITY:
text = string_format("%s Sensitivity", field->name()); text = string_format(_("%1$s Sensitivity"), field->name());
subtext = string_format("%d", settings.sensitivity); subtext = string_format("%d", settings.sensitivity);
data.cur = settings.sensitivity; data.cur = settings.sensitivity;
break; break;