/*
 * Copyright (C) 1996-1998 Szeredi Miklos
 * Email: mszeredi@inf.bme.hu
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version. See the file COPYING. 
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* #define DEBUG_KEYS */
#include "zxmisc.h"

#include "spkey.h"
#include "spkey_p.h"

#include "spperif.h"
#include "z80.h"

#include "sptape.h"
#include "snapshot.h"
#include "spsound.h"
#include "spscr.h"

#include "interf.h"

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

int keyboard_type = 0;

/* for kempston support via SK_KP_Up and so on.... */
int cursor_type = 2;


int spkb_allow_ascii = 1;
int spkb_trueshift = -1;
int spkb_funcshift = -1;

static unsigned trueshift;
static unsigned funcshift;

struct spkeydef {
  int type;
  spkeyboard kb;
};

struct spbasekey {
  int index;
  spkeyboard kb;
  spkeyboard misc;
};

struct spnamedkey {
  const char *name;
  spkeyboard kb;
  spkeyboard misc;
};

extern int endofsingle;
extern int sp_nosync;
extern int showframe;
extern int privatemap;

spkeyboard spkey_state;
spkeyboard spmisc_state;

static spkeyboard oldstate = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

struct keystate spkb_kbstate[NR_SPKEYS];
struct onekey   spkb_last;

int spkb_state_changed;

#define SKE {0, 0, 0, 0, 0, 0, 0, 0}

#define SKP(x) (1 << x)

#define SKN0(x) {SKP(x), 0, 0, 0, 0, 0, 0, 0} 
#define SKN1(x) {0, SKP(x), 0, 0, 0, 0, 0, 0} 
#define SKN2(x) {0, 0, SKP(x), 0, 0, 0, 0, 0} 
#define SKN3(x) {0, 0, 0, SKP(x), 0, 0, 0, 0} 
#define SKN4(x) {0, 0, 0, 0, SKP(x), 0, 0, 0} 
#define SKN5(x) {0, 0, 0, 0, 0, SKP(x), 0, 0} 
#define SKN6(x) {0, 0, 0, 0, 0, 0, SKP(x), 0} 
#define SKN7(x) {0, 0, 0, 0, 0, 0, 0, SKP(x)} 

#define SKCS0(x) {SKP(0) | SKP(x), 0, 0, 0, 0, 0, 0, 0} 
#define SKCS1(x) {SKP(0), SKP(x), 0, 0, 0, 0, 0, 0} 
#define SKCS2(x) {SKP(0), 0, SKP(x), 0, 0, 0, 0, 0} 
#define SKCS3(x) {SKP(0), 0, 0, SKP(x), 0, 0, 0, 0} 
#define SKCS4(x) {SKP(0), 0, 0, 0, SKP(x), 0, 0, 0} 
#define SKCS5(x) {SKP(0), 0, 0, 0, 0, SKP(x), 0, 0} 
#define SKCS6(x) {SKP(0), 0, 0, 0, 0, 0, SKP(x), 0} 
#define SKCS7(x) {SKP(0), 0, 0, 0, 0, 0, 0, SKP(x)} 


#define SKSS0(x) {SKP(x), 0, 0, 0, 0, 0, 0, SKP(1)} 
#define SKSS1(x) {0, SKP(x), 0, 0, 0, 0, 0, SKP(1)} 
#define SKSS2(x) {0, 0, SKP(x), 0, 0, 0, 0, SKP(1)} 
#define SKSS3(x) {0, 0, 0, SKP(x), 0, 0, 0, SKP(1)} 
#define SKSS4(x) {0, 0, 0, 0, SKP(x), 0, 0, SKP(1)} 
#define SKSS5(x) {0, 0, 0, 0, 0, SKP(x), 0, SKP(1)} 
#define SKSS6(x) {0, 0, 0, 0, 0, 0, SKP(x), SKP(1)} 
#define SKSS7(x) {0, 0, 0, 0, 0, 0, 0, SKP(x) | SKP(1)} 


#define KEMP(x)  {x, 0, 0, 0, 0, 0, 0, 0}

#define KEMPR 0x01
#define KEMPL 0x02
#define KEMPD 0x04
#define KEMPU 0x08
#define KEMPF 0x10

#define KEMP_PORT 0x1F


#define T_MAIN 0
#define T_CMPX 1
#define T_EXTR 2

#define SPK_SPACE SKN7(0)

#define SPK_0 SKN4(0)
#define SPK_1 SKN3(0)
#define SPK_2 SKN3(1)
#define SPK_3 SKN3(2)
#define SPK_4 SKN3(3)
#define SPK_5 SKN3(4)
#define SPK_6 SKN4(4)
#define SPK_7 SKN4(3)
#define SPK_8 SKN4(2)
#define SPK_9 SKN4(1)

#define SPK_A SKN1(0)
#define SPK_B SKN7(4)
#define SPK_C SKN0(3)
#define SPK_D SKN1(2)
#define SPK_E SKN2(2)
#define SPK_F SKN1(3)
#define SPK_G SKN1(4)
#define SPK_H SKN6(4)
#define SPK_I SKN5(2)
#define SPK_J SKN6(3)
#define SPK_K SKN6(2)
#define SPK_L SKN6(1)
#define SPK_M SKN7(2)
#define SPK_N SKN7(3)
#define SPK_O SKN5(1)
#define SPK_P SKN5(0)
#define SPK_Q SKN2(0)
#define SPK_R SKN2(3)
#define SPK_S SKN1(1)
#define SPK_T SKN2(4)
#define SPK_U SKN5(3)
#define SPK_V SKN0(4)
#define SPK_W SKN2(1)
#define SPK_X SKN0(2)
#define SPK_Y SKN5(4)
#define SPK_Z SKN0(1)

#define SPK_ENTER       SKN6(0)
#define SPK_CAPSSHIFT   SKN0(0)
#define SPK_SYMBOLSHIFT SKN7(1)

#define SPK_BS          SKCS4(0)
#define SPK_UP          SKCS4(3)
#define SPK_DOWN        SKCS4(4)
#define SPK_LEFT        SKCS3(4)
#define SPK_RIGHT       SKCS4(2)
#define SPK_CAPSLOCK    SKCS3(1)
#define SPK_EXTRA       SKCS7(1)
#define SPK_EDIT        SKCS3(0)

static spkeyboard spk_extra = SPK_EXTRA;

static struct spkeydef spkey_ascii[] = {
  {T_MAIN, SPK_SPACE},  /* space */
  {T_CMPX, SKSS3(0)}, /* ! */
  {T_CMPX, SKSS5(0)}, /* " */
  {T_CMPX, SKSS3(2)}, /* # */
  {T_CMPX, SKSS3(3)}, /* $ */
  {T_CMPX, SKSS3(4)}, /* % */
  {T_CMPX, SKSS4(4)}, /* & */
  {T_CMPX, SKSS4(3)}, /* ' */
  {T_CMPX, SKSS4(2)}, /* ( */
  {T_CMPX, SKSS4(1)}, /* ) */
  {T_CMPX, SKSS7(4)}, /* * */
  {T_CMPX, SKSS6(2)}, /* + */
  {T_CMPX, SKSS7(3)}, /* , */
  {T_CMPX, SKSS6(3)}, /* - */
  {T_CMPX, SKSS7(2)}, /* . */
  {T_CMPX, SKSS0(4)}, /* / */

  {T_MAIN, SPK_0},  /* 0 */
  {T_MAIN, SPK_1},  /* 1 */
  {T_MAIN, SPK_2},  /* 2 */
  {T_MAIN, SPK_3},  /* 3 */
  {T_MAIN, SPK_4},  /* 4 */
  {T_MAIN, SPK_5},  /* 5 */
  {T_MAIN, SPK_6},  /* 6 */
  {T_MAIN, SPK_7},  /* 7 */
  {T_MAIN, SPK_8},  /* 8 */
  {T_MAIN, SPK_9},  /* 9 */

  {T_CMPX, SKSS0(1)}, /* : */
  {T_CMPX, SKSS5(1)}, /* ; */
  {T_CMPX, SKSS2(3)}, /* < */
  {T_CMPX, SKSS6(1)}, /* = */
  {T_CMPX, SKSS2(4)}, /* > */
  {T_CMPX, SKSS0(3)}, /* ? */
  {T_CMPX, SKSS3(1)}, /* @ */

  {T_CMPX, SKCS1(0)}, /* A */
  {T_CMPX, SKCS7(4)}, /* B */
  {T_CMPX, SKCS0(3)}, /* C */
  {T_CMPX, SKCS1(2)}, /* D */
  {T_CMPX, SKCS2(2)}, /* E */
  {T_CMPX, SKCS1(3)}, /* F */
  {T_CMPX, SKCS1(4)}, /* G */
  {T_CMPX, SKCS6(4)}, /* H */
  {T_CMPX, SKCS5(2)}, /* I */
  {T_CMPX, SKCS6(3)}, /* J */
  {T_CMPX, SKCS6(2)}, /* K */
  {T_CMPX, SKCS6(1)}, /* L */
  {T_CMPX, SKCS7(2)}, /* M */
  {T_CMPX, SKCS7(3)}, /* N */
  {T_CMPX, SKCS5(1)}, /* O */
  {T_CMPX, SKCS5(0)}, /* P */
  {T_CMPX, SKCS2(0)}, /* Q */
  {T_CMPX, SKCS2(3)}, /* R */
  {T_CMPX, SKCS1(1)}, /* S */
  {T_CMPX, SKCS2(4)}, /* T */
  {T_CMPX, SKCS5(3)}, /* U */
  {T_CMPX, SKCS0(4)}, /* V */
  {T_CMPX, SKCS2(1)}, /* W */
  {T_CMPX, SKCS0(2)}, /* X */
  {T_CMPX, SKCS5(4)}, /* Y */
  {T_CMPX, SKCS0(1)}, /* Z */

  {T_EXTR, SKCS5(4)}, /* [ */
  {T_EXTR, SKCS1(2)}, /* backslash */
  {T_EXTR, SKCS5(3)}, /* ] */
  {T_CMPX, SKSS6(4)}, /* ^ */
  {T_CMPX, SKSS4(0)}, /* _ */
  {T_CMPX, SKSS0(2)}, /* ` */

  {T_MAIN, SPK_A},  /* a */
  {T_MAIN, SPK_B},  /* b */
  {T_MAIN, SPK_C},  /* c */
  {T_MAIN, SPK_D},  /* d */
  {T_MAIN, SPK_E},  /* e */
  {T_MAIN, SPK_F},  /* f */
  {T_MAIN, SPK_G},  /* g */
  {T_MAIN, SPK_H},  /* h */
  {T_MAIN, SPK_I},  /* i */
  {T_MAIN, SPK_J},  /* j */
  {T_MAIN, SPK_K},  /* k */
  {T_MAIN, SPK_L},  /* l */
  {T_MAIN, SPK_M},  /* m */
  {T_MAIN, SPK_N},  /* n */
  {T_MAIN, SPK_O},  /* o */
  {T_MAIN, SPK_P},  /* p */
  {T_MAIN, SPK_Q},  /* q */
  {T_MAIN, SPK_R},  /* r */
  {T_MAIN, SPK_S},  /* s */
  {T_MAIN, SPK_T},  /* t */
  {T_MAIN, SPK_U},  /* u */
  {T_MAIN, SPK_V},  /* v */
  {T_MAIN, SPK_W},  /* w */
  {T_MAIN, SPK_X},  /* x */
  {T_MAIN, SPK_Y},  /* y */
  {T_MAIN, SPK_Z},  /* z */

  {T_EXTR, SKCS1(3)}, /* { */
  {T_EXTR, SKCS1(1)}, /* | */
  {T_EXTR, SKCS1(4)}, /* } */
  {T_EXTR, SKCS1(0)}, /* ~ */
};

static struct spnamedkey spkey_misc[] = {
  {"none",            SKE,              SKE},

  {"space",           SPK_SPACE,        SKE},
  {"enter",           SPK_ENTER,        SKE},
  {"capsshift",       SPK_CAPSSHIFT,    SKE},
  {"symbolshift",     SPK_SYMBOLSHIFT,  SKE},
  
  {"kempston_up",     SKE,              KEMP(KEMPU)},
  {"kempston_down",   SKE,              KEMP(KEMPD)},
  {"kempston_left",   SKE,              KEMP(KEMPL)},
  {"kempston_right",  SKE,              KEMP(KEMPR)},
  {"kempston_fire",   SKE,              KEMP(KEMPF)},

  {NULL, SKE, SKE}
};

#define MAXBASEKEYS 128

static struct spbasekey basekeys[MAXBASEKEYS];
static int numbasekeys;

static struct spbasekey customkeys[MAXBASEKEYS];
static int numcustomkeys = 0;

static struct spbasekey normalkeys[] = {
  {'0',    SPK_0,   SKE},  /* 0 */
  {'1',    SPK_1,   SKE},  /* 1 */
  {'2',    SPK_2,   SKE},  /* 2 */
  {'3',    SPK_3,   SKE},  /* 3 */
  {'4',    SPK_4,   SKE},  /* 4 */
  {'5',    SPK_5,   SKE},  /* 5 */
  {'6',    SPK_6,   SKE},  /* 6 */
  {'7',    SPK_7,   SKE},  /* 7 */
  {'8',    SPK_8,   SKE},  /* 8 */
  {'9',    SPK_9,   SKE},  /* 9 */

  {'a',    SPK_A,   SKE},  /* a */
  {'b',    SPK_B,   SKE},  /* b */
  {'c',    SPK_C,   SKE},  /* c */
  {'d',    SPK_D,   SKE},  /* d */
  {'e',    SPK_E,   SKE},  /* e */
  {'f',    SPK_F,   SKE},  /* f */
  {'g',    SPK_G,   SKE},  /* g */
  {'h',    SPK_H,   SKE},  /* h */
  {'i',    SPK_I,   SKE},  /* i */
  {'j',    SPK_J,   SKE},  /* j */
  {'k',    SPK_K,   SKE},  /* k */
  {'l',    SPK_L,   SKE},  /* l */
  {'m',    SPK_M,   SKE},  /* m */
  {'n',    SPK_N,   SKE},  /* n */
  {'o',    SPK_O,   SKE},  /* o */
  {'p',    SPK_P,   SKE},  /* p */
  {'q',    SPK_Q,   SKE},  /* q */
  {'r',    SPK_R,   SKE},  /* r */
  {'s',    SPK_S,   SKE},  /* s */
  {'t',    SPK_T,   SKE},  /* t */
  {'u',    SPK_U,   SKE},  /* u */
  {'v',    SPK_V,   SKE},  /* v */
  {'w',    SPK_W,   SKE},  /* w */
  {'x',    SPK_X,   SKE},  /* x */
  {'y',    SPK_Y,   SKE},  /* y */
  {'z',    SPK_Z,   SKE},  /* z */
  
  {-1, SKE, SKE}
};

static struct spbasekey extendedkeys[] = {
  {' ',                SPK_SPACE,          SKE}, /* space */
  {TRKS(SK_Return),    SPK_ENTER,          SKE}, /* enter */
  {TRKS(SK_KP_Enter),  SPK_ENTER,          SKE},
  {TRKS(SK_Shift_L),   SPK_CAPSSHIFT,      SKE}, /* caps shift */
  {TRKS(SK_Shift_R),   SPK_SYMBOLSHIFT,    SKE}, /* symbol shift */
  {TRKS(SK_BackSpace), SPK_BS,             SKE}, /* backspace */
  {TRKS(SK_Delete),    SPK_BS,             SKE},
  {TRKS(SK_KP_Delete), SPK_BS,             SKE},
  {TRKS(SK_Escape),    SPK_EDIT,           SKE}, /* caps shift + '1' */

  {-1, SKE, SKE}
};

static struct spbasekey spectrumkeys[] = {
  {',',                SPK_SYMBOLSHIFT,   SKE},
  {'.',                SPK_SPACE,         SKE},
  {';',                SPK_ENTER,         SKE},

  {-1, SKE, SKE}
};                      
                      
                      
static struct spbasekey compatkeys[] = {  
  {TRKS(SK_Shift_L),   SPK_CAPSSHIFT,     SKE}, /* caps shift */
  {TRKS(SK_Shift_R),   SPK_CAPSSHIFT,     SKE}, 
  {TRKS(SK_Alt_L),     SPK_SYMBOLSHIFT,   SKE}, /* symbol shift */
  {TRKS(SK_Alt_R),     SPK_SYMBOLSHIFT,   SKE}, 
  {TRKS(SK_Meta_L),    SPK_SYMBOLSHIFT,   SKE},
  {TRKS(SK_Meta_R),    SPK_SYMBOLSHIFT,   SKE}, 
#ifdef TRUEKOMPAT                  
  {TRKS(SK_Control_L), SPK_EXTRA,         SKE}, /* caps shift + symbol shift */
  {TRKS(SK_Control_R), SPK_EXTRA,         SKE},
#endif                      

  {-1, SKE, SKE}
};                      
                      
static struct spbasekey shiftedcurs[] = { 
  {TRKS(SK_Up),        SPK_UP,            SKE}, /* up */
  {TRKS(SK_KP_Up),     SPK_UP,            SKE},
  {TRKS(SK_Down),      SPK_DOWN,          SKE}, /* down */
  {TRKS(SK_KP_Down),   SPK_DOWN,          SKE},
  {TRKS(SK_Left),      SPK_LEFT,          SKE}, /* left */
  {TRKS(SK_KP_Left),   SPK_LEFT,          SKE},
  {TRKS(SK_Right),     SPK_RIGHT,         SKE}, /* right */
  {TRKS(SK_KP_Right),  SPK_RIGHT,         SKE},

  {-1, SKE, SKE}
};                      

static struct spbasekey rawcurs[] = {      
  {TRKS(SK_Up),        SPK_7,             SKE}, /* up */
  {TRKS(SK_KP_Up),     SPK_7,             SKE},
  {TRKS(SK_Down),      SPK_6,             SKE}, /* down */
  {TRKS(SK_KP_Down),   SPK_6,             SKE},
  {TRKS(SK_Left),      SPK_5,             SKE}, /* left */
  {TRKS(SK_KP_Left),   SPK_5,             SKE},
  {TRKS(SK_Right),     SPK_8,             SKE}, /* right */
  {TRKS(SK_KP_Right),  SPK_8,             SKE},

  {-1, SKE, SKE}
};

static struct spbasekey joycurs[] = {
  {TRKS(SK_Up),           SKE,   KEMP(KEMPU)}, /* up */
  {TRKS(SK_KP_Up),        SKE,   KEMP(KEMPU)},
  {TRKS(SK_Down),         SKE,   KEMP(KEMPD)}, /* down */
  {TRKS(SK_KP_Down),      SKE,   KEMP(KEMPD)},
  {TRKS(SK_Left),         SKE,   KEMP(KEMPL)}, /* left */
  {TRKS(SK_KP_Left),      SKE,   KEMP(KEMPL)},
  {TRKS(SK_Right),        SKE,   KEMP(KEMPR)}, /* right */
  {TRKS(SK_KP_Right),     SKE,   KEMP(KEMPR)},
  {TRKS(SK_KP_Insert),    SKE,   KEMP(KEMPF)}, /* fire */
  {TRKS(SK_Insert),       SKE,   KEMP(KEMPF)},
  {TRKS(SK_KP_Delete),    SKE,   KEMP(KEMPF)},
  {TRKS(SK_KP_Home),      SKE,   KEMP(KEMPU | KEMPL)}, /* up + left*/
  {TRKS(SK_Home),         SKE,   KEMP(KEMPU | KEMPL)},
  {TRKS(SK_KP_Page_Up),   SKE,   KEMP(KEMPU | KEMPR)}, /* up + right*/
  {TRKS(SK_Page_Up),      SKE,   KEMP(KEMPU | KEMPR)},
  {TRKS(SK_KP_End),       SKE,   KEMP(KEMPD | KEMPL)}, /* down + left*/
  {TRKS(SK_End),          SKE,   KEMP(KEMPD | KEMPL)},
  {TRKS(SK_KP_Page_Down), SKE,   KEMP(KEMPD | KEMPR)}, /* down + right*/
  {TRKS(SK_Page_Down),    SKE,   KEMP(KEMPD | KEMPR)},

  {-1, SKE, SKE}
};

int spkey_new_custom(int key)
{
  if(numcustomkeys >= MAXBASEKEYS) return 0;

  customkeys[numcustomkeys].index = key;
  SP_SETEMPTY(customkeys[numcustomkeys].kb);
  SP_SETEMPTY(customkeys[numcustomkeys].misc);
  numcustomkeys++;

  return 1;
}

int spkey_add_custom(const char *name)
{
  int curr;

  curr = numcustomkeys - 1;

  if(!name[1] && isalnum(name[0])) {
    int ai;
    ai = tolower(name[0])-32;
    SP_COMBINE(customkeys[curr].kb, spkey_ascii[ai].kb);
    return 1;
  }
  else {
    int i;

    for(i = 0; spkey_misc[i].name != NULL; i++) {
      if(mis_strcasecmp(spkey_misc[i].name, name) == 0) {
    SP_COMBINE(customkeys[curr].kb, spkey_misc[i].kb);
    SP_COMBINE(customkeys[curr].misc, spkey_misc[i].misc);
    return 1;
      }
    }
  }
  return 0;
}

static int key_reset(struct keystate *ck)
{
  if(ck->state == 2 && sp_int_ctr >= ck->frame) {
    ck->state = 0;
    return 1;
  }
  else return 0;
}


void process_keys(void)
{
  int i;
  struct keystate *ck;
  int tsh;
  int kalone;
  static int extrai = 0;
  static qbyte extraendframe;
  
  if(extrai && !spkb_kbstate[extrai].state) extrai = 0;

  if(!spkb_state_changed && (!extrai || !extraendframe)) return;

  SP_SETEMPTY(spkey_state);
  SP_SETEMPTY(spmisc_state);

  kalone = 0;
  ck = spkb_kbstate + spkb_last.index;
  tsh = spkb_last.modif & trueshift;
  key_reset(ck);

  
  if(spkb_allow_ascii && ck->state && (!ck->base || tsh)) {
    unsigned ks;
    ks = tsh ? spkb_last.shifted : spkb_last.keysym;
    if(ks >= 32 && ks < 127) {
      if(spkey_ascii[ks-32].type <= T_CMPX) { 
    SP_COMBINE(spkey_state, spkey_ascii[ks-32].kb);
    kalone = 1;
      }
      else if(spkey_ascii[ks-32].type == T_EXTR) {
    if(!extrai || sp_int_ctr < extraendframe) {
      if(!extrai) {
        extrai = spkb_last.index;
        extraendframe = sp_int_ctr + 1;
      }
      SP_COMBINE(spkey_state, spk_extra);
    }
    else {
      SP_COMBINE(spkey_state, spkey_ascii[ks-32].kb);
      extraendframe = 0;
    }
    kalone = 1;
      }
    }
  }
  
  if(!kalone) {
    for(i = 0; i < numbasekeys; i++) {
      ck = spkb_kbstate + basekeys[i].index; 
      key_reset(ck);
      if(ck->state) {
    SP_COMBINE(spkey_state, basekeys[i].kb);
    SP_COMBINE(spmisc_state, basekeys[i].misc);
      }
    }
  }
  SP_COMBINE(spkey_state, kb_mkey);

  spkb_refresh();
  spkb_state_changed = 0;
}

void clear_keystates(void)
{
  int i;
  for(i = 0; i < NR_SPKEYS; i++) spkb_kbstate[i].state = 0;
  spkb_last.index = 0;
  SP_SETEMPTY(spkey_state);
  SP_SETEMPTY(kb_mkey);
  SP_SETEMPTY(spmisc_state);
  spkb_refresh();
}

static void keycpy(struct spbasekey *to, struct spbasekey *from)
{
  to->index = from->index;
  SP_COPY(to->kb, from->kb);
  SP_COPY(to->misc, from->misc);
}

static void copy_key(struct spbasekey *addk)
{
  int i;
  int nindex;

  nindex = addk->index;

  if(SP_NONEMPTY(addk->kb) || SP_NONEMPTY(addk->misc)) {
    for(i = 0; i < numbasekeys; i++) {
      if(basekeys[i].index == nindex) {                /* Replace */
    keycpy(&basekeys[i], addk); 
    return;
      }
    }
    if(numbasekeys < MAXBASEKEYS - 1) {                /* Add */
      keycpy(&basekeys[numbasekeys], addk);
      spkb_kbstate[nindex].base = 1;
      numbasekeys++;
    }
  }
  else {                                               /* Delete */
    for(i = 0; i < numbasekeys; i++) {
      if(basekeys[i].index == nindex) {
    i++;
    for(; i < numbasekeys; i++) keycpy(&basekeys[i-1], &basekeys[i]);
    spkb_kbstate[nindex].base = 0;
    numbasekeys--;
    break;
      }
    }
  }
}

static void copy_basekeys(struct spbasekey *addk)
{
  int i;

  for(i = 0; addk[i].index >= 0; i++) copy_key(&addk[i]);
}

static unsigned transform_shift(int modif)
{
  if(!modif) return 0;
  else return (1 << (modif - 1));
}


void init_basekeys(void)
{
  int i;
  numbasekeys = 0;

  for(i = 0; i < NR_SPKEYS; i++) spkb_kbstate[i].base = 0;

  customkeys[numcustomkeys].index = -1;

  copy_basekeys(normalkeys);
  copy_basekeys(extendedkeys);
  copy_basekeys(shiftedcurs);
  
  switch(keyboard_type) {
  case 0:
    break;
    
  case 1:
    copy_basekeys(spectrumkeys);
    break;
    
  case 2:
    if(spkb_trueshift == -1) spkb_trueshift = 0;
#ifdef TRUEKOMPAT
    if(spkb_funcshift == -1) spkb_funcshift = 0;
#endif

    copy_basekeys(compatkeys);
    break;

  case 3:
    copy_basekeys(customkeys);
    break;
  }
  
  switch(cursor_type) {
  case 0:
    break;
    
  case 1:
    copy_basekeys(rawcurs);
    break;
    
  case 2:
    copy_basekeys(joycurs);
    break;
  }

  if(spkb_trueshift == -1) spkb_trueshift = 4; /* mod1 */
  if(spkb_funcshift == -1) spkb_funcshift = 3; /* control */

  trueshift = transform_shift(spkb_trueshift);
  funcshift = transform_shift(spkb_funcshift);
}


void spkb_refresh(void)
{
  int port, pb;
  int i, j, changed;
  byte *km, *kmo;
  byte mm;
  byte pv;
  spkeyboard statemx;

  km = spkey_state;
  kmo = oldstate;
  changed = 0;
  for(i = 8; i; i--) {
    if(*km != *kmo) *kmo = *km, changed = 1;
    km++, kmo++;
  }

  if(changed) {

    /* Matrix behavior: ONLY 1 level, does anybody need more ? */

    for(i = 0; i < 8; i++) {  
      pv = spkey_state[i];
      mm = pv;
      if(pv) {
    km = spkey_state;
    for(j = 8; j; j--) {
      if((*km & pv) & 0x1F) mm |= *km;
      km++;
    }
      }
      statemx[i] = mm;
    }
    
    for(port = 0; port < 256; port++) {
      km = statemx;
      pv = 0;
      pb = port;
      for(i = 8; i; i--) {
    if(!(pb & 1)) pv |= *km;
    pb >>= 1;
    km++;
      }
      sp_fe_inport_high[port] = 
    (sp_fe_inport_high[port] | 0x1F) & ~(pv & 0x1F); 
    }
  }

  pv = spmisc_state[0];

  if((pv & KEMPR) && (pv & KEMPL)) pv &= ~(KEMPR | KEMPL);
  if((pv & KEMPU) && (pv & KEMPD)) pv &= ~(KEMPD | KEMPU);
  z80_inports[KEMP_PORT] = pv;
}

