#include "exec/types.h"
#include "hp11/hp11.h"
#include "hp11/kbd.h"
#include "hp11/codes.h"
#include "hp11/io.h"

/* Macros to initialise one field of the keyboard structure to a particular type.
  This simpilfies (& clarifies) this initialisation. */
#define CODE(code) {Instruction, (Decoder)(code) }
#define ACT(act) {Action, (Decoder)(act) }
#define PREFIX(adr) {Prefix, (adr) }
#define INVALID() {Invalid, NULL }

/* Often used macros which return their agument signaling that it is an instruction,
  action or error */
#define RETINS(val) { *code = (val); return(Instruction); }
#define RETACT(val) { *code = (val); return(Action); }
#define RETERR(key) { *code = (key); return(Invalid); }

/* Keys which can follow GTO (or GSB). A -1 indicates am invalid sequence, otherwise
  the value is the offset to add to KGTO to obtain the corresponding instruction.
  IGTO_LINE is different and valid only for GTO, it indicates a GTO .nnn action */
static BYTE gto_decode[NUMKEYS] = {
   10, 11, 12, 13, 14, -1, 7, 8, 9, -1,
   -1, -1, -1, -1, OIND_G, -1, 4, 5, 6, -1,
   -1, -1, -1, -1, -1, -1, 1, 2, 3, -1,
   -1, -1, -1, -1, -1, -1, 0, IGTO_LINE, -1, -1,
   -1, -1
};

/* For STO & RCL, cf above */
static BYTE sto_decode[NUMKEYS] = {
   -1, -1, -1, -1, -1, -1, 7, 8, 9, ODIV,
   -1, -1, -1, OIND_R, OI, -1, 4, 5, 6, OMUL,
   -1, -1, -1, -1, -1, KRANDOM, 1, 2, 3, OSUB,
   -1, -1, -1, -1, -1, -1, 0, KPOINT, KSIGMA_PLUS, OPLUS,
   -1, -1
};

/* Functions which take a numeric argument only (eg eng) can use the numbers
  from gto_decode, considering as invalid what isn't a number between 1 & 10 */
#define nb_decode gto_decode

/* Read 3 digits for GTO .nnn & return the value in line. If something other than
  a number is entered, return the keycode of the first incorrect code & FALSE */
static BOOL GetLine(short *line)
{
   register int cnt = 0, key;
   register int dec;

   *line = 0;

   do {
      key = GetKey(); dec = nb_decode[key]; /* Get numeric value */
      if (dec >= 0 && dec <= 9) { /* It is a digit */
	 cnt++;
	 *line = *line * 10 + dec;
      }
      else { /* error */
	 *line = key;
	 return(FALSE);
      }
   } while (cnt < 3);

   /* 3 digits reads */
   return(TRUE);
}

/* Decoder routine for FIX, SCI, ENG, SF, CF, Set. code returns the
  instruction/action/keycode, start is the offset for the instruction being
  decoded (eg KFIX), max is the maximum value which can be accepted (eg 1 for SF).
  For SCI & ENG, a number beyond their max (7) is treated as if it was the max
  value (So if you type 'f SCI 8' you will get 'f SCI 7' */
static enum KeyTypes NBDec(short *code, int start, int max)
{
   register int key, dec;

   key = GetKey(); dec = nb_decode[key];

   if (dec >= 0 && dec <= 9) { /* Is a digit */
      if (dec <= max) RETINS(start + dec) /* valid ins */
      else if (start == KSCI || start == KENG) RETINS(start + max)
	 /* Special treatment for SCI & ENG */
   }
   RETERR(key);
}

/* Decoding for HYP & ArcHYP */
static enum KeyTypes HypDec(short *code, int start)
{
   int key;

   key = GetKey();
   if (key >= 12 /* SIN */ && key <= 14 /* TAN */) RETINS(start + key - 12)
   else RETERR(key);
}

/* Decoding for GTO, GSB & LBL */
static enum KeyTypes JMPDec(short *code, int start)
{
   register int key, dec;
   short val;

   key = GetKey(); dec = gto_decode[key];

   if (dec >= 0 && dec <= 15) RETINS(start + dec); /* 0 to 9, A to E */
   switch (dec) {
      case IGTO_LINE: if (start == KGTO) /* GTO .nnn */
	 if (GetLine(&val)) RETACT(IGTO_LINE + val)
	 else RETERR(val);
      case OIND_G: if (start != KLBL) RETINS(start + OIND_G); /* GTO/GSB I */
   }
   RETERR(key);
}

/* Decoding for STO & RCL, deals with all possible STO's */
static enum KeyTypes REGDec(short *code, int start)
{
   register int dec, key, oldoff, offset = 0;

   do {
      key = GetKey();
      dec = sto_decode[key];
      oldoff = offset;

      if ((dec >= 0 && dec <= 9) /* 0 to 9 end an instruction */
	  || /* I & (i) end an instruction if no . was typed before. This
	       is visible if the offset (ignoring + - * /) is 10 */
	  ((offset % OPLUS) != 10 && (dec == OI || dec == OIND_R)))
	 RETINS(start + offset + dec);
      switch (dec) { /* Special cases & offsets */
	 case KRANDOM: if (offset == 0 && start == KSTO) RETINS(KSTO_RANDOM); /* STO Random */
	 case KSIGMA_PLUS: if (offset == 0 && start == KRCL) RETINS(KRCL_SIGMA); /* Recall stats */
	 case KPOINT: if ((offset % OPLUS) == 0) offset += 10; /* Only one . allowed */
	 case OPLUS: case ODIV: case OMUL: case OSUB: /* + - * / only if none yet */
	    if (offset == 0 && start == KSTO) offset = dec;
      }
   } while (offset != oldoff);
   /* if offset not changed then there was an error (the loop is repeated when
      the offset changes) */
   RETERR(key);
}

/* Decoding for prefixes */
/* --------------------- */
static enum KeyTypes FIXDec(short *code)
{
   return(NBDec(code, KFIX, 9));
}

static enum KeyTypes SCIDec(short *code)
{
   return(NBDec(code, KSCI, 7));
}

static enum KeyTypes ENGDec(short *code)
{
   return(NBDec(code, KENG, 7));
}

static enum KeyTypes SFDec(short *code)
{
   return(NBDec(code, KFLAGS + OSF, 1));
}

static enum KeyTypes SETDec(short *code)
{
   return(NBDec(code, KFLAGS + OSET, 1));
}

static enum KeyTypes CFDec(short *code)
{
   return(NBDec(code, KFLAGS + OCF, 1));
}

static enum KeyTypes HYPDec(short *code)
{
   return(HypDec(code, KHYP));
}

static enum KeyTypes ARCHYPDec(short *code)
{
   return(HypDec(code, KARCHYP));
}

static enum KeyTypes LBLDec(short *code)
{
   return(JMPDec(code, KLBL));
}

static enum KeyTypes GTODec(short *code)
{
   return(JMPDec(code, KGTO));
}

static enum KeyTypes GSBDec(short *code)
{
   return(JMPDec(code, KGSB));
}

static enum KeyTypes STODec(short *code)
{
   return(REGDec(code, KSTO));
}

static enum KeyTypes RCLDec(short *code)
{
   return(REGDec(code, KRCL));
}

/* The main kbd, f & g */
/* ------------------- */
struct Key mainKbd[3 * NUMKEYS] = {
/* First the main keyboard (unshifted). All the keys which can be entered
  MUST not be INVALID(), otherwise the program enters an infinite loop */
   CODE(KSQRT),
   CODE(KEXP),
   CODE(KEXP10),
   CODE(KEXP_YX),
   CODE(KINV),
   CODE(KCHS),
   CODE(KFIG + 7),
   CODE(KFIG + 8),
   CODE(KFIG + 9),
   CODE(KDIV),
   ACT(ISST),
   PREFIX(GTODec),
   CODE(KTRIG + OSIN),
   CODE(KTRIG + OCOS),
   CODE(KTRIG + OTAN),
   CODE(KEEX),
   CODE(KFIG + 4),
   CODE(KFIG + 5),
   CODE(KFIG + 6),
   CODE(KMUL),
   CODE(KR_S),
   PREFIX(GSBDec),
   CODE(KRDN),
   CODE(KEXG_XY),
   ACT(IBACK),
   CODE(KENTER),
   CODE(KFIG + 1),
   CODE(KFIG + 2),
   CODE(KFIG + 3),
   CODE(KSUB),
   ACT(ION),
   INVALID(), /* Never tested : f */
   INVALID(), /* Never tested : g */
   PREFIX(STODec),
   PREFIX(RCLDec),
   INVALID(), /* This key does not exist : it is hidden by ENTER */
   CODE(KFIG + 0),
   CODE(KPOINT),
   CODE(KSIGMA_PLUS),
   CODE(KPLUS),
   ACT(IRESET), /* These 2 are pseudo-keys */
   ACT(IDISPLAY),
/* now f codes, which can be INVALID() */
   CODE(KGSB + OA),
   CODE(KGSB + OB),
   CODE(KGSB + OC),
   CODE(KGSB + OD),
   CODE(KGSB + OE),
   CODE(KPI),
   PREFIX(FIXDec),
   PREFIX(SCIDec),
   PREFIX(ENGDec),
   CODE(KX_LE_Y),
   PREFIX(LBLDec),
   PREFIX(HYPDec),
   CODE(KEXG_X_IND),
   CODE(KRCL + OIND_R),
   CODE(KRCL + OI),
   CODE(KRECT),
   CODE(KEXG_XI),
   CODE(KDSE),
   CODE(KISG),
   CODE(KX_GT_Y),
   CODE(KPSE),
   CODE(KCLR_SIGMA),
   ACT(ICLR_PRGM),
   CODE(KCLR_REG),
   ACT(ICLR_PREFIX),
   CODE(KRANDOM),
   CODE(KPERM),
   CODE(KHMS),
   CODE(KTO_RAD),
   CODE(KX_NE_Y),
   INVALID(), INVALID(), INVALID(), /* ON, f & g */
   CODE(KFRAC),
   ACT(IUSER),
   INVALID(), /* dosen't exist */
   CODE(KFACT),
   CODE(KESTIMATE),
   CODE(KLR),
   CODE(KX_EQ_Y),
   INVALID(), INVALID(),
/* finally, g codes */
   CODE(KSQR),
   CODE(KLN),
   CODE(KLOG),
   CODE(KPERC),
   CODE(KDELTA_PERC),
   CODE(KABS),
   CODE(KDEG),
   CODE(KRAD),
   CODE(KGRD),
   CODE(KX_LT_0),
   ACT(IBST),
   PREFIX(ARCHYPDec),
   CODE(KARC + OSIN),
   CODE(KARC + OCOS),
   CODE(KARC + OTAN),
   CODE(KPOLAR),
   PREFIX(SFDec),
   PREFIX(CFDec),
   PREFIX(SETDec),
   CODE(KX_GT_0),
   ACT(IP_R),
   CODE(KRTN),
   CODE(KRUP),
   CODE(KRND),
   CODE(KCLX),
   CODE(KLSTX),
   CODE(KCOMB),
   CODE(KHR),
   CODE(KTO_DEG),
   CODE(KX_NE_0),
   INVALID(), INVALID(), INVALID(),
   CODE(KINT),
   ACT(IMEM),
   INVALID(),
   CODE(KMEAN),
   CODE(KSDEV),
   CODE(KSIGMA_SUB),
   CODE(KX_EQ_0),
   INVALID(), INVALID()
};

