#include <stdlib.h>
#include <string.h>

#include "quasi88.h"
#include "utility.h"
#include "fname.h"
#include "keyboard.h"
#include "romaji.h"
#include "event.h"

#include "soundbd.h"    /* sound_reg[]                  */
#include "graph.h"      /* set_key_and_mouse()          */
#include "pc88cpu.h"    /* z80main_cpu                  */
#include "intr.h"       /* state_of_cpu                 */

#include "snddrv.h"     /* xmame_XXX                    */
#include "wait.h"       /* wait_rate                    */
#include "pc88main.h"   /* boot_clock_4mhz              */

#include "drive.h"

#include "emu.h"
#include "statusbar.h"
#include "toolbar.h"
#include "pause.h"
#include "menu.h"
#include "screen.h"
#include "snapshot.h"

#include "suspend.h"

/******************************************************************************
 *
 *****************************************************************************/

/*
 * 内部状態 (ステートセーブ不要?)
 */

static int mouse_dx;					/* マウス x方向移動量 */
static int mouse_dy;					/* マウス y方向移動量 */

static int mouse_sx;					/* シリアルマウス x方向移動量 */
static int mouse_sy;					/* シリアルマウス y方向移動量 */
static int mouse_sb;					/* シリアルマウス ボタン */


/* IN(00h)..(0Eh) キースキャン
 * key_scan[0]..[14] が、 I/Oポート 00H..0EH と等価。
 * key_scan[15] は、ジョイスティックの一時ワークに使用。
 * key_scan[16]..[17] は、シフトキーの一時ワークに使用。*/
unsigned char key_scan[ 0x12 ];

static int key_func[ KEY88_END ];		/* キーの代替コード,機能割当 */


/*
 * PC88状態 / 内部状態
 */

static int jop1_step;				/* 汎用I/Oポートのリードステップ */
static int jop1_dx;					/* 汎用I/Oポートの値 (マウス x方向変位) */
static int jop1_dy;					/* 汎用I/Oポートの値 (マウス y方向変位) */
static int jop1_time;				/* 汎用I/Oポートのストローブ処理した時 */

int romaji_input_mode = FALSE;			/* 真:ローマ字入力中 */



/*
 * mouse_dx, jop1_dx の関係
 *       マウスが移動した時
 *               mouse_dx にその移動量をセットする。
 *       汎用I/Oポートからマウス座標をリードした時、
 *               mouse_dx をクリッピングした値を、 jop1_dx にセットする。
 */



/*
 * 設定
 */

int mouse_mode = 0;						/* マウス・ジョイステック処理 */

int mouse_sensitivity = 100;			/* マウス感度 */
int mouse_swap_button = FALSE;			/* マウスボタンを入れ替える */


int mouse_key_mode = 0;					/* マウス入力をキーに反映 */
int mouse_key_assign[6];				/* 0:なし 1:テンキー 2:任意 */


int joy_key_mode = 0;					/* ジョイ入力をキーに反映 */
int joy_key_assign[12];					/* 0:なし 1:テンキー 2:任意 */
int joy_swap_button = FALSE;			/* ボタンのABを入れ替える */


int joy2_key_mode = 0;					/* ジョイ２入力をキーに反映 */
int joy2_key_assign[12];				/* 0:なし 1:テンキー 2:任意 */
int joy2_swap_button = FALSE;			/* ボタンのABを入れ替える */



int cursor_key_mode = 0;				/* カーソルキーを別キーに反映 */
int cursor_key_assign[4];				/* 0:なし 1:テンキー 2:任意 */
/* Cursor KEY -> 10 KEY , original by funa. (thanks!) */
/* Cursor Key -> 任意のキー , original by floi. (thanks!) */



int tenkey_emu  = FALSE;				/* 真:数字キーをテンキーに */
int numlock_emu = FALSE;				/* 真:ソフトウェアNumLockを行う */




int function_f[ 1 + 20 ] =				/* ファンクションキーの機能 */
{
	FN_FUNC,	/* [0] はダミー */
	FN_FUNC,	FN_FUNC,	FN_FUNC,	FN_FUNC,	FN_FUNC,	/* 未使用   */
	FN_FUNC,	FN_FUNC,	FN_FUNC,	FN_FUNC,	FN_FUNC,	/* f6 ..f10 */
	FN_FUNC,	FN_FUNC,	FN_FUNC,	FN_FUNC,	FN_FUNC,	/* 未使用   */
	FN_FUNC,	FN_FUNC,	FN_FUNC,	FN_FUNC,	FN_FUNC,	/* 未使用   */
};


int    fn_max_speed = 1600;
double fn_max_clock = CONST_4MHZ_CLOCK * 16;
int    fn_max_boost = 16;


int romaji_type = 0;					/* ローマ字変換のタイプ */




static void mouse_to_key(int code, int on);

/*
 * キー操作 記録・再生
 */

char *file_rec = NULL;					/* キー入力記録のファイル名 */
char *file_pb  = NULL;					/* キー入力再生のファイル名 */

static OSD_FILE *fp_rec;
static OSD_FILE *fp_pb;

static struct {							/* キー入力記録構造体 */
	Uchar key[16];						/*      I/O 00H..0FH */
	char  dx_h;							/*      マウス dx 上位 */
	Uchar dx_l;							/*      マウス dx 下位 */
	char  dy_h;							/*      マウス dy 上位 */
	Uchar dy_l;							/*      マウス dy 下位 */
	char  image[2];						/*      イメージNo -1空,0同,1.. */
	char  resv[2];
} key_record;							/* 24 bytes */





/*---------------------------------------------------------------------------
 * キーコード と I/O ポートの対応
 *---------------------------------------------------------------------------*/

#define Port0	0x00
#define Port1	0x01
#define Port2	0x02
#define Port3	0x03
#define Port4	0x04
#define Port5	0x05
#define Port6	0x06
#define Port7	0x07
#define Port8	0x08
#define Port9	0x09
#define PortA	0x0a
#define PortB	0x0b
#define PortC	0x0c
#define PortD	0x0d
#define PortE	0x0e
#define PortX	0x0f
#define PortY	0x10
#define PortZ	0x11

#define Bit0	0x01
#define Bit1	0x02
#define Bit2	0x04
#define Bit3	0x08
#define Bit4	0x10
#define Bit5	0x20
#define Bit6	0x40
#define Bit7	0x80

#define PadA	Bit4
#define PadB	Bit5

#define PadU	Bit0
#define PadD	Bit1
#define PadL	Bit2
#define PadR	Bit3


typedef struct {
	unsigned char       port;
	unsigned char       mask;
} T_KEYPORT;

static const T_KEYPORT keyport[ KEY88_END ] = {
	{ 0, 0 },                     /*        KEY88_INVALID         = 0,    */

	{ 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },

	{ Port9, Bit6 },              /*        KEY88_SPACE           = 32,   */
	{ Port6, Bit1 },              /*        KEY88_EXCLAM          = 33,   */
	{ Port6, Bit2 },              /*        KEY88_QUOTEDBL        = 34,   */
	{ Port6, Bit3 },              /*        KEY88_NUMBERSIGN      = 35,   */
	{ Port6, Bit4 },              /*        KEY88_DOLLAR          = 36,   */
	{ Port6, Bit5 },              /*        KEY88_PERCENT         = 37,   */
	{ Port6, Bit6 },              /*        KEY88_AMPERSAND       = 38,   */
	{ Port6, Bit7 },              /*        KEY88_APOSTROPHE      = 39,   */
	{ Port7, Bit0 },              /*        KEY88_PARENLEFT       = 40,   */
	{ Port7, Bit1 },              /*        KEY88_PARENRIGHT      = 41,   */
	{ Port7, Bit2 },              /*        KEY88_ASTERISK        = 42,   */
	{ Port7, Bit3 },              /*        KEY88_PLUS            = 43,   */
	{ Port7, Bit4 },              /*        KEY88_COMMA           = 44,   */
	{ Port5, Bit7 },              /*        KEY88_MINUS           = 45,   */
	{ Port7, Bit5 },              /*        KEY88_PERIOD          = 46,   */
	{ Port7, Bit6 },              /*        KEY88_SLASH           = 47,   */
	{ Port6, Bit0 },              /*        KEY88_0               = 48,   */
	{ Port6, Bit1 },              /*        KEY88_1               = 49,   */
	{ Port6, Bit2 },              /*        KEY88_2               = 50,   */
	{ Port6, Bit3 },              /*        KEY88_3               = 51,   */
	{ Port6, Bit4 },              /*        KEY88_4               = 52,   */
	{ Port6, Bit5 },              /*        KEY88_5               = 53,   */
	{ Port6, Bit6 },              /*        KEY88_6               = 54,   */
	{ Port6, Bit7 },              /*        KEY88_7               = 55,   */
	{ Port7, Bit0 },              /*        KEY88_8               = 56,   */
	{ Port7, Bit1 },              /*        KEY88_9               = 57,   */
	{ Port7, Bit2 },              /*        KEY88_COLON           = 58,   */
	{ Port7, Bit3 },              /*        KEY88_SEMICOLON       = 59,   */
	{ Port7, Bit4 },              /*        KEY88_LESS            = 60,   */
	{ Port5, Bit7 },              /*        KEY88_EQUAL           = 61,   */
	{ Port7, Bit5 },              /*        KEY88_GREATER         = 62,   */
	{ Port7, Bit6 },              /*        KEY88_QUESTION        = 63,   */
	{ Port2, Bit0 },              /*        KEY88_AT              = 64,   */
	{ Port2, Bit1 },              /*        KEY88_A               = 65,   */
	{ Port2, Bit2 },              /*        KEY88_B               = 66,   */
	{ Port2, Bit3 },              /*        KEY88_C               = 67,   */
	{ Port2, Bit4 },              /*        KEY88_D               = 68,   */
	{ Port2, Bit5 },              /*        KEY88_E               = 69,   */
	{ Port2, Bit6 },              /*        KEY88_F               = 70,   */
	{ Port2, Bit7 },              /*        KEY88_G               = 71,   */
	{ Port3, Bit0 },              /*        KEY88_H               = 72,   */
	{ Port3, Bit1 },              /*        KEY88_I               = 73,   */
	{ Port3, Bit2 },              /*        KEY88_J               = 74,   */
	{ Port3, Bit3 },              /*        KEY88_K               = 75,   */
	{ Port3, Bit4 },              /*        KEY88_L               = 76,   */
	{ Port3, Bit5 },              /*        KEY88_M               = 77,   */
	{ Port3, Bit6 },              /*        KEY88_N               = 78,   */
	{ Port3, Bit7 },              /*        KEY88_O               = 79,   */
	{ Port4, Bit0 },              /*        KEY88_P               = 80,   */
	{ Port4, Bit1 },              /*        KEY88_Q               = 81,   */
	{ Port4, Bit2 },              /*        KEY88_R               = 82,   */
	{ Port4, Bit3 },              /*        KEY88_S               = 83,   */
	{ Port4, Bit4 },              /*        KEY88_T               = 84,   */
	{ Port4, Bit5 },              /*        KEY88_U               = 85,   */
	{ Port4, Bit6 },              /*        KEY88_V               = 86,   */
	{ Port4, Bit7 },              /*        KEY88_W               = 87,   */
	{ Port5, Bit0 },              /*        KEY88_X               = 88,   */
	{ Port5, Bit1 },              /*        KEY88_Y               = 89,   */
	{ Port5, Bit2 },              /*        KEY88_Z               = 90,   */
	{ Port5, Bit3 },              /*        KEY88_BRACKETLEFT     = 91,   */
	{ Port5, Bit4 },              /*        KEY88_YEN             = 92,   */
	{ Port5, Bit5 },              /*        KEY88_BRACKETRIGHT    = 93,   */
	{ Port5, Bit6 },              /*        KEY88_CARET           = 94,   */
	{ Port7, Bit7 },              /*        KEY88_UNDERSCORE      = 95,   */
	{ Port2, Bit0 },              /*        KEY88_BACKQUOTE       = 96,   */
	{ Port2, Bit1 },              /*        KEY88_a               = 97,   */
	{ Port2, Bit2 },              /*        KEY88_b               = 98,   */
	{ Port2, Bit3 },              /*        KEY88_c               = 99,   */
	{ Port2, Bit4 },              /*        KEY88_d               = 100,  */
	{ Port2, Bit5 },              /*        KEY88_e               = 101,  */
	{ Port2, Bit6 },              /*        KEY88_f               = 102,  */
	{ Port2, Bit7 },              /*        KEY88_g               = 103,  */
	{ Port3, Bit0 },              /*        KEY88_h               = 104,  */
	{ Port3, Bit1 },              /*        KEY88_i               = 105,  */
	{ Port3, Bit2 },              /*        KEY88_j               = 106,  */
	{ Port3, Bit3 },              /*        KEY88_k               = 107,  */
	{ Port3, Bit4 },              /*        KEY88_l               = 108,  */
	{ Port3, Bit5 },              /*        KEY88_m               = 109,  */
	{ Port3, Bit6 },              /*        KEY88_n               = 110,  */
	{ Port3, Bit7 },              /*        KEY88_o               = 111,  */
	{ Port4, Bit0 },              /*        KEY88_p               = 112,  */
	{ Port4, Bit1 },              /*        KEY88_q               = 113,  */
	{ Port4, Bit2 },              /*        KEY88_r               = 114,  */
	{ Port4, Bit3 },              /*        KEY88_s               = 115,  */
	{ Port4, Bit4 },              /*        KEY88_t               = 116,  */
	{ Port4, Bit5 },              /*        KEY88_u               = 117,  */
	{ Port4, Bit6 },              /*        KEY88_v               = 118,  */
	{ Port4, Bit7 },              /*        KEY88_w               = 119,  */
	{ Port5, Bit0 },              /*        KEY88_x               = 120,  */
	{ Port5, Bit1 },              /*        KEY88_y               = 121,  */
	{ Port5, Bit2 },              /*        KEY88_z               = 122,  */
	{ Port5, Bit3 },              /*        KEY88_BRACELEFT       = 123,  */
	{ Port5, Bit4 },              /*        KEY88_BAR             = 124,  */
	{ Port5, Bit5 },              /*        KEY88_BRACERIGHT      = 125,  */
	{ Port5, Bit6 },              /*        KEY88_TILDE           = 126,  */
	{     0,    0 },
	{ Port0, Bit0 },              /*        KEY88_KP_0            = 128,  */
	{ Port0, Bit1 },              /*        KEY88_KP_1            = 129,  */
	{ Port0, Bit2 },              /*        KEY88_KP_2            = 130,  */
	{ Port0, Bit3 },              /*        KEY88_KP_3            = 131,  */
	{ Port0, Bit4 },              /*        KEY88_KP_4            = 132,  */
	{ Port0, Bit5 },              /*        KEY88_KP_5            = 133,  */
	{ Port0, Bit6 },              /*        KEY88_KP_6            = 134,  */
	{ Port0, Bit7 },              /*        KEY88_KP_7            = 135,  */
	{ Port1, Bit0 },              /*        KEY88_KP_8            = 136,  */
	{ Port1, Bit1 },              /*        KEY88_KP_9            = 137,  */
	{ Port1, Bit2 },              /*        KEY88_KP_MULTIPLY     = 138,  */
	{ Port1, Bit3 },              /*        KEY88_KP_ADD          = 139,  */
	{ Port1, Bit4 },              /*        KEY88_KP_EQUAL        = 140,  */
	{ Port1, Bit5 },              /*        KEY88_KP_COMMA        = 141,  */
	{ Port1, Bit6 },              /*        KEY88_KP_PERIOD       = 142,  */
	{ PortA, Bit5 },              /*        KEY88_KP_SUB          = 143,  */
	{ PortA, Bit6 },              /*        KEY88_KP_DIVIDE       = 144,  */

	{ Port1, Bit7 },              /*        KEY88_RETURN          = 145,  */
	{ Port8, Bit0 },              /*        KEY88_HOME            = 146,  */
	{ Port8, Bit1 },              /*        KEY88_UP              = 147,  */
	{ Port8, Bit2 },              /*        KEY88_RIGHT           = 148,  */
	{ Port8, Bit3 },              /*        KEY88_INS_DEL         = 149,  */
	{ Port8, Bit4 },              /*        KEY88_GRAPH           = 150,  */
	{ Port8, Bit5 },              /*        KEY88_KANA            = 151,  */
	{ PortY, Bit0 },              /*        KEY88_SHIFT           = 152,  */
	{ Port8, Bit7 },              /*        KEY88_CTRL            = 153,  */
	{ Port9, Bit0 },              /*        KEY88_STOP            = 154,  */
	{ Port9, Bit6 },              /*        KEY88_SPACE           = 155,  */
	{ Port9, Bit7 },              /*        KEY88_ESC             = 156,  */
	{ PortA, Bit0 },              /*        KEY88_TAB             = 157,  */
	{ PortA, Bit1 },              /*        KEY88_DOWN            = 158,  */
	{ PortA, Bit2 },              /*        KEY88_LEFT            = 159,  */
	{ PortA, Bit3 },              /*        KEY88_HELP            = 160,  */
	{ PortA, Bit4 },              /*        KEY88_COPY            = 161,  */
	{ PortA, Bit7 },              /*        KEY88_CAPS            = 162,  */
	{ PortB, Bit0 },              /*        KEY88_ROLLUP          = 163,  */
	{ PortB, Bit1 },              /*        KEY88_ROLLDOWN        = 164,  */

	{ Port9, Bit1 },              /*        KEY88_F1              = 165,  */
	{ Port9, Bit2 },              /*        KEY88_F2              = 166,  */
	{ Port9, Bit3 },              /*        KEY88_F3              = 167,  */
	{ Port9, Bit4 },              /*        KEY88_F4              = 168,  */
	{ Port9, Bit5 },              /*        KEY88_F5              = 169,  */

	{     0,    0 },              /*        KEY88_SYS_PREV        = 170,  */
	{     0,    0 },              /*        KEY88_SYS_NEXT        = 171,  */
	{     0,    0 },              /*        KEY88_SYS_FORWARD     = 172,  */
	{     0,    0 },              /*        KEY88_SYS_BACK        = 173,  */
	{     0,    0 },              /*        KEY88_SYS_MODE        = 174,  */
	{ PortC, Bit0 },              /*        KEY88_SYS_F6          = 175,  */
	{ PortC, Bit1 },              /*        KEY88_SYS_F7          = 176,  */
	{ PortC, Bit2 },              /*        KEY88_SYS_F8          = 177,  */
	{ PortC, Bit3 },              /*        KEY88_SYS_F9          = 178,  */
	{ PortC, Bit4 },              /*        KEY88_SYS_F10         = 179,  */

	{ PortC, Bit0 },              /*        KEY88_F6              = 180,  */
	{ PortC, Bit1 },              /*        KEY88_F7              = 181,  */
	{ PortC, Bit2 },              /*        KEY88_F8              = 182,  */
	{ PortC, Bit3 },              /*        KEY88_F9              = 183,  */
	{ PortC, Bit4 },              /*        KEY88_F10             = 184,  */
	{ PortC, Bit5 },              /*        KEY88_BS              = 185,  */
	{ PortC, Bit6 },              /*        KEY88_INS             = 186,  */
	{ PortC, Bit7 },              /*        KEY88_DEL             = 187,  */
	{ PortD, Bit0 },              /*        KEY88_HENKAN          = 188,  */
	{ PortD, Bit1 },              /*        KEY88_KETTEI          = 189,  */
	{ PortD, Bit2 },              /*        KEY88_PC              = 190,  */
	{ PortD, Bit3 },              /*        KEY88_ZENKAKU         = 191,  */
	{ PortE, Bit0 },              /*        KEY88_RETURNL         = 192,  */
	{ PortE, Bit1 },              /*        KEY88_RETURNR         = 193,  */
	{ PortE, Bit2 },              /*        KEY88_SHIFTL          = 194,  */
	{ PortE, Bit3 },              /*        KEY88_SHIFTR          = 195,  */

	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },

	{     0,    0 },              /*        KEY88_MOUSE_UP        = 208,  */
	{     0,    0 },              /*        KEY88_MOUSE_DOWN      = 209,  */
	{     0,    0 },              /*        KEY88_MOUSE_LEFT      = 210,  */
	{     0,    0 },              /*        KEY88_MOUSE_RIGHT     = 211,  */
	{     0,    0 },              /*        KEY88_MOUSE_L         = 212,  */
	{     0,    0 },              /*        KEY88_MOUSE_M         = 213,  */
	{     0,    0 },              /*        KEY88_MOUSE_R         = 214,  */
	{     0,    0 },              /*        KEY88_MOUSE_WUP       = 215,  */
	{     0,    0 },              /*        KEY88_MOUSE_WDN       = 216,  */

	{ 0, 0 }, { 0, 0 }, { 0, 0 },
	{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },

	{ PortX, PadU },              /*        KEY88_PAD1_UP         = 224,  */
	{ PortX, PadD },              /*        KEY88_PAD1_DOWN       = 225,  */
	{ PortX, PadL },              /*        KEY88_PAD1_LEFT       = 226,  */
	{ PortX, PadR },              /*        KEY88_PAD1_RIGHT      = 227,  */
	{ PortX, PadA },              /*        KEY88_PAD1_A          = 228,  */
	{ PortX, PadB },              /*        KEY88_PAD1_B          = 229,  */
	{     0,    0 },              /*        KEY88_PAD1_C          = 230,  */
	{     0,    0 },              /*        KEY88_PAD1_D          = 231,  */
	{     0,    0 },              /*        KEY88_PAD1_E          = 232,  */
	{     0,    0 },              /*        KEY88_PAD1_F          = 233,  */
	{     0,    0 },              /*        KEY88_PAD1_G          = 234,  */
	{     0,    0 },              /*        KEY88_PAD1_H          = 235,  */

	{     0,    0 },              /*        KEY88_PAD2_UP         = 236,  */
	{     0,    0 },              /*        KEY88_PAD2_DOWN       = 237,  */
	{     0,    0 },              /*        KEY88_PAD2_LEFT       = 238,  */
	{     0,    0 },              /*        KEY88_PAD2_RIGHT      = 239,  */
	{     0,    0 },              /*        KEY88_PAD2_A          = 240,  */
	{     0,    0 },              /*        KEY88_PAD2_B          = 241,  */
	{     0,    0 },              /*        KEY88_PAD2_C          = 242,  */
	{     0,    0 },              /*        KEY88_PAD2_D          = 243,  */
	{     0,    0 },              /*        KEY88_PAD2_E          = 244,  */
	{     0,    0 },              /*        KEY88_PAD2_F          = 245,  */
	{     0,    0 },              /*        KEY88_PAD2_G          = 246,  */
	{     0,    0 },              /*        KEY88_PAD2_H          = 247,  */

	{ Port8, Bit1 },              /*        KEY88_SYS_UP          = 248,  */
	{ PortA, Bit1 },              /*        KEY88_SYS_DOWN        = 249,  */
	{ PortA, Bit2 },              /*        KEY88_SYS_LEFT        = 250,  */
	{ Port8, Bit2 },              /*        KEY88_SYS_RIGHT       = 251,  */
	{     0,    0 },              /*        KEY88_SYS_ROMAJI      = 252,  */
	{     0,    0 },              /*        KEY88_SYS_NUM         = 253,  */
	{     0,    0 },              /*        KEY88_SYS_STATUS      = 254,  */
	{     0,    0 },              /*        KEY88_SYS_MENU        = 255,  */
};

static const T_KEYPORT keyport_ext[][2] = {
	{ { Port9, Bit1 }, { PortY, Bit1 }, },  /*  KEY88_SYS_F6          = 175,  */
	{ { Port9, Bit2 }, { PortY, Bit2 }, },  /*  KEY88_SYS_F7          = 176,  */
	{ { Port9, Bit3 }, { PortY, Bit3 }, },  /*  KEY88_SYS_F8          = 177,  */
	{ { Port9, Bit4 }, { PortY, Bit4 }, },  /*  KEY88_SYS_F9          = 178,  */
	{ { Port9, Bit5 }, { PortY, Bit5 }, },  /*  KEY88_SYS_F10         = 179,  */
	{ { Port9, Bit1 }, { PortY, Bit6 }, },  /*  KEY88_F6              = 180,  */
	{ { Port9, Bit2 }, { PortY, Bit7 }, },  /*  KEY88_F7              = 181,  */
	{ { Port9, Bit3 }, { PortZ, Bit0 }, },  /*  KEY88_F8              = 182,  */
	{ { Port9, Bit4 }, { PortZ, Bit1 }, },  /*  KEY88_F9              = 183,  */
	{ { Port9, Bit5 }, { PortZ, Bit2 }, },  /*  KEY88_F10             = 184,  */
	{ { Port8, Bit3 }, {     0,    0 }, },  /*  KEY88_BS              = 185,  */
	{ { Port8, Bit3 }, { PortZ, Bit3 }, },  /*  KEY88_INS             = 186,  */
	{ { Port8, Bit3 }, {     0,    0 }, },  /*  KEY88_DEL             = 187,  */
	{ { Port9, Bit6 }, {     0,    0 }, },  /*  KEY88_HENKAN          = 188,  */
	{ { Port9, Bit6 }, {     0,    0 }, },  /*  KEY88_KETTEI          = 189,  */
	{ {     0,    0 }, {     0,    0 }, },  /*  KEY88_PC              = 190,  */
	{ {     0,    0 }, {     0,    0 }, },  /*  KEY88_ZENKAKU         = 191,  */
	{ { Port1, Bit7 }, {     0,    0 }, },  /*  KEY88_RETURNL         = 192,  */
	{ { Port1, Bit7 }, {     0,    0 }, },  /*  KEY88_RETURNR         = 193,  */
	{ {     0,    0 }, { PortZ, Bit4 }, },  /*  KEY88_SHIFTL          = 194,  */
	{ {     0,    0 }, { PortZ, Bit5 }, },  /*  KEY88_SHIFTR          = 195,  */
};


/*---------------------------------------------------------------------------
 *
 *---------------------------------------------------------------------------*/

#define IS_JOY_STATUS()			key_scan[ PortX ]

#define IS_KEY88_LATERTYPE(c)	(KEY88_SYS_F6 <= (c) && (c) <= KEY88_SHIFTR)

#define IS_KEY88_SYSTEMKEY(c)	((KEY88_SYS_PREV <= (c) && (c) <= KEY88_SYS_F10) ||	\
								 (KEY88_SYS_UP <= (c) && (c) <= KEY88_SYS_MENU))


#define KEY88_PRESS(code)												\
	do {																\
		if (IS_KEY88_LATERTYPE((code))) {								\
			int lcode = (code) - KEY88_SYS_F6;							\
			key_scan[ keyport_ext[lcode][0].port ] &= ~keyport_ext[lcode][0].mask; \
			key_scan[ keyport_ext[lcode][1].port ] &= ~keyport_ext[lcode][1].mask; \
		}																\
		key_scan[ keyport[(code)].port ] &= ~keyport[(code)].mask;		\
		if ((key_scan[ PortY ] & key_scan[ PortZ ]) != 0xff) {			\
			key_scan[ Port8 ] &= ~Bit6;									\
		}																\
	} while (0)

#define KEY88_RELEASE(code)												\
	do {																\
		if (IS_KEY88_LATERTYPE((code))) {								\
			int lcode = (code) - KEY88_SYS_F6;							\
			key_scan[ keyport_ext[lcode][0].port ] |=  keyport_ext[lcode][0].mask; \
			key_scan[ keyport_ext[lcode][1].port ] |=  keyport_ext[lcode][1].mask; \
		}																\
		key_scan[ keyport[(code)].port ] |=  keyport[(code)].mask;		\
		if ((key_scan[ PortY ] & key_scan[ PortZ ]) == 0xff) {			\
			key_scan[ Port8 ] |=  Bit6;									\
		}																\
	} while (0)

#define KEY88_TOGGLE(code)										\
	key_scan[ keyport[(code)].port ] ^=  keyport[(code)].mask

#define IS_KEY88_PRESS(code)	\
		(~key_scan[ keyport[(code)].port ] & keyport[(code)].mask)

#define IS_KEY88_RELEASE(code)	\
		( key_scan[ keyport[(code)].port ] & keyport[(code)].mask)






/*---------------------------------------------------------------------------
 * キーのバインディング変更 (キーコード、機能)
 *---------------------------------------------------------------------------*/
static void clr_key_function(void)
{
	int i;
	for (i = 0; i < COUNTOF(key_func); i++) {
		key_func[i] = 0;
	}

	/* システムのキーは、機能固定 */
	key_func[ KEY88_SYS_PREV   ] = FN_PREV_KEY;
	key_func[ KEY88_SYS_NEXT   ] = FN_NEXT_KEY;
	key_func[ KEY88_SYS_F6     ] = KEY88_F6;
	key_func[ KEY88_SYS_F7     ] = KEY88_F7;
	key_func[ KEY88_SYS_F8     ] = KEY88_F8;    
	key_func[ KEY88_SYS_F9     ] = KEY88_F9;
	key_func[ KEY88_SYS_F10    ] = KEY88_F10;
	key_func[ KEY88_SYS_UP     ] = KEY88_UP;
	key_func[ KEY88_SYS_DOWN   ] = KEY88_DOWN;
	key_func[ KEY88_SYS_RIGHT  ] = KEY88_RIGHT;
	key_func[ KEY88_SYS_LEFT   ] = KEY88_LEFT;
	key_func[ KEY88_SYS_ROMAJI ] = FN_ROMAJI_KEY;
	key_func[ KEY88_SYS_NUM    ] = FN_NUM_KEY;
	key_func[ KEY88_SYS_STATUS ] = FN_STATUS;
	key_func[ KEY88_SYS_MENU   ] = FN_MENU;
}


static void set_key_function(int keycode, int func_no)
{
	if (IS_KEY88_SYSTEMKEY(keycode)) {
		/* システムのキーは、機能固定 */
		return;
	}

	key_func[ keycode ] = func_no;
}


static void swap_key_function(int keycode1, int keycode2)
{
	int              tmp = key_func[ keycode1 ];
	key_func[ keycode1 ] = key_func[ keycode2 ];
	key_func[ keycode2 ] = tmp;
}


typedef struct {
	const unsigned char code;
	const unsigned char func;
	unsigned char       user;
} T_ASSIGN_KEY_TABLE;

#define ASSIGN_KEY_MODE_NONE		(0)
#define ASSIGN_KEY_MODE_SYS			(1)
#define ASSIGN_KEY_MODE_USER		(2)
static void assign_key_by_list(const T_ASSIGN_KEY_TABLE table[],
							   int nr_array,
							   int mode)
{
	int i;
	for (i = 0; i < nr_array; i++) {
		switch (mode) {
		case ASSIGN_KEY_MODE_USER:
			set_key_function(table[i].code, table[i].user);
			break;
		case ASSIGN_KEY_MODE_SYS:
			set_key_function(table[i].code, table[i].func);
			break;
		default:
			set_key_function(table[i].code, 0);
			break;
		}
	}
}


/* Num lock を有効に (or 数字キーをテンキーに) */
static void assign_key_numlock_tenkey(void)
{
	static const T_ASSIGN_KEY_TABLE table1[] = {
		{ KEY88_1,          KEY88_F1,          },
		{ KEY88_2,          KEY88_F2,          },
		{ KEY88_3,          KEY88_F3,          },
		{ KEY88_4,          KEY88_F4,          },
		{ KEY88_5,          KEY88_F5,          },
		{ KEY88_6,          KEY88_HOME,        },
		{ KEY88_7,          KEY88_KP_7,        },
		{ KEY88_8,          KEY88_KP_8,        },
		{ KEY88_9,          KEY88_KP_9,        },
		{ KEY88_0,          KEY88_KP_MULTIPLY, },
		{ KEY88_MINUS,      KEY88_KP_SUB,      },
		{ KEY88_e,          KEY88_UP,          },
		{ KEY88_y,          KEY88_HELP,        },
		{ KEY88_u,          KEY88_KP_4,        },
		{ KEY88_i,          KEY88_KP_5,        },
		{ KEY88_o,          KEY88_KP_6,        },
		{ KEY88_p,          KEY88_KP_ADD,      },
		{ KEY88_AT,         KEY88_KP_DIVIDE,   },
		{ KEY88_s,          KEY88_LEFT,        },
		{ KEY88_d,          KEY88_RIGHT,       },
		{ KEY88_j,          KEY88_KP_1,        },
		{ KEY88_k,          KEY88_KP_2,        },
		{ KEY88_l,          KEY88_KP_3,        },
		{ KEY88_SEMICOLON,  KEY88_KP_EQUAL,    },
		{ KEY88_x,          KEY88_DOWN,        },
		{ KEY88_m,          KEY88_KP_0,        },
		{ KEY88_COMMA,      KEY88_KP_COMMA,    },
		{ KEY88_PERIOD,     KEY88_KP_PERIOD,   },
		{ KEY88_SLASH,      KEY88_RETURNR,     },

		{ KEY88_EXCLAM,     KEY88_F1,          },
		{ KEY88_QUOTEDBL,   KEY88_F2,          },
		{ KEY88_NUMBERSIGN, KEY88_F3,          },
		{ KEY88_DOLLAR,     KEY88_F4,          },
		{ KEY88_PERCENT,    KEY88_F5,          },
		{ KEY88_AMPERSAND,  KEY88_HOME,        },
		{ KEY88_APOSTROPHE, KEY88_KP_7,        },
		{ KEY88_PARENLEFT,  KEY88_KP_8,        },
		{ KEY88_PARENRIGHT, KEY88_KP_9,        },
		{ KEY88_0,          KEY88_KP_MULTIPLY, },
		{ KEY88_EQUAL,      KEY88_KP_SUB,      },
		{ KEY88_E,          KEY88_UP,          },
		{ KEY88_Y,          KEY88_HELP,        },
		{ KEY88_U,          KEY88_KP_4,        },
		{ KEY88_I,          KEY88_KP_5,        },
		{ KEY88_O,          KEY88_KP_6,        },
		{ KEY88_P,          KEY88_KP_ADD,      },
		{ KEY88_BACKQUOTE,  KEY88_KP_DIVIDE,   },
		{ KEY88_S,          KEY88_LEFT,        },
		{ KEY88_D,          KEY88_RIGHT,       },
		{ KEY88_J,          KEY88_KP_1,        },
		{ KEY88_K,          KEY88_KP_2,        },
		{ KEY88_L,          KEY88_KP_3,        },
		{ KEY88_PLUS,       KEY88_KP_EQUAL,    },
		{ KEY88_X,          KEY88_DOWN,        },
		{ KEY88_M,          KEY88_KP_0,        },
		{ KEY88_LESS,       KEY88_KP_COMMA,    },
		{ KEY88_GREATER,    KEY88_KP_PERIOD,   },
		{ KEY88_QUESTION,   KEY88_RETURNR,     },
	};
	static const T_ASSIGN_KEY_TABLE table2[] = {
		{ KEY88_1,          KEY88_KP_1,        },
		{ KEY88_2,          KEY88_KP_2,        },
		{ KEY88_3,          KEY88_KP_3,        },
		{ KEY88_4,          KEY88_KP_4,        },
		{ KEY88_5,          KEY88_KP_5,        },
		{ KEY88_6,          KEY88_KP_6,        },
		{ KEY88_7,          KEY88_KP_7,        },
		{ KEY88_8,          KEY88_KP_8,        },
		{ KEY88_9,          KEY88_KP_9,        },
		{ KEY88_0,          KEY88_KP_0,        },

		{ KEY88_EXCLAM,     KEY88_KP_1,        },
		{ KEY88_QUOTEDBL,   KEY88_KP_2,        },
		{ KEY88_NUMBERSIGN, KEY88_KP_3,        },
		{ KEY88_DOLLAR,     KEY88_KP_4,        },
		{ KEY88_PERCENT,    KEY88_KP_5,        },
		{ KEY88_AMPERSAND,  KEY88_KP_6,        },
		{ KEY88_APOSTROPHE, KEY88_KP_7,        },
		{ KEY88_PARENLEFT,  KEY88_KP_8,        },
		{ KEY88_PARENRIGHT, KEY88_KP_9,        },
	};

	if (numlock_emu) {
		/* Num lock を有効に */
		assign_key_by_list(table1, COUNTOF(table1), ASSIGN_KEY_MODE_SYS);

	} else {
		assign_key_by_list(table1, COUNTOF(table1), ASSIGN_KEY_MODE_NONE);

		if (quasi88_is_exec()) {

			if (tenkey_emu) {
				/* 数字キーをテンキーに設定 (EMU モード中のみ) */
				assign_key_by_list(table2, COUNTOF(table2), ASSIGN_KEY_MODE_SYS);
			}
		}
	}
}

static void assign_key_cursorkey(void)
{
	static T_ASSIGN_KEY_TABLE table[] = {
		{ KEY88_UP,		KEY88_KP_8, },
		{ KEY88_DOWN,   KEY88_KP_2, },
		{ KEY88_LEFT,   KEY88_KP_4, },
		{ KEY88_RIGHT,  KEY88_KP_6, },
	};

	if (cursor_key_mode == 2) {
		size_t i;
		for (i = 0; i < COUNTOF(table); i++) {
			table[i].user = cursor_key_assign[i];
		}
	}
	assign_key_by_list(table, COUNTOF(table), cursor_key_mode);
	/* cursor_key_mode == 0 の場合は、割り当てなし(カーソルキーのまま)となる */
}

static void assign_key_mouse_joystick(void)
{
	/*
	  システムからのマウス入力、およびジョイスティック入力が、
	  QUASI88 のポートにどのように反映されるかの一覧。
	  
	                 |  マウス移動    マウスボタン | ジョイスティック
	  ---------------+--------------+--------------+-----------------
	  MOUSE_NONE     | なし         | なし         | なし
	  ---------------+--------------+--------------+-----------------
	  MOUSE_JOYMOUSE | 方向を反映   | 押下を反映   | なし
	  ---------------+--------------+--------------+-----------------
	  MOUSE_MOUSE    | 移動量を反映 | 押下を反映   | なし
	  ---------------+--------------+--------------+-----------------
	  MOUSE_JOYSTICK | なし         | なし         | 押下を反映
	  
	  「なし」の欄は、QUASI88には反映されないので、自由にキー割り当て可能。
	*/

	/* マウスの入力割り当て変更 */
	{
		static T_ASSIGN_KEY_TABLE table1[] = {
			{ KEY88_MOUSE_UP,    KEY88_KP_8, },
			{ KEY88_MOUSE_DOWN,  KEY88_KP_2, },
			{ KEY88_MOUSE_LEFT,  KEY88_KP_4, },
			{ KEY88_MOUSE_RIGHT, KEY88_KP_6, },
			{ KEY88_MOUSE_L,     KEY88_x,    },
			{ KEY88_MOUSE_R,     KEY88_z,    },
		};
		const static T_ASSIGN_KEY_TABLE table2[] = {
			{ KEY88_MOUSE_UP,    KEY88_PAD1_UP,    },
			{ KEY88_MOUSE_DOWN,  KEY88_PAD1_DOWN,  },
			{ KEY88_MOUSE_LEFT,  KEY88_PAD1_LEFT,  },
			{ KEY88_MOUSE_RIGHT, KEY88_PAD1_RIGHT, },
			{ KEY88_MOUSE_L,     KEY88_PAD1_A,     },
			{ KEY88_MOUSE_R,     KEY88_PAD1_B,     },
		};
		const static T_ASSIGN_KEY_TABLE table3[] = {
			{ KEY88_MOUSE_L,     KEY88_PAD1_A,     },
			{ KEY88_MOUSE_R,     KEY88_PAD1_B,     },
		};
		int swap = FALSE;

		assign_key_by_list(table1, COUNTOF(table1), ASSIGN_KEY_MODE_NONE);

		switch (mouse_mode) {
		case MOUSE_NONE:
		case MOUSE_JOYSTICK:
			/* マウスポート未接続なら、マウス移動をテンキーetcに割り当て可 */
			if (mouse_key_mode) {
				if (mouse_key_mode == 2) {
					size_t i;
					for (i = 0; i < COUNTOF(table1); i++) {
						table1[i].user = mouse_key_assign[i];
					}
				}
				assign_key_by_list(table1, COUNTOF(table1), mouse_key_mode);
				if (mouse_key_mode == 1) {
					swap = mouse_swap_button;
				}
			}
			break;

		case MOUSE_JOYMOUSE:
			/* マウスポートに、マウスをジョイスティックモードで接続中 */
			assign_key_by_list(table2, COUNTOF(table2), ASSIGN_KEY_MODE_SYS);
			swap = mouse_swap_button;
			break;

		case MOUSE_MOUSE:
			/* マウスポートに、マウス接続中 */
			assign_key_by_list(table3, COUNTOF(table3), ASSIGN_KEY_MODE_SYS);
			swap = mouse_swap_button;
			break;
		}
		if (swap) {
			swap_key_function(KEY88_MOUSE_L, KEY88_MOUSE_R);
		}
	}

	/* ジョイスティック入力割り当て変更 */
	{
		static T_ASSIGN_KEY_TABLE table4[] = {
			{ KEY88_PAD1_UP,    KEY88_KP_8, },
			{ KEY88_PAD1_DOWN,  KEY88_KP_2, },
			{ KEY88_PAD1_LEFT,  KEY88_KP_4, },
			{ KEY88_PAD1_RIGHT, KEY88_KP_6, },
			{ KEY88_PAD1_A,     KEY88_x,    },
			{ KEY88_PAD1_B,     KEY88_z,    },
			{ KEY88_PAD1_C,     0,          },
			{ KEY88_PAD1_D,     0,          },
			{ KEY88_PAD1_E,     0,          },
			{ KEY88_PAD1_F,     0,          },
			{ KEY88_PAD1_G,     0,          },
			{ KEY88_PAD1_H,     0,          },
		};
		int swap = FALSE;

		assign_key_by_list(table4, COUNTOF(table4), ASSIGN_KEY_MODE_NONE);

		switch (mouse_mode) {
		case MOUSE_NONE:
		case MOUSE_MOUSE:
		case MOUSE_JOYMOUSE:
			/* マウスポートに、ジョイスティックは接続されてない */
			if (joy_key_mode) {
				if (joy_key_mode == 2) {
					size_t i;
					for (i = 0; i < COUNTOF(table4); i++) {
						table4[i].user = joy_key_assign[i];
					}
				}
				assign_key_by_list(table4, COUNTOF(table4), joy_key_mode);
				if (joy_key_mode == 1) {
					swap = joy_swap_button;
				}
			}
			break;

		case MOUSE_JOYSTICK:
			/* マウスポートに、ジョイスティック接続中 */
			/* KEY88_PAD1_C .. KEY88_PAD1_H は割り当てなし */
			swap = joy_swap_button;
			break;
		}
		if (swap) {
			swap_key_function(KEY88_PAD1_A, KEY88_PAD1_B);
		}
	}

	/* ジョイスティック２入力割り当て変更 */
	{
		static T_ASSIGN_KEY_TABLE table5[] = {
			{ KEY88_PAD2_UP,    KEY88_KP_8, },
			{ KEY88_PAD2_DOWN,  KEY88_KP_2, },
			{ KEY88_PAD2_LEFT,  KEY88_KP_4, },
			{ KEY88_PAD2_RIGHT, KEY88_KP_6, },
			{ KEY88_PAD2_A,     KEY88_x,    },
			{ KEY88_PAD2_B,     KEY88_z,    },
			{ KEY88_PAD2_C,     0,          },
			{ KEY88_PAD2_D,     0,          },
			{ KEY88_PAD2_E,     0,          },
			{ KEY88_PAD2_F,     0,          },
			{ KEY88_PAD2_G,     0,          },
			{ KEY88_PAD2_H,     0,          },
		};
		int swap = FALSE;

		assign_key_by_list(table5, COUNTOF(table5), ASSIGN_KEY_MODE_NONE);

		if (joy2_key_mode) {
			if (joy2_key_mode == 2) {
				size_t i;
				for (i = 0; i < COUNTOF(table5); i++) {
					table5[i].user = joy2_key_assign[i];
				}
			}
			assign_key_by_list(table5, COUNTOF(table5), joy2_key_mode);
			if (joy2_key_mode == 1) {
				swap = joy2_swap_button;
			}
		}
		if (swap) {
			swap_key_function(KEY88_PAD2_A, KEY88_PAD2_B);
		}
	}
}


static void assign_key_functionkey_on(void)
{
	set_key_function(KEY88_F6,  function_f[  6 ]);
	set_key_function(KEY88_F7,  function_f[  7 ]);
	set_key_function(KEY88_F8,  function_f[  8 ]);
	set_key_function(KEY88_F9,  function_f[  9 ]);
	set_key_function(KEY88_F10, function_f[ 10 ]);
}

/****************************************************************************
 * キーのバインディング変更
 *      オプションや、メニューでの設定に基づき、キーバインドを変更する/戻す。
 *****************************************************************************/
void keyboard_switch(void)
{
	if (quasi88_is_exec()) {
		/* エミュ中は、キーバインディング変更 */

		clr_key_function();

		/* Num lock を有効に (or 数字キーをテンキーに) */
		assign_key_numlock_tenkey();

		/* カーソルの入力割り当て変更 */
		assign_key_cursorkey();

		/* マウス、ジョイスティックの入力割り当て変更 */
		assign_key_mouse_joystick();

		/* ファンクションキーの機能割り当て */
		assign_key_functionkey_on();


	} else {
		/* メニュー中などは、キーバインディング戻す */

		clr_key_function();

		/* Num lock を有効に (or 数字キーをテンキーに) */
		assign_key_numlock_tenkey();

		/* ファンクションキーの機能割り当て (基本的には機能は無効) */
		assign_key_functionkey_on();

		/* 全てのキーを離す (カナ/CAPS/NUMは除く) */
		keyboard_all_release();
	}


	/* マウス入力に備えて、マウス位置を再設定 */
	mouse_dx = 0;
	mouse_dy = 0;

	mouse_sx = 0;
	mouse_sy = 0;
	mouse_sb = 0;
}



/****************************************************************************
 * キー入力のワーク(ポート)初期化
 *****************************************************************************/
void keyboard_reset(void)
{
	size_t i;
	for (i = 0; i < sizeof(key_scan); i++) {
		key_scan[i] = 0xff;
	}

	romaji_init();
}



/****************************************************************************
 * 全てのキーを離す (カナ/CAPS/NUMは除く)
 *****************************************************************************/
void keyboard_all_release(void)
{
	size_t i;
	/* キースキャンワーク初期化 */
	for (i = 0; i < sizeof(key_scan); i++) {
		if (i == 0x08) {
			/* カナは残す */
			key_scan[i] |= 0xdf;

		} else if (i == 0x0a) {
			/* CAPSも残す */
			key_scan[i] |= 0x7f;

		} else {
			key_scan[i]  = 0xff;
		}
	}

	quasi88_notify_touchkey(TOUCHKEY_NOTIFY_REQ_SHIFT_OFF);
	quasi88_notify_touchkey(TOUCHKEY_NOTIFY_REQ_SHIFTL_OFF);
	quasi88_notify_touchkey(TOUCHKEY_NOTIFY_REQ_SHIFTR_OFF);

	romaji_clear();
}



/****************************************************************************
 * キー入力をワーク(ポート)に反映               エミュモードのみ
 *****************************************************************************/
static void record_playback(void);

void keyboard_update(void)
{
	int status;

	/* ローマ字入力モード時は、ローマ字変換後のカナを key_scan[] に反映 */

	if (romaji_input_mode) {
		romaji_output();
	}


	/* マウス入力を key_scan[] に反映 */

	switch (mouse_mode) {

	case MOUSE_NONE:
	case MOUSE_JOYSTICK:
		if (mouse_key_mode == 0) {
			/* マウスキー割り当て無しならマウス移動量は不要 */
			mouse_dx = 0;
			mouse_dy = 0;
			break;
		}
		/* FALLTHROUGH */

		/* マウスキー割り当て有りか、ジョイスティックモードなら
		   マウス移動量をポートに反映 */

	case MOUSE_JOYMOUSE:
		if (mouse_dx == 0) {
			if (mouse_dy == 0) {
				status = 0;				/* ---- */
			} else if (mouse_dy >  0) {
				status = (PadD);		/* ↓   */
			} else {
				status = (PadU);		/* ↑   */
			}
		} else if (mouse_dx > 0) {
			int a = mouse_dy * 100 / mouse_dx;
			if (a >  241) {
				status = (PadD);    	/* ↓   */
			} else if (a >   41) {
				status = (PadD | PadR);	/* ↓→ */
			} else if (a >  -41) {
				status = (PadR);		/*   → */
			} else if (a > -241) {
				status = (PadU | PadR);	/* ↑→ */
			} else {
				status = (PadU);		/* ↑   */
			}
		} else {
			int a = -mouse_dy * 100 / mouse_dx;
			if (a >  241) {
				status = (PadD);    	/* ↓   */
			} else if (a >   41) {
				status = (PadD | PadL);	/* ↓← */
			} else if (a >  -41) {
				status = (PadL);    	/*   ← */
			} else if (a > -241) {
				status = (PadU | PadL);	/* ↑← */
			} else {
				status = (PadU);		/* ↑   */
			}
		}

		mouse_to_key(KEY88_MOUSE_UP, (status & PadU));
		mouse_to_key(KEY88_MOUSE_DOWN, (status & PadD));
		mouse_to_key(KEY88_MOUSE_LEFT, (status & PadL));
		mouse_to_key(KEY88_MOUSE_RIGHT, (status & PadR));

		/* ポート反映後は移動量クリア */
		mouse_dx = 0;
		mouse_dy = 0;
		break;

	case MOUSE_MOUSE:
		/* マウス装着時なら、マウス移動量は保持しておく */
		break;
	}


	/* キー操作(scan_key[], mouse_dx, mouse_dy) を記録／再生 */

	record_playback();


	/* key_scan[0x0f] (ジョイスティック) をサウンド汎用入力ポートに反映 */

	switch (mouse_mode) {
	case MOUSE_NONE:
		sound_reg[ 0x0e ] = 0xff;
		sound_reg[ 0x0f ] = 0xff;
		break;

	case MOUSE_MOUSE:
		sound_reg[ 0x0f ] = (IS_JOY_STATUS() >> 4) | 0xfc;
		break;

	case MOUSE_JOYMOUSE:
	case MOUSE_JOYSTICK:
		sound_reg[ 0x0e ] = (IS_JOY_STATUS()) | 0xf0;
		sound_reg[ 0x0f ] = (IS_JOY_STATUS() >> 4) | 0xfc;
		/*printf("%02x\n",sound_reg[ 0x0e ]&0xff);*/
		break;
	}

}



/****************************************************************************
 * シリアルマウスからのシリアル入力値取得
 *
 *      ストップビット 1、データ長 7bit、パリティなし、ボーレート 1200bps？
 *                      +--+--+--+--+--+--+--+--+
 *      1バイト目       |０|１|Ｌ|Ｒ|Y7|Y6|X7|X6|
 *                      +--+--+--+--+--+--+--+--+
 *                      +--+--+--+--+--+--+--+--+
 *      2バイト目       |０|０|X5|X4|X3|X2|X1|X0|
 *                      +--+--+--+--+--+--+--+--+
 *                      +--+--+--+--+--+--+--+--+
 *      3バイト目       |０|０|Y5|Y4|Y3|Y2|Y1|Y0|
 *                      +--+--+--+--+--+--+--+--+
 *
 *      Ｌ Ｒ            … 左ボタン、右ボタン (押下時、1)
 *      X7X6X5X4X3X2X1X0 … X方向移動量 (符合付き)
 *      Y7Y6Y5Y4Y3Y2Y1Y0 … X方向移動量 (符合付き)
 *
 *****************************************************************************/
static int serial_mouse_x;
static int serial_mouse_y;
static int serial_mouse_step;

void init_serial_mouse_data(void)
{
	serial_mouse_x = 0;
	serial_mouse_y = 0;
	serial_mouse_step = 0;
}
int get_serial_mouse_data(void)
{
	int result;
	switch (serial_mouse_step) {
	case 0:
		if (mouse_sx >  127) {
			serial_mouse_x =  127;
		} else if (mouse_sx < -127) {
			serial_mouse_x = -127;
		} else {
			serial_mouse_x = mouse_sx;
		}
		if (mouse_sy >  127) {
			serial_mouse_y =  127;
		} else if (mouse_sy < -127) {
			serial_mouse_y = -127;
		} else {
			serial_mouse_y = mouse_sy;
		}
		mouse_sx = 0;
		mouse_sy = 0;

		result =
			0x40 |
			mouse_sb |
			((serial_mouse_y & 0xc0) >> 4) |
			((serial_mouse_x & 0xc0) >> 6);
		/*printf("%02x\n", result);*/
		break;

	case 1:
		result = (serial_mouse_x & 0x3f);
		/*printf("   %02x\n", result);*/
		break;

	default:
		result = (serial_mouse_y & 0x3f);
		/*printf("      %02x\n", result);*/
		break;
	}
	if (++serial_mouse_step >= 3) {
		serial_mouse_step = 0;
	}
	return result;
}



/****************************************************************************
 * キー入力 記録・再生
 *****************************************************************************/
void key_record_playback_init(void)
{
	int i;

	for (i = 0; i < 16; i++) {
		key_record.key[i] = 0xff;
	}
	key_record.dx_h = 0;
	key_record.dx_l = 0;
	key_record.dy_h = 0;
	key_record.dy_l = 0;
	key_record.image[0] = -1;
	key_record.image[1] = -1;


	fp_pb  = NULL;
	fp_rec = NULL;

	if (file_pb && file_pb[0]) {
		/* 再生用ファイルをオープン */

		fp_pb = osd_fopen(FTYPE_KEY_PB, file_pb, "rb");

		if (fp_pb) {
			if (verbose_proc) {
				printf("Key-Input Playback file <%s> ... OK\n", file_pb);
			}
		} else {
			printf("Can't open <%s>\nKey-Input PlayBack is invalid\n", file_pb);
		}
	}

	if (file_rec && file_rec[0]) {
		/* 記録用ファイルをオープン */

		fp_rec = osd_fopen(FTYPE_KEY_REC, file_rec, "wb");

		if (fp_rec) {
			if (verbose_proc) {
				printf("Key-Input Record file <%s> ... OK\n", file_rec);
			}
		} else {
			printf("Can't open <%s>\nKey-Input Record is invalid\n", file_rec);
		}
	}
}

void key_record_playback_exit(void)
{
	if (fp_pb) {
		osd_fclose(fp_pb);
		fp_pb = NULL;
		if (file_pb) {
			file_pb[0] = '\0';
		}
	}
	if (fp_rec) {
		osd_fclose(fp_rec);
		fp_rec = NULL;
		if (file_rec) {
			file_rec[0] = '\0';
		}
	}
}



/*----------------------------------------------------------------------
 * キー入力 記録・再生処理
 *----------------------------------------------------------------------*/
static void record_playback(void)
{
	int i, img;

	if (quasi88_is_exec() == FALSE) {
		return;
	}

	if (fp_rec) {
		for (i = 0; i < 0x10; i++) {
			key_record.key[i] = key_scan[i];
		}

		key_record.dx_h = (mouse_dx >> 8) & 0xff;
		key_record.dx_l =  mouse_dx       & 0xff;
		key_record.dy_h = (mouse_dy >> 8) & 0xff;
		key_record.dy_l =  mouse_dy       & 0xff;

		for (i = 0; i < 2; i++) {
			if (disk_image_exist(i) &&
				drive_check_empty(i) == FALSE) {
				img = disk_image_selected(i) + 1;
			} else {
				img = -1;
			}
			if (key_record.image[i] != img) {
				key_record.image[i] = img;
			} else {
				key_record.image[i] = 0;
			}
		}

		if (osd_fwrite(&key_record, sizeof(char), sizeof(key_record), fp_rec)
			== sizeof(key_record)) {
			;
		} else {
			printf("Can't write Record file <%s>\n", file_rec);
			osd_fclose(fp_rec);
			fp_rec = NULL;
		}
	}


	if (fp_pb) {

		if (osd_fread(&key_record, sizeof(char), sizeof(key_record),
					  fp_pb) == sizeof(key_record)) {
			for (i = 0; i < 0x10; i++) {
				key_scan[i] = key_record.key[i];
			}

			mouse_dx  = (int)key_record.dx_h << 8;
			mouse_dx |=      key_record.dx_l;
			mouse_dy  = (int)key_record.dy_h << 8;
			mouse_dy |=      key_record.dy_l;

			for (i = 0; i < 2; i++) {
				if (key_record.image[i] == -1) {
					drive_set_empty(i);
				} else if (disk_image_exist(i) &&
						   key_record.image[i] > 0 &&
						   key_record.image[i] <= disk_image_num(i)) {
					drive_unset_empty(i);
					disk_change_image(i, key_record.image[i] - 1);
				}
			}

		} else {
			printf(" (( %s : Playback file EOF ))\n", file_pb);
			statusbar_message(STATUS_INFO, "Playback  [EOF]", 0);
			osd_fclose(fp_pb);
			fp_pb = NULL;
		}
	}

	/* シリアルマウスは、反映タイミングが VSYNC とは異なるので、適用しない */
}



/****************************************************************************
 * 汎用 I/O ポートのワーク初期化
 *****************************************************************************/
/* 汎用 I/O ポートが、出力→入力 に切り替わった時の処理 */
void keyboard_jop1_reset(void)
{
#if 0 /* マウス関連のワークを初期化する  */

#if 0
	/* マウス座標も初期化したほうがいいかもしれないけど、
	   そこまでしなくてもいっか */
	event_get_mouse_pos(&mouse_x, &mouse_y);
	screen_mouse_movement_adjust(&mouse_x, &mouse_y);
#endif
	mouse_dx = 0;
	mouse_dy = 0;

#endif
	/* でも、実際にはこれをしなくても実害はでないと思う。*/
	/* 実害があったので、ver 0.6.2 以降では削除 */

	jop1_step = 0;
	jop1_dx = 0;
	jop1_dy = 0;
}



/****************************************************************************
 * 汎用 I/O ポート ストローブ ON/OFF
 *****************************************************************************/

/* ストローブ処理は 720state 以内に完了させる。   (×1.25はマージン) */
/*  (8MHzの場合は 1440state なんだけどまあいっか) */
#define JOP1_STROBE_LIMIT				((int)(720 * 1.25))

void keyboard_jop1_strobe(void)
{
	if (mouse_mode == MOUSE_MOUSE       &&
		(sound_reg[ 0x07 ] & 0x80) == 0) {
		/* マウス 有効 汎用I/O 入力設定時 */
		{
			int now = state_of_cpu + z80main_cpu.state0;

			/*int interval = now - jop1_time;
			  if( interval < 0 ) interval += state_of_vsync;
			  printf("JOP %d (%d)\n",jop1_step,interval);*/

			if (jop1_step == 2) {
				int interval = now - jop1_time;
				if (interval < 0) {
					interval += state_of_vsync;
				}
				if (interval > JOP1_STROBE_LIMIT) {
					keyboard_jop1_reset();
				}
			}

			jop1_time = now;
		}

		switch (jop1_step) {

		case 0:
			/* 最初のストローブ(ON)で、マウス移動量の値を確定し
			   2回目以降のストローブで、この確定した値を転送する */
		{
			int dx = mouse_dx;					/* x 方向 変位 */
			int dy = mouse_dy;					/* y 方向 変位 */

#if 1 /* 変位を±127の範囲内にクリッピングする */
			int f = 0;

			/* x、yのうち 変位が ±127を超えている方を探す。
			   ともに超えてたら、変位の大きいほうが超えたとする。*/
			if (dx < -127 || 127 < dx) {
				f |= 0x01;
			}
			if (dy < -127 || 127 < dy) {
				f |= 0x02;
			}
			if (f == 0x03) {
				if (ABS(dx) > ABS(dy)) {
					f = 0x01;
				} else {
					f = 0x02;
				}
			}
			if (f == 0x01) {
				/* x変位が ±127を超えた場合 x変位をmax値にしてyを補正 */
				dy = 127 * SGN(dx) * dy / dx;
				dx = 127 * SGN(dx);
			} else if (f == 0x02) {
				/* y変位が ±127を超えた場合  y変位をmax値にしてxを補正 */
				dx = 127 * SGN(dy) * dx / dy;
				dy = 127 * SGN(dy);
			}
#endif
			mouse_dx -= dx;
			mouse_dy -= dy;

			jop1_dx = dx;
			jop1_dy = dy;
			/*printf("%d,%d\n",jop1_dx,jop1_dy);*/
		}
		sound_reg[ 0x0e ] = ((-jop1_dx) >> 4) & 0x0f;
		break;

		case 1:
			sound_reg[ 0x0e ] = (-jop1_dx)     & 0x0f;
			break;
		case 2:
			sound_reg[ 0x0e ] = ((-jop1_dy) >> 4) & 0x0f;
			break;
		case 3:
			sound_reg[ 0x0e ] = (-jop1_dy)     & 0x0f;
			break;
		}

	} else {
		sound_reg[ 0x0e ] = 0xff;
	}

	jop1_step = (jop1_step + 1) & 0x03;
}



/***********************************************************************
 * ソフトウェアNumLock
 * カナ（ローマ字）キーロック
 ************************************************************************/
int  quasi88_now_key_numlock(void)
{
	return numlock_emu;
}
void quasi88_tgl_key_numlock(void)
{
	numlock_emu ^= 1;
	assign_key_numlock_tenkey();

	submenu_controll(CTRL_CHG_NUMLOCK);

	quasi88_notify_touchkey((numlock_emu) ?
							TOUCHKEY_NOTIFY_REQ_NUM_ON :
							TOUCHKEY_NOTIFY_REQ_NUM_OFF);
}
void quasi88_cfg_key_numlock(int on)
{
	if (((numlock_emu)          && (on == FALSE)) ||
		((numlock_emu == FALSE) && (on))) {

		quasi88_tgl_key_numlock();
	}
}

int  quasi88_now_key_caps(void)
{
	return IS_KEY88_PRESS(KEY88_CAPS);
}
void quasi88_tgl_key_caps(void)
{
	KEY88_TOGGLE(KEY88_CAPS);

	submenu_controll(CTRL_CHG_CAPSLOCK);

	quasi88_notify_touchkey(IS_KEY88_PRESS(KEY88_CAPS) ?
							TOUCHKEY_NOTIFY_REQ_CAPS_ON :
							TOUCHKEY_NOTIFY_REQ_CAPS_OFF);
}
void quasi88_cfg_key_caps(int on)
{
	if (((IS_KEY88_PRESS(KEY88_CAPS))          && (on == FALSE)) ||
		((IS_KEY88_PRESS(KEY88_CAPS) == FALSE) && (on))) {

		quasi88_tgl_key_caps();
	}
}

int  quasi88_now_key_kana(void)
{
	return IS_KEY88_PRESS(KEY88_KANA);
}
void quasi88_tgl_key_kana(void)
{
	KEY88_TOGGLE(KEY88_KANA);
	romaji_input_mode = FALSE;

	submenu_controll(CTRL_CHG_KANALOCK);

	quasi88_notify_touchkey(IS_KEY88_PRESS(KEY88_KANA) ?
							TOUCHKEY_NOTIFY_REQ_KANA_ON :
							TOUCHKEY_NOTIFY_REQ_KANA_OFF);
	quasi88_notify_touchkey(TOUCHKEY_NOTIFY_REQ_ROMAJI_OFF);
}
void quasi88_cfg_key_kana(int on)
{
	if (((IS_KEY88_PRESS(KEY88_KANA))          && (on == FALSE)) ||
		((IS_KEY88_PRESS(KEY88_KANA) == FALSE) && (on))) {

		quasi88_tgl_key_kana();
	}
}

int  quasi88_now_key_romaji(void)
{
	return romaji_input_mode;
}
void quasi88_tgl_key_romaji(void)
{
	KEY88_TOGGLE(KEY88_KANA);
	romaji_input_mode = FALSE;

	if (IS_KEY88_PRESS(KEY88_KANA)) {
		romaji_input_mode = TRUE;
		romaji_clear();
	}

	submenu_controll(CTRL_CHG_ROMAJILOCK);

	quasi88_notify_touchkey(IS_KEY88_PRESS(KEY88_KANA) ?
							TOUCHKEY_NOTIFY_REQ_KANA_ON :
							TOUCHKEY_NOTIFY_REQ_KANA_OFF);
	quasi88_notify_touchkey(romaji_input_mode ? 
							TOUCHKEY_NOTIFY_REQ_ROMAJI_ON :
							TOUCHKEY_NOTIFY_REQ_ROMAJI_OFF);
}
void quasi88_cfg_key_romaji(int on)
{
	if (((IS_KEY88_PRESS(KEY88_KANA))          && (on == FALSE)) ||
		((IS_KEY88_PRESS(KEY88_KANA) == FALSE) && (on))) {

		quasi88_tgl_key_romaji();
	}
}



/****************************************************************************
 * 【機種依存部より、キー押下時に呼び出される】
 *
 *      code は、キーコードで、 KEY88_SPACE <= code <= KEY88_SHIFTR
 *      on   は、キー押下なら真、キー開放なら偽
 *****************************************************************************/
static int do_func(int func, int on);
static void quasi88_set_last_key_function(int code);

void quasi88_key(int code, int on)
{
	int notify_code[2];

	/* SHIFT + CTRL 押下で、マウスグラブ解除 */
	{
		static char shift_on = FALSE;
		static char ctrl_on = FALSE;
		if (code == KEY88_CTRL ||
			code == KEY88_SHIFT ||
			code == KEY88_SHIFTL ||
			code == KEY88_SHIFTR) {
			if (code == KEY88_CTRL) {
				ctrl_on = on;
			} else {
				shift_on = on;
			}
			if (on && ctrl_on && shift_on) {
				if (grab_mouse == AUTO_MOUSE) {
					quasi88_ungrab();
				}
			}
		}
	}

	if (quasi88_is_exec()) {

		unsigned char p08 = key_scan[ Port8 ];

		if (key_func[ code ]) {
			/* 特殊処理割り当て済の場合 特殊機能処理を実行 */
			code = do_func(key_func[ code ], on);
			if (code == 0) {
				/* 戻値は 新キーコード */
				return;
			}
		}

		if (code == KEY88_KANA) {
			romaji_input_mode = FALSE;
			quasi88_notify_touchkey(TOUCHKEY_NOTIFY_REQ_ROMAJI_OFF);
		}

		if (romaji_input_mode && on) {
			/* ローマ字入力モードの場合 変換処理を実行 */
			if (romaji_input(code) == 0) {
				return;
			}
		}

		/* キー押下をIOポートに反映 */
		if (on) {
			KEY88_PRESS(code);
		} else {
			KEY88_RELEASE(code);
		}

		/* タッチキーに通知するキーコードを設定 */
		if (code == KEY88_SHIFT) {
			notify_code[0] = 0;
		} else {
			notify_code[0] = code;
		}
		if ((p08 ^ key_scan[ Port8 ]) & Bit6) {
			notify_code[1] = KEY88_SHIFT;
		} else {
			notify_code[1] = 0;
		}

	} else if (quasi88_is_menu_mode()) {
		int i;
		static const T_ASSIGN_KEY_TABLE menukey[] ={
			{ KEY88_KP_0,			KEY88_0,		},
			{ KEY88_KP_1,			KEY88_1,		},
			{ KEY88_KP_2,			KEY88_2,		},
			{ KEY88_KP_3,			KEY88_3,		},
			{ KEY88_KP_4,			KEY88_4,		},
			{ KEY88_KP_5,			KEY88_5,		},
			{ KEY88_KP_6,			KEY88_6,		},
			{ KEY88_KP_7,			KEY88_7,		},
			{ KEY88_KP_8,			KEY88_8,		},
			{ KEY88_KP_9,			KEY88_9,		},
			{ KEY88_KP_MULTIPLY,	KEY88_ASTERISK,	},
			{ KEY88_KP_ADD,			KEY88_PLUS,		},
			{ KEY88_KP_EQUAL,		KEY88_EQUAL,	},
			{ KEY88_KP_COMMA,		KEY88_COMMA,	},
			{ KEY88_KP_PERIOD,		KEY88_PERIOD,	},
			{ KEY88_KP_SUB,			KEY88_MINUS,	},
			{ KEY88_KP_DIVIDE,		KEY88_SLASH,	},
			{ KEY88_INS_DEL,		KEY88_BS,		},
/*			{ KEY88_DEL,			KEY88_BS,		},*/ /* DELのまま */
			{ KEY88_KETTEI,			KEY88_SPACE,	},
			{ KEY88_HENKAN,			KEY88_SPACE,	},
			{ KEY88_RETURNL,		KEY88_RETURN,	},
			{ KEY88_RETURNR,		KEY88_RETURN,	},
			{ KEY88_SHIFTL,			KEY88_SHIFT,	},
			{ KEY88_SHIFTR,			KEY88_SHIFT,	},
		};

		/* メニューではファンクションキーの機能割り当ては基本的に無効だが
		   あとから情報を引っ張ってこれるように、一時的に覚えておく */
		quasi88_set_last_key_function(code);

		/* メニューでは特殊処理割り当ては無効だが、以下だけは処理する */
		switch (code) {
		case KEY88_SYS_PREV:
			quasi88_notify_touchkey(TOUCHKEY_NOTIFY_REQ_PREV);
			return;
		case KEY88_SYS_NEXT:
			quasi88_notify_touchkey(TOUCHKEY_NOTIFY_REQ_NEXT);
			return;
		case KEY88_SYS_NUM:
			quasi88_cfg_key_numlock(on);
			return;
		}

		/* 先に、 NUM lock 時のキーや、システムのキーを読み替え */
		if (key_func[ code ] >= FN_end) {
			code = key_func[ code ];
		}

		/* 次に、メニュー用にキーを読み替え */
		for (i = 0; i < COUNTOF(menukey); i++) {
			if (code == menukey[i].code) {
				code = menukey[i].func;
				break;
			}
		}

		/* キー押下をQ8tkに通知 */
		if (on) {
			q8tk_event_key_on(code);
		} else {
			q8tk_event_key_off(code);
		}

		/* タッチキーに通知するキーコードを設定 */
		notify_code[0] = code;
		notify_code[1] = 0;
	}

	/* シフト、カナ、CAPSキーは、タッチキーにも状態を通知する */
	{
		static const struct {
			unsigned char code;
			unsigned char on_val;
			unsigned char off_val;
		} table[] = {
			{ KEY88_SHIFT,  TOUCHKEY_NOTIFY_REQ_SHIFT_ON,  TOUCHKEY_NOTIFY_REQ_SHIFT_OFF,  },
			{ KEY88_SHIFTL, TOUCHKEY_NOTIFY_REQ_SHIFTL_ON, TOUCHKEY_NOTIFY_REQ_SHIFTL_OFF, },
			{ KEY88_SHIFTR, TOUCHKEY_NOTIFY_REQ_SHIFTR_ON, TOUCHKEY_NOTIFY_REQ_SHIFTR_OFF, },
			{ KEY88_KANA,   TOUCHKEY_NOTIFY_REQ_KANA_ON,   TOUCHKEY_NOTIFY_REQ_KANA_OFF,   },
			{ KEY88_CAPS,   TOUCHKEY_NOTIFY_REQ_CAPS_ON,   TOUCHKEY_NOTIFY_REQ_CAPS_OFF,   },
			{ KEY88_CTRL,   TOUCHKEY_NOTIFY_REQ_CTRL_ON,   TOUCHKEY_NOTIFY_REQ_CTRL_OFF,   },
			{ KEY88_GRAPH,  TOUCHKEY_NOTIFY_REQ_GRAPH_ON,  TOUCHKEY_NOTIFY_REQ_GRAPH_OFF,  },
		};
		size_t i;
		for (i = 0; i < COUNTOF(table); i++) {
			if (notify_code[0] == table[i].code ||
				notify_code[1] == table[i].code) {
				if (on) {
					quasi88_notify_touchkey( table[i].on_val );
				} else {
					quasi88_notify_touchkey( table[i].off_val );
				}
			}
		}
	}
#if 0
	{
		static int first=FALSE;
		if (first == FALSE) {
			printf("    ssrr       ss dib09876       du c/-chldt es54321s cskgiruh _/.,;:98 76543210 -^]\\[ZYX WVUTSRQP ONMLKJIH GFEDCBA@ r.,=+*98 76543210\n");
			first = TRUE;
		}
		for (int i = 0x0e; i >= 0; i--)
			printf("%x%x%x%x%x%x%x%x ",
				   (key_scan[i] >> 7) & 1, (key_scan[i] >> 6) & 1,
				   (key_scan[i] >> 5) & 1, (key_scan[i] >> 4) & 1,
				   (key_scan[i] >> 3) & 1, (key_scan[i] >> 2) & 1,
				   (key_scan[i] >> 1) & 1, (key_scan[i] >> 0) & 1);
		printf("\r");
	}
#endif
}

/***********************************************************************
 * メニューモード時の、キーの機能割当を取得
 *
 ************************************************************************/
static int last_key_code;
static int last_key_function;
static void quasi88_set_last_key_function(int code)
{
	last_key_code = code;
	if (key_func[ code ]) {
		last_key_function = key_func[ code ];
	} else {
		last_key_function = code;
	}
}
int quasi88_get_last_key_function(int code)
{
	if (code == last_key_code) {
		return last_key_function;
	} else {
		return FN_FUNC;
	}
}





/*----------------------------------------------------------------------
 * ファンクションキーに割り当てた機能の処理
 *              戻り値は、新たなキーコード (0ならキー押下なし扱い)
 *----------------------------------------------------------------------*/
static void change_framerate(int sign);
static void change_volume(int sign);
static void change_wait(int sign);
static void change_max_speed(int new_speed);
static void change_max_clock(double new_clock);
static void change_max_boost(int new_boost);

static int do_func(int func, int on)
{
	switch (func) {
	case FN_FUNC:
		/* 機能なし */
		return 0;

	case FN_FRATE_UP:
		/* フレーム */
		if (on) {
			change_framerate(+1);
		}
		return 0;
	case FN_FRATE_DOWN:
		/* フレーム */
		if (on) {
			change_framerate(-1);
		}
		return 0;

	case FN_VOLUME_UP:
		/* 音量 */
		if (on) {
			change_volume(-1);
		}
		return 0;
	case FN_VOLUME_DOWN:
		/* 音量 */
		if (on) {
			change_volume(+1);
		}
		return 0;

	case FN_PAUSE:
		/* 一時停止 */
		if (on) {
			quasi88_pause();
		}
		return 0;

	case FN_RESIZE:
		/* リサイズ */
		if (on) {
			quasi88_cfg_set_size_large();
		}
		return 0;

	case FN_NOWAIT:
		/* ウエイト */
		if (on) {
			change_wait(0);
		}
		return 0;
	case FN_SPEED_UP:
		if (on) {
			change_wait(+1);
		}
		return 0;
	case FN_SPEED_DOWN:
		if (on) {
			change_wait(-1);
		}
		return 0;

	case FN_FULLSCREEN:
		/* 全画面切り替え */
		if (on) {
			if (quasi88_cfg_can_fullscreen()) {
				int now  = quasi88_cfg_now_fullscreen();
				int next = (now) ? FALSE : TRUE;
				quasi88_cfg_set_fullscreen(next);
			}
		}
		return 0;

	case FN_IMAGE_NEXT1:
		/* DRIVE1: イメージ変更 */
		if (on) {
			quasi88_disk_image_empty(DRIVE_1);
		} else {
			quasi88_disk_image_next(DRIVE_1);
		}
		return 0;
	case FN_IMAGE_PREV1:
		if (on) {
			quasi88_disk_image_empty(DRIVE_1);
		} else {
			quasi88_disk_image_prev(DRIVE_1);
		}
		return 0;
	case FN_IMAGE_NEXT2:
		/* DRIVE2: イメージ変更 */
		if (on) {
			quasi88_disk_image_empty(DRIVE_2);
		} else {
			quasi88_disk_image_next(DRIVE_2);
		}
		return 0;
	case FN_IMAGE_PREV2:
		if (on) {
			quasi88_disk_image_empty(DRIVE_2);
		} else {
			quasi88_disk_image_prev(DRIVE_2);
		}
		return 0;

	case FN_NUMLOCK:
		/* NUM lock */
		if (on) {
			quasi88_tgl_key_numlock();
		}
		return 0;

	case FN_RESET:
		/* リセット */
		if (on) {
			quasi88_reset(NULL);
		}
		return 0;

	case FN_KANA:
		/* カナ */
		if (on) {
			quasi88_tgl_key_kana();
		}
		return 0;

	case FN_ROMAJI:
		/* カナ(ローマ字) */
		if (on) {
			quasi88_tgl_key_romaji();
		}
		return 0;

	case FN_CAPS:
		/* CAPS */
		if (on) {
			quasi88_tgl_key_caps();
		}
		return 0;

	case FN_SNAPSHOT:
		/* スクリーンスナップショット*/
		if (on) {
			quasi88_screen_snapshot();
		}
		return 0;

	case FN_MAX_SPEED:
		if (on) {
			change_max_speed(fn_max_speed);
		}
		return 0;
	case FN_MAX_CLOCK:
		if (on) {
			change_max_clock(fn_max_clock);
		}
		return 0;
	case FN_MAX_BOOST:
		if (on) {
			change_max_boost(fn_max_boost);
		}
		return 0;

	case FN_ROMAJI_KEY:
		quasi88_cfg_key_romaji(on);
		return 0;

	case FN_NUM_KEY:
		quasi88_cfg_key_numlock(on);
		return 0;

	case FN_PREV_KEY:
		quasi88_notify_touchkey(TOUCHKEY_NOTIFY_REQ_PREV);
		return 0;

	case FN_NEXT_KEY:
		quasi88_notify_touchkey(TOUCHKEY_NOTIFY_REQ_NEXT);
		return 0;

	case FN_STATUS:
		/* FDDステータス表示 */
		if (on) {
#if 1
			int bit = ((quasi88_cfg_now_toolbar()   ? 0x0 : 0x2) |
					   (quasi88_cfg_now_statusbar() ? 0x0 : 0x1));
			bit = (bit + 1) & 0x3;
			quasi88_cfg_set_toolbar((bit & 0x2) ? FALSE : TRUE);
			quasi88_cfg_set_statusbar((bit & 0x1) ? FALSE : TRUE);
#else
			static const t[] = { 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, };
			static const s[] = { 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, };
			static int cycle = 0;
			cycle = (cycle + 1) % 13;
			quasi88_cfg_set_toolbar(t[cycle]);
			quasi88_cfg_set_statusbar(s[cycle]);
#endif
		}
		return 0;
	case FN_MENU:
		/* メニューモード */
		if (on) {
			quasi88_menu();
		}
		return 0;

	}
	return func;
}



/*
 * フレームスキップ数変更 : -frameskip の値を、変更する。
 */
static void change_framerate(int sign)
{
	int i, rate;
	/*                                 framerate up ←                  */
	static const int list[] = { 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60, };
	/*                                              → framerate down   */

	rate = quasi88_cfg_now_frameskip_rate();

	if (sign > 0) {
		/* framerate up */

		for (i = COUNTOF(list) - 1; i >= 0; i--) {
			if (list[i] < rate) {
				rate = list[i];
				break;
			}
		}
		if (i < 0) {
			rate = list[COUNTOF(list) - 1];
		}

	} else {
		/* framerate down*/

		for (i = 0; i < COUNTOF(list); i++) {
			if (rate < list[i]) {
				rate = list[i];
				break;
			}
		}
		if (i == COUNTOF(list)) {
			rate = list[0];
		}
	}

	quasi88_cfg_set_frameskip_rate(rate);
}



/*
 * ボリューム変更 : -vol の値を、変更する。
 */
static void change_volume(int sign)
{
#ifdef USE_SOUND
	int diff, vol;
	char str[32];

	if (xmame_has_sound()) {
		if (xmame_has_mastervolume()) {
			diff = (sign > 0) ? +1 : ((sign < 0) ? -1 : 0);
			if (diff) {
				vol = xmame_cfg_get_mastervolume() + diff;
				if (vol >   0) {
					vol = 0;
				}
				if (vol < -32) {
					vol = -32;
				}
				xmame_cfg_set_mastervolume(vol);
			}

			sprintf(str, "VOLUME  %3d[db]", xmame_cfg_get_mastervolume());
			statusbar_message(STATUS_INFO, str, 0);
			/* 変更した後は、しばらく画面に音量を表示させる */
		}
	}
#endif
}



/*
 * ウェイト量変更 : -nowait, -speed の値を、変更する。
 */
static void change_wait(int sign)
{
	int w;

	if (sign == 0) {
		/* ウェイト有無の変更 */

		w = quasi88_cfg_now_no_wait();
		w ^= 1;
		quasi88_cfg_set_no_wait(w);

	} else {
		/* ウェイト比率の増減 */

		w = quasi88_cfg_now_wait_rate();

		if (sign < 0) {
			w -= 10;
			if (w < 10) {
				w = 10;
			}
		} else {
			w += 10;
			if (w > 200) {
				w = 200;
			}
		}

		quasi88_cfg_set_wait_rate(w);
	}
}


/*
 * 速度変更 (speed/clock/boost)
 */
static void change_max_speed(int new_speed)
{
	char str[32];

	if (!(5 <= new_speed || new_speed <= 5000)) {
		new_speed = 1600;
	}

	if (wait_rate < new_speed) {
		wait_rate = new_speed;
	} else {
		wait_rate = 100;
	}

	wait_vsync_switch();

	no_wait = 0;

	sprintf(str, "WAIT  %4d[%%]", wait_rate);
	statusbar_message(STATUS_INFO, str, 0);
}
static void change_max_clock(double new_clock)
{
	double def_clock = (boot_clock_4mhz ? CONST_4MHZ_CLOCK : CONST_8MHZ_CLOCK);
	char str[32];

	if (!(0.1 <= new_clock && new_clock < 1000.0)) {
		new_clock = CONST_4MHZ_CLOCK * 16;
	}

	if (cpu_clock_mhz < new_clock) {
		cpu_clock_mhz = new_clock;
	} else {
		cpu_clock_mhz = def_clock;
	}
	interval_work_init_all();

	sprintf(str, "CLOCK %8.4f[MHz]", cpu_clock_mhz);
	statusbar_message(STATUS_INFO, str, 0);
}
static void change_max_boost(int new_boost)
{
	char str[32];

	if (!(1 <= new_boost || new_boost <= 100)) {
		new_boost = 16;
	}

	if (boost < new_boost) {
		boost_change(new_boost);
	} else {
		boost_change(1);
	}

	sprintf(str, "BOOST [x%2d]", boost);
	statusbar_message(STATUS_INFO, str, 0);
}













/******************************************************************************
 * 【機種依存部より、ジョイスティック入力時に呼び出される】
 *
 *      code は、キーコードで、 KEY88_PAD1_UP <= code <= KEY88_PAD1_H
 *      on   は、キー押下なら真、キー開放なら偽
 *****************************************************************************/
void quasi88_pad(int code, int on)
{
	if (quasi88_is_exec()) {
		/*===================================*/

		if (key_func[ code ]) {
			/* キーコードの読み替え */
			/* 特殊機能があれば、処理されるので注意 */
			code = do_func(key_func[ code ], on);
			if (code == 0) {
				return;
			}
		}
		/* ローマ字入力や、後期型キーは処理しない */

		/* I/Oポートへ反映 */
		if (on) {
			KEY88_PRESS(code);
		} else {
			KEY88_RELEASE(code);
		}

	}
}






/****************************************************************************
 * マウス移動
 *		dx, dy はマウスの移動量
 *		abs_mode が真なら、マウス移動は絶対座標モード
 *****************************************************************************/
void keyboard_mouse_move(int dx, int dy, int abs_mode)
{
	if (quasi88_is_exec()) {
		if (abs_mode) {
			/* マウスの速度調整はしない */
			;

		} else {
			/* マウスの速度調整 */
			dx = dx * mouse_sensitivity / 100;
			dy = dy * mouse_sensitivity / 100;
		}

		mouse_dx += dx;
		mouse_dy += dy;

		mouse_sx += dx;
		mouse_sy += dy;
	}
}



/******************************************************************************
 * マウスボタン押下
 *      code は、キーコードで、 KEY88_MOUSE_L <= code <= KEY88_MOUSE_DOWN
 *      on   は、キー押下なら真、キー開放なら偽
 *****************************************************************************/
void keyboard_mouse_button(int code, int on)
{
	if (quasi88_is_exec()) {

		/* シリアルマウス用のワークセット (キーコード読み替え前) */
		if (code == KEY88_MOUSE_L) {
			if (on) {
				mouse_sb |=  0x20;
			} else {
				mouse_sb &= ~0x20;
			}
		}
		if (code == KEY88_MOUSE_R) {
			if (on) {
				mouse_sb |=  0x10;
			} else {
				mouse_sb &= ~0x10;
			}
		}

		if (key_func[ code ]) {
			/* キーコードの読み替え */
			/* 特殊機能があれば、処理されるので注意 */
			code = do_func(key_func[ code ], on);
			if (code == 0) {
				return;
			}
		}
		/* ローマ字入力や、後期型キーは処理しない */

		/* I/Oポートへ反映 */
		if (on) {
			KEY88_PRESS(code);
		} else {
			KEY88_RELEASE(code);
		}
	}
}




/****************************************************************************
 *
 *****************************************************************************/
static void mouse_to_key(int code, int on)
{
	if (quasi88_is_exec()) {
		/*===================================*/

		if (key_func[ code ]) {
			/* キーコードの読み替え */
			/* 特殊機能があれば、処理されるので注意 */
			code = do_func(key_func[ code ], on);
			if (code == 0) {
				return;
			}
		}
		/* ローマ字入力は処理しない */

		/* I/Oポートへ反映 */
		if (on) {
			KEY88_PRESS(code);
		} else {
			KEY88_RELEASE(code);
		}
	}
}

/****************************************************************************
 * メニューモードのソフトウェアキーボード処理 (ワークとして scan_key を使用)
 *****************************************************************************/

/* ソフトウェアキーボードが押された状態なら真を返す */
int  softkey_is_pressed(int code)
{
	return ((key_scan[ keyport[(code)].port ] & keyport[(code)].mask) == 0);
}

/* ソフトウェアキーボードを押す */
void softkey_press(int code)
{
	KEY88_PRESS(code);
}

/* ソフトウェアキーボードを離す */
void  softkey_release(int code)
{
	KEY88_RELEASE(code);
}

/* ソフトウェアキーボードを全て離す */
void softkey_release_all(void)
{
	size_t i;
	for (i = 0; i < sizeof(key_scan); i++) {
		key_scan[i] = 0xff;
	}
}

/* ソフトウェアキーボードの、キーボードバグを再現 */
void softkey_bug(void)
{
	int  my_port, your_port;
	byte my_val,  your_val,  save_val;

	save_val = key_scan[8] & 0xf0;      /* port 8 の 上位 4bit は対象外 */
	key_scan[8] |= 0xf0;

	/* port 0..11(初期型キーボードの範囲) のみ、処理する */
	for (my_port = 0; my_port < 12; my_port++) {
		for (your_port = 0; your_port < 12; your_port++) {

			if (my_port == your_port) {
				continue;
			}

			my_val   = key_scan[ my_port ];
			your_val = key_scan[ your_port ];

			if ((my_val | your_val) != 0xff) {
				key_scan[ my_port ]   =
					key_scan[ your_port ] = my_val & your_val;
			}

		}
	}

	key_scan[8] &= ~0xf0;
	key_scan[8] |= save_val;
}



/*----------------------------------------------------------------------
 *
 *----------------------------------------------------------------------*/
int quasi88_now_mouse_mode(void)
{
	return mouse_mode;
}

void quasi88_set_mouse_mode(int mode)
{
	mouse_mode = mode;
	assign_key_mouse_joystick();

	submenu_controll(CTRL_CHG_MOUSE);
}

int quasi88_now_cursor_key_mode(void)
{
	return cursor_key_mode;
}

void quasi88_set_cursor_key_mode(int mode)
{
	cursor_key_mode = mode;
	assign_key_cursorkey();

	submenu_controll(CTRL_CHG_CURSOR);
}




/***********************************************************************
 * ステートロード／ステートセーブ
 ************************************************************************/
/* ver 0.6.2 以前と ver 0.6.3 以降でファンクションキーの機能が変わったので、
   ステートロード／ステートセーブの際に変換する。*/

enum { /* ver 0.6.2以前の、ファンクションキーの機能    */
	OLD_FN_FUNC,
	OLD_FN_FRATE_UP,
	OLD_FN_FRATE_DOWN,
	OLD_FN_VOLUME_UP,
	OLD_FN_VOLUME_DOWN,
	OLD_FN_PAUSE,
	OLD_FN_RESIZE,
	OLD_FN_NOWAIT,
	OLD_FN_SPEED_UP,
	OLD_FN_SPEED_DOWN,
	OLD_FN_MOUSE_HIDE,
	OLD_FN_FULLSCREEN,
	OLD_FN_IMAGE_NEXT1,
	OLD_FN_IMAGE_PREV1,
	OLD_FN_IMAGE_NEXT2,
	OLD_FN_IMAGE_PREV2,
	OLD_FN_NUMLOCK,
	OLD_FN_RESET,
	OLD_FN_KANA,
	OLD_FN_ROMAJI,
	OLD_FN_CAPS,
	OLD_FN_KETTEI,
	OLD_FN_HENKAN,
	OLD_FN_ZENKAKU,
	OLD_FN_PC,
	OLD_FN_SNAPSHOT,
	OLD_FN_STOP,
	OLD_FN_COPY,
	OLD_FN_STATUS,
	OLD_FN_MENU,
	OLD_FN_end
};
static struct { /* ver 0.6.3以降との、機能の対応表 */
	int     old;
	int     now;
} func_f_convert[] = {
	{ OLD_FN_FUNC,            FN_FUNC,        },
	{ OLD_FN_FRATE_UP,        FN_FRATE_UP,    },
	{ OLD_FN_FRATE_DOWN,      FN_FRATE_DOWN,  },
	{ OLD_FN_VOLUME_UP,       FN_VOLUME_UP,   },
	{ OLD_FN_VOLUME_DOWN,     FN_VOLUME_DOWN, },
	{ OLD_FN_PAUSE,           FN_PAUSE,       },
	{ OLD_FN_RESIZE,          FN_RESIZE,      },
	{ OLD_FN_NOWAIT,          FN_NOWAIT,      },
	{ OLD_FN_SPEED_UP,        FN_SPEED_UP,    },
	{ OLD_FN_SPEED_DOWN,      FN_SPEED_DOWN,  },
	{ OLD_FN_MOUSE_HIDE,      FN_FUNC,        },
	{ OLD_FN_FULLSCREEN,      FN_FULLSCREEN,  },
	{ OLD_FN_IMAGE_NEXT1,     FN_IMAGE_NEXT1, },
	{ OLD_FN_IMAGE_PREV1,     FN_IMAGE_PREV1, },
	{ OLD_FN_IMAGE_NEXT2,     FN_IMAGE_NEXT2, },
	{ OLD_FN_IMAGE_PREV2,     FN_IMAGE_PREV2, },
	{ OLD_FN_NUMLOCK,         FN_NUMLOCK,     },
	{ OLD_FN_RESET,           FN_RESET,       },
	{ OLD_FN_KANA,            FN_KANA,        },
	{ OLD_FN_ROMAJI,          FN_ROMAJI,      },
	{ OLD_FN_CAPS,            FN_CAPS,        },
	{ OLD_FN_KETTEI,          KEY88_KETTEI,   },
	{ OLD_FN_HENKAN,          KEY88_HENKAN,   },
	{ OLD_FN_ZENKAKU,         KEY88_ZENKAKU,  },
	{ OLD_FN_PC,              KEY88_PC,       },
	{ OLD_FN_SNAPSHOT,        FN_SNAPSHOT,    },
	{ OLD_FN_STOP,            KEY88_STOP,     },
	{ OLD_FN_COPY,            KEY88_COPY,     },
	{ OLD_FN_STATUS,          FN_STATUS,      },
	{ OLD_FN_MENU,            FN_MENU,        },
	{ OLD_FN_FUNC,            FN_MAX_SPEED,   },
	{ OLD_FN_FUNC,            FN_MAX_CLOCK,   },
	{ OLD_FN_FUNC,            FN_MAX_BOOST,   },
};
static int old_func_f[ 1 + 20 ];
static void function_new2old(void)
{
	int i, j;
	for (i = 1; i <= 20; i++) {
		old_func_f[i] = OLD_FN_FUNC;
		for (j = 0; j < COUNTOF(func_f_convert); j++) {
			if (function_f[i] == func_f_convert[j].now) {
				old_func_f[i] = func_f_convert[j].old;
				break;
			}
		}
	}
}
static void function_old2new(void)
{
	int i, j;
	for (i = 1; i <= 20; i++) {
		function_f[i] = FN_FUNC;
		for (j = 0; j < COUNTOF(func_f_convert); j++) {
			if (old_func_f[i] == func_f_convert[j].old) {
				function_f[i] = func_f_convert[j].now;
				break;
			}
		}
	}
}



#define SID		"KYBD"
#define SID2	"KYB2"
#define SID3	"KYB3"
#define SID4	"KYB4"

static T_SUSPEND_W suspend_keyboard_work[] = {
	{ TYPE_INT,   &jop1_step              },
	{ TYPE_INT,   &jop1_dx                },
	{ TYPE_INT,   &jop1_dy                },

	{ TYPE_INT,   &romaji_input_mode      },

	{ TYPE_INT,   &mouse_mode             },
	{ TYPE_INT,   &mouse_key_mode         },
	{ TYPE_INT,   &mouse_key_assign[ 0]   },
	{ TYPE_INT,   &mouse_key_assign[ 1]   },
	{ TYPE_INT,   &mouse_key_assign[ 2]   },
	{ TYPE_INT,   &mouse_key_assign[ 3]   },
	{ TYPE_INT,   &mouse_key_assign[ 4]   },
	{ TYPE_INT,   &mouse_key_assign[ 5]   },

	{ TYPE_INT,   &joy_key_mode           },
	{ TYPE_INT,   &joy_key_assign[ 0]     },
	{ TYPE_INT,   &joy_key_assign[ 1]     },
	{ TYPE_INT,   &joy_key_assign[ 2]     },
	{ TYPE_INT,   &joy_key_assign[ 3]     },
	{ TYPE_INT,   &joy_key_assign[ 4]     },
	{ TYPE_INT,   &joy_key_assign[ 5]     },
	{ TYPE_INT,   &joy_key_assign[ 6]     },
	{ TYPE_INT,   &joy_key_assign[ 7]     },
	{ TYPE_INT,   &joy_key_assign[ 8]     },
	{ TYPE_INT,   &joy_key_assign[ 9]     },
	{ TYPE_INT,   &joy_key_assign[10]     },
	{ TYPE_INT,   &joy_key_assign[11]     },
	{ TYPE_INT,   &joy_swap_button        },

	{ TYPE_INT,   &cursor_key_mode        },
	{ TYPE_INT,   &cursor_key_assign[0]   },
	{ TYPE_INT,   &cursor_key_assign[1]   },
	{ TYPE_INT,   &cursor_key_assign[2]   },
	{ TYPE_INT,   &cursor_key_assign[3]   },

	{ TYPE_INT,   &tenkey_emu             },
	{ TYPE_INT,   &numlock_emu            },

	{ TYPE_INT,   &old_func_f[ 1]         },
	{ TYPE_INT,   &old_func_f[ 2]         },
	{ TYPE_INT,   &old_func_f[ 3]         },
	{ TYPE_INT,   &old_func_f[ 4]         },
	{ TYPE_INT,   &old_func_f[ 5]         },
	{ TYPE_INT,   &old_func_f[ 6]         },
	{ TYPE_INT,   &old_func_f[ 7]         },
	{ TYPE_INT,   &old_func_f[ 8]         },
	{ TYPE_INT,   &old_func_f[ 9]         },
	{ TYPE_INT,   &old_func_f[10]         },
	{ TYPE_INT,   &old_func_f[11]         },
	{ TYPE_INT,   &old_func_f[12]         },
	{ TYPE_INT,   &old_func_f[13]         },
	{ TYPE_INT,   &old_func_f[14]         },
	{ TYPE_INT,   &old_func_f[15]         },
	{ TYPE_INT,   &old_func_f[16]         },
	{ TYPE_INT,   &old_func_f[17]         },
	{ TYPE_INT,   &old_func_f[18]         },
	{ TYPE_INT,   &old_func_f[19]         },
	{ TYPE_INT,   &old_func_f[20]         },

	{ TYPE_INT,   &romaji_type            },

	{ TYPE_END,   0                       },
};

static T_SUSPEND_W suspend_keyboard_work2[] = {
	{ TYPE_INT,   &jop1_time              },
	{ TYPE_END,   0                       },
};

static T_SUSPEND_W suspend_keyboard_work3[] = {
	{ TYPE_INT,   &function_f[ 1]         },
	{ TYPE_INT,   &function_f[ 2]         },
	{ TYPE_INT,   &function_f[ 3]         },
	{ TYPE_INT,   &function_f[ 4]         },
	{ TYPE_INT,   &function_f[ 5]         },
	{ TYPE_INT,   &function_f[ 6]         },
	{ TYPE_INT,   &function_f[ 7]         },
	{ TYPE_INT,   &function_f[ 8]         },
	{ TYPE_INT,   &function_f[ 9]         },
	{ TYPE_INT,   &function_f[10]         },
	{ TYPE_INT,   &function_f[11]         },
	{ TYPE_INT,   &function_f[12]         },
	{ TYPE_INT,   &function_f[13]         },
	{ TYPE_INT,   &function_f[14]         },
	{ TYPE_INT,   &function_f[15]         },
	{ TYPE_INT,   &function_f[16]         },
	{ TYPE_INT,   &function_f[17]         },
	{ TYPE_INT,   &function_f[18]         },
	{ TYPE_INT,   &function_f[19]         },
	{ TYPE_INT,   &function_f[20]         },
	{ TYPE_END,   0                       },
};

static T_SUSPEND_W suspend_keyboard_work4[] = {
	{ TYPE_INT,   &mouse_sensitivity      },
	{ TYPE_INT,   &mouse_swap_button      },

	{ TYPE_INT,   &joy2_key_mode          },
	{ TYPE_INT,   &joy2_key_assign[ 0]    },
	{ TYPE_INT,   &joy2_key_assign[ 1]    },
	{ TYPE_INT,   &joy2_key_assign[ 2]    },
	{ TYPE_INT,   &joy2_key_assign[ 3]    },
	{ TYPE_INT,   &joy2_key_assign[ 4]    },
	{ TYPE_INT,   &joy2_key_assign[ 5]    },
	{ TYPE_INT,   &joy2_key_assign[ 6]    },
	{ TYPE_INT,   &joy2_key_assign[ 7]    },
	{ TYPE_INT,   &joy2_key_assign[ 8]    },
	{ TYPE_INT,   &joy2_key_assign[ 9]    },
	{ TYPE_INT,   &joy2_key_assign[10]    },
	{ TYPE_INT,   &joy2_key_assign[11]    },
	{ TYPE_INT,   &joy2_swap_button       },

	{ TYPE_INT,   &serial_mouse_x         },
	{ TYPE_INT,   &serial_mouse_y         },
	{ TYPE_INT,   &serial_mouse_step      },

	{ TYPE_END,   0                       },
};


int statesave_keyboard(void)
{
	function_new2old();

	if (statesave_table(SID, suspend_keyboard_work) != STATE_OK) {
		return FALSE;
	}

	if (statesave_table(SID2, suspend_keyboard_work2) != STATE_OK) {
		return FALSE;
	}

	if (statesave_table(SID3, suspend_keyboard_work3) != STATE_OK) {
		return FALSE;
	}

	if (statesave_table(SID4, suspend_keyboard_work4) != STATE_OK) {
		return FALSE;
	}

	return TRUE;
}

int stateload_keyboard(void)
{
	if (stateload_table(SID, suspend_keyboard_work) != STATE_OK) {
		return FALSE;
	}

	if (stateload_table(SID2, suspend_keyboard_work2) != STATE_OK) {

		/* 旧バージョンなら、みのがす */

		printf("stateload : Statefile is old. (ver 0.6.0 or 1?)\n");

		goto NOT_HAVE_SID2;
	}

	if (stateload_table(SID3, suspend_keyboard_work3) != STATE_OK) {

		/* 旧バージョンなら、みのがす */

		printf("stateload : Statefile is old. (ver 0.6.0, 1 or 2?)\n");

		goto NOT_HAVE_SID3;
	}

	if (stateload_table(SID4, suspend_keyboard_work4) != STATE_OK) {

		/* 旧バージョンなら、みのがす */

		printf("stateload : Statefile is old. (ver 0.6.0, 1, 2 or 3?)\n");
	}

	return TRUE;



NOT_HAVE_SID2:
	/* この関数の呼び出し以前に、 stateload_pc88main と stateload_intr が
	   呼び出されていなければ、以下の初期化は意味がない */

	jop1_time = state_of_cpu + z80main_cpu.state0;


NOT_HAVE_SID3:
	/* function_f[] を差し替える */
	function_old2new();


	return TRUE;
}










/****************************************************************************
 *
 *      ユーティリティ
 *
 *****************************************************************************/

/* QUASI88 キーコードの文字列を int 値に変換するテーブル */

static const T_SYMBOL_TABLE key88sym_list[] = {
	{ "KEY88_INVALID",          KEY88_INVALID           },
	{ "KEY88_SPACE",            KEY88_SPACE             },
	{ "KEY88_EXCLAM",           KEY88_EXCLAM            },
	{ "KEY88_QUOTEDBL",         KEY88_QUOTEDBL          },
	{ "KEY88_NUMBERSIGN",       KEY88_NUMBERSIGN        },
	{ "KEY88_DOLLAR",           KEY88_DOLLAR            },
	{ "KEY88_PERCENT",          KEY88_PERCENT           },
	{ "KEY88_AMPERSAND",        KEY88_AMPERSAND         },
	{ "KEY88_APOSTROPHE",       KEY88_APOSTROPHE        },
	{ "KEY88_PARENLEFT",        KEY88_PARENLEFT         },
	{ "KEY88_PARENRIGHT",       KEY88_PARENRIGHT        },
	{ "KEY88_ASTERISK",         KEY88_ASTERISK          },
	{ "KEY88_PLUS",             KEY88_PLUS              },
	{ "KEY88_COMMA",            KEY88_COMMA             },
	{ "KEY88_MINUS",            KEY88_MINUS             },
	{ "KEY88_PERIOD",           KEY88_PERIOD            },
	{ "KEY88_SLASH",            KEY88_SLASH             },
	{ "KEY88_0",                KEY88_0                 },
	{ "KEY88_1",                KEY88_1                 },
	{ "KEY88_2",                KEY88_2                 },
	{ "KEY88_3",                KEY88_3                 },
	{ "KEY88_4",                KEY88_4                 },
	{ "KEY88_5",                KEY88_5                 },
	{ "KEY88_6",                KEY88_6                 },
	{ "KEY88_7",                KEY88_7                 },
	{ "KEY88_8",                KEY88_8                 },
	{ "KEY88_9",                KEY88_9                 },
	{ "KEY88_COLON",            KEY88_COLON             },
	{ "KEY88_SEMICOLON",        KEY88_SEMICOLON         },
	{ "KEY88_LESS",             KEY88_LESS              },
	{ "KEY88_EQUAL",            KEY88_EQUAL             },
	{ "KEY88_GREATER",          KEY88_GREATER           },
	{ "KEY88_QUESTION",         KEY88_QUESTION          },
	{ "KEY88_AT",               KEY88_AT                },
	{ "KEY88_A",                KEY88_A                 },
	{ "KEY88_B",                KEY88_B                 },
	{ "KEY88_C",                KEY88_C                 },
	{ "KEY88_D",                KEY88_D                 },
	{ "KEY88_E",                KEY88_E                 },
	{ "KEY88_F",                KEY88_F                 },
	{ "KEY88_G",                KEY88_G                 },
	{ "KEY88_H",                KEY88_H                 },
	{ "KEY88_I",                KEY88_I                 },
	{ "KEY88_J",                KEY88_J                 },
	{ "KEY88_K",                KEY88_K                 },
	{ "KEY88_L",                KEY88_L                 },
	{ "KEY88_M",                KEY88_M                 },
	{ "KEY88_N",                KEY88_N                 },
	{ "KEY88_O",                KEY88_O                 },
	{ "KEY88_P",                KEY88_P                 },
	{ "KEY88_Q",                KEY88_Q                 },
	{ "KEY88_R",                KEY88_R                 },
	{ "KEY88_S",                KEY88_S                 },
	{ "KEY88_T",                KEY88_T                 },
	{ "KEY88_U",                KEY88_U                 },
	{ "KEY88_V",                KEY88_V                 },
	{ "KEY88_W",                KEY88_W                 },
	{ "KEY88_X",                KEY88_X                 },
	{ "KEY88_Y",                KEY88_Y                 },
	{ "KEY88_Z",                KEY88_Z                 },
	{ "KEY88_BRACKETLEFT",      KEY88_BRACKETLEFT       },
	{ "KEY88_YEN",              KEY88_YEN               },
	{ "KEY88_BRACKETRIGHT",     KEY88_BRACKETRIGHT      },
	{ "KEY88_CARET",            KEY88_CARET             },
	{ "KEY88_UNDERSCORE",       KEY88_UNDERSCORE        },
	{ "KEY88_BACKQUOTE",        KEY88_BACKQUOTE         },
	{ "KEY88_a",                KEY88_a                 },
	{ "KEY88_b",                KEY88_b                 },
	{ "KEY88_c",                KEY88_c                 },
	{ "KEY88_d",                KEY88_d                 },
	{ "KEY88_e",                KEY88_e                 },
	{ "KEY88_f",                KEY88_f                 },
	{ "KEY88_g",                KEY88_g                 },
	{ "KEY88_h",                KEY88_h                 },
	{ "KEY88_i",                KEY88_i                 },
	{ "KEY88_j",                KEY88_j                 },
	{ "KEY88_k",                KEY88_k                 },
	{ "KEY88_l",                KEY88_l                 },
	{ "KEY88_m",                KEY88_m                 },
	{ "KEY88_n",                KEY88_n                 },
	{ "KEY88_o",                KEY88_o                 },
	{ "KEY88_p",                KEY88_p                 },
	{ "KEY88_q",                KEY88_q                 },
	{ "KEY88_r",                KEY88_r                 },
	{ "KEY88_s",                KEY88_s                 },
	{ "KEY88_t",                KEY88_t                 },
	{ "KEY88_u",                KEY88_u                 },
	{ "KEY88_v",                KEY88_v                 },
	{ "KEY88_w",                KEY88_w                 },
	{ "KEY88_x",                KEY88_x                 },
	{ "KEY88_y",                KEY88_y                 },
	{ "KEY88_z",                KEY88_z                 },
	{ "KEY88_BRACELEFT",        KEY88_BRACELEFT         },
	{ "KEY88_BAR",              KEY88_BAR               },
	{ "KEY88_BRACERIGHT",       KEY88_BRACERIGHT        },
	{ "KEY88_TILDE",            KEY88_TILDE             },
	{ "KEY88_KP_0",             KEY88_KP_0              },
	{ "KEY88_KP_1",             KEY88_KP_1              },
	{ "KEY88_KP_2",             KEY88_KP_2              },
	{ "KEY88_KP_3",             KEY88_KP_3              },
	{ "KEY88_KP_4",             KEY88_KP_4              },
	{ "KEY88_KP_5",             KEY88_KP_5              },
	{ "KEY88_KP_6",             KEY88_KP_6              },
	{ "KEY88_KP_7",             KEY88_KP_7              },
	{ "KEY88_KP_8",             KEY88_KP_8              },
	{ "KEY88_KP_9",             KEY88_KP_9              },
	{ "KEY88_KP_MULTIPLY",      KEY88_KP_MULTIPLY       },
	{ "KEY88_KP_ADD",           KEY88_KP_ADD            },
	{ "KEY88_KP_EQUAL",         KEY88_KP_EQUAL          },
	{ "KEY88_KP_COMMA",         KEY88_KP_COMMA          },
	{ "KEY88_KP_PERIOD",        KEY88_KP_PERIOD         },
	{ "KEY88_KP_SUB",           KEY88_KP_SUB            },
	{ "KEY88_KP_DIVIDE",        KEY88_KP_DIVIDE         },
	{ "KEY88_RETURN",           KEY88_RETURN            },
	{ "KEY88_HOME",             KEY88_HOME              },
	{ "KEY88_UP",               KEY88_UP                },
	{ "KEY88_RIGHT",            KEY88_RIGHT             },
	{ "KEY88_INS_DEL",          KEY88_INS_DEL           },
	{ "KEY88_GRAPH",            KEY88_GRAPH             },
	{ "KEY88_KANA",             KEY88_KANA              },
	{ "KEY88_SHIFT",            KEY88_SHIFT             },
	{ "KEY88_CTRL",             KEY88_CTRL              },
	{ "KEY88_STOP",             KEY88_STOP              },
	{ "KEY88_ESC",              KEY88_ESC               },
	{ "KEY88_TAB",              KEY88_TAB               },
	{ "KEY88_DOWN",             KEY88_DOWN              },
	{ "KEY88_LEFT",             KEY88_LEFT              },
	{ "KEY88_HELP",             KEY88_HELP              },
	{ "KEY88_COPY",             KEY88_COPY              },
	{ "KEY88_CAPS",             KEY88_CAPS              },
	{ "KEY88_ROLLUP",           KEY88_ROLLUP            },
	{ "KEY88_ROLLDOWN",         KEY88_ROLLDOWN          },
	{ "KEY88_F1",               KEY88_F1                },
	{ "KEY88_F2",               KEY88_F2                },
	{ "KEY88_F3",               KEY88_F3                },
	{ "KEY88_F4",               KEY88_F4                },
	{ "KEY88_F5",               KEY88_F5                },
	{ "KEY88_SYS_PREV",         KEY88_SYS_PREV          },
	{ "KEY88_SYS_NEXT",         KEY88_SYS_NEXT          },
	{ "KEY88_SYS_FORWARD",      KEY88_SYS_FORWARD       },
	{ "KEY88_SYS_BACK",         KEY88_SYS_BACK          },
	{ "KEY88_SYS_MODE",         KEY88_SYS_MODE          },
	{ "KEY88_SYS_F6",           KEY88_SYS_F6            },
	{ "KEY88_SYS_F7",           KEY88_SYS_F7            },
	{ "KEY88_SYS_F8",           KEY88_SYS_F8            },
	{ "KEY88_SYS_F9",           KEY88_SYS_F9            },
	{ "KEY88_SYS_F10",          KEY88_SYS_F10           },
	{ "KEY88_F6",               KEY88_F6                },
	{ "KEY88_F7",               KEY88_F7                },
	{ "KEY88_F8",               KEY88_F8                },
	{ "KEY88_F9",               KEY88_F9                },
	{ "KEY88_F10",              KEY88_F10               },
	{ "KEY88_BS",               KEY88_BS                },
	{ "KEY88_INS",              KEY88_INS               },
	{ "KEY88_DEL",              KEY88_DEL               },
	{ "KEY88_HENKAN",           KEY88_HENKAN            },
	{ "KEY88_KETTEI",           KEY88_KETTEI            },
	{ "KEY88_PC",               KEY88_PC                },
	{ "KEY88_ZENKAKU",          KEY88_ZENKAKU           },
	{ "KEY88_RETURNL",          KEY88_RETURNL           },
	{ "KEY88_RETURNR",          KEY88_RETURNR           },
	{ "KEY88_SHIFTL",           KEY88_SHIFTL            },
	{ "KEY88_SHIFTR",           KEY88_SHIFTR            },

	{ "KEY88_SYS_UP",           KEY88_SYS_UP            },
	{ "KEY88_SYS_DOWN",         KEY88_SYS_DOWN          },
	{ "KEY88_SYS_LEFT",         KEY88_SYS_LEFT          },
	{ "KEY88_SYS_RIGHT",        KEY88_SYS_RIGHT         },
	{ "KEY88_SYS_ROMAJI",       KEY88_SYS_ROMAJI        },
	{ "KEY88_SYS_NUM",          KEY88_SYS_NUM           },
	{ "KEY88_SYS_STATUS",       KEY88_SYS_STATUS        },
	{ "KEY88_SYS_MENU",         KEY88_SYS_MENU          },

	/* 以下のシンボルは廃止したが、読み替えてサポートする */
	{ "KEY88_F11",              KEY88_SYS_STATUS        },
	{ "KEY88_F12",              KEY88_SYS_MENU          },
};


/***********************************************************************
 * 与えられた文字列を、QUASI88 キーコードに変換する
 *      キー定義ファイルの解析などに使おう
 *
 *   例)
 *      "KEY88_SPACE" -> KEY88_SPACE    定義そのままの文字列は、直に変換
 *      "key88_SPACE" -> KEY88_SPACE    小文字混在でもよい
 *      "KEY88_Z"     -> KEY88_Z        これも、定義そのままの例
 *      "KEY88_z"     -> KEY88_z        これも、定義そのままの例
 *      "key88_z"     -> KEY88_Z        小文字混在の場合は大文字になるよ
 *      "0x20"        -> KEY88_SPACE    0x20..0xf7 は直にキーコードに変換
 *      "0x01"        -> KEY88_INVALID  上記の範囲外なら無効(0)を返す
 *      "32"          -> KEY88_SPACE    10進数や8進数でも同様
 *      "KP1"         -> KEY88_KP_1     KP と 1文字 で、テンキーとする
 *      "KP+"         -> KEY88_KP_ADD   記号でもよい
 *      "Kp9"         -> KEY88_KP_9     小文字混在でもよい
 *      "Err"         -> -1             どれにも合致しなかったら、負を返す
 ************************************************************************/

int keyboard_str2key88(const char *str)
{
	static const T_SYMBOL_TABLE tenkey_list[] = {
		/* テンキーに限り、例外的な表記が可能 */
		{ "KP0",        KEY88_KP_0              },
		{ "KP1",        KEY88_KP_1              },
		{ "KP2",        KEY88_KP_2              },
		{ "KP3",        KEY88_KP_3              },
		{ "KP4",        KEY88_KP_4              },
		{ "KP5",        KEY88_KP_5              },
		{ "KP6",        KEY88_KP_6              },
		{ "KP7",        KEY88_KP_7              },
		{ "KP8",        KEY88_KP_8              },
		{ "KP9",        KEY88_KP_9              },
		{ "KP*",        KEY88_KP_MULTIPLY       },
		{ "KP+",        KEY88_KP_ADD            },
		{ "KP=",        KEY88_KP_EQUAL          },
		{ "KP,",        KEY88_KP_COMMA          },
		{ "KP.",        KEY88_KP_PERIOD         },
		{ "KP-",        KEY88_KP_SUB            },
		{ "KP/",        KEY88_KP_DIVIDE         },
	};

	int len, i;
	char *conv_end;
	unsigned long l;

	if (str == NULL) {
		return -1;
	}
	len = (int)strlen(str);
	if (len == 0) {
		return -1;
	}


	/* 0..9 で始まれば、数字に変換 */
	if ('0' <= str[0] && str[0] <= '9') {
		l = strtoul(str, &conv_end, 0); /* 10進,16進,8進数が可能 */
		if (*conv_end == '\0') {
			if (32 <= l && l <= 247) {
				return l;
			} else {
				return KEY88_INVALID;
			}
		}
		return -1;
	}
	/* 3文字なら、テンキーかも */
	if (len == 3) {
		for (i = 0; i < COUNTOF(tenkey_list); i++) {
			if (strcmp(tenkey_list[i].name, str) == 0) {
				return tenkey_list[i].val;
			}
		}
		for (i = 0; i < COUNTOF(tenkey_list); i++) {
			if (my_strcmp(tenkey_list[i].name, str) == 0) {
				return tenkey_list[i].val;
			}
		}
	}
	/* 定義文字列に合致するのを探す */
	for (i = 0; i < COUNTOF(key88sym_list); i++) {
		if (strcmp(key88sym_list[i].name, str) == 0) {
			return key88sym_list[i].val;
		}
	}
	for (i = 0; i < COUNTOF(key88sym_list); i++) {
		if (my_strcmp(key88sym_list[i].name, str) == 0) {
			return key88sym_list[i].val;
		}
	}

	return -1;
}


const char *keyboard_key882str(int key88)
{
	int i;
	for (i = 0; i < COUNTOF(key88sym_list); i++) {
		if (key88sym_list[i].val == KEY88_INVALID) {
			continue;
		}
		if (key88sym_list[i].val == key88) {
			return key88sym_list[i].name;
		}
	}
	return NULL;
}





/****************************************************************************
 * キー設定ファイルを読み込んで、設定を行う。
 *
 *      内部的には、キー設定ファイルを１行読むたびに、コールバック関数を呼ぶ。
 *      この行の内容を設定に反映するかどうかは、コールバック関数次第。
 *
 * --------------------------------------------------------------------------
 * キー設定ファイルの書式は、こんな感じ
 * (一行につき 最大3個のトークンを並べられる。 # から行末まではコメントとする)
 *
 *      [SDL]                   dga
 *      SDLK_ESCAPE             KEY88_ESC               KEY88_KP_4
 *      <49>                    KEY88_ZENKAKU
 *
 * 1行目のように、[..] で始まる行は、「識別タグ行」とする。
 * 1番目のトークンは [..] の形式で、2番目、3番目のトークンは任意 (無くてもよい)
 * 「識別タグ行」を読むたびに、以下のコールバック関数が呼び出される。
 *
 *      identify_callback(const char *param1, const char *param2,
 *                        const char *param3)
 *
 *      上の例の場合、引数は以下がセットされる
 *              param1 … "[SDL]"
 *              param2 … "dga"
 *              param3 … NULL (3番目のトークンがないことを意味する)
 *
 * このコールバック関数は、この「識別タグ行」が有効であれば、 NULL を返すので、
 * この次の行から、次の「識別タグ行」 ([..] で始まる行) までを、処理する。
 * 返り値が NULL でない場合は、次の「識別タグ行」までスキップする。
 * なお、返り値が空文字列 "" でなければ、ワーニングとしてそれを表示する。
 *
 * 2行目は、キーの「設定行」とする。
 * 1番目、2番目のトークンは必須で、3番目のトークンは任意 (無くてもよい)
 * 「設定行」のたびに、以下のコールバック関数が呼び出される。
 *
 *      setting_callback(int type, int code, int key88, int alter_key88);
 *
 *      上の例の場合、引数は以下がセットされる。
 *              type          … 1
 *              code          … "SDLK_ESCAPE" を int に変換したもの (後述 ※1)
 *              key88         … KEY88_ESC の enum値
 *              alter_key88   … KEY88_KP_4 の enum値
 *
 * 3行目も、キーの「設定行」だが、書式が若干異なっている。
 * この場合も、同様にコールバック関数が呼び出される。
 *
 *      callback(int type, int code, int key88, int alter_key88);
 *
 *      上の例の場合、引数は以下がセットされる。
 *              type          … 2
 *              code          … 49
 *              key88         … KEY88_ZENKAKU の enum値
 *              alter_key88   … -1 (3番目のトークンがないことを意味する)
 *
 * つまり、2行目の形式の場合は、1番目のトークンを int に変換した値が code に
 * セットされるが、3行目の形式の場合は、 <..> の中の数値がセットされる。
 *
 * このコールバック関数は、設定が有効であれば NULL を返す。返り値が NULL で
 * なく、空文字列 "" でもない場合はワーニングとしてそれを表示する。
 *
 *
 * さて、※1 のような、 SDLK_ESCAPE を int に変換する方法は、以下の配列による。
 *
 *      T_SYMBOL_TABLE  table_symbol2int[]; (配列のサイズは、 table_size)
 *
 * この配列を先頭からチェックして、 table_symbol2int[].name と1番目のトークン
 * が一致した場合に、table_symbol2int[].val の値を返す。
 *
 * table_ignore_case が真の場合、大文字小文字は無視してチェックするので、
 * 上の例の場合は "SDLK_ESCAPE" "sdlk_escape" どちらでも一致とみなす。
 *
 *
 *
 *
 *****************************************************************************/
#include <file-op.h>

static int symbol2int(const char *str,
					  const T_SYMBOL_TABLE table_symbol2int[],
					  int                  table_size,
					  int                  table_ignore_case);

/* キー設定ファイル1行あたりの最大文字数 */
#define MAX_KEYFILE_LINE		(256)

int config_read_keyconf_file(
	const char *keyconf_filename,
	const char *(*identify_callback)(const char *parm1,
									 const char *parm2,
									 const char *parm3),
	const T_SYMBOL_TABLE table_symbol2int[],
	int                  table_size,
	int                  table_ignore_case,
	const char *(*setting_callback)(int type,
									int code,
									int key88,
									int alter_key88))
{
	const char *filename;
	OSD_FILE *fp = NULL;
	int  working   = FALSE;
	int  effective = FALSE;

	int    line_cnt = 0;
	char   line[ MAX_KEYFILE_LINE ];
	char buffer[ MAX_KEYFILE_LINE ];

	char *parm1, *parm2, *parm3, *parm4;

	int  type, code, key88, alter_key88;
	const char *err_mes;


	/* キー設定ファイルを開く */

	if (keyconf_filename == NULL) {
		/* ファイル名未指定ならばデフォルト名を取得 */
		filename = filename_alloc_keyboard_cfgname();
	} else {
		filename = keyconf_filename;
	}

	if (filename) {
		fp = osd_fopen(FTYPE_CFG, filename, "r");
		if (verbose_proc) {
			if (fp) {
				printf("\"%s\" read and initialize\n", filename);
			} else {
				printf("can't open keyboard configuration file \"%s\"\n",
					   filename);
				printf("\n");
			}
		}
		if (keyconf_filename == NULL) {
			/* デフォルトならばメモリ解放しとく */
			free((void *)filename);
		}
	}

	if (fp == NULL) {
		/* 開けなかったら偽を返す */
		return FALSE;
	}



	/* キー設定ファイルを1行づつ解析 */

	while (osd_fgets(line, MAX_KEYFILE_LINE, fp)) {

		line_cnt ++;
		parm1 = parm2 = parm3 = parm4 = NULL;

		/* 行の内容をトークンに分解。各トークンは、parm1 .. parm4 にセット */

		{
			char *str = line;
			char *b;
			{
				b = &buffer[0];
				str = my_strtok(b, str);
			}
			if (str) {
				parm1 = b;
				b += strlen(b) + 1;
				str = my_strtok(b, str);
			}
			if (str) {
				parm2 = b;
				b += strlen(b) + 1;
				str = my_strtok(b, str);
			}
			if (str) {
				parm3 = b;
				b += strlen(b) + 1;
				str = my_strtok(b, str);
			}
			if (str) {
				parm4 = b;
			}
		}


		/* トークンがなければ次の行へ */
		if (parm1 == NULL) {
			continue;
		}

		/* トークンが四個以上あれば、その行はエラーなので次の行へ */
		if (parm4 != NULL) {
			if (working) {
				fprintf(stderr,
						"warning: too many argument in line %d\n", line_cnt);
			}
			continue;
		}

		/* トークンが一個..三個なら解析 */
		if (parm1[0] == '[') {
			/* 「識別タグ行」を処理 */

			/* コールバック関数を呼び出して、有効な識別タグかを判定 */
			err_mes = (identify_callback)(parm1, parm2, parm3);

			if (err_mes == NULL) {
				/* 有効な識別タグだった */
				working   = TRUE;
				effective = TRUE;

				if (verbose_proc) {
					printf("(read start in line %d)\n", line_cnt);
				}

			} else {
				/* 無効な識別タグだった */

				if (working) {
					/* 処理中なら終了 */
					if (verbose_proc) {
						printf("(read stop  in line %d)\n", line_cnt - 1);
					}
				}
				/* でなければ無視 */

				if (err_mes[0] != '\0') {
					fprintf(stderr,
							"warning: %s in %d (ignored)\n",
							err_mes, line_cnt);
				}

				working = FALSE;
			}

		} else {
			/* 「設定行」を処理 */

			if (working) {

				/* 「設定行」で、トークン一個だけは、エラー。次の行へ */
				if (parm2 == NULL) {
					fprintf(stderr,
							"warning: error in line %d (ignored)\n", line_cnt);
				} else {

					code = symbol2int(parm1, table_symbol2int,
									  table_size, table_ignore_case);
					key88 = keyboard_str2key88(parm2);

					if (parm3) {
						alter_key88 = keyboard_str2key88(parm3);
					} else       {
						alter_key88 = -1;
					}

					if (code < 0 ||
						key88 < 0 ||
						(parm3 && alter_key88 < 0)) {
						fprintf(stderr,
								"warning: error in line %d (ignored)\n", line_cnt);
					} else {

						if (parm1[0] == '<') {
							type = KEYCODE_SCAN;
						} else                 {
							type = KEYCODE_SYM;
						}

						err_mes = (setting_callback)(type, code,
													 key88, alter_key88);

						if (err_mes == NULL) {
							/* 有効な設定だった */
							/* OK */ ;
						} else {
							/* 無効な設定だった */
							/* NG */
							if (err_mes[0] != '\0') {
								fprintf(stderr,
										"warning: %s in %d (ignored)\n",
										err_mes, line_cnt);
							}
						}
					}
				}
			}
		}
	}
	osd_fclose(fp);


	if (working) {
		if (verbose_proc) {
			printf("(read end   in line %d)\n", line_cnt - 1);
		}
	}

	if (effective == FALSE) {
		fprintf(stderr, "warning: not configured (use initial config)\n");
	}

	if (verbose_proc) {
		printf("\n");
	}

	return (effective) ? TRUE : FALSE;
}


static int symbol2int(const char *str,
					  const T_SYMBOL_TABLE table_symbol2int[],
					  int                  table_size,
					  int                  table_ignore_case)
{
	int i;
	char *conv_end;
	unsigned long l;

	if (str == NULL) {
		return -1;
	}
	if (*str == '\0') {
		return -1;
	}

	if (str[0] == '<') {
		/* <数字> の場合 */
		l = strtoul(&str[1], &conv_end, 0);
		if (*conv_end == '>') {
			return l;
		}
		return -1;
	}
	/* 定義文字列に合致するのを探す */
	for (i = 0; i < table_size; i++) {
		if (strcmp(table_symbol2int[i].name, str) == 0) {
			return table_symbol2int[i].val;
		}
	}
	if (table_ignore_case) {
		for (i = 0; i < table_size; i++) {
			if (my_strcmp(table_symbol2int[i].name, str) == 0) {
				return table_symbol2int[i].val;
			}
		}
	}

	return -1;
}
