diff options
author | 2012-05-03 09:00:08 +0000 | |
---|---|---|
committer | 2012-05-03 09:00:08 +0000 | |
commit | 2a88e54278acc526c11dba7961204f234f1f6e05 (patch) | |
tree | 1d78e932e47bb535e7e56ddac05232cdf551c3a1 /src/emu/ioport.c | |
parent | 605a48921b48e0127ff0330f1e895dcf16d084fb (diff) |
ioport.c C++ conversion. Mostly internal changes, with no
intended differences from previous behavior. For drivers,
the main change is that input_port_read() no longer exists.
Instead, the port must be fetched from the appropriate device,
and then read() is called.
For member functions, this is actually simpler/cleaner:
value = ioport("tag")->read()
For legacy functions which have a driver_data state, it goes:
value = state->ioport("tag")->read()
For other legacy functions, they need to fetch the root device:
value = machine.root_device().ioport("tag")->read()
The other big change for drivers is that IPT_VBLANK is gone.
Instead, it has been replaced by a device line callback on the
screen device. There's a new macro PORT_VBLANK("tag") which
automatically points things to the right spot.
Here's a set of imperfect search & replace strings to convert
the input_port_read calls and fix up IPT_VBLANK:
input_port_read( *\( *)(machine\(\)) *, *([^)]+ *\))
ioport\1\3->read\(\)
input_port_read( *\( *)(.*machine[()]*) *, *([^)]+ *\))
\2\.root_device\(\)\.ioport\1\3->read\(\)
(state = .*driver_data[^}]+)space->machine\(\)\.root_device\(\)\.
\1state->
(state = .*driver_data[^}]+)device->machine\(\)\.root_device\(\)\.
\1state->
input_port_read_safe( *\( *)(machine\(\)) *, *([^,]+), *([^)]+\))
ioport\1\3->read_safe\(\4\)
IPT_VBLANK( *\))
IPT_CUSTOM\1 PORT_VBLANK("screen")
Diffstat (limited to 'src/emu/ioport.c')
-rw-r--r-- | src/emu/ioport.c | 6807 |
1 files changed, 3228 insertions, 3579 deletions
diff --git a/src/emu/ioport.c b/src/emu/ioport.c index 081b1a7d6a6..2341aa9a51c 100644 --- a/src/emu/ioport.c +++ b/src/emu/ioport.c @@ -4,8 +4,36 @@ Input/output port handling. - Copyright Nicola Salmoria and the MAME Team. - Visit http://mamedev.org for licensing and usage restrictions. +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. **************************************************************************** @@ -103,459 +131,527 @@ #include <ctype.h> #include <time.h> -/* temporary: set this to 1 to enable the originally defined behavior that - a field specified via PORT_MODIFY which intersects a previously-defined - field completely wipes out the previous definition */ +// temporary: set this to 1 to enable the originally defined behavior that +// a field specified via PORT_MODIFY which intersects a previously-defined +// field completely wipes out the previous definition #define INPUT_PORT_OVERRIDE_FULLY_NUKES_PREVIOUS 1 -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -/* these constants must match the order of the joystick directions in the IPT definition */ -#define JOYDIR_UP 0 -#define JOYDIR_DOWN 1 -#define JOYDIR_LEFT 2 -#define JOYDIR_RIGHT 3 +//************************************************************************** +// DEBUGGING +//************************************************************************** + +#define LOG_NATURAL_KEYBOARD 0 + + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +const int SPACE_COUNT = 3; +const int KEY_BUFFER_SIZE = 4096; +const unicode_char INVALID_CHAR = '?'; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// live analog field information +class analog_field +{ + friend class simple_list<analog_field>; + friend class ioport_manager; + friend void ioport_field::set_user_settings(const ioport_field::user_settings &settings); + +public: + // construction/destruction + analog_field(ioport_field &field); + + // getters + analog_field *next() const { return m_next; } + ioport_manager &manager() const { return m_field.manager(); } + ioport_field &field() const { return m_field; } + INT32 sensitivity() const { return m_sensitivity; } + bool reverse() const { return m_reverse; } + INT32 delta() const { return m_delta; } + INT32 centerdelta() const { return m_centerdelta; } + + // helpers + ioport_value apply_min_max(ioport_value value) const; + ioport_value apply_settings(ioport_value value) const; + ioport_value apply_sensitivity(ioport_value value) const; + ioport_value apply_inverse_sensitivity(ioport_value value) const; + + // readers + void read(ioport_value &value); + float crosshair_read(); + void frame_update(running_machine &machine); + +private: + // internal state + analog_field * m_next; // link to the next analog state for this port + ioport_field & m_field; // pointer to the input field referenced + + // adjusted values (right-justified and tweaked) + UINT8 m_shift; // shift to align final value in the port + INT32 m_adjdefvalue; // adjusted default value from the config + INT32 m_adjmin; // adjusted minimum value from the config + INT32 m_adjmax; // adjusted maximum value from the config + + // live values of configurable parameters + INT32 m_sensitivity; // current live sensitivity (100=normal) + bool m_reverse; // current live reverse flag + INT32 m_delta; // current live delta to apply each frame a digital inc/dec key is pressed + INT32 m_centerdelta; // current live delta to apply each frame no digital inputs are pressed + + // live analog value tracking + INT32 m_accum; // accumulated value (including relative adjustments) + INT32 m_previous; // previous adjusted value + INT32 m_previousanalog; // previous analog value + + // parameters for modifying live values + INT32 m_minimum; // minimum adjusted value + INT32 m_maximum; // maximum adjusted value + INT32 m_center; // center adjusted value for autocentering + INT32 m_reverse_val; // value where we subtract from to reverse directions + + // scaling factors + INT64 m_scalepos; // scale factor to apply to positive adjusted values + INT64 m_scaleneg; // scale factor to apply to negative adjusted values + INT64 m_keyscalepos; // scale factor to apply to the key delta field when pos + INT64 m_keyscaleneg; // scale factor to apply to the key delta field when neg + INT64 m_positionalscale; // scale factor to divide a joystick into positions + + // misc flags + bool m_absolute; // is this an absolute or relative input? + bool m_wraps; // does the control wrap around? + bool m_autocenter; // autocenter this input? + bool m_single_scale; // scale joystick differently if default is between min/max + bool m_interpolate; // should we do linear interpolation for mid-frame reads? + bool m_lastdigital; // was the last modification caused by a digital form? +}; -#define JOYDIR_UP_BIT (1 << JOYDIR_UP) -#define JOYDIR_DOWN_BIT (1 << JOYDIR_DOWN) -#define JOYDIR_LEFT_BIT (1 << JOYDIR_LEFT) -#define JOYDIR_RIGHT_BIT (1 << JOYDIR_RIGHT) -#define LOG_INPUTX 0 -#define SPACE_COUNT 3 -#define INVALID_CHAR '?' -#define IP_NAME_DEFAULT NULL +// live device field information +class dynamic_field +{ + friend class simple_list<dynamic_field>; + +public: + // construction/destruction + dynamic_field(ioport_field &field); + + // getters + dynamic_field *next() const { return m_next; } + ioport_field &field() const { return m_field; } + + // read/write + void read(ioport_value &result); + void write(ioport_value newval); + +private: + // internal state + dynamic_field * m_next; // linked list of info for this port + ioport_field & m_field; // reference to the input field + UINT8 m_shift; // shift to apply to the final result + ioport_value m_oldval; // last value +}; -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ +// internal live state of an input field +struct ioport_field_live +{ + // construction/destruction + ioport_field_live(ioport_field &field, analog_field *analog); -/* live analog field information */ -typedef struct _analog_field_state analog_field_state; -struct _analog_field_state -{ - analog_field_state * next; /* link to the next analog state for this port */ - const input_field_config * field; /* pointer to the input field referenced */ - - /* adjusted values (right-justified and tweaked) */ - UINT8 shift; /* shift to align final value in the port */ - INT32 adjdefvalue; /* adjusted default value from the config */ - INT32 adjmin; /* adjusted minimum value from the config */ - INT32 adjmax; /* adjusted maximum value from the config */ - - /* live values of configurable parameters */ - INT32 sensitivity; /* current live sensitivity (100=normal) */ - UINT8 reverse; /* current live reverse flag */ - INT32 delta; /* current live delta to apply each frame a digital inc/dec key is pressed */ - INT32 centerdelta; /* current live delta to apply each frame no digital inputs are pressed */ - - /* live analog value tracking */ - INT32 accum; /* accumulated value (including relative adjustments) */ - INT32 previous; /* previous adjusted value */ - INT32 previousanalog; /* previous analog value */ - - /* parameters for modifying live values */ - INT32 minimum; /* minimum adjusted value */ - INT32 maximum; /* maximum adjusted value */ - INT32 center; /* center adjusted value for autocentering */ - INT32 reverse_val; /* value where we subtract from to reverse directions */ - - /* scaling factors */ - INT64 scalepos; /* scale factor to apply to positive adjusted values */ - INT64 scaleneg; /* scale factor to apply to negative adjusted values */ - INT64 keyscalepos; /* scale factor to apply to the key delta field when pos */ - INT64 keyscaleneg; /* scale factor to apply to the key delta field when neg */ - INT64 positionalscale; /* scale factor to divide a joystick into positions */ - - /* misc flags */ - UINT8 absolute; /* is this an absolute or relative input? */ - UINT8 wraps; /* does the control wrap around? */ - UINT8 autocenter; /* autocenter this input? */ - UINT8 single_scale; /* scale joystick differently if default is between min/max */ - UINT8 interpolate; /* should we do linear interpolation for mid-frame reads? */ - UINT8 lastdigital; /* was the last modification caused by a digital form? */ + // public state + analog_field * analog; // pointer to live analog data if this is an analog field + digital_joystick * joystick; // pointer to digital joystick information + input_seq seq[SEQ_TYPE_TOTAL];// currently configured input sequences + ioport_value value; // current value of this port + UINT8 impulse; // counter for impulse controls + bool last; // were we pressed last time? + digital_joystick::direction_t joydir; // digital joystick direction index + astring name; // overridden name }; -/* live device field information */ -typedef struct _device_field_info device_field_info; -struct _device_field_info +// internal live state of an input port +struct ioport_port_live { - device_field_info * next; /* linked list of info for this port */ - const input_field_config * field; /* pointer to the input field referenced */ - device_t * device; /* device */ - UINT8 shift; /* shift to apply to the final result */ - input_port_value oldval; /* last value */ + // construction/destruction + ioport_port_live(ioport_port &port); + + // public state + simple_list<analog_field> analoglist; // list of analog port info + simple_list<dynamic_field> readlist; // list of dynamic read fields + simple_list<dynamic_field> writelist; // list of dynamic write fields + ioport_value defvalue; // combined default value across the port + ioport_value digital; // current value from all digital inputs + ioport_value outputvalue; // current value for outputs }; -/* internal live state of an input field */ -struct _input_field_state +// character information +struct char_info { - analog_field_state * analog; /* pointer to live analog data if this is an analog field */ - digital_joystick_state * joystick; /* pointer to digital joystick information */ - input_seq seq[SEQ_TYPE_TOTAL];/* currently configured input sequences */ - input_port_value value; /* current value of this port */ - UINT8 impulse; /* counter for impulse controls */ - UINT8 last; /* were we pressed last time? */ - UINT8 joydir; /* digital joystick direction index */ - char * name; /* overridden name */ + unicode_char ch; + const char *name; + const char *alternate; // alternative string, in UTF-8 + + static const char_info *find(unicode_char target); }; -/* internal live state of an input port */ -struct _input_port_state -{ - analog_field_state * analoglist; /* pointer to list of analog port info */ - device_field_info * readdevicelist; /* pointer to list of input device info */ - device_field_info * writedevicelist; /* pointer to list of output device info */ - input_port_value defvalue; /* combined default value across the port */ - input_port_value digital; /* current value from all digital inputs */ - input_port_value vblank; /* value of all IPT_VBLANK bits */ - input_port_value outputvalue; /* current value for outputs */ -}; +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** -#define KEY_BUFFER_SIZE 4096 +//------------------------------------------------- +// compute_scale -- compute an 8.24 scale value +// from a numerator and a denominator +//------------------------------------------------- -typedef struct _char_info char_info; -struct _char_info +inline INT32 compute_scale(INT32 num, INT32 den) { - unicode_char ch; - const char *name; - const char *alternate; /* alternative string, in UTF-8 */ -}; + return (INT64(num) << 24) / den; +} -/*************************************************************************** - MACROS -***************************************************************************/ +//------------------------------------------------- +// recip_scale -- compute an 8.24 reciprocal of +// an 8.24 scale value +//------------------------------------------------- + +inline INT32 recip_scale(INT32 scale) +{ + return (INT64(1) << 48) / scale; +} + -#define APPLY_SENSITIVITY(x,s) (((INT64)(x) * (s)) / 100.0 + 0.5) -#define APPLY_INVERSE_SENSITIVITY(x,s) (((INT64)(x) * 100) / (s)) +//------------------------------------------------- +// apply_scale -- apply an 8.24 scale value to +// a 32-bit value +//------------------------------------------------- -#define COMPUTE_SCALE(num,den) (((INT64)(num) << 24) / (den)) -#define RECIP_SCALE(s) (((INT64)1 << 48) / (s)) -#define APPLY_SCALE(x,s) (((INT64)(x) * (s)) >> 24) +inline INT32 apply_scale(INT32 value, INT32 scale) +{ + return (INT64(value) * scale) >> 24; +} -/*************************************************************************** - GLOBAL VARIABLES -***************************************************************************/ +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** -/* XML attributes for the different types */ +// XML attributes for the different types static const char *const seqtypestrings[] = { "standard", "increment", "decrement" }; - +// master character info table static const char_info charinfo[] = { - { 0x0008, "Backspace", NULL }, /* Backspace */ - { 0x0009, "Tab", " " }, /* Tab */ - { 0x000c, "Clear", NULL }, /* Clear */ - { 0x000d, "Enter", NULL }, /* Enter */ - { 0x001a, "Esc", NULL }, /* Esc */ - { 0x0020, "Space", " " }, /* Space */ - { 0x0061, NULL, "A" }, /* a */ - { 0x0062, NULL, "B" }, /* b */ - { 0x0063, NULL, "C" }, /* c */ - { 0x0064, NULL, "D" }, /* d */ - { 0x0065, NULL, "E" }, /* e */ - { 0x0066, NULL, "F" }, /* f */ - { 0x0067, NULL, "G" }, /* g */ - { 0x0068, NULL, "H" }, /* h */ - { 0x0069, NULL, "I" }, /* i */ - { 0x006a, NULL, "J" }, /* j */ - { 0x006b, NULL, "K" }, /* k */ - { 0x006c, NULL, "L" }, /* l */ - { 0x006d, NULL, "M" }, /* m */ - { 0x006e, NULL, "N" }, /* n */ - { 0x006f, NULL, "O" }, /* o */ - { 0x0070, NULL, "P" }, /* p */ - { 0x0071, NULL, "Q" }, /* q */ - { 0x0072, NULL, "R" }, /* r */ - { 0x0073, NULL, "S" }, /* s */ - { 0x0074, NULL, "T" }, /* t */ - { 0x0075, NULL, "U" }, /* u */ - { 0x0076, NULL, "V" }, /* v */ - { 0x0077, NULL, "W" }, /* w */ - { 0x0078, NULL, "X" }, /* x */ - { 0x0079, NULL, "Y" }, /* y */ - { 0x007a, NULL, "Z" }, /* z */ - { 0x00a0, NULL, " " }, /* non breaking space */ - { 0x00a1, NULL, "!" }, /* inverted exclaimation mark */ - { 0x00a6, NULL, "|" }, /* broken bar */ - { 0x00a9, NULL, "(c)" }, /* copyright sign */ - { 0x00ab, NULL, "<<" }, /* left pointing double angle */ - { 0x00ae, NULL, "(r)" }, /* registered sign */ - { 0x00bb, NULL, ">>" }, /* right pointing double angle */ - { 0x00bc, NULL, "1/4" }, /* vulgar fraction one quarter */ - { 0x00bd, NULL, "1/2" }, /* vulgar fraction one half */ - { 0x00be, NULL, "3/4" }, /* vulgar fraction three quarters */ - { 0x00bf, NULL, "?" }, /* inverted question mark */ - { 0x00c0, NULL, "A" }, /* 'A' grave */ - { 0x00c1, NULL, "A" }, /* 'A' acute */ - { 0x00c2, NULL, "A" }, /* 'A' circumflex */ - { 0x00c3, NULL, "A" }, /* 'A' tilde */ - { 0x00c4, NULL, "A" }, /* 'A' diaeresis */ - { 0x00c5, NULL, "A" }, /* 'A' ring above */ - { 0x00c6, NULL, "AE" }, /* 'AE' ligature */ - { 0x00c7, NULL, "C" }, /* 'C' cedilla */ - { 0x00c8, NULL, "E" }, /* 'E' grave */ - { 0x00c9, NULL, "E" }, /* 'E' acute */ - { 0x00ca, NULL, "E" }, /* 'E' circumflex */ - { 0x00cb, NULL, "E" }, /* 'E' diaeresis */ - { 0x00cc, NULL, "I" }, /* 'I' grave */ - { 0x00cd, NULL, "I" }, /* 'I' acute */ - { 0x00ce, NULL, "I" }, /* 'I' circumflex */ - { 0x00cf, NULL, "I" }, /* 'I' diaeresis */ - { 0x00d0, NULL, "D" }, /* 'ETH' */ - { 0x00d1, NULL, "N" }, /* 'N' tilde */ - { 0x00d2, NULL, "O" }, /* 'O' grave */ - { 0x00d3, NULL, "O" }, /* 'O' acute */ - { 0x00d4, NULL, "O" }, /* 'O' circumflex */ - { 0x00d5, NULL, "O" }, /* 'O' tilde */ - { 0x00d6, NULL, "O" }, /* 'O' diaeresis */ - { 0x00d7, NULL, "X" }, /* multiplication sign */ - { 0x00d8, NULL, "O" }, /* 'O' stroke */ - { 0x00d9, NULL, "U" }, /* 'U' grave */ - { 0x00da, NULL, "U" }, /* 'U' acute */ - { 0x00db, NULL, "U" }, /* 'U' circumflex */ - { 0x00dc, NULL, "U" }, /* 'U' diaeresis */ - { 0x00dd, NULL, "Y" }, /* 'Y' acute */ - { 0x00df, NULL, "SS" }, /* sharp S */ - { 0x00e0, NULL, "a" }, /* 'a' grave */ - { 0x00e1, NULL, "a" }, /* 'a' acute */ - { 0x00e2, NULL, "a" }, /* 'a' circumflex */ - { 0x00e3, NULL, "a" }, /* 'a' tilde */ - { 0x00e4, NULL, "a" }, /* 'a' diaeresis */ - { 0x00e5, NULL, "a" }, /* 'a' ring above */ - { 0x00e6, NULL, "ae" }, /* 'ae' ligature */ - { 0x00e7, NULL, "c" }, /* 'c' cedilla */ - { 0x00e8, NULL, "e" }, /* 'e' grave */ - { 0x00e9, NULL, "e" }, /* 'e' acute */ - { 0x00ea, NULL, "e" }, /* 'e' circumflex */ - { 0x00eb, NULL, "e" }, /* 'e' diaeresis */ - { 0x00ec, NULL, "i" }, /* 'i' grave */ - { 0x00ed, NULL, "i" }, /* 'i' acute */ - { 0x00ee, NULL, "i" }, /* 'i' circumflex */ - { 0x00ef, NULL, "i" }, /* 'i' diaeresis */ - { 0x00f0, NULL, "d" }, /* 'eth' */ - { 0x00f1, NULL, "n" }, /* 'n' tilde */ - { 0x00f2, NULL, "o" }, /* 'o' grave */ - { 0x00f3, NULL, "o" }, /* 'o' acute */ - { 0x00f4, NULL, "o" }, /* 'o' circumflex */ - { 0x00f5, NULL, "o" }, /* 'o' tilde */ - { 0x00f6, NULL, "o" }, /* 'o' diaeresis */ - { 0x00f8, NULL, "o" }, /* 'o' stroke */ - { 0x00f9, NULL, "u" }, /* 'u' grave */ - { 0x00fa, NULL, "u" }, /* 'u' acute */ - { 0x00fb, NULL, "u" }, /* 'u' circumflex */ - { 0x00fc, NULL, "u" }, /* 'u' diaeresis */ - { 0x00fd, NULL, "y" }, /* 'y' acute */ - { 0x00ff, NULL, "y" }, /* 'y' diaeresis */ - { 0x2010, NULL, "-" }, /* hyphen */ - { 0x2011, NULL, "-" }, /* non-breaking hyphen */ - { 0x2012, NULL, "-" }, /* figure dash */ - { 0x2013, NULL, "-" }, /* en dash */ - { 0x2014, NULL, "-" }, /* em dash */ - { 0x2015, NULL, "-" }, /* horizontal dash */ - { 0x2018, NULL, "\'" }, /* left single quotation mark */ - { 0x2019, NULL, "\'" }, /* right single quotation mark */ - { 0x201a, NULL, "\'" }, /* single low quotation mark */ - { 0x201b, NULL, "\'" }, /* single high reversed quotation mark */ - { 0x201c, NULL, "\"" }, /* left double quotation mark */ - { 0x201d, NULL, "\"" }, /* right double quotation mark */ - { 0x201e, NULL, "\"" }, /* double low quotation mark */ - { 0x201f, NULL, "\"" }, /* double high reversed quotation mark */ - { 0x2024, NULL, "." }, /* one dot leader */ - { 0x2025, NULL, ".." }, /* two dot leader */ - { 0x2026, NULL, "..." }, /* horizontal ellipsis */ - { 0x2047, NULL, "??" }, /* double question mark */ - { 0x2048, NULL, "?!" }, /* question exclamation mark */ - { 0x2049, NULL, "!?" }, /* exclamation question mark */ - { 0xff01, NULL, "!" }, /* fullwidth exclamation point */ - { 0xff02, NULL, "\"" }, /* fullwidth quotation mark */ - { 0xff03, NULL, "#" }, /* fullwidth number sign */ - { 0xff04, NULL, "$" }, /* fullwidth dollar sign */ - { 0xff05, NULL, "%" }, /* fullwidth percent sign */ - { 0xff06, NULL, "&" }, /* fullwidth ampersand */ - { 0xff07, NULL, "\'" }, /* fullwidth apostrophe */ - { 0xff08, NULL, "(" }, /* fullwidth left parenthesis */ - { 0xff09, NULL, ")" }, /* fullwidth right parenthesis */ - { 0xff0a, NULL, "*" }, /* fullwidth asterisk */ - { 0xff0b, NULL, "+" }, /* fullwidth plus */ - { 0xff0c, NULL, "," }, /* fullwidth comma */ - { 0xff0d, NULL, "-" }, /* fullwidth minus */ - { 0xff0e, NULL, "." }, /* fullwidth period */ - { 0xff0f, NULL, "/" }, /* fullwidth slash */ - { 0xff10, NULL, "0" }, /* fullwidth zero */ - { 0xff11, NULL, "1" }, /* fullwidth one */ - { 0xff12, NULL, "2" }, /* fullwidth two */ - { 0xff13, NULL, "3" }, /* fullwidth three */ - { 0xff14, NULL, "4" }, /* fullwidth four */ - { 0xff15, NULL, "5" }, /* fullwidth five */ - { 0xff16, NULL, "6" }, /* fullwidth six */ - { 0xff17, NULL, "7" }, /* fullwidth seven */ - { 0xff18, NULL, "8" }, /* fullwidth eight */ - { 0xff19, NULL, "9" }, /* fullwidth nine */ - { 0xff1a, NULL, ":" }, /* fullwidth colon */ - { 0xff1b, NULL, ";" }, /* fullwidth semicolon */ - { 0xff1c, NULL, "<" }, /* fullwidth less than sign */ - { 0xff1d, NULL, "=" }, /* fullwidth equals sign */ - { 0xff1e, NULL, ">" }, /* fullwidth greater than sign */ - { 0xff1f, NULL, "?" }, /* fullwidth question mark */ - { 0xff20, NULL, "@" }, /* fullwidth at sign */ - { 0xff21, NULL, "A" }, /* fullwidth 'A' */ - { 0xff22, NULL, "B" }, /* fullwidth 'B' */ - { 0xff23, NULL, "C" }, /* fullwidth 'C' */ - { 0xff24, NULL, "D" }, /* fullwidth 'D' */ - { 0xff25, NULL, "E" }, /* fullwidth 'E' */ - { 0xff26, NULL, "F" }, /* fullwidth 'F' */ - { 0xff27, NULL, "G" }, /* fullwidth 'G' */ - { 0xff28, NULL, "H" }, /* fullwidth 'H' */ - { 0xff29, NULL, "I" }, /* fullwidth 'I' */ - { 0xff2a, NULL, "J" }, /* fullwidth 'J' */ - { 0xff2b, NULL, "K" }, /* fullwidth 'K' */ - { 0xff2c, NULL, "L" }, /* fullwidth 'L' */ - { 0xff2d, NULL, "M" }, /* fullwidth 'M' */ - { 0xff2e, NULL, "N" }, /* fullwidth 'N' */ - { 0xff2f, NULL, "O" }, /* fullwidth 'O' */ - { 0xff30, NULL, "P" }, /* fullwidth 'P' */ - { 0xff31, NULL, "Q" }, /* fullwidth 'Q' */ - { 0xff32, NULL, "R" }, /* fullwidth 'R' */ - { 0xff33, NULL, "S" }, /* fullwidth 'S' */ - { 0xff34, NULL, "T" }, /* fullwidth 'T' */ - { 0xff35, NULL, "U" }, /* fullwidth 'U' */ - { 0xff36, NULL, "V" }, /* fullwidth 'V' */ - { 0xff37, NULL, "W" }, /* fullwidth 'W' */ - { 0xff38, NULL, "X" }, /* fullwidth 'X' */ - { 0xff39, NULL, "Y" }, /* fullwidth 'Y' */ - { 0xff3a, NULL, "Z" }, /* fullwidth 'Z' */ - { 0xff3b, NULL, "[" }, /* fullwidth left bracket */ - { 0xff3c, NULL, "\\" }, /* fullwidth backslash */ - { 0xff3d, NULL, "]" }, /* fullwidth right bracket */ - { 0xff3e, NULL, "^" }, /* fullwidth caret */ - { 0xff3f, NULL, "_" }, /* fullwidth underscore */ - { 0xff40, NULL, "`" }, /* fullwidth backquote */ - { 0xff41, NULL, "a" }, /* fullwidth 'a' */ - { 0xff42, NULL, "b" }, /* fullwidth 'b' */ - { 0xff43, NULL, "c" }, /* fullwidth 'c' */ - { 0xff44, NULL, "d" }, /* fullwidth 'd' */ - { 0xff45, NULL, "e" }, /* fullwidth 'e' */ - { 0xff46, NULL, "f" }, /* fullwidth 'f' */ - { 0xff47, NULL, "g" }, /* fullwidth 'g' */ - { 0xff48, NULL, "h" }, /* fullwidth 'h' */ - { 0xff49, NULL, "i" }, /* fullwidth 'i' */ - { 0xff4a, NULL, "j" }, /* fullwidth 'j' */ - { 0xff4b, NULL, "k" }, /* fullwidth 'k' */ - { 0xff4c, NULL, "l" }, /* fullwidth 'l' */ - { 0xff4d, NULL, "m" }, /* fullwidth 'm' */ - { 0xff4e, NULL, "n" }, /* fullwidth 'n' */ - { 0xff4f, NULL, "o" }, /* fullwidth 'o' */ - { 0xff50, NULL, "p" }, /* fullwidth 'p' */ - { 0xff51, NULL, "q" }, /* fullwidth 'q' */ - { 0xff52, NULL, "r" }, /* fullwidth 'r' */ - { 0xff53, NULL, "s" }, /* fullwidth 's' */ - { 0xff54, NULL, "t" }, /* fullwidth 't' */ - { 0xff55, NULL, "u" }, /* fullwidth 'u' */ - { 0xff56, NULL, "v" }, /* fullwidth 'v' */ - { 0xff57, NULL, "w" }, /* fullwidth 'w' */ - { 0xff58, NULL, "x" }, /* fullwidth 'x' */ - { 0xff59, NULL, "y" }, /* fullwidth 'y' */ - { 0xff5a, NULL, "z" }, /* fullwidth 'z' */ - { 0xff5b, NULL, "{" }, /* fullwidth left brace */ - { 0xff5c, NULL, "|" }, /* fullwidth vertical bar */ - { 0xff5d, NULL, "}" }, /* fullwidth right brace */ - { 0xff5e, NULL, "~" }, /* fullwidth tilde */ - { 0xff5f, NULL, "((" }, /* fullwidth double left parenthesis */ - { 0xff60, NULL, "))" }, /* fullwidth double right parenthesis */ - { 0xffe0, NULL, "\xC2\xA2" }, /* fullwidth cent sign */ - { 0xffe1, NULL, "\xC2\xA3" }, /* fullwidth pound sign */ - { 0xffe4, NULL, "\xC2\xA4" }, /* fullwidth broken bar */ - { 0xffe5, NULL, "\xC2\xA5" }, /* fullwidth yen sign */ - { 0xffe6, NULL, "\xE2\x82\xA9" }, /* fullwidth won sign */ - { 0xffe9, NULL, "\xE2\x86\x90" }, /* fullwidth left arrow */ - { 0xffea, NULL, "\xE2\x86\x91" }, /* fullwidth up arrow */ - { 0xffeb, NULL, "\xE2\x86\x92" }, /* fullwidth right arrow */ - { 0xffec, NULL, "\xE2\x86\x93" }, /* fullwidth down arrow */ - { 0xffed, NULL, "\xE2\x96\xAA" }, /* fullwidth solid box */ - { 0xffee, NULL, "\xE2\x97\xA6" }, /* fullwidth open circle */ - { UCHAR_SHIFT_1, "Shift", NULL }, /* Shift key */ - { UCHAR_SHIFT_2, "Ctrl", NULL }, /* Ctrl key */ - { UCHAR_MAMEKEY(F1), "F1", NULL }, /* F1 function key */ - { UCHAR_MAMEKEY(F2), "F2", NULL }, /* F2 function key */ - { UCHAR_MAMEKEY(F3), "F3", NULL }, /* F3 function key */ - { UCHAR_MAMEKEY(F4), "F4", NULL }, /* F4 function key */ - { UCHAR_MAMEKEY(F5), "F5", NULL }, /* F5 function key */ - { UCHAR_MAMEKEY(F6), "F6", NULL }, /* F6 function key */ - { UCHAR_MAMEKEY(F7), "F7", NULL }, /* F7 function key */ - { UCHAR_MAMEKEY(F8), "F8", NULL }, /* F8 function key */ - { UCHAR_MAMEKEY(F9), "F9", NULL }, /* F9 function key */ - { UCHAR_MAMEKEY(F10), "F10", NULL }, /* F10 function key */ - { UCHAR_MAMEKEY(F11), "F11", NULL }, /* F11 function key */ - { UCHAR_MAMEKEY(F12), "F12", NULL }, /* F12 function key */ - { UCHAR_MAMEKEY(F13), "F13", NULL }, /* F13 function key */ - { UCHAR_MAMEKEY(F14), "F14", NULL }, /* F14 function key */ - { UCHAR_MAMEKEY(F15), "F15", NULL }, /* F15 function key */ - { UCHAR_MAMEKEY(ESC), "Esc", "\033" }, /* Esc key */ - { UCHAR_MAMEKEY(INSERT), "Insert", NULL }, /* Insert key */ - { UCHAR_MAMEKEY(DEL), "Delete", "\010" }, /* Delete key */ - { UCHAR_MAMEKEY(HOME), "Home", "\014" }, /* Home key */ - { UCHAR_MAMEKEY(END), "End", NULL }, /* End key */ - { UCHAR_MAMEKEY(PGUP), "Page Up", NULL }, /* Page Up key */ - { UCHAR_MAMEKEY(PGDN), "Page Down", NULL }, /* Page Down key */ - { UCHAR_MAMEKEY(LEFT), "Cursor Left", NULL }, /* Cursor Left */ - { UCHAR_MAMEKEY(RIGHT), "Cursor Right", NULL }, /* Cursor Right */ - { UCHAR_MAMEKEY(UP), "Cursor Up", NULL }, /* Cursor Up */ - { UCHAR_MAMEKEY(DOWN), "Cursor Down", NULL }, /* Cursor Down */ - { UCHAR_MAMEKEY(0_PAD), "Keypad 0", NULL }, /* 0 on the numeric keypad */ - { UCHAR_MAMEKEY(1_PAD), "Keypad 1", NULL }, /* 1 on the numeric keypad */ - { UCHAR_MAMEKEY(2_PAD), "Keypad 2", NULL }, /* 2 on the numeric keypad */ - { UCHAR_MAMEKEY(3_PAD), "Keypad 3", NULL }, /* 3 on the numeric keypad */ - { UCHAR_MAMEKEY(4_PAD), "Keypad 4", NULL }, /* 4 on the numeric keypad */ - { UCHAR_MAMEKEY(5_PAD), "Keypad 5", NULL }, /* 5 on the numeric keypad */ - { UCHAR_MAMEKEY(6_PAD), "Keypad 6", NULL }, /* 6 on the numeric keypad */ - { UCHAR_MAMEKEY(7_PAD), "Keypad 7", NULL }, /* 7 on the numeric keypad */ - { UCHAR_MAMEKEY(8_PAD), "Keypad 8", NULL }, /* 8 on the numeric keypad */ - { UCHAR_MAMEKEY(9_PAD), "Keypad 9", NULL }, /* 9 on the numeric keypad */ - { UCHAR_MAMEKEY(SLASH_PAD), "Keypad /", NULL }, /* / on the numeric keypad */ - { UCHAR_MAMEKEY(ASTERISK), "Keypad *", NULL }, /* * on the numeric keypad */ - { UCHAR_MAMEKEY(MINUS_PAD), "Keypad -", NULL }, /* - on the numeric Keypad */ - { UCHAR_MAMEKEY(PLUS_PAD), "Keypad +", NULL }, /* + on the numeric Keypad */ - { UCHAR_MAMEKEY(DEL_PAD), "Keypad .", NULL }, /* . on the numeric keypad */ - { UCHAR_MAMEKEY(ENTER_PAD), "Keypad Enter", NULL }, /* Enter on the numeric keypad */ - { UCHAR_MAMEKEY(PRTSCR), "Print Screen", NULL }, /* Print Screen key */ - { UCHAR_MAMEKEY(PAUSE), "Pause", NULL }, /* Pause key */ - { UCHAR_MAMEKEY(LSHIFT), "Left Shift", NULL }, /* Left Shift key */ - { UCHAR_MAMEKEY(RSHIFT), "Right Shift", NULL }, /* Right Shift key */ - { UCHAR_MAMEKEY(LCONTROL), "Left Ctrl", NULL }, /* Left Control key */ - { UCHAR_MAMEKEY(RCONTROL), "Right Ctrl", NULL }, /* Right Control key */ - { UCHAR_MAMEKEY(LALT), "Left Alt", NULL }, /* Left Alt key */ - { UCHAR_MAMEKEY(RALT), "Right Alt", NULL }, /* Right Alt key */ - { UCHAR_MAMEKEY(SCRLOCK), "Scroll Lock", NULL }, /* Scroll Lock key */ - { UCHAR_MAMEKEY(NUMLOCK), "Num Lock", NULL }, /* Num Lock key */ - { UCHAR_MAMEKEY(CAPSLOCK), "Caps Lock", NULL }, /* Caps Lock key */ - { UCHAR_MAMEKEY(LWIN), "Left Win", NULL }, /* Left Win key */ - { UCHAR_MAMEKEY(RWIN), "Right Win", NULL }, /* Right Win key */ - { UCHAR_MAMEKEY(MENU), "Menu", NULL }, /* Menu key */ - { UCHAR_MAMEKEY(CANCEL), "Break", NULL } /* Break/Pause key */ + { 0x0008, "Backspace", NULL }, // Backspace + { 0x0009, "Tab", " " }, // Tab + { 0x000c, "Clear", NULL }, // Clear + { 0x000d, "Enter", NULL }, // Enter + { 0x001a, "Esc", NULL }, // Esc + { 0x0020, "Space", " " }, // Space + { 0x0061, NULL, "A" }, // a + { 0x0062, NULL, "B" }, // b + { 0x0063, NULL, "C" }, // c + { 0x0064, NULL, "D" }, // d + { 0x0065, NULL, "E" }, // e + { 0x0066, NULL, "F" }, // f + { 0x0067, NULL, "G" }, // g + { 0x0068, NULL, "H" }, // h + { 0x0069, NULL, "I" }, // i + { 0x006a, NULL, "J" }, // j + { 0x006b, NULL, "K" }, // k + { 0x006c, NULL, "L" }, // l + { 0x006d, NULL, "M" }, // m + { 0x006e, NULL, "N" }, // n + { 0x006f, NULL, "O" }, // o + { 0x0070, NULL, "P" }, // p + { 0x0071, NULL, "Q" }, // q + { 0x0072, NULL, "R" }, // r + { 0x0073, NULL, "S" }, // s + { 0x0074, NULL, "T" }, // t + { 0x0075, NULL, "U" }, // u + { 0x0076, NULL, "V" }, // v + { 0x0077, NULL, "W" }, // w + { 0x0078, NULL, "X" }, // x + { 0x0079, NULL, "Y" }, // y + { 0x007a, NULL, "Z" }, // z + { 0x00a0, NULL, " " }, // non breaking space + { 0x00a1, NULL, "!" }, // inverted exclaimation mark + { 0x00a6, NULL, "|" }, // broken bar + { 0x00a9, NULL, "(c)" }, // copyright sign + { 0x00ab, NULL, "<<" }, // left pointing double angle + { 0x00ae, NULL, "(r)" }, // registered sign + { 0x00bb, NULL, ">>" }, // right pointing double angle + { 0x00bc, NULL, "1/4" }, // vulgar fraction one quarter + { 0x00bd, NULL, "1/2" }, // vulgar fraction one half + { 0x00be, NULL, "3/4" }, // vulgar fraction three quarters + { 0x00bf, NULL, "?" }, // inverted question mark + { 0x00c0, NULL, "A" }, // 'A' grave + { 0x00c1, NULL, "A" }, // 'A' acute + { 0x00c2, NULL, "A" }, // 'A' circumflex + { 0x00c3, NULL, "A" }, // 'A' tilde + { 0x00c4, NULL, "A" }, // 'A' diaeresis + { 0x00c5, NULL, "A" }, // 'A' ring above + { 0x00c6, NULL, "AE" }, // 'AE' ligature + { 0x00c7, NULL, "C" }, // 'C' cedilla + { 0x00c8, NULL, "E" }, // 'E' grave + { 0x00c9, NULL, "E" }, // 'E' acute + { 0x00ca, NULL, "E" }, // 'E' circumflex + { 0x00cb, NULL, "E" }, // 'E' diaeresis + { 0x00cc, NULL, "I" }, // 'I' grave + { 0x00cd, NULL, "I" }, // 'I' acute + { 0x00ce, NULL, "I" }, // 'I' circumflex + { 0x00cf, NULL, "I" }, // 'I' diaeresis + { 0x00d0, NULL, "D" }, // 'ETH' + { 0x00d1, NULL, "N" }, // 'N' tilde + { 0x00d2, NULL, "O" }, // 'O' grave + { 0x00d3, NULL, "O" }, // 'O' acute + { 0x00d4, NULL, "O" }, // 'O' circumflex + { 0x00d5, NULL, "O" }, // 'O' tilde + { 0x00d6, NULL, "O" }, // 'O' diaeresis + { 0x00d7, NULL, "X" }, // multiplication sign + { 0x00d8, NULL, "O" }, // 'O' stroke + { 0x00d9, NULL, "U" }, // 'U' grave + { 0x00da, NULL, "U" }, // 'U' acute + { 0x00db, NULL, "U" }, // 'U' circumflex + { 0x00dc, NULL, "U" }, // 'U' diaeresis + { 0x00dd, NULL, "Y" }, // 'Y' acute + { 0x00df, NULL, "SS" }, // sharp S + { 0x00e0, NULL, "a" }, // 'a' grave + { 0x00e1, NULL, "a" }, // 'a' acute + { 0x00e2, NULL, "a" }, // 'a' circumflex + { 0x00e3, NULL, "a" }, // 'a' tilde + { 0x00e4, NULL, "a" }, // 'a' diaeresis + { 0x00e5, NULL, "a" }, // 'a' ring above + { 0x00e6, NULL, "ae" }, // 'ae' ligature + { 0x00e7, NULL, "c" }, // 'c' cedilla + { 0x00e8, NULL, "e" }, // 'e' grave + { 0x00e9, NULL, "e" }, // 'e' acute + { 0x00ea, NULL, "e" }, // 'e' circumflex + { 0x00eb, NULL, "e" }, // 'e' diaeresis + { 0x00ec, NULL, "i" }, // 'i' grave + { 0x00ed, NULL, "i" }, // 'i' acute + { 0x00ee, NULL, "i" }, // 'i' circumflex + { 0x00ef, NULL, "i" }, // 'i' diaeresis + { 0x00f0, NULL, "d" }, // 'eth' + { 0x00f1, NULL, "n" }, // 'n' tilde + { 0x00f2, NULL, "o" }, // 'o' grave + { 0x00f3, NULL, "o" }, // 'o' acute + { 0x00f4, NULL, "o" }, // 'o' circumflex + { 0x00f5, NULL, "o" }, // 'o' tilde + { 0x00f6, NULL, "o" }, // 'o' diaeresis + { 0x00f8, NULL, "o" }, // 'o' stroke + { 0x00f9, NULL, "u" }, // 'u' grave + { 0x00fa, NULL, "u" }, // 'u' acute + { 0x00fb, NULL, "u" }, // 'u' circumflex + { 0x00fc, NULL, "u" }, // 'u' diaeresis + { 0x00fd, NULL, "y" }, // 'y' acute + { 0x00ff, NULL, "y" }, // 'y' diaeresis + { 0x2010, NULL, "-" }, // hyphen + { 0x2011, NULL, "-" }, // non-breaking hyphen + { 0x2012, NULL, "-" }, // figure dash + { 0x2013, NULL, "-" }, // en dash + { 0x2014, NULL, "-" }, // em dash + { 0x2015, NULL, "-" }, // horizontal dash + { 0x2018, NULL, "\'" }, // left single quotation mark + { 0x2019, NULL, "\'" }, // right single quotation mark + { 0x201a, NULL, "\'" }, // single low quotation mark + { 0x201b, NULL, "\'" }, // single high reversed quotation mark + { 0x201c, NULL, "\"" }, // left double quotation mark + { 0x201d, NULL, "\"" }, // right double quotation mark + { 0x201e, NULL, "\"" }, // double low quotation mark + { 0x201f, NULL, "\"" }, // double high reversed quotation mark + { 0x2024, NULL, "." }, // one dot leader + { 0x2025, NULL, ".." }, // two dot leader + { 0x2026, NULL, "..." }, // horizontal ellipsis + { 0x2047, NULL, "??" }, // double question mark + { 0x2048, NULL, "?!" }, // question exclamation mark + { 0x2049, NULL, "!?" }, // exclamation question mark + { 0xff01, NULL, "!" }, // fullwidth exclamation point + { 0xff02, NULL, "\"" }, // fullwidth quotation mark + { 0xff03, NULL, "#" }, // fullwidth number sign + { 0xff04, NULL, "$" }, // fullwidth dollar sign + { 0xff05, NULL, "%" }, // fullwidth percent sign + { 0xff06, NULL, "&" }, // fullwidth ampersand + { 0xff07, NULL, "\'" }, // fullwidth apostrophe + { 0xff08, NULL, "(" }, // fullwidth left parenthesis + { 0xff09, NULL, ")" }, // fullwidth right parenthesis + { 0xff0a, NULL, "*" }, // fullwidth asterisk + { 0xff0b, NULL, "+" }, // fullwidth plus + { 0xff0c, NULL, "," }, // fullwidth comma + { 0xff0d, NULL, "-" }, // fullwidth minus + { 0xff0e, NULL, "." }, // fullwidth period + { 0xff0f, NULL, "/" }, // fullwidth slash + { 0xff10, NULL, "0" }, // fullwidth zero + { 0xff11, NULL, "1" }, // fullwidth one + { 0xff12, NULL, "2" }, // fullwidth two + { 0xff13, NULL, "3" }, // fullwidth three + { 0xff14, NULL, "4" }, // fullwidth four + { 0xff15, NULL, "5" }, // fullwidth five + { 0xff16, NULL, "6" }, // fullwidth six + { 0xff17, NULL, "7" }, // fullwidth seven + { 0xff18, NULL, "8" }, // fullwidth eight + { 0xff19, NULL, "9" }, // fullwidth nine + { 0xff1a, NULL, ":" }, // fullwidth colon + { 0xff1b, NULL, ";" }, // fullwidth semicolon + { 0xff1c, NULL, "<" }, // fullwidth less than sign + { 0xff1d, NULL, "=" }, // fullwidth equals sign + { 0xff1e, NULL, ">" }, // fullwidth greater than sign + { 0xff1f, NULL, "?" }, // fullwidth question mark + { 0xff20, NULL, "@" }, // fullwidth at sign + { 0xff21, NULL, "A" }, // fullwidth 'A' + { 0xff22, NULL, "B" }, // fullwidth 'B' + { 0xff23, NULL, "C" }, // fullwidth 'C' + { 0xff24, NULL, "D" }, // fullwidth 'D' + { 0xff25, NULL, "E" }, // fullwidth 'E' + { 0xff26, NULL, "F" }, // fullwidth 'F' + { 0xff27, NULL, "G" }, // fullwidth 'G' + { 0xff28, NULL, "H" }, // fullwidth 'H' + { 0xff29, NULL, "I" }, // fullwidth 'I' + { 0xff2a, NULL, "J" }, // fullwidth 'J' + { 0xff2b, NULL, "K" }, // fullwidth 'K' + { 0xff2c, NULL, "L" }, // fullwidth 'L' + { 0xff2d, NULL, "M" }, // fullwidth 'M' + { 0xff2e, NULL, "N" }, // fullwidth 'N' + { 0xff2f, NULL, "O" }, // fullwidth 'O' + { 0xff30, NULL, "P" }, // fullwidth 'P' + { 0xff31, NULL, "Q" }, // fullwidth 'Q' + { 0xff32, NULL, "R" }, // fullwidth 'R' + { 0xff33, NULL, "S" }, // fullwidth 'S' + { 0xff34, NULL, "T" }, // fullwidth 'T' + { 0xff35, NULL, "U" }, // fullwidth 'U' + { 0xff36, NULL, "V" }, // fullwidth 'V' + { 0xff37, NULL, "W" }, // fullwidth 'W' + { 0xff38, NULL, "X" }, // fullwidth 'X' + { 0xff39, NULL, "Y" }, // fullwidth 'Y' + { 0xff3a, NULL, "Z" }, // fullwidth 'Z' + { 0xff3b, NULL, "[" }, // fullwidth left bracket + { 0xff3c, NULL, "\\" }, // fullwidth backslash + { 0xff3d, NULL, "]" }, // fullwidth right bracket + { 0xff3e, NULL, "^" }, // fullwidth caret + { 0xff3f, NULL, "_" }, // fullwidth underscore + { 0xff40, NULL, "`" }, // fullwidth backquote + { 0xff41, NULL, "a" }, // fullwidth 'a' + { 0xff42, NULL, "b" }, // fullwidth 'b' + { 0xff43, NULL, "c" }, // fullwidth 'c' + { 0xff44, NULL, "d" }, // fullwidth 'd' + { 0xff45, NULL, "e" }, // fullwidth 'e' + { 0xff46, NULL, "f" }, // fullwidth 'f' + { 0xff47, NULL, "g" }, // fullwidth 'g' + { 0xff48, NULL, "h" }, // fullwidth 'h' + { 0xff49, NULL, "i" }, // fullwidth 'i' + { 0xff4a, NULL, "j" }, // fullwidth 'j' + { 0xff4b, NULL, "k" }, // fullwidth 'k' + { 0xff4c, NULL, "l" }, // fullwidth 'l' + { 0xff4d, NULL, "m" }, // fullwidth 'm' + { 0xff4e, NULL, "n" }, // fullwidth 'n' + { 0xff4f, NULL, "o" }, // fullwidth 'o' + { 0xff50, NULL, "p" }, // fullwidth 'p' + { 0xff51, NULL, "q" }, // fullwidth 'q' + { 0xff52, NULL, "r" }, // fullwidth 'r' + { 0xff53, NULL, "s" }, // fullwidth 's' + { 0xff54, NULL, "t" }, // fullwidth 't' + { 0xff55, NULL, "u" }, // fullwidth 'u' + { 0xff56, NULL, "v" }, // fullwidth 'v' + { 0xff57, NULL, "w" }, // fullwidth 'w' + { 0xff58, NULL, "x" }, // fullwidth 'x' + { 0xff59, NULL, "y" }, // fullwidth 'y' + { 0xff5a, NULL, "z" }, // fullwidth 'z' + { 0xff5b, NULL, "{" }, // fullwidth left brace + { 0xff5c, NULL, "|" }, // fullwidth vertical bar + { 0xff5d, NULL, "}" }, // fullwidth right brace + { 0xff5e, NULL, "~" }, // fullwidth tilde + { 0xff5f, NULL, "((" }, // fullwidth double left parenthesis + { 0xff60, NULL, "))" }, // fullwidth double right parenthesis + { 0xffe0, NULL, "\xC2\xA2" }, // fullwidth cent sign + { 0xffe1, NULL, "\xC2\xA3" }, // fullwidth pound sign + { 0xffe4, NULL, "\xC2\xA4" }, // fullwidth broken bar + { 0xffe5, NULL, "\xC2\xA5" }, // fullwidth yen sign + { 0xffe6, NULL, "\xE2\x82\xA9" }, // fullwidth won sign + { 0xffe9, NULL, "\xE2\x86\x90" }, // fullwidth left arrow + { 0xffea, NULL, "\xE2\x86\x91" }, // fullwidth up arrow + { 0xffeb, NULL, "\xE2\x86\x92" }, // fullwidth right arrow + { 0xffec, NULL, "\xE2\x86\x93" }, // fullwidth down arrow + { 0xffed, NULL, "\xE2\x96\xAA" }, // fullwidth solid box + { 0xffee, NULL, "\xE2\x97\xA6" }, // fullwidth open circle + { UCHAR_SHIFT_1, "Shift", NULL }, // Shift key + { UCHAR_SHIFT_2, "Ctrl", NULL }, // Ctrl key + { UCHAR_MAMEKEY(F1), "F1", NULL }, // F1 function key + { UCHAR_MAMEKEY(F2), "F2", NULL }, // F2 function key + { UCHAR_MAMEKEY(F3), "F3", NULL }, // F3 function key + { UCHAR_MAMEKEY(F4), "F4", NULL }, // F4 function key + { UCHAR_MAMEKEY(F5), "F5", NULL }, // F5 function key + { UCHAR_MAMEKEY(F6), "F6", NULL }, // F6 function key + { UCHAR_MAMEKEY(F7), "F7", NULL }, // F7 function key + { UCHAR_MAMEKEY(F8), "F8", NULL }, // F8 function key + { UCHAR_MAMEKEY(F9), "F9", NULL }, // F9 function key + { UCHAR_MAMEKEY(F10), "F10", NULL }, // F10 function key + { UCHAR_MAMEKEY(F11), "F11", NULL }, // F11 function key + { UCHAR_MAMEKEY(F12), "F12", NULL }, // F12 function key + { UCHAR_MAMEKEY(F13), "F13", NULL }, // F13 function key + { UCHAR_MAMEKEY(F14), "F14", NULL }, // F14 function key + { UCHAR_MAMEKEY(F15), "F15", NULL }, // F15 function key + { UCHAR_MAMEKEY(ESC), "Esc", "\033" }, // Esc key + { UCHAR_MAMEKEY(INSERT), "Insert", NULL }, // Insert key + { UCHAR_MAMEKEY(DEL), "Delete", "\010" }, // Delete key + { UCHAR_MAMEKEY(HOME), "Home", "\014" }, // Home key + { UCHAR_MAMEKEY(END), "End", NULL }, // End key + { UCHAR_MAMEKEY(PGUP), "Page Up", NULL }, // Page Up key + { UCHAR_MAMEKEY(PGDN), "Page Down", NULL }, // Page Down key + { UCHAR_MAMEKEY(LEFT), "Cursor Left", NULL }, // Cursor Left + { UCHAR_MAMEKEY(RIGHT), "Cursor Right", NULL }, // Cursor Right + { UCHAR_MAMEKEY(UP), "Cursor Up", NULL }, // Cursor Up + { UCHAR_MAMEKEY(DOWN), "Cursor Down", NULL }, // Cursor Down + { UCHAR_MAMEKEY(0_PAD), "Keypad 0", NULL }, // 0 on the numeric keypad + { UCHAR_MAMEKEY(1_PAD), "Keypad 1", NULL }, // 1 on the numeric keypad + { UCHAR_MAMEKEY(2_PAD), "Keypad 2", NULL }, // 2 on the numeric keypad + { UCHAR_MAMEKEY(3_PAD), "Keypad 3", NULL }, // 3 on the numeric keypad + { UCHAR_MAMEKEY(4_PAD), "Keypad 4", NULL }, // 4 on the numeric keypad + { UCHAR_MAMEKEY(5_PAD), "Keypad 5", NULL }, // 5 on the numeric keypad + { UCHAR_MAMEKEY(6_PAD), "Keypad 6", NULL }, // 6 on the numeric keypad + { UCHAR_MAMEKEY(7_PAD), "Keypad 7", NULL }, // 7 on the numeric keypad + { UCHAR_MAMEKEY(8_PAD), "Keypad 8", NULL }, // 8 on the numeric keypad + { UCHAR_MAMEKEY(9_PAD), "Keypad 9", NULL }, // 9 on the numeric keypad + { UCHAR_MAMEKEY(SLASH_PAD), "Keypad /", NULL }, // / on the numeric keypad + { UCHAR_MAMEKEY(ASTERISK), "Keypad *", NULL }, // * on the numeric keypad + { UCHAR_MAMEKEY(MINUS_PAD), "Keypad -", NULL }, // - on the numeric Keypad + { UCHAR_MAMEKEY(PLUS_PAD), "Keypad +", NULL }, // + on the numeric Keypad + { UCHAR_MAMEKEY(DEL_PAD), "Keypad .", NULL }, // . on the numeric keypad + { UCHAR_MAMEKEY(ENTER_PAD), "Keypad Enter", NULL }, // Enter on the numeric keypad + { UCHAR_MAMEKEY(PRTSCR), "Print Screen", NULL }, // Print Screen key + { UCHAR_MAMEKEY(PAUSE), "Pause", NULL }, // Pause key + { UCHAR_MAMEKEY(LSHIFT), "Left Shift", NULL }, // Left Shift key + { UCHAR_MAMEKEY(RSHIFT), "Right Shift", NULL }, // Right Shift key + { UCHAR_MAMEKEY(LCONTROL), "Left Ctrl", NULL }, // Left Control key + { UCHAR_MAMEKEY(RCONTROL), "Right Ctrl", NULL }, // Right Control key + { UCHAR_MAMEKEY(LALT), "Left Alt", NULL }, // Left Alt key + { UCHAR_MAMEKEY(RALT), "Right Alt", NULL }, // Right Alt key + { UCHAR_MAMEKEY(SCRLOCK), "Scroll Lock", NULL }, // Scroll Lock key + { UCHAR_MAMEKEY(NUMLOCK), "Num Lock", NULL }, // Num Lock key + { UCHAR_MAMEKEY(CAPSLOCK), "Caps Lock", NULL }, // Caps Lock key + { UCHAR_MAMEKEY(LWIN), "Left Win", NULL }, // Left Win key + { UCHAR_MAMEKEY(RWIN), "Right Win", NULL }, // Right Win key + { UCHAR_MAMEKEY(MENU), "Menu", NULL }, // Menu key + { UCHAR_MAMEKEY(CANCEL), "Break", NULL } // Break/Pause key }; -static TIMER_CALLBACK(inputx_timerproc); -/* Debugging commands and handlers. */ -static void execute_input(running_machine &machine, int ref, int params, const char *param[]); -static void execute_dumpkbd(running_machine &machine, int ref, int params, const char *param[]); - -/*************************************************************************** - COMMON SHARED STRINGS -***************************************************************************/ +//************************************************************************** +// COMMON SHARED STRINGS +//************************************************************************** static const struct { @@ -686,2526 +782,2331 @@ static const struct -/*************************************************************************** - BUILT-IN CORE MAPPINGS -***************************************************************************/ +//************************************************************************** +// BUILT-IN CORE MAPPINGS +//************************************************************************** #include "inpttype.h" -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - -/* core system management */ -static void input_port_exit(running_machine &machine); - -/* port reading */ -static INT32 apply_analog_settings(INT32 current, analog_field_state *analog); - -/* initialization helpers */ -static void init_port_types(running_machine &machine); -static void init_port_state(running_machine &machine); -static void init_autoselect_devices(running_machine &machine, int type1, int type2, int type3, const char *option, const char *ananame); -static device_field_info *init_field_device_info(const input_field_config *field,const char *device_name); -static analog_field_state *init_field_analog_state(const input_field_config *field); - -/* once-per-frame updates */ -static void frame_update_callback(running_machine &machine); -static void frame_update(running_machine &machine); -static void frame_update_digital_joysticks(running_machine &machine); -static void frame_update_analog_field(running_machine &machine, analog_field_state *analog); -static int frame_get_digital_field_state(const input_field_config *field, int mouse_down); - -/* tokenization helpers */ -static int token_to_input_field_type(running_machine &machine, const char *string, int *player); -static const char *input_field_type_to_token(running_machine &machine, int type, int player); -static int token_to_seq_type(const char *string); - -/* settings load */ -static void load_config_callback(running_machine &machine, int config_type, xml_data_node *parentnode); -static void load_remap_table(running_machine &machine, xml_data_node *parentnode); -static int load_default_config(running_machine &machine, xml_data_node *portnode, int type, int player, const input_seq *newseq); -static int load_game_config(running_machine &machine, xml_data_node *portnode, int type, int player, const input_seq *newseq); - -/* settings save */ -static void save_config_callback(running_machine &machine, int config_type, xml_data_node *parentnode); -static void save_sequence(running_machine &machine, xml_data_node *parentnode, int type, int porttype, const input_seq &seq); -static int save_this_input_field_type(int type); -static void save_default_inputs(running_machine &machine, xml_data_node *parentnode); -static void save_game_inputs(running_machine &machine, xml_data_node *parentnode); - -/* input playback */ -static time_t playback_init(running_machine &machine); -static void playback_end(running_machine &machine, const char *message); -static void playback_frame(running_machine &machine, attotime curtime); -static void playback_port(const input_port_config *port); - -/* input recording */ -static void record_init(running_machine &machine); -static void record_end(running_machine &machine, const char *message); -static void record_frame(running_machine &machine, attotime curtime); -static void record_port(const input_port_config *port); +//************************************************************************** +// PORT CONFIGURATIONS +//************************************************************************** +//************************************************************************** +// I/O PORT LIST +//************************************************************************** +//------------------------------------------------- +// append - append the given device's input ports +// to the current list +//------------------------------------------------- -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ - -/*------------------------------------------------- - apply_analog_min_max - clamp the given input - value to the appropriate min/max for the - analog control --------------------------------------------------*/ - -INLINE INT32 apply_analog_min_max(const analog_field_state *analog, INT32 value) +void ioport_list::append(device_t &device, astring &errorbuf) { - /* take the analog minimum and maximum values and apply the inverse of the */ - /* sensitivity so that we can clamp against them before applying sensitivity */ - INT32 adjmin = APPLY_INVERSE_SENSITIVITY(analog->minimum, analog->sensitivity); - INT32 adjmax = APPLY_INVERSE_SENSITIVITY(analog->maximum, analog->sensitivity); + // no constructor, no list + ioport_constructor constructor = device.input_ports(); + if (constructor == NULL) + return; - /* for absolute devices, clamp to the bounds absolutely */ - if (!analog->wraps) - { - if (value > adjmax) - value = adjmax; - else if (value < adjmin) - value = adjmin; - } + // reset error buffer + errorbuf.reset(); - /* for relative devices, wrap around when we go past the edge */ - else - { - INT32 range = adjmax - adjmin; - /* rolls to other end when 1 position past end. */ - value = (value - adjmin) % range; - if (value < 0) - value += range; - value += adjmin; - } + // detokenize into the list + (*constructor)(device, *this, errorbuf); - return value; + // collapse fields and sort the list + for (ioport_port *port = first(); port != NULL; port = port->next()) + port->collapse_fields(errorbuf); } -/*------------------------------------------------- - get_port_tag - return a guaranteed tag for - a port --------------------------------------------------*/ - -INLINE const char *get_port_tag(const input_port_config *port, char *tempbuffer) -{ - return port->tag(); -} +//************************************************************************** +// INPUT TYPE ENTRY +//************************************************************************** -/*------------------------------------------------- - condition_equal - TRUE if two conditions are - equivalent --------------------------------------------------*/ +//------------------------------------------------- +// input_type_entry - constructors +//------------------------------------------------- -INLINE int condition_equal(const input_condition *cond1, const input_condition *cond2) +input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard) + : m_next(NULL), + m_type(type), + m_group(group), + m_player(player), + m_token(token), + m_name(name) { - return (cond1->mask == cond2->mask && cond1->value == cond2->value && cond1->condition == cond2->condition && strcmp(cond1->tag, cond2->tag) == 0); -} - - - -/*************************************************************************** - CORE SYSTEM MANAGEMENT -***************************************************************************/ - -/*------------------------------------------------- - input_port_init - initialize the input port - system --------------------------------------------------*/ - -ioport_manager::ioport_manager(running_machine &machine) - : safe_to_read(false), - last_frame_time(attotime::zero), - last_delta_nsec(0), - record_file(NULL), - playback_file(NULL), - playback_accumulated_speed(0), - playback_accumulated_frames(0), - codes(NULL), - inputx_timer(NULL), - queue_chars(NULL), - accept_char(NULL), - charqueue_empty(NULL), - current_rate(attotime::zero), - m_machine(machine) -{ - memset(type_to_entry, 0, sizeof(type_to_entry)); - memset(joystick_info, 0, sizeof(joystick_info)); + m_defseq[SEQ_TYPE_STANDARD] = m_seq[SEQ_TYPE_STANDARD] = standard; } -time_t ioport_manager::initialize() +input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard, input_seq decrement, input_seq increment) + : m_next(NULL), + m_type(type), + m_group(group), + m_player(player), + m_token(token), + m_name(name) { - /* add an exit callback and a frame callback */ - machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(input_port_exit), &machine())); - machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(FUNC(frame_update_callback), &machine())); - - /* initialize the default port info from the OSD */ - init_port_types(machine()); - - /* if we have a token list, proceed */ - device_iterator iter(machine().root_device()); - for (device_t *device = iter.first(); device != NULL; device = iter.next()) - { - astring errors; - input_port_list_init(*device, m_portlist, errors); - if (errors) - mame_printf_error("Input port errors:\n%s", errors.cstr()); - } - - init_port_state(machine()); - /* register callbacks for when we load configurations */ - config_register(machine(), "input", config_saveload_delegate(FUNC(load_config_callback), &machine()), config_saveload_delegate(FUNC(save_config_callback), &machine())); - - /* open playback and record files if specified */ - time_t basetime = playback_init(machine()); - record_init(machine()); - - return basetime; + m_defseq[SEQ_TYPE_STANDARD] = m_seq[SEQ_TYPE_STANDARD] = standard; + m_defseq[SEQ_TYPE_INCREMENT] = m_seq[SEQ_TYPE_INCREMENT] = increment; + m_defseq[SEQ_TYPE_DECREMENT] = m_seq[SEQ_TYPE_DECREMENT] = decrement; } -/*------------------------------------------------- - input_port_exit - exit callback to ensure - we clean up and close our files --------------------------------------------------*/ +//------------------------------------------------- +// configure_osd - set the token and name of an +// OSD entry +//------------------------------------------------- -static void input_port_exit(running_machine &machine) +void input_type_entry::configure_osd(const char *token, const char *name) { - /* close any playback or recording files */ - playback_end(machine, NULL); - record_end(machine, NULL); + assert(m_type >= IPT_OSD_1 && m_type <= IPT_OSD_16); + m_token = token; + m_name = name; } +//************************************************************************** +// DIGITAL JOYSTICKS +//************************************************************************** -/*************************************************************************** - PORT CONFIGURATIONS -***************************************************************************/ - -/*------------------------------------------------- - input_port_list_init - initialize an input - port list structure and allocate ports - according to the given tokens --------------------------------------------------*/ +//------------------------------------------------- +// digital_joystick - constructor +//------------------------------------------------- -void input_port_list_init(device_t &device, ioport_list &portlist, astring &errorbuf) +digital_joystick::digital_joystick(int player, int number) + : m_player(player), + m_number(number), + m_current(0), + m_current4way(0), + m_previous(0) { - /* no constructor, no list */ - ioport_constructor constructor = device.input_ports(); - if (constructor == NULL) - return; + memset(m_field, 0, sizeof(m_field)); +} - /* reset error buffer */ - errorbuf.reset(); - /* detokenize into the list */ - (*constructor)(device, portlist, errorbuf); +//------------------------------------------------- +// set_axis - configure a single axis of a +// digital joystick +//------------------------------------------------- - // collapse fields and sort the list - for (input_port_config *port = portlist.first(); port != NULL; port = port->next()) - port->collapse_fields(errorbuf); +digital_joystick::direction_t digital_joystick::set_axis(ioport_field &field) +{ + direction_t direction = direction_t((field.type() - (IPT_DIGITAL_JOYSTICK_FIRST + 1)) % 4); + m_field[direction] = &field; + return direction; } -/*------------------------------------------------- - input_field_by_tag_and_mask - return a pointer - to the first field that intersects the given - mask on the tagged port --------------------------------------------------*/ +//------------------------------------------------- +// frame_update - update the state of digital +// joysticks prior to accumulating the results +// in a port +//------------------------------------------------- -const input_field_config *input_field_by_tag_and_mask(running_machine &machine, const char *tag, input_port_value mask) +void digital_joystick::frame_update() { - const input_port_config *port = machine.root_device().ioport(tag); + // remember previous state and reset current state + m_previous = m_current; + m_current = 0; - /* if we got the port, look for the field */ - if (port != NULL) - for (const input_field_config *field = port->first_field(); field != NULL; field = field->next()) - if ((field->mask & mask) != 0) - return field; + // read all the associated ports + running_machine *machine = NULL; + for (direction_t direction = JOYDIR_UP; direction < JOYDIR_COUNT; direction++) + if (m_field[direction] != NULL) + { + machine = &m_field[direction]->machine(); + if (machine->input().seq_pressed(m_field[direction]->seq(SEQ_TYPE_STANDARD))) + m_current |= 1 << direction; + } - return NULL; + // lock out opposing directions (left + right or up + down) + if ((m_current & (UP_BIT | DOWN_BIT)) == (UP_BIT | DOWN_BIT)) + m_current &= ~(UP_BIT | DOWN_BIT); + if ((m_current & (LEFT_BIT | RIGHT_BIT)) == (LEFT_BIT | RIGHT_BIT)) + m_current &= ~(LEFT_BIT | RIGHT_BIT); + + // only update 4-way case if joystick has moved + if (m_current != m_previous) + { + m_current4way = m_current; + + // + // If joystick is pointing at a diagonal, acknowledge that the player moved + // the joystick by favoring a direction change. This minimizes frustration + // when using a keyboard for input, and maximizes responsiveness. + // + // For example, if you are holding "left" then switch to "up" (where both left + // and up are briefly pressed at the same time), we'll transition immediately + // to "up." + // + // Zero any switches that didn't change from the previous to current state. + // + if ((m_current4way & (UP_BIT | DOWN_BIT)) && + (m_current4way & (LEFT_BIT | RIGHT_BIT))) + { + m_current4way ^= m_current4way & m_previous; + } + + // + // If we are still pointing at a diagonal, we are in an indeterminant state. + // + // This could happen if the player moved the joystick from the idle position directly + // to a diagonal, or from one diagonal directly to an extreme diagonal. + // + // The chances of this happening with a keyboard are slim, but we still need to + // constrain this case. + // + // For now, just resolve randomly. + // + if ((m_current4way & (UP_BIT | DOWN_BIT)) && + (m_current4way & (LEFT_BIT | RIGHT_BIT))) + { + if (machine->rand() & 1) + m_current4way &= ~(LEFT_BIT | RIGHT_BIT); + else + m_current4way &= ~(UP_BIT | DOWN_BIT); + } + } } -/*************************************************************************** - ACCESSORS FOR INPUT FIELDS -***************************************************************************/ +//************************************************************************** +// NATURAL KEYBOARD +//************************************************************************** -/*------------------------------------------------- - input_field_name - return the field name for - a given input field --------------------------------------------------*/ +//------------------------------------------------- +// natural_keyboard - constructor +//------------------------------------------------- -const char *input_field_name(const input_field_config *field) +natural_keyboard::natural_keyboard(running_machine &machine) + : m_machine(machine), + m_bufbegin(0), + m_bufend(0), + m_status_keydown(false), + m_last_cr(false), + m_timer(NULL), + m_current_rate(attotime::zero) { - /* if we have a non-default name, use that */ - if ((field->state != NULL) && (field->state->name != NULL)) - return field->state->name; - if (field->name != NULL) - return field->name; + // reigster debugger commands + if (machine.debug_flags & DEBUG_FLAG_ENABLED) + { + debug_console_register_command(machine, "input", CMDFLAG_NONE, 0, 1, 1, execute_input); + debug_console_register_command(machine, "dumpkbd", CMDFLAG_NONE, 0, 0, 1, execute_dumpkbd); + } - /* otherwise, return the name associated with the type */ - return input_type_name(field->machine(), field->type, field->player); + // posting keys directly only makes sense for a computer + if (machine.ioport().has_keyboard()) + { + m_buffer.resize(KEY_BUFFER_SIZE); + m_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(natural_keyboard::timer), this)); + build_codes(machine.ioport()); + } } -/*------------------------------------------------- - input_field_seq - return the input sequence - for the given input field --------------------------------------------------*/ +//------------------------------------------------- +// configure - configure callbacks for full- +// featured keyboard support +//------------------------------------------------- -const input_seq &input_field_seq(const input_field_config *field, input_seq_type seqtype) +void natural_keyboard::configure(ioport_queue_chars_delegate queue_chars, ioport_accept_char_delegate accept_char, ioport_charqueue_empty_delegate charqueue_empty) { - /* if the field is disabled, return no key */ - if (field->flags & FIELD_FLAG_UNUSED) - return input_seq::empty_seq; - - /* select either the live or config state depending on whether we have live state */ - const input_seq &portseq = (field->state == NULL) ? field->seq[seqtype] : field->state->seq[seqtype]; - - /* if the portseq is the special default code, return the expanded default value */ - if (portseq.is_default()) - return input_type_seq(field->machine(), field->type, field->player, seqtype); - - /* otherwise, return the sequence as-is */ - return portseq; + // set the callbacks + m_queue_chars = queue_chars; + m_accept_char = accept_char; + m_charqueue_empty = charqueue_empty; } -/*------------------------------------------------- - input_field_get_user_settings - return the current - settings for the given input field --------------------------------------------------*/ +//------------------------------------------------- +// post - post a single character +//------------------------------------------------- -void input_field_get_user_settings(const input_field_config *field, input_field_user_settings *settings) +void natural_keyboard::post(unicode_char ch) { - int seqtype; - - /* zap the entire structure */ - memset(settings, 0, sizeof(*settings)); - - /* copy the basics */ - for (seqtype = 0; seqtype < ARRAY_LENGTH(settings->seq); seqtype++) - settings->seq[seqtype] = field->state->seq[seqtype]; - - /* if there's a list of settings or we're an adjuster, copy the current value */ - if (field->settinglist().count() != 0 || field->type == IPT_ADJUSTER) - settings->value = field->state->value; - - /* if there's analog data, extract the analog settings */ - if (field->state->analog != NULL) + // ignore any \n that are preceded by \r + if (m_last_cr && ch == '\n') { - settings->sensitivity = field->state->analog->sensitivity; - settings->delta = field->state->analog->delta; - settings->centerdelta = field->state->analog->centerdelta; - settings->reverse = field->state->analog->reverse; + m_last_cr = false; + return; } -} + // change all eolns to '\r' + if (ch == '\n') + ch = '\r'; + else + m_last_cr = (ch == '\r'); -/*------------------------------------------------- - input_field_set_user_settings - modify the current - settings for the given input field --------------------------------------------------*/ - -void input_field_set_user_settings(const input_field_config *field, const input_field_user_settings *settings) -{ - int seqtype; - - /* copy the basics */ - for (seqtype = 0; seqtype < ARRAY_LENGTH(settings->seq); seqtype++) + // logging + if (LOG_NATURAL_KEYBOARD) { - const input_seq &defseq = input_type_seq(field->machine(), field->type, field->player, (input_seq_type)seqtype); - if (defseq == settings->seq[seqtype]) - field->state->seq[seqtype].set_default(); - else - field->state->seq[seqtype] = settings->seq[seqtype]; + const keycode_map_entry *code = find_code(ch); + astring tempstr; + logerror("natural_keyboard::post(): code=%i (%s) field->name='%s'\n", int(ch), unicode_to_string(tempstr, ch), (code != NULL && code->field[0] != NULL) ? code->field[0]->name() : "<null>"); } - /* if there's a list of settings or we're an adjuster, copy the current value */ - if (field->settinglist().count() != 0 || field->type == IPT_ADJUSTER) - field->state->value = settings->value; + // can we post this key in the queue directly? + if (can_post_directly(ch)) + internal_post(ch); - /* if there's analog data, extract the analog settings */ - if (field->state->analog != NULL) + // can we post this key with an alternate representation? + else if (can_post_alternate(ch)) { - field->state->analog->sensitivity = settings->sensitivity; - field->state->analog->delta = settings->delta; - field->state->analog->centerdelta = settings->centerdelta; - field->state->analog->reverse = settings->reverse; + const char_info *info = char_info::find(ch); + assert(info != NULL && info->alternate != NULL); + const char *altstring = info->alternate; + while (*altstring != 0) + { + altstring += uchar_from_utf8(&ch, altstring, strlen(altstring)); + internal_post(ch); + } } } -/*------------------------------------------------- - input_field_setting_name - return the expanded - setting name for a field --------------------------------------------------*/ +//------------------------------------------------- +// post - post a unicode encoded string +//------------------------------------------------- -const char *input_field_setting_name(const input_field_config *field) +void natural_keyboard::post(const unicode_char *text, size_t length, attotime rate) { - const input_setting_config *setting; - - /* only makes sense if we have settings */ - assert(field->settinglist().count() != 0); + // set the fixed rate + m_current_rate = rate; + + // 0 length means strlen + if (length == 0) + for (const unicode_char *scan = text; *scan != 0; scan++) + length++; - /* scan the list of settings looking for a match on the current value */ - for (setting = field->settinglist().first(); setting != NULL; setting = setting->next()) - if (input_condition_true(field->machine(), &setting->condition, field->port().owner())) - if (setting->value == field->state->value) - return setting->name; - - return "INVALID"; + // iterate over characters or until the buffer is full up + while (length > 0 && !full()) + { + // fetch next character + post(*text++); + length--; + } } -/*------------------------------------------------- - input_field_has_previous_setting - return TRUE - if the given field has a "previous" setting --------------------------------------------------*/ +//------------------------------------------------- +// post_utf8 - post a UTF-8 encoded string +//------------------------------------------------- -int input_field_has_previous_setting(const input_field_config *field) +void natural_keyboard::post_utf8(const char *text, size_t length, attotime rate) { - const input_setting_config *setting; - - /* only makes sense if we have settings */ - assert(field->settinglist().count() != 0); - - /* scan the list of settings looking for a match on the current value */ - for (setting = field->settinglist().first(); setting != NULL; setting = setting->next()) - if (input_condition_true(field->machine(), &setting->condition, field->port().owner())) - return (setting->value != field->state->value); + // set the fixed rate + m_current_rate = rate; + + // 0-length means strlen + if (length == 0) + length = strlen(text); - return FALSE; + // iterate until out of characters + while (length > 0) + { + // decode the next character + unicode_char uc; + int count = uchar_from_utf8(&uc, text, length); + if (count < 0) + { + count = 1; + uc = INVALID_CHAR; + } + + // append to the buffer + post(uc); + text += count; + length -= count; + } } -/*------------------------------------------------- - input_field_select_previous_setting - select - the previous item for a DIP switch or - configuration field --------------------------------------------------*/ +//------------------------------------------------- +// post_coded - post a coded string +//------------------------------------------------- -void input_field_select_previous_setting(const input_field_config *field) +void natural_keyboard::post_coded(const char *text, size_t length, attotime rate) { - const input_setting_config *setting, *prevsetting; - int found_match = FALSE; + static const struct + { + const char *key; + unicode_char code; + } codes[] = + { + { "BACKSPACE", 8 }, + { "BS", 8 }, + { "BKSP", 8 }, + { "DEL", UCHAR_MAMEKEY(DEL) }, + { "DELETE", UCHAR_MAMEKEY(DEL) }, + { "END", UCHAR_MAMEKEY(END) }, + { "ENTER", 13 }, + { "ESC", '\033' }, + { "HOME", UCHAR_MAMEKEY(HOME) }, + { "INS", UCHAR_MAMEKEY(INSERT) }, + { "INSERT", UCHAR_MAMEKEY(INSERT) }, + { "PGDN", UCHAR_MAMEKEY(PGDN) }, + { "PGUP", UCHAR_MAMEKEY(PGUP) }, + { "SPACE", 32 }, + { "TAB", 9 }, + { "F1", UCHAR_MAMEKEY(F1) }, + { "F2", UCHAR_MAMEKEY(F2) }, + { "F3", UCHAR_MAMEKEY(F3) }, + { "F4", UCHAR_MAMEKEY(F4) }, + { "F5", UCHAR_MAMEKEY(F5) }, + { "F6", UCHAR_MAMEKEY(F6) }, + { "F7", UCHAR_MAMEKEY(F7) }, + { "F8", UCHAR_MAMEKEY(F8) }, + { "F9", UCHAR_MAMEKEY(F9) }, + { "F10", UCHAR_MAMEKEY(F10) }, + { "F11", UCHAR_MAMEKEY(F11) }, + { "F12", UCHAR_MAMEKEY(F12) }, + { "QUOTE", '\"' } + }; - /* only makes sense if we have settings */ - assert(field->settinglist().count() != 0); + // set the fixed rate + m_current_rate = rate; + + // 0-length means strlen + if (length == 0) + length = strlen(text); - /* scan the list of settings looking for a match on the current value */ - prevsetting = NULL; - for (setting = field->settinglist().first(); setting != NULL; setting = setting->next()) - if (input_condition_true(field->machine(), &setting->condition, field->port().owner())) - { - if (setting->value == field->state->value) + // iterate through the source string + size_t curpos = 0; + while (curpos < length) + { + // extract next character + unicode_char ch = text[curpos]; + size_t increment = 1; + + // look for escape characters + if (ch == '{') + for (int codenum = 0; codenum < ARRAY_LENGTH(codes); codenum++) { - found_match = TRUE; - if (prevsetting != NULL) - break; + size_t keylen = strlen(codes[codenum].key); + if (curpos + keylen + 2 <= length) + if (core_strnicmp(codes[codenum].key, &text[curpos + 1], keylen) == 0 && text[curpos + keylen + 1] == '}') + { + ch = codes[codenum].code; + increment = keylen + 2; + } } - prevsetting = setting; - } - /* if we didn't find a matching value, select the first */ - if (!found_match) - { - for (prevsetting = field->settinglist().first(); prevsetting != NULL; prevsetting = prevsetting->next()) - if (input_condition_true(field->machine(), &prevsetting->condition, field->port().owner())) - break; + // if we got a code, post it + if (ch != 0) + post(ch); + curpos += increment; } - - /* update the value to the previous one */ - if (prevsetting != NULL) - field->state->value = prevsetting->value; } -/*------------------------------------------------- - input_field_has_next_setting - return TRUE - if the given field has a "next" setting --------------------------------------------------*/ +//------------------------------------------------- +// build_codes - given an input port table, create +// an input code table useful for mapping unicode +// chars +//------------------------------------------------- -int input_field_has_next_setting(const input_field_config *field) +void natural_keyboard::build_codes(ioport_manager &manager) { - const input_setting_config *setting; - int found = FALSE; + // iterate over shift keys + ioport_field *shift[UCHAR_SHIFT_END + 1 - UCHAR_SHIFT_BEGIN] = { 0 }; + for (int curshift = 0; curshift <= ARRAY_LENGTH(shift); curshift++) + if (curshift == 0 || shift[curshift - 1] != NULL) - /* only makes sense if we have settings */ - assert(field->settinglist().count() != 0); + // iterate over ports and fields + for (ioport_port *port = manager.first_port(); port != NULL; port = port->next()) + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) + if (field->type() == IPT_KEYBOARD) + { + // fetch the code, ignoring 0 + unicode_char code = field->keyboard_code(curshift); + if (code == 0) + continue; - /* scan the list of settings looking for a match on the current value */ - for (setting = field->settinglist().first(); setting != NULL; setting = setting->next()) - if (input_condition_true(field->machine(), &setting->condition, field->port().owner())) - { - if (found) - return TRUE; - if (setting->value == field->state->value) - found = TRUE; - } + // is this a shifter key? + if (code >= UCHAR_SHIFT_BEGIN && code <= UCHAR_SHIFT_END) + shift[code - UCHAR_SHIFT_BEGIN] = field; - return FALSE; + // not a shifter key; record normally + else + { + keycode_map_entry newcode; + if (curshift == 0) + newcode.field[0] = field; + else + { + newcode.field[0] = shift[curshift - 1]; + newcode.field[1] = field; + } + newcode.ch = code; + m_keycode_map.append(newcode); + + if (LOG_NATURAL_KEYBOARD) + { + astring tempstr; + logerror("natural_keyboard: code=%i (%s) port=%p field->name='%s'\n", int(code), unicode_to_string(tempstr, code), port, field->name()); + } + } + } } -/*------------------------------------------------- - input_field_select_next_setting - select the - next item for a DIP switch or - configuration field --------------------------------------------------*/ +//------------------------------------------------- +// can_post_directly - determine if the given +// unicode character can be directly posted +//------------------------------------------------- -void input_field_select_next_setting(const input_field_config *field) +bool natural_keyboard::can_post_directly(unicode_char ch) { - const input_setting_config *setting, *nextsetting; + // if we have a queueing callback, then it depends on whether we can accept the character + if (!m_queue_chars.isnull()) + return m_accept_char.isnull() ? true : m_accept_char(ch); - /* only makes sense if we have settings */ - assert(field->settinglist().count() != 0); + // otherwise, it depends on the input codes + const keycode_map_entry *code = find_code(ch); + return (code != NULL && code->field[0] != NULL); +} - /* scan the list of settings looking for a match on the current value */ - nextsetting = NULL; - for (setting = field->settinglist().first(); setting != NULL; setting = setting->next()) - if (input_condition_true(field->machine(), &setting->condition, field->port().owner())) - if (setting->value == field->state->value) - break; - /* if we found one, scan forward for the next valid one */ - if (setting != NULL) - for (nextsetting = setting->next(); nextsetting != NULL; nextsetting = nextsetting->next()) - if (input_condition_true(field->machine(), &nextsetting->condition, field->port().owner())) - break; +//------------------------------------------------- +// can_post_alternate - determine if the given +// unicode character can be posted via translation +//------------------------------------------------- - /* if we hit the end, search from the beginning */ - if (nextsetting == NULL) - for (nextsetting = field->settinglist().first(); nextsetting != NULL; nextsetting = nextsetting->next()) - if (input_condition_true(field->machine(), &nextsetting->condition, field->port().owner())) - break; +bool natural_keyboard::can_post_alternate(unicode_char ch) +{ + const char_info *info = char_info::find(ch); + if (info == NULL) + return false; + + const char *altstring = info->alternate; + if (altstring == NULL) + return false; - /* update the value to the previous one */ - if (nextsetting != NULL) - field->state->value = nextsetting->value; + while (*altstring != 0) + { + unicode_char uchar; + int count = uchar_from_utf8(&uchar, altstring, strlen(altstring)); + if (count <= 0) + return false; + if (!can_post_directly(uchar)) + return false; + altstring += count; + } + return true; } +//------------------------------------------------- +// choose_delay - determine the delay between +// posting keyboard events +//------------------------------------------------- -/*************************************************************************** - ACCESSORS FOR INPUT TYPES -***************************************************************************/ +attotime natural_keyboard::choose_delay(unicode_char ch) +{ + // if we have a live rate, just use that + if (m_current_rate != attotime::zero) + return m_current_rate; -/*------------------------------------------------- - input_type_is_analog - return TRUE if - the given type represents an analog control --------------------------------------------------*/ + // systems with queue_chars can afford a much smaller delay + if (!m_queue_chars.isnull()) + return attotime::from_msec(10); -int input_type_is_analog(int type) -{ - return (type >= __ipt_analog_start && type <= __ipt_analog_end); + // otherwise, default to constant delay with a longer delay on CR + return attotime::from_msec((ch == '\r') ? 200 : 50); } -/*------------------------------------------------- - input_type_name - return the name - for the given type/player --------------------------------------------------*/ +//------------------------------------------------- +// internal_post - post a keyboard event +//------------------------------------------------- -const char *input_type_name(running_machine &machine, int type, int player) +void natural_keyboard::internal_post(unicode_char ch) { - /* if we have a machine, use the live state and quick lookup */ - ioport_manager &portdata = machine.ioport(); - input_type_entry *entry = portdata.type_to_entry[type][player]; - if (entry != NULL) - return entry->name; + // need to start up the timer? + if (empty()) + { + m_timer->adjust(choose_delay(ch)); + m_status_keydown = 0; + } - /* if we find nothing, return an invalid group */ - return "???"; + // add to the buffer, resizing if necessary + m_buffer[m_bufend++] = ch; + if ((m_bufend + 1) % m_buffer.count() == m_bufbegin) + m_buffer.resize(m_buffer.count() + KEY_BUFFER_SIZE, true); + m_bufend %= m_buffer.count(); } -/*------------------------------------------------- - input_type_group - return the group - for the given type/player --------------------------------------------------*/ +//------------------------------------------------- +// timer - timer callback to keep things flowing +// when posting a string of characters +//------------------------------------------------- -int input_type_group(running_machine &machine, int type, int player) +void natural_keyboard::timer(void *ptr, int param) { - ioport_manager &portdata = machine.ioport(); - input_type_entry *entry = portdata.type_to_entry[type][player]; - if (entry != NULL) - return entry->group; + // the driver has a queue_chars handler + if (!m_queue_chars.isnull()) + { + while (!empty() && m_queue_chars(&m_buffer[m_bufbegin], 1)) + { + m_bufbegin = (m_bufbegin + 1) % m_buffer.count(); + if (m_current_rate != attotime::zero) + break; + } + } - /* if we find nothing, return an invalid group */ - return IPG_INVALID; + // the driver does not have a queue_chars handler + else + { + if (m_status_keydown) + m_bufbegin = (m_bufbegin + 1) % m_buffer.count(); + m_status_keydown = !m_status_keydown; + } + + // need to make sure timerproc is called again if buffer not empty + if (!empty()) + m_timer->adjust(choose_delay(m_buffer[m_bufbegin])); } -/*------------------------------------------------- - input_type_seq - return the input - sequence for the given type/player --------------------------------------------------*/ +//------------------------------------------------- +// unicode_to_string - obtain a string +// representation of a given code; used for +// logging and debugging +//------------------------------------------------- -const input_seq &input_type_seq(running_machine &machine, int type, int player, input_seq_type seqtype) +const char *natural_keyboard::unicode_to_string(astring &buffer, unicode_char ch) { - assert(type >= 0 && type < __ipt_max); - assert(player >= 0 && player < MAX_PLAYERS); + buffer.reset(); + switch (ch) + { + // check some magic values + case '\0': buffer.cpy("\\0"); break; + case '\r': buffer.cpy("\\r"); break; + case '\n': buffer.cpy("\\n"); break; + case '\t': buffer.cpy("\\t"); break; - /* if we have a machine, use the live state and quick lookup */ - ioport_manager &portdata = machine.ioport(); - input_type_entry *entry = portdata.type_to_entry[type][player]; - if (entry != NULL) - return entry->seq[seqtype]; + default: + // seven bit ASCII is easy + if (ch >= 32 && ch < 128) + { + char temp[2] = { char(ch), 0 }; + buffer.cpy(temp); + } + else if (ch >= UCHAR_MAMEKEY_BEGIN) + { + // try to obtain a codename with code_name(); this can result in an empty string + input_code code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(ch - UCHAR_MAMEKEY_BEGIN)); + machine().input().code_name(buffer, code); + } - /* if we find nothing, return an empty sequence */ - return input_seq::empty_seq; + // did we fail to resolve? if so, we have a last resort + if (buffer.len() == 0) + buffer.format("U+%04X", unsigned(ch)); + break; + } + return buffer; } -/*------------------------------------------------- - input_type_set_seq - change the input - sequence for the given type/player --------------------------------------------------*/ +//------------------------------------------------- +// find_code - find a code in our lookup table +//------------------------------------------------- -void input_type_set_seq(running_machine &machine, int type, int player, input_seq_type seqtype, const input_seq *newseq) +const natural_keyboard::keycode_map_entry *natural_keyboard::find_code(unicode_char ch) const { - ioport_manager &portdata = machine.ioport(); - input_type_entry *entry = portdata.type_to_entry[type][player]; - if (entry != NULL) - entry->seq[seqtype] = *newseq; + for (int code = 0; m_keycode_map[code].ch != 0; code++) + if (m_keycode_map[code].ch == ch) + return &m_keycode_map[code]; + return NULL; } -/*------------------------------------------------- - input_type_pressed - return TRUE if - the sequence for the given input type/player - is pressed --------------------------------------------------*/ +//------------------------------------------------- +// frame_update - once per frame update of the +// natural keyboard state +//------------------------------------------------- -int input_type_pressed(running_machine &machine, int type, int player) +void natural_keyboard::frame_update(ioport_port &port, ioport_value &digital) { - return machine.input().seq_pressed(input_type_seq(machine, type, player, SEQ_TYPE_STANDARD)); + // is there currently a key down? + if (m_status_keydown && !empty()) + { + // loop through this character's component codes + const keycode_map_entry *code = find_code(m_buffer[m_bufbegin]); + if (code != NULL) + for (int fieldnum = 0; fieldnum < ARRAY_LENGTH(code->field) && code->field[fieldnum] != NULL; fieldnum++) + if (&code->field[fieldnum]->port() == &port) + digital |= code->field[fieldnum]->mask(); + } } -/*------------------------------------------------- - input_type_list - return the list of types --------------------------------------------------*/ +//------------------------------------------------- +// key_name - returns the name of a specific key +//------------------------------------------------- -const simple_list<input_type_entry> &input_type_list(running_machine &machine) +const char *natural_keyboard::key_name(astring &string, unicode_char ch) { - ioport_manager &portdata = machine.ioport(); - return portdata.typelist; -} - + // attempt to get the string from the character info table + const char_info *ci = char_info::find(ch); + const char *result = (ci != NULL) ? ci->name : NULL; + if (result != NULL) + string.cpy(result); + // if that doesn't work, convert to UTF-8 + else if (ch > 0x7F || isprint(ch)) + { + char buf[10]; + int count = utf8_from_uchar(buf, ARRAY_LENGTH(buf), ch); + buf[count] = 0; + string.cpy(buf); + } -/*************************************************************************** - PORT CHECKING -***************************************************************************/ - - -/*------------------------------------------------- - input_port_exists - return whether an input - port exists --------------------------------------------------*/ - -bool input_port_exists(running_machine &machine, const char *tag) -{ - return machine.root_device().ioport(tag) != 0; + // otherwise, opt for question marks + else + string.cpy("???"); + return string; } -/*------------------------------------------------- - input_port_active - return a bitmask of which - bits of an input port are active (i.e. not - unused or unknown) --------------------------------------------------*/ +//------------------------------------------------- +// execute_input - debugger command to enter +// natural keyboard input +//------------------------------------------------- -input_port_value input_port_active(running_machine &machine, const char *tag) +void natural_keyboard::execute_input(running_machine &machine, int ref, int params, const char *param[]) { - const input_port_config *port = machine.root_device().ioport(tag); - if (port == NULL) - fatalerror("Unable to locate input port '%s'", tag); - return port->active; + machine.ioport().natkeyboard().post_coded(param[0]); } -/*------------------------------------------------- - input_port_active_safe - return a bitmask of - which bits of an input port are active (i.e. - not unused or unknown), or a default value if - the port does not exist --------------------------------------------------*/ +//------------------------------------------------- +// execute_dumpkbd - debugger command to natural +// keyboard codes +//------------------------------------------------- -input_port_value input_port_active_safe(running_machine &machine, const char *tag, input_port_value defvalue) +void natural_keyboard::execute_dumpkbd(running_machine &machine, int ref, int params, const char *param[]) { - const input_port_config *port = machine.root_device().ioport(tag); - return port == NULL ? defvalue : port->active; -} + // was there a file specified? + const char *filename = (params > 0) ? param[0] : NULL; + FILE *file = NULL; + if (filename != NULL) + { + // if so, open it + file = fopen(filename, "w"); + if (file == NULL) + { + debug_console_printf(machine, "Cannot open \"%s\"\n", filename); + return; + } + } + + // loop through all codes + natural_keyboard &natkeyboard = machine.ioport().natkeyboard(); + dynamic_array<keycode_map_entry> &keycode_map = natkeyboard.m_keycode_map; + astring buffer, tempstr; + const size_t left_column_width = 24; + for (int index = 0; index < keycode_map.count(); index++) + { + // describe the character code + const keycode_map_entry &code = keycode_map[index]; + buffer.printf("%08X (%s) ", code.ch, natkeyboard.unicode_to_string(tempstr, code.ch)); + // pad with spaces + while (buffer.len() < left_column_width) + buffer.cat(' '); + // identify the keys used + for (int field = 0; field < ARRAY_LENGTH(code.field) && code.field[field] != 0; field++) + buffer.catprintf("%s'%s'", (field > 0) ? ", " : "", code.field[field]->name()); -/*************************************************************************** - PORT READING -***************************************************************************/ + // and output it as appropriate + if (file != NULL) + fprintf(file, "%s\n", buffer.cstr()); + else + debug_console_printf(machine, "%s\n", buffer.cstr()); + } -/*------------------------------------------------- - input_port_read_direct - return the value of - an input port --------------------------------------------------*/ + // cleanup + if (file != NULL) + fclose(file); +} -input_port_value input_port_read_direct(const input_port_config *port) -{ - assert(port != NULL); - ioport_manager &portdata = port->machine().ioport(); - analog_field_state *analog; - device_field_info *device_field; - input_port_value result; - assert_always(portdata.safe_to_read, "Input ports cannot be read at init time!"); +//************************************************************************** +// I/O PORT CONDITION +//************************************************************************** - /* start with the digital */ - result = port->state->digital; +//------------------------------------------------- +// eval - evaluate condition +//------------------------------------------------- - /* update read values */ - for (device_field = port->state->readdevicelist; device_field != NULL; device_field = device_field->next) - if (input_condition_true(port->machine(), &device_field->field->condition, port->owner())) - { - /* replace the bits with bits from the device */ - input_port_value newval = device_field->field->read(*device_field->field, device_field->field->read_param); - device_field->oldval = newval; - result = (result & ~device_field->field->mask) | ((newval << device_field->shift) & device_field->field->mask); - } +bool ioport_condition::eval(device_t &device) const +{ + // always condition is always true + if (m_condition == ALWAYS) + return true; - /* update VBLANK bits */ - if (port->state->vblank != 0) + // otherwise, read the referenced port and switch off the condition type + ioport_value condvalue = device.ioport(m_tag)->read(); + switch (m_condition) { - if (port->machine().primary_screen->vblank()) - result |= port->state->vblank; - else - result &= ~port->state->vblank; + case ALWAYS: return true; + case EQUALS: return ((condvalue & m_mask) == m_value); + case NOTEQUALS: return ((condvalue & m_mask) != m_value); + case GREATERTHAN: return ((condvalue & m_mask) > m_value); + case NOTGREATERTHAN: return ((condvalue & m_mask) <= m_value); + case LESSTHAN: return ((condvalue & m_mask) < m_value); + case NOTLESSTHAN: return ((condvalue & m_mask) >= m_value); } + return true; +} - /* apply active high/low state to digital, read, and VBLANK inputs */ - result ^= port->state->defvalue; - - /* merge in analog portions */ - for (analog = port->state->analoglist; analog != NULL; analog = analog->next) - if (input_condition_true(port->machine(), &analog->field->condition, port->owner())) - { - /* start with the raw value */ - INT32 value = analog->accum; - /* interpolate if appropriate and if time has passed since the last update */ - if (analog->interpolate && !(analog->field->flags & ANALOG_FLAG_RESET) && portdata.last_delta_nsec != 0) - { - attoseconds_t nsec_since_last = (port->machine().time() - portdata.last_frame_time).as_attoseconds() / ATTOSECONDS_PER_NANOSECOND; - value = analog->previous + ((INT64)(analog->accum - analog->previous) * nsec_since_last / portdata.last_delta_nsec); - } - /* apply standard analog settings */ - value = apply_analog_settings(value, analog); +//************************************************************************** +// I/O PORT SETTING +//************************************************************************** - /* remap the value if needed */ - if (analog->field->remap_table != NULL) - value = analog->field->remap_table[value]; +//------------------------------------------------- +// ioport_setting - constructor +//------------------------------------------------- - /* invert bits if needed */ - if (analog->field->flags & ANALOG_FLAG_INVERT) - value = ~value; +ioport_setting::ioport_setting(ioport_field &field, ioport_value _value, const char *_name) + : m_next(NULL), + m_field(field), + m_value(_value), + m_name(_name) +{ +} - /* insert into the port */ - result = (result & ~analog->field->mask) | ((value << analog->shift) & analog->field->mask); - } - return result; -} +//************************************************************************** +// I/O PORT DIP LOCATION +//************************************************************************** -/*------------------------------------------------- - input_port_read - return the value of - an input port specified by tag --------------------------------------------------*/ +//------------------------------------------------- +// ioport_diplocation - constructor +//------------------------------------------------- -input_port_value input_port_read(running_machine &machine, const char *tag) +ioport_diplocation::ioport_diplocation(const char *name, UINT8 swnum, bool invert) + : m_next(NULL), + m_name(name), + m_number(swnum), + m_invert(invert) { - const input_port_config *port = machine.root_device().ioport(tag); - if (port == NULL) - fatalerror("Unable to locate input port '%s'", tag); - return input_port_read_direct(port); } -/*------------------------------------------------- - input_port_read - return the value of - a device input port specified by tag --------------------------------------------------*/ -input_port_value input_port_read(device_t &device, const char *tag) -{ - const input_port_config *port = device.ioport(tag); - if (port == NULL) - fatalerror("Unable to locate input port '%s'", tag); - return input_port_read_direct(port); +//************************************************************************** +// I/O PORT FIELD +//************************************************************************** + +//------------------------------------------------- +// ioport_field - constructor +//------------------------------------------------- + +ioport_field::ioport_field(ioport_port &port, ioport_type type, ioport_value defvalue, ioport_value maskbits, const char *name) + : m_next(NULL), + m_port(port), + m_live(NULL), + m_modcount(port.modcount()), + m_mask(maskbits), + m_defvalue(defvalue & maskbits), + m_type(type), + m_player(0), + m_flags(0), + m_impulse(0), + m_name(name), + m_read_param(NULL), + m_write_param(NULL), + m_min(0), + m_max(maskbits), + m_sensitivity(0), + m_delta(0), + m_centerdelta(0), + m_crosshair_axis(CROSSHAIR_AXIS_NONE), + m_crosshair_scale(1.0), + m_crosshair_offset(0), + m_crosshair_altaxis(0), + m_full_turn_count(0), + m_remap_table(NULL), + m_way(0) +{ + // reset sequences and chars + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + m_seq[seqtype].set_default(); + m_chars[0] = m_chars[1] = m_chars[2] = unicode_char(0); + + // for DIP switches and configs, look for a default value from the owner + if (type == IPT_DIPSWITCH || type == IPT_CONFIG) + { + const input_device_default *def = device().input_ports_defaults(); + if (def != NULL) + { + const char *fulltag = port.tag(); + astring fullpath; + for ( ; def->tag != NULL; def++) + if (device().subtag(fullpath, def->tag) == fulltag && def->mask == m_mask) + m_defvalue = def->defvalue & m_mask; + } + } } -/*------------------------------------------------- - input_port_read_safe - return the value of - an input port specified by tag, or a default - value if the port does not exist --------------------------------------------------*/ +//------------------------------------------------- +// ~ioport_field - destructor +//------------------------------------------------- -input_port_value input_port_read_safe(running_machine &machine, const char *tag, UINT32 defvalue) +ioport_field::~ioport_field() { - const input_port_config *port = machine.root_device().ioport(tag); - return (port == NULL) ? defvalue : input_port_read_direct(port); + global_free(m_live); } -/*------------------------------------------------- - input_port_read_crosshair - return the - extracted crosshair values for the given - player --------------------------------------------------*/ +//------------------------------------------------- +// name - return the field name for a given input +// field +//------------------------------------------------- -int input_port_get_crosshair_position(running_machine &machine, int player, float *x, float *y) +const char *ioport_field::name() const { - const input_port_config *port; - const input_field_config *field; - int gotx = FALSE, goty = FALSE; + // if we have a non-default name, use that + if (m_live != NULL && m_live->name) + return m_live->name; + if (m_name != NULL) + return m_name; - /* read all the lightgun values */ - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - for (field = port->first_field(); field != NULL; field = field->next()) - if (field->player == player && field->crossaxis != CROSSHAIR_AXIS_NONE) - if (input_condition_true(machine, &field->condition, port->owner())) - { - analog_field_state *analog = field->state->analog; - INT32 rawvalue = apply_analog_settings(analog->accum, analog) & (analog->field->mask >> analog->shift); - float value = (float)(rawvalue - field->state->analog->adjmin) / (float)(field->state->analog->adjmax - field->state->analog->adjmin); + // otherwise, return the name associated with the type + return machine().ioport().type_name(m_type, m_player); +} - /* apply the scale and offset */ - if (field->crossscale < 0) - value = -(1.0 - value) * field->crossscale; - else - value *= field->crossscale; - value += field->crossoffset; - /* apply custom mapping if necessary */ - if (!field->crossmapper.isnull()) - value = field->crossmapper(*field, value); +//------------------------------------------------- +// seq - return the input sequence for the given +// input field +//------------------------------------------------- - /* handle X axis */ - if (field->crossaxis == CROSSHAIR_AXIS_X) - { - *x = value; - gotx = TRUE; - if (field->crossaltaxis != 0) - { - *y = field->crossaltaxis; - goty = TRUE; - } - } +const input_seq &ioport_field::seq(input_seq_type seqtype) const +{ + // if the field is disabled, return no key + if (m_flags & FIELD_FLAG_UNUSED) + return input_seq::empty_seq; - /* handle Y axis */ - else - { - *y = value; - goty = TRUE; - if (field->crossaltaxis != 0) - { - *x = field->crossaltaxis; - gotx = TRUE; - } - } + // select either the live or config state depending on whether we have live state + const input_seq &portseq = (m_live == NULL) ? m_seq[seqtype] : m_live->seq[seqtype]; - /* if we got both, stop */ - if (gotx && goty) - break; - } + // if the portseq is the special default code, return the expanded default value + if (portseq.is_default()) + return machine().ioport().type_seq(m_type, m_player, seqtype); - return (gotx && goty); + // otherwise, return the sequence as-is + return portseq; } -/*------------------------------------------------- - input_port_update_defaults - force an update - to the input port values based on current - conditions --------------------------------------------------*/ +//------------------------------------------------- +// type_class - return the type class for this +// field +//------------------------------------------------- -void input_port_update_defaults(running_machine &machine) +ioport_type_class ioport_field::type_class() const { - int loopnum; + ioport_type_class result; - /* two passes to catch conditionals properly */ - for (loopnum = 0; loopnum < 2; loopnum++) + switch (m_type) { - const input_port_config *port; + case IPT_JOYSTICK_UP: + case IPT_JOYSTICK_DOWN: + case IPT_JOYSTICK_LEFT: + case IPT_JOYSTICK_RIGHT: + case IPT_JOYSTICKLEFT_UP: + case IPT_JOYSTICKLEFT_DOWN: + case IPT_JOYSTICKLEFT_LEFT: + case IPT_JOYSTICKLEFT_RIGHT: + case IPT_JOYSTICKRIGHT_UP: + case IPT_JOYSTICKRIGHT_DOWN: + case IPT_JOYSTICKRIGHT_LEFT: + case IPT_JOYSTICKRIGHT_RIGHT: + case IPT_BUTTON1: + case IPT_BUTTON2: + case IPT_BUTTON3: + case IPT_BUTTON4: + case IPT_BUTTON5: + case IPT_BUTTON6: + case IPT_BUTTON7: + case IPT_BUTTON8: + case IPT_BUTTON9: + case IPT_BUTTON10: + case IPT_AD_STICK_X: + case IPT_AD_STICK_Y: + case IPT_AD_STICK_Z: + case IPT_TRACKBALL_X: + case IPT_TRACKBALL_Y: + case IPT_LIGHTGUN_X: + case IPT_LIGHTGUN_Y: + case IPT_MOUSE_X: + case IPT_MOUSE_Y: + case IPT_START: + case IPT_SELECT: + result = INPUT_CLASS_CONTROLLER; + break; - /* loop over all input ports */ - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - { - const input_field_config *field; + case IPT_KEYPAD: + case IPT_KEYBOARD: + result = INPUT_CLASS_KEYBOARD; + break; - /* only clear on the first pass */ - if (loopnum == 0) - port->state->defvalue = 0; + case IPT_CONFIG: + result = INPUT_CLASS_CONFIG; + break; - /* first compute the default value for the entire port */ - for (field = port->first_field(); field != NULL; field = field->next()) - if (input_condition_true(machine, &field->condition, port->owner())) - port->state->defvalue = (port->state->defvalue & ~field->mask) | (field->state->value & field->mask); - } + case IPT_DIPSWITCH: + result = INPUT_CLASS_DIPSWITCH; + break; + + case 0: + if (m_name != NULL && m_name != (const char *)-1) + result = INPUT_CLASS_MISC; + else + result = INPUT_CLASS_INTERNAL; + break; + + default: + result = INPUT_CLASS_INTERNAL; + break; } + return result; } -/*------------------------------------------------- - apply_analog_settings - return the value of - an input port --------------------------------------------------*/ +//------------------------------------------------- +// keyboard_code - accesses a particular keyboard +// code +//------------------------------------------------- -static INT32 apply_analog_settings(INT32 value, analog_field_state *analog) +unicode_char ioport_field::keyboard_code(int which) const { - /* apply the min/max and then the sensitivity */ - value = apply_analog_min_max(analog, value); - value = APPLY_SENSITIVITY(value, analog->sensitivity); + unicode_char ch = m_chars[which]; - /* apply reversal if needed */ - if (analog->reverse) - value = analog->reverse_val - value; - else if (analog->single_scale) - /* it's a pedal or the default value is equal to min/max */ - /* so we need to adjust the center to the minimum */ - value -= INPUT_ABSOLUTE_MIN; - - /* map differently for positive and negative values */ - if (value >= 0) - value = APPLY_SCALE(value, analog->scalepos); - else - value = APPLY_SCALE(value, analog->scaleneg); - value += analog->adjdefvalue; - - return value; + // special hack to allow for PORT_CODE('\xA3') + if (ch >= 0xffffff80 && ch <= 0xffffffff) + ch &= 0xff; + return ch; } -/*************************************************************************** - PORT WRITING -***************************************************************************/ - -/*------------------------------------------------- - input_port_write_direct - write a value - to a port --------------------------------------------------*/ +//------------------------------------------------- +// get_user_settings - return the current +// settings for the given input field +//------------------------------------------------- -void input_port_write_direct(const input_port_config *port, input_port_value data, input_port_value mem_mask) +void ioport_field::get_user_settings(user_settings &settings) { - /* call device line write handlers */ - device_field_info *device_field; + // zap the entire structure + memset(&settings, 0, sizeof(settings)); - COMBINE_DATA(&port->state->outputvalue); + // copy the basics + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + settings.seq[seqtype] = m_live->seq[seqtype]; - for (device_field = port->state->writedevicelist; device_field; device_field = device_field->next) - if (device_field->field->type == IPT_OUTPUT && input_condition_true(port->machine(), &device_field->field->condition, port->owner())) - { - input_port_value newval = ( (port->state->outputvalue ^ device_field->field->defvalue ) & device_field->field->mask) >> device_field->shift; - - /* if the bits have write, call the handler */ - if (device_field->oldval != newval) - { - device_field->field->write(*device_field->field, device_field->field->write_param, device_field->oldval, newval); + // if there's a list of settings or we're an adjuster, copy the current value + if (first_setting() != NULL || m_type == IPT_ADJUSTER) + settings.value = m_live->value; - device_field->oldval = newval; - } - } + // if there's analog data, extract the analog settings + if (m_live->analog != NULL) + { + settings.sensitivity = m_live->analog->sensitivity(); + settings.delta = m_live->analog->delta(); + settings.centerdelta = m_live->analog->centerdelta(); + settings.reverse = m_live->analog->reverse(); + } } -/*------------------------------------------------- - input_port_write - write a value to a - port specified by tag --------------------------------------------------*/ +//------------------------------------------------- +// set_user_settings - modify the current +// settings for the given input field +//------------------------------------------------- -void input_port_write(running_machine &machine, const char *tag, input_port_value value, input_port_value mask) +void ioport_field::set_user_settings(const user_settings &settings) { - const input_port_config *port = machine.root_device().ioport(tag); - if (port == NULL) - fatalerror("Unable to locate input port '%s'", tag); - input_port_write_direct(port, value, mask); -} - + // copy the basics + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + { + const input_seq &defseq = manager().type_seq(m_type, m_player, input_seq_type(seqtype)); + if (defseq == settings.seq[seqtype]) + m_live->seq[seqtype].set_default(); + else + m_live->seq[seqtype] = settings.seq[seqtype]; + } -/*------------------------------------------------- - input_port_write_safe - write a value to - a port, ignore if the port does not exist --------------------------------------------------*/ + // if there's a list of settings or we're an adjuster, copy the current value + if (first_setting() != NULL || m_type == IPT_ADJUSTER) + m_live->value = settings.value; -void input_port_write_safe(running_machine &machine, const char *tag, input_port_value value, input_port_value mask) -{ - const input_port_config *port = machine.root_device().ioport(tag); - if (port != NULL) - input_port_write_direct(port, value, mask); + // if there's analog data, extract the analog settings + if (m_live->analog != NULL) + { + m_live->analog->m_sensitivity = settings.sensitivity; + m_live->analog->m_delta = settings.delta; + m_live->analog->m_centerdelta = settings.centerdelta; + m_live->analog->m_reverse = settings.reverse; + } } +//------------------------------------------------- +// setting_name - return the expanded setting +// name for a field +//------------------------------------------------- -/*************************************************************************** - MISC HELPER FUNCTIONS -***************************************************************************/ - -/*------------------------------------------------- - input_condition_true - return the TRUE - if the given condition attached is true --------------------------------------------------*/ - -int input_condition_true(running_machine &machine, const input_condition *condition, device_t &owner) +const char *ioport_field::setting_name() const { - input_port_value condvalue; - - /* always condition is always true */ - if (condition->condition == PORTCOND_ALWAYS) - return TRUE; + // only makes sense if we have settings + assert(first_setting() != NULL); - /* otherwise, read the referenced port */ - astring conditiontag; - owner.subtag(conditiontag, condition->tag); - condvalue = input_port_read(machine, conditiontag.cstr()); + // scan the list of settings looking for a match on the current value + for (ioport_setting *setting = first_setting(); setting != NULL; setting = setting->next()) + if (setting->enabled()) + if (setting->value() == m_live->value) + return setting->name(); - /* based on the condition encoded, determine truth */ - switch (condition->condition) - { - case PORTCOND_EQUALS: - return ((condvalue & condition->mask) == condition->value); + return "INVALID"; +} - case PORTCOND_NOTEQUALS: - return ((condvalue & condition->mask) != condition->value); - case PORTCOND_GREATERTHAN: - return ((condvalue & condition->mask) > condition->value); +//------------------------------------------------- +// has_previous_setting - return true if the +// given field has a "previous" setting +//------------------------------------------------- - case PORTCOND_NOTGREATERTHAN: - return ((condvalue & condition->mask) <= condition->value); +bool ioport_field::has_previous_setting() const +{ + // only makes sense if we have settings + assert(first_setting() != NULL); - case PORTCOND_LESSTHAN: - return ((condvalue & condition->mask) < condition->value); + // scan the list of settings looking for a match on the current value + for (ioport_setting *setting = first_setting(); setting != NULL; setting = setting->next()) + if (setting->enabled()) + return (setting->value() != m_live->value); - case PORTCOND_NOTLESSTHAN: - return ((condvalue & condition->mask) >= condition->value); - } - return TRUE; + return false; } -/*------------------------------------------------- - input_port_string_from_token - convert an - input_port_token to a default string --------------------------------------------------*/ +//------------------------------------------------- +// select_previous_setting - select the previous +// item for a DIP switch or configuration field +//------------------------------------------------- -const char *input_port_string_from_token(const char *string) +void ioport_field::select_previous_setting() { - /* 0 is an invalid index */ - if (string == NULL) - return NULL; - - /* if the index is greater than the count, assume it to be a pointer */ - if (FPTR(string) >= INPUT_STRING_COUNT) - return string; + // only makes sense if we have settings + assert(first_setting() != NULL); -#if FALSE // Set TRUE, If you want to take care missing-token or wrong-sorting + // scan the list of settings looking for a match on the current value + ioport_setting *prevsetting = NULL; + bool found_match = false; + for (ioport_setting *setting = first_setting(); setting != NULL; setting = setting->next()) + if (setting->enabled()) + { + if (setting->value() == m_live->value) + { + found_match = true; + if (prevsetting != NULL) + break; + } + prevsetting = setting; + } - /* otherwise, scan the list for a matching string and return it */ + // if we didn't find a matching value, select the first + if (!found_match) { - int index; - for (index = 0; index < ARRAY_LENGTH(input_port_default_strings); index++) - if (input_port_default_strings[index].id == FPTR(string)) - return input_port_default_strings[index].string; + for (prevsetting = first_setting(); prevsetting != NULL; prevsetting = prevsetting->next()) + if (prevsetting->enabled()) + break; } - return "(Unknown Default)"; - -#else - - return input_port_default_strings[FPTR(string)-1].string; -#endif + // update the value to the previous one + if (prevsetting != NULL) + m_live->value = prevsetting->value(); } +//------------------------------------------------- +// has_next_setting - return true if the given +// field has a "next" setting +//------------------------------------------------- -/*************************************************************************** - INITIALIZATION HELPERS -***************************************************************************/ - -/*------------------------------------------------- - init_port_types - initialize the default - type list --------------------------------------------------*/ - -static void init_port_types(running_machine &machine) +bool ioport_field::has_next_setting() const { - ioport_manager &portdata = machine.ioport(); + // only makes sense if we have settings + assert(first_setting() != NULL); - /* convert the array into a list of type states that can be modified */ - construct_core_types(portdata.typelist); - - /* ask the OSD to customize the list */ - machine.osd().customize_input_type_list(portdata.typelist); - - /* now iterate over the OSD-modified types */ - for (input_type_entry *curtype = portdata.typelist.first(); curtype != NULL; curtype = curtype->next()) - { - /* first copy all the OSD-updated sequences into our current state */ - for (int seqtype = 0; seqtype < ARRAY_LENGTH(curtype->seq); seqtype++) - curtype->seq[seqtype] = curtype->defseq[seqtype]; + // scan the list of settings looking for a match on the current value + bool found = false; + for (ioport_setting *setting = first_setting(); setting != NULL; setting = setting->next()) + if (setting->enabled()) + { + if (found) + return true; + if (setting->value() == m_live->value) + found = true; + } - /* also make a lookup table mapping type/player to the appropriate type list entry */ - portdata.type_to_entry[curtype->type][curtype->player] = curtype; - } + return false; } -/*------------------------------------------------- - get_keyboard_code - accesses a particular - keyboard code --------------------------------------------------*/ +//------------------------------------------------- +// select_next_setting - select the next item for +// a DIP switch or configuration field +//------------------------------------------------- -static unicode_char get_keyboard_code(const input_field_config *field, int i) +void ioport_field::select_next_setting() { - unicode_char ch = field->chars[i]; + // only makes sense if we have settings + assert(first_setting() != NULL); - /* special hack to allow for PORT_CODE('\xA3') */ - if ((ch >= 0xFFFFFF80) && (ch <= 0xFFFFFFFF)) - ch &= 0xFF; - return ch; -} + // scan the list of settings looking for a match on the current value + ioport_setting *nextsetting = NULL; + ioport_setting *setting; + for (setting = first_setting(); setting != NULL; setting = setting->next()) + if (setting->enabled()) + if (setting->value() == m_live->value) + break; + // if we found one, scan forward for the next valid one + if (setting != NULL) + for (nextsetting = setting->next(); nextsetting != NULL; nextsetting = nextsetting->next()) + if (nextsetting->enabled()) + break; -/*************************************************************************** - MISCELLANEOUS -***************************************************************************/ + // if we hit the end, search from the beginning + if (nextsetting == NULL) + for (nextsetting = first_setting(); nextsetting != NULL; nextsetting = nextsetting->next()) + if (nextsetting->enabled()) + break; + + // update the value to the previous one + if (nextsetting != NULL) + m_live->value = nextsetting->value(); +} -/*------------------------------------------------- - find_charinfo - looks up information about a - particular character --------------------------------------------------*/ -static const char_info *find_charinfo(unicode_char target_char) +//------------------------------------------------- +// frame_update_digital - get the state of a +// digital field +//------------------------------------------------- + +void ioport_field::frame_update(ioport_value &result, bool mouse_down) { - int low = 0; - int high = ARRAY_LENGTH(charinfo); - int i; - unicode_char ch; + // skip if not enabled + if (!enabled()) + return; - /* perform a simple binary search to find the proper alternate */ - while(high > low) + // handle analog inputs first + if (m_live->analog != NULL) { - i = (high + low) / 2; - ch = charinfo[i].ch; - if (ch < target_char) - low = i + 1; - else if (ch > target_char) - high = i; - else - return &charinfo[i]; + m_live->analog->frame_update(machine()); + return; } - return NULL; -} - -/*------------------------------------------------- - inputx_key_name - returns the name of a - specific key --------------------------------------------------*/ + + // if UI is active, ignore digital inputs + if (ui_is_menu_active()) + return; -static const char *inputx_key_name(unicode_char ch) -{ - static char buf[UTF8_CHAR_MAX + 1]; - const char_info *ci; - const char *result; - int pos; + // if the state changed, look for switch down/switch up + bool curstate = mouse_down || machine().input().seq_pressed(seq()); + bool changed = false; + if (curstate != m_live->last) + { + m_live->last = curstate; + changed = true; + } - ci = find_charinfo(ch); - result = ci ? ci->name : NULL; + // if we're a keyboard type and using natural keyboard, bail + if (m_type == IPT_KEYBOARD && ui_get_use_natural_keyboard(machine())) + return; - if (ci && ci->name) + // coin impulse option + int effective_impulse = m_impulse; + int impulse_option_val = machine().options().coin_impulse(); + if (impulse_option_val != 0) { - result = ci->name; + if (impulse_option_val < 0) + effective_impulse = 0; + else if ((m_type >= IPT_COIN1 && m_type <= IPT_COIN12) || m_impulse != 0) + effective_impulse = impulse_option_val; } - else + + // if this is a switch-down event, handle impulse and toggle + if (changed && curstate) { - if ((ch > 0x7F) || isprint(ch)) + // impluse controls: reset the impulse counter + if (effective_impulse != 0 && m_live->impulse == 0) + m_live->impulse = effective_impulse; + + // toggle controls: flip the toggle state or advance to the next setting + if (toggle()) { - pos = utf8_from_uchar(buf, ARRAY_LENGTH(buf), ch); - buf[pos] = '\0'; - result = buf; + if (m_settinglist.count() == 0) + m_live->value ^= m_mask; + else + select_next_setting(); } - else - result = "???"; } - return result; -} -/*------------------------------------------------- - get_keyboard_key_name - builds the name of - a key based on natural keyboard characters --------------------------------------------------*/ - -static astring &get_keyboard_key_name(astring &name, const input_field_config *field) -{ - int i; - unicode_char ch; - - name.reset(); - /* loop through each character on the field*/ - for (i = 0; i < ARRAY_LENGTH(field->chars) && (field->chars[i] != '\0'); i++) + // update the current state with the impulse state + if (effective_impulse != 0) { - ch = get_keyboard_code(field, i); - name.catprintf("%-*s ", MAX(SPACE_COUNT - 1, 0), inputx_key_name(ch)); + curstate = (m_live->impulse != 0); + if (curstate) + m_live->impulse--; } - /* trim extra spaces */ - name.trimspace(); - - /* special case */ - if (name.len() == 0) - name.cpy("Unnamed Key"); - - return name; -} - -/*------------------------------------------------- - init_port_state - initialize the live port - states based on the tokens --------------------------------------------------*/ + // for toggle switches, the current value is folded into the port's default value + // so we always return FALSE here + if (toggle()) + curstate = false; -static void init_port_state(running_machine &machine) -{ - const char *joystick_map_default = machine.options().joystick_map(); - ioport_manager &portdata = machine.ioport(); - input_field_config *field; - input_port_config *port; + // additional logic to restrict digital joysticks + if (curstate && !mouse_down && m_live->joystick != NULL && m_way != 16 && !machine().options().joystick_contradictory()) + { + UINT8 mask = (m_way == 4) ? m_live->joystick->current4way() : m_live->joystick->current(); + if (!(mask & (1 << m_live->joydir))) + curstate = false; + } - /* allocate live structures to mirror the configuration */ - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) + // skip locked-out coin inputs + if (curstate && m_type >= IPT_COIN1 && m_type <= IPT_COIN12 && coin_lockout_get_state(machine(), m_type - IPT_COIN1)) { - analog_field_state **analogstatetail; - device_field_info **readdevicetail; - device_field_info **writedevicetail; - input_port_state *portstate; - - /* allocate a new input_port_info structure */ - portstate = auto_alloc_clear(machine, input_port_state); - ((input_port_config *)port)->state = portstate; - - /* start with tail pointers to all the data */ - analogstatetail = &portstate->analoglist; - readdevicetail = &portstate->readdevicelist; - writedevicetail = &portstate->writedevicelist; - - /* iterate over fields */ - for (field = port->first_field(); field != NULL; field = field->next()) + bool verbose = machine().options().verbose(); +#ifdef MAME_DEBUG + verbose = true; +#endif + if (machine().options().coin_lockout()) { - input_field_state *fieldstate; - int seqtype; - - /* allocate a new input_field_info structure */ - fieldstate = auto_alloc_clear(machine, input_field_state); - ((input_field_config *)field)->state = fieldstate; - - /* fill in the basic values */ - for (seqtype = 0; seqtype < ARRAY_LENGTH(fieldstate->seq); seqtype++) - fieldstate->seq[seqtype] = field->seq[seqtype]; - fieldstate->value = field->defvalue; - - /* if this is an analog field, allocate memory for the analog data */ - if (field->type >= __ipt_analog_start && field->type <= __ipt_analog_end) - { - *analogstatetail = fieldstate->analog = init_field_analog_state(field); - analogstatetail = &(*analogstatetail)->next; - } - - /* if this is a digital joystick field, make a note of it */ - if (field->type >= __ipt_digital_joystick_start && field->type <= __ipt_digital_joystick_end) - { - fieldstate->joystick = &portdata.joystick_info[field->player][(field->type - __ipt_digital_joystick_start) / 4]; - fieldstate->joydir = (field->type - __ipt_digital_joystick_start) % 4; - fieldstate->joystick->field[fieldstate->joydir] = field; - fieldstate->joystick->inuse = TRUE; - } - - /* if this entry has device input, allocate memory for the tracking structure */ - astring devicetag; - if (!field->read.isnull()) - { - *readdevicetail = init_field_device_info(field, port->owner().subtag(devicetag, field->read_device)); - field->read.late_bind(*(*readdevicetail)->device); - if (!field->read.has_object()) - fatalerror("Input port %s, unable to find valid device with tag '%s'", port->tag(), devicetag.cstr()); - readdevicetail = &(*readdevicetail)->next; - } - - /* if this entry has device output, allocate memory for the tracking structure */ - if (!field->write.isnull()) - { - *writedevicetail = init_field_device_info(field, port->owner().subtag(devicetag, field->write_device)); - field->write.late_bind(*(*writedevicetail)->device); - if (!field->write.has_object()) - fatalerror("Input port %s, unable to find valid device with tag '%s'", port->tag(), devicetag.cstr()); - writedevicetail = &(*writedevicetail)->next; - } - - /* if this entry has device output, allocate memory for the tracking structure */ - if (!field->crossmapper.isnull()) - { - device_t *device = machine.device(port->owner().subtag(devicetag, field->crossmapper_device)); - field->crossmapper.late_bind(*device); - } - - /* Name keyboard key names */ - if ((field->type == IPT_KEYBOARD || field->type == IPT_KEYPAD) && (field->name == NULL)) - { - astring name; - field->state->name = auto_strdup(machine, get_keyboard_key_name(name, field)); - } + if (verbose) + ui_popup_time(3, "Coinlock disabled %s.", name()); + curstate = false; } + else + if (verbose) + ui_popup_time(3, "Coinlock disabled, but broken through %s.", name()); } - /* handle autoselection of devices */ - init_autoselect_devices(machine, IPT_AD_STICK_X, IPT_AD_STICK_Y, IPT_AD_STICK_Z, OPTION_ADSTICK_DEVICE, "analog joystick"); - init_autoselect_devices(machine, IPT_PADDLE, IPT_PADDLE_V, 0, OPTION_PADDLE_DEVICE, "paddle"); - init_autoselect_devices(machine, IPT_PEDAL, IPT_PEDAL2, IPT_PEDAL3, OPTION_PEDAL_DEVICE, "pedal"); - init_autoselect_devices(machine, IPT_LIGHTGUN_X, IPT_LIGHTGUN_Y, 0, OPTION_LIGHTGUN_DEVICE, "lightgun"); - init_autoselect_devices(machine, IPT_POSITIONAL, IPT_POSITIONAL_V, 0, OPTION_POSITIONAL_DEVICE, "positional"); - init_autoselect_devices(machine, IPT_DIAL, IPT_DIAL_V, 0, OPTION_DIAL_DEVICE, "dial"); - init_autoselect_devices(machine, IPT_TRACKBALL_X, IPT_TRACKBALL_Y, 0, OPTION_TRACKBALL_DEVICE, "trackball"); - init_autoselect_devices(machine, IPT_MOUSE_X, IPT_MOUSE_Y, 0, OPTION_MOUSE_DEVICE, "mouse"); - - /* look for 4-way joysticks and change the default map if we find any */ - if (joystick_map_default[0] == 0 || strcmp(joystick_map_default, "auto") == 0) - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - for (field = port->first_field(); field != NULL; field = field->next()) - if (field->state->joystick != NULL && field->way == 4) - { - machine.input().set_global_joystick_map((field->flags & FIELD_FLAG_ROTATED) ? joystick_map_4way_diagonal : joystick_map_4way_sticky); - break; - } + // if we're active, set the appropriate bits in the digital state + if (curstate) + result |= m_mask; } -/*------------------------------------------------- - init_autoselect_devices - autoselect a single - device based on the input port list passed - in and the corresponding option --------------------------------------------------*/ +//------------------------------------------------- +// crosshair_position - compute the crosshair +// position +//------------------------------------------------- -static void init_autoselect_devices(running_machine &machine, int type1, int type2, int type3, const char *option, const char *ananame) +void ioport_field::crosshair_position(float &x, float &y, bool &gotx, bool &goty) { - const char *stemp = machine.options().value(option); - input_device_class autoenable = DEVICE_CLASS_KEYBOARD; - const char *autostring = "keyboard"; - const input_field_config *field; - const input_port_config *port; + float value = m_live->analog->crosshair_read(); - /* if nothing specified, ignore the option */ - if (stemp[0] == 0) - return; + // apply the scale and offset + if (m_crosshair_scale < 0) + value = -(1.0 - value) * m_crosshair_scale; + else + value *= m_crosshair_scale; + value += m_crosshair_offset; - /* extract valid strings */ - if (strcmp(stemp, "mouse") == 0) - { - autoenable = DEVICE_CLASS_MOUSE; - autostring = "mouse"; - } - else if (strcmp(stemp, "joystick") == 0) - { - autoenable = DEVICE_CLASS_JOYSTICK; - autostring = "joystick"; - } - else if (strcmp(stemp, "lightgun") == 0) + // apply custom mapping if necessary + if (!m_crosshair_mapper.isnull()) + value = m_crosshair_mapper(*this, value); + + // handle X axis + if (m_crosshair_axis == CROSSHAIR_AXIS_X) { - autoenable = DEVICE_CLASS_LIGHTGUN; - autostring = "lightgun"; + x = value; + gotx = true; + if (m_crosshair_altaxis != 0) + { + y = m_crosshair_altaxis; + goty = true; + } } - else if (strcmp(stemp, "none") == 0) + + // handle Y axis + else { - /* nothing specified */ - return; + y = value; + goty = true; + if (m_crosshair_altaxis != 0) + { + x = m_crosshair_altaxis; + gotx = true; + } } - else if (strcmp(stemp, "keyboard") != 0) - mame_printf_error("Invalid %s value %s; reverting to keyboard\n", option, stemp); - - /* only scan the list if we haven't already enabled this class of control */ - if (machine.ioport().first_port() != NULL && !machine.input().device_class(autoenable).enabled()) - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - for (field = port->first_field(); field != NULL; field = field->next()) - - /* if this port type is in use, apply the autoselect criteria */ - if ((type1 != 0 && field->type == type1) || - (type2 != 0 && field->type == type2) || - (type3 != 0 && field->type == type3)) - { - mame_printf_verbose("Input: Autoenabling %s due to presence of a %s\n", autostring, ananame); - machine.input().device_class(autoenable).enable(); - break; - } } -/*------------------------------------------------- - init_field_device_info - allocate and populate - information about a device callback --------------------------------------------------*/ +//------------------------------------------------- +// expand_diplocation - expand a string-based +// DIP location into a linked list of +// descriptions +//------------------------------------------------- -static device_field_info *init_field_device_info(const input_field_config *field, const char *device_name) +void ioport_field::expand_diplocation(const char *location, astring &errorbuf) { - device_field_info *info; - input_port_value mask; - - /* allocate memory */ - info = auto_alloc_clear(field->machine(), device_field_info); - - /* fill in the data */ - info->field = field; - for (mask = field->mask; !(mask & 1); mask >>= 1) - info->shift++; + // if nothing present, bail + if (location == NULL) + return; - info->device = (device_name != NULL) ? field->machine().device(device_name) : &field->port().owner(); + m_diploclist.reset(); - info->oldval = field->defvalue >> info->shift; - return info; -} + // parse the string + astring name; // Don't move this variable inside the loop, lastname's lifetime depends on it being outside + const char *lastname = NULL; + const char *curentry = location; + int entries = 0; + while (*curentry != 0) + { + // find the end of this entry + const char *comma = strchr(curentry, ','); + if (comma == NULL) + comma = curentry + strlen(curentry); + // extract it to tempbuf + astring tempstr; + tempstr.cpy(curentry, comma - curentry); -/*------------------------------------------------- - init_field_analog_state - allocate and populate - information about an analog port --------------------------------------------------*/ + // first extract the switch name if present + const char *number = tempstr; + const char *colon = strchr(tempstr, ':'); -static analog_field_state *init_field_analog_state(const input_field_config *field) -{ - analog_field_state *state; - input_port_value mask; + // allocate and copy the name if it is present + if (colon != NULL) + { + lastname = name.cpy(number, colon - number); + number = colon + 1; + } - /* allocate memory */ - state = auto_alloc_clear(field->machine(), analog_field_state); + // otherwise, just copy the last name + else + { + if (lastname == NULL) + { + errorbuf.catprintf("Switch location '%s' missing switch name!\n", location); + lastname = (char *)"UNK"; + } + name.cpy(lastname); + } - /* compute the shift amount and number of bits */ - for (mask = field->mask; !(mask & 1); mask >>= 1) - state->shift++; + // if the number is preceded by a '!' it's active high + bool invert = false; + if (*number == '!') + { + invert = true; + number++; + } - /* initialize core data */ - state->field = field; - state->adjdefvalue = (field->defvalue & field->mask) >> state->shift; - state->adjmin = (field->min & field->mask) >> state->shift; - state->adjmax = (field->max & field->mask) >> state->shift; - state->sensitivity = field->sensitivity; - state->reverse = ((field->flags & ANALOG_FLAG_REVERSE) != 0); - state->delta = field->delta; - state->centerdelta = field->centerdelta; - state->minimum = INPUT_ABSOLUTE_MIN; - state->maximum = INPUT_ABSOLUTE_MAX; + // now scan the switch number + int swnum = -1; + if (sscanf(number, "%d", &swnum) != 1) + errorbuf.catprintf("Switch location '%s' has invalid format!\n", location); - /* set basic parameters based on the configured type */ - switch (field->type) - { - /* paddles and analog joysticks are absolute and autocenter */ - case IPT_AD_STICK_X: - case IPT_AD_STICK_Y: - case IPT_AD_STICK_Z: - case IPT_PADDLE: - case IPT_PADDLE_V: - state->absolute = TRUE; - state->autocenter = TRUE; - state->interpolate = TRUE; - break; + // allocate a new entry + m_diploclist.append(*global_alloc(ioport_diplocation(name, swnum, invert))); + entries++; - /* pedals start at and autocenter to the min range */ - case IPT_PEDAL: - case IPT_PEDAL2: - case IPT_PEDAL3: - state->center = INPUT_ABSOLUTE_MIN; - state->accum = APPLY_INVERSE_SENSITIVITY(state->center, state->sensitivity); - state->absolute = TRUE; - state->autocenter = TRUE; - state->interpolate = TRUE; - break; + // advance to the next item + curentry = comma; + if (*curentry != 0) + curentry++; + } - /* lightguns are absolute as well, but don't autocenter and don't interpolate their values */ - case IPT_LIGHTGUN_X: - case IPT_LIGHTGUN_Y: - state->absolute = TRUE; - state->autocenter = FALSE; - state->interpolate = FALSE; - break; + // then verify the number of bits in the mask matches + ioport_value temp; + int bits; + for (bits = 0, temp = m_mask; temp != 0 && bits < 32; bits++) + temp &= temp - 1; + if (bits != entries) + errorbuf.catprintf("Switch location '%s' does not describe enough bits for mask %X\n", location, m_mask); +} - /* positional devices are absolute, but can also wrap like relative devices */ - /* set each position to be 512 units */ - case IPT_POSITIONAL: - case IPT_POSITIONAL_V: - state->positionalscale = COMPUTE_SCALE(field->max, INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN); - state->adjmin = 0; - state->adjmax = field->max - 1; - state->wraps = ((field->flags & ANALOG_FLAG_WRAPS) != 0); - state->autocenter = !state->wraps; - break; - /* dials, mice and trackballs are relative devices */ - /* these have fixed "min" and "max" values based on how many bits are in the port */ - /* in addition, we set the wrap around min/max values to 512 * the min/max values */ - /* this takes into account the mapping that one mouse unit ~= 512 analog units */ - case IPT_DIAL: - case IPT_DIAL_V: - case IPT_TRACKBALL_X: - case IPT_TRACKBALL_Y: - case IPT_MOUSE_X: - case IPT_MOUSE_Y: - state->absolute = FALSE; - state->wraps = TRUE; - state->interpolate = TRUE; - break; +//------------------------------------------------- +// init_live_state - create live state structures +//------------------------------------------------- - default: - fatalerror("Unknown analog port type -- don't know if it is absolute or not"); - break; - } +void ioport_field::init_live_state(analog_field *analog) +{ + // resolve callbacks + m_read.bind_relative_to(device()); + m_write.bind_relative_to(device()); + m_crosshair_mapper.bind_relative_to(device()); - /* further processing for absolute controls */ - if (state->absolute) - { - /* if the default value is pegged at the min or max, use a single scale value for the whole axis */ - state->single_scale = (state->adjdefvalue == state->adjmin) || (state->adjdefvalue == state->adjmax); + // allocate live state + m_live = global_alloc(ioport_field_live(*this, analog)); +} - /* if not "single scale", compute separate scales for each side of the default */ - if (!state->single_scale) - { - /* unsigned */ - state->scalepos = COMPUTE_SCALE(state->adjmax - state->adjdefvalue, INPUT_ABSOLUTE_MAX - 0); - state->scaleneg = COMPUTE_SCALE(state->adjdefvalue - state->adjmin, 0 - INPUT_ABSOLUTE_MIN); - if (state->adjmin > state->adjmax) - state->scaleneg = -state->scaleneg; - /* reverse point is at center */ - state->reverse_val = 0; - } - else - { - /* single axis that increases from default */ - state->scalepos = COMPUTE_SCALE(state->adjmax - state->adjmin, INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN); +//************************************************************************** +// I/O PORT FIELD LIVE +//************************************************************************** - /* move from default */ - if (state->adjdefvalue == state->adjmax) - state->scalepos = -state->scalepos; +//------------------------------------------------- +// ioport_field_live - constructor +//------------------------------------------------- - /* make the scaling the same for easier coding when we need to scale */ - state->scaleneg = state->scalepos; +ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog) + : analog(analog), + joystick(NULL), + value(field.defvalue()), + impulse(0), + last(0), + joydir(digital_joystick::JOYDIR_COUNT) +{ + // fill in the basic values + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + seq[seqtype] = field.seq(seqtype); - /* reverse point is at max */ - state->reverse_val = state->maximum; - } + // if this is a digital joystick field, make a note of it + if (field.is_digital_joystick()) + { + joystick = &field.manager().digjoystick(field.player(), (field.type() - (IPT_DIGITAL_JOYSTICK_FIRST + 1)) / 4); + joydir = joystick->set_axis(field); } - /* relative and positional controls all map directly with a 512x scale factor */ - else + // Name keyboard key names + if (field.type_class() == INPUT_CLASS_KEYBOARD && field.specific_name() == NULL) { - /* The relative code is set up to allow specifing PORT_MINMAX and default values. */ - /* The validity checks are purposely set up to not allow you to use anything other */ - /* a default of 0 and PORT_MINMAX(0,mask). This is in case the need arises to use */ - /* this feature in the future. Keeping the code in does not hurt anything. */ - if (state->adjmin > state->adjmax) - /* adjust for signed */ - state->adjmin = -state->adjmin; - - if (state->wraps) - state->adjmax++; - - state->minimum = (state->adjmin - state->adjdefvalue) * INPUT_RELATIVE_PER_PIXEL; - state->maximum = (state->adjmax - state->adjdefvalue) * INPUT_RELATIVE_PER_PIXEL; - - /* make the scaling the same for easier coding when we need to scale */ - state->scaleneg = state->scalepos = COMPUTE_SCALE(1, INPUT_RELATIVE_PER_PIXEL); - - if (field->flags & ANALOG_FLAG_RESET) - /* delta values reverse from center */ - state->reverse_val = 0; - else + // loop through each character on the field + astring tempstr; + for (int which = 0; ; which++) { - /* positional controls reverse from their max range */ - state->reverse_val = state->maximum + state->minimum; - - /* relative controls reverse from 1 past their max range */ - if (state->wraps) - state->reverse_val -= INPUT_RELATIVE_PER_PIXEL; + unicode_char ch = field.keyboard_code(which); + if (ch == 0) + break; + name.catprintf("%-*s ", MAX(SPACE_COUNT - 1, 0), field.manager().natkeyboard().key_name(tempstr, ch)); } - } - /* compute scale for keypresses */ - state->keyscalepos = RECIP_SCALE(state->scalepos); - state->keyscaleneg = RECIP_SCALE(state->scaleneg); + // trim extra spaces + name.trimspace(); - return state; + // special case + if (name.len() == 0) + name.cpy("Unnamed Key"); + } } -/*************************************************************************** - ONCE-PER-FRAME UPDATES -***************************************************************************/ +//************************************************************************** +// I/O PORT +//************************************************************************** -/*------------------------------------------------- - frame_update_callback - system-wide callback to - update the input ports once per frame, but - only if we are not paused --------------------------------------------------*/ +//------------------------------------------------- +// ioport_port - constructor +//------------------------------------------------- -static void frame_update_callback(running_machine &machine) +ioport_port::ioport_port(device_t &owner, const char *tag) + : m_next(NULL), + m_device(owner), + m_tag(tag), + m_modcount(0), + m_active(0), + m_live(NULL) { - /* if we're paused, don't do anything */ - if (machine.paused()) - return; - - /* otherwise, use the common code */ - frame_update(machine); } -static key_buffer *get_buffer(running_machine &machine) + +//------------------------------------------------- +// ~ioport_port - destructor +//------------------------------------------------- + +ioport_port::~ioport_port() { - ioport_manager &portdata = machine.ioport(); - assert(inputx_can_post(machine)); - return (key_buffer *)&portdata.keybuffer; + global_free(m_live); } +//------------------------------------------------- +// machine - return a reference to the running +// machine +//------------------------------------------------- -static const inputx_code *find_code(inputx_code *codes, unicode_char ch) +running_machine &ioport_port::machine() const { - int i; - - assert(codes); - for (i = 0; codes[i].ch; i++) - { - if (codes[i].ch == ch) - return &codes[i]; - } - return NULL; + return m_device.machine(); } -/*------------------------------------------------- - input_port_update_hook - hook function - called from core to allow for natural keyboard --------------------------------------------------*/ -static void input_port_update_hook(running_machine &machine, const input_port_config *port, input_port_value *digital) +//------------------------------------------------- +// manager - return a reference to the +// ioport_manager on the running machine +//------------------------------------------------- + +ioport_manager &ioport_port::manager() const { - ioport_manager &portdata = machine.ioport(); - const key_buffer *keybuf; - const inputx_code *code; - unicode_char ch; - int i; - UINT32 value; + return machine().ioport(); +} - if (inputx_can_post(machine)) - { - keybuf = get_buffer(machine); - /* is the key down right now? */ - if (keybuf && keybuf->status_keydown && (keybuf->begin_pos != keybuf->end_pos)) - { - /* identify the character that is down right now, and its component codes */ - ch = keybuf->buffer[keybuf->begin_pos]; - code = find_code(portdata.codes, ch); +//------------------------------------------------- +// field - return a pointer to the first field +// that intersects the given mask +//------------------------------------------------- - /* loop through this character's component codes */ - if (code != NULL) - { - for (i = 0; i < ARRAY_LENGTH(code->field) && (code->field[i] != NULL); i++) - { - if (&code->field[i]->port() == port) - { - value = code->field[i]->mask; - *digital |= value; - } - } - } - } - } +ioport_field *ioport_port::field(ioport_value mask) +{ + // if we got the port, look for the field + for (ioport_field *field = first_field(); field != NULL; field = field->next()) + if ((field->mask() & mask) != 0) + return field; + return NULL; } -/*------------------------------------------------- - frame_update - core logic for per-frame input - port updating --------------------------------------------------*/ +//------------------------------------------------- +// read - return the value of an I/O port +//------------------------------------------------- -static void frame_update(running_machine &machine) +ioport_value ioport_port::read() { - ioport_manager &portdata = machine.ioport(); - const input_field_config *mouse_field = NULL; - int ui_visible = ui_is_menu_active(); - attotime curtime = machine.time(); - const input_port_config *port; - render_target *mouse_target; - INT32 mouse_target_x; - INT32 mouse_target_y; - int mouse_button; + assert_always(manager().safe_to_read(), "Input ports cannot be read at init time!"); -g_profiler.start(PROFILER_INPUT); + // start with the digital state + ioport_value result = m_live->digital; - /* record/playback information about the current frame */ - playback_frame(machine, curtime); - record_frame(machine, curtime); + // insert dynamic read values + for (dynamic_field *dynfield = m_live->readlist.first(); dynfield != NULL; dynfield = dynfield->next()) + dynfield->read(result); - /* track the duration of the previous frame */ - portdata.last_delta_nsec = (curtime - portdata.last_frame_time).as_attoseconds() / ATTOSECONDS_PER_NANOSECOND; - portdata.last_frame_time = curtime; + // apply active high/low state to digital and dynamic read inputs + result ^= m_live->defvalue; - /* update the digital joysticks */ - frame_update_digital_joysticks(machine); + // insert analog portions + for (analog_field *analog = m_live->analoglist.first(); analog != NULL; analog = analog->next()) + analog->read(result); - /* compute default values for all the ports */ - input_port_update_defaults(machine); + return result; +} - /* perform the mouse hit test */ - mouse_target = ui_input_find_mouse(machine, &mouse_target_x, &mouse_target_y, &mouse_button); - if (mouse_button && mouse_target) - { - const char *tag = NULL; - input_port_value mask; - float x, y; - if (mouse_target->map_point_input(mouse_target_x, mouse_target_y, tag, mask, x, y)) - mouse_field = input_field_by_tag_and_mask(machine, tag, mask); - } - /* loop over all input ports */ - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - { - const input_field_config *field; - device_field_info *device_field; - input_port_value newvalue; +//------------------------------------------------- +// write - write a value to a port +//------------------------------------------------- - /* start with 0 values for the digital and VBLANK bits */ - port->state->digital = 0; - port->state->vblank = 0; +void ioport_port::write(ioport_value data, ioport_value mem_mask) +{ + // call device line write handlers + COMBINE_DATA(&m_live->outputvalue); + for (dynamic_field *dynfield = m_live->writelist.first(); dynfield != NULL; dynfield = dynfield->next()) + if (dynfield->field().type() == IPT_OUTPUT) + dynfield->write(m_live->outputvalue); +} - /* now loop back and modify based on the inputs */ - for (field = port->first_field(); field != NULL; field = field->next()) - if (input_condition_true(port->machine(), &field->condition, port->owner())) - { - /* accumulate VBLANK bits */ - if (field->type == IPT_VBLANK) - port->state->vblank ^= field->mask; - /* handle analog inputs */ - else if (field->state->analog != NULL) - frame_update_analog_field(machine, field->state->analog); +//------------------------------------------------- +// frame_update - once/frame update +//------------------------------------------------- - /* handle non-analog types, but only when the UI isn't visible */ - else if (!ui_visible && frame_get_digital_field_state(field, field == mouse_field)) - port->state->digital |= field->mask; - } +void ioport_port::frame_update(ioport_field *mouse_field) +{ + // start with 0 values for the digital bits + m_live->digital = 0; - /* hook for MESS's natural keyboard support */ - input_port_update_hook(machine, port, &port->state->digital); + // now loop back and modify based on the inputs + for (ioport_field *field = first_field(); field != NULL; field = field->next()) + field->frame_update(m_live->digital, field == mouse_field); - /* handle playback/record */ - playback_port(port); - record_port(port); + // hook for MESS's natural keyboard support + manager().natkeyboard().frame_update(*this, m_live->digital); - /* call device line write handlers */ - newvalue = input_port_read_direct(port); - for (device_field = port->state->writedevicelist; device_field; device_field = device_field->next) - if (device_field->field->type != IPT_OUTPUT && input_condition_true(port->machine(), &device_field->field->condition, port->owner())) - { - input_port_value newval = (newvalue & device_field->field->mask) >> device_field->shift; + // call device line write handlers + ioport_value newvalue = read(); + for (dynamic_field *dynfield = m_live->writelist.first(); dynfield != NULL; dynfield = dynfield->next()) + if (dynfield->field().type() != IPT_OUTPUT) + dynfield->write(newvalue); +} - /* if the bits have write, call the handler */ - if (device_field->oldval != newval) - { - device_field->field->write(*device_field->field, device_field->field->write_param, device_field->oldval, newval); - device_field->oldval = newval; - } - } - } +//------------------------------------------------- +// collapse_fields - remove any fields that are +// wholly overlapped by other fields +//------------------------------------------------- -g_profiler.stop(); +void ioport_port::collapse_fields(astring &errorbuf) +{ + ioport_value maskbits = 0; + int lastmodcount = -1; + + // remove the whole list and start from scratch + ioport_field *field = m_fieldlist.detach_all(); + while (field != NULL) + { + // if this modcount doesn't match, reset + if (field->modcount() != lastmodcount) + { + lastmodcount = field->modcount(); + maskbits = 0; + } + + // reinsert this field + ioport_field *current = field; + field = field->next(); + insert_field(*current, maskbits, errorbuf); + } } -/*------------------------------------------------- - frame_update_digital_joysticks - update the - state of digital joysticks prior to - accumulating the results in a port --------------------------------------------------*/ +//------------------------------------------------- +// insert_field - insert a new field, checking +// for errors +//------------------------------------------------- -static void frame_update_digital_joysticks(running_machine &machine) +void ioport_port::insert_field(ioport_field &newfield, ioport_value &disallowedbits, astring &errorbuf) { - ioport_manager &portdata = machine.ioport(); - int player, joyindex; + // verify against the disallowed bits, but only if we are condition-free + if (newfield.condition().none()) + { + if ((newfield.mask() & disallowedbits) != 0) + errorbuf.catprintf("INPUT_TOKEN_FIELD specifies duplicate port bits (port=%s mask=%X)\n", tag(), newfield.mask()); + disallowedbits |= newfield.mask(); + } - /* loop over all the joysticks */ - for (player = 0; player < MAX_PLAYERS; player++) - for (joyindex = 0; joyindex < DIGITAL_JOYSTICKS_PER_PLAYER; joyindex++) + // first modify/nuke any entries that intersect our maskbits + ioport_field *nextfield; + for (ioport_field *field = first_field(); field != NULL; field = nextfield) + { + nextfield = field->next(); + if ((field->mask() & newfield.mask()) != 0 && + (newfield.condition().none() || field->condition().none() || field->condition() == newfield.condition())) { - digital_joystick_state *joystick = &portdata.joystick_info[player][joyindex]; - if (joystick->inuse) - { - joystick->previous = joystick->current; - joystick->current = 0; - - /* read all the associated ports */ - if (joystick->field[JOYDIR_UP] != NULL && machine.input().seq_pressed(input_field_seq(joystick->field[JOYDIR_UP], SEQ_TYPE_STANDARD))) - joystick->current |= JOYDIR_UP_BIT; - if (joystick->field[JOYDIR_DOWN] != NULL && machine.input().seq_pressed(input_field_seq(joystick->field[JOYDIR_DOWN], SEQ_TYPE_STANDARD))) - joystick->current |= JOYDIR_DOWN_BIT; - if (joystick->field[JOYDIR_LEFT] != NULL && machine.input().seq_pressed(input_field_seq(joystick->field[JOYDIR_LEFT], SEQ_TYPE_STANDARD))) - joystick->current |= JOYDIR_LEFT_BIT; - if (joystick->field[JOYDIR_RIGHT] != NULL && machine.input().seq_pressed(input_field_seq(joystick->field[JOYDIR_RIGHT], SEQ_TYPE_STANDARD))) - joystick->current |= JOYDIR_RIGHT_BIT; - - /* lock out opposing directions (left + right or up + down) */ - if ((joystick->current & (JOYDIR_UP_BIT | JOYDIR_DOWN_BIT)) == (JOYDIR_UP_BIT | JOYDIR_DOWN_BIT)) - joystick->current &= ~(JOYDIR_UP_BIT | JOYDIR_DOWN_BIT); - if ((joystick->current & (JOYDIR_LEFT_BIT | JOYDIR_RIGHT_BIT)) == (JOYDIR_LEFT_BIT | JOYDIR_RIGHT_BIT)) - joystick->current &= ~(JOYDIR_LEFT_BIT | JOYDIR_RIGHT_BIT); - - /* only update 4-way case if joystick has moved */ - if (joystick->current != joystick->previous) - { - joystick->current4way = joystick->current; + // reduce the mask of the field we found + field->reduce_mask(newfield.mask()); - /* - If joystick is pointing at a diagonal, acknowledge that the player moved - the joystick by favoring a direction change. This minimizes frustration - when using a keyboard for input, and maximizes responsiveness. + // if the new entry fully overrides the previous one, we nuke + if (INPUT_PORT_OVERRIDE_FULLY_NUKES_PREVIOUS || field->mask() == 0) + m_fieldlist.remove(*field); + } + } - For example, if you are holding "left" then switch to "up" (where both left - and up are briefly pressed at the same time), we'll transition immediately - to "up." + // make a mask of just the low bit + ioport_value lowbit = (newfield.mask() ^ (newfield.mask() - 1)) & newfield.mask(); - Zero any switches that didn't change from the previous to current state. - */ - if ((joystick->current4way & (JOYDIR_UP_BIT | JOYDIR_DOWN_BIT)) && - (joystick->current4way & (JOYDIR_LEFT_BIT | JOYDIR_RIGHT_BIT))) - { - joystick->current4way ^= joystick->current4way & joystick->previous; - } + // scan forward to find where to insert ourselves + ioport_field *field; + for (field = first_field(); field != NULL; field = field->next()) + if (field->mask() > lowbit) + break; - /* - If we are still pointing at a diagonal, we are in an indeterminant state. + // insert it into the list + m_fieldlist.insert_before(newfield, field); +} - This could happen if the player moved the joystick from the idle position directly - to a diagonal, or from one diagonal directly to an extreme diagonal. - The chances of this happening with a keyboard are slim, but we still need to - constrain this case. +//------------------------------------------------- +// init_live_state - create the live state +//------------------------------------------------- - For now, just resolve randomly. - */ - if ((joystick->current4way & (JOYDIR_UP_BIT | JOYDIR_DOWN_BIT)) && - (joystick->current4way & (JOYDIR_LEFT_BIT | JOYDIR_RIGHT_BIT))) - { - if (machine.rand() & 1) - joystick->current4way &= ~(JOYDIR_LEFT_BIT | JOYDIR_RIGHT_BIT); - else - joystick->current4way &= ~(JOYDIR_UP_BIT | JOYDIR_DOWN_BIT); - } - } - } - } +void ioport_port::init_live_state() +{ + m_live = global_alloc(ioport_port_live(*this)); } -/*------------------------------------------------- - frame_update_analog_field - update the - internals of a single analog field --------------------------------------------------*/ - -static void frame_update_analog_field(running_machine &machine, analog_field_state *analog) -{ - input_item_class itemclass; - int keypressed = FALSE; - INT64 keyscale; - INT32 rawvalue; - INT32 delta = 0; - /* clamp the previous value to the min/max range and remember it */ - analog->previous = analog->accum = apply_analog_min_max(analog, analog->accum); +//************************************************************************** +// I/O PORT LIVE STATE +//************************************************************************** - /* get the new raw analog value and its type */ - rawvalue = machine.input().seq_axis_value(input_field_seq(analog->field, SEQ_TYPE_STANDARD), itemclass); +//------------------------------------------------- +// ioport_port_live - constructor +//------------------------------------------------- - /* if we got an absolute input, it overrides everything else */ - if (itemclass == ITEM_CLASS_ABSOLUTE) +ioport_port_live::ioport_port_live(ioport_port &port) + : defvalue(0), + digital(0), + outputvalue(0) +{ + // iterate over fields + for (ioport_field *field = port.first_field(); field != NULL; field = field->next()) { - if (analog->previousanalog != rawvalue) - { - /* only update if analog value changed */ - analog->previousanalog = rawvalue; + // allocate analog state if it's analog + analog_field *analog = NULL; + if (field->is_analog()) + analog = &analoglist.append(*global_alloc(analog_field(*field))); + + // allocate a dynamic field for reading + if (field->has_dynamic_read()) + readlist.append(*global_alloc(dynamic_field(*field))); + + // allocate a dynamic field for writing + if (field->has_dynamic_write()) + writelist.append(*global_alloc(dynamic_field(*field))); + + // let the field initialize its live state + field->init_live_state(analog); + } +} - /* apply the inverse of the sensitivity to the raw value so that */ - /* it will still cover the full min->max range requested after */ - /* we apply the sensitivity adjustment */ - if (analog->absolute || (analog->field->flags & ANALOG_FLAG_RESET)) - { - /* if port is absolute, then just return the absolute data supplied */ - analog->accum = APPLY_INVERSE_SENSITIVITY(rawvalue, analog->sensitivity); - } - else if (analog->positionalscale != 0) - { - /* 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, */ - /* it moves through all the positions */ - rawvalue = APPLY_SCALE(rawvalue - INPUT_ABSOLUTE_MIN, analog->positionalscale) * INPUT_RELATIVE_PER_PIXEL + analog->minimum; - - /* clamp the high value so it does not roll over */ - rawvalue = MIN(rawvalue, analog->maximum); - analog->accum = APPLY_INVERSE_SENSITIVITY(rawvalue, analog->sensitivity); - } - else - /* if port is relative, we use the value to simulate the speed of relative movement */ - /* sensitivity adjustment is allowed for this mode */ - analog->accum += rawvalue; - analog->lastdigital = FALSE; - /* do not bother with other control types if the analog data is changing */ - return; - } - else - { - /* we still have to update fake relative from joystick control */ - if (!analog->absolute && analog->positionalscale == 0) - analog->accum += rawvalue; - } - } - /* 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 */ - if (itemclass == ITEM_CLASS_RELATIVE && rawvalue != 0) - { - delta = rawvalue; - analog->lastdigital = FALSE; - } +//************************************************************************** +// I/O PORT MANAGER +//************************************************************************** - keyscale = (analog->accum >= 0) ? analog->keyscalepos : analog->keyscaleneg; +//------------------------------------------------- +// ioport_manager - constructor +//------------------------------------------------- - /* if the decrement code sequence is pressed, add the key delta to */ - /* the accumulated delta; also note that the last input was a digital one */ - if (machine.input().seq_pressed(input_field_seq(analog->field, SEQ_TYPE_DECREMENT))) - { - keypressed = TRUE; - if (analog->delta != 0) - delta -= APPLY_SCALE(analog->delta, keyscale); - else if (!analog->lastdigital) - /* decrement only once when first pressed */ - delta -= APPLY_SCALE(1, keyscale); - analog->lastdigital = TRUE; - } +ioport_manager::ioport_manager(running_machine &machine) + : m_machine(machine), + m_safe_to_read(false), + m_natkeyboard(machine), + m_last_frame_time(attotime::zero), + m_last_delta_nsec(0), + m_record_file(machine.options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS), + m_playback_file(machine.options().input_directory(), OPEN_FLAG_READ), + m_playback_accumulated_speed(0), + m_playback_accumulated_frames(0) +{ + memset(m_type_to_entry, 0, sizeof(m_type_to_entry)); +} - /* same for the increment code sequence */ - if (machine.input().seq_pressed(input_field_seq(analog->field, SEQ_TYPE_INCREMENT))) - { - keypressed = TRUE; - if (analog->delta) - delta += APPLY_SCALE(analog->delta, keyscale); - else if (!analog->lastdigital) - /* increment only once when first pressed */ - delta += APPLY_SCALE(1, keyscale); - analog->lastdigital = TRUE; - } - /* if resetting is requested, clear the accumulated position to 0 before */ - /* applying the deltas so that we only return this frame's delta */ - /* note that centering only works for relative controls */ - /* no need to check if absolute here because it is checked by the validity tests */ - if (analog->field->flags & ANALOG_FLAG_RESET) - analog->accum = 0; +//------------------------------------------------- +// initialize - walk the configured ports and +// create live state information +//------------------------------------------------- + +time_t ioport_manager::initialize() +{ + // add an exit callback and a frame callback + machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(ioport_manager::exit), this)); + machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(FUNC(ioport_manager::frame_update), this)); - /* apply the delta to the accumulated value */ - analog->accum += delta; + // initialize the default port info from the OSD + init_port_types(); - /* if our last movement was due to a digital input, and if this control */ - /* type autocenters, and if neither the increment nor the decrement seq */ - /* was pressed, apply autocentering */ - if (analog->autocenter) + // if we have a token list, proceed + device_iterator iter(machine().root_device()); + for (device_t *device = iter.first(); device != NULL; device = iter.next()) { - INT32 center = APPLY_INVERSE_SENSITIVITY(analog->center, analog->sensitivity); - if (analog->lastdigital && !keypressed) - { - /* autocenter from positive values */ - if (analog->accum >= center) - { - analog->accum -= APPLY_SCALE(analog->centerdelta, analog->keyscalepos); - if (analog->accum < center) - { - analog->accum = center; - analog->lastdigital = FALSE; - } - } + astring errors; + m_portlist.append(*device, errors); + if (errors) + mame_printf_error("Input port errors:\n%s", errors.cstr()); + } - /* autocenter from negative values */ - else - { - analog->accum += APPLY_SCALE(analog->centerdelta, analog->keyscaleneg); - if (analog->accum > center) + // allocate live structures to mirror the configuration + for (ioport_port *port = first_port(); port != NULL; port = port->next()) + port->init_live_state(); + + // handle autoselection of devices + init_autoselect_devices(IPT_AD_STICK_X, IPT_AD_STICK_Y, IPT_AD_STICK_Z, OPTION_ADSTICK_DEVICE, "analog joystick"); + init_autoselect_devices(IPT_PADDLE, IPT_PADDLE_V, 0, OPTION_PADDLE_DEVICE, "paddle"); + init_autoselect_devices(IPT_PEDAL, IPT_PEDAL2, IPT_PEDAL3, OPTION_PEDAL_DEVICE, "pedal"); + init_autoselect_devices(IPT_LIGHTGUN_X, IPT_LIGHTGUN_Y, 0, OPTION_LIGHTGUN_DEVICE, "lightgun"); + init_autoselect_devices(IPT_POSITIONAL, IPT_POSITIONAL_V, 0, OPTION_POSITIONAL_DEVICE, "positional"); + init_autoselect_devices(IPT_DIAL, IPT_DIAL_V, 0, OPTION_DIAL_DEVICE, "dial"); + init_autoselect_devices(IPT_TRACKBALL_X, IPT_TRACKBALL_Y, 0, OPTION_TRACKBALL_DEVICE, "trackball"); + init_autoselect_devices(IPT_MOUSE_X, IPT_MOUSE_Y, 0, OPTION_MOUSE_DEVICE, "mouse"); + + // look for 4-way joysticks and change the default map if we find any + const char *joystick_map_default = machine().options().joystick_map(); + if (joystick_map_default[0] == 0 || strcmp(joystick_map_default, "auto") == 0) + for (ioport_port *port = first_port(); port != NULL; port = port->next()) + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) + if (field->live().joystick != NULL && field->way() == 4) { - analog->accum = center; - analog->lastdigital = FALSE; + machine().input().set_global_joystick_map(field->rotated() ? joystick_map_4way_diagonal : joystick_map_4way_sticky); + break; } - } - } - } - else if (!keypressed) - analog->lastdigital = FALSE; + + // register callbacks for when we load configurations + config_register(machine(), "input", config_saveload_delegate(FUNC(ioport_manager::load_config), this), config_saveload_delegate(FUNC(ioport_manager::save_config), this)); + + // open playback and record files if specified + time_t basetime = playback_init(); + record_init(); + return basetime; } -/*------------------------------------------------- - frame_get_digital_field_state - get the state - of a digital field --------------------------------------------------*/ +//------------------------------------------------- +// init_port_types - initialize the default +// type list +//------------------------------------------------- -static int frame_get_digital_field_state(const input_field_config *field, int mouse_down) +void ioport_manager::init_port_types() { - int curstate = mouse_down || field->machine().input().seq_pressed(input_field_seq(field, SEQ_TYPE_STANDARD)); - int changed = FALSE; - int temp_field_impulse; + // convert the array into a list of type states that can be modified + construct_core_types(m_typelist); - /* if the state changed, look for switch down/switch up */ - if (curstate != field->state->last) + // ask the OSD to customize the list + machine().osd().customize_input_type_list(m_typelist); + + // now iterate over the OSD-modified types + for (input_type_entry *curtype = first_type(); curtype != NULL; curtype = curtype->next()) { - field->state->last = curstate; - changed = TRUE; + // first copy all the OSD-updated sequences into our current state + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + curtype->m_seq[seqtype] = curtype->defseq(seqtype); + + // also make a lookup table mapping type/player to the appropriate type list entry + m_type_to_entry[curtype->type()][curtype->player()] = curtype; } +} - if (field->type == IPT_KEYBOARD && ui_get_use_natural_keyboard(field->machine())) - return FALSE; - /* coin impulse option */ - { - int temp_option_impulse = field->machine().options().coin_impulse(); - temp_field_impulse = field->impulse; - if ( temp_option_impulse != 0) - { - if (temp_option_impulse < 0) - temp_field_impulse = 0; - else if ((field->type >= IPT_COIN1 && field->type <= IPT_COIN12) || field->impulse != 0) - temp_field_impulse = temp_option_impulse; - } - } +//------------------------------------------------- +// init_autoselect_devices - autoselect a single +// device based on the input port list passed +// in and the corresponding option +//------------------------------------------------- - /* if this is a switch-down event, handle impulse and toggle */ - if (changed && curstate) - { - /* impluse controls: reset the impulse counter */ - if (temp_field_impulse != 0 && field->state->impulse == 0) - field->state->impulse = temp_field_impulse; +void ioport_manager::init_autoselect_devices(int type1, int type2, int type3, const char *option, const char *ananame) +{ + // if nothing specified, ignore the option + const char *stemp = machine().options().value(option); + if (stemp[0] == 0) + return; - /* toggle controls: flip the toggle state or advance to the next setting */ - if (field->flags & FIELD_FLAG_TOGGLE) - { - if (field->settinglist().count() == 0) - field->state->value ^= field->mask; - else - input_field_select_next_setting(field); - } + // extract valid strings + const char *autostring = "keyboard"; + input_device_class autoenable = DEVICE_CLASS_KEYBOARD; + if (strcmp(stemp, "mouse") == 0) + { + autoenable = DEVICE_CLASS_MOUSE; + autostring = "mouse"; } - - /* update the current state with the impulse state */ - if (temp_field_impulse != 0) + else if (strcmp(stemp, "joystick") == 0) { - if (field->state->impulse != 0) - { - field->state->impulse--; - curstate = TRUE; - } - else - curstate = FALSE; + autoenable = DEVICE_CLASS_JOYSTICK; + autostring = "joystick"; } - - /* for toggle switches, the current value is folded into the port's default value */ - /* so we always return FALSE here */ - if (field->flags & FIELD_FLAG_TOGGLE) - curstate = FALSE; - - /* additional logic to restrict digital joysticks */ - if (curstate && !mouse_down && field->state->joystick != NULL && field->way != 16 && !field->machine().options().joystick_contradictory()) + else if (strcmp(stemp, "lightgun") == 0) { - UINT8 mask = (field->way == 4) ? field->state->joystick->current4way : field->state->joystick->current; - if (!(mask & (1 << field->state->joydir))) - curstate = FALSE; + autoenable = DEVICE_CLASS_LIGHTGUN; + autostring = "lightgun"; } - - /* skip locked-out coin inputs */ - if (curstate && field->type >= IPT_COIN1 && field->type <= IPT_COIN12 && coin_lockout_get_state(field->machine(), field->type - IPT_COIN1)) + else if (strcmp(stemp, "none") == 0) { - int verbose = field->machine().options().verbose(); -#ifdef MAME_DEBUG - verbose = 1; -#endif - if (field->machine().options().coin_lockout()) - { - if (verbose) - ui_popup_time(3, "Coinlock disabled %s.", input_field_name(field)); - return FALSE; /* curstate = FALSE; */ - } - else - if (verbose) - ui_popup_time(3, "Coinlock disabled, but broken through %s.", input_field_name(field)); + // nothing specified + return; } + else if (strcmp(stemp, "keyboard") != 0) + mame_printf_error("Invalid %s value %s; reverting to keyboard\n", option, stemp); + + // only scan the list if we haven't already enabled this class of control + if (first_port() != NULL && !machine().input().device_class(autoenable).enabled()) + for (ioport_port *port = first_port(); port != NULL; port = port->next()) + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) - return curstate; + // if this port type is in use, apply the autoselect criteria + if ((type1 != 0 && field->type() == type1) || (type2 != 0 && field->type() == type2) || (type3 != 0 && field->type() == type3)) + { + mame_printf_verbose("Input: Autoenabling %s due to presence of a %s\n", autostring, ananame); + machine().input().device_class(autoenable).enable(); + break; + } } +//------------------------------------------------- +// exit - exit callback to ensure we clean up +// and close our files +//------------------------------------------------- -/*************************************************************************** - PORT CONFIGURATION HELPERS -***************************************************************************/ - -/*------------------------------------------------- - port_default_value - updates default value - of port settings according to device settings --------------------------------------------------*/ - -UINT32 port_default_value(const char *fulltag, UINT32 mask, UINT32 defval, device_t &owner) +void ioport_manager::exit() { - const input_device_default *def = owner.input_ports_defaults(); - if (def != NULL) - { - astring fullpath; - for ( ; def->tag != NULL; def++) - if (owner.subtag(fullpath, def->tag) == fulltag && def->mask == mask) - return def->defvalue; - } - return defval; + // close any playback or recording files + playback_end(); + record_end(); } -/*------------------------------------------------- - input_port_config - constructor for an - I/O port configuration object --------------------------------------------------*/ +//------------------------------------------------- +// type_name - return the name for the given +// type/player +//------------------------------------------------- -input_port_config::input_port_config(device_t &owner, const char *tag) - : state(NULL), - active(0), - m_next(NULL), - m_owner(owner), - m_tag(tag), - m_modcount(0) +const char *ioport_manager::type_name(ioport_type type, UINT8 player) { + // if we have a machine, use the live state and quick lookup + input_type_entry *entry = m_type_to_entry[type][player]; + if (entry != NULL) + return entry->name(); + + // if we find nothing, return an invalid group + return "???"; } -running_machine &input_port_config::machine() const +//------------------------------------------------- +// type_group - return the group for the given +// type/player +//------------------------------------------------- + +ioport_group ioport_manager::type_group(ioport_type type, int player) { - return m_owner.machine(); + input_type_entry *entry = m_type_to_entry[type][player]; + if (entry != NULL) + return entry->group(); + + // if we find nothing, return an invalid group + return IPG_INVALID; } -/*------------------------------------------------- - field_config_alloc - allocate a new input - port field config --------------------------------------------------*/ +//------------------------------------------------- +// type_seq - return the input sequence for the +// given type/player +//------------------------------------------------- -input_field_config::input_field_config(input_port_config &port, int _type, input_port_value _defvalue, input_port_value _maskbits, const char *_name) - : mask(_maskbits), - defvalue(_defvalue & _maskbits), - type(_type), - player(0), - flags(0), - impulse(0), - name(_name), - read_param(NULL), - read_device(DEVICE_SELF), - write_param(NULL), - write_device(DEVICE_SELF), - min(0), - max(_maskbits), - sensitivity(0), - delta(0), - centerdelta(0), - crossaxis(0), - crossscale(0), - crossoffset(0), - crossaltaxis(0), - crossmapper_device(DEVICE_SELF), - full_turn_count(0), - remap_table(NULL), - way(0), - state(NULL), - m_next(NULL), - m_port(port), - m_modcount(port.modcount()) +const input_seq &ioport_manager::type_seq(ioport_type type, int player, input_seq_type seqtype) { - memset(&condition, 0, sizeof(condition)); - for (int seqtype = 0; seqtype < ARRAY_LENGTH(seq); seqtype++) - seq[seqtype].set_default(); - chars[0] = chars[1] = chars[2] = (unicode_char) 0; + assert(type >= 0 && type < IPT_COUNT); + assert(player >= 0 && player < MAX_PLAYERS); + + // if we have a machine, use the live state and quick lookup + input_type_entry *entry = m_type_to_entry[type][player]; + if (entry != NULL) + return entry->seq(seqtype); + + // if we find nothing, return an empty sequence + return input_seq::empty_seq; } -input_setting_config::input_setting_config(input_field_config &field, input_port_value _value, const char *_name) - : value(_value), - name(_name), - m_field(field), - m_next(NULL) +//------------------------------------------------- +// set_type_seq - change the input sequence for +// the given type/player +//------------------------------------------------- + +void ioport_manager::set_type_seq(ioport_type type, int player, input_seq_type seqtype, const input_seq &newseq) { - memset(&condition, 0, sizeof(condition)); + input_type_entry *entry = m_type_to_entry[type][player]; + if (entry != NULL) + entry->m_seq[seqtype] = newseq; } -input_field_diplocation::input_field_diplocation(const char *string, UINT8 _swnum, bool _invert) - : swname(string), - swnum(_swnum), - invert(_invert) + +//------------------------------------------------- +// type_pressed - return true if the sequence for +// the given input type/player is pressed +//------------------------------------------------- + +bool ioport_manager::type_pressed(ioport_type type, int player) { + return machine().input().seq_pressed(type_seq(type, player)); } -/*------------------------------------------------- - field_config_insert - insert an allocated - input port field config, replacing any - intersecting fields already present and - inserting at the correct sorted location --------------------------------------------------*/ -void input_port_config::collapse_fields(astring &errorbuf) +//------------------------------------------------- +// type_class_present - return true if the given +// ioport_type_class exists in at least one port +//------------------------------------------------- + +bool ioport_manager::type_class_present(ioport_type_class inputclass) { - input_field_config *list = m_fieldlist.detach_all(); - input_port_value maskbits = 0; - int lastmodcount = -1; - while (list != NULL) - { - if (list->modcount() != lastmodcount) - { - lastmodcount = list->modcount(); - maskbits = 0; - } - input_field_config *current = list; - list = list->next(); - field_config_insert(*current, maskbits, errorbuf); - } + for (ioport_port *port = first_port(); port != NULL; port = port->next()) + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) + if (field->type_class() == inputclass) + return true; + return false; } -void field_config_insert(input_field_config &newfield, input_port_value &disallowedbits, astring &errorbuf) -{ - input_port_value lowbit; - /* verify against the disallowed bits, but only if we are condition-free */ - if (newfield.condition.condition == PORTCOND_ALWAYS) - { - if ((newfield.mask & disallowedbits) != 0) - errorbuf.catprintf("INPUT_TOKEN_FIELD specifies duplicate port bits (port=%s mask=%X)\n", newfield.port().tag(), newfield.mask); - disallowedbits |= newfield.mask; - } +//------------------------------------------------- +// has_keyboard - determine if there is a +// keyboard present in the control list +//------------------------------------------------- - /* first modify/nuke any entries that intersect our maskbits */ - input_field_config *nextfield; - for (input_field_config *field = newfield.port().fieldlist().first(); field != NULL; field = nextfield) - { - nextfield = field->next(); - if ((field->mask & newfield.mask) != 0 && (newfield.condition.condition == PORTCOND_ALWAYS || - field->condition.condition == PORTCOND_ALWAYS || - condition_equal(&field->condition, &newfield.condition))) +bool ioport_manager::has_keyboard() const +{ + // iterate over ports and fields + for (ioport_port *port = first_port(); port != NULL; port = port->next()) + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) { - /* reduce the mask of the field we found */ - field->mask &= ~newfield.mask; + // if we are at init, check IPT_KEYBOARD + if (!m_safe_to_read && field->type() == IPT_KEYBOARD) + return true; - /* if the new entry fully overrides the previous one, we nuke */ - if (INPUT_PORT_OVERRIDE_FULLY_NUKES_PREVIOUS || field->mask == 0) - newfield.port().fieldlist().remove(*field); + // else, check if there is a keyboard and if such a keyboard is enabled + if (field->type() == IPT_KEYBOARD && field->enabled()) + return true; } - } - /* make a mask of just the low bit */ - lowbit = (newfield.mask ^ (newfield.mask - 1)) & newfield.mask; + return false; +} + +//------------------------------------------------- +// count_players - counts the number of active +// players +//------------------------------------------------- - /* scan forward to find where to insert ourselves */ - input_field_config *field; - for (field = newfield.port().fieldlist().first(); field != NULL; field = field->next()) - if (field->mask > lowbit) - break; +int ioport_manager::count_players() const +{ + int max_player = 0; + for (ioport_port *port = first_port(); port != NULL; port = port->next()) + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) + if (field->type_class() == INPUT_CLASS_CONTROLLER && max_player <= field->player() + 1) + max_player = field->player() + 1; - /* insert it into the list */ - newfield.port().fieldlist().insert_before(newfield, field); + return max_player; } -/*------------------------------------------------- - diplocation_expand - expand a string-based - DIP location into a linked list of - descriptions --------------------------------------------------*/ +//------------------------------------------------- +// crosshair_position - return the extracted +// crosshair values for the given player +//------------------------------------------------- -void diplocation_list_alloc(input_field_config &field, const char *location, astring &errorbuf) +bool ioport_manager::crosshair_position(int player, float &x, float &y) { - /* if nothing present, bail */ - if (location == NULL) - return; - - field.diploclist().reset(); + // read all the lightgun values + bool gotx = false, goty = false; + for (ioport_port *port = first_port(); port != NULL; port = port->next()) + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) + if (field->player() == player && field->crosshair_axis() != CROSSHAIR_AXIS_NONE && field->enabled()) + { + field->crosshair_position(x, y, gotx, goty); - /* parse the string */ - astring name; // Don't move this variable inside the loop, lastname's lifetime depends on it being outside - const char *lastname = NULL; - const char *curentry = location; - int entries = 0; - while (*curentry != 0) - { - /* find the end of this entry */ - const char *comma = strchr(curentry, ','); - if (comma == NULL) - comma = curentry + strlen(curentry); + // if we got both, stop + if (gotx && goty) + break; + } - /* extract it to tempbuf */ - astring tempstr; - tempstr.cpy(curentry, comma - curentry); + return (gotx && goty); +} - /* first extract the switch name if present */ - const char *number = tempstr; - const char *colon = strchr(tempstr, ':'); - /* allocate and copy the name if it is present */ - if (colon != NULL) - { - lastname = name.cpy(number, colon - number); - number = colon + 1; - } +//------------------------------------------------- +// update_defaults - force an update to the input +// port values based on current conditions +//------------------------------------------------- - /* otherwise, just copy the last name */ - else +void ioport_manager::update_defaults() +{ + // two passes to catch conditionals properly + for (int loopnum = 0; loopnum < 2; loopnum++) + { + // loop over all input ports + for (ioport_port *port = first_port(); port != NULL; port = port->next()) { - if (lastname == NULL) - { - errorbuf.catprintf("Switch location '%s' missing switch name!\n", location); - lastname = (char *)"UNK"; - } - name.cpy(lastname); - } + // only clear on the first pass + if (loopnum == 0) + port->live().defvalue = 0; - /* if the number is preceded by a '!' it's active high */ - bool invert = false; - if (*number == '!') - { - invert = true; - number++; + // first compute the default value for the entire port + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) + if (field->enabled()) + port->live().defvalue = (port->live().defvalue & ~field->mask()) | (field->live().value & field->mask()); } + } +} - /* now scan the switch number */ - int swnum = -1; - if (sscanf(number, "%d", &swnum) != 1) - errorbuf.catprintf("Switch location '%s' has invalid format!\n", location); - - /* allocate a new entry */ - field.diploclist().append(*global_alloc(input_field_diplocation(name, swnum, invert))); - entries++; - /* advance to the next item */ - curentry = comma; - if (*curentry != 0) - curentry++; - } +//------------------------------------------------- +// frame_update - core logic for per-frame input +// port updating +//------------------------------------------------- - /* then verify the number of bits in the mask matches */ - input_port_value temp; - int bits; - for (bits = 0, temp = field.mask; temp != 0 && bits < 32; bits++) - temp &= temp - 1; - if (bits != entries) - errorbuf.catprintf("Switch location '%s' does not describe enough bits for mask %X\n", location, field.mask); +digital_joystick &ioport_manager::digjoystick(int player, int number) +{ + // find it in the list + for (digital_joystick *joystick = m_joystick_list.first(); joystick != NULL; joystick = joystick->next()) + if (joystick->player() == player && joystick->number() == number) + return *joystick; + + // create a new one + return m_joystick_list.append(*global_alloc(digital_joystick(player, number))); } +//------------------------------------------------- +// frame_update - core logic for per-frame input +// port updating +//------------------------------------------------- -/*************************************************************************** - TOKENIZATION HELPERS -***************************************************************************/ +void ioport_manager::frame_update() +{ + // if we're paused, don't do anything + if (machine().paused()) + return; -/*------------------------------------------------- - token_to_input_field_type - convert a string - token to an input field type and player --------------------------------------------------*/ +g_profiler.start(PROFILER_INPUT); -static int token_to_input_field_type(running_machine &machine, const char *string, int *player) -{ - ioport_manager &portdata = machine.ioport(); - int ipnum; + // record/playback information about the current frame + attotime curtime = machine().time(); + playback_frame(curtime); + record_frame(curtime); - /* check for our failsafe case first */ - if (sscanf(string, "TYPE_OTHER(%d,%d)", &ipnum, player) == 2) - return ipnum; + // track the duration of the previous frame + m_last_delta_nsec = (curtime - m_last_frame_time).as_attoseconds() / ATTOSECONDS_PER_NANOSECOND; + m_last_frame_time = curtime; - /* find the token in the list */ - for (input_type_entry *entry = portdata.typelist.first(); entry != NULL; entry = entry->next()) - if (entry->token != NULL && !strcmp(entry->token, string)) - { - *player = entry->player; - return entry->type; - } + // update the digital joysticks + for (digital_joystick *joystick = m_joystick_list.first(); joystick != NULL; joystick = joystick->next()) + joystick->frame_update(); - /* if we fail, return IPT_UNKNOWN */ - *player = 0; - return IPT_UNKNOWN; -} + // compute default values for all the ports + update_defaults(); + // perform mouse hit testing + INT32 mouse_target_x, mouse_target_y; + int mouse_button; + render_target *mouse_target = ui_input_find_mouse(machine(), &mouse_target_x, &mouse_target_y, &mouse_button); -/*------------------------------------------------- - input_field_type_to_token - convert an input - field type and player to a string token --------------------------------------------------*/ + // if the button is pressed, map the point and determine what was hit + ioport_field *mouse_field = NULL; + if (mouse_button && mouse_target != NULL) + { + const char *tag = NULL; + ioport_value mask; + float x, y; + if (mouse_target->map_point_input(mouse_target_x, mouse_target_y, tag, mask, x, y)) + { + ioport_port *port = machine().root_device().ioport(tag); + if (port != NULL) + mouse_field = port->field(mask); + } + } -static const char *input_field_type_to_token(running_machine &machine, int type, int player) -{ - ioport_manager &portdata = machine.ioport(); - static char tempbuf[32]; + // loop over all input ports + for (ioport_port *port = first_port(); port != NULL; port = port->next()) + { + port->frame_update(mouse_field); - /* look up the port and return the token */ - input_type_entry *entry = portdata.type_to_entry[type][player]; - if (entry != NULL) - return entry->token; + // handle playback/record + playback_port(*port); + record_port(*port); + } - /* if that fails, carry on */ - sprintf(tempbuf, "TYPE_OTHER(%d,%d)", type, player); - return tempbuf; +g_profiler.stop(); } -/*------------------------------------------------- - token_to_seq_type - convert a string to - a sequence type --------------------------------------------------*/ +//------------------------------------------------- +// frame_interpolate - interpolate between two +// values based on the time between frames +//------------------------------------------------- -static int token_to_seq_type(const char *string) +ioport_value ioport_manager::frame_interpolate(ioport_value oldval, ioport_value newval) { - int seqindex; + // if no last delta, just use new value + if (m_last_delta_nsec == 0) + return newval; - /* look up the string in the table of possible sequence types and return the index */ - for (seqindex = 0; seqindex < ARRAY_LENGTH(seqtypestrings); seqindex++) - if (!mame_stricmp(string, seqtypestrings[seqindex])) - return seqindex; - - return -1; + // otherwise, interpolate + attoseconds_t nsec_since_last = (machine().time() - m_last_frame_time).as_attoseconds() / ATTOSECONDS_PER_NANOSECOND; + return oldval + (INT64(newval - oldval) * nsec_since_last / m_last_delta_nsec); } +//------------------------------------------------- +// load_config - callback to extract configuration +// data from the XML nodes +//------------------------------------------------- -/*************************************************************************** - SETTINGS LOAD -***************************************************************************/ - -/*------------------------------------------------- - load_config_callback - callback to extract - configuration data from the XML nodes --------------------------------------------------*/ - -static void load_config_callback(running_machine &machine, int config_type, xml_data_node *parentnode) +void ioport_manager::load_config(int config_type, xml_data_node *parentnode) { - ioport_manager &portdata = machine.ioport(); - xml_data_node *portnode; - int seqtype; - - /* in the completion phase, we finish the initialization with the final ports */ + // in the completion phase, we finish the initialization with the final ports if (config_type == CONFIG_TYPE_FINAL) { - portdata.safe_to_read = TRUE; - frame_update(machine); + m_safe_to_read = true; + frame_update(); } - /* early exit if no data to parse */ + // early exit if no data to parse if (parentnode == NULL) return; - /* iterate over all the remap nodes for controller configs only */ + // iterate over all the remap nodes for controller configs only if (config_type == CONFIG_TYPE_CONTROLLER) - load_remap_table(machine, parentnode); + load_remap_table(parentnode); - /* iterate over all the port nodes */ - for (portnode = xml_get_sibling(parentnode->child, "port"); portnode; portnode = xml_get_sibling(portnode->next, "port")) + // iterate over all the port nodes + for (xml_data_node *portnode = xml_get_sibling(parentnode->child, "port"); portnode; portnode = xml_get_sibling(portnode->next, "port")) { - input_seq newseq[SEQ_TYPE_TOTAL], tempseq; - xml_data_node *seqnode; - int type, player; + // get the basic port info from the attributes + int player; + int type = token_to_input_type(xml_get_attribute_string(portnode, "type", ""), player); - /* get the basic port info from the attributes */ - type = token_to_input_field_type(machine, xml_get_attribute_string(portnode, "type", ""), &player); - - /* initialize sequences to invalid defaults */ - for (seqtype = 0; seqtype < ARRAY_LENGTH(newseq); seqtype++) + // initialize sequences to invalid defaults + input_seq newseq[SEQ_TYPE_TOTAL]; + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) newseq[seqtype].set(INPUT_CODE_INVALID); - /* loop over new sequences */ - for (seqnode = xml_get_sibling(portnode->child, "newseq"); seqnode; seqnode = xml_get_sibling(seqnode->next, "newseq")) + // loop over new sequences + for (xml_data_node *seqnode = xml_get_sibling(portnode->child, "newseq"); seqnode; seqnode = xml_get_sibling(seqnode->next, "newseq")) { - /* with a valid type, parse out the new sequence */ - seqtype = token_to_seq_type(xml_get_attribute_string(seqnode, "type", "")); + // with a valid type, parse out the new sequence + input_seq_type seqtype = token_to_seq_type(xml_get_attribute_string(seqnode, "type", "")); if (seqtype != -1 && seqnode->value != NULL) { if (strcmp(seqnode->value, "NONE") == 0) newseq[seqtype].set(); else - machine.input().seq_from_tokens(newseq[seqtype], seqnode->value); + machine().input().seq_from_tokens(newseq[seqtype], seqnode->value); } } - /* if we're loading default ports, apply to the defaults */ + // if we're loading default ports, apply to the defaults if (config_type != CONFIG_TYPE_GAME) - load_default_config(machine, portnode, type, player, newseq); + load_default_config(portnode, type, player, newseq); else - load_game_config(machine, portnode, type, player, newseq); + load_game_config(portnode, type, player, newseq); } - /* after applying the controller config, push that back into the backup, since that is */ - /* what we will diff against */ + // after applying the controller config, push that back into the backup, since that is + // what we will diff against if (config_type == CONFIG_TYPE_CONTROLLER) - for (input_type_entry *entry = portdata.typelist.first(); entry != NULL; entry = entry->next()) - for (seqtype = 0; seqtype < ARRAY_LENGTH(entry->seq); seqtype++) - entry->defseq[seqtype] = entry->seq[seqtype]; + for (input_type_entry *entry = m_typelist.first(); entry != NULL; entry = entry->next()) + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + entry->defseq(seqtype) = entry->seq(seqtype); } -/*------------------------------------------------- - load_remap_table - extract and apply the - global remapping table --------------------------------------------------*/ +//------------------------------------------------- +// load_remap_table - extract and apply the +// global remapping table +//------------------------------------------------- -static void load_remap_table(running_machine &machine, xml_data_node *parentnode) +void ioport_manager::load_remap_table(xml_data_node *parentnode) { - ioport_manager &portdata = machine.ioport(); - input_code *oldtable, *newtable; - xml_data_node *remapnode; - int count; - - /* count items first so we can allocate */ - count = 0; - for (remapnode = xml_get_sibling(parentnode->child, "remap"); remapnode != NULL; remapnode = xml_get_sibling(remapnode->next, "remap")) + // count items first so we can allocate + int count = 0; + for (xml_data_node *remapnode = xml_get_sibling(parentnode->child, "remap"); remapnode != NULL; remapnode = xml_get_sibling(remapnode->next, "remap")) count++; - /* if we have some, deal with them */ + // if we have some, deal with them if (count > 0) { - int remapnum; - - /* allocate tables */ - oldtable = global_alloc_array(input_code, count); - newtable = global_alloc_array(input_code, count); + // allocate tables + dynamic_array<input_code> oldtable(count); + dynamic_array<input_code> newtable(count); - /* build up the remap table */ + // build up the remap table count = 0; - for (remapnode = xml_get_sibling(parentnode->child, "remap"); remapnode != NULL; remapnode = xml_get_sibling(remapnode->next, "remap")) + for (xml_data_node *remapnode = xml_get_sibling(parentnode->child, "remap"); remapnode != NULL; remapnode = xml_get_sibling(remapnode->next, "remap")) { - input_code origcode = machine.input().code_from_token(xml_get_attribute_string(remapnode, "origcode", "")); - input_code newcode = machine.input().code_from_token(xml_get_attribute_string(remapnode, "newcode", "")); + input_code origcode = machine().input().code_from_token(xml_get_attribute_string(remapnode, "origcode", "")); + input_code newcode = machine().input().code_from_token(xml_get_attribute_string(remapnode, "newcode", "")); if (origcode != INPUT_CODE_INVALID && newcode != INPUT_CODE_INVALID) { oldtable[count] = origcode; @@ -3214,214 +3115,187 @@ static void load_remap_table(running_machine &machine, xml_data_node *parentnode } } - /* loop over the remapping table, operating only if something was specified */ - for (remapnum = 0; remapnum < count; remapnum++) - { - input_code oldcode = oldtable[remapnum]; - input_code newcode = newtable[remapnum]; - - /* loop over all default ports, remapping the requested keys */ - for (input_type_entry *entry = portdata.typelist.first(); entry != NULL; entry = entry->next()) - { - /* remap anything in the default sequences */ - for (int seqtype = 0; seqtype < ARRAY_LENGTH(entry->seq); seqtype++) - entry->seq[seqtype].replace(oldcode, newcode); - } - } - - /* release the tables */ - global_free(oldtable); - global_free(newtable); + // loop over the remapping table, then over default ports, replacing old with new + for (int remapnum = 0; remapnum < count; remapnum++) + for (input_type_entry *entry = m_typelist.first(); entry != NULL; entry = entry->next()) + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + entry->m_seq[seqtype].replace(oldtable[remapnum], newtable[remapnum]); } } -/*------------------------------------------------- - load_default_config - apply configuration - data to the default mappings --------------------------------------------------*/ +//------------------------------------------------- +// load_default_config - apply configuration +// data to the default mappings +//------------------------------------------------- -static int load_default_config(running_machine &machine, xml_data_node *portnode, int type, int player, const input_seq *newseq) +bool ioport_manager::load_default_config(xml_data_node *portnode, int type, int player, const input_seq *newseq) { - ioport_manager &portdata = machine.ioport(); - - /* find a matching port in the list */ - for (input_type_entry *entry = portdata.typelist.first(); entry != NULL; entry = entry->next()) - if (entry->type == type && entry->player == player) + // find a matching port in the list + for (input_type_entry *entry = m_typelist.first(); entry != NULL; entry = entry->next()) + if (entry->type() == type && entry->player() == player) { - for (int seqtype = 0; seqtype < ARRAY_LENGTH(entry->seq); seqtype++) + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) if (newseq[seqtype][0] != INPUT_CODE_INVALID) - entry->seq[seqtype] = newseq[seqtype]; - return TRUE; + entry->m_seq[seqtype] = newseq[seqtype]; + return true; } - return FALSE; + return false; } -/*------------------------------------------------- - load_game_config - apply configuration - data to the current set of input ports --------------------------------------------------*/ +//------------------------------------------------- +// load_game_config - apply configuration +// data to the current set of input ports +//------------------------------------------------- -static int load_game_config(running_machine &machine, xml_data_node *portnode, int type, int player, const input_seq *newseq) +bool ioport_manager::load_game_config(xml_data_node *portnode, int type, int player, const input_seq *newseq) { - input_port_value mask, defvalue; - const input_field_config *field; - const input_port_config *port; - char tempbuffer[20]; - const char *tag; - - /* read the mask, index, and defvalue attributes */ - tag = xml_get_attribute_string(portnode, "tag", NULL); - mask = xml_get_attribute_int(portnode, "mask", 0); - defvalue = xml_get_attribute_int(portnode, "defvalue", 0); + // read the mask, index, and defvalue attributes + const char *tag = xml_get_attribute_string(portnode, "tag", NULL); + ioport_value mask = xml_get_attribute_int(portnode, "mask", 0); + ioport_value defvalue = xml_get_attribute_int(portnode, "defvalue", 0); - /* find the port we want; if no tag, search them all */ - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - if (tag == NULL || strcmp(get_port_tag(port, tempbuffer), tag) == 0) - for (field = port->first_field(); field != NULL; field = field->next()) + // find the port we want; if no tag, search them all + for (ioport_port *port = first_port(); port != NULL; port = port->next()) + if (tag == NULL || strcmp(port->tag(), tag) == 0) + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) - /* find the matching mask and defvalue */ - if (field->type == type && field->player == player && - field->mask == mask && (field->defvalue & mask) == (defvalue & mask)) + // find the matching mask and defvalue + if (field->type() == type && field->player() == player && + field->mask() == mask && (field->defvalue() & mask) == (defvalue & mask)) { - const char *revstring; - int seqtype; - - /* if a sequence was specified, copy it in */ - for (seqtype = 0; seqtype < ARRAY_LENGTH(field->state->seq); seqtype++) + // if a sequence was specified, copy it in + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) if (newseq[seqtype][0] != INPUT_CODE_INVALID) - field->state->seq[seqtype] = newseq[seqtype]; + field->live().seq[seqtype] = newseq[seqtype]; - /* for non-analog fields, fetch the value */ - if (field->state->analog == NULL) - field->state->value = xml_get_attribute_int(portnode, "value", field->defvalue); + // for non-analog fields, fetch the value + if (field->live().analog == NULL) + field->live().value = xml_get_attribute_int(portnode, "value", field->defvalue()); - /* for analog fields, fetch configurable analog attributes */ + // for analog fields, fetch configurable analog attributes else { - /* get base attributes */ - field->state->analog->delta = xml_get_attribute_int(portnode, "keydelta", field->delta); - field->state->analog->centerdelta = xml_get_attribute_int(portnode, "centerdelta", field->centerdelta); - field->state->analog->sensitivity = xml_get_attribute_int(portnode, "sensitivity", field->sensitivity); + // get base attributes + field->live().analog->m_delta = xml_get_attribute_int(portnode, "keydelta", field->delta()); + field->live().analog->m_centerdelta = xml_get_attribute_int(portnode, "centerdelta", field->centerdelta()); + field->live().analog->m_sensitivity = xml_get_attribute_int(portnode, "sensitivity", field->sensitivity()); - /* fetch yes/no for reverse setting */ - revstring = xml_get_attribute_string(portnode, "reverse", NULL); + // fetch yes/no for reverse setting + const char *revstring = xml_get_attribute_string(portnode, "reverse", NULL); if (revstring != NULL) - field->state->analog->reverse = (strcmp(revstring, "yes") == 0); + field->live().analog->m_reverse = (strcmp(revstring, "yes") == 0); } - return TRUE; + return true; } - return FALSE; + return false; } -/*************************************************************************** - SETTINGS SAVE -***************************************************************************/ +//************************************************************************** +// SETTINGS SAVE +//************************************************************************** -/*------------------------------------------------- - save_config_callback - config callback for - saving input port configuration --------------------------------------------------*/ +//------------------------------------------------- +// save_config - config callback for saving input +// port configuration +//------------------------------------------------- -static void save_config_callback(running_machine &machine, int config_type, xml_data_node *parentnode) +void ioport_manager::save_config(int config_type, xml_data_node *parentnode) { - /* if no parentnode, ignore */ + // if no parentnode, ignore if (parentnode == NULL) return; - /* default ports save differently */ + // default ports save differently if (config_type == CONFIG_TYPE_DEFAULT) - save_default_inputs(machine, parentnode); + save_default_inputs(parentnode); else - save_game_inputs(machine, parentnode); + save_game_inputs(parentnode); } -/*------------------------------------------------- - save_sequence - add a node for an input - sequence --------------------------------------------------*/ +//------------------------------------------------- +// save_sequence - add a node for an input +// sequence +//------------------------------------------------- -static void save_sequence(running_machine &machine, xml_data_node *parentnode, int type, int porttype, const input_seq &seq) +void ioport_manager::save_sequence(xml_data_node *parentnode, input_seq_type type, ioport_type porttype, const input_seq &seq) { + // get the string for the sequence astring seqstring; - xml_data_node *seqnode; - - /* get the string for the sequence */ if (seq.length() == 0) seqstring.cpy("NONE"); else - machine.input().seq_to_tokens(seqstring, seq); + machine().input().seq_to_tokens(seqstring, seq); - /* add the new node */ - seqnode = xml_add_child(parentnode, "newseq", seqstring); + // add the new node + xml_data_node *seqnode = xml_add_child(parentnode, "newseq", seqstring); if (seqnode != NULL) xml_set_attribute(seqnode, "type", seqtypestrings[type]); } -/*------------------------------------------------- - save_this_input_field_type - determine if the given - port type is worth saving --------------------------------------------------*/ +//------------------------------------------------- +// save_this_input_field_type - determine if the +// given port type is worth saving +//------------------------------------------------- -static int save_this_input_field_type(int type) +bool ioport_manager::save_this_input_field_type(ioport_type type) { switch (type) { case IPT_UNUSED: case IPT_END: case IPT_PORT: - case IPT_VBLANK: case IPT_UNKNOWN: - return FALSE; + return false; + + default: + break; } - return TRUE; + return true; } -/*------------------------------------------------- - save_default_inputs - add nodes for any default - mappings that have changed --------------------------------------------------*/ +//------------------------------------------------- +// save_default_inputs - add nodes for any default +// mappings that have changed +//------------------------------------------------- -static void save_default_inputs(running_machine &machine, xml_data_node *parentnode) +void ioport_manager::save_default_inputs(xml_data_node *parentnode) { - ioport_manager &portdata = machine.ioport(); - input_type_entry *entry; - - /* iterate over ports */ - for (entry = portdata.typelist.first(); entry != NULL; entry = entry->next()) + // iterate over ports + for (input_type_entry *entry = m_typelist.first(); entry != NULL; entry = entry->next()) { - /* only save if this port is a type we save */ - if (save_this_input_field_type(entry->type)) + // only save if this port is a type we save + if (save_this_input_field_type(entry->type())) { - int seqtype; - - /* see if any of the sequences have changed */ - for (seqtype = 0; seqtype < ARRAY_LENGTH(entry->seq); seqtype++) - if (entry->seq[seqtype] != entry->defseq[seqtype]) + // see if any of the sequences have changed + input_seq_type seqtype; + for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + if (entry->seq(seqtype) != entry->defseq(seqtype)) break; - /* if so, we need to add a node */ - if (seqtype < ARRAY_LENGTH(entry->seq)) + // if so, we need to add a node + if (seqtype < SEQ_TYPE_TOTAL) { - /* add a new port node */ + // add a new port node xml_data_node *portnode = xml_add_child(parentnode, "port", NULL); if (portnode != NULL) { - /* add the port information and attributes */ - xml_set_attribute(portnode, "type", input_field_type_to_token(machine, entry->type, entry->player)); - - /* add only the sequences that have changed from the defaults */ - for (seqtype = 0; seqtype < ARRAY_LENGTH(entry->seq); seqtype++) - if (entry->seq[seqtype] != entry->defseq[seqtype]) - save_sequence(machine, portnode, seqtype, entry->type, entry->seq[seqtype]); + // add the port information and attributes + astring tempstr; + xml_set_attribute(portnode, "type", input_type_to_token(tempstr, entry->type(), entry->player())); + + // add only the sequences that have changed from the defaults + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + if (entry->seq(seqtype) != entry->defseq(seqtype)) + save_sequence(portnode, seqtype, entry->type(), entry->seq(seqtype)); } } } @@ -3429,79 +3303,73 @@ static void save_default_inputs(running_machine &machine, xml_data_node *parentn } -/*------------------------------------------------- - save_game_inputs - add nodes for any game - mappings that have changed --------------------------------------------------*/ +//------------------------------------------------- +// save_game_inputs - add nodes for any game +// mappings that have changed +//------------------------------------------------- -static void save_game_inputs(running_machine &machine, xml_data_node *parentnode) +void ioport_manager::save_game_inputs(xml_data_node *parentnode) { - const input_field_config *field; - const input_port_config *port; - - /* iterate over ports */ - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - for (field = port->first_field(); field != NULL; field = field->next()) - if (save_this_input_field_type(field->type)) + // iterate over ports + for (ioport_port *port = machine().ioport().first_port(); port != NULL; port = port->next()) + for (ioport_field *field = port->first_field(); field != NULL; field = field->next()) + if (save_this_input_field_type(field->type())) { + // determine if we changed bool changed = false; - int seqtype; + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + changed |= (field->live().seq[seqtype] != field->seq(seqtype)); - /* determine if we changed */ - for (seqtype = 0; seqtype < ARRAY_LENGTH(field->state->seq); seqtype++) - changed |= (field->state->seq[seqtype] != field->seq[seqtype]); + // non-analog changes + if (field->live().analog == NULL) + changed |= ((field->live().value & field->mask()) != (field->defvalue() & field->mask())); - /* non-analog changes */ - if (field->state->analog == NULL) - changed |= ((field->state->value & field->mask) != (field->defvalue & field->mask)); - - /* analog changes */ + // analog changes else { - changed |= (field->state->analog->delta != field->delta); - changed |= (field->state->analog->centerdelta != field->centerdelta); - changed |= (field->state->analog->sensitivity != field->sensitivity); - changed |= (field->state->analog->reverse != ((field->flags & ANALOG_FLAG_REVERSE) != 0)); + changed |= (field->live().analog->m_delta != field->delta()); + changed |= (field->live().analog->m_centerdelta != field->centerdelta()); + changed |= (field->live().analog->m_sensitivity != field->sensitivity()); + changed |= (field->live().analog->m_reverse != field->analog_reverse()); } - /* if we did change, add a new node */ + // if we did change, add a new node if (changed) { - /* add a new port node */ + // add a new port node xml_data_node *portnode = xml_add_child(parentnode, "port", NULL); if (portnode != NULL) { - char tempbuffer[20]; - - /* add the identifying information and attributes */ - xml_set_attribute(portnode, "tag", get_port_tag(port, tempbuffer)); - xml_set_attribute(portnode, "type", input_field_type_to_token(machine, field->type, field->player)); - xml_set_attribute_int(portnode, "mask", field->mask); - xml_set_attribute_int(portnode, "defvalue", field->defvalue & field->mask); - - /* add sequences if changed */ - for (seqtype = 0; seqtype < ARRAY_LENGTH(field->state->seq); seqtype++) - if (field->state->seq[seqtype] != field->seq[seqtype]) - save_sequence(machine, portnode, seqtype, field->type, field->state->seq[seqtype]); - - /* write out non-analog changes */ - if (field->state->analog == NULL) + // add the identifying information and attributes + astring tempstr; + xml_set_attribute(portnode, "tag", port->tag()); + xml_set_attribute(portnode, "type", input_type_to_token(tempstr, field->type(), field->player())); + xml_set_attribute_int(portnode, "mask", field->mask()); + xml_set_attribute_int(portnode, "defvalue", field->defvalue() & field->mask()); + + // add sequences if changed + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) + if (field->live().seq[seqtype] != field->seq(seqtype)) + save_sequence(portnode, seqtype, field->type(), field->live().seq[seqtype]); + + // write out non-analog changes + if (field->live().analog == NULL) { - if ((field->state->value & field->mask) != (field->defvalue & field->mask)) - xml_set_attribute_int(portnode, "value", field->state->value & field->mask); + if ((field->live().value & field->mask()) != (field->defvalue() & field->mask())) + xml_set_attribute_int(portnode, "value", field->live().value & field->mask()); } - /* write out analog changes */ + // write out analog changes else { - if (field->state->analog->delta != field->delta) - xml_set_attribute_int(portnode, "keydelta", field->state->analog->delta); - if (field->state->analog->centerdelta != field->centerdelta) - xml_set_attribute_int(portnode, "centerdelta", field->state->analog->centerdelta); - if (field->state->analog->sensitivity != field->sensitivity) - xml_set_attribute_int(portnode, "sensitivity", field->state->analog->sensitivity); - if (field->state->analog->reverse != ((field->flags & ANALOG_FLAG_REVERSE) != 0)) - xml_set_attribute(portnode, "reverse", field->state->analog->reverse ? "yes" : "no"); + if (field->live().analog->m_delta != field->delta()) + xml_set_attribute_int(portnode, "keydelta", field->live().analog->m_delta); + if (field->live().analog->m_centerdelta != field->centerdelta()) + xml_set_attribute_int(portnode, "centerdelta", field->live().analog->m_centerdelta); + if (field->live().analog->m_sensitivity != field->sensitivity()) + xml_set_attribute_int(portnode, "sensitivity", field->live().analog->m_sensitivity); + if (field->live().analog->m_reverse != field->analog_reverse()) + xml_set_attribute(portnode, "reverse", field->live().analog->m_reverse ? "yes" : "no"); } } } @@ -3510,311 +3378,212 @@ static void save_game_inputs(running_machine &machine, xml_data_node *parentnode -/*************************************************************************** - INPUT PLAYBACK -***************************************************************************/ +//************************************************************************** +// INPUT PLAYBACK +//************************************************************************** -/*------------------------------------------------- - playback_read_uint8 - read an 8-bit value - from the playback file --------------------------------------------------*/ +//------------------------------------------------- +// playback_read - read a value from the playback +// file +//------------------------------------------------- -static UINT8 playback_read_uint8(running_machine &machine) +template<typename _Type> +_Type ioport_manager::playback_read(_Type &result) { - ioport_manager &portdata = machine.ioport(); - UINT8 result; + // protect against NULL handles if previous reads fail + if (!m_playback_file.is_open()) + result = 0; - /* protect against NULL handles if previous reads fail */ - if (portdata.playback_file == NULL) - return 0; - - /* read the value; if we fail, end playback */ - if (portdata.playback_file->read(&result, sizeof(result)) != sizeof(result)) + // read the value; if we fail, end playback + else if (m_playback_file.read(&result, sizeof(result)) != sizeof(result)) { - playback_end(machine, "End of file"); - return 0; + playback_end("End of file"); + result = 0; } - /* return the appropriate value */ + // return the appropriate value + else if (sizeof(result) == 8) + result = LITTLE_ENDIANIZE_INT64(result); + else if (sizeof(result) == 4) + result = LITTLE_ENDIANIZE_INT32(result); + else if (sizeof(result) == 2) + result = LITTLE_ENDIANIZE_INT16(result); return result; } - -/*------------------------------------------------- - playback_read_uint32 - read a 32-bit value - from the playback file --------------------------------------------------*/ - -static UINT32 playback_read_uint32(running_machine &machine) -{ - ioport_manager &portdata = machine.ioport(); - UINT32 result; - - /* protect against NULL handles if previous reads fail */ - if (portdata.playback_file == NULL) - return 0; - - /* read the value; if we fail, end playback */ - if (portdata.playback_file->read(&result, sizeof(result)) != sizeof(result)) - { - playback_end(machine, "End of file"); - return 0; - } - - /* return the appropriate value */ - return LITTLE_ENDIANIZE_INT32(result); -} - - -/*------------------------------------------------- - playback_read_uint64 - read a 64-bit value - from the playback file --------------------------------------------------*/ - -static UINT64 playback_read_uint64(running_machine &machine) +template<> +bool ioport_manager::playback_read<bool>(bool &result) { - ioport_manager &portdata = machine.ioport(); - UINT64 result; - - /* protect against NULL handles if previous reads fail */ - if (portdata.playback_file == NULL) - return 0; - - /* read the value; if we fail, end playback */ - if (portdata.playback_file->read(&result, sizeof(result)) != sizeof(result)) - { - playback_end(machine, "End of file"); - return 0; - } - - /* return the appropriate value */ - return LITTLE_ENDIANIZE_INT64(result); + UINT8 temp; + playback_read(temp); + return result = bool(temp); } -/*------------------------------------------------- - playback_init - initialize INP playback --------------------------------------------------*/ +//------------------------------------------------- +// playback_init - initialize INP playback +//------------------------------------------------- -static time_t playback_init(running_machine &machine) +time_t ioport_manager::playback_init() { - const char *filename = machine.options().playback(); - ioport_manager &portdata = machine.ioport(); - UINT8 header[INP_HEADER_SIZE]; - time_t basetime; - - /* if no file, nothing to do */ + // if no file, nothing to do + const char *filename = machine().options().playback(); if (filename[0] == 0) return 0; - /* open the playback file */ - portdata.playback_file = auto_alloc(machine, emu_file(machine.options().input_directory(), OPEN_FLAG_READ)); - file_error filerr = portdata.playback_file->open(filename); + // open the playback file + file_error filerr = m_playback_file.open(filename); assert_always(filerr == FILERR_NONE, "Failed to open file for playback"); - /* read the header and verify that it is a modern version; if not, print an error */ - if (portdata.playback_file->read(header, sizeof(header)) != sizeof(header)) + // read the header and verify that it is a modern version; if not, print an error + UINT8 header[INP_HEADER_SIZE]; + if (m_playback_file.read(header, sizeof(header)) != sizeof(header)) fatalerror("Input file is corrupt or invalid (missing header)"); if (memcmp(header, "MAMEINP\0", 8) != 0) fatalerror("Input file invalid or in an older, unsupported format"); if (header[0x10] != INP_HEADER_MAJVERSION) fatalerror("Input file format version mismatch"); - /* output info to console */ + // output info to console mame_printf_info("Input file: %s\n", filename); mame_printf_info("INP version %d.%d\n", header[0x10], header[0x11]); - basetime = header[0x08] | (header[0x09] << 8) | (header[0x0a] << 16) | (header[0x0b] << 24) | - ((UINT64)header[0x0c] << 32) | ((UINT64)header[0x0d] << 40) | ((UINT64)header[0x0e] << 48) | ((UINT64)header[0x0f] << 56); + time_t basetime = header[0x08] | (header[0x09] << 8) | (header[0x0a] << 16) | (header[0x0b] << 24) | + ((UINT64)header[0x0c] << 32) | ((UINT64)header[0x0d] << 40) | ((UINT64)header[0x0e] << 48) | ((UINT64)header[0x0f] << 56); mame_printf_info("Created %s", ctime(&basetime)); mame_printf_info("Recorded using %s\n", header + 0x20); - /* verify the header against the current game */ - if (memcmp(machine.system().name, header + 0x14, strlen(machine.system().name) + 1) != 0) - mame_printf_info("Input file is for %s '%s', not for current %s '%s'\n", emulator_info::get_gamenoun(), header + 0x14, emulator_info::get_gamenoun(), machine.system().name); - - /* enable compression */ - portdata.playback_file->compress(FCOMPRESS_MEDIUM); + // verify the header against the current game + if (memcmp(machine().system().name, header + 0x14, strlen(machine().system().name) + 1) != 0) + mame_printf_info("Input file is for %s '%s', not for current %s '%s'\n", emulator_info::get_gamenoun(), header + 0x14, emulator_info::get_gamenoun(), machine().system().name); + // enable compression + m_playback_file.compress(FCOMPRESS_MEDIUM); return basetime; } -/*------------------------------------------------- - playback_end - end INP playback --------------------------------------------------*/ +//------------------------------------------------- +// playback_end - end INP playback +//------------------------------------------------- -static void playback_end(running_machine &machine, const char *message) +void ioport_manager::playback_end(const char *message) { - ioport_manager &portdata = machine.ioport(); - - /* only applies if we have a live file */ - if (portdata.playback_file != NULL) + // only applies if we have a live file + if (m_playback_file.is_open()) { - /* close the file */ - auto_free(machine, portdata.playback_file); - portdata.playback_file = NULL; + // close the file + m_playback_file.close(); - /* pop a message */ + // pop a message if (message != NULL) popmessage("Playback Ended\nReason: %s", message); - /* display speed stats */ - portdata.playback_accumulated_speed /= portdata.playback_accumulated_frames; - mame_printf_info("Total playback frames: %d\n", (UINT32)portdata.playback_accumulated_frames); - mame_printf_info("Average recorded speed: %d%%\n", (UINT32)((portdata.playback_accumulated_speed * 200 + 1) >> 21)); + // display speed stats + m_playback_accumulated_speed /= m_playback_accumulated_frames; + mame_printf_info("Total playback frames: %d\n", UINT32(m_playback_accumulated_frames)); + mame_printf_info("Average recorded speed: %d%%\n", UINT32((m_playback_accumulated_speed * 200 + 1) >> 21)); } } -/*------------------------------------------------- - playback_frame - start of frame callback for - playback --------------------------------------------------*/ +//------------------------------------------------- +// playback_frame - start of frame callback for +// playback +//------------------------------------------------- -static void playback_frame(running_machine &machine, attotime curtime) +void ioport_manager::playback_frame(attotime curtime) { - ioport_manager &portdata = machine.ioport(); - - /* if playing back, fetch the information and verify */ - if (portdata.playback_file != NULL) + // if playing back, fetch the information and verify + if (m_playback_file.is_open()) { + // first the absolute time attotime readtime; - - /* first the absolute time */ - readtime.seconds = playback_read_uint32(machine); - readtime.attoseconds = playback_read_uint64(machine); + playback_read(readtime.seconds); + playback_read(readtime.attoseconds); if (readtime != curtime) - playback_end(machine, "Out of sync"); + playback_end("Out of sync"); - /* then the speed */ - portdata.playback_accumulated_speed += playback_read_uint32(machine); - portdata.playback_accumulated_frames++; + // then the speed + UINT32 curspeed; + m_playback_accumulated_speed += playback_read(curspeed); + m_playback_accumulated_frames++; } } -/*------------------------------------------------- - playback_port - per-port callback for playback --------------------------------------------------*/ +//------------------------------------------------- +// playback_port - per-port callback for playback +//------------------------------------------------- -static void playback_port(const input_port_config *port) +void ioport_manager::playback_port(ioport_port &port) { - ioport_manager &portdata = port->machine().ioport(); - - /* if playing back, fetch information about this port */ - if (portdata.playback_file != NULL) + // if playing back, fetch information about this port + if (m_playback_file.is_open()) { - analog_field_state *analog; - - /* read the default value and the digital state */ - port->state->defvalue = playback_read_uint32(port->machine()); - port->state->digital = playback_read_uint32(port->machine()); + // read the default value and the digital state + playback_read(port.live().defvalue); + playback_read(port.live().digital); - /* loop over analog ports and save their data */ - for (analog = port->state->analoglist; analog != NULL; analog = analog->next) + // loop over analog ports and save their data + for (analog_field *analog = port.live().analoglist.first(); analog != NULL; analog = analog->next()) { - /* read current and previous values */ - analog->accum = playback_read_uint32(port->machine()); - analog->previous = playback_read_uint32(port->machine()); + // read current and previous values + playback_read(analog->m_accum); + playback_read(analog->m_previous); - /* read configuration information */ - analog->sensitivity = playback_read_uint32(port->machine()); - analog->reverse = playback_read_uint8(port->machine()); + // read configuration information + playback_read(analog->m_sensitivity); + playback_read(analog->m_reverse); } } } +//------------------------------------------------- +// record_write - write a value to the record file +//------------------------------------------------- -/*************************************************************************** - INPUT RECORDING -***************************************************************************/ - -/*------------------------------------------------- - record_write_uint8 - write an 8-bit value - to the record file --------------------------------------------------*/ - -static void record_write_uint8(running_machine &machine, UINT8 data) +template<typename _Type> +void ioport_manager::record_write(_Type value) { - ioport_manager &portdata = machine.ioport(); - UINT8 result = data; - - /* protect against NULL handles if previous reads fail */ - if (portdata.record_file == NULL) - return; - - /* read the value; if we fail, end playback */ - if (portdata.record_file->write(&result, sizeof(result)) != sizeof(result)) - record_end(machine, "Out of space"); -} - - -/*------------------------------------------------- - record_write_uint32 - write a 32-bit value - to the record file --------------------------------------------------*/ - -static void record_write_uint32(running_machine &machine, UINT32 data) -{ - ioport_manager &portdata = machine.ioport(); - UINT32 result = LITTLE_ENDIANIZE_INT32(data); - - /* protect against NULL handles if previous reads fail */ - if (portdata.record_file == NULL) + // protect against NULL handles if previous reads fail + if (!m_record_file.is_open()) return; - /* read the value; if we fail, end playback */ - if (portdata.record_file->write(&result, sizeof(result)) != sizeof(result)) - record_end(machine, "Out of space"); + // read the value; if we fail, end playback + if (m_record_file.write(&value, sizeof(value)) != sizeof(value)) + record_end("Out of space"); } - -/*------------------------------------------------- - record_write_uint64 - write a 64-bit value - to the record file --------------------------------------------------*/ - -static void record_write_uint64(running_machine &machine, UINT64 data) +template<> +void ioport_manager::record_write<bool>(bool value) { - ioport_manager &portdata = machine.ioport(); - UINT64 result = LITTLE_ENDIANIZE_INT64(data); - - /* protect against NULL handles if previous reads fail */ - if (portdata.record_file == NULL) - return; - - /* read the value; if we fail, end playback */ - if (portdata.record_file->write(&result, sizeof(result)) != sizeof(result)) - record_end(machine, "Out of space"); + UINT8 byte = UINT8(value); + record_write(byte); } -/*------------------------------------------------- - record_init - initialize INP recording --------------------------------------------------*/ +//------------------------------------------------- +// record_init - initialize INP recording +//------------------------------------------------- -static void record_init(running_machine &machine) +void ioport_manager::record_init() { - const char *filename = machine.options().record(); - ioport_manager &portdata = machine.ioport(); - UINT8 header[INP_HEADER_SIZE]; - system_time systime; - - /* if no file, nothing to do */ + // if no file, nothing to do + const char *filename = machine().options().record(); if (filename[0] == 0) return; - /* open the record file */ - portdata.record_file = auto_alloc(machine, emu_file(machine.options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS)); - file_error filerr = portdata.record_file->open(filename); + // open the record file + file_error filerr = m_record_file.open(filename); assert_always(filerr == FILERR_NONE, "Failed to open file for recording"); - /* get the base time */ - machine.base_datetime(systime); + // get the base time + system_time systime; + machine().base_datetime(systime); - /* fill in the header */ - memset(header, 0, sizeof(header)); + // fill in the header + UINT8 header[INP_HEADER_SIZE] = { 0 }; memcpy(header, "MAMEINP\0", 8); header[0x08] = systime.time >> 0; header[0x09] = systime.time >> 8; @@ -3826,1042 +3595,922 @@ static void record_init(running_machine &machine) header[0x0f] = systime.time >> 56; header[0x10] = INP_HEADER_MAJVERSION; header[0x11] = INP_HEADER_MINVERSION; - strcpy((char *)header + 0x14, machine.system().name); + strcpy((char *)header + 0x14, machine().system().name); sprintf((char *)header + 0x20, "%s %s", emulator_info::get_appname(), build_version); - /* write it */ - portdata.record_file->write(header, sizeof(header)); + // write it + m_record_file.write(header, sizeof(header)); - /* enable compression */ - portdata.record_file->compress(FCOMPRESS_MEDIUM); + // enable compression + m_record_file.compress(FCOMPRESS_MEDIUM); } -/*------------------------------------------------- - record_end - end INP recording --------------------------------------------------*/ +//------------------------------------------------- +// record_end - end INP recording +//------------------------------------------------- -static void record_end(running_machine &machine, const char *message) +void ioport_manager::record_end(const char *message) { - ioport_manager &portdata = machine.ioport(); - - /* only applies if we have a live file */ - if (portdata.record_file != NULL) + // only applies if we have a live file + if (m_record_file.is_open()) { - /* close the file */ - auto_free(machine, portdata.record_file); - portdata.record_file = NULL; + // close the file + m_record_file.close(); - /* pop a message */ + // pop a message if (message != NULL) popmessage("Recording Ended\nReason: %s", message); } } -/*------------------------------------------------- - record_frame - start of frame callback for - recording --------------------------------------------------*/ +//------------------------------------------------- +// record_frame - start of frame callback for +// recording +//------------------------------------------------- -static void record_frame(running_machine &machine, attotime curtime) +void ioport_manager::record_frame(attotime curtime) { - ioport_manager &portdata = machine.ioport(); - - /* if recording, record information about the current frame */ - if (portdata.record_file != NULL) + // if recording, record information about the current frame + if (m_record_file.is_open()) { - /* first the absolute time */ - record_write_uint32(machine, curtime.seconds); - record_write_uint64(machine, curtime.attoseconds); + // first the absolute time + record_write(curtime.seconds); + record_write(curtime.attoseconds); - /* then the current speed */ - record_write_uint32(machine, machine.video().speed_percent() * (double)(1 << 20)); + // then the current speed + record_write(UINT32(machine().video().speed_percent() * double(1 << 20))); } } -/*------------------------------------------------- - record_port - per-port callback for record --------------------------------------------------*/ +//------------------------------------------------- +// record_port - per-port callback for record +//------------------------------------------------- -static void record_port(const input_port_config *port) +void ioport_manager::record_port(ioport_port &port) { - ioport_manager &portdata = port->machine().ioport(); - - /* if recording, store information about this port */ - if (portdata.record_file != NULL) + // if recording, store information about this port + if (m_record_file.is_open()) { - analog_field_state *analog; + // store the default value and digital state + record_write(port.live().defvalue); + record_write(port.live().digital); - /* store the default value and digital state */ - record_write_uint32(port->machine(), port->state->defvalue); - record_write_uint32(port->machine(), port->state->digital); - - /* loop over analog ports and save their data */ - for (analog = port->state->analoglist; analog != NULL; analog = analog->next) + // loop over analog ports and save their data + for (analog_field *analog = port.live().analoglist.first(); analog != NULL; analog = analog->next()) { - /* store current and previous values */ - record_write_uint32(port->machine(), analog->accum); - record_write_uint32(port->machine(), analog->previous); + // store current and previous values + record_write(analog->m_accum); + record_write(analog->m_previous); - /* store configuration information */ - record_write_uint32(port->machine(), analog->sensitivity); - record_write_uint8(port->machine(), analog->reverse); + // store configuration information + record_write(analog->m_sensitivity); + record_write(analog->m_reverse); } } } -int input_machine_has_keyboard(running_machine &machine) -{ - int have_keyboard = FALSE; - const input_field_config *field; - const input_port_config *port; - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - { - for (field = port->first_field(); field != NULL; field = field->next()) - { - // if we are at init, check IPT_KEYBOARD for inputx_init - if (!port->machine().ioport().safe_to_read && field->type == IPT_KEYBOARD) - { - have_keyboard = TRUE; - break; - } - // else, check if there is a keyboard and if such a keyboard is enabled - if (field->type == IPT_KEYBOARD && input_condition_true(field->machine(), &field->condition, field->port().owner())) - { - have_keyboard = TRUE; - break; - } - } - } - return have_keyboard; -} -/*************************************************************************** - CODE ASSEMBLING -***************************************************************************/ +//************************************************************************** +// I/O PORT CONFIGURER +//************************************************************************** -/*------------------------------------------------- - code_point_string - obtain a string representation of a - given code; used for logging and debugging --------------------------------------------------*/ +//------------------------------------------------- +// ioport_configurer - constructor +//------------------------------------------------- -static const char *code_point_string(running_machine &machine, unicode_char ch) +ioport_configurer::ioport_configurer(device_t &owner, ioport_list &portlist, astring &errorbuf) + : m_owner(owner), + m_portlist(portlist), + m_errorbuf(errorbuf), + m_curport(NULL), + m_curfield(NULL), + m_cursetting(NULL) { - static char buf[16]; - const char *result = buf; - - switch(ch) - { - /* check some magic values */ - case '\0': strcpy(buf, "\\0"); break; - case '\r': strcpy(buf, "\\r"); break; - case '\n': strcpy(buf, "\\n"); break; - case '\t': strcpy(buf, "\\t"); break; - - default: - if ((ch >= 32) && (ch < 128)) - { - /* seven bit ASCII is easy */ - buf[0] = (char) ch; - buf[1] = '\0'; - } - else if (ch >= UCHAR_MAMEKEY_BEGIN) - { - /* try to obtain a codename with code_name(); this can result in an empty string */ - input_code code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(ch - UCHAR_MAMEKEY_BEGIN)); - astring tempstr; - snprintf(buf, ARRAY_LENGTH(buf), "%s", machine.input().code_name(tempstr, code)); - } - else - { - /* empty string; resolve later */ - buf[0] = '\0'; - } - - /* did we fail to resolve? if so, we have a last resort */ - if (buf[0] == '\0') - snprintf(buf, ARRAY_LENGTH(buf), "U+%04X", (unsigned) ch); - break; - } - return result; } -/*------------------------------------------------- - scan_keys - scans through input ports and - sets up natural keyboard input mapping --------------------------------------------------*/ +//------------------------------------------------- +// string_from_token - convert an +// ioport_token to a default string +//------------------------------------------------- -static int scan_keys(running_machine &machine, const input_port_config *portconfig, inputx_code *codes, const input_port_config * *ports, const input_field_config * *shift_ports, int keys, int shift) +const char *ioport_configurer::string_from_token(const char *string) { - int code_count = 0; - const input_port_config *port; - const input_field_config *field; - unicode_char code; + // 0 is an invalid index + if (string == NULL) + return NULL; + + // if the index is greater than the count, assume it to be a pointer + if (FPTR(string) >= INPUT_STRING_COUNT) + return string; - assert(keys < NUM_SIMUL_KEYS); +#if FALSE // Set TRUE, If you want to take care missing-token or wrong-sorting - for (port = portconfig; port != NULL; port = port->next()) + // otherwise, scan the list for a matching string and return it { - for (field = port->first_field(); field != NULL; field = field->next()) - { - if (field->type == IPT_KEYBOARD) - { - code = get_keyboard_code(field, shift); - if (code != 0) - { - /* is this a shifter key? */ - if ((code >= UCHAR_SHIFT_BEGIN) && (code <= UCHAR_SHIFT_END)) - { - shift_ports[keys] = field; - code_count += scan_keys(machine, - portconfig, - codes ? &codes[code_count] : NULL, - ports, - shift_ports, - keys+1, - code - UCHAR_SHIFT_1 + 1); - } - else - { - /* not a shifter key; record normally */ - if (codes) - { - /* if we have a destination, record the codes used here */ - memcpy((void *) codes[code_count].field, shift_ports, sizeof(shift_ports[0]) * keys); - codes[code_count].ch = code; - codes[code_count].field[keys] = field; - } + int index; + for (index = 0; index < ARRAY_LENGTH(input_port_default_strings); index++) + if (input_port_default_strings[index].id == FPTR(string)) + return input_port_default_strings[index].string; + } + return "(Unknown Default)"; - /* increment the count */ - code_count++; +#else - if (LOG_INPUTX) - logerror("inputx: code=%i (%s) port=%p field->name='%s'\n", (int) code, code_point_string(machine, code), port, field->name); - } - } - } - } - } - return code_count; -} + return input_port_default_strings[FPTR(string)-1].string; +#endif +} -/*------------------------------------------------- - build_codes - given an input port table, create - a input code table useful for mapping unicode - chars --------------------------------------------------*/ +//------------------------------------------------- +// port_alloc - allocate a new port +//------------------------------------------------- -static inputx_code *build_codes(running_machine &machine, const input_port_config *portconfig) +void ioport_configurer::port_alloc(const char *tag) { - inputx_code *codes = NULL; - const input_port_config *ports[NUM_SIMUL_KEYS]; - const input_field_config *fields[NUM_SIMUL_KEYS]; - int code_count; + // create the full tag + astring fulltag; + m_owner.subtag(fulltag, tag); + + // add it to the list, and reset current field/setting + m_curport = &m_portlist.append(fulltag, *global_alloc(ioport_port(m_owner, fulltag))); + m_curfield = NULL; + m_cursetting = NULL; +} - /* first count the number of codes */ - code_count = scan_keys(machine, portconfig, NULL, ports, fields, 0, 0); - if (code_count > 0) - { - /* allocate the codes */ - codes = auto_alloc_array_clear(machine, inputx_code, code_count + 1); - /* and populate them */ - scan_keys(machine, portconfig, codes, ports, fields, 0, 0); - } - return codes; -} +//------------------------------------------------- +// port_modify - find an existing port and +// modify it +//------------------------------------------------- +void ioport_configurer::port_modify(const char *tag) +{ + // create the full tag + astring fulltag; + m_owner.subtag(fulltag, tag); + // find the existing port + m_curport = m_portlist.find(fulltag.cstr()); + if (m_curport == NULL) + throw emu_fatalerror("Requested to modify nonexistent port '%s'", fulltag.cstr()); + + // bump the modification count, and reset current field/setting + m_curport->m_modcount++; + m_curfield = NULL; + m_cursetting = NULL; +} -/*************************************************************************** - VALIDITY CHECKS -***************************************************************************/ -/*------------------------------------------------- - validate_natural_keyboard_statics - - validates natural keyboard static data --------------------------------------------------*/ +//------------------------------------------------- +// field_alloc - allocate a new field +//------------------------------------------------- -int validate_natural_keyboard_statics(void) +void ioport_configurer::field_alloc(ioport_type type, ioport_value defval, ioport_value mask, const char *name) { - int i; - int error = FALSE; - unicode_char last_char = 0; - const char_info *ci; + // make sure we have a port + if (m_curport == NULL) + throw emu_fatalerror("alloc_field called with no active port (mask=%X defval=%X)\n", mask, defval); \ - /* check to make sure that charinfo is in order */ - for (i = 0; i < ARRAY_LENGTH(charinfo); i++) - { - if (last_char >= charinfo[i].ch) - { - mame_printf_error("inputx: charinfo is out of order; 0x%08x should be higher than 0x%08x\n", charinfo[i].ch, last_char); - error = TRUE; - } - last_char = charinfo[i].ch; - } + // append the field + if (type != IPT_UNKNOWN && type != IPT_UNUSED) + m_curport->m_active |= mask; + m_curfield = &m_curport->m_fieldlist.append(*global_alloc(ioport_field(*m_curport, type, defval, mask, string_from_token(name)))); - /* check to make sure that I can look up everything on alternate_charmap */ - for (i = 0; i < ARRAY_LENGTH(charinfo); i++) - { - ci = find_charinfo(charinfo[i].ch); - if (ci != &charinfo[i]) - { - mame_printf_error("inputx: expected find_charinfo(0x%08x) to work properly\n", charinfo[i].ch); - error = TRUE; - } - } - return error; + // reset the current setting + m_cursetting = NULL; } +//------------------------------------------------- +// field_add_char - add a character to a field +//------------------------------------------------- -/*************************************************************************** - CORE IMPLEMENTATION -***************************************************************************/ - -static void clear_keybuffer(running_machine &machine) +void ioport_configurer::field_add_char(unicode_char ch) { - ioport_manager &portdata = machine.ioport(); - portdata.keybuffer.buffer = NULL; - portdata.queue_chars = NULL; - portdata.codes = NULL; + for (int index = 0; index < ARRAY_LENGTH(m_curfield->m_chars); index++) + if (m_curfield->m_chars[index] == 0) + { + m_curfield->m_chars[index] = ch; + break; + } } +//------------------------------------------------- +// field_add_code - add a character to a field +//------------------------------------------------- -static void setup_keybuffer(running_machine &machine) +void ioport_configurer::field_add_code(input_seq_type which, input_code code) { - ioport_manager &portdata = machine.ioport(); - portdata.inputx_timer = machine.scheduler().timer_alloc(FUNC(inputx_timerproc)); - portdata.keybuffer.begin_pos = 0; - portdata.keybuffer.end_pos = 0; - portdata.keybuffer.status_keydown = 0; - portdata.keybuffer.size = KEY_BUFFER_SIZE; - portdata.keybuffer.buffer = auto_alloc_array(machine, unicode_char, portdata.keybuffer.size); - machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(clear_keybuffer), &machine)); + m_curfield->m_seq[which] |= code; } +//------------------------------------------------- +// setting_alloc - allocate a new setting +//------------------------------------------------- -void inputx_init(running_machine &machine) +void ioport_configurer::setting_alloc(ioport_value value, const char *name) { - ioport_manager &portdata = machine.ioport(); - portdata.inputx_timer = NULL; - portdata.accept_char = NULL; - portdata.charqueue_empty = NULL; - clear_keybuffer(machine); + // make sure we have a field + if (m_curfield == NULL) + throw emu_fatalerror("alloc_setting called with no active field (value=%X name=%s)\n", value, name); - if (machine.debug_flags & DEBUG_FLAG_ENABLED) - { - debug_console_register_command(machine, "input", CMDFLAG_NONE, 0, 1, 1, execute_input); - debug_console_register_command(machine, "dumpkbd", CMDFLAG_NONE, 0, 0, 1, execute_dumpkbd); - } - - /* posting keys directly only makes sense for a computer */ - if (input_machine_has_keyboard(machine)) - { - portdata.codes = build_codes(machine, machine.ioport().first_port()); - setup_keybuffer(machine); - } + // append a new setting + m_curfield->m_settinglist.append(*global_alloc(ioport_setting(*m_curfield, value & m_curfield->mask(), string_from_token(name)))); } +//------------------------------------------------- +// set_condition - set the condition for either +// the current setting or field +//------------------------------------------------- -void inputx_setup_natural_keyboard( - running_machine &machine, - int (*queue_chars)(running_machine &machine, const unicode_char *text, size_t text_len), - int (*accept_char)(running_machine &machine, unicode_char ch), - int (*charqueue_empty)(running_machine &machine)) +void ioport_configurer::set_condition(ioport_condition::condition_t condition, const char *tag, ioport_value mask, ioport_value value) { - ioport_manager &portdata = machine.ioport(); - portdata.queue_chars = queue_chars; - portdata.accept_char = accept_char; - portdata.charqueue_empty = charqueue_empty; + ioport_condition &target = (m_cursetting != NULL) ? m_cursetting->condition() : m_curfield->condition(); + target.set(condition, tag, mask, value); } -int inputx_can_post(running_machine &machine) -{ - ioport_manager &portdata = machine.ioport(); - return portdata.queue_chars || portdata.codes; -} +//------------------------------------------------- +// onoff_alloc - allocate an on/off DIP switch +//------------------------------------------------- -static int can_post_key_directly(running_machine &machine, unicode_char ch) +void ioport_configurer::onoff_alloc(const char *name, ioport_value defval, ioport_value mask, const char *diplocation) { - ioport_manager &portdata = machine.ioport(); - int rc = FALSE; - const inputx_code *code; + // allocate a field normally + field_alloc(IPT_DIPSWITCH, defval, mask, name); - if (portdata.queue_chars) - { - rc = portdata.accept_char ? (*portdata.accept_char)(machine, ch) : TRUE; - } - else + // special case service mode + if (name == DEF_STR(Service_Mode)) { - code = find_code(portdata.codes, ch); - if (code) - rc = code->field[0] != NULL; + field_set_toggle(); + m_curfield->m_seq[SEQ_TYPE_STANDARD].set(KEYCODE_F2); } - return rc; + + // expand the diplocation + if (diplocation != NULL) + field_set_diplocation(diplocation); + + // allocate settings + setting_alloc(defval & mask, DEF_STR(Off)); + setting_alloc(~defval & mask, DEF_STR(On)); } -static int can_post_key_alternate(running_machine &machine, unicode_char ch) -{ - const char *s; - const char_info *ci; - unicode_char uchar; - int rc; +/*************************************************************************** + MISCELLANEOUS +***************************************************************************/ - ci = find_charinfo(ch); - s = ci ? ci->alternate : NULL; - if (!s) - return 0; +//------------------------------------------------- +// find - look up information about a particular +// character +//------------------------------------------------- - while(*s) +const char_info *char_info::find(unicode_char target) +{ + // perform a simple binary search to find the proper alternate + int low = 0; + int high = ARRAY_LENGTH(charinfo); + while (high > low) { - rc = uchar_from_utf8(&uchar, s, strlen(s)); - if (rc <= 0) - return 0; - if (!can_post_key_directly(machine, uchar)) - return 0; - s += rc; + int middle = (high + low) / 2; + unicode_char ch = charinfo[middle].ch; + if (ch < target) + low = middle + 1; + else if (ch > target) + high = middle; + else + return &charinfo[middle]; } - return 1; + return NULL; } -static attotime choose_delay(ioport_manager &portdata, unicode_char ch) -{ - if (portdata.current_rate != attotime::zero) - return portdata.current_rate; - attotime delay = attotime::zero; - if (portdata.queue_chars) - { - /* systems with queue_chars can afford a much smaller delay */ - delay = attotime::from_msec(10); - } - else - { - switch(ch) { - case '\r': - delay = attotime::from_msec(200); - break; +//------------------------------------------------- +// dynamic_field - constructor +//------------------------------------------------- - default: - delay = attotime::from_msec(50); - break; - } - } - return delay; +dynamic_field::dynamic_field(ioport_field &field) + : m_next(NULL), + m_field(field), + m_shift(0), + m_oldval(field.defvalue()) +{ + // fill in the data + for (ioport_value mask = field.mask(); !(mask & 1); mask >>= 1) + m_shift++; + m_oldval >>= m_shift; } +//------------------------------------------------- +// read - read the updated value and merge it +// into the target +//------------------------------------------------- -static void internal_post_key(running_machine &machine, unicode_char ch) +void dynamic_field::read(ioport_value &result) { - ioport_manager &portdata = machine.ioport(); - key_buffer *keybuf; + // skip if not enabled + if (!m_field.enabled()) + return; + + // call the callback to read a new value + ioport_value newval = m_field.m_read(m_field, m_field.m_read_param); + m_oldval = newval; + + // merge in the bits (don't invert yet, as all digitals are inverted together) + result = (result & ~m_field.mask()) | ((newval << m_shift) & m_field.mask()); +} - keybuf = get_buffer(machine); - /* need to start up the timer? */ - if (keybuf->begin_pos == keybuf->end_pos) - { - portdata.inputx_timer->adjust(choose_delay(portdata, ch)); - keybuf->status_keydown = 0; - } +//------------------------------------------------- +// write - track a change to a value and call +// the write callback if there's something new +//------------------------------------------------- + +void dynamic_field::write(ioport_value newval) +{ + // skip if not enabled + if (!m_field.enabled()) + return; - keybuf->buffer[keybuf->end_pos++] = ch; - if ((keybuf->end_pos+1) % keybuf->size == keybuf->begin_pos) + // if the bits have changed, call the handler + newval = ((newval ^ m_field.defvalue()) & m_field.mask()) >> m_shift; + if (m_oldval != newval) { - // Buffer full - unicode_char *old_buffer = keybuf->buffer; - keybuf->size = keybuf->size + KEY_BUFFER_SIZE; - keybuf->buffer = auto_alloc_array(machine, unicode_char, keybuf->size); - for( int i = keybuf->begin_pos; i <= keybuf->end_pos; i++ ) - { - keybuf->buffer[i] = old_buffer[i]; - } - auto_free(machine, old_buffer); + m_field.m_write(m_field, m_field.m_write_param, m_oldval, newval); + m_oldval = newval; } - keybuf->end_pos %= keybuf->size; } +//------------------------------------------------- +// analog_field - constructor +//------------------------------------------------- -static int buffer_full(running_machine &machine) -{ - key_buffer *keybuf; - keybuf = get_buffer(machine); - return ((keybuf->end_pos + 1) % keybuf->size) == keybuf->begin_pos; -} +analog_field::analog_field(ioport_field &field) + : m_next(NULL), + m_field(field), + m_shift(0), + m_adjdefvalue(field.defvalue() & field.mask()), + m_adjmin(field.minval() & field.mask()), + m_adjmax(field.maxval() & field.mask()), + m_sensitivity(field.sensitivity()), + m_reverse(field.analog_reverse()), + m_delta(field.delta()), + m_centerdelta(field.centerdelta()), + m_accum(0), + m_previous(0), + m_previousanalog(0), + m_minimum(INPUT_ABSOLUTE_MIN), + m_maximum(INPUT_ABSOLUTE_MAX), + m_center(0), + m_reverse_val(0), + m_scalepos(0), + m_scaleneg(0), + m_keyscalepos(0), + m_keyscaleneg(0), + m_positionalscale(0), + m_absolute(false), + m_wraps(false), + m_autocenter(false), + m_single_scale(false), + m_interpolate(false), + m_lastdigital(false) +{ + // compute the shift amount and number of bits + for (ioport_value mask = field.mask(); !(mask & 1); mask >>= 1) + m_shift++; + + // initialize core data + m_adjdefvalue >>= m_shift; + m_adjmin >>= m_shift; + m_adjmax >>= m_shift; + + // set basic parameters based on the configured type + switch (field.type()) + { + // paddles and analog joysticks are absolute and autocenter + case IPT_AD_STICK_X: + case IPT_AD_STICK_Y: + case IPT_AD_STICK_Z: + case IPT_PADDLE: + case IPT_PADDLE_V: + m_absolute = true; + m_autocenter = true; + m_interpolate = !field.analog_reset(); + break; + // pedals start at and autocenter to the min range + case IPT_PEDAL: + case IPT_PEDAL2: + case IPT_PEDAL3: + m_center = INPUT_ABSOLUTE_MIN; + m_accum = apply_inverse_sensitivity(m_center); + m_absolute = true; + m_autocenter = true; + m_interpolate = !field.analog_reset(); + break; + // lightguns are absolute as well, but don't autocenter and don't interpolate their values + case IPT_LIGHTGUN_X: + case IPT_LIGHTGUN_Y: + m_absolute = true; + m_autocenter = false; + m_interpolate = false; + break; -static void inputx_postn_rate(running_machine &machine, const unicode_char *text, size_t text_len, attotime rate) -{ - ioport_manager &portdata = machine.ioport(); - int last_cr = 0; - unicode_char ch; - const char *s; - const char_info *ci; - const inputx_code *code; + // positional devices are absolute, but can also wrap like relative devices + // set each position to be 512 units + case IPT_POSITIONAL: + case IPT_POSITIONAL_V: + m_positionalscale = compute_scale(field.maxval(), INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN); + m_adjmin = 0; + m_adjmax = field.maxval() - 1; + m_wraps = field.analog_wraps(); + m_autocenter = !m_wraps; + break; - portdata.current_rate = rate; + // dials, mice and trackballs are relative devices + // these have fixed "min" and "max" values based on how many bits are in the port + // in addition, we set the wrap around min/max values to 512 * the min/max values + // this takes into account the mapping that one mouse unit ~= 512 analog units + case IPT_DIAL: + case IPT_DIAL_V: + case IPT_TRACKBALL_X: + case IPT_TRACKBALL_Y: + case IPT_MOUSE_X: + case IPT_MOUSE_Y: + m_absolute = false; + m_wraps = true; + m_interpolate = !field.analog_reset(); + break; + + default: + fatalerror("Unknown analog port type -- don't know if it is absolute or not"); + break; + } - if (inputx_can_post(machine)) + // further processing for absolute controls + if (m_absolute) { - while((text_len > 0) && !buffer_full(machine)) - { - ch = *(text++); - text_len--; + // if the default value is pegged at the min or max, use a single scale value for the whole axis + m_single_scale = (m_adjdefvalue == m_adjmin) || (m_adjdefvalue == m_adjmax); - /* change all eolns to '\r' */ - if ((ch != '\n') || !last_cr) - { - if (ch == '\n') - ch = '\r'; - else - last_cr = (ch == '\r'); + // if not "single scale", compute separate scales for each side of the default + if (!m_single_scale) + { + // unsigned + m_scalepos = compute_scale(m_adjmax - m_adjdefvalue, INPUT_ABSOLUTE_MAX - 0); + m_scaleneg = compute_scale(m_adjdefvalue - m_adjmin, 0 - INPUT_ABSOLUTE_MIN); - if (LOG_INPUTX) - { - code = find_code(portdata.codes, ch); - logerror("inputx_postn(): code=%i (%s) field->name='%s'\n", (int) ch, code_point_string(machine, ch), (code && code->field[0]) ? code->field[0]->name : "<null>"); - } + if (m_adjmin > m_adjmax) + m_scaleneg = -m_scaleneg; - if (can_post_key_directly(machine, ch)) - { - /* we can post this key in the queue directly */ - internal_post_key(machine, ch); - } - else if (can_post_key_alternate(machine, ch)) - { - /* we can post this key with an alternate representation */ - ci = find_charinfo(ch); - assert(ci && ci->alternate); - s = ci->alternate; - while(*s) - { - s += uchar_from_utf8(&ch, s, strlen(s)); - internal_post_key(machine, ch); - } - } - } - else - { - last_cr = 0; - } + // reverse point is at center + m_reverse_val = 0; } - } -} - - + else + { + // single axis that increases from default + m_scalepos = compute_scale(m_adjmax - m_adjmin, INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN); -static TIMER_CALLBACK(inputx_timerproc) -{ - ioport_manager &portdata = machine.ioport(); - key_buffer *keybuf; - attotime delay; + // move from default + if (m_adjdefvalue == m_adjmax) + m_scalepos = -m_scalepos; - keybuf = get_buffer(machine); + // make the scaling the same for easier coding when we need to scale + m_scaleneg = m_scalepos; - if (portdata.queue_chars) - { - /* the driver has a queue_chars handler */ - while((keybuf->begin_pos != keybuf->end_pos) && (*portdata.queue_chars)(machine, &keybuf->buffer[keybuf->begin_pos], 1)) - { - keybuf->begin_pos++; - keybuf->begin_pos %= keybuf->size; - - if (portdata.current_rate != attotime::zero) - break; + // reverse point is at max + m_reverse_val = m_maximum; } } + + // relative and positional controls all map directly with a 512x scale factor else { - /* the driver does not have a queue_chars handler */ - if (keybuf->status_keydown) - { - keybuf->status_keydown = FALSE; - keybuf->begin_pos++; - keybuf->begin_pos %= keybuf->size; - } + // The relative code is set up to allow specifing PORT_MINMAX and default values. + // The validity checks are purposely set up to not allow you to use anything other + // a default of 0 and PORT_MINMAX(0,mask). This is in case the need arises to use + // this feature in the future. Keeping the code in does not hurt anything. + if (m_adjmin > m_adjmax) + // adjust for signed + m_adjmin = -m_adjmin; + + if (m_wraps) + m_adjmax++; + + m_minimum = (m_adjmin - m_adjdefvalue) * INPUT_RELATIVE_PER_PIXEL; + m_maximum = (m_adjmax - m_adjdefvalue) * INPUT_RELATIVE_PER_PIXEL; + + // make the scaling the same for easier coding when we need to scale + m_scaleneg = m_scalepos = compute_scale(1, INPUT_RELATIVE_PER_PIXEL); + + if (m_field.analog_reset()) + // delta values reverse from center + m_reverse_val = 0; else { - keybuf->status_keydown = TRUE; - } - } + // positional controls reverse from their max range + m_reverse_val = m_maximum + m_minimum; - /* need to make sure timerproc is called again if buffer not empty */ - if (keybuf->begin_pos != keybuf->end_pos) - { - delay = choose_delay(portdata, keybuf->buffer[keybuf->begin_pos]); - portdata.inputx_timer->adjust(delay); + // relative controls reverse from 1 past their max range + if (m_wraps) + m_reverse_val -= INPUT_RELATIVE_PER_PIXEL; + } } -} -int inputx_is_posting(running_machine &machine) -{ - ioport_manager &portdata = machine.ioport(); - const key_buffer *keybuf; - keybuf = get_buffer(machine); - return (keybuf->begin_pos != keybuf->end_pos) || (portdata.charqueue_empty && !(*portdata.charqueue_empty)(machine)); + // compute scale for keypresses + m_keyscalepos = recip_scale(m_scalepos); + m_keyscaleneg = recip_scale(m_scaleneg); } -/*************************************************************************** - Coded input +//------------------------------------------------- +// apply_min_max - clamp the given input value to +// the appropriate min/max for the analog control +//------------------------------------------------- -***************************************************************************/ -static void inputx_postc_rate(running_machine &machine, unicode_char ch, attotime rate); - -static void inputx_postn_coded_rate(running_machine &machine, const char *text, size_t text_len, attotime rate) +inline ioport_value analog_field::apply_min_max(ioport_value value) const { - size_t i, j, key_len, increment; - unicode_char ch; + // take the analog minimum and maximum values and apply the inverse of the + // sensitivity so that we can clamp against them before applying sensitivity + INT32 adjmin = apply_inverse_sensitivity(m_minimum); + INT32 adjmax = apply_inverse_sensitivity(m_maximum); - static const struct - { - const char *key; - unicode_char code; - } codes[] = + // for absolute devices, clamp to the bounds absolutely + if (!m_wraps) { - { "BACKSPACE", 8 }, - { "BS", 8 }, - { "BKSP", 8 }, - { "DEL", UCHAR_MAMEKEY(DEL) }, - { "DELETE", UCHAR_MAMEKEY(DEL) }, - { "END", UCHAR_MAMEKEY(END) }, - { "ENTER", 13 }, - { "ESC", '\033' }, - { "HOME", UCHAR_MAMEKEY(HOME) }, - { "INS", UCHAR_MAMEKEY(INSERT) }, - { "INSERT", UCHAR_MAMEKEY(INSERT) }, - { "PGDN", UCHAR_MAMEKEY(PGDN) }, - { "PGUP", UCHAR_MAMEKEY(PGUP) }, - { "SPACE", 32 }, - { "TAB", 9 }, - { "F1", UCHAR_MAMEKEY(F1) }, - { "F2", UCHAR_MAMEKEY(F2) }, - { "F3", UCHAR_MAMEKEY(F3) }, - { "F4", UCHAR_MAMEKEY(F4) }, - { "F5", UCHAR_MAMEKEY(F5) }, - { "F6", UCHAR_MAMEKEY(F6) }, - { "F7", UCHAR_MAMEKEY(F7) }, - { "F8", UCHAR_MAMEKEY(F8) }, - { "F9", UCHAR_MAMEKEY(F9) }, - { "F10", UCHAR_MAMEKEY(F10) }, - { "F11", UCHAR_MAMEKEY(F11) }, - { "F12", UCHAR_MAMEKEY(F12) }, - { "QUOTE", '\"' } - }; + if (value > adjmax) + value = adjmax; + else if (value < adjmin) + value = adjmin; + } - i = 0; - while(i < text_len) + // for relative devices, wrap around when we go past the edge + else { - ch = text[i]; - increment = 1; - - if (ch == '{') - { - for (j = 0; j < ARRAY_LENGTH(codes); j++) - { - key_len = strlen(codes[j].key); - if (i + key_len + 2 <= text_len) - { - if (!core_strnicmp(codes[j].key, &text[i + 1], key_len) && (text[i + key_len + 1] == '}')) - { - ch = codes[j].code; - increment = key_len + 2; - } - } - } - } - - if (ch) - inputx_postc_rate(machine, ch, rate); - i += increment; + INT32 range = adjmax - adjmin; + // rolls to other end when 1 position past end. + value = (value - adjmin) % range; + if (value < 0) + value += range; + value += adjmin; } + + return value; } +//------------------------------------------------- +// apply_sensitivity - apply a sensitivity +// adjustment for a current value +//------------------------------------------------- -/*************************************************************************** +inline ioport_value analog_field::apply_sensitivity(ioport_value value) const +{ + return INT32((INT64(value) * m_sensitivity) / 100.0 + 0.5); +} - Alternative calls -***************************************************************************/ +//------------------------------------------------- +// apply_inverse_sensitivity - reverse-apply the +// sensitivity adjustment for a current value +//------------------------------------------------- -static void inputx_postc_rate(running_machine &machine, unicode_char ch, attotime rate) +inline ioport_value analog_field::apply_inverse_sensitivity(ioport_value value) const { - inputx_postn_rate(machine, &ch, 1, rate); + return INT32((INT64(value) * 100) / m_sensitivity); } -void inputx_postc(running_machine &machine, unicode_char ch) + +//------------------------------------------------- +// apply_settings - return the value of an +// analog input +//------------------------------------------------- + +ioport_value analog_field::apply_settings(ioport_value value) const { - inputx_postc_rate(machine, ch, attotime::zero); + // apply the min/max and then the sensitivity + value = apply_min_max(value); + value = apply_sensitivity(value); + + // apply reversal if needed + if (m_reverse) + value = m_reverse_val - value; + else if (m_single_scale) + // it's a pedal or the default value is equal to min/max + // so we need to adjust the center to the minimum + value -= INPUT_ABSOLUTE_MIN; + + // map differently for positive and negative values + if (value >= 0) + value = apply_scale(value, m_scalepos); + else + value = apply_scale(value, m_scaleneg); + value += m_adjdefvalue; + + return value; } -static void inputx_postn_utf8_rate(running_machine &machine, const char *text, size_t text_len, attotime rate) + +//------------------------------------------------- +// frame_update - update the internals of a +// single analog field periodically +//------------------------------------------------- + +void analog_field::frame_update(running_machine &machine) { - size_t len = 0; - unicode_char buf[256]; - unicode_char c; - int rc; + // clamp the previous value to the min/max range and remember it + m_previous = m_accum = apply_min_max(m_accum); - while(text_len > 0) + // get the new raw analog value and its type + input_item_class itemclass; + INT32 rawvalue = machine.input().seq_axis_value(m_field.seq(SEQ_TYPE_STANDARD), itemclass); + + // if we got an absolute input, it overrides everything else + if (itemclass == ITEM_CLASS_ABSOLUTE) { - if (len == ARRAY_LENGTH(buf)) + if (m_previousanalog != rawvalue) { - inputx_postn_rate(machine, buf, len, attotime::zero); - len = 0; - } + // only update if analog value changed + m_previousanalog = rawvalue; + + // apply the inverse of the sensitivity to the raw value so that + // it will still cover the full min->max range requested after + // we apply the sensitivity adjustment + if (m_absolute || m_field.analog_reset()) + { + // if port is absolute, then just return the absolute data supplied + m_accum = apply_inverse_sensitivity(rawvalue); + } + else if (m_positionalscale != 0) + { + // 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, + // it moves through all the positions + rawvalue = apply_scale(rawvalue - INPUT_ABSOLUTE_MIN, m_positionalscale) * INPUT_RELATIVE_PER_PIXEL + m_minimum; + + // clamp the high value so it does not roll over + rawvalue = MIN(rawvalue, m_maximum); + 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; - rc = uchar_from_utf8(&c, text, text_len); - if (rc < 0) + m_lastdigital = false; + // do not bother with other control types if the analog data is changing + return; + } + else { - rc = 1; - c = INVALID_CHAR; + // we still have to update fake relative from joystick control + if (!m_absolute && m_positionalscale == 0) + m_accum += rawvalue; } - text += rc; - text_len -= rc; - buf[len++] = c; } - inputx_postn_rate(machine, buf, len, rate); -} - -void inputx_post_utf8(running_machine &machine, const char *text) -{ - inputx_postn_utf8_rate(machine, text, strlen(text), attotime::zero); -} - -void inputx_post_utf8_rate(running_machine &machine, const char *text, attotime rate) -{ - inputx_postn_utf8_rate(machine, text, strlen(text), rate); -} - -/*************************************************************************** - Other stuff + // 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 + INT32 delta = 0; + if (itemclass == ITEM_CLASS_RELATIVE && rawvalue != 0) + { + delta = rawvalue; + m_lastdigital = false; + } - This stuff is here more out of convienience than anything else -***************************************************************************/ + INT64 keyscale = (m_accum >= 0) ? m_keyscalepos : m_keyscaleneg; -int input_classify_port(const input_field_config *field) -{ - int result; + // if the decrement code sequence is pressed, add the key delta to + // the accumulated delta; also note that the last input was a digital one + bool keypressed = false; + if (machine.input().seq_pressed(m_field.seq(SEQ_TYPE_DECREMENT))) + { + keypressed = true; + if (m_delta != 0) + delta -= apply_scale(m_delta, keyscale); + else if (!m_lastdigital) + // decrement only once when first pressed + delta -= apply_scale(1, keyscale); + m_lastdigital = true; + } - switch(field->type) + // same for the increment code sequence + if (machine.input().seq_pressed(m_field.seq(SEQ_TYPE_INCREMENT))) { - case IPT_JOYSTICK_UP: - case IPT_JOYSTICK_DOWN: - case IPT_JOYSTICK_LEFT: - case IPT_JOYSTICK_RIGHT: - case IPT_JOYSTICKLEFT_UP: - case IPT_JOYSTICKLEFT_DOWN: - case IPT_JOYSTICKLEFT_LEFT: - case IPT_JOYSTICKLEFT_RIGHT: - case IPT_JOYSTICKRIGHT_UP: - case IPT_JOYSTICKRIGHT_DOWN: - case IPT_JOYSTICKRIGHT_LEFT: - case IPT_JOYSTICKRIGHT_RIGHT: - case IPT_BUTTON1: - case IPT_BUTTON2: - case IPT_BUTTON3: - case IPT_BUTTON4: - case IPT_BUTTON5: - case IPT_BUTTON6: - case IPT_BUTTON7: - case IPT_BUTTON8: - case IPT_BUTTON9: - case IPT_BUTTON10: - case IPT_AD_STICK_X: - case IPT_AD_STICK_Y: - case IPT_AD_STICK_Z: - case IPT_TRACKBALL_X: - case IPT_TRACKBALL_Y: - case IPT_LIGHTGUN_X: - case IPT_LIGHTGUN_Y: - case IPT_MOUSE_X: - case IPT_MOUSE_Y: - case IPT_START: - case IPT_SELECT: - result = INPUT_CLASS_CONTROLLER; - break; + keypressed = true; + if (m_delta) + delta += apply_scale(m_delta, keyscale); + else if (!m_lastdigital) + // increment only once when first pressed + delta += apply_scale(1, keyscale); + m_lastdigital = true; + } - case IPT_KEYPAD: - case IPT_KEYBOARD: - result = INPUT_CLASS_KEYBOARD; - break; + // if resetting is requested, clear the accumulated position to 0 before + // applying the deltas so that we only return this frame's delta + // note that centering only works for relative controls + // no need to check if absolute here because it is checked by the validity tests + if (m_field.analog_reset()) + m_accum = 0; - case IPT_CONFIG: - result = INPUT_CLASS_CONFIG; - break; + // apply the delta to the accumulated value + m_accum += delta; - case IPT_DIPSWITCH: - result = INPUT_CLASS_DIPSWITCH; - break; + // if our last movement was due to a digital input, and if this control + // type autocenters, and if neither the increment nor the decrement seq + // was pressed, apply autocentering + if (m_autocenter) + { + INT32 center = apply_inverse_sensitivity(m_center); + if (m_lastdigital && !keypressed) + { + // autocenter from positive values + if (m_accum >= center) + { + m_accum -= apply_scale(m_centerdelta, m_keyscalepos); + if (m_accum < center) + { + m_accum = center; + m_lastdigital = false; + } + } - case 0: - if (field->name && (field->name != (const char *) -1)) - result = INPUT_CLASS_MISC; + // autocenter from negative values else - result = INPUT_CLASS_INTERNAL; - break; - - default: - result = INPUT_CLASS_INTERNAL; - break; + { + m_accum += apply_scale(m_centerdelta, m_keyscaleneg); + if (m_accum > center) + { + m_accum = center; + m_lastdigital = false; + } + } + } } - return result; + else if (!keypressed) + m_lastdigital = false; } +//------------------------------------------------- +// read - read the current value and insert into +// the provided ioport_value +//------------------------------------------------- -int input_player_number(const input_field_config *port) +void analog_field::read(ioport_value &result) { - return port->player; -} + // do nothing if we're not enabled + if (!m_field.enabled()) + return; + // start with the raw value + INT32 value = m_accum; + // interpolate if appropriate and if time has passed since the last update + if (m_interpolate) + value = manager().frame_interpolate(m_previous, m_accum); -/*------------------------------------------------- - input_has_input_class - checks to see if a - particular input class is present --------------------------------------------------*/ + // apply standard analog settings + value = apply_settings(value); -int input_has_input_class(running_machine &machine, int inputclass) -{ - const input_port_config *port; - const input_field_config *field; + // remap the value if needed + if (m_field.remap_table() != NULL) + value = m_field.remap_table()[value]; - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - { - for (field = port->first_field(); field != NULL; field = field->next()) - { - if (input_classify_port(field) == inputclass) - return TRUE; - } - } - return FALSE; -} + // invert bits if needed + if (m_field.analog_invert()) + value = ~value; + // insert into the port + result = (result & ~m_field.mask()) | ((value << m_shift) & m_field.mask()); +} -/*------------------------------------------------- - input_count_players - counts the number of - active players --------------------------------------------------*/ +//------------------------------------------------- +// crosshair_read - read a value for crosshairs, +// scaled between 0 and 1 +//------------------------------------------------- -int input_count_players(running_machine &machine) +float analog_field::crosshair_read() { - const input_port_config *port; - const input_field_config *field; - int joystick_count; - - joystick_count = 0; - for (port = machine.ioport().first_port(); port != NULL; port = port->next()) - { - for (field = port->first_field(); field != NULL; field = field->next()) - { - if (input_classify_port(field) == INPUT_CLASS_CONTROLLER) - { - if (joystick_count <= field->player + 1) - joystick_count = field->player + 1; - } - } - } - return joystick_count; + INT32 rawvalue = apply_settings(m_accum) & (m_field.mask() >> m_shift); + return float(rawvalue - m_adjmin) / float(m_adjmax - m_adjmin); } /*************************************************************************** - DEBUGGER SUPPORT + TOKENIZATION HELPERS ***************************************************************************/ -/*------------------------------------------------- - execute_input - debugger command to enter - natural keyboard input --------------------------------------------------*/ +//------------------------------------------------- +// token_to_input_type - convert a string token +// to an input field type and player +//------------------------------------------------- -static void execute_input(running_machine &machine, int ref, int params, const char *param[]) +ioport_type ioport_manager::token_to_input_type(const char *string, int &player) const { - inputx_postn_coded_rate(machine, param[0], strlen(param[0]), attotime::zero); -} - - - -/*------------------------------------------------- - execute_dumpkbd - debugger command to natural - keyboard codes --------------------------------------------------*/ + // check for our failsafe case first + int ipnum; + if (sscanf(string, "TYPE_OTHER(%d,%d)", &ipnum, &player) == 2) + return ioport_type(ipnum); -static void execute_dumpkbd(running_machine &machine, int ref, int params, const char *param[]) -{ - inputx_code *codes = machine.ioport().codes; - const char *filename; - FILE *file = NULL; - const inputx_code *code; - char buffer[512]; - size_t pos; - int i, j; - size_t left_column_width = 24; - - /* was there a file specified? */ - filename = (params > 0) ? param[0] : NULL; - if (filename != NULL) - { - /* if so, open it */ - file = fopen(filename, "w"); - if (file == NULL) + // find the token in the list + for (input_type_entry *entry = m_typelist.first(); entry != NULL; entry = entry->next()) + if (entry->token() != NULL && !strcmp(entry->token(), string)) { - debug_console_printf(machine, "Cannot open \"%s\"\n", filename); - return; + player = entry->player(); + return entry->type(); } - } - - if ((codes != NULL) && (codes[0].ch != 0)) - { - /* loop through all codes */ - for (i = 0; codes[i].ch; i++) - { - code = &codes[i]; - pos = 0; - /* describe the character code */ - pos += snprintf(&buffer[pos], ARRAY_LENGTH(buffer) - pos, "%08X (%s) ", - code->ch, - code_point_string(machine, code->ch)); - - /* pad with spaces */ - while(pos < left_column_width) - buffer[pos++] = ' '; - buffer[pos] = '\0'; + // if we fail, return IPT_UNKNOWN + player = 0; + return IPT_UNKNOWN; +} - /* identify the keys used */ - for (j = 0; j < ARRAY_LENGTH(code->field) && (code->field[j] != NULL); j++) - { - pos += snprintf(&buffer[pos], ARRAY_LENGTH(buffer) - pos, "%s'%s'", - (j > 0) ? ", " : "", - code->field[j]->name); - } - /* and output it as appropriate */ - if (file != NULL) - fprintf(file, "%s\n", buffer); - else - debug_console_printf(machine, "%s\n", buffer); - } - } - else - { - debug_console_printf(machine, "No natural keyboard support\n"); - } +//------------------------------------------------- +// input_type_to_token - convert an input field +// type and player to a string token +//------------------------------------------------- - /* cleanup */ - if (file != NULL) - fclose(file); +const char *ioport_manager::input_type_to_token(astring &string, ioport_type type, int player) +{ + // look up the port and return the token + input_type_entry *entry = m_type_to_entry[type][player]; + if (entry != NULL) + return string.cpy(entry->token()); + // if that fails, carry on + return string.format("TYPE_OTHER(%d,%d)", type, player); } +//------------------------------------------------- +// token_to_seq_type - convert a string to +// a sequence type +//------------------------------------------------- -input_port_config *ioconfig_alloc_port(ioport_list &portlist, device_t &device, const char *tag) +input_seq_type ioport_manager::token_to_seq_type(const char *string) { - astring fulltag; - device.subtag(fulltag, tag); - return &portlist.append(fulltag, *global_alloc(input_port_config(device, fulltag))); + // look up the string in the table of possible sequence types and return the index + for (int seqindex = 0; seqindex < ARRAY_LENGTH(seqtypestrings); seqindex++) + if (!mame_stricmp(string, seqtypestrings[seqindex])) + return input_seq_type(seqindex); + return SEQ_TYPE_INVALID; } -input_port_config *ioconfig_modify_port(ioport_list &portlist, device_t &device, const char *tag) -{ - astring fulltag; - device.subtag(fulltag, tag); - input_port_config *port = portlist.find(fulltag.cstr()); - if (port == NULL) - throw emu_fatalerror("Requested to modify nonexistent port '%s'", fulltag.cstr()); - port->bump_modcount(); - return port; -} -input_field_config *ioconfig_alloc_field(input_port_config &port, int type, input_port_value defval, input_port_value mask, const char *name) -{ - if (&port == NULL) - throw emu_fatalerror("INPUT_TOKEN_FIELD encountered with no active port (mask=%X defval=%X)\n", mask, defval); \ - if (type != IPT_UNKNOWN && type != IPT_UNUSED) - port.active |= mask; - if (type == IPT_DIPSWITCH || type == IPT_CONFIG) - defval = port_default_value(port.tag(), mask, defval, port.owner()); - return &port.fieldlist().append(*global_alloc(input_field_config(port, type, defval, mask, input_port_string_from_token(name)))); -} -input_field_config *ioconfig_alloc_onoff(input_port_config &port, const char *name, input_port_value defval, input_port_value mask, const char *diplocation, astring &errorbuf) -{ - input_field_config *curfield = ioconfig_alloc_field(port, IPT_DIPSWITCH, defval, mask, name); - if (name == DEF_STR(Service_Mode)) - { - curfield->flags |= FIELD_FLAG_TOGGLE; - curfield->seq[SEQ_TYPE_STANDARD].set(KEYCODE_F2); - } - if (diplocation != NULL) - diplocation_list_alloc(*curfield, diplocation, errorbuf); - ioconfig_alloc_setting(*curfield, defval & mask, DEF_STR(Off)); - ioconfig_alloc_setting(*curfield, ~defval & mask, DEF_STR(On)); - return curfield; -} +//------------------------------------------------- +// validate_natural_keyboard_statics - +// validates natural keyboard static data +//------------------------------------------------- -input_setting_config *ioconfig_alloc_setting(input_field_config &field, input_port_value value, const char *name) +/* +int validate_natural_keyboard_statics(void) { - return &field.settinglist().append(*global_alloc(input_setting_config(field, value, input_port_string_from_token(name)))); -} + int i; + int error = FALSE; + unicode_char last_char = 0; + const char_info *ci; -void ioconfig_field_add_char(input_field_config &field, unicode_char ch, astring &errorbuf) -{ - for (int index = 0; index < ARRAY_LENGTH(field.chars); index++) - if (field.chars[index] == 0) + // check to make sure that charinfo is in order + for (i = 0; i < ARRAY_LENGTH(charinfo); i++) + { + if (last_char >= charinfo[i].ch) { - field.chars[index] = ch; - break; + mame_printf_error("inputx: charinfo is out of order; 0x%08x should be higher than 0x%08x\n", charinfo[i].ch, last_char); + error = TRUE; } -} + last_char = charinfo[i].ch; + } -void ioconfig_add_code(input_field_config &field, int which, input_code code) -{ - field.seq[which] |= code; + // check to make sure that I can look up everything on alternate_charmap + for (i = 0; i < ARRAY_LENGTH(charinfo); i++) + { + ci = char_info::find(charinfo[i].ch); + if (ci != &charinfo[i]) + { + mame_printf_error("ioport: expected char_info::find(0x%08x) to work properly\n", charinfo[i].ch); + error = TRUE; + } + } + return error; } +*/ - -input_type_entry::input_type_entry(UINT32 _type, ioport_group _group, int _player, const char *_token, const char *_name, input_seq standard) - : type(_type), - group(_group), - player(_player), - token(_token), - name(_name), - m_next(NULL) -{ - defseq[SEQ_TYPE_STANDARD] = seq[SEQ_TYPE_STANDARD] = standard; -} - -input_type_entry::input_type_entry(UINT32 _type, ioport_group _group, int _player, const char *_token, const char *_name, input_seq standard, input_seq decrement, input_seq increment) - : type(_type), - group(_group), - player(_player), - token(_token), - name(_name), - m_next(NULL) -{ - defseq[SEQ_TYPE_STANDARD] = seq[SEQ_TYPE_STANDARD] = standard; - defseq[SEQ_TYPE_INCREMENT] = seq[SEQ_TYPE_INCREMENT] = increment; - defseq[SEQ_TYPE_DECREMENT] = seq[SEQ_TYPE_DECREMENT] = decrement; -} |