/* codenv60.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Code Generator NEC V60                                                    */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"
#include <string.h>
#include "bpemu.h"
#include "strutil.h"

#include "headids.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmallg.h"
#include "asmitree.h"
#include "codevars.h"
#include "codepseudo.h"
#include "motpseudo.h"
#include "onoff_common.h"

#include "codev60.h"

#define REG_SP 31
#define REG_FP 30
#define REG_AP 29
#define REG_PC 64

/* NOTE: only 3 code flags are available, since machine sub code
   is 5 bits long: */

#define CODE_FLAG_SUPMODE (1 << 15)
#define CODE_FLAG_OP2_IMM (1 << 14)
#define CODE_FLAG_LIM32 (1 << 13)

typedef enum
{
  ModReg = 0,
  ModImm = 1,
  ModMem = 2,
  ModIO  = 3
} tAdrMode;

#define MModReg (1 << ModReg)
#define MModImm (1 << ModImm)
#define MModMem (1 << ModMem)
#define MModIO  (1 << ModIO)
#define MModImm7Bit (1 << 7)
#define MModImmCondition (1 << 6)

/* Count = 0 implies error/invalid, since at least mod byte must be present */

typedef struct
{
  unsigned count;
  Byte m, vals[10];
  tSymbolSize forced_disp_size, data_op_size;
  tSymbolFlags immediate_flags;
  Boolean bit_offset_immediate;
  LongInt bit_offset;
} tAdrVals;

typedef struct
{
  Byte val;
  tSymbolFlags immediate_flags;
} tLenVals;

typedef struct
{
  char name[3];
  Byte code;
} tCondition;

static tSymbolSize OpSize;
static tCondition *Conditions;

/*-------------------------------------------------------------------------*/
/* Register Symbols */

/*!------------------------------------------------------------------------
 * \fn     DecodeRegCore(const char *pArg, Byte *pResult)
 * \brief  check whether argument is a CPU register
 * \param  pArg argument to check
 * \param  pResult numeric register value if yes
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean DecodeRegCore(const char *pArg, Byte *pResult)
{
  size_t l;

  if (!as_strcasecmp(pArg, "SP"))
  {
    *pResult = REG_SP | REGSYM_FLAG_ALIAS;
    return True;
  }
  else if (!as_strcasecmp(pArg, "FP"))
  {
    *pResult = REG_FP | REGSYM_FLAG_ALIAS;
    return True;
  }
  else if (!as_strcasecmp(pArg, "AP"))
  {
    *pResult = REG_AP | REGSYM_FLAG_ALIAS;
    return True;
  }

  l = strlen(pArg);
  if ((l < 2) || (l > 3) || (as_toupper(*pArg) != 'R'))
    return False;
  *pResult = 0;
  for (pArg++; *pArg; pArg++)
  {
    if (!isdigit(*pArg))
      return False;
    *pResult = (*pResult * 10) + (*pArg - '0');
  }
  return (*pResult < 32);
}

/*!------------------------------------------------------------------------
 * \fn     DissectReg_V60(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
 * \brief  dissect register symbols - V60 variant
 * \param  pDest destination buffer
 * \param  DestSize destination buffer size
 * \param  Value numeric register value
 * \param  InpSize register size
 * ------------------------------------------------------------------------ */

static void DissectReg_V60(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
{
  if (InpSize == eSymbolSize32Bit)
  {
    switch (Value)
    {
      case REGSYM_FLAG_ALIAS | REG_SP:
        as_snprintf(pDest, DestSize, "SP");
        break;
      case REGSYM_FLAG_ALIAS | REG_FP:
        as_snprintf(pDest, DestSize, "FP");
        break;
      case REGSYM_FLAG_ALIAS | REG_AP:
        as_snprintf(pDest, DestSize, "AP");
        break;
      default:
        as_snprintf(pDest, DestSize, "R%u", (unsigned)(Value & 31));
    }
  }
  else
    as_snprintf(pDest, DestSize, "%d-%u", (int)InpSize, (unsigned)Value);
}

/*--------------------------------------------------------------------------*/
/* Address Expression Parser */

/*!------------------------------------------------------------------------
 * \fn     ResetAdrVals(tAdrVals *p_vals)
 * \brief  reset to invalid/empty state
 * \param  p_vals arg to clear
 * ------------------------------------------------------------------------ */

static void ResetAdrVals(tAdrVals *p_vals)
{
  p_vals->count = 0;
  p_vals->m = 0;
  p_vals->forced_disp_size =
  p_vals->data_op_size = eSymbolSizeUnknown;
  p_vals->immediate_flags = eSymbolFlag_None;
  p_vals->bit_offset_immediate = False;
  p_vals->bit_offset = 0;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeReg(const tStrComp *pArg, Byte *pResult, Boolean MustBeReg)
 * \brief  check whether argument is a CPU register or register alias
 * \param  pArg argument to check
 * \param  pResult numeric register value if yes
 * \param  MustBeReg argument is expected to be a register
 * \return RegEvalResult
 * ------------------------------------------------------------------------ */

static tRegEvalResult DecodeReg(const tStrComp *pArg, Byte *pResult, Boolean MustBeReg)
{
  tRegDescr RegDescr;
  tEvalResult EvalResult;
  tRegEvalResult RegEvalResult;

  if (DecodeRegCore(pArg->str.p_str, pResult))
  {
    *pResult &= ~REGSYM_FLAG_ALIAS;
    return eIsReg;
  }

  RegEvalResult = EvalStrRegExpressionAsOperand(pArg, &RegDescr, &EvalResult, eSymbolSize32Bit, MustBeReg);
  *pResult = RegDescr.Reg & ~REGSYM_FLAG_ALIAS;
  return RegEvalResult;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeCondition(const char *p_arg, LongWord *p_result)
 * \brief  check whether argument is a condition identifier
 * \param  p_arg source argument
 * \param  p_result result buffer if yes
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean DecodeCondition(const char *p_arg, LongWord *p_result)
{
  int z;

  for (z = 0; Conditions[z].name[0]; z++)
    if (!as_strcasecmp(p_arg, Conditions[z].name))
    {
      *p_result = Conditions[z].code;
      return True;
    }
  return False;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeAdr(const tStrComp *pArg, tAdrVals *pResult, unsigned ModeMask)
 * \brief  decode address expression
 * \param  pArg source argument
 * \param  pResult result buffer
 * \param  MayImm allow immediate?
 * \return True if successfully decoded
 * ------------------------------------------------------------------------ */

static Boolean check_mode_mask(const tStrComp *p_arg, tAdrMode address_mode, unsigned mode_mask)
{
  if ((mode_mask >> address_mode) & 1)
    return True;
  {
    WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
    return False;
  }
}

static int BaseQualifier(const char *pArg, int LastNonBlankPos, int SplitPos)
{
  return (pArg[LastNonBlankPos] == ']') ? SplitPos : -1;
}

static void CutSize(tStrComp *pArg, tSymbolSize *pSize)
{
  static const char Suffixes[6][3] = { "B", "8", "H", "16", "W", "32" };
  static const tSymbolSize Sizes[3] = { eSymbolSize8Bit, eSymbolSize16Bit, eSymbolSize32Bit };
  size_t ArgLen = strlen(pArg->str.p_str), ThisLen;
  unsigned z;

  for (z = 0; z < 6; z++)
  {
    ThisLen = strlen(Suffixes[z]);
    if ((ArgLen > ThisLen + 1)
     && (pArg->str.p_str[ArgLen - ThisLen - 1] == '.')
     && !as_strcasecmp(pArg->str.p_str + ArgLen - ThisLen, Suffixes[z]))
    {
      *pSize = Sizes[z / 2];
      StrCompShorten(pArg, ThisLen + 1);
      break;
    }
  }
}

static void AppendToVals(tAdrVals *p_vals, LongWord Value, tSymbolSize Size)
{
  p_vals->vals[p_vals->count++] = Value & 0xff;
  if (Size >= eSymbolSize16Bit)
  {
    p_vals->vals[p_vals->count++] = (Value >> 8) & 0xff;
    if (Size >= eSymbolSize32Bit)
    {
      p_vals->vals[p_vals->count++] = (Value >> 16) & 0xff;
      p_vals->vals[p_vals->count++] = (Value >> 24) & 0xff;
    }
  }
}

static Boolean IsPredecrement(const tStrComp *pArg, Byte *pResult, tRegEvalResult *pRegEvalResult)
{
  tStrComp RegComp;

  if (*pArg->str.p_str != '-')
    return False;
  StrCompRefRight(&RegComp, pArg, 1);
  KillPrefBlanksStrComp(&RegComp);
  *pRegEvalResult = DecodeReg(&RegComp, pResult, False);
  return (*pRegEvalResult != eIsNoReg);
}

static Boolean IsPostincrement(const tStrComp *pArg, Byte *pResult, tRegEvalResult *pRegEvalResult)
{
  String Reg;
  tStrComp RegComp;
  size_t ArgLen = strlen(pArg->str.p_str);

  if (!ArgLen || (pArg->str.p_str[ArgLen - 1] != '+'))
    return False;
  StrCompMkTemp(&RegComp, Reg, sizeof(Reg));
  StrCompCopySub(&RegComp, pArg, 0, ArgLen - 1);
  KillPostBlanksStrComp(&RegComp);
  *pRegEvalResult = DecodeReg(&RegComp, pResult, False);
  return (*pRegEvalResult != eIsNoReg);
}

static Boolean DecodeRegOrPC(const tStrComp *pArg, Byte *pResult)
{
  if (!as_strcasecmp(pArg->str.p_str, "PC"))
  {
    *pResult = REG_PC;
    return True;
  }
  else
    return (DecodeReg(pArg, pResult, True) == eIsReg);
}

static LongInt EvalDispOpt(const tStrComp *pArg, tEvalResult *pEvalResult, Boolean PCRelative)
{
  if (*pArg->str.p_str)
    return EvalStrIntExpressionWithResult(pArg, Int32, pEvalResult) - (PCRelative ? EProgCounter() : 0);
  else
  {
    memset(pEvalResult, 0, sizeof(*pEvalResult));
    pEvalResult->OK = True;
    return 0;
  }
}

static Boolean ProcessAbsolute(const tStrComp *pArg, tAdrVals *pResult, Byte IndexReg, Byte ModByte, unsigned mode_mask)
{
  tEvalResult eval_result;
  LongWord Address;

  Address = EvalStrIntExpressionOffsWithResult(pArg, 1, UInt32, &eval_result);
  pResult->count = 0;
  if (!eval_result.OK)
    return False;
  if (mode_mask & MModIO)
    ChkSpace(SegIO, eval_result.AddrSpaceMask);
  if (!check_mode_mask(pArg, ModMem, mode_mask))
    return False;

  if (IndexReg != REGSYM_FLAG_ALIAS)
  {
    pResult->vals[pResult->count++] = 0xc0 | IndexReg;
    pResult->m = 1;
  }
  else
    pResult->m = 0;
  pResult->vals[pResult->count++] = ModByte;
  AppendToVals(pResult, Address, eSymbolSize32Bit);

  return True;
}

static Boolean DeduceAndCheckDispSize(const tStrComp *pArg, LongInt Value, tSymbolSize *pSize, const tEvalResult *pEvalResult)
{
  if (*pSize == eSymbolSizeUnknown)
  {
    if (RangeCheck(Value, SInt8))
      *pSize = eSymbolSize8Bit;
    else if (RangeCheck(Value, SInt16))
      *pSize = eSymbolSize16Bit;
    else
      *pSize = eSymbolSize32Bit;
  }
  if (mFirstPassUnknownOrQuestionable(pEvalResult->Flags))
    return True;
  switch (*pSize)
  {
    case eSymbolSize8Bit:
      return ChkRangePos(Value, -128, 127, pArg);
    case eSymbolSize16Bit:
      return ChkRangePos(Value, -32768, 32767, pArg);
    case eSymbolSize32Bit:
      return True;
    default:
      WrStrErrorPos(ErrNum_OverRange, pArg);
  }
  return False;
}

static Boolean DecodeAdr(tStrComp *pArg, tAdrVals *pResult, unsigned ModeMask)
{
  int IndexSplitPos, OuterDispSplitPos, ArgLen;
  Byte IndexReg = REGSYM_FLAG_ALIAS, BaseReg = REGSYM_FLAG_ALIAS;
  tEvalResult EvalResult;

  ResetAdrVals(pResult);
  pResult->data_op_size = OpSize;

  /* immediate */

  if (*pArg->str.p_str == '#')
  {
    LongWord Value;

    if (!check_mode_mask(pArg, ModImm, ModeMask))
      return False;
    if ((ModeMask & MModImmCondition) && DecodeCondition(pArg->str.p_str + 1, &Value))
      EvalResult.OK = True;
    else switch (OpSize)
    {
      case eSymbolSize8Bit:
        Value = EvalStrIntExpressionOffsWithResult(pArg, 1, (ModeMask & MModImm7Bit) ? UInt7 : Int8, &EvalResult);
        break;
      case eSymbolSize16Bit:
        Value = EvalStrIntExpressionOffsWithResult(pArg, 1, Int16, &EvalResult);
        break;
      case eSymbolSize32Bit:
        Value = EvalStrIntExpressionOffsWithResult(pArg, 1, Int32, &EvalResult);
        break;
      default:
        WrStrErrorPos(ErrNum_InvOpSize, pArg);
        EvalResult.OK = False;
        break;
    }
    if (!EvalResult.OK)
      return EvalResult.OK;

    pResult->m = 0;
    if (Value <= 15)
    {
      pResult->vals[0] = 0xe0 | (Value & 0x0f);
      pResult->count = 1;
    }
    else
    {
      pResult->vals[0] = 0xf4;
      pResult->count = 1;
      AppendToVals(pResult, Value, OpSize);
    }
    pResult->immediate_flags = EvalResult.Flags;
    return EvalResult.OK;
  }

  /* split off index register '(Rx)': */

  IndexSplitPos = FindDispBaseSplitWithQualifier(pArg->str.p_str, &ArgLen, BaseQualifier, "()");
  if (IndexSplitPos > 0)
  {
    String RegStr;
    tStrComp RegComp;

    StrCompMkTemp(&RegComp, RegStr, sizeof(RegStr));
    StrCompCopySub(&RegComp, pArg, IndexSplitPos + 1, ArgLen - IndexSplitPos - 2);
    KillPostBlanksStrComp(&RegComp);
    KillPrefBlanksStrComp(&RegComp);
    switch (DecodeReg(&RegComp, &IndexReg, False))
    {
      case eRegAbort:
        return False;
      case eIsReg:
        StrCompShorten(pArg, ArgLen - IndexSplitPos);
        break;
      default:
        break;
    }
  }

  /* absolute */

  if (*pArg->str.p_str == '/')
    return ProcessAbsolute(pArg, pResult, IndexReg, 0xf3, ModeMask);

  /* indirect modes remain (must end on ]) */

  KillPostBlanksStrComp(pArg);
  OuterDispSplitPos = FindDispBaseSplitWithQualifier(pArg->str.p_str, &ArgLen, NULL, "[]");
  if (OuterDispSplitPos >= 0)
  {
    tStrComp OuterDisp, InnerComp, InnerDisp;
    LongInt OuterDispValue = 0, InnerDispValue = 0;
    tSymbolSize OuterDispSize = eSymbolSizeUnknown,
                InnerDispSize = eSymbolSizeUnknown;
    tEvalResult OuterEvalResult, InnerEvalResult;
    tRegEvalResult RegEvalResult;
    int InnerDispSplitPos;

    /* split off outer displacement */

    StrCompSplitRef(&OuterDisp, &InnerComp, pArg, &pArg->str.p_str[OuterDispSplitPos]);
    StrCompShorten(&InnerComp, 1);
    KillPostBlanksStrComp(&InnerComp);
    KillPrefBlanksStrCompRef(&InnerComp);
    KillPostBlanksStrComp(&OuterDisp);
#if 0
    DumpStrComp("OuterDisp", &OuterDisp);
    DumpStrComp("InnerComp", &InnerComp);
#endif

    /* nested indirect? */

    InnerDispSplitPos = FindDispBaseSplitWithQualifier(InnerComp.str.p_str, &ArgLen, NULL, "[]");
    if (InnerDispSplitPos >= 0)
    {
      StrCompSplitRef(&InnerDisp, &InnerComp, &InnerComp, &InnerComp.str.p_str[InnerDispSplitPos]);
      StrCompShorten(&InnerComp, 1);
      KillPostBlanksStrComp(&InnerComp);
      KillPrefBlanksStrCompRef(&InnerComp);
      KillPostBlanksStrComp(&InnerDisp);
#if 0
      DumpStrComp("InnerDisp", &InnerDisp);
      DumpStrComp("InnerComp(2)", &InnerComp);
#endif

      /* inner comp must be register */

      if (!DecodeRegOrPC(&InnerComp, &BaseReg))
        return False;

      if (*InnerDisp.str.p_str)
        CutSize(&InnerDisp, &InnerDispSize);
      InnerDispValue = EvalDispOpt(&InnerDisp, &InnerEvalResult, BaseReg == REG_PC);
      if (!InnerEvalResult.OK)
        return False;
      (void)InnerDispValue;
    }

    /* postincr/predecr? */

    else if (IsPredecrement(&InnerComp, &pResult->vals[0], &RegEvalResult))
    {
      if ((eRegAbort == RegEvalResult) || !check_mode_mask(pArg, ModMem, ModeMask))
        return False;
      if (*OuterDisp.str.p_str)
      {
        WrStrErrorPos(ErrNum_InvAddrMode, pArg);
        return False;
      }
      pResult->vals[0] |= 0xa0;
      pResult->count = 1;
      pResult->m = 1;
      return True;
    }

    else if (IsPostincrement(&InnerComp, &pResult->vals[0], &RegEvalResult))
    {
      if ((eRegAbort == RegEvalResult) || !check_mode_mask(pArg, ModMem, ModeMask))
        return False;
      if (*OuterDisp.str.p_str)
      {
        WrStrErrorPos(ErrNum_InvAddrMode, pArg);
        return False;
      }
      pResult->vals[0] |= 0x80;
      pResult->count = 1;
      pResult->m = 1;
      return True;
    }

    /* direct deferred: */

    else if (*InnerComp.str.p_str == '/')
      return ProcessAbsolute(&InnerComp, pResult, IndexReg, 0xfb, ModeMask);

    /* no pre/post, double indir, direct -> must be plain register */

    else
    {
      if (!DecodeRegOrPC(&InnerComp, &BaseReg))
        return False;
    }

    /* for both single & double indirect, outer displacement */

    if (*OuterDisp.str.p_str)
      CutSize(&OuterDisp, &OuterDispSize);
    OuterDispValue = EvalDispOpt(&OuterDisp, &OuterEvalResult, (BaseReg == REG_PC) && (InnerDispSplitPos < 0));
    if (!OuterEvalResult.OK)
      return False;

    /* now, decide about the whole mess: no double []: */

    if (InnerDispSplitPos < 0)
    {
      /* [Rn] */

      if (!*OuterDisp.str.p_str && (BaseReg != REG_PC))
      {
        if (!check_mode_mask(pArg, ModMem, ModeMask))
          return False;
        if (IndexReg != REGSYM_FLAG_ALIAS)
        {
          pResult->vals[pResult->count++] = 0xc0 | IndexReg;
          pResult->vals[pResult->count++] = 0x60 | BaseReg;
          pResult->m = 1;
        }
        else
        {
          pResult->vals[pResult->count++] = 0x60 | BaseReg;
          pResult->m = 0;
        }
        return True;
      }

      pResult->forced_disp_size = InnerDispSize;
      if (!DeduceAndCheckDispSize(&OuterDisp, OuterDispValue, &OuterDispSize, &OuterEvalResult))
        return False;

      if (IndexReg != REGSYM_FLAG_ALIAS)
      {
        pResult->vals[pResult->count++] = 0xc0 | IndexReg;
        pResult->m = 1;
      }
      else
        pResult->m = 0;
      if (REG_PC == BaseReg)
        pResult->vals[pResult->count++] = 0xf0 + OuterDispSize;
      else
        pResult->vals[pResult->count++] = (OuterDispSize << 5) | BaseReg;
      AppendToVals(pResult, OuterDispValue, OuterDispSize);
    }

    /* double [[]] and no outer displacement: */

    else if (!*OuterDisp.str.p_str)
    {
      pResult->forced_disp_size = InnerDispSize;
      if (!DeduceAndCheckDispSize(&InnerDisp, InnerDispValue, &InnerDispSize, &InnerEvalResult))
        return False;

      if (IndexReg != REGSYM_FLAG_ALIAS)
      {
        pResult->vals[pResult->count++] = 0xc0 | IndexReg;
        pResult->m = 1;
      }
      else
        pResult->m = 0;
      if (REG_PC == BaseReg)
        pResult->vals[pResult->count++] = 0xf8 + InnerDispSize;
      else
        pResult->vals[pResult->count++] = 0x80 | (InnerDispSize << 5) | BaseReg;
      AppendToVals(pResult, InnerDispValue, InnerDispSize);
    }

    /* double [[]] and both displacements - no index register allowed! */

    else if (IndexReg != REGSYM_FLAG_ALIAS)
    {
      WrStrErrorPos(ErrNum_InvAddrMode, pArg);
      return False;
    }
    else
    {
      /* Both displacements must have same size.  If both are left undefined, use the maximum needed: */

      if ((InnerDispSize == eSymbolSizeUnknown) && (OuterDispSize == eSymbolSizeUnknown))
      {
        DeduceAndCheckDispSize(&InnerDisp, InnerDispValue, &InnerDispSize, &InnerEvalResult);
        DeduceAndCheckDispSize(&OuterDisp, OuterDispValue, &OuterDispSize, &OuterEvalResult);
        if (InnerDispSize > OuterDispSize)
          OuterDispSize = InnerDispSize;
        else if (OuterDispSize > InnerDispSize)
          InnerDispSize = OuterDispSize;
      }

      /* Otherwise, accept one size hint also for the other displacement: */

      else
      {
        if (InnerDispSize == eSymbolSizeUnknown)
          pResult->forced_disp_size = InnerDispSize = OuterDispSize;
        else if (OuterDispSize == eSymbolSizeUnknown)
          pResult->forced_disp_size = OuterDispSize = InnerDispSize;
        if (InnerDispSize != OuterDispSize)
        {
          WrStrErrorPos(ErrNum_ConfOpSizes, pArg);
          return False;
        }
        else
          pResult->forced_disp_size = OuterDispSize;
        if (!DeduceAndCheckDispSize(&InnerDisp, InnerDispValue, &InnerDispSize, &InnerEvalResult)
         || !DeduceAndCheckDispSize(&OuterDisp, OuterDispValue, &OuterDispSize, &OuterEvalResult))
          return False;
      }

      if (REG_PC == BaseReg)
      {
        pResult->vals[pResult->count++] = 0xfc + InnerDispSize;
        pResult->m = 0;
      }
      else
      {
        pResult->vals[pResult->count++] = (InnerDispSize << 5) | BaseReg;
        pResult->m = 1;
      }
      AppendToVals(pResult, InnerDispValue, InnerDispSize);
      AppendToVals(pResult, OuterDispValue, OuterDispSize);
    }
  }
  else
  {
    tSymbolSize DispSize = eSymbolSizeUnknown;
    LongInt DispValue;

    /* bare address: register? */

    switch (DecodeReg(pArg, pResult->vals, False))
    {
      case eIsReg:
        if (!check_mode_mask(pArg, ModReg, ModeMask))
          return False;
        if (((OpSize == eSymbolSize64Bit) || (OpSize == eSymbolSizeFloat64Bit))
         && (pResult->vals[0] == 31))
          WrStrErrorPos(ErrNum_Unpredictable, pArg);
        pResult->vals[0] |= 0x60;
        pResult->m = 1;
        pResult->count = 1;
        return True;
      case eRegAbort:
        return False;
      default:
        break;
    }

    /* treat bare address as PC-relative */

    CutSize(pArg, &DispSize);
    DispValue = EvalDispOpt(pArg, &EvalResult, True);
    if (!EvalResult.OK)
      return False;
    pResult->forced_disp_size = DispSize;
    if (!DeduceAndCheckDispSize(pArg, DispValue, &DispSize, &EvalResult))
      return False;
    if (IndexReg != REGSYM_FLAG_ALIAS)
    {
      pResult->vals[pResult->count++] = 0xc0 | IndexReg;
      pResult->m = 1;
    }
    else
      pResult->m = 0;
    pResult->vals[pResult->count++] = 0xf0 + DispSize;
    AppendToVals(pResult, DispValue, DispSize);
  }

  return (pResult->count > 0) && check_mode_mask(pArg, ModMem, ModeMask);
}

/*!------------------------------------------------------------------------
 * \fn     IsReg(const tAdrVals *p_vals)
 * \brief  check whether encoded address expression is register-direct ( Rn )
 * \param  p_vals encoded address expression
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsReg(const tAdrVals *p_vals)
{
  return ((p_vals->count == 1)
       && (p_vals->m == 1)
       && ((p_vals->vals[0] & 0xe0) == 0x60));
}

/*!------------------------------------------------------------------------
 * \fn     IsRegIndirect(const tAdrVals *p_vals)
 * \brief  check whether encoded address expression is register indirect ( [Rn] )
 * \param  p_vals encoded address expression
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsRegIndirect(const tAdrVals *p_vals)
{
  return ((p_vals->count == 1)
       && (p_vals->m == 0)
       && ((p_vals->vals[0] & 0xe0) == 0x60));
} 

/*!------------------------------------------------------------------------
 * \fn     IsPreDecrement(const tAdrVals *p_vals, Byte *p_reg)
 * \brief  check whether encoded address expression is pre-decrement ( [-Rn] )
 * \param  p_vals encoded address expression
 * \param  p_reg return buffer for used register
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsPreDecrement(const tAdrVals *p_vals, Byte *p_reg)
{
  if ((p_vals->count == 1)
   && (p_vals->m == 1)
   && ((p_vals->vals[0] & 0xe0) == 0xa0))
  {
    if (p_reg) *p_reg = p_vals->vals[0] & 0x1f;
    return True;
  }
  else
    return False;
}

/*!------------------------------------------------------------------------
 * \fn     IsPostIncrement(const tAdrVals *p_vals, Byte *p_reg)
 * \brief  check whether encoded address expression is post-increment ( [Rn+] )
 * \param  p_vals encoded address expression
 * \param  p_reg return buffer for used register
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsPostIncrement(const tAdrVals *p_vals, Byte *p_reg)
{
  if ((p_vals->count == 1)
   && (p_vals->m == 1)
   && ((p_vals->vals[0] & 0xe0) == 0x80))
  {
    if (p_reg) *p_reg = p_vals->vals[0] & 0x1f;
    return True;
  }
  else
    return False;
}

/*!------------------------------------------------------------------------
 * \fn     IsAbsolute(const tAdrVals *p_vals)
 * \brief  check whether encoded address expression is absolute ( /abs )
 * \param  p_vals encoded address expression
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsAbsolute(const tAdrVals *p_vals)
{
  return ((p_vals->count == 5)
       && (p_vals->m == 0)
       && (p_vals->vals[0] == 0xf3));
}

/*!------------------------------------------------------------------------
 * \fn     IsAbsoluteIndirect(const tAdrVals *p_vals)
 * \brief  check whether encoded address expression is absolute indirect ( [/abs] )
 * \param  p_vals encoded address expression
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsAbsoluteIndirect(const tAdrVals *p_vals)
{
  return ((p_vals->count == 5)
       && (p_vals->m == 0)
       && (p_vals->vals[0] == 0xfb));
}

/*!------------------------------------------------------------------------
 * \fn     IsDisplacement(const tAdrVals *p_vals)
 * \brief  check whether encoded address expression is displacement
 * \param  p_vals encoded address expression
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsDisplacement(const tAdrVals *p_vals)
{
  Byte base_mode = p_vals->vals[0],
       base_mode_mode = base_mode & 0xe0;

  return (p_vals->m == 0)
      && (p_vals->count > 1)
      && ((base_mode_mode == 0x000) /* disp8[Rn]  */
       || (base_mode      == 0x0f0) /* disp8[PC]  */
       || (base_mode_mode == 0x020) /* disp16[Rn] */
       || (base_mode      == 0x0f1) /* disp16[PC] */
       || (base_mode_mode == 0x040) /* disp32[Rn] */
       || (base_mode      == 0x0f2)); /* disp32[PC] */
}

/*!------------------------------------------------------------------------
 * \fn     IsIndirectDisplacement(const tAdrVals *p_vals)
 * \brief  check whether encoded address expression is indirect displacement
 * \param  p_vals encoded address expression
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsIndirectDisplacement(const tAdrVals *p_vals)
{
  Byte base_mode = p_vals->vals[0],
       base_mode_mode = base_mode & 0xe0;

  return (p_vals->m == 0)
      && (p_vals->count > 1)
      && ((base_mode_mode == 0x080) /* [disp8[Rn]]  */
       || (base_mode      == 0x0f8) /* [disp8[PC]]  */
       || (base_mode_mode == 0x0a0) /* [disp16[Rn]] */
       || (base_mode      == 0x0f9) /* [disp16[PC]] */
       || (base_mode_mode == 0x0c0) /* [disp32[Rn]] */
       || (base_mode      == 0x0fa)); /* [disp32[PC]] */
}

/*!------------------------------------------------------------------------
 * \fn     IsImmediate(const tAdrVals *p_adr_vals, LongWord *p_imm_value)
 * \brief  check whether encoded address expression is immediate
 * \param  p_vals encoded address expression
 * \param  p_imm_value return buffer for immediate value
 * \return True if immediate
 * ------------------------------------------------------------------------ */

static Boolean IsImmediate(const tAdrVals *p_adr_vals, LongWord *p_imm_value)
{
  if ((p_adr_vals->vals[0] & 0xf0) == 0xe0)
  {
    *p_imm_value = p_adr_vals->vals[0] & 0x0f;
    return True;
  }
  else if ((p_adr_vals->vals[0] == 0xf4) && (p_adr_vals->count > 1))
  {
    unsigned z;

    *p_imm_value = 0;
    for (z = p_adr_vals->count - 1; z > 0; z--)
      *p_imm_value = (*p_imm_value << 8) | p_adr_vals->vals[z];
    return True;
  }
  else
    return False;
}

/*!------------------------------------------------------------------------
 * \fn     IsSPAutoIncDec(const tAdrVals *p_adr_vals)
 * \brief  check whether encoded address expression is auto-increment/decrement with SP
 * \param  p_adr_vals encoded address expression
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsSPAutoIncDec(const tAdrVals *p_adr_vals)
{
  return ((p_adr_vals->count == 1)
       && p_adr_vals->m
       && ((p_adr_vals->vals[0] == (0x80 | REG_SP))
        || (p_adr_vals->vals[0] == (0xa0 | REG_SP))));
}

/*!------------------------------------------------------------------------
 * \fn     IsIndexed(const tAdrVals *p_adr_vals, Byte *p_reg)
 * \brief  check whether encoded address expression uses an index register
 * \param  p_adr_vals encoded address expression
 * \param  p_reg return buffer for index register if yes
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsIndexed(const tAdrVals *p_adr_vals, Byte *p_reg)
{
  if ((p_adr_vals->count >= 1)
   && p_adr_vals->m
   && ((p_adr_vals->vals[0] & 0xe0) == 0xc0))
  {
    if (p_reg) *p_reg = p_adr_vals->vals[0] & 0x1f;
    return True;
  }
  else
    return False;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitAdr(tStrComp *p_arg, tAdrVals *p_result, unsigned ModeMask)
 * \brief  parse bit address expression
 * \param  p_arg source argument
 * \param  p_result result buffer
 * \return True if success
 * ------------------------------------------------------------------------ */

static void insert_index(tAdrVals *p_result, Byte index_reg)
{
  memmove(p_result->vals + 1, p_result->vals, p_result->count);
  p_result->vals[0] = 0xc0 | index_reg;
  p_result->count++;
  p_result->m = 1;
}

static void extend_adrvals_size(tAdrVals *p_result, tSymbolSize *p_curr_size, tSymbolSize target_size, Byte mod_incr)
{
  while (*p_curr_size < target_size)
  {
    Byte ext = (p_result->vals[p_result->count - 1] & 0x80) ? 0xff : 0x00;
    p_result->vals[0] += mod_incr;
    *p_curr_size = (tSymbolSize)(*p_curr_size + 1);
    switch (*p_curr_size)
    {
      case eSymbolSize32Bit:
        p_result->vals[p_result->count++] = ext;
        /* FALL-THRU */
      case eSymbolSize16Bit:
        p_result->vals[p_result->count++] = ext;
        break;
      default:
        break;
    }
  }
}

static Boolean DecodeBitAdr(tStrComp *p_arg, tAdrVals *p_result, unsigned ModeMask)
{
  char *p_sep;
  tStrComp offset_arg, base_arg;
  Byte offs_reg;
  tEvalResult eval_result;

  /* find '@' that separates bit offset & byte base address, which MUST be present: */

  p_sep = QuotPos(p_arg->str.p_str, '@');
  if (!p_sep)
  {
    WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
    return False;
  }
  StrCompSplitRef(&offset_arg, &base_arg, p_arg, p_sep);
  KillPostBlanksStrComp(&offset_arg);
  KillPrefBlanksStrCompRef(&base_arg);

  /* parse the base address */

  (void)ModeMask;
  if (!DecodeAdr(&base_arg, p_result, MModMem))
    return False;

  /* no offset: the encoding remains the same, just check for valid addressing modes: */

  if (!offset_arg.str.p_str[0])
  {
    eval_result.OK =
         IsRegIndirect(p_result)
      || IsPostIncrement(p_result, NULL)
      || IsPreDecrement(p_result, NULL)
      || IsIndirectDisplacement(p_result)
      || IsAbsolute(p_result)
      || IsAbsoluteIndirect(p_result);
    if (!eval_result.OK)
      WrStrErrorPos(ErrNum_InvAddrMode, &base_arg);
  }

  else switch (DecodeReg(&offset_arg, &offs_reg, False))
  {
    /* offset is register: if this is a valid addressing mode, insert register
       as index register: */

    case eIsReg:
      eval_result.OK = True;
      if (IsRegIndirect(p_result)
       || IsDisplacement(p_result)
       || IsIndirectDisplacement(p_result)
       || IsAbsolute(p_result)
       || IsAbsoluteIndirect(p_result))
        insert_index(p_result, offs_reg);
      else
        eval_result.OK = False;
      if (!eval_result.OK)
        WrStrErrorPos(ErrNum_InvAddrMode, &base_arg);
      break;

    /* offset is immediate value: */

    case eIsNoReg:
    {
      tSymbolSize forced_offset_size = eSymbolSizeUnknown, offset_size;

      CutSize(&offset_arg, &forced_offset_size);
      p_result->bit_offset = EvalStrIntExpressionWithResult(&offset_arg, SInt32, &eval_result);
      if (!eval_result.OK)
        break;
      p_result->immediate_flags = eval_result.Flags;
      offset_size = forced_offset_size;
      if (!(eval_result.OK = DeduceAndCheckDispSize(&offset_arg, p_result->bit_offset, &offset_size, &eval_result)))
        break;

      /* base addressing mode has no displacement so far: append as first displacement */

      if (IsRegIndirect(p_result))
      {
        p_result->vals[0] = (p_result->vals[0] & 0x1f) | (offset_size << 5);
        AppendToVals(p_result, p_result->bit_offset, offset_size);
      }

      /* single (indirect) displacement becomes double displacement */

      else if (IsIndirectDisplacement(p_result))
      {
        Boolean base_pc = !!(p_result->vals[0] & 0x10);
        /* extract operand size of base displacement */
        tSymbolSize base_disp_size = (tSymbolSize)((p_result->vals[0] >> (base_pc ? 0 : 5)) & 3);

        /* Similar to disp1 & disp2, base & offset must have same length, so if
           lengths are unequal, extend one of them, or complain if we cannot make
           things fit: */

        /* (1) No explicit length given for either: extend the other one as needed
           if actual sizes differ: */

        if ((p_result->forced_disp_size == eSymbolSizeUnknown)
         && (forced_offset_size == eSymbolSizeUnknown))
        {
          if (offset_size < base_disp_size)
            offset_size = base_disp_size;
          else if (base_disp_size < offset_size)
            extend_adrvals_size(p_result, &base_disp_size, offset_size, p_result->vals[0] += base_pc ? 0x01 : 0x20);
        }

        /* (2a) Only base displacement size is fixed: see if offset size can be extended */

        else if (forced_offset_size == eSymbolSizeUnknown)
        {
          if (offset_size < base_disp_size)
            offset_size = base_disp_size;
          else
          {
            WrStrErrorPos(ErrNum_OverRange, &offset_arg);
            return False;
          }
        }

        /* (2b) Vice versa, if only offset size is fixed: */

        else if (p_result->forced_disp_size == eSymbolSizeUnknown)
        {
          if (base_disp_size < offset_size)
            extend_adrvals_size(p_result, &base_disp_size, offset_size, p_result->vals[0] += base_pc ? 0x01 : 0x20);
          else
          {
            WrStrErrorPos(ErrNum_OverRange, &base_arg);
            return False;
          }
        }

        /* (3) if both sizes are fixed, they must be equal: */

        else if (p_result->forced_disp_size != forced_offset_size)
        {
          WrStrErrorPos(ErrNum_ConfOpSizes, p_arg);
          return False;
        }

        /* All fine: Change addressing mode to double displacement,
           and add bit offset as second displacement */

        if (base_pc)
          p_result->vals[0] |= 0x04;
        else
        {
          p_result->m = 1;
          p_result->vals[0] &= ~0x80;
        }
        AppendToVals(p_result, p_result->bit_offset, offset_size);
      }

      /* everything else not allowed */

      else
      {
        WrStrErrorPos(ErrNum_InvAddrMode, &base_arg);
        eval_result.OK = False;
      }

      p_result->bit_offset_immediate = True;
      break;
    }
    default: /* eRegAbort */
      eval_result.OK = False;
  }

  return eval_result.OK;
}

/*--------------------------------------------------------------------------*/
/* Utility Functions */

/*!------------------------------------------------------------------------
 * \fn     ChkNoAttrPart(void)
 * \brief  check for no attribute part
 * \return True if no attribute
 * ------------------------------------------------------------------------ */

static Boolean ChkNoAttrPart(void)
{
  if (*AttrPart.str.p_str)
  {
    WrError(ErrNum_UseLessAttr);
    return False;
  }
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     ChkOpSize(int Op8, int Op16, int Op32, int Op64)
 * \brief  check for valid (single) (integer) operand size
 * \param  Op8 machine code if size is 8 bits
 * \param  Op16 machine code if size is 16 bits
 * \param  Op32 machine code if size is 32 bits
 * \param  Op64 machine code if size is 64 bits
 * \return True if size is OK
 * ------------------------------------------------------------------------ */

static Boolean ChkOpSize(int Op8, int Op16, int Op32, int Op64)
{
  if (AttrPartOpSize[1] != eSymbolSizeUnknown)
  {
    WrStrErrorPos(ErrNum_InvOpSize, &AttrPart);
    return False;
  }
  if (OpSize == eSymbolSizeUnknown)
  {
    if (AttrPartOpSize[0] != eSymbolSizeUnknown)
      OpSize = AttrPartOpSize[0];
    else if ((Op8 >= 0) && Hi(Op8))
      OpSize = eSymbolSize8Bit;
    else if ((Op16 >= 0) && Hi(Op16))
      OpSize = eSymbolSize16Bit;
    else if ((Op32 >= 0) && Hi(Op32))
      OpSize = eSymbolSize32Bit;
    else if ((Op64 >= 0) && Hi(Op64))
      OpSize = eSymbolSize64Bit;
  }
  switch (OpSize)
  {
    case eSymbolSize8Bit:
      if (Op8 < 0)
        goto Bad;
      BAsmCode[CodeLen] = Op8;
      return True;
    case eSymbolSize16Bit:
      if (Op16 < 0)
        goto Bad;
      BAsmCode[CodeLen] = Op16;
      return True;
    case eSymbolSize32Bit:
      if (Op32 < 0)
        goto Bad;
      BAsmCode[CodeLen] = Op32;
      return True;
    case eSymbolSize64Bit:
      if (Op64 < 0)
        goto Bad;
      BAsmCode[CodeLen] = Op64;
      return True;
    default:
    Bad:
      WrStrErrorPos(ErrNum_InvOpSize, &OpPart);
      return False;
  }
}

/*!------------------------------------------------------------------------
 * \fn     ChkFOpSize(int op_short, int op_long)
 * \brief  check for valid (real) operand size
 * \param  op_short machine code if size short real
 * \param  op_long machine code if size is long real
 * \return True if size is OK
 * ------------------------------------------------------------------------ */

static Boolean ChkFOpSize(int op_short, int op_long)
{
  if (AttrPartOpSize[1] != eSymbolSizeUnknown)
  {
    WrStrErrorPos(ErrNum_InvOpSize, &AttrPart);
    return False;
  }
  if (OpSize == eSymbolSizeUnknown)
  {
    if (AttrPartOpSize[0] != eSymbolSizeUnknown)
      OpSize = AttrPartOpSize[0];
    else if ((op_short >= 0) && Hi(op_short))
      OpSize = eSymbolSizeFloat32Bit;
    else if ((op_long >= 0) && Hi(op_long))
      OpSize = eSymbolSizeFloat64Bit;
  }
  switch (OpSize)
  {
    case eSymbolSizeFloat32Bit:
      if (op_short < 0)
        goto Bad;
      BAsmCode[CodeLen] = op_short;
      return True;
    case eSymbolSizeFloat64Bit:
      if (op_long < 0)
        goto Bad;
      BAsmCode[CodeLen] = op_long;
      return True;
    default:
    Bad:
      WrStrErrorPos(ErrNum_InvOpSize, &OpPart);
      return False;
  }
}

/*!------------------------------------------------------------------------
 * \fn     ChkPtrOpSize(int op_ptr)
 * \brief  check for valid (pointer) operand size
 * \param  op_ptr machine code if size is pointer
 * \return True if size is OK
 * ------------------------------------------------------------------------ */

static Boolean ChkPtrOpSize(int op_ptr)
{
  if (AttrPartOpSize[1] != eSymbolSizeUnknown)
  {
    WrStrErrorPos(ErrNum_InvOpSize, &AttrPart);
    return False;
  }
  if (OpSize == eSymbolSizeUnknown)
  {
    if (AttrPartOpSize[0] != eSymbolSizeUnknown)
      OpSize = AttrPartOpSize[0];
    else if ((op_ptr >= 0) && Hi(op_ptr))
      OpSize = eSymbolSize24Bit;
  }
  switch (OpSize)
  {
    case eSymbolSize24Bit:
      if (op_ptr < 0)
        goto Bad;
      OpSize = eSymbolSize32Bit;
      BAsmCode[CodeLen] = Lo(op_ptr);
      return True;
    default:
    Bad:
      WrStrErrorPos(ErrNum_InvOpSize, &OpPart);
      return False;
  }
}

/*!------------------------------------------------------------------------
 * \fn     chk_sup_mode(Word code)
 * \brief  check whether privileged instructions are enabled
 * \param  code machine code with sup mode flag
 * \return True if enabled or not required
 * ------------------------------------------------------------------------ */

static Boolean chk_sup_mode(Word code)
{
  if ((code & CODE_FLAG_SUPMODE) && !SupAllowed)
  {
    WrStrErrorPos(ErrNum_PrivOrder, &OpPart);
    return False;
  }
  else
    return True;
}

/*!------------------------------------------------------------------------
 * \fn     AppendAdrVals(const tAdrVals *p_vals)
 * \brief  append addressing mode extension values to instruction
 * \param  p_vals holds encoded addressing expression
 * ------------------------------------------------------------------------ */

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

/*!------------------------------------------------------------------------
 * \fn     DecodeLength(tStrComp *p_arg, tLenVals *p_len_vals)
 * \brief  decode length argument of bit string insns
 * \param  p_arg source argument
 * \param  p_len_vals encoded length argument
 * \return True if success
 * ------------------------------------------------------------------------ */

static Boolean DecodeLength(tStrComp *p_arg, tLenVals *p_len_vals)
{
  tAdrVals adr_vals;
  LongWord length;

  if (!DecodeAdr(p_arg, &adr_vals, MModReg | MModImm | MModImm7Bit))
    return False;
  p_len_vals->immediate_flags = adr_vals.immediate_flags;
  if (IsReg(&adr_vals))
    p_len_vals->val = 0x80 | (adr_vals.vals[0] & 0x1f);
  else if (IsImmediate(&adr_vals, &length))
    p_len_vals->val = length & 127;
  else
  {
    WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
    return False;
  }
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     IsImmediateLength(const tLenVals *p_len_vals, LongWord *p_imm_val)
 * \brief  check whether encoded bit field length is immediate value
 * \param  p_len_vals encoded length field from instruction
 * \param  p_imm_val return buffer if immediate
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsImmediateLength(const tLenVals *p_len_vals, LongWord *p_imm_val)
{
  if (p_len_vals->val & 0x80)
    return False;
  else
  {
    *p_imm_val = p_len_vals->val & 0x7f;
    return True;
  }
}

/*!------------------------------------------------------------------------
 * \fn     imm_mask_cond(Word code)
 * \brief  check whether immediate mode is allowed and return accordingly
 * \param  code machine code holding CODE_FLAG_OP2_IMM in MSB or not
 * \return immediate mask if allowed or 0
 * ------------------------------------------------------------------------ */

static unsigned imm_mask_cond(Word code)
{
  return (code & CODE_FLAG_OP2_IMM) ? MModImm : 0;
}

/*!------------------------------------------------------------------------
 * \fn     check_addrmodes_unpredictable(const tAdrVals *p_first_vals, const tAdrVals *p_second_vals)
 * \brief  check whether the combination of two addressing modes may yield unpredictable results
 * \param  p_first_vals encoded first operand
 * \param  p_second_vals encoded second operand
 * \return True if unpredictable result may occur
 * ------------------------------------------------------------------------ */

static Boolean check_addrmodes_unpredictable(const tAdrVals *p_first_vals, const tAdrVals *p_second_vals)
{
  Byte first_reg = 0, second_reg = 0;

  /* For all unpredictable combinations, first operand must be auto-increment: */

  if (!IsPreDecrement(p_first_vals, &first_reg)
   && !IsPostIncrement(p_first_vals, &first_reg))
    return False;

  /* If second operand is auto-increment with same register,
     result is unpredictable if both operand sizes differ: */

  if ((IsPreDecrement(p_second_vals, &second_reg) || IsPostIncrement(p_second_vals, &second_reg))
   && (second_reg == first_reg)
   && (p_first_vals->data_op_size != p_second_vals->data_op_size))
  {
    WrError(ErrNum_Unpredictable);
    return True;
  }

  /* If second operand uses same register as index register,
     result is unpredictable: */

  else if (IsIndexed(p_second_vals, &second_reg) && (second_reg == first_reg))
  {
    WrError(ErrNum_Unpredictable);
    return True;
  }

  else
    return False;
}

/*!------------------------------------------------------------------------
 * \fn     EncodeFormat1(Byte Reg, const tAdrVals *pSecond)
 * \brief  encode instruction in format I
 * \param  Reg Register# + direction bit
 * \param  pSecond second operand
 * ------------------------------------------------------------------------ */

static void EncodeFormat1(Byte Reg, const tAdrVals *pSecond)
{
  CodeLen++;
  BAsmCode[CodeLen++] = Reg | (pSecond->m << 6);
  AppendAdrVals(pSecond);
}

/*!------------------------------------------------------------------------
 * \fn     EncodeFormat2(Byte SubOp, const tAdrVals *pFirst, const tAdrVals *pSecond)
 * \brief  encode instruction in format II
 * \param  SubOp extension byte
 * \param  pFirst first operand
 * \param  pSecond second operand
 * ------------------------------------------------------------------------ */

static void EncodeFormat2(Byte SubOp, const tAdrVals *pFirst, const tAdrVals *pSecond)
{
  CodeLen++;
  BAsmCode[CodeLen++] = SubOp | 0x80 | (pSecond->m << 5) | (pFirst->m << 6);
  AppendAdrVals(pFirst);
  AppendAdrVals(pSecond);
  check_addrmodes_unpredictable(pFirst, pSecond);
}

/*!------------------------------------------------------------------------
 * \fn     EncodeFormat1Or2(Byte SubOp, const tAdrVals *pFirst, const tAdrVals *pSecond)
 * \brief  encode instruction in format I if possible or format II
 * \param  SubOp extension byte for format II
 * \param  pFirst first operand
 * \param  pSecond second operand
 * ------------------------------------------------------------------------ */

static void EncodeFormat1Or2(Byte SubOp, const tAdrVals *pFirst, const tAdrVals *pSecond)
{
  if (IsReg(pFirst))
    EncodeFormat1(pFirst->vals[0] & 0x1f, pSecond);
  else if (IsReg(pSecond))
    EncodeFormat1((pSecond->vals[0] & 0x1f) | 0x20, pFirst);
  else
    EncodeFormat2(SubOp, pFirst, pSecond);
}

/*!------------------------------------------------------------------------
 * \fn     EncodeFormat3(const tAdrVals *p_adr_vals)
 * \brief  encode instruction in format III
 * \param  p_adr_vals operand
 * ------------------------------------------------------------------------ */

static void EncodeFormat3(const tAdrVals *p_adr_vals)
{
  BAsmCode[CodeLen] |= p_adr_vals->m;
  CodeLen++;
  AppendAdrVals(p_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     chk_imm_bitfield_range(const tStrComp *p_bitfield_src_arg, const tAdrVals *p_bitfield_adr_vals, const tLenVals *p_len_vals)
 * \brief  check whether bit field's length plus offset do not exceed allowed range
 * \param  p_bitfield_src_arg source argument of bit field start+offset
 * \param  p_bitfield_adr_vals encoded value of bit field start+offset
 * \param  p_len_vals encoded value of bit field length
 * ------------------------------------------------------------------------ */

static void chk_imm_bitfield_range(const tStrComp *p_bitfield_src_arg, const tAdrVals *p_bitfield_adr_vals, const tLenVals *p_len_vals)
{
  LongWord imm_length;

  /* optional check for offset+length <= 32, if possible: */

  if (p_bitfield_adr_vals->bit_offset_immediate
   && !mFirstPassUnknownOrQuestionable(p_bitfield_adr_vals->immediate_flags)
   && IsImmediateLength(p_len_vals, &imm_length)
   && !mFirstPassUnknownOrQuestionable(p_len_vals->immediate_flags)
   && (p_bitfield_adr_vals->bit_offset + imm_length > 32))
  {
    char Msg[64];

    as_snprintf(Msg, sizeof(Msg), "%u + %u > 32",
                (unsigned)p_bitfield_adr_vals->bit_offset, 
                (unsigned)imm_length);
    WrXErrorPos(ErrNum_ArgOutOfRange, Msg, &p_bitfield_src_arg->Pos);
  }
}

/*!------------------------------------------------------------------------
 * \fn     chk_imm_value_range(const tStrComp *p_src_arg, const tAdrVals *p_adr_vals, const char *p_name, unsigned max_value)
 * \brief  check whether immediate value is in range
 * \param  p_arg source argument of value
 * \param  p_adr_vals encoded value of value
 * ------------------------------------------------------------------------ */

static void chk_imm_value_range(const tStrComp *p_src_arg, const tAdrVals *p_adr_vals, const char *p_name, unsigned max_value)
{
  LongWord imm_value;

  if (IsImmediate(p_adr_vals, &imm_value)
   && !mFirstPassUnknownOrQuestionable(p_adr_vals->immediate_flags)
   && (imm_value > max_value))
  {
    char Msg[64];

    as_snprintf(Msg, sizeof(Msg), "%s %u > %u",
                p_name, (unsigned)imm_value, max_value);
    WrXErrorPos(ErrNum_ArgOutOfRange, Msg, &p_src_arg->Pos);
  }
}

/*!------------------------------------------------------------------------
 * \fn     chk_imm_level_range(const tStrComp *p_level_src_arg, const tAdrVals *p_level_adr_vals)
 * \brief  check whether (privilege) level is in range (0..3)
 * \param  p_level_src_arg source argument of level
 * \param  p_level_adr_vals encoded value of level
 * ------------------------------------------------------------------------ */

static void chk_imm_level_range(const tStrComp *p_level_src_arg, const tAdrVals *p_level_adr_vals)
{
  chk_imm_value_range(p_level_src_arg, p_level_adr_vals, "lvl", 3);
}

/*--------------------------------------------------------------------------*/
/* Instruction Handlers */

/*!------------------------------------------------------------------------
 * \fn     DecodeFixed(Word Code)
 * \brief  handle instructions with no argument
 * \param  Code machine code
 * ------------------------------------------------------------------------ */

static void DecodeFixed(Word Code)
{
  if (ChkArgCnt(0, 0)
   && chk_sup_mode(Code)
   && ChkNoAttrPart())
    BAsmCode[CodeLen++] = Lo(Code);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeArithF(Word code)
 * \brief  handle FPU arithmetic instructions
 * \param  code machine opcode for short op size
 * ------------------------------------------------------------------------ */

static void DecodeArithF(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

  if (ChkArgCnt(2, 2)
   && ChkFOpSize(0x100 | Lo(code), Lo(code) | 0x02)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, MModMem | MModReg)
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, MModMem | MModReg))
  {
    EncodeFormat2(Hi(code), &src_adr_vals, &dest_adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeSCLF(Word code)
 * \brief  handle SCLF Instruction
 * \param  code machine opcode for short op size
 * ------------------------------------------------------------------------ */

static void DecodeSCLF(Word code)
{
  if (ChkArgCnt(2, 2)
   && ChkFOpSize(0x100 | Lo(code), Lo(code) | 0x02))
  {
    tAdrVals count_adr_vals;
    tSymbolSize save_op_size;

    save_op_size = OpSize;
    OpSize = eSymbolSize16Bit;
    if (DecodeAdr(&ArgStr[1], &count_adr_vals, MModMem | MModReg | MModImm))
    {
      tAdrVals  dest_adr_vals;

      OpSize = save_op_size;

      if (DecodeAdr(&ArgStr[2], &dest_adr_vals, MModMem | MModReg))
        EncodeFormat2(Hi(code), &count_adr_vals, &dest_adr_vals);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeArith(Word code)
 * \brief  handle arithmetic instructions
 * \param  code machine code for 8 bit in LSB, bit 8 -> dest may be immediate
 * ------------------------------------------------------------------------ */

static void DecodeArith(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

  if (ChkArgCnt(2, 2)
   && ChkOpSize(Lo(code), Lo(code) + 2, 0x100 | (Lo(code) + 4), -1)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, MModMem | MModReg | MModImm)
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, MModMem | MModReg | imm_mask_cond(code)))
    EncodeFormat1Or2(0x00, &src_adr_vals, &dest_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeArithX(Word code)
 * \brief  handle arithmetic instructions with double dest width
 * \param  code machine code for 8 bit in LSB
 * ------------------------------------------------------------------------ */

static void DecodeArithX(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

  if (ChkArgCnt(2, 2)
   && ChkOpSize(-1, -1, 0x100 | Lo(code), -1)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, MModMem | MModReg | MModImm)
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, MModMem | MModReg))
    EncodeFormat1Or2(0x00, &src_adr_vals, &dest_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeXCH(Word code)
 * \brief  handle XCH instruction
 * \param  code machine code for 8 bit operand size
 * ------------------------------------------------------------------------ */

static void DecodeXCH(Word code)
{
  tAdrVals dest1_adr_vals, dest2_adr_vals;

  if (ChkArgCnt(2, 2)
   && ChkOpSize(Lo(code), Lo(code) + 2, 0x100 | (Lo(code) + 4), -1)
   && DecodeAdr(&ArgStr[1], &dest1_adr_vals, MModMem | MModReg)
   && DecodeAdr(&ArgStr[2], &dest2_adr_vals, MModMem | MModReg))
  {
    if (IsReg(&dest1_adr_vals))
      EncodeFormat1(dest1_adr_vals.vals[0] & 0x1f, &dest2_adr_vals);
    else if (IsReg(&dest2_adr_vals))
      EncodeFormat1(dest2_adr_vals.vals[0] & 0x1f, &dest1_adr_vals);
    else
      WrError(ErrNum_InvArgPair);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeSETF(Word code)
 * \brief  handle SETF instruction
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeSETF(Word code)
{
  tAdrVals cond_adr_vals, dest_adr_vals;

  if (ChkArgCnt(2, 2)
   && ChkOpSize(0x100 | Lo(code), -1, -1, -1)
   && DecodeAdr(&ArgStr[1], &cond_adr_vals, MModMem | MModReg | MModImm | MModImmCondition)
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, MModMem | MModReg))
  {
    EncodeFormat1Or2(0, &cond_adr_vals, &dest_adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeShift(Word code)
 * \brief  handle shift instructions
 * \param  code machine code for 8 bit in LSB
 * ------------------------------------------------------------------------ */

static Boolean DecodeShiftCnt(tStrComp *p_arg, tAdrVals *p_adr_vals)
{
  tSymbolSize save_size;
  Boolean ret;

  save_size = OpSize;
  OpSize = eSymbolSize8Bit;
  ret = DecodeAdr(p_arg, p_adr_vals, MModMem | MModReg | MModImm);
  OpSize = save_size;
  return ret;
}

static void DecodeShift(Word code)
{
  tAdrVals shift_adr_vals, dest_adr_vals;

  if (ChkArgCnt(2, 2)
   && ChkOpSize(Lo(code), Lo(code) + 2, 0x100 | (Lo(code) + 4), -1)
   && DecodeShiftCnt(&ArgStr[1], &shift_adr_vals)
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, MModMem | MModReg))
    EncodeFormat1Or2(0x00, &shift_adr_vals, &dest_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeArith_7c(Word code)
 * \brief  handle arithmetic instructions
 * \param  code machine code in LSB, sub-code in MSB
 * ------------------------------------------------------------------------ */

static void DecodeArith_7c(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

  if (ChkArgCnt(3, 3)
   && ChkOpSize(0x100 | Lo(code), -1, -1, -1)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, MModMem | MModReg | MModImm)
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, MModMem | MModReg))
  {
    Boolean ok;
    Byte mask = EvalStrIntExpressionOffs(&ArgStr[3], ArgStr[3].str.p_str[0] == '#', Int8, &ok);

    if (ok)
    {
      EncodeFormat2(Hi(code), &src_adr_vals, &dest_adr_vals);
      BAsmCode[CodeLen++] = mask;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitString_7b(Word code)
 * \brief  handle bit string instructions
 * \param  code machine code in LSB, sub-code in MSB
 * ------------------------------------------------------------------------ */

static void DecodeBitString_7b(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;
  tLenVals length;

  if (ChkArgCnt(3, 3)
   && ChkNoAttrPart())
  {
    OpSize = eSymbolSize8Bit;

    if (DecodeBitAdr(&ArgStr[1], &src_adr_vals, MModMem)
     && DecodeLength(&ArgStr[2], &length)
     && DecodeBitAdr(&ArgStr[3], &dest_adr_vals, MModMem))
    {
      BAsmCode[CodeLen++] = Lo(code);
      BAsmCode[CodeLen++] = Hi(code) | 0x80 | (dest_adr_vals.m << 5) | (src_adr_vals.m << 6);
      AppendAdrVals(&src_adr_vals);
      BAsmCode[CodeLen++] = length.val;
      AppendAdrVals(&dest_adr_vals);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitField_7b(Word code)
 * \brief  handle bit field instructions
 * \param  code machine code in LSB, sub-code in MSB
 * ------------------------------------------------------------------------ */

static void DecodeBitField_7b(Word code)
{
  tAdrVals bsrc_adr_vals, src_adr_vals;
  tLenVals length;

  if (ChkArgCnt(3, 3)
   && ChkNoAttrPart())
  {
    OpSize = eSymbolSize32Bit;

    if (DecodeBitAdr(&ArgStr[1], &bsrc_adr_vals, MModMem)
     && DecodeLength(&ArgStr[2], &length)
     && DecodeAdr(&ArgStr[3], &src_adr_vals, MModReg | MModMem | imm_mask_cond(code)))
    {
      /* optional check for offset+length <= 32, if possible: */

      if (code & CODE_FLAG_LIM32)
        chk_imm_bitfield_range(&ArgStr[1], &bsrc_adr_vals, &length);

      BAsmCode[CodeLen++] = Lo(code);
      BAsmCode[CodeLen++] = (Hi(code) & 0x1f) | 0x80 | (src_adr_vals.m << 5) | (bsrc_adr_vals.m << 6);
      AppendAdrVals(&bsrc_adr_vals);
      BAsmCode[CodeLen++] = length.val;
      AppendAdrVals(&src_adr_vals);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitField_7c(Word code)
 * \brief  handle bit field instructions
 * \param  code machine code in LSB, sub-code in MSB
 * ------------------------------------------------------------------------ */

static void DecodeBitField_7c(Word code)
{
  tAdrVals src_adr_vals, bdst_adr_vals;
  tLenVals length;

  if (ChkArgCnt(3, 3)
   && ChkNoAttrPart())
  {
    OpSize = eSymbolSize32Bit;

    if (DecodeAdr(&ArgStr[1], &src_adr_vals, MModReg | MModMem | MModImm)
     && DecodeBitAdr(&ArgStr[2], &bdst_adr_vals, MModMem)
     && DecodeLength(&ArgStr[3], &length))
    {
      /* optional check for offset+length <= 32, if possible: */

      if (code & CODE_FLAG_LIM32)
        chk_imm_bitfield_range(&ArgStr[2], &bdst_adr_vals, &length);

      BAsmCode[CodeLen++] = Lo(code);
      BAsmCode[CodeLen++] = (Hi(code) & 0x1f) | 0x80 | (bdst_adr_vals.m << 5) | (src_adr_vals.m << 6);
      AppendAdrVals(&src_adr_vals);
      AppendAdrVals(&bdst_adr_vals);
      BAsmCode[CodeLen++] = length.val;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeString_7a(Word code)
 * \brief  Handle Format VIIa String Instructons
 * \param  code machine code (LSB) and subcode (MSB)
 * ------------------------------------------------------------------------ */

static void DecodeString_7a(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;
  tLenVals src_length, dest_length;

  if (ChkArgCnt(4, 4)
   && ChkOpSize(0x100 | (Lo(code)), Lo(code) + 2, -1, -1)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, MModMem)
   && DecodeLength(&ArgStr[2], &src_length)
   && DecodeAdr(&ArgStr[3], &dest_adr_vals, MModMem)
   && DecodeLength(&ArgStr[4], &dest_length))
  {
    CodeLen++;
    BAsmCode[CodeLen++] = (Hi(code) & 0x1f) | 0x80 | (dest_adr_vals.m << 5) | (src_adr_vals.m << 6);
    AppendAdrVals(&src_adr_vals);
    BAsmCode[CodeLen++] = src_length.val;
    AppendAdrVals(&dest_adr_vals);
    BAsmCode[CodeLen++] = dest_length.val;
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeString_7b(Word code)
 * \brief  Handle Format VIIb String Instructons
 * \param  code machine code (LSB) and subcode (MSB)
 * ------------------------------------------------------------------------ */

static void DecodeString_7b(Word code)
{
  tAdrVals src_adr_vals, char_adr_vals;
  tLenVals src_length;

  if (ChkArgCnt(3, 3)
   && ChkOpSize(0x100 | (Lo(code)), Lo(code) + 2, -1, -1)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, MModMem)
   && DecodeLength(&ArgStr[2], &src_length)
   && DecodeAdr(&ArgStr[3], &char_adr_vals, MModReg | MModMem | MModImm))
  {
    CodeLen++;
    BAsmCode[CodeLen++] = (Hi(code) & 0x1f) | 0x80 | (char_adr_vals.m << 5) | (src_adr_vals.m << 6);
    AppendAdrVals(&src_adr_vals);
    BAsmCode[CodeLen++] = src_length.val;
    AppendAdrVals(&char_adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     Decode_4(Word code)
 * \brief  handle relative branches
 * \param  code machine code for 16 bit displacement
 * ------------------------------------------------------------------------ */

static void Decode_4(Word code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean ok;
    const Boolean allow_8 = (code & 0xf0) == 0x70;
    tSymbolFlags flags;
    LongWord addr = EvalStrIntExpressionWithFlags(&ArgStr[1], UInt32, &ok, &flags);

    if (ok)
    {
      LongInt delta = addr - EProgCounter();

      switch (AttrPartOpSize[0])
      {
        case eSymbolSizeUnknown:
          if ((delta > -128) && (delta < 127) && allow_8)
            goto br_short;
          else
            goto br_long;
        case eSymbolSizeFloat32Bit: /* .s */
        case eSymbolSize8Bit:  /* .b */
        br_short:
          if (!allow_8)
          {
            WrStrErrorPos(ErrNum_InvOpSize, &AttrPart);
            return;
          }
          code -= 0x10;
          if (!mFirstPassUnknownOrQuestionable(flags) && ((delta < -128) || (delta > 127))) WrError(ErrNum_JmpDistTooBig);
          else
          {
            BAsmCode[CodeLen++] = code;
            BAsmCode[CodeLen++] = delta & 0xff;
          }
          break;
        case eSymbolSizeFloat64Bit: /* .l */
        case eSymbolSize16Bit: /* .h */
        br_long:
          if (!mFirstPassUnknownOrQuestionable(flags) && ((delta < -32768) || (delta > 32767))) WrError(ErrNum_JmpDistTooBig);
          else
          {
            BAsmCode[CodeLen++] = code;
            BAsmCode[CodeLen++] = delta & 0xff;
            BAsmCode[CodeLen++] = (delta >> 8) & 0xff;
          }
          break;
        default:
          WrStrErrorPos(ErrNum_InvOpSize, &AttrPart);
          return;
      }
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     Decode_6(Word code)
 * \brief  handle loop instructions
 * \param  code machine code for 16 bit displacement
 * ------------------------------------------------------------------------ */

static void Decode_6(Word code)
{
  tAdrVals reg_adr_vals;

  /* allow no attribute at all since it would be unclear whether to check
     for register operand's size (32 bits) or displacement operand's size
     (16 bits): */

  if (ChkArgCnt(2, 2)
   && ChkNoAttrPart()
   && DecodeAdr(&ArgStr[1], &reg_adr_vals, MModReg))
  {
    Boolean ok;
    tSymbolFlags flags;
    LongWord addr = EvalStrIntExpressionWithFlags(&ArgStr[2], UInt32, &ok, &flags);

    if (ok)
    {
      LongInt delta = addr - EProgCounter();

      if (!mFirstPassUnknownOrQuestionable(flags) && ((delta < -32768) || (delta > 32767))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        BAsmCode[CodeLen++] = Lo(code);
        BAsmCode[CodeLen++] = (reg_adr_vals.vals[0] & 0x1f) | (Hi(code) << 5);
        BAsmCode[CodeLen++] = delta & 0xff;
        BAsmCode[CodeLen++] = (delta >> 8) & 0xff;
      }
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeCLRTLB(Word code)
 * \brief  handle CLRTLB instruction
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeCLRTLB(Word code)
{
  tAdrVals adr_vals;

  if (ChkArgCnt(1, 1)
   && ChkPtrOpSize(Lo(code) | 0x100)
   && chk_sup_mode(code)
   && DecodeAdr(&ArgStr[1], &adr_vals, MModMem | MModImm | MModReg))
  {
    EncodeFormat3(&adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeGETPSW(Word code)
 * \brief  handle GETPSW instruction
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeGETPSW(Word code)
{
  tAdrVals adr_vals;

  if (ChkArgCnt(1, 1)
   && ChkOpSize(-1, -1, 0x100 | Lo(code), -1)
   && DecodeAdr(&ArgStr[1], &adr_vals, MModMem | MModReg))
  {
    EncodeFormat3(&adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeUPDPSW(Word code)
 * \brief  handle UPDPSW instruction
 * ------------------------------------------------------------------------ */

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

  if (ChkArgCnt(2, 2)
   && ChkOpSize(-1, 0x14a, 0x13, -1)
   && chk_sup_mode((OpSize == eSymbolSize32Bit) ? CODE_FLAG_SUPMODE : 0))
  {
    tAdrVals new_psw_adr_vals, mask_adr_vals;

    OpSize = eSymbolSize32Bit;

    if (DecodeAdr(&ArgStr[1], &new_psw_adr_vals, MModImm | MModMem | MModReg)
     && DecodeAdr(&ArgStr[2], &mask_adr_vals, MModImm | MModMem | MModReg))
      EncodeFormat1Or2(0, &new_psw_adr_vals, &mask_adr_vals);
  } 
}

/*!------------------------------------------------------------------------
 * \fn     DecodeFormat3_bhw(Word code)
 * \brief  handle Format 3 instructions with 8/16/32 bit operand size
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeFormat3_bhw(Word code)
{
  tAdrVals adr_vals;

  if (ChkArgCnt(1, 1)
   && ChkOpSize(Lo(code), Lo(code) + 2, 0x100 | (Lo(code) + 4), -1)
   && DecodeAdr(&ArgStr[1], &adr_vals, MModMem | MModReg | imm_mask_cond(code)))
  {
    EncodeFormat3(&adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeJMP_JSR(Word code)
 * \brief  handle JMP/JSR instructions
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeJMP_JSR(Word code)
{
  tAdrVals adr_vals;

  if (ChkArgCnt(1, 1)
   && ChkNoAttrPart()
   && DecodeAdr(&ArgStr[1], &adr_vals, MModMem))
  {
    if (IsSPAutoIncDec(&adr_vals))
      WrStrErrorPos(ErrNum_Unpredictable, &ArgStr[1]);
    BAsmCode[CodeLen] = code;
    EncodeFormat3(&adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeFormat3_W(Word code)
 * \brief  handle instructions with single argument and 32 bit operand size
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeFormat3_W(Word code)
{
  tAdrVals adr_vals;

  /* TODO: allow PUSHM/POPM immediate arg as register list similar to 68K? */

  if (ChkArgCnt(1, 1)
   && chk_sup_mode(code)
   && ChkOpSize(-1, -1, 0x100 | Lo(code), -1)
   && DecodeAdr(&ArgStr[1], &adr_vals, MModMem | MModReg | imm_mask_cond(code)))
  {
    EncodeFormat3(&adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeFormat3_H(Word code)
 * \brief  handle instructions with single argument and 16 bit operand size
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeFormat3_H(Word code)
{
  tAdrVals adr_vals;

  if (ChkArgCnt(1, 1)
   && chk_sup_mode(code)
   && ChkOpSize(-1, 0x100 | Lo(code), -1, -1)
   && DecodeAdr(&ArgStr[1], &adr_vals, MModMem | MModReg | imm_mask_cond(code)))
  {
    EncodeFormat3(&adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeFormat3_B(Word code)
 * \brief  handle instructions with single argument and 8 bit operand size
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeFormat3_B(Word code)
{
  tAdrVals adr_vals;

  if (ChkArgCnt(1, 1)
   && chk_sup_mode(code)
   && ChkOpSize(0x100 | Lo(code), -1, -1, -1)
   && DecodeAdr(&ArgStr[1], &adr_vals, MModMem | MModReg | imm_mask_cond(code)))
  {
    EncodeFormat3(&adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeCALL(Word code)
 * \brief  handle CALL instructions
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeCALL(Word code)
{
  tAdrVals target_adr_vals, arg_adr_vals;

  /* TODO: If register direct mode is disallowed for both arguments,
     checking for instruction format I does not make sense? */

  if (ChkArgCnt(2, 2)
   && ChkNoAttrPart()
   && DecodeAdr(&ArgStr[1], &target_adr_vals, MModMem)
   && DecodeAdr(&ArgStr[2], &arg_adr_vals, MModMem))
  {
    if (IsSPAutoIncDec(&target_adr_vals))
      WrStrErrorPos(ErrNum_Unpredictable, &ArgStr[1]);
    if (IsSPAutoIncDec(&arg_adr_vals))
      WrStrErrorPos(ErrNum_Unpredictable, &ArgStr[2]);
    BAsmCode[CodeLen] = Lo(code);
    EncodeFormat1Or2(0x00, &target_adr_vals, &arg_adr_vals);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeCHKA(Word Code)
 * \brief  Decode CHKA instruction(s)
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeCHKA(Word code)
{
  tAdrVals va_adr_vals;

  /* TODO: va is an address operand, so Rn and #imm do not make sense for va? */

  if (ChkArgCnt(2, 2)
   && ChkNoAttrPart()
   && DecodeAdr(&ArgStr[1], &va_adr_vals, MModMem | MModReg | MModImm))
  {
    tAdrVals level_adr_vals;

    OpSize = eSymbolSize8Bit;
    if (DecodeAdr(&ArgStr[2], &level_adr_vals, MModMem | MModReg | MModImm))
    {
      chk_imm_level_range(&ArgStr[1], &level_adr_vals);

      BAsmCode[CodeLen] = Lo(code);
      EncodeFormat1Or2(0x00, &va_adr_vals, &level_adr_vals);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeCAXI(Word Code)
 * \brief  Decode CAXI instruction
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeCAXI(Word code)
{
  tAdrVals reg_adr_vals, mem_adr_vals;

  if (ChkArgCnt(2, 2)
   && ChkOpSize(-1, -1, 0x100 | Lo(code), -1)
   && DecodeAdr(&ArgStr[1], &reg_adr_vals, MModReg)
   && DecodeAdr(&ArgStr[2], &mem_adr_vals, MModMem | MModReg))
    EncodeFormat1(reg_adr_vals.vals[0] & 0x1f, &mem_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeCHLVL(Word Code)
 * \brief  Decode CHLVL instruction
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeCHLVL(Word code)
{
  tAdrVals level_adr_vals, arg_adr_vals;

  if (ChkArgCnt(2, 2)
   && ChkNoAttrPart())
  {
    OpSize = eSymbolSize8Bit;

    if (DecodeAdr(&ArgStr[1], &level_adr_vals, MModMem | MModReg | MModImm)
     && DecodeAdr(&ArgStr[2], &arg_adr_vals, MModMem | MModReg | MModImm))
    {
      chk_imm_level_range(&ArgStr[1], &level_adr_vals);

      BAsmCode[CodeLen] = Lo(code);
      EncodeFormat1Or2(0, &level_adr_vals, &arg_adr_vals);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeGETXTE(Word Code)
 * \brief  Decode GETATE/GETPTE instructions
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeGETXTE(Word code)
{
  tAdrVals va_adr_vals, dst_adr_vals;

  if (ChkArgCnt(2, 2)
   && chk_sup_mode(code)
   && ChkNoAttrPart())
  {
    OpSize = eSymbolSize32Bit;

    if (DecodeAdr(&ArgStr[1], &va_adr_vals, MModMem | MModReg | MModImm)
     && DecodeAdr(&ArgStr[2], &dst_adr_vals, MModMem | MModReg | imm_mask_cond(code)))
    {
      BAsmCode[CodeLen] = Lo(code);
      EncodeFormat1Or2(0, &va_adr_vals, &dst_adr_vals);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeCVT(Word code)
 * \brief  Handle CVT Instruction
 * \param  code machine code (abused as scratch)
 * ------------------------------------------------------------------------ */

static void DecodeCVT(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

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

  if ((AttrPartOpSize[0] == eSymbolSizeFloat32Bit) && (AttrPartOpSize[1] == eSymbolSizeFloat64Bit))
    code = 0x105f;
  else if ((AttrPartOpSize[0] == eSymbolSizeFloat64Bit) && (AttrPartOpSize[1] == eSymbolSizeFloat32Bit))
    code = 0x085f;
  else if ((AttrPartOpSize[0] == eSymbolSize32Bit) && (AttrPartOpSize[1] == eSymbolSizeFloat32Bit))
    code = 0x005f;
  else if ((AttrPartOpSize[0] == eSymbolSize32Bit) && (AttrPartOpSize[1] == eSymbolSizeFloat64Bit))
    code = 0x115f;
  else if ((AttrPartOpSize[0] == eSymbolSizeFloat32Bit) && (AttrPartOpSize[1] == eSymbolSize32Bit))
    code = 0x015f;
  else if ((AttrPartOpSize[0] == eSymbolSizeFloat64Bit) && (AttrPartOpSize[1] == eSymbolSize32Bit))
    code = 0x095f;
  else
  {
    WrStrErrorPos(ErrNum_UndefAttr, &AttrPart);
    return;
  }

  OpSize = AttrPartOpSize[0];
  if (!DecodeAdr(&ArgStr[1], &src_adr_vals, MModReg | MModMem))
    return;
  OpSize = AttrPartOpSize[1];
  if (!DecodeAdr(&ArgStr[2], &dest_adr_vals, MModReg | MModMem))
    return;

  BAsmCode[CodeLen] = Lo(code);
  EncodeFormat2(Hi(code), &src_adr_vals, &dest_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeCVT(Word code)
 * \brief  Handle CVT Instruction
 * \param  code machine code (abused as scratch)
 * ------------------------------------------------------------------------ */

static void DecodeCVTD(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;
  Byte mask;
  Boolean ok;

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

  if ((AttrPartOpSize[0] == eSymbolSize24Bit) && (AttrPartOpSize[1] == eSymbolSizeFloatDec96Bit));
  else if ((AttrPartOpSize[0] == eSymbolSizeFloatDec96Bit) && (AttrPartOpSize[1] == eSymbolSize24Bit))
    code |= 0x0800;
  else
  {
    WrStrErrorPos(ErrNum_UndefAttr, &AttrPart);
    return;
  }

  OpSize = (AttrPartOpSize[0] == eSymbolSize24Bit) ? eSymbolSize8Bit : eSymbolSize16Bit;
  if (!DecodeAdr(&ArgStr[1], &src_adr_vals, MModReg | MModMem | MModImm))
    return;
  OpSize = (AttrPartOpSize[1] == eSymbolSize24Bit) ? eSymbolSize8Bit : eSymbolSize16Bit;
  if (!DecodeAdr(&ArgStr[2], &dest_adr_vals, MModReg | MModMem))
    return;
  mask = EvalStrIntExpressionOffs(&ArgStr[3], ArgStr[3].str.p_str[0] == '#', Int8, &ok);

  BAsmCode[CodeLen] = Lo(code);
  EncodeFormat2(Hi(code), &src_adr_vals, &dest_adr_vals);
  BAsmCode[CodeLen++] = mask;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeMOVS_MOVZ(Word code)
 * \brief  Handle MOVS/MOVZ Instructions
 * \param  code machine code for .bh
 * ------------------------------------------------------------------------ */

static void DecodeMOVS_MOVZ(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

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

  if ((AttrPartOpSize[0] == eSymbolSize8Bit) && (AttrPartOpSize[1] == eSymbolSize16Bit))
    ;
  else if ((AttrPartOpSize[0] == eSymbolSize8Bit) && (AttrPartOpSize[1] == eSymbolSize32Bit))
    code += 0x02;
  else if ((AttrPartOpSize[0] == eSymbolSize16Bit) && (AttrPartOpSize[1] == eSymbolSize32Bit))
    code += 0x12;
  else
  {
    WrStrErrorPos(ErrNum_UndefAttr, &AttrPart);
    return;
  }

  OpSize = AttrPartOpSize[0];
  if (!DecodeAdr(&ArgStr[1], &src_adr_vals, MModReg | MModMem | MModImm))
    return;
  OpSize = AttrPartOpSize[1];
  if (!DecodeAdr(&ArgStr[2], &dest_adr_vals, MModReg | MModMem))
    return;

  BAsmCode[CodeLen] = Lo(code);
  EncodeFormat1Or2(Lo(code), &src_adr_vals, &dest_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeMOVT(Word code)
 * \brief  Handle MOVT Instructions
 * \param  code machine code for .hb
 * ------------------------------------------------------------------------ */

static void DecodeMOVT(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

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

  if ((AttrPartOpSize[0] == eSymbolSize16Bit) && (AttrPartOpSize[1] == eSymbolSize8Bit))
    ;
  else if ((AttrPartOpSize[0] == eSymbolSize32Bit) && (AttrPartOpSize[1] == eSymbolSize8Bit))
    code += 0x10;
  else if ((AttrPartOpSize[0] == eSymbolSize32Bit) && (AttrPartOpSize[1] == eSymbolSize16Bit))
    code += 0x12;
  else
  {
    WrStrErrorPos(ErrNum_UndefAttr, &AttrPart);
    return;
  }

  OpSize = AttrPartOpSize[0];
  if (!DecodeAdr(&ArgStr[1], &src_adr_vals, MModReg | MModMem | MModImm))
    return;
  OpSize = AttrPartOpSize[1];
  if (!DecodeAdr(&ArgStr[2], &dest_adr_vals, MModReg | MModMem))
    return;

  BAsmCode[CodeLen] = Lo(code);
  EncodeFormat1Or2(Lo(code), &src_adr_vals, &dest_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBit(Word code)
 * \brief  decode bit operations
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeBit(Word code)
{
  tAdrVals offset_adr_vals, base_adr_vals;

  if (ChkArgCnt(2, 2)
   && ChkNoAttrPart())
  {
    OpSize = eSymbolSize32Bit;

    if (DecodeAdr(&ArgStr[1], &offset_adr_vals, MModMem | MModReg | MModImm)
     && DecodeAdr(&ArgStr[2], &base_adr_vals, MModMem | MModReg))
    {
      chk_imm_value_range(&ArgStr[1], &offset_adr_vals, "offset", 31);

      BAsmCode[CodeLen] = Lo(code);
      EncodeFormat1Or2(0, &offset_adr_vals, &base_adr_vals);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeIN_OUT(Word code)
 * \brief  handle IN & OUT instructions
 * \param  code machine code for 8 bit opsize; MSB -> is OUT
 * ------------------------------------------------------------------------ */

static void DecodeIN_OUT(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;
  Boolean is_out = !!(code & 1);

  if (ChkArgCnt(2, 2)
   && chk_sup_mode(code)
   && ChkOpSize(Lo(code), Lo(code) + 2, 0x100 | Lo(code + 4), -1)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, is_out ? (MModImm | MModReg | MModMem) : (MModMem | MModIO))
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, is_out ? (MModMem | MModIO) : (MModReg | MModMem)))
    EncodeFormat1Or2(0, &src_adr_vals, &dest_adr_vals);
}


/*!------------------------------------------------------------------------
 * \fn     DecodeArith_B(Word code)
 * \brief  handle format 1/2 instructions with fixed 8 bit size
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeArith_B(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

  if (ChkArgCnt(2, 2)
   && chk_sup_mode(code)
   && ChkOpSize(0x100 | Lo(code), -1, -1, -1)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, MModImm | MModReg | MModMem)
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, imm_mask_cond(code) | MModReg | MModMem))
    EncodeFormat1Or2(0, &src_adr_vals, &dest_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeArith_W(Word code)
 * \brief  handle format 1/2 instructions with fixed 32 bit size
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeArith_W(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

  if (ChkArgCnt(2, 2)
   && chk_sup_mode(code)
   && ChkOpSize(-1, -1, 0x100 | Lo(code), -1)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, MModImm | MModReg | MModMem)
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, imm_mask_cond(code) | MModReg | MModMem))
    EncodeFormat1Or2(0, &src_adr_vals, &dest_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeLDTASK(Word code)
 * \brief  Handle LDTASK Instruction
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void DecodeLDTASK(Word code)
{
  if (ChkArgCnt(2, 2)
   && chk_sup_mode(code)
   && ChkNoAttrPart())
  {
    tAdrVals list_adr_vals, tcb_ptr_adr_vals;

    OpSize = eSymbolSize32Bit;
    if (DecodeAdr(&ArgStr[1], &list_adr_vals, MModImm | MModReg | MModMem)
     && DecodeAdr(&ArgStr[2], &tcb_ptr_adr_vals, MModImm | MModReg | MModMem))
    {
      BAsmCode[CodeLen] = Lo(code);
      EncodeFormat1Or2(0, &list_adr_vals, &tcb_ptr_adr_vals);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeMOVEA(Word code)
 * \brief  Handle MOVEA Instruction
 * \param  code machine code for 8 bit operand size
 * ------------------------------------------------------------------------ */

static void DecodeMOVEA(Word code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

  if (ChkArgCnt(2, 2)
    && ChkOpSize(Lo(code), Lo(code) + 2, 0x100 | (Lo(code) + 4), -1)
    && DecodeAdr(&ArgStr[1], &src_adr_vals, MModMem))
  {
    OpSize = eSymbolSize32Bit;
    if (DecodeAdr(&ArgStr[2], &dest_adr_vals, MModReg | MModMem))
      EncodeFormat1Or2(0, &src_adr_vals, &dest_adr_vals);
  }
}

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

static void DecodeMOV(Word Code)
{
  tAdrVals src_adr_vals, dest_adr_vals;

  UNUSED(Code);

  if (ChkArgCnt(2, 2)
   && ChkOpSize(0x09, 0x1b, 0x12d, 0x3f)
   && DecodeAdr(&ArgStr[1], &src_adr_vals, MModMem | MModImm | MModReg)
   && DecodeAdr(&ArgStr[2], &dest_adr_vals, MModMem | MModReg))
    EncodeFormat1Or2(0x00, &src_adr_vals, &dest_adr_vals);
}

/*!------------------------------------------------------------------------
 * \fn     CodePORT(Word code)
 * \brief  handle PORT statement
 * ------------------------------------------------------------------------ */

static void CodePORT(Word code)
{
  UNUSED(code);
  code_equate_type(SegIO, UInt24);
}

/*--------------------------------------------------------------------------*/
/* Instruction Lookup Table */

/*!------------------------------------------------------------------------
 * \fn     InitFields(void)
 * \brief  create lookup table
 * ------------------------------------------------------------------------ */

static void AddCondition(const char *p_name, Word code)
{
  char name[10];

  order_array_rsv_end(Conditions, tCondition);
  strmaxcpy(Conditions[InstrZ].name, p_name, sizeof(Conditions[InstrZ].name));
  Conditions[InstrZ].code = code;
  InstrZ++;

  if (*p_name)
  {
    as_snprintf(name, sizeof(name), "B%s", p_name);
    AddInstTable(InstTable, name, 0x70 | code, Decode_4);
    as_snprintf(name, sizeof(name), "DB%s", p_name);
    AddInstTable(InstTable, name, 0xc6 | (code & 1) | ((code << 7) & 0x0700), Decode_6);
  }
}

static void InitFields(void)
{
  InstTable = CreateInstTable(201);
  SetDynamicInstTable(InstTable);

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "ABSF"   , 0x0a5c, DecodeArithF);
  AddInstTable(InstTable, "ADDF"   , 0x185c, DecodeArithF);
  AddInstTable(InstTable, "CMPF"   , 0x005c, DecodeArithF);
  AddInstTable(InstTable, "DIVF"   , 0x1b5c, DecodeArithF);
  AddInstTable(InstTable, "MOVF"   , 0x085c, DecodeArithF);
  AddInstTable(InstTable, "MULF"   , 0x1a5c, DecodeArithF);
  AddInstTable(InstTable, "NEGF"   , 0x095c, DecodeArithF);
  AddInstTable(InstTable, "SUBF"   , 0x195c, DecodeArithF);
  AddInstTable(InstTable, "SCLF"   , 0x105c, DecodeSCLF);

  AddInstTable(InstTable, "ADD"    , 0x80, DecodeArith);
  AddInstTable(InstTable, "ADDC"   , 0x90, DecodeArith);
  AddInstTable(InstTable, "AND"    , 0xa0, DecodeArith);
  AddInstTable(InstTable, "CMP"    , CODE_FLAG_OP2_IMM | 0xb8, DecodeArith);
  AddInstTable(InstTable, "DIV"    , 0xa1, DecodeArith);
  AddInstTable(InstTable, "DIVU"   , 0xb1, DecodeArith);
  AddInstTable(InstTable, "MUL"    , 0x81, DecodeArith);
  AddInstTable(InstTable, "MULU"   , 0x91, DecodeArith);
  AddInstTable(InstTable, "NEG"    , 0x39, DecodeArith);
  AddInstTable(InstTable, "NOT"    , 0x38, DecodeArith);
  AddInstTable(InstTable, "OR"     , 0x88, DecodeArith);
  AddInstTable(InstTable, "REM"    , 0x50, DecodeArith);
  AddInstTable(InstTable, "REMU"   , 0x51, DecodeArith);
  AddInstTable(InstTable, "SUB"    , 0xa8, DecodeArith);
  AddInstTable(InstTable, "SUBC"   , 0x98, DecodeArith);
  AddInstTable(InstTable, "XOR"    , 0xb0, DecodeArith);

  AddInstTable(InstTable, "DIVX"   , 0x00a6, DecodeArithX);
  AddInstTable(InstTable, "DIVUX"  , 0x00b6, DecodeArithX);
  AddInstTable(InstTable, "MULX"   , 0x0086, DecodeArithX);
  AddInstTable(InstTable, "MULUX"  , 0x0096, DecodeArithX);

  AddInstTable(InstTable, "ADDDC"  , 0x0059, DecodeArith_7c);
  AddInstTable(InstTable, "SUBDC"  , 0x0159, DecodeArith_7c);
  AddInstTable(InstTable, "SUBRDC" , 0x0259, DecodeArith_7c);

  AddInstTable(InstTable, "ANDBSU" , 0x105b, DecodeBitString_7b);
  AddInstTable(InstTable, "ANDBSD" , 0x115b, DecodeBitString_7b);
  AddInstTable(InstTable, "ANDNBSU", 0x125b, DecodeBitString_7b);
  AddInstTable(InstTable, "ANDNBSD", 0x135b, DecodeBitString_7b);
  AddInstTable(InstTable, "NOTBSU" , 0x0a5b, DecodeBitString_7b);
  AddInstTable(InstTable, "NOTBSD" , 0x0b5b, DecodeBitString_7b);
  AddInstTable(InstTable, "MOVBSU" , 0x085b, DecodeBitString_7b);
  AddInstTable(InstTable, "MOVBSD" , 0x095b, DecodeBitString_7b);
  AddInstTable(InstTable, "ORBSU"  , 0x145b, DecodeBitString_7b);
  AddInstTable(InstTable, "ORBSD"  , 0x155b, DecodeBitString_7b);
  AddInstTable(InstTable, "ORNBSU" , 0x165b, DecodeBitString_7b);
  AddInstTable(InstTable, "ORNBSD" , 0x175b, DecodeBitString_7b);
  AddInstTable(InstTable, "XORBSU" , 0x185b, DecodeBitString_7b);
  AddInstTable(InstTable, "XORBSD" , 0x195b, DecodeBitString_7b);
  AddInstTable(InstTable, "XORNBSU", 0x1a5b, DecodeBitString_7b);
  AddInstTable(InstTable, "XORNBSD", 0x1b5b, DecodeBitString_7b);

  AddInstTable(InstTable, "CMPC",   0x0058, DecodeString_7a);
  AddInstTable(InstTable, "CMPCF",  0x0158, DecodeString_7a);
  AddInstTable(InstTable, "CMPCS",  0x0258, DecodeString_7a);
  AddInstTable(InstTable, "MOVCU",  0x0858, DecodeString_7a);
  AddInstTable(InstTable, "MOVCD",  0x0958, DecodeString_7a);
  AddInstTable(InstTable, "MOVCFU", 0x0a58, DecodeString_7a);
  AddInstTable(InstTable, "MOVCFD", 0x0b58, DecodeString_7a);
  AddInstTable(InstTable, "MOVCS",  0x0c58, DecodeString_7a);
  AddInstTable(InstTable, "SCHCU",  0x1858, DecodeString_7b);
  AddInstTable(InstTable, "SCHCD",  0x1958, DecodeString_7b);
  AddInstTable(InstTable, "SKPCU",  0x1a58, DecodeString_7b);
  AddInstTable(InstTable, "SKPCD",  0x1b58, DecodeString_7b);

  AddInstTable(InstTable, "CMPBFS", CODE_FLAG_OP2_IMM | 0x005d, DecodeBitField_7b);
  AddInstTable(InstTable, "CMPBFZ", CODE_FLAG_OP2_IMM | 0x015d, DecodeBitField_7b);
  AddInstTable(InstTable, "CMPBFL", CODE_FLAG_OP2_IMM | 0x025d, DecodeBitField_7b);
  AddInstTable(InstTable, "SCH0BSU", 0x005b, DecodeBitField_7b);
  AddInstTable(InstTable, "SCH0BSD", 0x015b, DecodeBitField_7b);
  AddInstTable(InstTable, "SCH1BSU", 0x025b, DecodeBitField_7b);
  AddInstTable(InstTable, "SCH1BSD", 0x035b, DecodeBitField_7b);
  AddInstTable(InstTable, "EXTBFS", CODE_FLAG_LIM32 | 0x085d, DecodeBitField_7b);
  AddInstTable(InstTable, "EXTBFZ", CODE_FLAG_LIM32 | 0x095d, DecodeBitField_7b);
  AddInstTable(InstTable, "EXTBFL", CODE_FLAG_LIM32 | 0x0a5d, DecodeBitField_7b);
  AddInstTable(InstTable, "INSBFR", CODE_FLAG_LIM32 | 0x185d, DecodeBitField_7c);
  AddInstTable(InstTable, "INSBFL", CODE_FLAG_LIM32 | 0x195d, DecodeBitField_7c);

  AddInstTable(InstTable, "BRK"    , 0xc8, DecodeFixed);
  AddInstTable(InstTable, "BRKV"   , 0xc9, DecodeFixed);
  AddInstTable(InstTable, "CLRTLBA", CODE_FLAG_SUPMODE | 0x10, DecodeFixed);
  AddInstTable(InstTable, "DISPOSE", 0xcc, DecodeFixed);
  AddInstTable(InstTable, "HALT"   , 0x00, DecodeFixed);
  AddInstTable(InstTable, "NOP"    , 0xcd, DecodeFixed);
  AddInstTable(InstTable, "RSR"    , 0xca, DecodeFixed);
  AddInstTable(InstTable, "TRAPFL" , 0xcb, DecodeFixed);

  AddInstTable(InstTable, "MOV"    , 0x00, DecodeMOV);

  InstrZ = 0;
  AddCondition("GT", 0xf);
  AddCondition("GE", 0xd);
  AddCondition("LT", 0xc);
  AddCondition("LE", 0xe);
  AddCondition("H" , 0x7);
  AddCondition("NL", 0x3);
  AddCondition("L" , 0x2);
  AddCondition("NH", 0x6);
  AddCondition("E" , 0x4);
  AddCondition("NE", 0x5);
  AddCondition("V" , 0x0);
  AddCondition("NV", 0x1);
  AddCondition("N" , 0x8);
  AddCondition("P" , 0x9);
  AddCondition("C" , 0x2);
  AddCondition("NC", 0x3);
  AddCondition("Z" , 0x4);
  AddCondition("NZ", 0x5);
  AddCondition(""  , 0);
  AddInstTable(InstTable, "BR", 0x7a, Decode_4);
  AddInstTable(InstTable, "DBR", 0x05c6, Decode_6);
  AddInstTable(InstTable, "TB", 0x05c7, Decode_6);
  AddInstTable(InstTable, "BSR", 0x48, Decode_4);

  AddInstTable(InstTable, "CLRTLB", CODE_FLAG_SUPMODE | 0xfe, DecodeCLRTLB);
  AddInstTable(InstTable, "GETPSW", 0xf6, DecodeGETPSW);
  AddInstTable(InstTable, "UPDPSW", 0x134a, DecodeUPDPSW);
  AddInstTable(InstTable, "DEC", 0xd0, DecodeFormat3_bhw);
  AddInstTable(InstTable, "INC", 0xd8, DecodeFormat3_bhw);
  AddInstTable(InstTable, "TEST", 0xf0 | CODE_FLAG_OP2_IMM, DecodeFormat3_bhw);
  AddInstTable(InstTable, "JMP", 0xd6, DecodeJMP_JSR);
  AddInstTable(InstTable, "JSR", 0xe8, DecodeJMP_JSR);
  AddInstTable(InstTable, "POP", 0xe6, DecodeFormat3_W);
  AddInstTable(InstTable, "PUSH", CODE_FLAG_OP2_IMM | 0xee, DecodeFormat3_W);
  AddInstTable(InstTable, "POPM", CODE_FLAG_OP2_IMM | 0xe4, DecodeFormat3_W);
  AddInstTable(InstTable, "PUSHM", CODE_FLAG_OP2_IMM | 0xec, DecodeFormat3_W);
  AddInstTable(InstTable, "PREPARE", CODE_FLAG_OP2_IMM | 0xde, DecodeFormat3_W);
  AddInstTable(InstTable, "STTASK", CODE_FLAG_OP2_IMM | CODE_FLAG_SUPMODE | 0xfc, DecodeFormat3_W);
  AddInstTable(InstTable, "RET", CODE_FLAG_OP2_IMM | 0xe2, DecodeFormat3_H);
  AddInstTable(InstTable, "RETIS", CODE_FLAG_OP2_IMM | CODE_FLAG_SUPMODE | 0xfa, DecodeFormat3_H);
  AddInstTable(InstTable, "RETIU", CODE_FLAG_OP2_IMM | CODE_FLAG_SUPMODE | 0xea, DecodeFormat3_H);
  AddInstTable(InstTable, "TASI", 0xe0, DecodeFormat3_B);
  AddInstTable(InstTable, "TRAP", CODE_FLAG_OP2_IMM | 0xf8, DecodeFormat3_B);
  AddInstTable(InstTable, "CALL", 0x49, DecodeCALL);
  AddInstTable(InstTable, "ROT",  0x89, DecodeShift);
  AddInstTable(InstTable, "ROTC", 0x99, DecodeShift);
  AddInstTable(InstTable, "SHA",  0xb9, DecodeShift);
  AddInstTable(InstTable, "SHL",  0xa9, DecodeShift);
  AddInstTable(InstTable, "CHKAR", 0x4d, DecodeCHKA);
  AddInstTable(InstTable, "CHKAW", 0x4e, DecodeCHKA);
  AddInstTable(InstTable, "CHKAE", 0x4f, DecodeCHKA);
  AddInstTable(InstTable, "CAXI", 0x4c, DecodeCAXI);
  AddInstTable(InstTable, "CHLVL", 0x4b, DecodeCHLVL);
  AddInstTable(InstTable, "GETATE", CODE_FLAG_SUPMODE | 0x05, DecodeGETXTE);
  AddInstTable(InstTable, "UPDATE", CODE_FLAG_SUPMODE | 0x15, DecodeGETXTE);
  AddInstTable(InstTable, "GETPTE", CODE_FLAG_SUPMODE | 0x04, DecodeGETXTE);
  AddInstTable(InstTable, "UPDPTE", CODE_FLAG_OP2_IMM | CODE_FLAG_SUPMODE | 0x14, DecodeGETXTE);
  AddInstTable(InstTable, "GETRA", CODE_FLAG_SUPMODE | 0x03, DecodeGETXTE);
  AddInstTable(InstTable, "CLR1", 0xa7, DecodeBit);
  AddInstTable(InstTable, "NOT1", 0xb7, DecodeBit);
  AddInstTable(InstTable, "SET1", 0x97, DecodeBit);
  AddInstTable(InstTable, "TEST1", 0x87, DecodeBit);
  AddInstTable(InstTable, "CVT", 0, DecodeCVT);
  AddInstTable(InstTable, "CVTD", 0x1059, DecodeCVTD);
  AddInstTable(InstTable, "MOVS", 0x0a, DecodeMOVS_MOVZ);
  AddInstTable(InstTable, "MOVT", 0x19, DecodeMOVT);
  AddInstTable(InstTable, "MOVZ", 0x0b, DecodeMOVS_MOVZ);
  AddInstTable(InstTable, "IN" , CODE_FLAG_SUPMODE | 0x0020, DecodeIN_OUT);
  AddInstTable(InstTable, "OUT", CODE_FLAG_SUPMODE | 0x0021, DecodeIN_OUT);
  AddInstTable(InstTable, "RVBIT", 0x08, DecodeArith_B);
  AddInstTable(InstTable, "RVBYTE", 0x2c, DecodeArith_W);
  AddInstTable(InstTable, "LDPR", CODE_FLAG_SUPMODE | CODE_FLAG_OP2_IMM | 0x12, DecodeArith_W);
  AddInstTable(InstTable, "STPR", CODE_FLAG_SUPMODE | 0x02, DecodeArith_W);
  AddInstTable(InstTable, "LDTASK", CODE_FLAG_SUPMODE  | 0x01, DecodeLDTASK);
  AddInstTable(InstTable, "MOVEA", 0x40, DecodeMOVEA);
  AddInstTable(InstTable, "XCH", 0x41, DecodeXCH);
  AddInstTable(InstTable, "SETF", 0x47, DecodeSETF);

  AddInstTable(InstTable, "REG", 0, CodeREG);
  AddInstTable(InstTable, "PORT", 0, CodePORT);
  AddMoto16Pseudo(InstTable, e_moto_pseudo_flags_le);
}

/*!------------------------------------------------------------------------
 * \fn     DeinitFields(void)
 * \brief  destroy/cleanup lookup table
 * ------------------------------------------------------------------------ */

static void DeinitFields(void)
{
  order_array_free(Conditions);
  DestroyInstTable(InstTable);
}

/*--------------------------------------------------------------------------*/
/* Interface Functions */

/*!------------------------------------------------------------------------
 * \fn     InternSymbol_V60(char *pArg, TempResult *pResult)
 * \brief  handle built-in (register) symbols for V60
 * \param  pArg source argument
 * \param  pResult result buffer
 * ------------------------------------------------------------------------ */

static void InternSymbol_V60(char *pArg, TempResult *pResult)
{
  Byte RegNum;

  if (DecodeRegCore(pArg, &RegNum))
  {
    pResult->Typ = TempReg;
    pResult->DataSize = eSymbolSize32Bit;
    pResult->Contents.RegDescr.Reg = RegNum;
    pResult->Contents.RegDescr.Dissect = DissectReg_V60;
    pResult->Contents.RegDescr.compare = NULL;
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeAttrPart_V60(void)
 * \brief  handle/decode attribute
 * \return True if success
 * ------------------------------------------------------------------------ */

static Boolean DecodeAttrPart_V60(void)
{
  int l = strlen(AttrPart.str.p_str), z;

  if (l > 2)
  {
badattr:
    WrStrErrorPos(ErrNum_UndefAttr, &AttrPart);
    return False;
  }

  for (z = 0; z < l; z++)
    switch (as_toupper(AttrPart.str.p_str[z]))
    {
      case 'B': AttrPartOpSize[z] = eSymbolSize8Bit; break;
      case 'H': AttrPartOpSize[z] = eSymbolSize16Bit; break;
      case 'W': AttrPartOpSize[z] = eSymbolSize32Bit; break;
      case 'D': AttrPartOpSize[z] = eSymbolSize64Bit; break;
      case 'P': AttrPartOpSize[z] = eSymbolSize24Bit; break;
      case 'S': AttrPartOpSize[z] = eSymbolSizeFloat32Bit; break;
      case 'L': AttrPartOpSize[z] = eSymbolSizeFloat64Bit; break;
      case 'Z': AttrPartOpSize[z] = eSymbolSizeFloatDec96Bit; break; /* just for cvtd.pz/zp */
      case '\0': break;
      default:
        goto badattr;
    }
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     MakeCode_V60(void)
 * \brief  encode machine instruction
 * ------------------------------------------------------------------------ */

static void MakeCode_V60(void)
{
  OpSize = eSymbolSizeUnknown;

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

/*!------------------------------------------------------------------------
 * \fn     IsDef_V60(void)
 * \brief  check whether insn makes own use of label
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean IsDef_V60(void)
{
  return Memo("REG")
      || Memo("PORT");
}

/*!------------------------------------------------------------------------
 * \fn     SwitchFrom_V60(void)
 * \brief  deinitialize as target
 * ------------------------------------------------------------------------ */

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

/*!------------------------------------------------------------------------
 * \fn     SwitchTo_V60(void *pUser)
 * \brief  prepare to assemble code for this target
 * \param  pUser CPU properties
 * ------------------------------------------------------------------------ */

static void SwitchTo_V60(void)
{
  const TFamilyDescr *pDescr = FindFamilyByName("V60");

  TurnWords = True;
  SetIntConstMode(eIntConstModeIntel);

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

  ValidSegs = (1 << SegCode) | (1 << SegIO);
  Grans[SegCode] = Grans[SegIO] = 1;
  ListGrans[SegCode] = ListGrans[SegIO] = 1;
  SegInits[SegCode] = SegInits[SegIO] = 0;
  SegLimits[SegCode] = 0xfffffffful;
  SegLimits[SegIO] = 0xfffffful;

  DecodeAttrPart = DecodeAttrPart_V60;
  MakeCode = MakeCode_V60;
  IsDef = IsDef_V60;
  SwitchFrom = SwitchFrom_V60;
  InternSymbol = InternSymbol_V60;
  DissectReg = DissectReg_V60;
  AddMoto16PseudoONOFF(False);

  onoff_supmode_add();

  InitFields();
}

/*!------------------------------------------------------------------------
 * \fn     codev60_init(void)
 * \brief  register target to AS
 * ------------------------------------------------------------------------ */

void codev60_init(void)
{
  (void)AddCPU("70616", SwitchTo_V60);
}
