Top secrets sources NedoPC pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

/* code78c10.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator NEC uPD78(C)(0|1)x                                          */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"
#include <ctype.h>
#include <string.h>

#include "bpemu.h"
#include "strutil.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmstructs.h"
#include "asmitree.h"
#include "asmcode.h"
#include "codepseudo.h"
#include "intpseudo.h"
#include "codevars.h"
#include "errmsg.h"
#include "headids.h"

#include "code78c10.h"

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

typedef struct
{
  const char *p_name;
  Byte code;
} intflag_t;

typedef struct
{
  const char name[5];
  Byte code, flags;
} reg_t;

typedef struct
{
  const char *p_name;
  Byte code, flags, core_mask;
} sreg_t;

typedef struct
{
  const char *pName;
  Byte Code;
  Byte MayIndirect;
} tAdrMode;

typedef enum
{
  eCoreNone,
  eCore7800Low,
  eCore7800High,
  eCore7807,
  eCore7810
} tCore;

#define core_mask_no_low ((1 << eCore7800High) | (1 << eCore7807) | (1 << eCore7810))
#define core_mask_7800 ((1 << eCore7800Low) | (1 << eCore7800High))
#define core_mask_7800_low (1 << eCore7800Low)
#define core_mask_7800_high (1 << eCore7800High)
#define core_mask_7807 (1 << eCore7807)
#define core_mask_7810 (1 << eCore7810)
#define core_mask_7807_7810 ((1 << eCore7807) | (1 << eCore7810))
#define core_mask_all ((1 << eCore7800Low) | (1 << eCore7800High) | (1 << eCore7807) | (1 << eCore7810))
#define core_mask_cmos 0x80

enum
{
  eFlagHasV = 1 << 0,
  eFlagCMOS = 1 << 1,
  eFlagSR   = 1 << 2, /* sr  -> may be written from A */
  eFlagSR1  = 1 << 3, /* sr1 -> may be read to A */
  eFlagSR2  = 1 << 4, /* sr2 -> load or read/modify/write with immediate */
  eFlagSR3  = 1 << 5, /* sr3 -> may be written from EA */
  eFlagSR4  = 1 << 6  /* sr4 -> may be read to EA */
};

/* Flags in high byte of ALU operations: */

#define ALUImm_SR (1 << 0)
#define ALUReg_Src (1 << 1)
#define ALUReg_Dest (1 << 2)
#define ALUReg_MayZ80 (1 << 3)

typedef struct
{
  char Name[6];
  Byte Core;
  Byte Flags;
} tCPUProps;

typedef enum { e_decode_reg_unknown, e_decode_reg_ok, e_decode_reg_error } decode_reg_res_t;

typedef struct
{
  Word code;
  unsigned core_mask;
} order_t;

typedef enum
{
  e_mod_none = -1,
  e_mod_reg8 = 0,
  e_mod_reg16 = 1,
  e_mod_imm = 2,
  e_mod_indir = 3,
  e_mod_wa = 4,
  e_mod_abs = 5,
  e_mod_sreg8 = 6,
  e_mod_sreg16 = 7
} z80_adr_mode_t;

#define MModReg8 (1 << e_mod_reg8)
#define MModReg16 (1 << e_mod_reg16)
#define MModImm (1 << e_mod_imm)
#define MModIndir (1 << e_mod_indir)
#define MModWA (1 << e_mod_wa)
#define MModAbs (1 << e_mod_abs)
#define MModSReg8 (1 << e_mod_sreg8)
#define MModSReg16 (1 << e_mod_sreg16)

#define REG_V 0
#define REG_A 1
#define REG_B 2
#define REG_C 3
#define REG_D 4
#define REG_H 6
#define REG_EAH 8
#define REG_BC 1
#define REG_DE 2
#define REG_HL 3
#define REG_EA 4

typedef struct
{
  z80_adr_mode_t mode;
  unsigned count;
  Boolean force_long;
  Byte val, vals[2];
} z80_adr_vals_t;

static Boolean is_7807_781x;

static LongInt WorkArea;

static const tCPUProps *pCurrCPUProps;

static order_t *fixed_orders, *reg2_orders;
static sreg_t *s_regs8, *s_regs16;
static intflag_t *int_flags;
static tSymbolSize z80_op_size, z80_def_op_size;

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

/*!------------------------------------------------------------------------
 * \fn     check_core(unsigned core_mask)
 * \brief  check whether active target uses given core
 * \param  core_mask list of allowed cores
 * \return True if OK
 * ------------------------------------------------------------------------ */


static Boolean check_core(unsigned core_mask)
{
  if ((core_mask & core_mask_cmos) && !(pCurrCPUProps->Flags & eFlagCMOS))
    return False;
  return !!((core_mask >> pCurrCPUProps->Core) & 1);
}

/*!------------------------------------------------------------------------
 * \fn     decode_reg8(const char *p_arg, Byte *p_res)
 * \brief  decode 8 bit register
 * \param  p_arg source argument
 * \param  p_res encoded name
 * \return True if valid name
 * ------------------------------------------------------------------------ */


static Boolean decode_reg8(const char *p_arg, Byte *p_res)
{
  switch (strlen(p_arg))
  {
    case 1:
    {
      static const char names[] = "VABCDEHL";
      const char *p;
      int no_v = !(pCurrCPUProps->Flags & eFlagHasV);

      p = strchr(&names[no_v], as_toupper(*p_arg));
      if (!p)
        return False;
      *p_res = p - names;
      return True;
    }
    case 3:
      if (!is_7807_781x
       || (as_toupper(p_arg[0]) != 'E')
       || (as_toupper(p_arg[1]) != 'A'))
        return False;
      if (as_toupper(p_arg[2]) == 'L')
        *p_res = REG_EAH + 1;
      else if (as_toupper(p_arg[2]) == 'H')
        *p_res = REG_EAH;
      else
        return False;
      return True;
    default:
      return False;
  }
}

/*!------------------------------------------------------------------------
 * \fn     decode_r(const tStrComp *p_arg, Byte *p_res)
 * \brief  decode name of 8 bit register
 * \param  p_arg source argument
 * \param  p_res return buffer
 * \return e_decode_reg_ok -> register known & OK
 * ------------------------------------------------------------------------ */


static decode_reg_res_t decode_r(const tStrComp *p_arg, Byte *p_res)
{
  if (!decode_reg8(p_arg->str.p_str, p_res))
    return e_decode_reg_unknown;
  if (*p_res >= 8)
  {
    WrStrErrorPos(ErrNum_InvReg, p_arg);
    return e_decode_reg_error;
  }
  else
    return e_decode_reg_ok;
}

/*!------------------------------------------------------------------------
 * \fn     decode_r1(const tStrComp *p_arg, Byte *p_res)
 * \brief  decode name of 8 bit register, plus EAL/H on 78C1x
 * \param  p_arg source argument
 * \param  p_res return buffer
 * \return e_decode_reg_ok -> register known & OK
 * ------------------------------------------------------------------------ */


static decode_reg_res_t decode_r1(const tStrComp *p_arg, Byte *p_res)
{
  if (!decode_reg8(p_arg->str.p_str, p_res))
    return e_decode_reg_unknown;
  if (*p_res < 2)
  {
    WrStrErrorPos(ErrNum_InvReg, p_arg);
    return e_decode_reg_error;
  }
  *p_res &= 7;
  return e_decode_reg_ok;
}

/*!------------------------------------------------------------------------
 * \fn     decode_r2(const tStrComp *p_arg, Byte *p_res)
 * \brief  decode name of 8 bit register A, B, or C
 * \param  p_arg source argument
 * \param  p_res return buffer
 * \return e_decode_reg_ok -> register known & OK
 * ------------------------------------------------------------------------ */


static decode_reg_res_t decode_r2(const tStrComp *p_arg, Byte *p_res)
{
  if (!decode_reg8(p_arg->str.p_str, p_res))
    return e_decode_reg_unknown;
  if ((*p_res == 0) || (*p_res >= 4))
  {
    WrStrErrorPos(ErrNum_InvReg, p_arg);
    return e_decode_reg_error;
  }
  return e_decode_reg_ok;
}

/*!------------------------------------------------------------------------
 * \fn     decode_reg16(char *p_arg, Byte *p_res, Boolean allow_single_letter)
 * \brief  decode 16 bit register argument
 * \param  p_arg source argument
 * \param  p_res result value
 * \param  allow_single_letter allow register names with single letters (not for Z80 syntax)
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean decode_reg16(char *p_arg, Byte *p_res, Boolean allow_single_letter)
{
  static const reg_t regs[] =
  {
    { "SP" , 0, 0 },
    { "B"  , REG_BC, 0 },
    { "BC" , REG_BC, 0 },
    { "D"  , REG_DE, 0 },
    { "DE" , REG_DE, 0 },
    { "H"  , REG_HL, 0 },
    { "HL" , REG_HL, 0 },
    { "EA" , REG_EA, 0 },
    { ""   , 0, 0 },
  };

  for (*p_res = 0; regs[*p_res].name[0]; (*p_res)++)
    if (!as_strcasecmp(p_arg, regs[*p_res].name))
    {
      if (!p_arg[1] && !allow_single_letter)
        return False;
      *p_res = regs[*p_res].code;
      return True;
    }
  return False;
}

/*!------------------------------------------------------------------------
 * \fn     Decode_rp(char *Asc, Byte *Erg)
 * \brief  decode rp2 argument (SP/BC/DE/HL/EA)
 * \param  Asc source argument
 * \param  Erg resulting register #
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean Decode_rp2(char *p_arg, Byte *p_res)
{
  return decode_reg16(p_arg, p_res, True);
}

/*!------------------------------------------------------------------------
 * \fn     Decode_rp(char *Asc, Byte *Erg)
 * \brief  decode rp argument (SP/BC/DE/HL)
 * \param  Asc source argument
 * \param  Erg resulting register #
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean Decode_rp(char *Asc, Byte *Erg)
{
  if (!Decode_rp2(Asc, Erg)) return False;
  return (*Erg < 4);
}

/*!------------------------------------------------------------------------
 * \fn     Decode_rp1(char *Asc, Byte *Erg)
 * \brief  decode rp1 argument (VA/BC/DE/HL/EA)
 * \param  Asc source argument
 * \param  Erg resulting register #
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean Decode_rp1(char *Asc, Byte *Erg)
{
  if (!as_strcasecmp(Asc, "V")) *Erg = 0;
  else
  {
    if (!Decode_rp2(Asc, Erg)) return False;
    return (*Erg != 0);
  }
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     Decode_rp3(char *Asc, Byte *Erg)
 * \brief  decode rp3 argument (BC/DE/HL)
 * \param  Asc source argument
 * \param  Erg resulting register #
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean Decode_rp3(char *Asc, Byte *Erg)
{
  if (!Decode_rp2(Asc, Erg)) return False;
  return ((*Erg < 4) && (*Erg > 0));
}

/*!------------------------------------------------------------------------
 * \fn     parse_rpa(const tStrComp *p_arg, z80_adr_vals_t *p_vals, unsigned mode_mask, int *p_auto_val, Boolean force)
 * \brief  parse indirect address expression
 * \param  p_arg source argument
 * \param  p_vals buffer for parse result
 * \param  mode_mask allowed addressing modes
 * \param  p_auto_val returns auto-in/decrement value
 * \param  force assume expression is indirect, even without outer (...)
 * \return True if expression was detected as indirect (parsing may still have failed)
 * ------------------------------------------------------------------------ */


static int split_auto_val(tStrComp *p_arg)
{
  int l = strlen(p_arg->str.p_str);

  if (l >= 2)
  {
    if (!strcmp(p_arg->str.p_str + l - 2, "--"))
    {
      StrCompShorten(p_arg, 2);
      return - 2;
    }
    if (!strcmp(p_arg->str.p_str + l - 2, "++"))
    {
      StrCompShorten(p_arg, 2);
      return + 2;
    }
  }

  if (l >= 1)
  {
    if (p_arg->str.p_str[l - 1] == '-')
    {
      StrCompShorten(p_arg, 1);
      return -1;
    }
    if (p_arg->str.p_str[l - 1] == '+')
    {
      StrCompShorten(p_arg, 1);
      return +1;
    }
  }

  return 0;
}

static void parse_indirect_list(z80_adr_vals_t *p_vals, const tStrComp *p_arg, unsigned mode_mask, int auto_val)
{
  char *p;
  tStrComp rem, arg;
  Byte reg, base = 0, index = 0;
  LongInt disp_acc = 0;
  Boolean bad_reg, first_unknown = False, this_minus, next_minus;

  StrCompRefRight(&arg, p_arg, 0);
  KillPostBlanksStrComp(&arg);
  this_minus = False;
  do
  {
    /* split off one component */

    KillPrefBlanksStrCompRef(&arg);
    next_minus = False;
    p = indir_split_pos(arg.str.p_str); /* TODO: parentheses */
    if (p)
    {
      next_minus = (*p == '-');
      StrCompSplitRef(&arg, &rem, &arg, p);
      KillPostBlanksStrComp(&arg);
    }
    bad_reg = False;

    /* 8 bit register? Note that a B/D/H may actually mean BC/DE/HL
       in 'old syntax': */


    if (decode_reg8(arg.str.p_str, &reg))
    {
      if (this_minus)
        bad_reg = True;
      else switch (reg)
      {
        case REG_A:
          if (index)
            bad_reg = True;
          else
            index = reg;
          break;
        case REG_B:
          if (!index)
            index = reg;
          else if (!base)
            base = REG_BC;
          else
            bad_reg = True;
          break;
        case REG_D:
          if (!base)
            base = REG_DE;
          else
            bad_reg = True;
          break;
        case REG_H:
          if (!base)
            base = REG_HL;
          else
            bad_reg = True;
          break;
        default:
          bad_reg = True;
      }
    }

    /* 16 bit register? */

    else if (decode_reg16(arg.str.p_str, &reg, False))
    {
      if (this_minus)
        bad_reg = True;
      else switch (reg)
      {
        case REG_EA:
          if (index)
            bad_reg = True;
          else
            index = reg;
          break;
        case REG_BC:
        case REG_DE:
        case REG_HL:
          if (base)
            bad_reg = True;
          else
            base = reg;
          break;
        default:
          bad_reg = True;
      }
    }
    else
    {
      tEvalResult eval_result;
      Word value;

      value = EvalStrIntExpressionWithResult(&arg, UInt16, &eval_result);
      if (!eval_result.OK)
        return;
      if (mFirstPassUnknownOrQuestionable(eval_result.Flags))
        first_unknown = True;
      disp_acc = this_minus ? (disp_acc - value) : (disp_acc + value);
    }

    if (bad_reg)
    {
      WrStrErrorPos(ErrNum_InvReg, &arg);
      return;
    }

    if (p)
    {
      arg = rem;
      this_minus = next_minus;
    }
  }
  while (p);

  /* Dissolve ambiguities */

  if ((index == REG_B) && !base)
  {
    index = 0;
    base = REG_BC;
  }

  /* For auto-in/decrement, only a plain base register DE/HL is allowed.
     Furthermore, (..)-- is not implemented: */


  if (auto_val)
  {
    if (index || (base < REG_BC) || (base > REG_HL) || disp_acc || (auto_val == -2))
      WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
    else
    {
      p_vals->val = (base + 2) + (2 * !!(auto_val < 0));
      p_vals->mode = e_mod_indir;
    }
  }
  else
  {
    /* (BC) (DE) (HL) */
    if ((base >= REG_BC) && (base <= REG_HL) && !index && !disp_acc)
    {
      p_vals->val = base;
      p_vals->mode = e_mod_indir;
    }
    /* (DE+byte) (HL+byte) */
    else if ((base >= REG_DE) && (base <= REG_HL) && !index)
    {
      if (first_unknown)
        disp_acc &= 0xff;
      if ((disp_acc > 0xff) || (disp_acc < -0x80))
      {
        WrStrErrorPos(ErrNum_OverRange, p_arg);
        return;
      }
      p_vals->val = (base << 2) + 3;
      p_vals->mode = e_mod_indir;
      p_vals->vals[p_vals->count++] = Lo(disp_acc);
    }
    /* (HL+A) (HL+B) */
    else if ((base == REG_HL) && (index >= REG_A) && (index <= REG_B) && !disp_acc)
    {
      p_vals->val = index + 11;
      p_vals->mode = e_mod_indir;
    }
    /* (HL+EA) */
    else if ((base == REG_HL) && (index == REG_EA) && !disp_acc)
    {
      p_vals->val = 14;
      p_vals->mode = e_mod_indir;
    }
    /* abs/wa */
    else if (!base && !index)
    {
      Boolean is_wa = (Hi(disp_acc) == WorkArea),
              may_wa = !!(mode_mask & MModWA);

      p_vals->vals[p_vals->count++] = Lo(disp_acc);
      if ((may_wa && is_wa) || (!may_wa && first_unknown))
        p_vals->mode = e_mod_wa;
      else
      {
        p_vals->vals[p_vals->count++] = Hi(disp_acc);
        p_vals->mode = e_mod_abs;
      }
    }
    else
      WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
  }
}

static Boolean parse_rpa(const tStrComp *p_arg, z80_adr_vals_t *p_vals, unsigned mode_mask, int *p_auto_val, Boolean force)
{
  tStrComp arg;
  Boolean is_indirect;

  /* split off auto-in/decrement */

  StrCompRefRight(&arg, p_arg, 0);
  *p_auto_val = split_auto_val(&arg);

  /* Indirect? */

  is_indirect = IsIndirect(arg.str.p_str);
  if (is_indirect)
  {
    StrCompIncRefLeft(&arg, 1);
    StrCompShorten(&arg, 1);
    KillPostBlanksStrComp(&arg);
    if (!*p_auto_val)
      *p_auto_val = split_auto_val(&arg);
  }

  /* Auto-in/decrement enforces indirect parsing: */

  if (is_indirect || *p_auto_val || force)
  {
    parse_indirect_list(p_vals, &arg, mode_mask, *p_auto_val);
    return True;
  }
  else
    return False;
}

/*!------------------------------------------------------------------------
 * \fn     reset_z80_adr_vals(z80_adr_vals_t *p_vals)
 * \brief  clear encoded addressing mode container
 * \param  p_vals container to clear
 * ------------------------------------------------------------------------ */


static void reset_z80_adr_vals(z80_adr_vals_t *p_vals)
{
  p_vals->mode = e_mod_none;
  p_vals->force_long = 0;
  p_vals->val = 0;
  p_vals->count = 0;
}

/*!------------------------------------------------------------------------
 * \fn     Decode_rpa2(const tStrComp *pArg, Byte *Erg, Byte *Disp)
 * \brief  Decode rpa2 argument (indirect 8 bit argument in memory)
 * \param  pArg source argument
 * \param  Erg resulting addressing mode
 * \param  Disp resulting addressing displacement
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean Decode_rpa2(const tStrComp *pArg, Byte *Erg, Byte *Disp)
{
  z80_adr_vals_t vals;
  int auto_val;

  /* We force parsing as indirect, so the return value is always true: */

  reset_z80_adr_vals(&vals);
  (void)parse_rpa(pArg, &vals, MModIndir, &auto_val, True);
  switch (vals.mode)
  {
    case e_mod_indir:
      if ((auto_val < -1) || (auto_val > 1))
        goto bad_mode;
      *Erg = vals.val;
      *Disp = (vals.count > 0) ? vals.vals[0] : 0;
      return True;
    case e_mod_wa:
    case e_mod_abs:
    bad_mode:
      WrStrErrorPos(ErrNum_InvAddrMode, pArg);
      return False;
    default:
      return False;
  }
}

/*!------------------------------------------------------------------------
 * \fn     Decode_rpa(const tStrComp *pArg, Byte *Erg)
 * \brief  Decode rpa argument (indirect 8 bit argument in memory, without index)
 * \param  pArg source argument
 * \param  Erg resulting addressing mode
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean is_rpa(Byte value)
{
  return (value >= 1) && (value <= 7);
}

static Boolean Decode_rpa(const tStrComp *pArg, Byte *Erg)
{
  Byte Dummy;

  if (!Decode_rpa2(pArg, Erg, &Dummy)) return False;
  if (!is_rpa(*Erg))
  {
    WrStrErrorPos(ErrNum_InvAddrMode, pArg);
    return False;
  }
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     Decode_rpa1(const tStrComp *pArg, Byte *Erg)
 * \brief  Decode rpa1 argument (indirect 8 bit argument in memory, without index or auto-in/decrement)
 * \param  pArg source argument
 * \param  Erg resulting addressing mode
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean is_rpa1(Byte value)
{
  return (value >= 1) && (value <= 3);
}

static Boolean Decode_rpa1(const tStrComp *pArg, Byte *Erg)
{
  Byte Dummy;

  if (!Decode_rpa2(pArg, Erg, &Dummy)) return False;
  if (!is_rpa1(*Erg))
  {
    WrStrErrorPos(ErrNum_InvAddrMode, pArg);
    return False;
  }
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     Decode_rpa3(const tStrComp *pArg, Byte *Erg, ShortInt *Disp)
 * \brief  Decode rpa3 argument (indirect 16 bit argument in memory)
 * \param  pArg source argument
 * \param  Erg resulting addressing mode
 * \param  Disp resulting addressing displacement
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean is_rpa3(Byte value)
{
  return ((value >= 2) && (value <= 5))
      || ((value >= 11) && (value <= 15));
}

static Boolean Decode_rpa3(const tStrComp *pArg, Byte *Erg, Byte *Disp)
{
  z80_adr_vals_t vals;
  int auto_val;

  /* We force parsing as indirect, so the return value is always true: */

  reset_z80_adr_vals(&vals);
  (void)parse_rpa(pArg, &vals, MModIndir, &auto_val, True);
  switch (vals.mode)
  {
    case e_mod_indir:
      if ((auto_val != -2) && (auto_val != 0) && (auto_val != 2))
        goto bad_mode;
      if (!is_rpa3(vals.val))
        goto bad_mode;
      *Erg = vals.val;
      *Disp = (vals.count > 0) ? vals.vals[0] : 0;
      return True;
    case e_mod_wa:
    case e_mod_abs:
    bad_mode:
      WrStrErrorPos(ErrNum_InvAddrMode, pArg);
      return False;
    default:
      return False;
  }
}

/*!------------------------------------------------------------------------
 * \fn     Decode_f(char *Asc, Byte *Erg)
 * \brief  descode status flag
 * \param  Asc source argument
 * \param  Erg returns encoded flag
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean Decode_f(char *Asc, Byte *Erg)
{
#define FlagCnt 3
  static const char Flags[FlagCnt][3] = {"CY", "HC", "Z"};

  for (*Erg = 0; *Erg < FlagCnt; (*Erg)++)
   if (!as_strcasecmp(Flags[*Erg], Asc)) break;
  *Erg += 2; return (*Erg <= 4);
}

/*!------------------------------------------------------------------------
 * \fn     decode_sr_core(const tStrComp *p_arg)
 * \brief  core register list decode
 * \param  p_arg source argument
 * \return * to record of decoded register
 * ------------------------------------------------------------------------ */


static const sreg_t *decode_sr_core(const sreg_t *p_sregs, const tStrComp *p_arg)
{
  for (; p_sregs->p_name; p_sregs++)
    if (check_core(p_sregs->core_mask) && !as_strcasecmp(p_arg->str.p_str, p_sregs->p_name))
      return p_sregs;
  return NULL;
}

/*!------------------------------------------------------------------------
 * \fn     check_sr_flag(Byte reg_flags, Byte req_flag, const tStrComp *p_arg)
 * \brief  check whether special register's flags fulfill requirement, and
           issue error if not
 * \param  reg_flags register's suppoerted flags
 * \param  req_flag required flag
 * \param  p_arg source argument
 * \return True if OK
 * ------------------------------------------------------------------------ */


static Boolean check_sr_flag(Byte reg_flags, Byte req_flag, const tStrComp *p_arg)
{
  if (reg_flags & req_flag)
    return True;
  WrStrErrorPos(reg_flags ? ErrNum_InvOpOnReg : ErrNum_InvReg, p_arg);
  return False;
}

static decode_reg_res_t decode_sr_flag(const tStrComp *p_arg, const sreg_t *p_sregs, Byte *p_res, Byte flag)
{
  const sreg_t *p_reg = decode_sr_core(p_sregs, p_arg);

  if (p_reg)
  {
    if (check_sr_flag(p_reg->flags, flag, p_arg))
    {
      *p_res = p_reg->code;
      return e_decode_reg_ok;
    }
    else
      return e_decode_reg_error;
  }
  else
    return e_decode_reg_unknown;
}

#define decode_sr1(p_arg, p_res) decode_sr_flag(p_arg, s_regs8, p_res, eFlagSR1)
#define decode_sr(p_arg, p_res) decode_sr_flag(p_arg, s_regs8, p_res, eFlagSR)
#define decode_sr2(p_arg, p_res) decode_sr_flag(p_arg, s_regs8, p_res, eFlagSR2)

/*!------------------------------------------------------------------------
 * \fn     Decode_irf(const tStrComp *p_arg, ShortInt *p_res)
 * \brief  decode interrupt flag argument
 * \param  p_arg source argument
 * \param  p_res resulting flag #
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean Decode_irf(const tStrComp *p_arg, ShortInt *p_res)
{
  for (*p_res = 0; int_flags[*p_res].p_name; (*p_res)++)
    if (!as_strcasecmp(int_flags[*p_res].p_name, p_arg->str.p_str))
    {
      *p_res = int_flags[*p_res].code;
      return True;
    }
  WrStrErrorPos(ErrNum_UnknownInt, p_arg);
  return False;
}

/*!------------------------------------------------------------------------
 * \fn     Decode_wa(const tStrComp *pArg, Byte *Erg, Byte max_address)
 * \brief  decode working area address argument
 * \param  pArg source argument
 * \param  Erg resulting (short) address
 * \param  max_address range limit
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean Decode_wa(const tStrComp *pArg, Byte *Erg, Byte max_address)
{
  Word Adr;
  Boolean OK;
  tSymbolFlags Flags;

  Adr = EvalStrIntExpressionWithFlags(pArg, Int16, &OK, &Flags);
  if (!OK)
    return False;

  if (!mFirstPassUnknown(Flags) && (Hi(Adr) != WorkArea)) WrStrErrorPos(ErrNum_InAccPage, pArg);
  *Erg = Lo(Adr);

  if (mFirstPassUnknownOrQuestionable(Flags))
    *Erg &= max_address;

  return ChkRangePos(*Erg, 0, max_address, pArg);
}

static Boolean HasDisp(ShortInt Mode)
{
  return ((Mode & 11) == 11);
}

/*!------------------------------------------------------------------------
 * \fn     set_op_size(tSymbolSize new_op_size, const tStrComp *p_arg)
 * \brief  Set operand size and throw error in case of conflict
 * \param  new_op_size operand size to set
 * \param  p_arg source argument carrying this size
 * ------------------------------------------------------------------------ */


static Boolean set_op_size(tSymbolSize new_op_size, const tStrComp *p_arg)
{
  if (z80_op_size == eSymbolSizeUnknown)
    z80_op_size = new_op_size;
  else if (z80_op_size != new_op_size)
  {
    WrStrErrorPos(ErrNum_ConfOpSizes, p_arg);
    return False;
  }
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     decode_z80_adr(const tStrComp *p_arg, z80_adr_vals_t *p_vals, unsigned mode_mask)
 * \brief  decode Z80 style address expression
 * \param  p_arg source argument
 * \param  p_vals encoded values
 * \param  mode_mask bit mask of allowe daddressing modes
 * \return resulting addressing mode
 * ------------------------------------------------------------------------ */


static z80_adr_mode_t decode_z80_adr(const tStrComp *p_arg, z80_adr_vals_t *p_vals, unsigned mode_mask)
{
  Boolean ok;
  int auto_val;
  const sreg_t *p_sreg;

  reset_z80_adr_vals(p_vals);

  /* Registers: */

  if (!as_strcasecmp(p_arg->str.p_str, ">A"))
  {
    if (set_op_size(eSymbolSize8Bit, p_arg))
    {
      p_vals->force_long = True;
      p_vals->val = REG_A;
      p_vals->mode = e_mod_reg8;
    }
    goto found;
  }

  if (decode_reg8(p_arg->str.p_str, &p_vals->val))
  {
    /* No V register on low function core */
    if ((pCurrCPUProps->Core == eCore7800Low) && (p_vals->val == REG_V));
    /* EA register only on 7807++ */
    else if ((pCurrCPUProps->Core < eCore7807) && (p_vals->val >= REG_EAH));
    else
    {
      if (set_op_size(eSymbolSize8Bit, p_arg))
        p_vals->mode = e_mod_reg8;
      goto found;
    }
  }

  if (decode_reg16(p_arg->str.p_str, &p_vals->val, False))
  {
    /* EA register only on 7807++ */
    if ((pCurrCPUProps->Core < eCore7807) && (p_vals->val >= REG_EA));
    else
    {
      if (set_op_size(eSymbolSize16Bit, p_arg))
        p_vals->mode = e_mod_reg16;
      goto found;
    }
  }

  p_sreg = decode_sr_core(s_regs8, p_arg);
  if (p_sreg)
  {
    if (set_op_size(eSymbolSize8Bit, p_arg))
    {
      p_vals->mode = e_mod_sreg8;
      p_vals->val = p_sreg->code;
      p_vals->vals[0] = p_sreg->flags;
    }
    goto found;
  }

  p_sreg = decode_sr_core(s_regs16, p_arg);
  if (p_sreg)
  {
    if (set_op_size(eSymbolSize16Bit, p_arg))
    {
      p_vals->mode = e_mod_sreg16;
      p_vals->val = p_sreg->code;
      p_vals->vals[0] = p_sreg->flags;
    }
    goto found;
  }

  /* indirect stuff: */

  if (parse_rpa(p_arg, p_vals, mode_mask, &auto_val, False))
  {
    /* single or double auto-in/decrement implicitly describes operand size: */

    if (auto_val)
    {
      if (!set_op_size(((auto_val == 1) || (auto_val == -1)) ? eSymbolSize8Bit : eSymbolSize16Bit, p_arg))
        reset_z80_adr_vals(p_vals);
    }
    goto found;
  }

  /* Immediate: */

  if (!(mode_mask & MModImm))
  {
    WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
    goto found;
  }
  if (z80_op_size == eSymbolSizeUnknown)
  {
    z80_op_size = z80_def_op_size;
    z80_def_op_size = eSymbolSizeUnknown;
  }
  switch (z80_op_size)
  {
    case eSymbolSize8Bit:
      p_vals->vals[0] = EvalStrIntExpression(p_arg, Int8, &ok);
      if (ok)
      {
        p_vals->count = 1;
        p_vals->mode = e_mod_imm;
      }
      break;
    case eSymbolSize16Bit:
    {
      Word tmp = EvalStrIntExpression(p_arg, Int16, &ok);
      if (ok)
      {
        p_vals->vals[p_vals->count++] = Lo(tmp);
        p_vals->vals[p_vals->count++] = Hi(tmp);
        p_vals->mode = e_mod_imm;
      }
      break;
    }
    default:
      WrStrErrorPos(ErrNum_UndefOpSizes, p_arg);
  }

found:
  if ((p_vals->mode != e_mod_none) && !((mode_mask >> p_vals->mode) & 1))
  {
    WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
    reset_z80_adr_vals(p_vals);
  }
  return p_vals->mode;
}

/*!------------------------------------------------------------------------
 * \fn     append_adr_vals(const z80_adr_vals_t *p_vals)
 * \brief  append encoded addess extension bytes to instruction
 * \param  p_vals contains bytes to append
 * ------------------------------------------------------------------------ */


static void append_adr_vals(const z80_adr_vals_t *p_vals)
{
  memcpy(&BAsmCode[CodeLen], p_vals->vals, p_vals->count);
  CodeLen += p_vals->count;
}

/*--------------------------------------------------------------------------*/
/* Bit Symbol Handling (uPD7807...7809 only) */

/*
 * Compact representation of bits in symbol table:
 * bits 0..2: bit position
 * bits 3...6/18: address in I/O or memory space
 * bit 19: 0 for memory space, 1 for I/O space
 * bits 20..23: core type
 */


/*!------------------------------------------------------------------------
 * \fn     eval_bit_position(const tStrComp *p_arg, Boolean *p_ok)
 * \brief  evaluate bit position
 * \param  bit position argument
 * \param  p_ok parsing OK?
 * \return numeric bit position
 * ------------------------------------------------------------------------ */


static LongWord eval_bit_position(const tStrComp *p_arg, Boolean *p_ok)
{
  return EvalStrIntExpression(p_arg, UInt3, p_ok);
}

/*!------------------------------------------------------------------------
 * \fn     assemble_bit_symbol(Byte core, Boolean is_io, Word address, Byte bit_pos)
 * \brief  transform bit symbol components into compact representation
 * \param  core core used to define this bit
 * \param  is_io special register or memory bit?
 * \param  address (I/O) register address
 * \param  bit_pos bit position
 * \return compact storage value
 * ------------------------------------------------------------------------ */


static LongWord assemble_bit_symbol(Byte core, Boolean is_io, Word address, Byte bit_pos)
{
  LongWord result = bit_pos | ((LongWord)address << 3);

  if (is_io)
    result |= 1ul << 19;
  result |= ((LongWord)core) << 20;
  return result;
}

/*!------------------------------------------------------------------------
 * \fn     decode_bit_arg_2(LongWord *p_result, const tStrComp *p_reg_arg, const tStrComp *p_bit_arg)
 * \brief  encode a bit symbol, address & bit position separated
 * \param  p_result resulting encoded bit
 * \param  p_reg_arg register argument
 * \param  p_bit_arg bit argument
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean decode_bit_arg_2(LongWord *p_result, const tStrComp *p_reg_arg, const tStrComp *p_bit_arg)
{
  Boolean ok;
  LongWord addr;
  Boolean is_io;
  Byte bit_pos;
  Byte s_reg;

  bit_pos = eval_bit_position(p_bit_arg, &ok);
  if (!ok)
    return False;

  if (decode_sr_flag(p_reg_arg, s_regs8, &s_reg, eFlagSR2) == e_decode_reg_ok)
  {
    if (s_reg > 0x0f)
    {
      WrStrErrorPos(ErrNum_InvReg, p_reg_arg);
      return False;
    }
    is_io = True;
    addr = s_reg;
  }

  else
  {
    addr = EvalStrIntExpression(p_reg_arg, UInt16, &ok);
    if (!ok)
      return False;
    is_io = False;
  }

  *p_result = assemble_bit_symbol(pCurrCPUProps->Core, is_io, addr, bit_pos);
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     decode_bit_arg(LongWord *p_result, int start, int stop)
 * \brief  encode a bit symbol from instruction argument(s)
 * \param  p_result resulting encoded bit
 * \param  start first argument
 * \param  stop last argument
 * \return True if success
 * ------------------------------------------------------------------------ */


static Boolean decode_bit_arg(LongWord *p_result, int start, int stop)
{
  *p_result = 0;

  /* Just one argument -> parse as bit argument */

  if (start == stop)
  {
    char *p_sep = RQuotPos(ArgStr[start].str.p_str, '.');

    if (p_sep)
    {
      tStrComp reg_comp, bit_comp;

      StrCompSplitRef(&reg_comp, &bit_comp, &ArgStr[start], p_sep);
      return decode_bit_arg_2(p_result, &reg_comp, &bit_comp);
    }
    else
    {
      tEvalResult eval_result;

      *p_result = EvalStrIntExpressionWithResult(&ArgStr[start], UInt24, &eval_result);
      if (eval_result.OK)
        ChkSpace(SegBData, eval_result.AddrSpaceMask);
      return eval_result.OK;
    }
  }

  /* register & bit position are given as separate arguments */

  else if (stop == start + 1)
    return decode_bit_arg_2(p_result, &ArgStr[start], &ArgStr[stop]);

  /* other # of arguments not allowed */

  else
  {
    WrError(ErrNum_WrongArgCnt);
    return False;
  }
}

/*!------------------------------------------------------------------------
 * \fn     dissect_bit_symbol(LongWord bit_symbol, Byte *p_core, Boolean *p_is_io, Word *p_address, Byte *p_bit_pos)
 * \brief  transform compact representation of bit (field) symbol into components
 * \param  bit_symbol compact storage
 * \param  p_core core used to define bit
 * \param  p_is_io special register or memory bit?
 * \param  p_address (I/O) register address
 * \param  p_bit_pos (start) bit position
 * \return constant True
 * ------------------------------------------------------------------------ */


static Boolean dissect_bit_symbol(LongWord bit_symbol, Byte *p_core, Boolean *p_is_io, Word *p_address, Byte *p_bit_pos)
{
  *p_core = (bit_symbol >> 20) & 15;
  *p_is_io = (bit_symbol >> 19) & 1;
  *p_address = (bit_symbol >> 3) & (*p_is_io ? 0xf : 0xffff);
  *p_bit_pos = bit_symbol & 7;
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     dissect_bit_7807(char *p_dest, size_t dest_size, LargeWord inp)
 * \brief  dissect compact storage of bit (field) into readable form for listing
 * \param  p_dest destination for ASCII representation
 * \param  dest_size destination buffer size
 * \param  inp compact storage
 * ------------------------------------------------------------------------ */


static void dissect_bit_7807(char *p_dest, size_t dest_size, LargeWord inp)
{
  Byte bit_pos, core;
  Word address;
  Boolean is_io;

  dissect_bit_symbol(inp, &core, &is_io, &address, &bit_pos);

  if (is_io)
  {
    const sreg_t *p_sreg;

    for (p_sreg = s_regs8; p_sreg->p_name; p_sreg++)
    {
      if (!((p_sreg->core_mask >> core) & 1))
        continue;
      if (address == p_sreg->code)
      {
        as_snprintf(p_dest, dest_size, "%s.%u", p_sreg->p_name, (unsigned)bit_pos);
        return;
      }
    }
    as_snprintf(p_dest, dest_size, "SR%u.%u", address, (unsigned)bit_pos);
  }
  else
    as_snprintf(p_dest, dest_size, "%~.*u%s.%u",
                ListRadixBase, (unsigned)address, GetIntConstIntelSuffix(ListRadixBase),
                (unsigned)bit_pos);
}

/*!------------------------------------------------------------------------
 * \fn     expand_bit_7807(const tStrComp *p_var_name, const struct sStructElem *p_struct_elem, LargeWord base)
 * \brief  expands bit definition when a structure is instantiated
 * \param  p_var_name desired symbol name
 * \param  p_struct_elem element definition
 * \param  base base address of instantiated structure
 * ------------------------------------------------------------------------ */


static void expand_bit_7807(const tStrComp *p_var_name, const struct sStructElem *p_struct_elem, LargeWord base)
{
  LongWord address = base + p_struct_elem->Offset;

  if (pInnermostNamedStruct)
  {
    PStructElem p_elem = CloneStructElem(p_var_name, p_struct_elem);

    if (!p_elem)
      return;
    p_elem->Offset = address;
    AddStructElem(pInnermostNamedStruct->StructRec, p_elem);
  }
  else
  {
    if (!ChkRange(address, 0, 0xffff)
     || !ChkRange(p_struct_elem->BitPos, 0, 7))
      return;

    PushLocHandle(-1);
    EnterIntSymbol(p_var_name, assemble_bit_symbol(pCurrCPUProps->Core, False, address, p_struct_elem->BitPos), SegBData, False);
    PopLocHandle();
    /* TODO: MakeUseList? */
  }
}

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

static Boolean check_core_and_error(unsigned core_mask)
{
  Boolean ret = check_core(core_mask);

  if (!ret)
    WrStrErrorPos(ErrNum_InstructionNotSupported, &OpPart);
  return ret;
}

static void PutCode(Word Code)
{
  if (Hi(Code) != 0)
    BAsmCode[CodeLen++] = Hi(Code);
  BAsmCode[CodeLen++] = Lo(Code);
}

/*!------------------------------------------------------------------------
 * \fn     decode_bit_7807_core(const tStrComp *p_arg, Byte code)
 * \brief  core routine for uPD7807 bit-oriented instructions
 * \param  p_arg source bit argument
 * \param  code machine code of instruction
 * ------------------------------------------------------------------------ */


static void decode_bit_7807_core(int arg_idx, Byte code)
{
  LongWord packed_bit;
  Boolean is_io;
  Word address;
  Byte bit_pos, core;

  if (!decode_bit_arg(&packed_bit, arg_idx, arg_idx))
    return;
  dissect_bit_symbol(packed_bit, &core, &is_io, &address, &bit_pos);

  if (!is_io)
  {
    if ((Hi(address) != WorkArea) || (Lo(address) > 15))
      WrStrErrorPos(ErrNum_InAccPage, &ArgStr[arg_idx]);
  }

  BAsmCode[1] = (is_io ? 0x80 : 0x00)
              | ((address & 15) << 3)
              | (bit_pos & 7);
  BAsmCode[0] = code;
  CodeLen = 2;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeFixed(Word Code)
 * \brief  Handle instructions without arguments
 * \param  index index into instruction table
 * ------------------------------------------------------------------------ */


static void DecodeFixed(Word index)
{
  const order_t *p_order = &fixed_orders[index];

  if (ChkArgCnt(0, 0) && check_core_and_error(p_order->core_mask))
    PutCode(p_order->code);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeMOV(Word Code)
 * \brief  handle MOV instruction
 * ------------------------------------------------------------------------ */


static void DecodeMOV(Word Code)
{
  Boolean OK;
  Byte HReg;
  Integer AdrInt;
  decode_reg_res_t res1;

  UNUSED(Code);

  if (!ChkArgCnt(2, 2));
  else if (!as_strcasecmp(ArgStr[1].str.p_str, "A"))
  {
    decode_reg_res_t res2;

    if ((res2 = decode_sr1(&ArgStr[2], &HReg)) != e_decode_reg_unknown)
    {
      if (res2 == e_decode_reg_ok)
      {
        CodeLen = 2;
        BAsmCode[0] = 0x4c;
        BAsmCode[1] = 0xc0 + HReg;
      }
    }
    else if ((res2 = decode_r1(&ArgStr[2], &HReg)) != e_decode_reg_unknown)
    {
      if (res2 == e_decode_reg_ok)
      {
        CodeLen = 1;
        BAsmCode[0] = 0x08 + HReg;
      }
    }
    else
    {
      AdrInt = EvalStrIntExpression(&ArgStr[2], Int16, &OK);
      if (OK)
      {
        CodeLen = 4;
        BAsmCode[0] = 0x70;
        BAsmCode[1] = 0x69;
        BAsmCode[2] = Lo(AdrInt);
        BAsmCode[3] = Hi(AdrInt);
      }
    }
  }
  else if (!as_strcasecmp(ArgStr[2].str.p_str, "A"))
  {
    decode_reg_res_t res2;

    if ((res2 = decode_sr(&ArgStr[1], &HReg)) != e_decode_reg_unknown)
    {
      if (res2 == e_decode_reg_ok)
      {
        CodeLen = 2;
        BAsmCode[0] = 0x4d;
        BAsmCode[1] = 0xc0 + HReg;
      }
    }
    else if ((res2 = decode_r1(&ArgStr[1], &HReg)) != e_decode_reg_unknown)
    {
      if (res2 == e_decode_reg_ok)
      {
        CodeLen = 1;
        BAsmCode[0] = 0x18 + HReg;
      }
    }
    else
    {
      AdrInt = EvalStrIntExpression(&ArgStr[1], Int16, &OK);
      if (OK)
      {
        CodeLen = 4;
        BAsmCode[0] = 0x70;
        BAsmCode[1] = 0x79;
        BAsmCode[2] = Lo(AdrInt);
        BAsmCode[3] = Hi(AdrInt);
      }
    }
  }
  else if ((pCurrCPUProps->Core == eCore7807) && !as_strcasecmp(ArgStr[1].str.p_str, "CY"))
    decode_bit_7807_core(2, 0x5f);
  else if ((pCurrCPUProps->Core == eCore7807) && !as_strcasecmp(ArgStr[2].str.p_str, "CY"))
    decode_bit_7807_core(1, 0x5a);
  else if ((res1 = decode_r(&ArgStr[1], &HReg)) != e_decode_reg_unknown)
  {
    if (res1 == e_decode_reg_ok)
    {
      AdrInt = EvalStrIntExpression(&ArgStr[2], Int16, &OK);
      if (OK)
      {
        CodeLen = 4;
        BAsmCode[0] = 0x70;
        BAsmCode[1] = 0x68 + HReg;
        BAsmCode[2] = Lo(AdrInt);
        BAsmCode[3] = Hi(AdrInt);
      }
    }
  }
  else if ((res1 = decode_r(&ArgStr[2], &HReg)) != e_decode_reg_unknown)
  {
    if (res1 == e_decode_reg_ok)
    {
      AdrInt = EvalStrIntExpression(&ArgStr[1], Int16, &OK);
      if (OK)
      {
        CodeLen = 4;
        BAsmCode[0] = 0x70;
        BAsmCode[1] = 0x78 + HReg;
        BAsmCode[2] = Lo(AdrInt);
        BAsmCode[3] = Hi(AdrInt);
      }
    }
  }

  /* Cannot say in this case if src or dest is invalid register: */

  else
    WrError(ErrNum_InvReg);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeMVI(Word Code)
 * \brief  handle MVI instruction
 * ------------------------------------------------------------------------ */


static void DecodeMVI(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(2, 2))
  {
    Byte HReg;
    Boolean OK;

    BAsmCode[1] = EvalStrIntExpression(&ArgStr[2], Int8, &OK);
    if (OK)
    {
      decode_reg_res_t res;

      if ((res = decode_r(&ArgStr[1], &HReg)) != e_decode_reg_unknown)
      {
        if (res == e_decode_reg_ok)
        {
          CodeLen = 2;
          BAsmCode[0] = 0x68 + HReg;
        }
      }
      else if ((res = decode_sr2(&ArgStr[1], &HReg)) != e_decode_reg_unknown)
      {
        if (res == e_decode_reg_ok)
        {
          if (!is_7807_781x) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
          else
          {
            CodeLen = 3;
            BAsmCode[2] = BAsmCode[1];
            BAsmCode[0] = 0x64;
            BAsmCode[1] = (HReg & 7) + ((HReg & 8) << 4);
          }
        }
      }
      else WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeMVIW(Word Code)
 * \brief  handle MVIW instruction
 * ------------------------------------------------------------------------ */


static void DecodeMVIW(Word Code)
{
  Boolean OK;

  UNUSED(Code);

  if (!ChkArgCnt(2, 2) || !check_core_and_error(core_mask_no_low));
  else if (Decode_wa(&ArgStr[1], BAsmCode + 1, 0xff))
  {
    BAsmCode[2] = EvalStrIntExpression(&ArgStr[2], Int8, &OK);
    if (OK)
    {
      CodeLen = 3;
      BAsmCode[0] = 0x71;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeMVIX(Word Code)
 * \brief  handle MVIX instruction
 * ------------------------------------------------------------------------ */


static void DecodeMVIX(Word Code)
{
  Boolean OK;
  Byte HReg;

  UNUSED(Code);

  if (!ChkArgCnt(2, 2) || !check_core_and_error(core_mask_no_low));
  else if (Decode_rpa1(&ArgStr[1], &HReg))
  {
    BAsmCode[1] = EvalStrIntExpression(&ArgStr[2], Int8, &OK);
    if (OK)
    {
      BAsmCode[0] = 0x48 + HReg;
      CodeLen = 2;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeLDAX_STAX(Word Code)
 * \brief  handle LDAX/STAX instructions
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeLDAX_STAX(Word Code)
{
  Byte HReg;

  if (ChkArgCnt(1, 1))
  {
    Boolean ok = is_7807_781x
               ? Decode_rpa2(&ArgStr[1], &HReg, &BAsmCode[1])
               : Decode_rpa(&ArgStr[1], &HReg);

    if (ok)
    {
      CodeLen = 1 + Ord(HasDisp(HReg));
      BAsmCode[0] = Code + ((HReg & 8) << 4) + (HReg & 7);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeLDEAX_STEAX(Word Code)
 * \brief  handle LDEAX/STEAX instructions
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeLDEAX_STEAX(Word Code)
{
  Byte HReg;

  if (ChkArgCnt(1, 1)
   && check_core_and_error(core_mask_7807_7810)
   && Decode_rpa3(&ArgStr[1], &HReg, &BAsmCode[2]))
  {
    CodeLen = 2 + Ord(HasDisp(HReg));
    BAsmCode[0] = 0x48;
    BAsmCode[1] = Code + HReg;
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeLXI(Word Code)
 * \brief  Handle LXI instruction
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeLXI(Word Code)
{
  Byte HReg;
  Integer AdrInt;
  Boolean OK;

  UNUSED(Code);

  if (!ChkArgCnt(2, 2))
    return;
  OK = (pCurrCPUProps->Core >= eCore7807)
     ? Decode_rp2(ArgStr[1].str.p_str, &HReg)
     : Decode_rp(ArgStr[1].str.p_str, &HReg);
  if (!OK) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
  else
  {
    AdrInt = EvalStrIntExpression(&ArgStr[2], Int16, &OK);
    if (OK)
    {
      CodeLen = 3;
      BAsmCode[0] = 0x04 + (HReg << 4);
      BAsmCode[1] = Lo(AdrInt);
      BAsmCode[2] = Hi(AdrInt);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodePUSH_POP(Word Code)
 * \brief  Handle PUSH/POP instruction
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodePUSH_POP(Word Code)
{
  Byte HReg;

  if (!ChkArgCnt(1, 1));
  else if (!Decode_rp1(ArgStr[1].str.p_str, &HReg)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
  else
    PutCode(Code | (HReg << (Hi(Code) ? 4 : 0)));
}

/*!------------------------------------------------------------------------
 * \fn     DecodeDMOV(Word Code)
 * \brief  Handle DMOV instruction
 * ------------------------------------------------------------------------ */


static void DecodeDMOV(Word Code)
{
  Byte HReg, SReg;

  UNUSED(Code);

  if (ChkArgCnt(2, 2) && check_core_and_error(core_mask_7807_7810))
  {
    Boolean Swap = as_strcasecmp(ArgStr[1].str.p_str, "EA") || False;
    const tStrComp *pArg1 = Swap ? &ArgStr[2] : &ArgStr[1],
                   *pArg2 = Swap ? &ArgStr[1] : &ArgStr[2];

    if (as_strcasecmp(pArg1->str.p_str, "EA")) WrStrErrorPos(ErrNum_InvAddrMode, pArg1);
    else if (Decode_rp3(pArg2->str.p_str, &HReg))
    {
      CodeLen = 1;
      BAsmCode[0] = 0xa4 + HReg;
      if (Swap)
        BAsmCode[0] += 0x10;
    }
    else switch (decode_sr_flag(pArg2, s_regs16, &SReg, Swap ? eFlagSR3 : eFlagSR4))
    {
      case e_decode_reg_ok:
        CodeLen = 2;
        BAsmCode[0] = 0x48;
        BAsmCode[1] = 0xc0 + SReg;
        if (Swap)
          BAsmCode[1] += 0x12;
        break;
      case e_decode_reg_error:
        WrStrErrorPos(ErrNum_InvCtrlReg, pArg2);
        break;
      default:
        WrStrErrorPos(ErrNum_InvAddrMode, pArg2);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     decode_alu_z80(Word code)
 * \brief  handle ALU instruction, Z80-style
 * \param  code machine code & flags
 * ------------------------------------------------------------------------ */


static void decode_alu_z80(Word code)
{
  if (ChkArgCnt(2, 2)
   && ChkZ80Syntax(eSyntaxZ80))
  {
    const Byte flags = Hi(code);
    z80_adr_vals_t src_adr_vals, dest_adr_vals;
    code = Lo(code);

    switch (decode_z80_adr(&ArgStr[1], &dest_adr_vals,
                           MModReg8 | MModReg16 | MModSReg8
                         | ((code & 1) ? MModWA : 0)))
    {
      case e_mod_reg8:
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals,
                               MModReg8
                             | MModImm
                             | ((dest_adr_vals.val == REG_A) ? MModIndir : 0)
                             | (((dest_adr_vals.val == REG_A) && (pCurrCPUProps->Core >= eCore7800High)) ? MModWA : 0)))
        {
          case e_mod_reg8:
            if ((dest_adr_vals.val == REG_A) && !dest_adr_vals.force_long && (src_adr_vals.val < 8) && (flags & ALUReg_Src))
            {
              BAsmCode[CodeLen++] = 0x60;
              BAsmCode[CodeLen++] = 0x80 | (code << 3) | src_adr_vals.val;
            }
            else if ((src_adr_vals.val == REG_A) && !src_adr_vals.force_long && (dest_adr_vals.val  < 8) && (flags & ALUReg_Dest))
            {
              BAsmCode[CodeLen++] = 0x60;
              BAsmCode[CodeLen++] = (code << 3) | dest_adr_vals.val;
              /* ONA/OFFA only exist as A,r, but since binary AND is commutative, we can just swap operands: */
              if ((code == 9) || (code == 11))
                BAsmCode[CodeLen - 1] |= 0x80;
            }
            else
              WrError(ErrNum_InvAddrMode);
            break;
          case e_mod_imm:
            if ((dest_adr_vals.val == REG_A) && !dest_adr_vals.force_long)
              BAsmCode[CodeLen++] = 0x06 | ((code & 14) << 3) | (code & 1);
            else if (pCurrCPUProps->Core < eCore7800High) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
            else
            {
              BAsmCode[CodeLen++] = 0x74;
              BAsmCode[CodeLen++] = (code << 3) | dest_adr_vals.val;
            }
            if (CodeLen > 0)
              BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            break;
          case e_mod_indir:
            if (!is_rpa(src_adr_vals.val)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else
            {
              BAsmCode[CodeLen++] = 0x70;
              BAsmCode[CodeLen++] = 0x80 | (code << 3) | src_adr_vals.val;
            }
            break;
          case e_mod_wa:
            BAsmCode[CodeLen++] = 0x74;
            BAsmCode[CodeLen++] = 0x80 | (code << 3);
            BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            break;
          default:
            break;
        }
        break;
      case e_mod_reg16:
      {
        unsigned mask = MModReg16;

        if (dest_adr_vals.val != REG_EA)
        {
          WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
          return;
        }
        if ((code == 8) || (code == 12))
        {
          mask |= MModReg8;
          z80_op_size = eSymbolSizeUnknown;
        }
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals, mask))
        {
          case e_mod_reg8:
            if ((src_adr_vals.val < REG_A) || (src_adr_vals.val > REG_C)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else
            {
              BAsmCode[CodeLen++] = 0x70;
              BAsmCode[CodeLen++] = (code << 3) | src_adr_vals.val;
            }
            break;
          case e_mod_reg16:
            if ((src_adr_vals.val < REG_BC) || (src_adr_vals.val > REG_HL)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else
            {
              BAsmCode[CodeLen++] = 0x74;
              BAsmCode[CodeLen++] = 0x84 | (code << 3) | src_adr_vals.val;
            }
            break;
          default:
            break;
        }
        break;
      }
      case e_mod_sreg8:
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals, (flags & ALUImm_SR) ? MModImm : 0))
        {
          case e_mod_imm:
            if (check_sr_flag(dest_adr_vals.vals[0], eFlagSR2, &ArgStr[1]))
            {
              BAsmCode[CodeLen++] = 0x64;
              BAsmCode[CodeLen++] = (code << 3)
                                  | ((pCurrCPUProps->Core < eCore7800High) ? 0x80 : 0x00)
                                  | (dest_adr_vals.val & 7)
                                  | ((dest_adr_vals.val & 8) << 4);
              BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            }
            break;
          default:
            break;
        }
        break;
      case e_mod_wa:
        z80_def_op_size = eSymbolSize8Bit;
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals, MModImm))
        {
          case e_mod_imm:
            BAsmCode[CodeLen++] = ((code & 0x0e) << 3) | 0x05;
            BAsmCode[CodeLen++] = dest_adr_vals.vals[0];
            BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeALUImm(Word Code)
 * \brief  handle 8 Bit ALU instructions with immediate source
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeALUImm(Word Code)
{
  ShortInt HVal8;
  Byte HReg;
  Boolean OK;
  Byte Flags;

  Flags = Hi(Code);
  Code = Lo(Code);

  if (ChkArgCnt(2, 2))
  {
    HVal8 = EvalStrIntExpression(&ArgStr[2], Int8, &OK);
    if (OK)
    {
      tStrComp Arg1;
      decode_reg_res_t res;

      /* allow >A to enforce long addressing */

      StrCompRefRight(&Arg1, &ArgStr[1], as_strcasecmp(ArgStr[1].str.p_str, ">A") ? 0 : 1);
      if (!as_strcasecmp(ArgStr[1].str.p_str, "A"))
      {
        CodeLen = 2;
        BAsmCode[0] = 0x06 + ((Code & 14) << 3) + (Code & 1);
        BAsmCode[1] = HVal8;
      }
      else if ((res = decode_r(&Arg1, &HReg)) != e_decode_reg_unknown)
      {
        if (res != e_decode_reg_ok);
        else if (pCurrCPUProps->Core == eCore7800Low) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
        else
        {
          CodeLen = 3;
          BAsmCode[0] = is_7807_781x ? 0x74 : 0x64;
          BAsmCode[2] = HVal8;
          BAsmCode[1] = HReg + (Code << 3);
        }
      }
      else if ((res = decode_sr2(&ArgStr[1], &HReg)) != e_decode_reg_unknown)
      {
        if (res != e_decode_reg_ok);
        else if (!(Flags & ALUImm_SR)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
        else
        {
          CodeLen = 3;
          BAsmCode[0] = 0x64;
          BAsmCode[1] = (HReg & 7) | (Code << 3)
                      | (is_7807_781x ? ((HReg & 8) << 4) : 0x80);
          BAsmCode[2] = HVal8;
        }
      }
      else WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeALUReg(Word Code)
 * \brief  Handle ALU instructions with A and register as argument
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeALUReg(Word Code)
{
  Byte HReg;
  Byte Flags;

  Flags = Hi(Code);
  if ((CurrZ80Syntax & eSyntaxZ80) && (Flags & ALUReg_MayZ80))
  {
    decode_alu_z80(Code);
    return;
  }
  Code = Lo(Code);

  if (ChkArgCnt(2, 2))
  {
    Boolean NoSwap = !as_strcasecmp(ArgStr[1].str.p_str, "A");
    tStrComp *pArg1 = NoSwap ? &ArgStr[1] : &ArgStr[2],
             *pArg2 = NoSwap ? &ArgStr[2] : &ArgStr[1],
             Arg2;
    decode_reg_res_t res;

    /* allow >A to enforce <op> r,A instrad of <op> A,r */

    StrCompRefRight(&Arg2, pArg2, as_strcasecmp(pArg2->str.p_str, ">A") ? 0 : 1);

    if (as_strcasecmp(pArg1->str.p_str, "A")) WrStrErrorPos(ErrNum_InvAddrMode, pArg1);
    else if ((res = decode_r(&Arg2, &HReg)) == e_decode_reg_unknown) WrStrErrorPos(ErrNum_InvReg, &Arg2);
    else if (res == e_decode_reg_ok)
    {
      if (NoSwap && !(Flags & ALUReg_Src)) WrStrErrorPos(ErrNum_InvAddrMode, &Arg2);
      else if (!NoSwap && !(Flags & ALUReg_Dest)) WrStrErrorPos(ErrNum_InvAddrMode, &Arg2);
      else
      {
        CodeLen = 2;
        BAsmCode[0] = 0x60;
        BAsmCode[1] = (Code << 3) + HReg;
        if ((NoSwap) || (Memo("ONA")) || (Memo("OFFA")))
          BAsmCode[1] += 0x80;
      }
    }
  }
}

static void DecodeALURegW(Word Code)
{
  if (ChkArgCnt(1, 1)
   && check_core_and_error(core_mask_no_low)
   && Decode_wa(&ArgStr[1], BAsmCode + 2, 0xff))
  {
    CodeLen = 3;
    BAsmCode[0] = 0x74;
    BAsmCode[1] = 0x80 + (Code << 3);
  }
}

static void DecodeALURegX(Word Code)
{
  Byte HReg;

  if (!ChkArgCnt(1, 1));
  else if (Decode_rpa(&ArgStr[1], &HReg))
  {
    CodeLen = 2;
    BAsmCode[0] = 0x70;
    BAsmCode[1] = 0x80 + (Code << 3) + HReg;
  }
}

static void DecodeALUEA(Word Code)
{
  Byte HReg;

  if (!ChkArgCnt(2, 2));
  else if (!check_core_and_error(core_mask_7807_7810));
  else if (as_strcasecmp(ArgStr[1].str.p_str, "EA")) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
  else if (!Decode_rp3(ArgStr[2].str.p_str, &HReg)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
  else
  {
    CodeLen = 2;
    BAsmCode[0] = 0x74;
    BAsmCode[1] = 0x84 + (Code << 3) + HReg;
  }
}

static void DecodeALUImmW(Word Code)
{
  Boolean OK;

  if (!ChkArgCnt(2, 2));
  else if (Decode_wa(&ArgStr[1], BAsmCode + 1, 0xff))
  {
    BAsmCode[2] = EvalStrIntExpression(&ArgStr[2], Int8, &OK);
    if (OK)
    {
      CodeLen = 3;
      BAsmCode[0] = 0x05 + ((Code >> 1) << 4);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeAbs(Word Code)
 * \brief  Handle instructions with absolute address as argument
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeAbs(Word Code)
{
  if (!ChkArgCnt(1, 1));
  else
  {
    Boolean OK;
    Integer AdrInt;

    AdrInt = EvalStrIntExpression(&ArgStr[1], Int16, &OK);
    if (OK)
    {
      PutCode(Code);
      BAsmCode[CodeLen++] = Lo(AdrInt);
      BAsmCode[CodeLen++] = Hi(AdrInt);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeReg2(Word index)
 * \brief  Handle instructions with r2 as argument
 * \param  index index into instruction table
 * ------------------------------------------------------------------------ */


static void DecodeReg2(Word index)
{
  const order_t *p_order = &reg2_orders[index];
  Byte HReg;
  decode_reg_res_t res;

  if (!ChkArgCnt(1, 1) || !check_core_and_error(p_order->core_mask));
  else if ((res = decode_r2(&ArgStr[1], &HReg)) == e_decode_reg_unknown) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
  else if (res == e_decode_reg_ok)
    PutCode(p_order->code + HReg);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeWA(Word Code)
 * \brief  Handle instructions with work area address as argument
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeWork(Word Code)
{
  if (ChkArgCnt(1, 1)
   && Decode_wa(&ArgStr[1], BAsmCode + 1, 0xff))
  {
    CodeLen = 2;
    BAsmCode[0] = Code;
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeA(Word Code)
 * \brief  Handle instructions with A as argument
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeA(Word Code)
{
  if (!ChkArgCnt(1, 1));
  else if (as_strcasecmp(ArgStr[1].str.p_str, "A")) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
  else
    PutCode(Code);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeEA(Word Code)
 * \brief  Handle instructions with EA as argument
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeEA(Word Code)
{
  if (!ChkArgCnt(1, 1) || !check_core_and_error(core_mask_7807_7810));
  else if (as_strcasecmp(ArgStr[1].str.p_str, "EA")) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
  else
  {
    CodeLen = 2;
    BAsmCode[0] = Hi(Code);
    BAsmCode[1] = Lo(Code);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeDCX_INX(Word Code)
 * \brief  Handle INX/DCX Instructions
 * \param  Code machine code
 * ------------------------------------------------------------------------ */


static void DecodeDCX_INX(Word Code)
{
  Byte HReg;

  if (!ChkArgCnt(1, 1));
  else if (check_core(core_mask_7807_7810) && !as_strcasecmp(ArgStr[1].str.p_str, "EA"))
  {
    CodeLen = 1;
    BAsmCode[0] = 0xa8 + Code;
  }
  else if (Decode_rp(ArgStr[1].str.p_str, &HReg))
  {
    CodeLen = 1;
    BAsmCode[0] = 0x02 + Code + (HReg << 4);
  }
  else
    WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
}

/*!------------------------------------------------------------------------
 * \fn     decode_inc_dec(word code)
 * \brief  Handle Z80-style INC/DEC instructions
 * \param  code machine code
 * ------------------------------------------------------------------------ */


static void decode_inc_dec(Word code)
{
  if (ChkArgCnt(1, 1)
   && ChkZ80Syntax(eSyntaxZ80))
  {
    z80_adr_vals_t adr_vals;

    switch (decode_z80_adr(&ArgStr[1], &adr_vals, MModReg8 | MModReg16 | MModWA))
    {
      case e_mod_reg8:
        if ((adr_vals.val < REG_A) || (adr_vals.val > REG_C)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
        else
          BAsmCode[CodeLen++] = 0x40 | (code << 4) | adr_vals.val;
        break;
      case e_mod_reg16:
        BAsmCode[CodeLen++] = (adr_vals.val == REG_EA)
                            ? 0xa8 | code
                            : 0x02 | code | (adr_vals.val << 4);
        break;
      case e_mod_wa:
        BAsmCode[CodeLen++] = 0x20 | (code << 4);
        BAsmCode[CodeLen++] = adr_vals.vals[0];
        break;
      default:
        break;
    }
  }
}

static void DecodeEADD_ESUB(Word Code)
{
  Byte HReg;
  decode_reg_res_t res;

  if (!ChkArgCnt(2, 2) || !check_core_and_error(core_mask_7807_7810));
  else if (as_strcasecmp(ArgStr[1].str.p_str, "EA")) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
  else if ((res = decode_r2(&ArgStr[2], &HReg)) == e_decode_reg_unknown) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
  else if (res == e_decode_reg_ok)
  {
    CodeLen = 2;
    BAsmCode[0] = 0x70;
    BAsmCode[1] = Code + HReg;
  }
}

static void DecodeJ_JR_JRE(Word Type)
{
  Boolean OK;
  Integer AdrInt;
  tSymbolFlags Flags;

  if (!ChkArgCnt(1, 1))
    return;

  AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[1], Int16, &OK, &Flags) - (EProgCounter() + 1);
  if (!OK)
    return;

  if (!Type) /* generic J */
    Type = RangeCheck(AdrInt, SInt6) ? 1 : 2;

  switch (Type)
  {
    case 1: /* JR */
      if (!mSymbolQuestionable(Flags) && !RangeCheck(AdrInt, SInt6)) WrStrErrorPos(ErrNum_JmpDistTooBig, &ArgStr[1]);
      else
      {
        CodeLen = 1;
        BAsmCode[0] = 0xc0 + (AdrInt & 0x3f);
      }
      break;
    case 2:
      AdrInt--; /* JRE is 2 bytes long */
      if (!mSymbolQuestionable(Flags) && !RangeCheck(AdrInt, SInt9)) WrStrErrorPos(ErrNum_JmpDistTooBig, &ArgStr[1]);
      else
      {
        CodeLen = 2;
        BAsmCode[0] = 0x4e + (Hi(AdrInt) & 1);
        BAsmCode[1] = Lo(AdrInt);
      }
      break;
  }
}

static void DecodeCALF(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Integer AdrInt;
    tSymbolFlags Flags;

    AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[1], Int16, &OK, &Flags);
    if (OK)
    {
      if (!mFirstPassUnknown(Flags) && ((AdrInt >> 11) != 1)) WrStrErrorPos(ErrNum_NotFromThisAddress, &ArgStr[1]);
      else
      {
        CodeLen = 2;
        BAsmCode[0] = Hi(AdrInt) + 0x70;
        BAsmCode[1] = Lo(AdrInt);
      }
    }
  }
}

static void DecodeCALT(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Integer AdrInt;
    tSymbolFlags Flags;

    AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[1], Int16, &OK, &Flags);
    if (OK)
    {
      Word AdrMask = is_7807_781x ? 0xffc1 : 0xff81;

      if (!mFirstPassUnknown(Flags) && ((AdrInt & AdrMask) != 0x80)) WrStrErrorPos(ErrNum_NotFromThisAddress, &ArgStr[1]);
      else
      {
        CodeLen = 1;
        BAsmCode[0] = 0x80 + ((AdrInt & ~AdrMask) >> 1);
      }
    }
  }
}

static void DecodeBIT(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(2, 2) && check_core_and_error(core_mask_no_low))
  {
    Boolean OK;
    ShortInt HReg;

    HReg = EvalStrIntExpression(&ArgStr[1], UInt3, &OK);
    if (OK)
     if (Decode_wa(&ArgStr[2], BAsmCode + 1, 0xff))
     {
       CodeLen = 2; BAsmCode[0] = 0x58 + HReg;
     }
  }
}

static void DecodeSK_SKN(Word Code)
{
  Byte HReg;

  if (!ChkArgCnt(1, 1) || !check_core_and_error(Hi(Code)));
  else if (Decode_f(ArgStr[1].str.p_str, &HReg))
  {
    CodeLen = 2;
    BAsmCode[0] = 0x48;
    BAsmCode[1] = Lo(Code) + HReg;
  }
  else if (pCurrCPUProps->Core == eCore7807)
    decode_bit_7807_core(1, (Code & 0x10) ? 0x50 :0x5d);
  else
    WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
}

static void DecodeSKIT_SKNIT(Word Code)
{
  ShortInt HReg;

  if (ChkArgCnt(1, 1)
   && check_core_and_error(Hi(Code))
   && Decode_irf(&ArgStr[1], &HReg))
  {
    CodeLen = 2;
    BAsmCode[0] = 0x48;
    BAsmCode[1] = Lo(Code) + HReg;
  }
}

static void DecodeIN_OUT(Word Code)
{
  const tStrComp *p_port_arg;

  switch (ArgCnt)
  {
    case 2:
    {
      const tStrComp *p_acc_arg = &ArgStr[(Code & 1) + 1];

      if (as_strcasecmp(p_acc_arg->str.p_str, "A"))
      {
        WrStrErrorPos(ErrNum_InvAddrMode, p_acc_arg);
        return;
      }
      p_port_arg = &ArgStr[2 - (Code & 1)];
      goto common;
    }
    case 1:
      p_port_arg = &ArgStr[1];
      /* fall-thru */
    common:
    {
      Boolean OK;

      BAsmCode[1] = EvalStrIntExpression(p_port_arg, UInt8, &OK);
      if (OK)
      {
        BAsmCode[0] = Code;
        CodeLen = 2;
      }
      break;
    }
    default:
      (void)ChkArgCnt(1, 2);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBLOCK_7807(Word code)
 * \brief  Handle 7807-style BLOCK instruction
 * \param  code machine code
 * ------------------------------------------------------------------------ */


static void DecodeBLOCK_7807(Word code)
{
  if (ChkArgCnt(1, 1))
  {
    Byte mode;

    if (Decode_rpa(&ArgStr[1], &mode))
    {
      if ((mode != 4) && (mode != 6)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
      else
      {
        BAsmCode[0] = code | ((mode >> 1) & 1);
        CodeLen = 1;
      }
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBit1_7807(Word code)
 * \brief  handle 7807-specific bit operations with one operand
 * \param  code machine code
 * ------------------------------------------------------------------------ */


static void DecodeBit1_7807(Word code)
{
  if (!ChkArgCnt(1, 1));
  else if (!check_core_and_error(core_mask_7807));
  else
    decode_bit_7807_core(1, code);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBit2_7807(Word code)
 * \brief  handle 7807-specific bit operations with two operands,
           and Z80-style logical instructions
 * \param  code machine code
 * ------------------------------------------------------------------------ */


static void DecodeBit2_7807(Word code)
{
  if (!ChkArgCnt(2, 2));
  else if (!as_strcasecmp(ArgStr[1].str.p_str, "CY"))
  {
    if (check_core_and_error(core_mask_7807))
      decode_bit_7807_core(2, Lo(code));
  }
  else if (CurrZ80Syntax & eSyntaxZ80)
  {
    Word Flags = ALUReg_Src;
    if (pCurrCPUProps->Core != eCore7800Low)
      Flags |= ALUReg_Dest | ALUImm_SR;
    else if (Hi(code) != 2)
      Flags |= ALUImm_SR;
    decode_alu_z80((Flags << 8) | Hi(code));
  }
  else
    WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeDEFBIT(Word code)
 * \brief  handle DEFBIT instruction (7807...7809 only)
 * ------------------------------------------------------------------------ */


static void DecodeDEFBIT(Word code)
{
  LongWord BitSpec;

  UNUSED(code);

  /* if in structure definition, add special element to structure */

  if (ActPC == StructSeg)
  {
    Boolean OK;
    Byte BitPos;
    PStructElem pElement;

    if (!ChkArgCnt(2, 2))
      return;
    BitPos = eval_bit_position(&ArgStr[2], &OK);
    if (!OK)
      return;
    pElement = CreateStructElem(&LabPart);
    if (!pElement)
      return;
    pElement->pRefElemName = as_strdup(ArgStr[1].str.p_str);
    pElement->OpSize = eSymbolSize8Bit;
    pElement->BitPos = BitPos;
    pElement->ExpandFnc = expand_bit_7807;
    AddStructElem(pInnermostNamedStruct->StructRec, pElement);
  }
  else
  {
    if (decode_bit_arg(&BitSpec, 1, ArgCnt))
    {
      *ListLine = '=';
      dissect_bit_7807(ListLine + 1, STRINGSIZE - 3, BitSpec);
      PushLocHandle(-1);
      EnterIntSymbol(&LabPart, BitSpec, SegBData, False);
      PopLocHandle();
      /* TODO: MakeUseList? */
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     decode_ld(Word code)
 * \brief  handle LD (Z80 style) instruction
 * ------------------------------------------------------------------------ */


static void decode_ld(Word code)
{
  UNUSED(code);

  if (ChkArgCnt(2, 2)
   && ChkZ80Syntax(eSyntaxZ80))
  {
    z80_adr_vals_t dest_adr_vals, src_adr_vals;

    switch (decode_z80_adr(&ArgStr[1], &dest_adr_vals, MModReg8 | MModReg16 | MModAbs | MModWA | MModIndir | MModSReg8 | MModSReg16))
    {
      case e_mod_reg8:
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals,
                               MModReg8
                             | ((dest_adr_vals.val < 8) ? MModImm : 0)
                             | ((dest_adr_vals.val < 8) ? MModAbs : 0)
                             | ((dest_adr_vals.val == REG_A) ? MModWA : 0)
                             | ((dest_adr_vals.val == REG_A) ? MModIndir : 0)
                             | ((dest_adr_vals.val == REG_A) ? MModSReg8 : 0)))
        {
          case e_mod_imm:
            BAsmCode[CodeLen++] = 0x68 + dest_adr_vals.val;
            BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            break;
          case e_mod_abs:
            BAsmCode[CodeLen++] = 0x70;
            BAsmCode[CodeLen++] = 0x68 + dest_adr_vals.val;
            BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            BAsmCode[CodeLen++] = src_adr_vals.vals[1];
            break;
          case e_mod_wa:
            BAsmCode[CodeLen++] = (pCurrCPUProps->Core >= eCore7800High) ? 0x01 : 0x28;
            BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            break;
          case e_mod_reg8:
            if ((src_adr_vals.val == REG_A) && (dest_adr_vals.val >= 2))
              BAsmCode[CodeLen++] = 0x18 | (dest_adr_vals.val & 7);
            else if ((dest_adr_vals.val == REG_A) && (src_adr_vals.val >= 2))
              BAsmCode[CodeLen++] = 0x08 | (src_adr_vals.val & 7);
            else
              WrError(ErrNum_InvAddrMode);
            break;
          case e_mod_indir:
            if ((pCurrCPUProps->Core <= eCore7800High) && !is_rpa(src_adr_vals.val)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else
            {
              BAsmCode[CodeLen++] = 0x28 | (src_adr_vals.val & 0x07) | ((src_adr_vals.val & 0x08) << 4);
              append_adr_vals(&src_adr_vals);
            }
            break;
          case e_mod_sreg8:
            if (check_sr_flag(src_adr_vals.vals[0], eFlagSR1, &ArgStr[2]))
            {
              BAsmCode[CodeLen++] = 0x4c;
              BAsmCode[CodeLen++] = 0xc0 | src_adr_vals.val;
            }
            break;
          default:
            break;
        }
        break;
      case e_mod_reg16:
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals,
                               MModImm
                             | MModReg16
                             | ((dest_adr_vals.val < REG_EA) ? MModAbs : 0)
                             | ((dest_adr_vals.val == REG_EA) ? MModIndir : 0)
                             | ((dest_adr_vals.val == REG_EA) ? MModSReg16 : 0)))
        {
          case e_mod_imm:
            BAsmCode[CodeLen++] = 0x04 | (dest_adr_vals.val << 4);
            BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            BAsmCode[CodeLen++] = src_adr_vals.vals[1];
            break;
          case e_mod_reg16:
            if ((dest_adr_vals.val == REG_EA) && (src_adr_vals.val >= REG_BC) && (src_adr_vals.val <= REG_HL))
              BAsmCode[CodeLen++] = 0xa4 | src_adr_vals.val;
            else if ((src_adr_vals.val == REG_EA) && (dest_adr_vals.val >= REG_BC) && (dest_adr_vals.val <= REG_HL))
              BAsmCode[CodeLen++] = 0xb4 | dest_adr_vals.val;
            else
              WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            break;
          case e_mod_abs:
            BAsmCode[CodeLen++] = 0x70;
            BAsmCode[CodeLen++] = 0x0f | (dest_adr_vals.val << 4);
            BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            BAsmCode[CodeLen++] = src_adr_vals.vals[1];
            break;
          case e_mod_indir:
            if (!is_rpa3(src_adr_vals.val)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else
            {
              BAsmCode[CodeLen++] = 0x48;
              BAsmCode[CodeLen++] = 0x80 | src_adr_vals.val;
              append_adr_vals(&src_adr_vals);
            }
            break;
          case e_mod_sreg16:
            if (check_sr_flag(src_adr_vals.vals[0], eFlagSR4, &ArgStr[2]))
            {
              BAsmCode[CodeLen++] = 0x48;
              BAsmCode[CodeLen++] = 0xc0 | src_adr_vals.val;
            }
            break;
          default:
            break;
        }
        break;
      case e_mod_abs:
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals, MModReg8 | MModReg16))
        {
          case e_mod_reg8:
            if (src_adr_vals.val > 7) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else
            {
              BAsmCode[CodeLen++] = 0x70;
              BAsmCode[CodeLen++] = 0x78 | src_adr_vals.val;
              append_adr_vals(&dest_adr_vals);
            }
            break;
          case e_mod_reg16:
            if (src_adr_vals.val >= REG_EA) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else
            {
              BAsmCode[CodeLen++] = 0x70;
              BAsmCode[CodeLen++] = 0x0e | (src_adr_vals.val << 4);
              append_adr_vals(&dest_adr_vals);
            }
            break;
          default:
            break;
        }
        break;
      case e_mod_wa:
        z80_def_op_size = eSymbolSize8Bit;
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals, MModReg8 | MModReg16
                               | ((pCurrCPUProps->Core >= eCore7800High) ? MModImm : 0)
                              ))
        {
          case e_mod_reg8:
            if (src_adr_vals.val == REG_A)
            {
              BAsmCode[CodeLen++] = (pCurrCPUProps->Core >= eCore7800High) ? 0x63 : 0x38;
              BAsmCode[CodeLen++] = dest_adr_vals.vals[0];
            }
            else if (src_adr_vals.val <= 7) /* map to absolute addressing */
            {
              BAsmCode[CodeLen++] = 0x70;
              BAsmCode[CodeLen++] = 0x78 | src_adr_vals.val;
              BAsmCode[CodeLen++] = dest_adr_vals.vals[0];
              BAsmCode[CodeLen++] = WorkArea;
            }
            else
              WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            break;
          case e_mod_reg16: /* map to absolute addressing */
            if (src_adr_vals.val >= REG_EA) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else
            {
              BAsmCode[CodeLen++] = 0x70;
              BAsmCode[CodeLen++] = 0x0e | (src_adr_vals.val << 4);
              BAsmCode[CodeLen++] = dest_adr_vals.vals[0];
              BAsmCode[CodeLen++] = WorkArea;
            }
            break;
          case e_mod_imm:
            BAsmCode[CodeLen++] = 0x71;
            BAsmCode[CodeLen++] = dest_adr_vals.vals[0];
            BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            break;
          default:
            break;
        }
        break;
      case e_mod_indir:
        z80_def_op_size = eSymbolSize8Bit;
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals, MModReg8 | MModReg16
                            | ((pCurrCPUProps->Core >= eCore7800High) ? MModImm : 0)))
        {
          case e_mod_reg8:
            if (src_adr_vals.val != REG_A) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else if ((pCurrCPUProps->Core <= eCore7800High) && !is_rpa(dest_adr_vals.val)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
            else
            {
              BAsmCode[CodeLen++] = 0x38 | (dest_adr_vals.val & 0x07) | ((dest_adr_vals.val & 0x08) << 4);
              append_adr_vals(&dest_adr_vals);
            }
            break;
          case e_mod_reg16:
            if (src_adr_vals.val != REG_EA) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else if (!is_rpa3(dest_adr_vals.val)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
            else
            {
              BAsmCode[CodeLen++] = 0x48;
              BAsmCode[CodeLen++] = 0x90 | dest_adr_vals.val;
              append_adr_vals(&dest_adr_vals);
            }
            break;
          case e_mod_imm:
            if (!is_rpa1(dest_adr_vals.val)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
            else
            {
              BAsmCode[CodeLen++] = 0x48 | dest_adr_vals.val;
              BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            }
            break;
          default:
            break;
        }
        break;
      case e_mod_sreg8:
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals, MModReg8 | MModImm))
        {
          case e_mod_reg8:
            if (src_adr_vals.val != REG_A) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else if (check_sr_flag(dest_adr_vals.vals[0], eFlagSR, &ArgStr[1]))
            {
              BAsmCode[CodeLen++] = 0x4d;
              BAsmCode[CodeLen++] = 0xc0 | dest_adr_vals.val;
            }
            break;
          case e_mod_imm:
            if (check_sr_flag(dest_adr_vals.vals[0], eFlagSR2, &ArgStr[1]))
            {
              BAsmCode[CodeLen++] = 0x64;
              BAsmCode[CodeLen++] = (dest_adr_vals.val & 0x07) | ((dest_adr_vals.val & 0x08) << 4);
              BAsmCode[CodeLen++] = src_adr_vals.vals[0];
            }
          default:
            break;
        }
        break;
      case e_mod_sreg16:
        switch (decode_z80_adr(&ArgStr[2], &src_adr_vals, MModReg16))
        {
          case e_mod_reg16:
            if (src_adr_vals.val != REG_EA) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
            else if (check_sr_flag(dest_adr_vals.vals[0], eFlagSR3, &ArgStr[1]))
            {
              BAsmCode[CodeLen++] = 0x48;
              BAsmCode[CodeLen++] = 0xd2 | dest_adr_vals.val;
            }
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
  }
}

/*--------------------------------------------------------------------------------*/
/* Dynamic Code Table Handling */

static void AddFixed(const char *p_name, Word code, unsigned core_mask)
{
  order_array_rsv_end(fixed_orders, order_t);
  fixed_orders[InstrZ].code = code;
  fixed_orders[InstrZ].core_mask = core_mask;
  AddInstTable(InstTable, p_name, InstrZ++, DecodeFixed);
}

static void AddIntFlag(const char *p_name, Byte code)
{
  order_array_rsv_end(int_flags, intflag_t);
  int_flags[InstrZ].p_name = p_name;
  int_flags[InstrZ++].code = code;
}

static void AddALU(Byte NCode, Word Flags, const char *NNameI, const char *NNameReg, const char *NNameEA, const char *NNameZ80)
{
  char Name[20];

  AddInstTable(InstTable, NNameI, ((Flags & ALUImm_SR) << 8) | NCode, DecodeALUImm);
  AddInstTable(InstTable, NNameReg, ((Flags & (ALUReg_Src | ALUReg_Dest | ALUImm_SR | ALUReg_MayZ80)) << 8) | NCode, DecodeALUReg);
  AddInstTable(InstTable, NNameEA, NCode, DecodeALUEA);
  as_snprintf(Name, sizeof(Name), "%sW", NNameReg);
  AddInstTable(InstTable, Name, NCode, DecodeALURegW);
  as_snprintf(Name, sizeof(Name), "%sX", NNameReg);
  AddInstTable(InstTable, Name, NCode, DecodeALURegX);
  if (NCode & 1)
  {
    as_snprintf(Name, sizeof(Name), "%sW", NNameI);
    AddInstTable(InstTable, Name, NCode, DecodeALUImmW);
  }
  if (NNameZ80)
    AddInstTable(InstTable, NNameZ80, NCode, decode_alu_z80);
}

static void add_alu_z80(const char *p_name, Byte code, Word flags)
{
  AddInstTable(InstTable, p_name, (flags << 8) | code, decode_alu_z80);
}

static void AddAbs(const char *NName, Word NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeAbs);
}

static void AddReg2(const char *p_name, Word code, unsigned core_mask)
{
  order_array_rsv_end(reg2_orders, order_t);
  reg2_orders[InstrZ].code = code;
  reg2_orders[InstrZ].core_mask = core_mask;
  AddInstTable(InstTable, p_name, InstrZ++, DecodeReg2);
}

static void add_sreg8(const char *p_name, Byte code, Byte flags, Byte core_mask)
{
  order_array_rsv_end(s_regs8, sreg_t);
  s_regs8[InstrZ].p_name = p_name;
  s_regs8[InstrZ].code = code;
  s_regs8[InstrZ].flags = flags;
  s_regs8[InstrZ].core_mask = core_mask;
  InstrZ++;
}

static void add_sreg16(const char *p_name, Byte code, Byte flags, Byte core_mask)
{
  order_array_rsv_end(s_regs16, sreg_t);
  s_regs16[InstrZ].p_name = p_name;
  s_regs16[InstrZ].code = code;
  s_regs16[InstrZ].flags = flags;
  s_regs16[InstrZ].core_mask = core_mask;
  InstrZ++;
}

static void AddWork(const char *NName, Word NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeWork);
}

static void AddA(const char *NName, Word NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeA);
}

static void AddEA(const char *NName, Word NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeEA);
}

static void InitFields(void)
{
  Boolean IsLow = pCurrCPUProps->Core == eCore7800Low,
          IsHigh = pCurrCPUProps->Core == eCore7800High,
          Is7807 = pCurrCPUProps->Core == eCore7807,
          Is781x = pCurrCPUProps->Core == eCore7810;

  InstTable = CreateInstTable(301);
  SetDynamicInstTable(InstTable);

  AddInstTable(InstTable, "MOV", 0, DecodeMOV);
  AddInstTable(InstTable, "MVI", 0, DecodeMVI);
  AddInstTable(InstTable, "MVIW", 0, DecodeMVIW);
  AddInstTable(InstTable, "MVIX", 0, DecodeMVIX);
  AddInstTable(InstTable, "LDAX", 0x28, DecodeLDAX_STAX);
  AddInstTable(InstTable, "STAX", 0x38, DecodeLDAX_STAX);
  AddInstTable(InstTable, "LDEAX", 0x80, DecodeLDEAX_STEAX);
  AddInstTable(InstTable, "STEAX", 0x90, DecodeLDEAX_STEAX);
  AddInstTable(InstTable, "LXI", 0, DecodeLXI);
  AddInstTable(InstTable, "PUSH", is_7807_781x ? 0xb0 : 0x480e, DecodePUSH_POP);
  AddInstTable(InstTable, "POP", is_7807_781x ? 0xa0 : 0x480f, DecodePUSH_POP);
  AddInstTable(InstTable, "DMOV", 0, DecodeDMOV);
  AddInstTable(InstTable, "DCX", 1, DecodeDCX_INX);
  AddInstTable(InstTable, "INX", 0, DecodeDCX_INX);
  AddInstTable(InstTable, "EADD", 0x40, DecodeEADD_ESUB);
  AddInstTable(InstTable, "ESUB", 0x60, DecodeEADD_ESUB);
  AddInstTable(InstTable, "JR", 1, DecodeJ_JR_JRE);
  AddInstTable(InstTable, "JRE", 2, DecodeJ_JR_JRE);
  AddInstTable(InstTable, "J", 0, DecodeJ_JR_JRE);
  AddInstTable(InstTable, "CALF", 0, DecodeCALF);
  AddInstTable(InstTable, "CALT", 0, DecodeCALT);
  AddInstTable(InstTable, "BIT", 0, DecodeBIT);
  AddInstTable(InstTable, "SK" , (core_mask_no_low << 8) | 0x08, DecodeSK_SKN);
  AddInstTable(InstTable, "SKN", (core_mask_all    << 8) | 0x18, DecodeSK_SKN);
  AddInstTable(InstTable, "SKIT" , (core_mask_no_low << 8) | (is_7807_781x ? 0x40 : 0x00), DecodeSKIT_SKNIT);
  AddInstTable(InstTable, "SKNIT", (core_mask_all    << 8) | (is_7807_781x ? 0x60 : 0x10), DecodeSKIT_SKNIT);

  InstrZ = 0;
  AddFixed("EX"   , 0x0010                   , (1 << eCore7800High));
  AddFixed("PEN"  , 0x482c                   , (1 << eCore7800High));
  AddFixed("RCL"  , 0x4832                   , (1 << eCore7800High));
  AddFixed("RCR"  , 0x4833                   , (1 << eCore7800High));
  AddFixed("SHAL" , 0x4834                   , (1 << eCore7800High));
  AddFixed("SHAR" , 0x4835                   , (1 << eCore7800High));
  AddFixed("SHCL" , 0x4836                   , (1 << eCore7800High));
  AddFixed("SHCR" , 0x4837                   , (1 << eCore7800High));
  AddFixed("EXX"  , Is7807 ? 0x48af : 0x0011 , core_mask_no_low);
  AddFixed("EXA"  , Is7807 ? 0x48ac : 0x0010 , core_mask_all);
  AddFixed("EXH"  , Is7807 ? 0x48ae : 0x0050 , core_mask_all);
  AddFixed("EXR"  , 0x48ad                   , core_mask_7807);
  if (Is7807)
    AddInstTable(InstTable, "BLOCK", 0x0010, DecodeBLOCK_7807);
  else
    AddFixed("BLOCK", 0x0031                   , core_mask_no_low);
  AddFixed("TABLE", is_7807_781x ? 0x48a8 : 0x0021 , core_mask_no_low);
  AddFixed("DAA"  , 0x0061                   , core_mask_all);
  AddFixed("STC"  , 0x482b                   , core_mask_all);
  AddFixed("CLC"  , 0x482a                   , core_mask_all);
  AddFixed("CMC"  , 0x48aa                   , core_mask_7807);
  AddFixed("NEGA" , 0x483a                   , core_mask_all);
  AddFixed("RLD"  , 0x4838                   , core_mask_all);
  AddFixed("RRD"  , 0x4839                   , core_mask_all);
  AddFixed("JB"   , is_7807_781x ? 0x0021 : 0x0073 , core_mask_all);
  AddFixed("JEA"  , 0x4828                   , core_mask_all);
  AddFixed("CALB" , is_7807_781x ? 0x4829 : 0x0063 , core_mask_no_low);
  AddFixed("SOFTI", 0x0072                   , core_mask_no_low);
  AddFixed("RET"  , is_7807_781x ? 0x00b8 : 0x0008 , core_mask_all);
  AddFixed("RETS" , is_7807_781x ? 0x00b9 : 0x0018 , core_mask_all);
  AddFixed("RETI" , 0x0062                   , core_mask_all);
  AddFixed("NOP"  , 0x0000                   , core_mask_all);
  AddFixed("EI"   , is_7807_781x ? 0x00aa : 0x4820 , core_mask_all);
  AddFixed("DI"   , is_7807_781x ? 0x00ba : 0x4824 , core_mask_all);
  AddFixed("HLT"  , is_7807_781x ? 0x483b : 0x0001 , core_mask_no_low);
  AddFixed("SIO"  , 0x0009                   , core_mask_7800);
  AddFixed("STM"  , 0x0019                   , core_mask_7800);
  AddFixed("PEX"  , 0x482d                   , core_mask_7800);
  AddFixed("RAL"  , is_7807_781x ? 0x4835 : 0x4830 , core_mask_all);
  AddFixed("RAR"  , 0x4831                   , core_mask_all);
  AddFixed("PER"  , 0x483c                   , core_mask_7800);
  if (pCurrCPUProps->Flags & eFlagCMOS)
    AddFixed("STOP" , 0x48bb, core_mask_all);

  if (is_7807_781x)
  {
    if (Is781x)
    {
    }
    else
    {
    }
    /* 0x28 eFlagSR ? */
  }
  else
  {
  }

  InstrZ = 0;
  if (is_7807_781x)
  {
    AddIntFlag("NMI" , 0);
    AddIntFlag("FT0" , 1);
    AddIntFlag("FT1" , 2);
    AddIntFlag("F1"  , 3);
    AddIntFlag("F2"  , 4);
    AddIntFlag("FE0" , 5);
    AddIntFlag("FE1" , 6);
    AddIntFlag("FEIN", 7);
    AddIntFlag("FSR" , 9);
    AddIntFlag("FST" ,10);
    AddIntFlag("ER"  ,11);
    AddIntFlag("OV"  ,12);
    AddIntFlag("SB"  ,20);
    if (Is781x)
    {
      AddIntFlag("FAD" , 8);
      AddIntFlag("AN4" ,16);
      AddIntFlag("AN5" ,17);
      AddIntFlag("AN6" ,18);
      AddIntFlag("AN7" ,19);
    }
    else
    {
      /* value yet unknown */
      /* AddIntFlag("IFE2", ...); */
    }
  }
  else
  {
    AddIntFlag("F0"  , 0);
    AddIntFlag("FT"  , 1);
    AddIntFlag("F1"  , 2);
    if (IsHigh)
      AddIntFlag("F2"  , 3);
    AddIntFlag("FS"  , 4);
  }
  AddIntFlag(NULL, 0);

  AddALU(10, IsLow ?             ALUReg_Src | ALUReg_MayZ80 : ALUImm_SR | ALUReg_Src | ALUReg_Dest | ALUReg_MayZ80, "ACI"  , "ADC"  , "DADC"  , NULL );
  AddALU( 4, IsLow ?             ALUReg_Src | ALUReg_MayZ80 : ALUImm_SR | ALUReg_Src | ALUReg_Dest | ALUReg_MayZ80, "ADINC", "ADDNC", "DADDNC", NULL );
  AddALU( 8, IsLow ?             ALUReg_Src | ALUReg_MayZ80 : ALUImm_SR | ALUReg_Src | ALUReg_Dest | ALUReg_MayZ80, "ADI"  , "ADD"  , "DADD"  , NULL );
  AddALU( 1, IsLow ? ALUImm_SR | ALUReg_Src                 : ALUImm_SR | ALUReg_Src | ALUReg_Dest                , "ANI"  , "ANA"  , "DAN"   , NULL );
  AddALU(15, IsLow ?             ALUReg_Src                 : ALUImm_SR | ALUReg_Src | ALUReg_Dest                , "EQI"  , "EQA"  , "DEQ"   , NULL );
  AddALU( 5, IsLow ?             ALUReg_Src                 : ALUImm_SR | ALUReg_Src | ALUReg_Dest                , "GTI"  , "GTA"  , "DGT"   , NULL );
  AddALU( 7, IsLow ?             ALUReg_Src                 : ALUImm_SR | ALUReg_Src | ALUReg_Dest                , "LTI"  , "LTA"  , "DLT"   , NULL );
  AddALU(13, IsLow ?             ALUReg_Src                 : ALUImm_SR | ALUReg_Src | ALUReg_Dest                , "NEI"  , "NEA"  , "DNE"   , NULL );
  AddALU(11, IsLow ? ALUImm_SR                              : ALUImm_SR | ALUReg_Src | ALUReg_Dest                , "OFFI" , "OFFA" , "DOFF"  , NULL );
  AddALU( 9, IsLow ? ALUImm_SR                              : ALUImm_SR | ALUReg_Src | ALUReg_Dest                , "ONI"  , "ONA"  , "DON"   , NULL );
  AddALU( 3, IsLow ? ALUImm_SR | ALUReg_Src                 : ALUImm_SR | ALUReg_Src | ALUReg_Dest                , "ORI"  , "ORA"  , "DOR"   , NULL );
  AddALU(14, IsLow ?             ALUReg_Src | ALUReg_MayZ80 : ALUImm_SR | ALUReg_Src | ALUReg_Dest | ALUReg_MayZ80, "SBI"  , "SBB"  , "DSBB"  , NULL );
  AddALU( 6, IsLow ?             ALUReg_Src | ALUReg_MayZ80 : ALUImm_SR | ALUReg_Src | ALUReg_Dest | ALUReg_MayZ80, "SUINB", "SUBNB", "DSUBNB", NULL );
  AddALU(12, IsLow ?             ALUReg_Src | ALUReg_MayZ80 : ALUImm_SR | ALUReg_Src | ALUReg_Dest | ALUReg_MayZ80, "SUI"  , "SUB"  , "DSUB"  , NULL );
  AddALU( 2, IsLow ?             ALUReg_Src                 : ALUImm_SR | ALUReg_Src | ALUReg_Dest                , "XRI"  , "XRA"  , "DXR"   , NULL );

  AddAbs("CALL", is_7807_781x ? 0x0040 : 0x0044);
  AddAbs("JMP" , 0x0054);
  AddAbs("LBCD", 0x701f);
  AddAbs("LDED", 0x702f);
  AddAbs("LHLD", 0x703f);
  AddAbs("LSPD", 0x700f);
  AddAbs("SBCD", 0x701e);
  AddAbs("SDED", 0x702e);
  AddAbs("SHLD", 0x703e);
  AddAbs("SSPD", 0x700e);

  InstrZ = 0;
  AddReg2("DCR" , 0x0050, core_mask_all);
  AddReg2("DIV" , 0x483c, core_mask_7807_7810);
  AddReg2("INR" , 0x0040, core_mask_all);
  AddReg2("MUL" , 0x482c, core_mask_7807_7810);
  if (is_7807_781x)
  {
    AddReg2("RLL" , 0x4834, core_mask_7807_7810);
    AddReg2("RLR" , 0x4830, core_mask_7807_7810);
  }
  else
  {
    AddA("RLL", 0x4830);
    AddA("RLR", 0x4831);
  }
  AddReg2("SLL" , 0x4824, core_mask_7807_7810);
  AddReg2("SLR" , 0x4820, core_mask_7807_7810);
  AddReg2("SLLC", 0x4804, core_mask_7807_7810);
  AddReg2("SLRC", 0x4800, core_mask_7807_7810);

  AddWork("DCRW", 0x30);
  AddWork("INRW", 0x20);
  AddWork("LDAW", is_7807_781x ? 0x01 : 0x28);
  AddWork("STAW", is_7807_781x ? 0x63 : 0x38);

  AddEA("DRLL", 0x48b4); AddEA("DRLR", 0x48b0);
  AddEA("DSLL", 0x48a4); AddEA("DSLR", 0x48a0);

  if (!is_7807_781x)
  {
    AddInstTable(InstTable, "IN" , 0x4c, DecodeIN_OUT);
    AddInstTable(InstTable, "OUT", 0x4d, DecodeIN_OUT);
  }

  AddInstTable(InstTable, "AND" , 0x0131, DecodeBit2_7807);
  AddInstTable(InstTable, "OR"  , 0x035c, DecodeBit2_7807);
  AddInstTable(InstTable, "XOR" , 0x025e, DecodeBit2_7807);
  AddInstTable(InstTable, "SETB", 0x58, DecodeBit1_7807);
  AddInstTable(InstTable, "CLR" , 0x5b, DecodeBit1_7807);
  AddInstTable(InstTable, "NOT" , 0x59, DecodeBit1_7807);

  /* Array with special regs is created upon first usage and remains
     allocated, because we need it in dissect_bit_7807(), which may
     be called after we switched away fro mtarget (e.g. when printing
     symbol table: */


  if (!s_regs8)
  {
    InstrZ = 0;
    add_sreg8("PA"  , 0x00, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_all);
    add_sreg8("PB"  , 0x01, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_all);
    add_sreg8("PC"  , 0x02, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7807_7810);
    add_sreg8("PD"  , 0x03, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7807_7810);
    add_sreg8("PF"  , 0x05, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7807_7810);
    add_sreg8("MKH" , 0x06, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7807_7810);
    add_sreg8("MKL" , 0x07, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7807_7810);
    add_sreg8("SMH" , 0x09, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7807_7810);
    add_sreg8("SML" , 0x0a, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("EOM" , 0x0b, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7807_7810);
    add_sreg8("ETMM", 0x0c, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("TMM" , 0x0d, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7807_7810);
    add_sreg8("MM"  , 0x10, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("MCC" , 0x11, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("MA"  , 0x12, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("MB"  , 0x13, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("MC"  , 0x14, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("MF"  , 0x17, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("TXB" , 0x18, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("RXB" , 0x19, eFlagSR1                     , core_mask_7807_7810);
    add_sreg8("TM0" , 0x1a, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("TM1" , 0x1b, eFlagSR                      , core_mask_7807_7810);
    add_sreg8("ZCM" , 0x28, eFlagSR                      , core_mask_7807_7810 | core_mask_cmos);
    add_sreg8("ANM" , 0x08, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7810     );
    add_sreg8("CR0" , 0x20, eFlagSR1                     , core_mask_7810     );
    add_sreg8("CR1" , 0x21, eFlagSR1                     , core_mask_7810     );
    add_sreg8("CR2" , 0x22, eFlagSR1                     , core_mask_7810     );
    add_sreg8("CR3" , 0x23, eFlagSR1                     , core_mask_7810     );
    add_sreg8("PT"  , 0x0e, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7807     );
    add_sreg8("WDM" , 0x20, eFlagSR | eFlagSR1           , core_mask_7807     );
    add_sreg8("MT"  , 0x21, eFlagSR | eFlagSR1           , core_mask_7807     );
    add_sreg8("MK"  , 0x03, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7800     );
    add_sreg8("MB"  , 0x04, eFlagSR                      , core_mask_7800     );
    add_sreg8("PC"  , 0x02, eFlagSR | eFlagSR1 | eFlagSR2, core_mask_7800_high);
    add_sreg8("PC"  , 0x02, eFlagSR1 | eFlagSR2          , core_mask_7800_low );
    add_sreg8("MC"  , 0x05, eFlagSR                      , core_mask_7800_high);
    add_sreg8("MC"  , 0x05, 0                            , core_mask_7800_low );
    add_sreg8("TM0" , 0x06, eFlagSR                      , core_mask_7800_high);
    add_sreg8("TM0" , 0x06, 0                            , core_mask_7800_low );
    add_sreg8("TM1" , 0x07, eFlagSR                      , core_mask_7800_high);
    add_sreg8("TM1" , 0x07, 0                            , core_mask_7800_low );
    add_sreg8("TM"  , 0x06, eFlagSR                      , core_mask_7800_low );
    add_sreg8("TM"  , 0x06, 0                            , core_mask_7800_high);
    add_sreg8("SM"  , 0x0a, eFlagSR | eFlagSR1           , core_mask_7800_low );
    add_sreg8("SM"  , 0x0a, 0                            , core_mask_7800_high);
    add_sreg8("SC"  , 0x0b, eFlagSR | eFlagSR1           , core_mask_7800_low );
    add_sreg8("SC"  , 0x0b, 0                            , core_mask_7800_high);
    add_sreg8("S"   , 0x08, eFlagSR | eFlagSR1           , core_mask_7800     );
    add_sreg8("TMM" , 0x09, eFlagSR | eFlagSR1           , core_mask_7800     );
    add_sreg8(NULL  , 0x00, 0                            , 0                  );
  }

  InstrZ = 0;
  add_sreg16("ETM0" , 0x00, eFlagSR3, core_mask_all );
  add_sreg16("ETM1" , 0x01, eFlagSR3, core_mask_all );
  add_sreg16("ECNT" , 0x00, eFlagSR4, core_mask_all );
  add_sreg16("ECPT" , 0x01, eFlagSR4, core_mask_7810);
  add_sreg16("ECPT0", 0x01, eFlagSR4, core_mask_7807);
  add_sreg16("ECPT1", 0x02, eFlagSR4, core_mask_7807);
  add_sreg16(NULL   , 0x00, 0       , 0             );

  if (Is7807)
    AddInstTable(InstTable, "DEFBIT", 0, DecodeDEFBIT);

  AddZ80Syntax(InstTable);
  AddInstTable(InstTable, "LD", 0, decode_ld);
  AddInstTable(InstTable, "INC", 0, decode_inc_dec);
  AddInstTable(InstTable, "DEC", 1, decode_inc_dec);
  add_alu_z80("SKGT", 5, IsLow ? ALUReg_Src : ALUImm_SR | ALUReg_Src | ALUReg_Dest);
  add_alu_z80("SKLT", 7, IsLow ? ALUReg_Src : ALUImm_SR | ALUReg_Src | ALUReg_Dest);
  add_alu_z80("SKNE",13, IsLow ? ALUReg_Src : ALUImm_SR | ALUReg_Src | ALUReg_Dest);
  add_alu_z80("SKEQ",15, IsLow ? ALUReg_Src : ALUImm_SR | ALUReg_Src | ALUReg_Dest);
  add_alu_z80("SKON", 9, IsLow ? ALUImm_SR  : ALUImm_SR | ALUReg_Src | ALUReg_Dest);
  add_alu_z80("SKOFF",11, IsLow ? ALUImm_SR  : ALUImm_SR | ALUReg_Src | ALUReg_Dest);
}

static void DeinitFields(void)
{
  DestroyInstTable(InstTable);
  order_array_free(int_flags);
  order_array_free(fixed_orders);
  order_array_free(reg2_orders);
  /* See above why s_regs8 does not get freed upon deinit: */
  /*order_array_free(s_regs8);*/
  order_array_free(s_regs16);
}

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

/*!------------------------------------------------------------------------
 * \fn     MakeCode_78C10(void)
 * \brief  generate code from source code line
 * ------------------------------------------------------------------------ */


static void MakeCode_78C10(void)
{
  CodeLen = 0;
  DontPrint = False;
  z80_op_size =
  z80_def_op_size = eSymbolSizeUnknown;

  /* zu ignorierendes */

  if (Memo("")) return;

  /* Pseudoanweisungen */

  if (DecodeIntelPseudo(False)) return;

  if (!LookupInstTable(InstTable, OpPart.str.p_str))
    WrStrErrorPos(ErrNum_UnknownInstruction, &OpPart);
}

/*!------------------------------------------------------------------------
 * \fn     InitCode_78C10(void)
 * \brief  target-specific initializations at begin of pass
 * ------------------------------------------------------------------------ */


static void InitCode_78C10(void)
{
  WorkArea = 0x100;
}

/*!------------------------------------------------------------------------
 * \fn     IsDef_78C10(void)
 * \brief  check whether instruction consumes label field itself
 * \return True if yes
 * ------------------------------------------------------------------------ */


static Boolean IsDef_78C10(void)
{
  return (pCurrCPUProps->Core == eCore7807) && Memo("DEFBIT");
}

/*!------------------------------------------------------------------------
 * \fn     SwitchFrom_78C10(void)
 * \brief  cleanups when switching away from target
 * ------------------------------------------------------------------------ */


static void SwitchFrom_78C10(void)
{
  DeinitFields();
}

/*!------------------------------------------------------------------------
 * \fn     SwitchTo_78C10(void *pUser)
 * \brief  initializations when switching to target
 * \param  pUser * to target details
 * ------------------------------------------------------------------------ */


static void SwitchTo_78C10(void *pUser)
{
  const TFamilyDescr *pDescr = FindFamilyByName("78(C)xx");

  pCurrCPUProps = (const tCPUProps*)pUser;
  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);

  PCSymbol = "$"; HeaderID = pDescr->Id; NOPCode = 0x00;
  DivideChars = ","; HasAttrs = False;

  ValidSegs = 1 << SegCode;
  Grans[SegCode] = 1; ListGrans[SegCode] = 1; SegInits[SegCode] = 0;
  SegLimits[SegCode] = 0xffff;

  if (pCurrCPUProps->Flags & eFlagHasV)
  {
    static ASSUMERec ASSUME78C10s[] =
    {
      {"V" , &WorkArea, 0, 0xff, 0x100, NULL}
    };

    pASSUMERecs = ASSUME78C10s;
    ASSUMERecCnt = sizeof(ASSUME78C10s) / sizeof(*ASSUME78C10s);
  }
  else
    WorkArea = 0xff;
  is_7807_781x = (pCurrCPUProps->Core == eCore7807) || (pCurrCPUProps->Core == eCore7810);

  MakeCode = MakeCode_78C10; IsDef = IsDef_78C10;
  SwitchFrom = SwitchFrom_78C10;
  if (pCurrCPUProps->Core == eCore7807)
    DissectBit = dissect_bit_7807;
  InitFields();
}

/*!------------------------------------------------------------------------
 * \fn     code78c10_init(void)
 * \brief  Attach 78Cxx target
 * ------------------------------------------------------------------------ */


static const tCPUProps CPUProps[] =
{
  { "7800" , eCore7800High, eFlagHasV             }, /* ROMless , 128B RAM */
  { "7801" , eCore7800High, eFlagHasV             }, /* 4KB ROM , 128B RAM */
  { "7802" , eCore7800High, eFlagHasV             }, /* 6KB ROM , 128B RAM */
  { "78C05", eCore7800Low,  0                     }, /* ROMless , 128B RAM */
  { "78C06", eCore7800Low,  0                     }, /* 4KB ROM , 128B RAM */
  { "7807" , eCore7807,     eFlagHasV             }, /* ROMless , 256B RAM */
  { "7808" , eCore7807,     eFlagHasV             }, /* 4KB ROM , 256B RAM */
  { "7809" , eCore7807,     eFlagHasV             }, /* 8KB ROM , 256B RAM */
  { "7810" , eCore7810,     eFlagHasV             }, /* ROMless , 256B RAM */
  { "78C10", eCore7810,     eFlagHasV | eFlagCMOS }, /* ROMless , 256B RAM */
  { "78C11", eCore7810,     eFlagHasV | eFlagCMOS }, /* 4KB ROM , 256B RAM */
  { "78C12", eCore7810,     eFlagHasV | eFlagCMOS }, /* 8KB ROM , 256B RAM */
  { "78C14", eCore7810,     eFlagHasV | eFlagCMOS }, /* 16KB ROM, 256B RAM */
  { "78C17", eCore7810,     eFlagHasV | eFlagCMOS }, /* ROMless ,  1KB RAM */
  { "78C18", eCore7810,     eFlagHasV | eFlagCMOS }, /* 32KB ROM,  1KB RAM */
  { ""     , eCoreNone,     0                     },
};

void code78c10_init(void)
{
  const tCPUProps *pProp;

  for (pProp = CPUProps; pProp->Name[0]; pProp++)
    (void)AddCPUUserWithArgs(pProp->Name, SwitchTo_78C10, (void*)pProp, NULL, NULL);

  AddInitPassProc(InitCode_78C10);
}