summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/input.c')
-rw-r--r--src/emu/input.c1603
1 files changed, 1603 insertions, 0 deletions
diff --git a/src/emu/input.c b/src/emu/input.c
new file mode 100644
index 00000000000..9a961ea0b7a
--- /dev/null
+++ b/src/emu/input.c
@@ -0,0 +1,1603 @@
+/***************************************************************************
+
+ input.c
+
+ Handle input from the user.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+****************************************************************************
+
+ To do:
+ * auto-selecting joystick configs
+ * per-joystick configs?
+ * test half-axis selections
+ * add input test menu
+ * get rid of osd_customize_inputport_list
+
+***************************************************************************/
+
+#include "input.h"
+#include "mame.h"
+#include "emuopts.h"
+#include "profiler.h"
+#include <ctype.h>
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+/* joystick mapping codes */
+#define JOYSTICK_MAP_NEUTRAL 0x00
+#define JOYSTICK_MAP_LEFT 0x01
+#define JOYSTICK_MAP_RIGHT 0x02
+#define JOYSTICK_MAP_UP 0x04
+#define JOYSTICK_MAP_DOWN 0x08
+#define JOYSTICK_MAP_STICKY 0x0f
+
+/* the largest number of tracked pressed switches for memory */
+#define MAX_PRESSED_SWITCHES 16
+
+/* invalid memory value for axis polling */
+#define INVALID_AXIS_VALUE 0x7fffffff
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* single device item */
+typedef struct _input_device_item input_device_item;
+struct _input_device_item
+{
+ input_device_class devclass; /* device class of parent item */
+ int devindex; /* device index of parent item */
+ astring * name; /* string name of item */
+ astring * token; /* tokenized name for non-standard items */
+ void * internal; /* internal callback pointer */
+ input_item_class itemclass; /* class of the item */
+ input_item_id itemid; /* originally specified item id */
+ item_get_state_cb getstate; /* get state callback */
+ INT32 current; /* current raw value */
+ INT32 memory; /* "memory" value, to remember where we started during polling */
+ INT32 oncelatch; /* latched "once" value, cleared after each read */
+
+ /* keyboard information */
+ INT32 steadykey; /* the live steadykey state */
+ INT32 oldkey; /* old live state */
+};
+
+
+/* a 9x9 joystick map */
+typedef struct _joystick_map joystick_map;
+struct _joystick_map
+{
+ UINT8 map[9][9]; /* 9x9 grid */
+};
+
+
+/* a single input device */
+struct _input_device
+{
+ astring * name; /* string name of device */
+ input_device_class devclass; /* class of this device */
+ int devindex; /* device index of this device */
+ input_device_item * item[ITEM_ID_ABSOLUTE_MAXIMUM]; /* array of pointers to items */
+ input_item_id maxitem; /* maximum item index */
+ void * internal; /* internal callback pointer */
+
+ /* joystick information */
+ joystick_map joymap; /* joystick map for this device */
+ UINT8 lastmap; /* last joystick map value for this device */
+};
+
+
+/* a list of devices as an array */
+typedef struct _input_device_list input_device_list;
+struct _input_device_list
+{
+ input_device * list; /* the array */
+ int count; /* elements in the array */
+ UINT8 enabled; /* is this class enabled? */
+ UINT8 multi; /* are multiple instances of this class allowed? */
+};
+
+
+/* code <-> string matching */
+typedef struct _code_string_table code_string_table;
+struct _code_string_table
+{
+ UINT32 code;
+ const char * string;
+};
+
+
+
+/***************************************************************************
+ TOKEN/STRING TABLES
+***************************************************************************/
+
+/* token strings for device classes */
+static const code_string_table devclass_token_table[] =
+{
+ { DEVICE_CLASS_KEYBOARD, "KEYCODE" },
+ { DEVICE_CLASS_MOUSE, "MOUSECODE" },
+ { DEVICE_CLASS_LIGHTGUN, "GUNCODE" },
+ { DEVICE_CLASS_JOYSTICK, "JOYCODE" },
+ { ~0, "UNKCODE" }
+};
+
+/* friendly strings for device classes */
+static const code_string_table devclass_string_table[] =
+{
+ { DEVICE_CLASS_KEYBOARD, "Kbd" },
+ { DEVICE_CLASS_MOUSE, "Mouse" },
+ { DEVICE_CLASS_LIGHTGUN, "Gun" },
+ { DEVICE_CLASS_JOYSTICK, "Joy" },
+ { ~0, "Unk" }
+};
+
+/* token strings for item modifiers */
+static const code_string_table modifier_token_table[] =
+{
+ { ITEM_MODIFIER_POS, "POS" },
+ { ITEM_MODIFIER_NEG, "NEG" },
+ { ITEM_MODIFIER_LEFT, "LEFT" },
+ { ITEM_MODIFIER_RIGHT, "RIGHT" },
+ { ITEM_MODIFIER_UP, "UP" },
+ { ITEM_MODIFIER_DOWN, "DOWN" },
+ { ~0, "" }
+};
+
+/* friendly strings for item modifiers */
+static const code_string_table modifier_string_table[] =
+{
+ { ITEM_MODIFIER_POS, "+" },
+ { ITEM_MODIFIER_NEG, "-" },
+ { ITEM_MODIFIER_LEFT, "Left" },
+ { ITEM_MODIFIER_RIGHT, "Right" },
+ { ITEM_MODIFIER_UP, "Up" },
+ { ITEM_MODIFIER_DOWN, "Down" },
+ { ~0, "" }
+};
+
+/* token strings for item classes */
+static const code_string_table itemclass_token_table[] =
+{
+ { ITEM_CLASS_SWITCH, "SWITCH" },
+ { ITEM_CLASS_ABSOLUTE, "ABSOLUTE" },
+ { ITEM_CLASS_RELATIVE, "RELATIVE" },
+ { ~0, "" }
+};
+
+/* token strings for standard item ids */
+static const code_string_table itemid_token_table[] =
+{
+ /* standard keyboard codes */
+ { ITEM_ID_A, "A" },
+ { ITEM_ID_B, "B" },
+ { ITEM_ID_C, "C" },
+ { ITEM_ID_D, "D" },
+ { ITEM_ID_E, "E" },
+ { ITEM_ID_F, "F" },
+ { ITEM_ID_G, "G" },
+ { ITEM_ID_H, "H" },
+ { ITEM_ID_I, "I" },
+ { ITEM_ID_J, "J" },
+ { ITEM_ID_K, "K" },
+ { ITEM_ID_L, "L" },
+ { ITEM_ID_M, "M" },
+ { ITEM_ID_N, "N" },
+ { ITEM_ID_O, "O" },
+ { ITEM_ID_P, "P" },
+ { ITEM_ID_Q, "Q" },
+ { ITEM_ID_R, "R" },
+ { ITEM_ID_S, "S" },
+ { ITEM_ID_T, "T" },
+ { ITEM_ID_U, "U" },
+ { ITEM_ID_V, "V" },
+ { ITEM_ID_W, "W" },
+ { ITEM_ID_X, "X" },
+ { ITEM_ID_Y, "Y" },
+ { ITEM_ID_Z, "Z" },
+ { ITEM_ID_0, "0" },
+ { ITEM_ID_1, "1" },
+ { ITEM_ID_2, "2" },
+ { ITEM_ID_3, "3" },
+ { ITEM_ID_4, "4" },
+ { ITEM_ID_5, "5" },
+ { ITEM_ID_6, "6" },
+ { ITEM_ID_7, "7" },
+ { ITEM_ID_8, "8" },
+ { ITEM_ID_9, "9" },
+ { ITEM_ID_F1, "F1" },
+ { ITEM_ID_F2, "F2" },
+ { ITEM_ID_F3, "F3" },
+ { ITEM_ID_F4, "F4" },
+ { ITEM_ID_F5, "F5" },
+ { ITEM_ID_F6, "F6" },
+ { ITEM_ID_F7, "F7" },
+ { ITEM_ID_F8, "F8" },
+ { ITEM_ID_F9, "F9" },
+ { ITEM_ID_F10, "F10" },
+ { ITEM_ID_F11, "F11" },
+ { ITEM_ID_F12, "F12" },
+ { ITEM_ID_F13, "F13" },
+ { ITEM_ID_F14, "F14" },
+ { ITEM_ID_F15, "F15" },
+ { ITEM_ID_ESC, "ESC" },
+ { ITEM_ID_TILDE, "TILDE" },
+ { ITEM_ID_MINUS, "MINUS" },
+ { ITEM_ID_EQUALS, "EQUALS" },
+ { ITEM_ID_BACKSPACE, "BACKSPACE" },
+ { ITEM_ID_TAB, "TAB" },
+ { ITEM_ID_OPENBRACE, "OPENBRACE" },
+ { ITEM_ID_CLOSEBRACE, "CLOSEBRACE" },
+ { ITEM_ID_ENTER, "ENTER" },
+ { ITEM_ID_COLON, "COLON" },
+ { ITEM_ID_QUOTE, "QUOTE" },
+ { ITEM_ID_BACKSLASH, "BACKSLASH" },
+ { ITEM_ID_BACKSLASH2, "BACKSLASH2" },
+ { ITEM_ID_COMMA, "COMMA" },
+ { ITEM_ID_STOP, "STOP" },
+ { ITEM_ID_SLASH, "SLASH" },
+ { ITEM_ID_SPACE, "SPACE" },
+ { ITEM_ID_INSERT, "INSERT" },
+ { ITEM_ID_DEL, "DEL" },
+ { ITEM_ID_HOME, "HOME" },
+ { ITEM_ID_END, "END" },
+ { ITEM_ID_PGUP, "PGUP" },
+ { ITEM_ID_PGDN, "PGDN" },
+ { ITEM_ID_LEFT, "LEFT" },
+ { ITEM_ID_RIGHT, "RIGHT" },
+ { ITEM_ID_UP, "UP" },
+ { ITEM_ID_DOWN, "DOWN" },
+ { ITEM_ID_0_PAD, "0PAD" },
+ { ITEM_ID_1_PAD, "1PAD" },
+ { ITEM_ID_2_PAD, "2PAD" },
+ { ITEM_ID_3_PAD, "3PAD" },
+ { ITEM_ID_4_PAD, "4PAD" },
+ { ITEM_ID_5_PAD, "5PAD" },
+ { ITEM_ID_6_PAD, "6PAD" },
+ { ITEM_ID_7_PAD, "7PAD" },
+ { ITEM_ID_8_PAD, "8PAD" },
+ { ITEM_ID_9_PAD, "9PAD" },
+ { ITEM_ID_SLASH_PAD, "SLASHPAD" },
+ { ITEM_ID_ASTERISK, "ASTERISK" },
+ { ITEM_ID_MINUS_PAD, "MINUSPAD" },
+ { ITEM_ID_PLUS_PAD, "PLUSPAD" },
+ { ITEM_ID_DEL_PAD, "DELPAD" },
+ { ITEM_ID_ENTER_PAD, "ENTERPAD" },
+ { ITEM_ID_PRTSCR, "PRTSCR" },
+ { ITEM_ID_PAUSE, "PAUSE" },
+ { ITEM_ID_LSHIFT, "LSHIFT" },
+ { ITEM_ID_RSHIFT, "RSHIFT" },
+ { ITEM_ID_LCONTROL, "LCONTROL" },
+ { ITEM_ID_RCONTROL, "RCONTROL" },
+ { ITEM_ID_LALT, "LALT" },
+ { ITEM_ID_RALT, "RALT" },
+ { ITEM_ID_SCRLOCK, "SCRLOCK" },
+ { ITEM_ID_NUMLOCK, "NUMLOCK" },
+ { ITEM_ID_CAPSLOCK, "CAPSLOCK" },
+ { ITEM_ID_LWIN, "LWIN" },
+ { ITEM_ID_RWIN, "RWIN" },
+ { ITEM_ID_MENU, "MENU" },
+ { ITEM_ID_CANCEL, "CANCEL" },
+
+ /* standard mouse/joystick/gun codes */
+ { ITEM_ID_XAXIS, "XAXIS" },
+ { ITEM_ID_YAXIS, "YAXIS" },
+ { ITEM_ID_ZAXIS, "ZAXIS" },
+ { ITEM_ID_RXAXIS, "RXAXIS" },
+ { ITEM_ID_RYAXIS, "RYAXIS" },
+ { ITEM_ID_RZAXIS, "RZAXIS" },
+ { ITEM_ID_SLIDER1, "SLIDER1" },
+ { ITEM_ID_SLIDER2, "SLIDER2" },
+ { ITEM_ID_BUTTON1, "BUTTON1" },
+ { ITEM_ID_BUTTON2, "BUTTON2" },
+ { ITEM_ID_BUTTON3, "BUTTON3" },
+ { ITEM_ID_BUTTON4, "BUTTON4" },
+ { ITEM_ID_BUTTON5, "BUTTON5" },
+ { ITEM_ID_BUTTON6, "BUTTON6" },
+ { ITEM_ID_BUTTON7, "BUTTON7" },
+ { ITEM_ID_BUTTON8, "BUTTON8" },
+ { ITEM_ID_BUTTON9, "BUTTON9" },
+ { ITEM_ID_BUTTON10, "BUTTON10" },
+ { ITEM_ID_BUTTON11, "BUTTON11" },
+ { ITEM_ID_BUTTON12, "BUTTON12" },
+ { ITEM_ID_BUTTON13, "BUTTON13" },
+ { ITEM_ID_BUTTON14, "BUTTON14" },
+ { ITEM_ID_BUTTON15, "BUTTON15" },
+ { ITEM_ID_BUTTON16, "BUTTON16" },
+ { ITEM_ID_START, "START" },
+ { ITEM_ID_SELECT, "SELECT" },
+
+ { ~0, NULL }
+};
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+/* array of devices for each class */
+static input_device_list device_list[DEVICE_CLASS_MAXIMUM];
+static input_code code_pressed_memory[MAX_PRESSED_SWITCHES];
+
+/* device configuration */
+static UINT8 steadykey_enabled;
+static UINT8 lightgun_reload_button;
+static const char * joystick_map_default;
+static INT32 joystick_deadzone;
+static INT32 joystick_saturation;
+
+/* standard joystick mappings */
+const char * joystick_map_8way = "7778...4445";
+const char * joystick_map_4way_sticky = "s8.4s8.44s8.4445";
+const char * joystick_map_4way_diagonal = "4444s8888..444458888.444555888.ss5.222555666.222256666.2222s6666.2222s6666";
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void input_frame(running_machine *machine);
+static input_device_item *input_code_item(input_code code);
+static INT32 convert_absolute_value(input_code code, input_device_item *item);
+static INT32 convert_relative_value(input_code code, input_device_item *item);
+static INT32 convert_switch_value(input_code code, input_device_item *item);
+static INT32 apply_deadzone_and_saturation(input_code code, INT32 result);
+static int joystick_map_parse(const char *mapstring, joystick_map *map);
+static void joystick_map_print(const char *header, const char *origstring, const joystick_map *map);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ input_code_device - given an input_code return
+ a pointer to the associated device
+-------------------------------------------------*/
+
+INLINE input_device *input_code_device(input_code code)
+{
+ /* if the class is valid... */
+ input_device_class devclass = INPUT_CODE_DEVCLASS(code);
+ if (devclass > DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM)
+ {
+ /* ...and the index is valid for that class, return a pointer to the device */
+ int devindex = INPUT_CODE_DEVINDEX(code);
+ if (devindex < device_list[devclass].count)
+ return &device_list[devclass].list[devindex];
+ }
+
+ /* otherwise, return NULL */
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ device_item_to_code - convert a device/item
+ pair to a standard code
+-------------------------------------------------*/
+
+INLINE input_code device_item_to_code(input_device *device, input_item_id itemid)
+{
+ int devindex = device->devindex;
+
+ assert(devindex < device_list[device->devclass].count);
+ assert(itemid < ITEM_ID_ABSOLUTE_MAXIMUM);
+ assert(device->item[itemid] != NULL);
+
+ return INPUT_CODE(device->devclass, devindex, device->item[itemid]->itemclass, ITEM_MODIFIER_NONE, itemid);
+}
+
+
+/*-------------------------------------------------
+ input_item_standard_class - return the class
+ of a standard item
+-------------------------------------------------*/
+
+INLINE input_item_class input_item_standard_class(input_device_class devclass, input_item_id itemid)
+{
+ /* most everything standard is a switch, apart from the axes */
+ if (itemid == ITEM_ID_OTHER_SWITCH || itemid < ITEM_ID_XAXIS || itemid > ITEM_ID_SLIDER2)
+ return ITEM_CLASS_SWITCH;
+
+ /* standard mouse axes are relative */
+ else if (devclass == DEVICE_CLASS_MOUSE || itemid == ITEM_ID_OTHER_AXIS_RELATIVE)
+ return ITEM_CLASS_RELATIVE;
+
+ /* all other standard axes are absolute */
+ else
+ return ITEM_CLASS_ABSOLUTE;
+}
+
+
+/*-------------------------------------------------
+ input_item_update_value - update the value
+ of an input item
+-------------------------------------------------*/
+
+INLINE void input_item_update_value(input_device_item *item)
+{
+ item->current = (*item->getstate)(device_list[item->devclass].list[item->devindex].internal, item->internal);
+}
+
+
+/*-------------------------------------------------
+ code_pressed_memory_reset - reset the array
+ of memory for pressed switches
+-------------------------------------------------*/
+
+INLINE void code_pressed_memory_reset(void)
+{
+ int memnum;
+ for (memnum = 0; memnum < MAX_PRESSED_SWITCHES; memnum++)
+ code_pressed_memory[memnum] = INPUT_CODE_INVALID;
+}
+
+
+/*-------------------------------------------------
+ string_to_code - convert a string to a code
+ via table lookup
+-------------------------------------------------*/
+
+INLINE UINT32 string_to_code(const code_string_table *table, const char *string)
+{
+ /* find a matching string */
+ for ( ; table->code != ~0; table++)
+ if (strcmp(string, table->string) == 0)
+ return table->code;
+
+ /* on failure, return ~0 */
+ return ~0;
+}
+
+
+/*-------------------------------------------------
+ code_to_string - convert a code to a string
+ via table lookup
+-------------------------------------------------*/
+
+INLINE const char *code_to_string(const code_string_table *table, UINT32 code)
+{
+ /* find a matching code, or */
+ for ( ; table->code != ~0; table++)
+ if (table->code == code)
+ return table->string;
+
+ /* return the default string */
+ return table->string;
+}
+
+
+
+/***************************************************************************
+ INITIALIZATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ input_init - initialize the input lists
+-------------------------------------------------*/
+
+void input_init(running_machine *machine)
+{
+ joystick_map map;
+
+ /* reset the device lists */
+ memset(device_list, 0, sizeof(device_list));
+
+ /* reset code memory */
+ code_pressed_memory_reset();
+
+ /* request a per-frame callback for bookkeeping */
+ add_frame_callback(machine, input_frame);
+
+ /* read input enable options */
+ device_list[DEVICE_CLASS_KEYBOARD].enabled = TRUE;
+ device_list[DEVICE_CLASS_MOUSE].enabled = options_get_bool(mame_options(), OPTION_MOUSE);
+ device_list[DEVICE_CLASS_LIGHTGUN].enabled = options_get_bool(mame_options(), OPTION_LIGHTGUN);
+ device_list[DEVICE_CLASS_JOYSTICK].enabled = options_get_bool(mame_options(), OPTION_JOYSTICK);
+
+ /* read input device multi options */
+ device_list[DEVICE_CLASS_KEYBOARD].multi = options_get_bool(mame_options(), OPTION_MULTIKEYBOARD);
+ device_list[DEVICE_CLASS_MOUSE].multi = options_get_bool(mame_options(), OPTION_MULTIMOUSE);
+ device_list[DEVICE_CLASS_LIGHTGUN].multi = TRUE;
+ device_list[DEVICE_CLASS_JOYSTICK].multi = TRUE;
+
+ /* read other input options */
+ steadykey_enabled = options_get_bool(mame_options(), OPTION_STEADYKEY);
+ lightgun_reload_button = options_get_bool(mame_options(), OPTION_OFFSCREEN_RELOAD);
+ joystick_deadzone = (INT32)(options_get_float(mame_options(), OPTION_JOYSTICK_DEADZONE) * INPUT_ABSOLUTE_MAX);
+ joystick_saturation = (INT32)(options_get_float(mame_options(), OPTION_JOYSTICK_SATURATION) * INPUT_ABSOLUTE_MAX);
+
+ /* get the default joystick map */
+ joystick_map_default = options_get_string(mame_options(), OPTION_JOYSTICK_MAP);
+ if (joystick_map_default[0] == 0 || strcmp(joystick_map_default, "auto") == 0)
+ joystick_map_default = joystick_map_8way;
+ if (!joystick_map_parse(joystick_map_default, &map))
+ mame_printf_error("Invalid joystick map: %s\n", joystick_map_default);
+ else if (joystick_map_default != joystick_map_8way)
+ joystick_map_print("Input: Default joystick map", joystick_map_default, &map);
+}
+
+
+/*-------------------------------------------------
+ input_device_class_enable - enable or disable
+ a device class
+-------------------------------------------------*/
+
+void input_device_class_enable(input_device_class devclass, UINT8 enable)
+{
+ assert(devclass > DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM);
+ device_list[devclass].enabled = enable;
+}
+
+
+/*-------------------------------------------------
+ input_enable_device_class - is a device class
+ enabled?
+-------------------------------------------------*/
+
+UINT8 input_device_class_enabled(input_device_class devclass)
+{
+ assert(devclass > DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM);
+ return device_list[devclass].enabled;
+}
+
+
+/*-------------------------------------------------
+ input_device_set_joystick_map - set the
+ joystick map for a device
+-------------------------------------------------*/
+
+int input_device_set_joystick_map(int devindex, const char *mapstring)
+{
+ int startindex = devindex;
+ int stopindex = devindex;
+ joystick_map map;
+ int joynum;
+
+ /* parse the map */
+ if (!joystick_map_parse(mapstring, &map))
+ return FALSE;
+
+ /* devindex -1 means set the same for all */
+ if (devindex == -1)
+ {
+ startindex = 0;
+ stopindex = device_list[DEVICE_CLASS_JOYSTICK].count - 1;
+ joystick_map_print("Input: Changing default joystick map", mapstring, &map);
+ }
+
+ /* ignore if out of range */
+ else if (devindex >= device_list[DEVICE_CLASS_JOYSTICK].count)
+ return TRUE;
+
+ /* iterate over joysticks and set the map */
+ for (joynum = startindex; joynum <= stopindex; joynum++)
+ device_list[DEVICE_CLASS_JOYSTICK].list[joynum].joymap = map;
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ input_frame - per-frame callback for various
+ bookkeeping
+-------------------------------------------------*/
+
+static void input_frame(running_machine *machine)
+{
+ /* if steadykey is enabled, do processing here */
+ if (steadykey_enabled)
+ {
+ int devnum;
+
+ /* iterate over keyboards */
+ for (devnum = 0; devnum < device_list[DEVICE_CLASS_KEYBOARD].count; devnum++)
+ {
+ input_device *device = &device_list[DEVICE_CLASS_KEYBOARD].list[devnum];
+ input_item_id itemid;
+ int changed = FALSE;
+
+ /* update the state of all the keys and see if any changed state */
+ for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++)
+ {
+ input_device_item *item = device->item[itemid];
+ if (item != NULL && item->itemclass == ITEM_CLASS_SWITCH)
+ {
+ input_item_update_value(item);
+ if ((item->current ^ item->oldkey) & 1)
+ {
+ changed = TRUE;
+
+ /* if the keypress was missed, turn it on for one frame */
+ if (((item->current | item->steadykey) & 1) == 0)
+ item->steadykey = 1;
+ }
+ }
+ }
+
+ /* if the keyboard state is stable, copy it over */
+ for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++)
+ {
+ input_device_item *item = device->item[itemid];
+ if (item != NULL && item->itemclass == ITEM_CLASS_SWITCH)
+ {
+ if (!changed)
+ item->steadykey = item->current;
+ item->oldkey = item->current;
+ }
+ }
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ OSD CONFIGURATION AND ACCESS
+***************************************************************************/
+
+/*-------------------------------------------------
+ input_device_add - add a new input device
+-------------------------------------------------*/
+
+input_device *input_device_add(input_device_class devclass, const char *name, void *internal)
+{
+ input_device_list *devlist = &device_list[devclass];
+ input_device *device;
+
+ assert_always(mame_get_phase(Machine) == MAME_PHASE_INIT, "Can only call input_device_add at init time!");
+ assert(name != NULL);
+ assert(devclass != DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM);
+
+ /* allocate a new device */
+ devlist->list = auto_realloc(devlist->list, (devlist->count + 1) * sizeof(devlist->list[0]));
+ device = &devlist->list[devlist->count++];
+ memset(device, 0, sizeof(*device));
+
+ /* fill in the data */
+ device->name = astring_cpyc(auto_astring_alloc(), name);
+ device->devclass = devclass;
+ device->devindex = devlist->count - 1;
+ device->internal = internal;
+
+ /* default to 8-way map for joysticks */
+ if (devclass == DEVICE_CLASS_JOYSTICK)
+ {
+ joystick_map_parse(joystick_map_default, &device->joymap);
+ device->lastmap = JOYSTICK_MAP_NEUTRAL;
+ }
+
+ mame_printf_verbose("Input: Adding %s #%d: %s\n", code_to_string(devclass_string_table, devclass), devlist->count, astring_c(device->name));
+ return device;
+}
+
+
+/*-------------------------------------------------
+ input_device_item_add - add a new item to an
+ input device
+-------------------------------------------------*/
+
+void input_device_item_add(input_device *device, const char *name, void *internal, input_item_id itemid, item_get_state_cb getstate)
+{
+ input_device_item *item;
+
+ assert_always(mame_get_phase(Machine) == MAME_PHASE_INIT, "Can only call input_device_item_add at init time!");
+ assert(name != NULL);
+ assert(itemid > ITEM_ID_INVALID && itemid < ITEM_ID_MAXIMUM);
+ assert(getstate != NULL);
+
+ /* if we have a generic ID, pick a new internal one */
+ if (itemid >= ITEM_ID_OTHER_SWITCH && itemid <= ITEM_ID_OTHER_AXIS_RELATIVE)
+ for (itemid = ITEM_ID_MAXIMUM + 1; itemid <= ITEM_ID_ABSOLUTE_MAXIMUM; itemid++)
+ if (device->item[itemid] == NULL)
+ break;
+ assert(itemid <= ITEM_ID_ABSOLUTE_MAXIMUM);
+
+ /* make sure we don't have any overlap */
+ assert(device->item[itemid] == NULL);
+
+ /* allocate a new item and copy data into it */
+ item = auto_malloc(sizeof(*item));
+ memset(item, 0, sizeof(*item));
+ device->item[itemid] = item;
+ device->maxitem = MAX(device->maxitem, itemid);
+
+ /* copy in the data passed in from the item list */
+ item->devclass = device->devclass;
+ item->devindex = device->devindex;
+ item->name = astring_cpyc(auto_astring_alloc(), name);
+ item->token = NULL;
+ item->internal = internal;
+ item->itemclass = input_item_standard_class(device->devclass, itemid);
+ item->itemid = itemid;
+ item->getstate = getstate;
+
+ /* if we're custom, create a tokenized name */
+ if (itemid > ITEM_ID_MAXIMUM)
+ {
+ /* copy the item name, removing spaces/underscores and making all caps */
+ item->token = astring_toupper(astring_cpyc(auto_astring_alloc(), name));
+ astring_delchr(item->token, ' ');
+ astring_delchr(item->token, '_');
+ }
+
+ /* otherwise, make sure we have a valid standard token */
+ else
+ {
+ assert(code_to_string(itemid_token_table, itemid) != NULL);
+ }
+}
+
+
+
+/***************************************************************************
+ STATE QUERIES
+***************************************************************************/
+
+/*-------------------------------------------------
+ input_code_value - return the value of a
+ given input code
+-------------------------------------------------*/
+
+INT32 input_code_value(input_code code)
+{
+ input_device_class devclass = INPUT_CODE_DEVCLASS(code);
+ int startindex = INPUT_CODE_DEVINDEX(code);
+ int stopindex = startindex;
+ INT32 result = 0;
+ int curindex;
+
+ profiler_mark(PROFILER_INPUT);
+
+ /* return 0 for any disabled or invalid device classes */
+ if (devclass <= DEVICE_CLASS_INVALID || devclass >= DEVICE_CLASS_MAXIMUM || !device_list[devclass].enabled)
+ goto exit;
+ if (startindex >= device_list[devclass].count)
+ goto exit;
+
+ /* if this is not a multi device, only return data for item 0 */
+ if (!device_list[devclass].multi)
+ {
+ if (startindex != 0)
+ goto exit;
+
+ /* otherwise, iterate over all */
+ startindex = 0;
+ stopindex = device_list[devclass].count - 1;
+ }
+
+ /* iterate over all indices */
+ for (curindex = startindex; curindex <= stopindex; curindex++)
+ {
+ /* lookup the item for the appropriate index */
+ input_device_item *item = input_code_item(INPUT_CODE_SET_DEVINDEX(code, curindex));
+ if (item == NULL)
+ continue;
+
+ /* update the value of this item */
+ input_item_update_value(item);
+
+ /* process items according to their native type */
+ switch (item->itemclass)
+ {
+ case ITEM_CLASS_ABSOLUTE:
+ result = convert_absolute_value(code, item);
+ break;
+
+ case ITEM_CLASS_RELATIVE:
+ result += convert_relative_value(code, item);
+ break;
+
+ case ITEM_CLASS_SWITCH:
+ result |= convert_switch_value(code, item);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+exit:
+ profiler_mark(PROFILER_END);
+ return result;
+}
+
+
+/*-------------------------------------------------
+ input_code_pressed - return non-zero if a
+ given input code has been pressed
+-------------------------------------------------*/
+
+int input_code_pressed(input_code code)
+{
+ return (input_code_value(code) != 0);
+}
+
+
+/*-------------------------------------------------
+ input_code_pressed_once - return non-zero if a
+ given input code has transitioned from off to
+ on since the last call
+-------------------------------------------------*/
+
+int input_code_pressed_once(input_code code)
+{
+ int curvalue = input_code_pressed(code);
+ int memnum, empty = -1;
+
+ /* look for the code in the memory */
+ for (memnum = 0; memnum < MAX_PRESSED_SWITCHES; memnum++)
+ {
+ /* were we previous pressed on the last time through here? */
+ if (code_pressed_memory[memnum] == code)
+ {
+ /* if no longer pressed, clear entry */
+ if (!curvalue)
+ code_pressed_memory[memnum] = INPUT_CODE_INVALID;
+
+ /* always return 0 */
+ return 0;
+ }
+
+ /* remember the first empty entry */
+ if (empty == -1 && code_pressed_memory[memnum] == INPUT_CODE_INVALID)
+ empty = memnum;
+ }
+
+ /* if we get here, we were not previously pressed; if still not pressed, return 0 */
+ if (!curvalue)
+ return 0;
+
+ /* otherwise, add ourself to the memory and return 1 */
+ assert(empty != -1);
+ if (empty != -1)
+ code_pressed_memory[empty] = code;
+ return 1;
+}
+
+
+/*-------------------------------------------------
+ input_code_poll_switches - poll for any input
+-------------------------------------------------*/
+
+input_code input_code_poll_switches(int reset)
+{
+ input_device_class devclass;
+
+ /* if resetting memory, do it now */
+ if (reset)
+ code_pressed_memory_reset();
+
+ /* iterate over device classes and devices */
+ for (devclass = DEVICE_CLASS_INVALID + 1; devclass < DEVICE_CLASS_MAXIMUM; devclass++)
+ {
+ input_device_list *devlist = &device_list[devclass];
+ int devnum;
+
+ /* iterate over devices within each class */
+ for (devnum = 0; devnum < devlist->count; devnum++)
+ {
+ input_device *device = &devlist->list[devnum];
+ input_item_id itemid;
+
+ /* iterate over items within each device */
+ for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++)
+ {
+ input_device_item *item = device->item[itemid];
+ if (item != NULL)
+ {
+ input_code code = device_item_to_code(device, itemid);
+
+ /* if the item is natively a switch, poll it */
+ if (item->itemclass == ITEM_CLASS_SWITCH)
+ {
+ if (input_code_pressed_once(code))
+ return code;
+ }
+
+ /* otherwise, poll axes digitally */
+ else
+ {
+ code = INPUT_CODE_SET_ITEMCLASS(code, ITEM_CLASS_SWITCH);
+
+ /* if this is a joystick X axis, check with left/right modifiers */
+ if (devclass == DEVICE_CLASS_JOYSTICK && INPUT_CODE_ITEMID(code) == ITEM_ID_XAXIS)
+ {
+ code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_LEFT);
+ if (input_code_pressed_once(code))
+ return code;
+ code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_RIGHT);
+ if (input_code_pressed_once(code))
+ return code;
+ }
+
+ /* if this is a joystick Y axis, check with up/down modifiers */
+ else if (devclass == DEVICE_CLASS_JOYSTICK && INPUT_CODE_ITEMID(code) == ITEM_ID_YAXIS)
+ {
+ code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_UP);
+ if (input_code_pressed_once(code))
+ return code;
+ code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_DOWN);
+ if (input_code_pressed_once(code))
+ return code;
+ }
+
+ /* any other axis, check with pos/neg modifiers */
+ else
+ {
+ code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_POS);
+ if (input_code_pressed_once(code))
+ return code;
+ code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_NEG);
+ if (input_code_pressed_once(code))
+ return code;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* if nothing, return an invalid code */
+ return INPUT_CODE_INVALID;
+}
+
+
+/*-------------------------------------------------
+ input_code_poll_axes - poll for any input
+-------------------------------------------------*/
+
+input_code input_code_poll_axes(int reset)
+{
+ input_device_class devclass;
+
+ /* iterate over device classes and devices */
+ for (devclass = DEVICE_CLASS_INVALID + 1; devclass < DEVICE_CLASS_MAXIMUM; devclass++)
+ {
+ input_device_list *devlist = &device_list[devclass];
+ int devnum;
+
+ /* iterate over devices within each class */
+ for (devnum = 0; devnum < devlist->count; devnum++)
+ {
+ input_device *device = &devlist->list[devnum];
+ input_item_id itemid;
+
+ /* iterate over items within each device */
+ for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++)
+ {
+ input_device_item *item = device->item[itemid];
+ if (item != NULL)
+ {
+ input_code code = device_item_to_code(device, itemid);
+ INT32 curval, diff;
+
+ /* skip any switches or already-reported axes */
+ if (item->itemclass == ITEM_CLASS_SWITCH)
+ continue;
+
+ /* poll the current value and reset the memory */
+ curval = input_code_value(code);
+ if (reset)
+ item->memory = curval;
+
+ /* if we've already reported this one, don't bother */
+ if (item->memory == INVALID_AXIS_VALUE)
+ continue;
+
+ /* compute the diff against memory */
+ diff = curval - item->memory;
+ if (diff < 0)
+ diff = -diff;
+
+ /* for absolute axes, look for 25% of maximum */
+ if (item->itemclass == ITEM_CLASS_ABSOLUTE && diff > (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN) / 4)
+ {
+ item->memory = INVALID_AXIS_VALUE;
+ return code;
+ }
+
+ /* for relative axes, look for ~20 pixels movement */
+ if (item->itemclass == ITEM_CLASS_RELATIVE && diff > 20 * INPUT_RELATIVE_PER_PIXEL)
+ {
+ item->memory = INVALID_AXIS_VALUE;
+ return code;
+ }
+ }
+ }
+ }
+ }
+
+ /* if nothing, return an invalid code */
+ return INPUT_CODE_INVALID;
+}
+
+
+
+/***************************************************************************
+ STRINGS AND TOKENIZATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ input_code_name - convert an input code into
+ a friendly name
+-------------------------------------------------*/
+
+astring *input_code_name(astring *string, input_code code)
+{
+ input_device_item *item = input_code_item(code);
+ const char *devclass;
+ const char *devcode;
+ const char *modifier;
+ char devindex[10];
+
+ /* if nothing there, return an empty string */
+ if (item == NULL)
+ return astring_reset(string);
+
+ /* determine the devclass part */
+ devclass = code_to_string(devclass_string_table, INPUT_CODE_DEVCLASS(code));
+
+ /* determine the devindex part */
+ sprintf(devindex, "%d", INPUT_CODE_DEVINDEX(code) + 1);
+
+ /* if we're unifying all devices, don't display a number */
+ if (!device_list[INPUT_CODE_DEVCLASS(code)].multi)
+ devindex[0] = 0;
+
+ /* keyboard 0 doesn't show a class or index if it is the only one */
+ if (item->devclass == DEVICE_CLASS_KEYBOARD && device_list[DEVICE_CLASS_KEYBOARD].count == 1)
+ {
+ devclass = "";
+ devindex[0] = 0;
+ }
+
+ /* devcode part comes from the item name */
+ devcode = astring_c(item->name);
+
+ /* determine the modifier part */
+ modifier = code_to_string(modifier_string_table, INPUT_CODE_MODIFIER(code));
+
+ /* devcode is redundant with joystick switch left/right/up/down */
+ if (item->devclass == DEVICE_CLASS_JOYSTICK && INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH)
+ if (INPUT_CODE_MODIFIER(code) >= ITEM_MODIFIER_LEFT && INPUT_CODE_MODIFIER(code) <= ITEM_MODIFIER_DOWN)
+ devcode = "";
+
+ /* concatenate the strings */
+ astring_cpyc(string, devclass);
+ if (devindex[0] != 0)
+ astring_catc(astring_catc(string, " "), devindex);
+ if (devcode[0] != 0)
+ astring_catc(astring_catc(string, " "), devcode);
+ if (modifier[0] != 0)
+ astring_catc(astring_catc(string, " "), modifier);
+
+ /* delete any leading spaces */
+ return astring_trimspace(string);
+}
+
+
+/*-------------------------------------------------
+ input_code_to_token - create a token for
+ a given code
+-------------------------------------------------*/
+
+astring *input_code_to_token(astring *string, input_code code)
+{
+ input_device_item *item = input_code_item(code);
+ const char *devclass;
+ const char *devcode;
+ const char *itemclass;
+ const char *modifier;
+ char devindex[10];
+
+ /* determine the devclass part */
+ devclass = code_to_string(devclass_token_table, INPUT_CODE_DEVCLASS(code));
+
+ /* determine the devindex part; keyboard 0 doesn't show an index */
+ sprintf(devindex, "%d", INPUT_CODE_DEVINDEX(code) + 1);
+ if (INPUT_CODE_DEVCLASS(code) == DEVICE_CLASS_KEYBOARD && INPUT_CODE_DEVINDEX(code) == 0)
+ devindex[0] = 0;
+
+ /* determine the itemid part; look up in the table if we don't have a token */
+ if (item != NULL && item->token != NULL)
+ devcode = astring_c(item->token);
+ else
+ devcode = code_to_string(itemid_token_table, INPUT_CODE_ITEMID(code));
+ assert(devcode != NULL);
+
+ /* determine the modifier part */
+ modifier = code_to_string(modifier_token_table, INPUT_CODE_MODIFIER(code));
+
+ /* determine the itemclass part; if we match the native class, we don't include this */
+ if (item == NULL || item->itemclass == INPUT_CODE_ITEMCLASS(code))
+ itemclass = "";
+ else
+ itemclass = code_to_string(itemclass_token_table, INPUT_CODE_ITEMCLASS(code));
+
+ /* concatenate the strings */
+ astring_cpyc(string, devclass);
+ if (devindex[0] != 0)
+ astring_catc(astring_catc(string, "_"), devindex);
+ if (devcode[0] != 0)
+ astring_catc(astring_catc(string, "_"), devcode);
+ if (modifier[0] != 0)
+ astring_catc(astring_catc(string, "_"), modifier);
+ if (itemclass[0] != 0)
+ astring_catc(astring_catc(string, "_"), itemclass);
+ return string;
+}
+
+
+/*-------------------------------------------------
+ input_code_from_token - extract an input
+ code from a token
+-------------------------------------------------*/
+
+input_code input_code_from_token(const char *_token)
+{
+ UINT32 devclass, itemid, devindex, modifier, standard;
+ UINT32 itemclass = ITEM_CLASS_INVALID;
+ input_code code = INPUT_CODE_INVALID;
+ astring *token[6] = { NULL };
+ int numtokens, curtok;
+
+ /* copy the token and break it into pieces */
+ for (numtokens = 0; numtokens < ARRAY_LENGTH(token); )
+ {
+ /* make a token up to the next underscore */
+ char *score = strchr(_token, '_');
+ token[numtokens++] = astring_dupch(_token, (score == NULL) ? strlen(_token) : (score - _token));
+
+ /* if we hit the end, we're done, else advance our pointer */
+ if (score == NULL)
+ break;
+ _token = score + 1;
+ }
+
+ /* first token should be the devclass */
+ curtok = 0;
+ devclass = string_to_code(devclass_token_table, astring_c(token[curtok++]));
+ if (devclass == ~0)
+ goto exit;
+
+ /* second token might be index; look for number */
+ devindex = 0;
+ if (numtokens > 2 && sscanf(astring_c(token[curtok]), "%d", &devindex) == 1)
+ {
+ curtok++;
+ devindex--;
+ }
+ if (curtok >= numtokens)
+ goto exit;
+
+ /* next token is the item ID */
+ itemid = string_to_code(itemid_token_table, astring_c(token[curtok]));
+ standard = (itemid != ~0);
+
+ /* if we're a standard code, default the itemclass based on it */
+ if (standard)
+ itemclass = input_item_standard_class(devclass, itemid);
+
+ /* otherwise, keep parsing */
+ else
+ {
+ input_device *device;
+
+ /* if this is an invalid device, we have nothing to look up */
+ if (devindex >= device_list[devclass].count)
+ goto exit;
+ device = &device_list[devclass].list[devindex];
+
+ /* if not a standard code, look it up in the device specific codes */
+ for (itemid = ITEM_ID_INVALID + 1; itemid <= device->maxitem; itemid++)
+ {
+ input_device_item *item = device->item[itemid];
+ if (item != NULL && item->token != NULL && astring_cmp(token[curtok], item->token) == 0)
+ {
+ /* take the itemclass from the item */
+ itemclass = item->itemclass;
+ break;
+ }
+ }
+
+ /* bail on fail */
+ if (itemid > device->maxitem)
+ goto exit;
+ }
+ curtok++;
+
+ /* if we have another token, it is probably a modifier */
+ modifier = ITEM_MODIFIER_NONE;
+ if (curtok < numtokens)
+ {
+ modifier = string_to_code(modifier_token_table, astring_c(token[curtok]));
+ if (modifier != ~0)
+ curtok++;
+ else
+ modifier = ITEM_MODIFIER_NONE;
+ }
+
+ /* if we have another token, it is the item class */
+ if (curtok < numtokens)
+ {
+ UINT32 temp = string_to_code(itemclass_token_table, astring_c(token[curtok]));
+ if (temp != ~0)
+ {
+ curtok++;
+ itemclass = temp;
+ }
+ }
+
+ /* we should have consumed all tokens */
+ if (curtok != numtokens)
+ goto exit;
+
+ /* assemble the final code */
+ code = INPUT_CODE(devclass, devindex, itemclass, modifier, itemid);
+
+exit:
+ for (curtok = 0; curtok < ARRAY_LENGTH(token); curtok++)
+ if (token[curtok] != NULL)
+ astring_free(token[curtok]);
+ return code;
+}
+
+
+
+/***************************************************************************
+ INTERNAL FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ input_code_item - get a pointer to the item
+ from the code
+-------------------------------------------------*/
+
+static input_device_item *input_code_item(input_code code)
+{
+ input_device *device = input_code_device(code);
+ input_item_id itemid;
+
+ /* if no device, we fail */
+ if (device == NULL)
+ return NULL;
+
+ /* if the devcode is in range, return a pointer to the item */
+ itemid = INPUT_CODE_ITEMID(code);
+ if (itemid < ARRAY_LENGTH(device->item))
+ return device->item[itemid];
+
+ /* otherwise fail with NULL */
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ convert_absolute_value - convert an absolute
+ value into the class specified by code
+-------------------------------------------------*/
+
+static INT32 convert_absolute_value(input_code code, input_device_item *item)
+{
+ /* make sure values are valid */
+ assert(item->current >= INPUT_ABSOLUTE_MIN && item->current <= INPUT_ABSOLUTE_MAX);
+
+ /* relative value conversion not supported */
+ if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_RELATIVE)
+ return 0;
+
+ /* if we want the absolute value, process it according to the modifier field */
+ else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_ABSOLUTE)
+ {
+ input_item_modifier modifier = INPUT_CODE_MODIFIER(code);
+ INT32 result = apply_deadzone_and_saturation(code, item->current);
+
+ /* if we're doing a lightgun reload hack, override the value */
+ if (lightgun_reload_button && item->devclass == DEVICE_CLASS_LIGHTGUN)
+ {
+ input_device_item *button2_item = device_list[item->devclass].list[item->devindex].item[ITEM_ID_BUTTON2];
+ if (button2_item != NULL)
+ {
+ /* if it is pressed, return (min,max) */
+ input_item_update_value(button2_item);
+ if (button2_item->current)
+ result = (INPUT_CODE_ITEMID(code) == ITEM_ID_XAXIS) ? INPUT_ABSOLUTE_MIN : INPUT_ABSOLUTE_MAX;
+ }
+ }
+
+ /* standard axis: apply deadzone and saturation */
+ if (modifier == ITEM_MODIFIER_NONE)
+ return result;
+
+ /* positive/negative: scale to full axis */
+ else if (modifier == ITEM_MODIFIER_POS)
+ return MAX(result, 0) * 2 + INPUT_ABSOLUTE_MIN;
+ else if (modifier == ITEM_MODIFIER_NEG)
+ return MAX(-result, 0) * 2 + INPUT_ABSOLUTE_MIN;
+ }
+
+ /* if we want a switch value, process it according to the modifier field */
+ else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH)
+ {
+ input_item_modifier modifier = INPUT_CODE_MODIFIER(code);
+
+ /* left/right/up/down: if this is a joystick, fetch the paired X/Y axis values and convert */
+ if (modifier >= ITEM_MODIFIER_LEFT && modifier <= ITEM_MODIFIER_DOWN && item->devclass == DEVICE_CLASS_JOYSTICK)
+ {
+ input_device *device = &device_list[item->devclass].list[item->devindex];
+ input_device_item *xaxis_item = device->item[ITEM_ID_XAXIS];
+ input_device_item *yaxis_item = device->item[ITEM_ID_YAXIS];
+ if (xaxis_item != NULL && yaxis_item != NULL)
+ {
+ INT32 xaxisval, yaxisval;
+ UINT8 mapval;
+
+ /* determine which item we didn't update, and update it */
+ assert(item == xaxis_item || item == yaxis_item);
+ input_item_update_value((item == xaxis_item) ? yaxis_item : xaxis_item);
+
+ /* now map the X and Y axes to a 9x9 grid */
+ xaxisval = ((xaxis_item->current - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1);
+ yaxisval = ((yaxis_item->current - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1);
+ mapval = device->joymap.map[yaxisval][xaxisval];
+
+ /* handle stickiness */
+ if (mapval == JOYSTICK_MAP_STICKY)
+ mapval = device->lastmap;
+ else
+ device->lastmap = mapval;
+
+ /* return based on whether the appropriate bit is set */
+ return (mapval >> (modifier - ITEM_MODIFIER_LEFT)) & 1;
+ }
+ }
+
+ /* positive/negative: TRUE if past the deadzone in either direction */
+ if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN)
+ return (apply_deadzone_and_saturation(code, item->current) > 0);
+ else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP)
+ return (apply_deadzone_and_saturation(code, item->current) < 0);
+ }
+
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ convert_relative_value - convert a relative
+ value into the class specified by code
+-------------------------------------------------*/
+
+static INT32 convert_relative_value(input_code code, input_device_item *item)
+{
+ /* if we want relative values, take it as-is */
+ if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_RELATIVE)
+ return item->current;
+
+ /* absolute value conversion not supported */
+ else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_ABSOLUTE)
+ return 0;
+
+ /* if we want a switch value, process it according to the modifier field */
+ else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH)
+ {
+ input_item_modifier modifier = INPUT_CODE_MODIFIER(code);
+
+ /* positive/negative: TRUE if non-zero in either direction */
+ if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN)
+ return (item->current > 0);
+ else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP)
+ return (item->current < 0);
+ }
+
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ convert_switch_value - convert a switch
+ value into the class specified by code
+-------------------------------------------------*/
+
+static INT32 convert_switch_value(input_code code, input_device_item *item)
+{
+ /* only a switch is supported */
+ if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH)
+ {
+ /* if we're doing a lightgun reload hack, button 1 and 2 operate differently */
+ if (lightgun_reload_button && item->devclass == DEVICE_CLASS_LIGHTGUN)
+ {
+ /* button 1 is pressed if either button 1 or 2 are active */
+ if (INPUT_CODE_ITEMID(code) == ITEM_ID_BUTTON1)
+ {
+ input_device_item *button2_item = device_list[item->devclass].list[item->devindex].item[ITEM_ID_BUTTON2];
+ if (button2_item != NULL)
+ {
+ input_item_update_value(button2_item);
+ return item->current | button2_item->current;
+ }
+ }
+
+ /* button 2 is never officially pressed */
+ if (INPUT_CODE_ITEMID(code) == ITEM_ID_BUTTON2)
+ return 0;
+ }
+
+ /* steadykey for keyboards */
+ if (steadykey_enabled && item->devclass == DEVICE_CLASS_KEYBOARD)
+ return item->steadykey;
+
+ /* everything else is just the current value as-is */
+ return item->current;
+ }
+
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ apply_deadzone_and_saturation - apply global
+ deadzone and saturation parameters to an
+ absolute value
+-------------------------------------------------*/
+
+static INT32 apply_deadzone_and_saturation(input_code code, INT32 result)
+{
+ int negative = FALSE;
+
+ /* ignore if not a joystick */
+ if (INPUT_CODE_DEVCLASS(code) != DEVICE_CLASS_JOYSTICK)
+ return result;
+
+ /* properties are symmetric */
+ if (result < 0)
+ {
+ negative = TRUE;
+ result = -result;
+ }
+
+ /* if in the deadzone, return 0 */
+ if (result < joystick_deadzone)
+ result = 0;
+
+ /* if saturated, return the max */
+ else if (result > joystick_saturation)
+ result = INPUT_ABSOLUTE_MAX;
+
+ /* otherwise, scale */
+ else
+ result = (INT64)(result - joystick_deadzone) * (INT64)INPUT_ABSOLUTE_MAX / (INT64)(joystick_saturation - joystick_deadzone);
+
+ /* apply sign and return */
+ return negative ? -result : result;
+}
+
+
+/*-------------------------------------------------
+ joystick_map_parse - parse a string into
+ a joystick map
+-------------------------------------------------*/
+
+static int joystick_map_parse(const char *mapstring, joystick_map *map)
+{
+ int rownum, colnum;
+
+ /* iterate over rows */
+ for (rownum = 0; rownum < 9; rownum++)
+ {
+ /* if we're done, copy from another row */
+ if (*mapstring == 0 || *mapstring == '.')
+ {
+ int symmetric = (rownum >= 5 && *mapstring == 0);
+ const UINT8 *srcrow = &map->map[symmetric ? (8 - rownum) : (rownum - 1)][0];
+
+ /* if this is row 0, we don't have a source row -- invalid */
+ if (rownum == 0)
+ return FALSE;
+
+ /* copy from the srcrow, applying up/down symmetry if in the bottom half */
+ for (colnum = 0; colnum < 9; colnum++)
+ {
+ UINT8 val = srcrow[colnum];
+ if (symmetric)
+ val = (val & (JOYSTICK_MAP_LEFT | JOYSTICK_MAP_RIGHT)) | ((val & JOYSTICK_MAP_UP) << 1) | ((val & JOYSTICK_MAP_DOWN) >> 1);
+ map->map[rownum][colnum] = val;
+ }
+ }
+
+ /* otherwise, parse this column */
+ else
+ {
+ for (colnum = 0; colnum < 9; colnum++)
+ {
+ /* if we're at the end of row, copy previous to the middle, then apply left/right symmetry */
+ if (*mapstring == 0 || *mapstring == '.')
+ {
+ int symmetric = (colnum >= 5);
+ UINT8 val = map->map[rownum][symmetric ? (8 - colnum) : (colnum - 1)];
+ if (symmetric)
+ val = (val & (JOYSTICK_MAP_UP | JOYSTICK_MAP_DOWN)) | ((val & JOYSTICK_MAP_LEFT) << 1) | ((val & JOYSTICK_MAP_RIGHT) >> 1);
+ map->map[rownum][colnum] = val;
+ }
+
+ /* otherwise, convert the character to its value */
+ else
+ {
+ static const UINT8 charmap[] =
+ {
+ JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT,
+ JOYSTICK_MAP_UP,
+ JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT,
+ JOYSTICK_MAP_LEFT,
+ JOYSTICK_MAP_NEUTRAL,
+ JOYSTICK_MAP_RIGHT,
+ JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT,
+ JOYSTICK_MAP_DOWN,
+ JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT,
+ JOYSTICK_MAP_STICKY
+ };
+ static const char *validchars = "789456123s";
+ const char *ptr = strchr(validchars, *mapstring++);
+
+ /* invalid characters exit immediately */
+ if (ptr == NULL)
+ return FALSE;
+ map->map[rownum][colnum] = charmap[ptr - validchars];
+ }
+ }
+ }
+
+ /* if we ended with a period, advance to the next row */
+ if (*mapstring == '.')
+ mapstring++;
+ }
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ joystick_map_print - print a joystick map via
+ the verbose output
+-------------------------------------------------*/
+
+static void joystick_map_print(const char *header, const char *origstring, const joystick_map *map)
+{
+ int rownum, colnum;
+
+ mame_printf_verbose("%s = %s\n", header, origstring);
+ for (rownum = 0; rownum < 9; rownum++)
+ {
+ mame_printf_verbose(" ");
+ for (colnum = 0; colnum < 9; colnum++)
+ switch (map->map[rownum][colnum])
+ {
+ case JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT: mame_printf_verbose("7"); break;
+ case JOYSTICK_MAP_UP: mame_printf_verbose("8"); break;
+ case JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT: mame_printf_verbose("9"); break;
+ case JOYSTICK_MAP_LEFT: mame_printf_verbose("4"); break;
+ case JOYSTICK_MAP_NEUTRAL: mame_printf_verbose("5"); break;
+ case JOYSTICK_MAP_RIGHT: mame_printf_verbose("6"); break;
+ case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT: mame_printf_verbose("1"); break;
+ case JOYSTICK_MAP_DOWN: mame_printf_verbose("2"); break;
+ case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT:mame_printf_verbose("3"); break;
+ case JOYSTICK_MAP_STICKY: mame_printf_verbose("s"); break;
+ default: mame_printf_verbose("?"); break;
+ }
+
+ mame_printf_verbose("\n");
+ }
+}