/* code166.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* AS-Codegenerator Siemens 80C16x                                           */
/*                                                                           */
/*****************************************************************************/

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

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

#include "code166.h"

typedef struct
{
  CPUVar MinCPU;
  Word Code1, Code2;
} BaseOrder;

typedef struct
{
  const char *Name;
  Byte Code;
} Condition;

#define COND_CODE_TRUE 0x0

#define DPPCount 4
static const char RegNames[6][5] = { "DPP0", "DPP1", "DPP2", "DPP3", "CP", "SP" };

static CPUVar CPU80C166, CPU80C167, CPU80C167CS;

static BaseOrder *FixedOrders;
static Condition *Conditions;
static int TrueCond;

static LongInt DPPAssumes[DPPCount];
static IntType MemInt, MemInt2;
static tSymbolSize OpSize;

static Boolean DPPChanged[DPPCount], N_DPPChanged[DPPCount];
static Boolean SPChanged, CPChanged, N_SPChanged, N_CPChanged;

static ShortInt ExtCounter;
static enum
{
  MemModeStd,       /* normal */
  MemModeNoCheck,   /* EXTS Rn */
  MemModeZeroPage,  /* EXTP Rn */
  MemModeFixedBank, /* EXTS nn */
  MemModeFixedPage  /* EXTP nn */
} MemMode;
static Word MemPage;
static Boolean ExtSFRs;

static as_assume_rec_t ASSUME166s[] =
{
  { "DPP0", DPPAssumes + 0, 0, 15, -1, NULL },
  { "DPP1", DPPAssumes + 1, 0, 15, -1, NULL },
  { "DPP2", DPPAssumes + 2, 0, 15, -1, NULL },
  { "DPP3", DPPAssumes + 3, 0, 15, -1, NULL }
};

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

enum
{
  ModNone = -1,
  ModReg = 0,
  ModImm = 1,
  ModIReg = 2,
  ModPreDec = 3,
  ModPostInc = 4,
  ModIndex = 5,
  ModAbs = 6,
  ModMReg = 7,
  ModLAbs = 8
};

typedef enum
{
  eForceNone = 0,
  eForceShort = 1,
  eForceLong = 2
} tForceSize;

#define MModReg (1 << ModReg)
#define MModImm (1 << ModImm)
#define MModIReg (1 << ModIReg)
#define MModPreDec (1 << ModPreDec)
#define MModPostInc (1 << ModPostInc)
#define MModIndex (1 << ModIndex)
#define MModAbs (1 << ModAbs)
#define MModMReg (1 << ModMReg)
#define MModLAbs (1 << ModLAbs)

#define M_InCode (1 << 14)
#define M_Dest (1 << 13)

typedef struct
{
  Byte Mode;
  Byte Vals[2];
  ShortInt Type;
  tSymbolFlags SymFlags;
  tForceSize ForceSize;
  int Cnt;
} tAdrResult;

/*!------------------------------------------------------------------------
 * \fn     IsRegCore(const char *pArg, tRegInt *pValue, tSymbolSize *pSize)
 * \brief  check whether argument describes a CPU (general purpose) register
 * \param  pArg argument
 * \param  pValue resulting register # if yes
 * \param  pSize resulting register size if yes
 * \return true if yes
 * ------------------------------------------------------------------------ */

static Boolean IsRegCore(const char *pArg, tRegInt *pValue, tSymbolSize *pSize)
{
  int l = strlen(pArg);
  char *p_end;

  if ((l < 2) || (as_toupper(*pArg) != 'R'))
    return False;
  else if ((l > 2) && (as_toupper(pArg[1]) == 'L'))
  {
    *pValue = strtoul(pArg + 2, &p_end, 10) << 1;
    *pSize = eSymbolSize8Bit;
    return (!*p_end && (*pValue <= 15));
  }
  else if ((l > 2) && (as_toupper(pArg[1]) == 'H'))
  {
    *pValue = (strtoul(pArg + 2, &p_end, 10) << 1) + 1;
    *pSize = eSymbolSize8Bit;
    return (!*p_end && (*pValue <= 15));
  }
  else
  {
    *pValue = strtoul(pArg + 1, &p_end, 10);
    *pSize = eSymbolSize16Bit;
    return (!*p_end && (*pValue <= 15));
  }
}

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

static void DissectReg_166(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
{
  switch (InpSize)
  {
    case eSymbolSize8Bit:
      as_snprintf(pDest, DestSize, "R%c%u", Value & 1 ? 'H' : 'L', (unsigned)(Value >> 1));
      break;
    case eSymbolSize16Bit:
      as_snprintf(pDest, DestSize, "R%u", (unsigned)Value);
      break;
    default:
      as_snprintf(pDest, DestSize, "%d-%u", (int)InpSize, (unsigned)Value);
  }
}

/*!------------------------------------------------------------------------
 * \fn     IsReg(const tStrComp *pArg, Byte *pValue, tSymbolSize *pSize, tSymbolSize ReqSize, Boolean MustBeReg)
 * \brief  check whether argument is a CPU register or user-defined register alias
 * \param  pArg argument
 * \param  pValue resulting register # if yes
 * \param  pSize resulting register size if yes
 * \param  ReqSize requested register size
 * \param  MustBeReg expecting register or maybe not?
 * \return reg eval result
 * ------------------------------------------------------------------------ */

/* NOTE: If requester register size is 8 bits, R0..R15 is allowed as
   alias for R0L,R0H,R1L,...,R7H: */

static Boolean ChkRegSize(tSymbolSize ReqSize, tSymbolSize ActSize)
{
  return (ReqSize == eSymbolSizeUnknown)
      || (ReqSize == ActSize)
      || ((ReqSize == eSymbolSize8Bit) && (ActSize == eSymbolSize16Bit));
}

static tRegEvalResult IsReg(const tStrComp *pArg, Byte *pValue, tSymbolSize *pSize, tSymbolSize ReqSize, Boolean MustBeReg)
{
  tRegDescr RegDescr;
  tEvalResult EvalResult;
  tRegEvalResult RegEvalResult;

  if (IsRegCore(pArg->str.p_str, &RegDescr.Reg, &EvalResult.DataSize))
    RegEvalResult = eIsReg;
  else
    RegEvalResult = EvalStrRegExpressionAsOperand(pArg, &RegDescr, &EvalResult, eSymbolSizeUnknown, MustBeReg);

  if (RegEvalResult == eIsReg)
  {
    if (!ChkRegSize(ReqSize, EvalResult.DataSize))
    {
      WrStrErrorPos(ErrNum_InvOpSize, pArg);
      RegEvalResult = MustBeReg ? eIsNoReg : eRegAbort;
    }
  }

  *pValue = RegDescr.Reg;
  if (pSize) *pSize = EvalResult.DataSize;
  return RegEvalResult;
}

static tRegEvalResult IsRegM1(const tStrComp *pArg, Byte *pValue, tSymbolSize ReqSize, Boolean MustBeReg)
{
  if (*pArg->str.p_str)
  {
    int l;
    char tmp = pArg->str.p_str[l = (strlen(pArg->str.p_str) - 1)];
    tRegEvalResult b;

    pArg->str.p_str[l] = '\0';
    b = IsReg(pArg, pValue, NULL, ReqSize, MustBeReg);
    pArg->str.p_str[l] = tmp;
    return b;
  }
  else
    return eIsNoReg;
}

static tRegEvalResult IsRegP1(const tStrComp *pArg, Byte *pValue, tSymbolSize ReqSize, Boolean MustBeReg)
{
  tStrComp Arg;

  StrCompRefRight(&Arg, pArg, 1);
  return IsReg(&Arg, pValue, NULL, ReqSize, MustBeReg);
}

static LongInt SFRStart(void)
{
  return (ExtSFRs) ? 0xf000 : 0xfe00;
}

static LongInt SFREnd(void)
{
  return (ExtSFRs) ? 0xf1de : 0xffde;
}

static Boolean CalcPage(LongInt *Adr, Boolean DoAnyway)
{
  int z;
  Word Bank;

  switch (MemMode)
  {
    case MemModeStd:
      z = 0;
      while ((z <= 3) && (((*Adr) >> 14) != DPPAssumes[z]))
        z++;
      if (z > 3)
      {
        WrError(ErrNum_InAccPage);
        (*Adr) &= 0xffff;
        return DoAnyway;
      }
      else
      {
        *Adr = ((*Adr) & 0x3fff) + (z << 14);
        if (DPPChanged[z])
          WrXError(ErrNum_Pipeline, RegNames[z]);
        return True;
      }
    case MemModeZeroPage:
      (*Adr) &= 0x3fff;
      return True;
    case MemModeFixedPage:
      Bank = (*Adr) >> 14;
      (*Adr) &= 0x3fff;
      if (Bank != MemPage)
      {
        WrError(ErrNum_InAccPage);
        return (DoAnyway);
      }
      else
        return True;
    case MemModeNoCheck:
      (*Adr) &= 0xffff;
      return True;
    case MemModeFixedBank:
      Bank = (*Adr) >> 16; (*Adr) &= 0xffff;
      if (Bank != MemPage)
      {
        WrError(ErrNum_InAccPage);
        return (DoAnyway);
      }
      else
        return True;
    default:
      return False;
  }
}

static void DecideAbsolute(LongInt DispAcc, Word Mask, tAdrResult *pResult)
{
#define DPPAdr 0xfe00
#define SPAdr 0xfe12
#define CPAdr 0xfe10

  int z;

  if (Mask & M_InCode)
  {
    if ((HiWord(EProgCounter()) == HiWord(DispAcc)) && (Mask & MModAbs))
    {
      pResult->Type = ModAbs;
      pResult->Cnt = 2;
      pResult->Vals[0] = Lo(DispAcc);
      pResult->Vals[1] = Hi(DispAcc);
    }
    else
    {
      pResult->Type = ModLAbs;
      pResult->Cnt = 2;
      pResult->Mode = DispAcc >> 16;
      pResult->Vals[0] = Lo(DispAcc);
      pResult->Vals[1] = Hi(DispAcc);
    }
  }
  else if (((Mask & MModMReg) != 0) && (DispAcc >= SFRStart()) && (DispAcc <= SFREnd()) && (!(DispAcc & 1)))
  {
    pResult->Type = ModMReg;
    pResult->Cnt = 1;
    pResult->Vals[0] = (DispAcc - SFRStart()) >> 1;
  }
  else switch (MemMode)
  {
    case MemModeStd:
      z = 0;
      while ((z <= 3) && ((DispAcc >> 14) != DPPAssumes[z]))
        z++;
      if (z > 3)
      {
        WrError(ErrNum_InAccPage);
        z = (DispAcc >> 14) & 3;
      }
      pResult->Type = ModAbs;
      pResult->Cnt = 2;
      pResult->Vals[0] = Lo(DispAcc);
      pResult->Vals[1] = (Hi(DispAcc) & 0x3f) + (z << 6);
      if (DPPChanged[z])
        WrXError(ErrNum_Pipeline, RegNames[z]);
      break;
    case MemModeZeroPage:
      pResult->Type = ModAbs;
      pResult->Cnt = 2;
      pResult->Vals[0] = Lo(DispAcc);
      pResult->Vals[1] = Hi(DispAcc) & 0x3f;
      break;
    case MemModeFixedPage:
      if ((DispAcc >> 14) != MemPage)
        WrError(ErrNum_InAccPage);
      pResult->Type = ModAbs;
      pResult->Cnt = 2;
      pResult->Vals[0] = Lo(DispAcc);
      pResult->Vals[1] = Hi(DispAcc) & 0x3f;
      break;
    case MemModeNoCheck:
      pResult->Type = ModAbs;
      pResult->Cnt = 2;
      pResult->Vals[0] = Lo(DispAcc);
      pResult->Vals[1] = Hi(DispAcc);
      break;
    case MemModeFixedBank:
      if ((DispAcc >> 16) != MemPage)
        WrError(ErrNum_InAccPage);
      pResult->Type = ModAbs;
      pResult->Cnt = 2;
      pResult->Vals[0] = Lo(DispAcc);
      pResult->Vals[1] = Hi(DispAcc);
      break;
  }

  if ((pResult->Type != ModNone) && (Mask & M_Dest))
  {
    switch ((Word)DispAcc)
    {
      case SPAdr    : N_SPChanged = True; break;
      case CPAdr    : N_CPChanged = True; break;
      case DPPAdr   :
      case DPPAdr + 1 : N_DPPChanged[0] = True; break;
      case DPPAdr + 2 :
      case DPPAdr + 3 : N_DPPChanged[1] = True; break;
      case DPPAdr + 4 :
      case DPPAdr + 5 : N_DPPChanged[2] = True; break;
      case DPPAdr + 6 :
      case DPPAdr + 7 : N_DPPChanged[3] = True; break;
    }
  }
}

static int SplitForceSize(const char *pArg, tForceSize *pForceSize)
{
  switch (*pArg)
  {
    case '>': *pForceSize = eForceLong; return 1;
    case '<': *pForceSize = eForceShort; return 1;
    default: return 0;
  }
}

typedef struct
{
  as_eval_cb_data_t cb_data;
  tAdrResult *p_result;
} s166_eval_cb_data_t;

DECLARE_AS_EVAL_CB(s166_eval_cb)
{
  s166_eval_cb_data_t *p_s166_eval_cb_data = (s166_eval_cb_data_t*)p_data;
  Byte reg;

  switch (IsReg(p_arg, &reg, NULL, eSymbolSize16Bit, False))
  {
    case eIsNoReg:
      return e_eval_none;
    case eIsReg:
      if ((p_s166_eval_cb_data->p_result->Mode != 0xff)
       || !as_eval_cb_data_stack_plain_add(p_data->p_stack))
      {
        WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
        return e_eval_fail;
      }
      p_s166_eval_cb_data->p_result->Mode = reg;
      as_tempres_set_int(p_res, 0);
      return e_eval_ok;
    default:
      return e_eval_fail;
  }
}

static ShortInt DecodeAdr(const tStrComp *pArg, Word Mask, tAdrResult *pResult)
{
  LongInt HDisp, DispAcc;
  Boolean OK;
  int Offs;
  tRegEvalResult RegEvalResult;

  pResult->Type = ModNone;
  pResult->Cnt = 0;
  pResult->ForceSize = eForceNone;

  /* immediate ? */

  if (*pArg->str.p_str == '#')
  {
    Offs = SplitForceSize(pArg->str.p_str + 1, &pResult->ForceSize);
    switch (OpSize)
    {
      case eSymbolSize8Bit:
        pResult->Vals[0] = EvalStrIntExpressionOffsWithFlags(pArg, 1 + Offs, Int8, &OK, &pResult->SymFlags);
        pResult->Vals[1] = 0;
        break;
      case eSymbolSize16Bit:
        HDisp = EvalStrIntExpressionOffsWithFlags(pArg, 1 + Offs, Int16, &OK, &pResult->SymFlags);
        pResult->Vals[0] = Lo(HDisp);
        pResult->Vals[1] = Hi(HDisp);
        break;
      default:
        OK = False;
        break;
    }
    if (OK)
    {
      pResult->Type = ModImm;
      AdrCnt = OpSize + 1;
    }
  }

  /* Register ? */

  else if ((RegEvalResult = IsReg(pArg, &pResult->Mode, NULL, OpSize, False)) != eIsNoReg)
  {
    if (RegEvalResult == eRegAbort)
      return pResult->Type;
    if ((Mask & MModReg) != 0)
      pResult->Type = ModReg;
    else
    {
      pResult->Type = ModMReg;
      pResult->Vals[0] = 0xf0 + pResult->Mode;
      AdrCnt = 1;
    }
    if (CPChanged)
      WrXError(ErrNum_Pipeline, RegNames[4]);
  }

  /* indirekt ? */

  else if ((*pArg->str.p_str == '[') && (pArg->str.p_str[strlen(pArg->str.p_str) - 1] == ']'))
  {
    tStrComp Arg;
    int ArgLen;

    StrCompRefRight(&Arg, pArg, 1);
    StrCompShorten(&Arg, 1);
    KillPrefBlanksStrCompRef(&Arg);
    KillPostBlanksStrComp(&Arg);
    ArgLen = strlen(Arg.str.p_str);

    /* Predekrement ? */

    if ((ArgLen > 2) && (*Arg.str.p_str == '-') && ((RegEvalResult = IsRegP1(&Arg, &pResult->Mode, eSymbolSize16Bit, False)) != eIsNoReg))
    {
      if (eRegAbort == RegEvalResult)
        return pResult->Type;
      pResult->Type = ModPreDec;
    }

    /* Postinkrement ? */

    else if ((ArgLen > 2) && (Arg.str.p_str[ArgLen - 1] == '+') && ((RegEvalResult = IsRegM1(&Arg, &pResult->Mode, eSymbolSize16Bit, False)) != eIsNoReg))
    {
      if (eRegAbort == RegEvalResult)
        return pResult->Type;
      pResult->Type = ModPostInc;
    }

    /* indiziert ? */

    else
    {
      s166_eval_cb_data_t s166_eval_cb_data;
      tEvalResult eval_result;

      as_eval_cb_data_ini(&s166_eval_cb_data.cb_data, s166_eval_cb);
      pResult->Mode = 0xff;
      s166_eval_cb_data.p_result = pResult;
      DispAcc = EvalStrIntExprWithResultAndCallback(&Arg, Int16, &eval_result, &s166_eval_cb_data.cb_data);
      if (!eval_result.OK)
        return pResult->Type;

      if (pResult->Mode == 0xff)
        DecideAbsolute(DispAcc, Mask, pResult);
      else if (DispAcc == 0)
        pResult->Type = ModIReg;
      else if (DispAcc > 0xffff)
        WrError(ErrNum_OverRange);
      else if (DispAcc < -0x8000l)
        WrError(ErrNum_UnderRange);
      else
      {
        pResult->Vals[0] = Lo(DispAcc);
        pResult->Vals[1] = Hi(DispAcc);
        pResult->Type = ModIndex;
        pResult->Cnt = 2;
      }
    }
  }
  else
  {
    int Offset = SplitForceSize(pArg->str.p_str, &pResult->ForceSize);

    DispAcc = EvalStrIntExpressionOffsWithFlags(pArg, Offset, MemInt, &OK, &pResult->SymFlags);
    if (OK)
      DecideAbsolute(DispAcc, Mask, pResult);
  }

  if ((pResult->Type != ModNone) && (!((1 << pResult->Type) & Mask)))
  {
    WrError(ErrNum_InvAddrMode);
    pResult->Type = ModNone;
    pResult->Cnt = 0;
  }
  return pResult->Type;
}

static Boolean DecodeCondition(const char *Name, Byte *p_cond_code)
{
  int z;

  for (z = 0; Conditions[z].Name; z++)
    if (!as_strcasecmp(Conditions[z].Name, Name))
    {
      *p_cond_code = Conditions[z].Code;
      return True;
    }
  return False;
}

static Boolean DecodeBitAddr(const tStrComp *pArg, Word *Adr, Byte *Bit, Boolean MayBeOut)
{
  char *p;
  Word LAdr;
  Byte Reg;
  Boolean OK;

  p = QuotPos(pArg->str.p_str, '.');
  if (!p)
  {
    LAdr = EvalStrIntExpression(pArg, UInt16, &OK) & 0x1fff;
    if (OK)
    {
      if ((!MayBeOut) && ((LAdr >> 12) != Ord(ExtSFRs)))
      {
        WrError(ErrNum_InAccReg);
        return False;
      }
      *Adr = LAdr >> 4;
      *Bit = LAdr & 15;
      if (!MayBeOut)
        *Adr = Lo(*Adr);
      return True;
    }
    else return False;
  }
  else if (p == pArg->str.p_str)
  {
    WrError(ErrNum_InvAddrMode);
    return False;
  }
  else
  {
    tStrComp AddrComp, BitComp;

    StrCompSplitRef(&AddrComp, &BitComp, pArg, p);

    switch (IsReg(&AddrComp, &Reg, NULL, eSymbolSize16Bit, False))
    {
      case eIsReg:
        *Adr = 0xf0 + Reg;
        break;
      case eRegAbort:
        return False;
      case eIsNoReg:
      {
        tSymbolFlags Flags;

        LAdr = EvalStrIntExpressionWithFlags(&AddrComp, UInt16, &OK, &Flags);
        if (!OK)
          return False;
        if (mFirstPassUnknown(Flags))
          LAdr = 0xfd00;

        /* full addresses must be even, since bitfields in memory are 16 bit: */

        if ((LAdr > 0xff) && (LAdr & 1))
        {
          WrStrErrorPos(ErrNum_NotAligned, &AddrComp);
          return False;
        }

        /* coded bit address: */

        if (LAdr <= 0xff)
          *Adr = LAdr;

        /* 1st RAM bank: */

        else if ((LAdr >= 0xfd00) && (LAdr <= 0xfdfe))
          *Adr = (LAdr - 0xfd00)/2;

        /* SFR space: */

        else if ((LAdr >= 0xff00) && (LAdr <= 0xffde))
        {
          if ((ExtSFRs) && (!MayBeOut))
          {
            WrStrErrorPos(ErrNum_InAccReg, &AddrComp);
            return False;
          }
          *Adr = 0x80 + ((LAdr - 0xff00) / 2);
        }

        /* extended SFR space: */

        else if ((LAdr >= 0xf100) && (LAdr <= 0xf1de))
        {
          if ((!ExtSFRs) && (!MayBeOut))
          {
            WrStrErrorPos(ErrNum_InAccReg, &AddrComp);
            return False;
          }
          *Adr = 0x80 + ((LAdr - 0xf100) / 2);
          if (MayBeOut)
            (*Adr) += 0x100;
        }
        else
        {
          WrStrErrorPos(ErrNum_OverRange, &AddrComp);
          return False;
        }
      }
    }

    *Bit = EvalStrIntExpression(&BitComp, UInt4, &OK);
    return OK;
  }
}

static Word WordVal(const tAdrResult *pResult)
{
  return pResult->Vals[0] + (((Word)pResult->Vals[1]) << 8);
}

static Boolean DecodePref(const tStrComp *pArg, Byte *Erg)
{
  Boolean OK;
  tSymbolFlags Flags;

  if (*pArg->str.p_str != '#')
  {
    WrError(ErrNum_InvAddrMode);
    return False;
  }
  *Erg = EvalStrIntExpressionOffsWithFlags(pArg, 1, UInt3, &OK, &Flags);
  if (mFirstPassUnknown(Flags))
    *Erg = 1;
  if (!OK)
    return False;
  if (*Erg < 1)
    WrError(ErrNum_UnderRange);
  else if (*Erg > 4)
    WrError(ErrNum_OverRange);
  else
  {
    (*Erg)--;
    return True;
  }
  return False;
}

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

static void DecodeFixed(Word Index)
{
  const BaseOrder *pOrder = FixedOrders + Index;

  if (ChkArgCnt(0, 0))
  {
    CodeLen = 2;
    BAsmCode[0] = Lo(pOrder->Code1);
    BAsmCode[1] = Hi(pOrder->Code1);
    if (pOrder->Code2 != 0)
    {
      CodeLen = 4;
      BAsmCode[2] = Lo(pOrder->Code2);
      BAsmCode[3] = Hi(pOrder->Code2);
      if ((!strncmp(OpPart.str.p_str, "RET", 3)) && (SPChanged))
        WrXError(ErrNum_Pipeline, RegNames[5]);
    }
  }
}

static void DecodeMOV(Word Code)
{
  LongInt AdrLong;

  OpSize = (tSymbolSize)Hi(Code);
  Code = 1 - OpSize;

  if (ChkArgCnt(2, 2))
  {
    tAdrResult DestResult;

    switch (DecodeAdr(&ArgStr[1], MModReg | MModMReg | MModIReg | MModPreDec | MModPostInc | MModIndex | MModAbs | M_Dest, &DestResult))
    {
      case ModReg:
      {
        tAdrResult SrcResult;

        switch (DecodeAdr(&ArgStr[2], MModReg | MModImm | MModIReg | MModPostInc | MModIndex | MModAbs, &SrcResult))
        {
          case ModReg:
            CodeLen = 2;
             BAsmCode[0] = 0xf0 + Code;
            BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Mode;
            break;
          case ModImm:
          {
            Boolean IsShort = WordVal(&SrcResult) <= 15;

            if (!SrcResult.ForceSize)
              SrcResult.ForceSize = IsShort ? eForceShort : eForceLong;
            if (SrcResult.ForceSize == eForceShort)
            {
              if (!IsShort && !mSymbolQuestionable(SrcResult.SymFlags)) WrStrErrorPos(ErrNum_OverRange, &ArgStr[2]);
              else
              {
                CodeLen = 2;
                BAsmCode[0] = 0xe0 + Code;
                BAsmCode[1] = (WordVal(&SrcResult) << 4) + DestResult.Mode;
              }
            }
            else
            {
              CodeLen = 4;
              BAsmCode[0] = 0xe6 + Code;
              BAsmCode[1] = DestResult.Mode + 0xf0;
              memcpy(BAsmCode + 2, SrcResult.Vals, 2);
            }
            break;
          }
          case ModIReg:
            CodeLen = 2;
            BAsmCode[0] = 0xa8 + Code;
            BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Mode;
            break;
          case ModPostInc:
            CodeLen = 2;
            BAsmCode[0] = 0x98 + Code;
            BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Mode;
            break;
          case ModIndex:
            CodeLen = 2 + SrcResult.Cnt;
            BAsmCode[0] = 0xd4 + (Code << 5);
            BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Mode;
            memcpy(BAsmCode + 2, SrcResult.Vals, SrcResult.Cnt);
            break;
          case ModAbs:
            CodeLen = 2 + SrcResult.Cnt;
            BAsmCode[0] = 0xf2 + Code;
            BAsmCode[1] = 0xf0 + DestResult.Mode;
            memcpy(BAsmCode + 2, SrcResult.Vals, SrcResult.Cnt);
            break;
        }
        break;
      }
      case ModMReg:
      {
        tAdrResult SrcResult;

        BAsmCode[1] = DestResult.Vals[0];
        switch (DecodeAdr(&ArgStr[2], MModImm | MModMReg | ((DPPAssumes[3] == 3) ? MModIReg : 0) | MModAbs, &SrcResult))
        {
          case ModImm:
            CodeLen = 4;
            BAsmCode[0] = 0xe6 + Code;
            memcpy(BAsmCode + 2, SrcResult.Vals, 2);
            break;
          case ModMReg: /* BAsmCode[1] sicher absolut darstellbar, da Rn vorher */
                        /* abgefangen wird! */
            BAsmCode[0] = 0xf6 + Code;
            AdrLong = SFRStart() + (((Word)BAsmCode[1]) << 1);
            CalcPage(&AdrLong, True);
            BAsmCode[2] = Lo(AdrLong);
            BAsmCode[3] = Hi(AdrLong);
            BAsmCode[1] = SrcResult.Vals[0];
            CodeLen = 4;
            break;
          case ModIReg:
            CodeLen = 4; BAsmCode[0] = 0x94 + (Code << 5);
            BAsmCode[2] = BAsmCode[1] << 1;
            BAsmCode[3] = 0xfe + (BAsmCode[1] >> 7); /* ANSI :-0 */
            BAsmCode[1] = SrcResult.Mode;
            break;
          case ModAbs:
            CodeLen = 2 + SrcResult.Cnt;
            BAsmCode[0] = 0xf2 + Code;
            memcpy(BAsmCode + 2, SrcResult.Vals, SrcResult.Cnt);
            break;
        }
        break;
      }
      case ModIReg:
      {
        tAdrResult SrcResult;

        switch (DecodeAdr(&ArgStr[2], MModReg | MModIReg | MModPostInc | MModAbs, &SrcResult))
        {
          case ModReg:
            CodeLen = 2;
            BAsmCode[0] = 0xb8 + Code;
            BAsmCode[1] = DestResult.Mode + (SrcResult.Mode << 4);
            break;
          case ModIReg:
            CodeLen = 2;
           BAsmCode[0] = 0xc8 + Code;
            BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Mode;
            break;
          case ModPostInc:
            CodeLen = 2;
            BAsmCode[0] = 0xe8 + Code;
            BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Mode;
            break;
          case ModAbs:
            CodeLen = 2 + SrcResult.Cnt;
            BAsmCode[0] = 0x84 + (Code << 5);
            BAsmCode[1] = DestResult.Mode;
            memcpy(BAsmCode + 2, SrcResult.Vals, SrcResult.Cnt);
            break;
        }
        break;
      }
      case ModPreDec:
      {
        tAdrResult SrcResult;

        switch (DecodeAdr(&ArgStr[2], MModReg, &SrcResult))
        {
          case ModReg:
            CodeLen = 2;
            BAsmCode[0] = 0x88 + Code;
            BAsmCode[1] = DestResult.Mode + (SrcResult.Mode << 4);
            break;
        }
        break;
      }
      case ModPostInc:
      {
        tAdrResult SrcResult;

        switch (DecodeAdr(&ArgStr[2], MModIReg, &SrcResult))
        {
          case ModIReg:
            CodeLen = 2;
            BAsmCode[0] = 0xd8 + Code;
            BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Mode;
            break;
        }
        break;
      }
      case ModIndex:
      {
        tAdrResult SrcResult;

        BAsmCode[1] = DestResult.Mode;
        memcpy(BAsmCode + 2, DestResult.Vals, DestResult.Cnt);
        switch (DecodeAdr(&ArgStr[2], MModReg, &SrcResult))
        {
          case ModReg:
            BAsmCode[0] = 0xc4 + (Code << 5);
            CodeLen = 4;
            BAsmCode[1] += SrcResult.Mode << 4;
            break;
        }
        break;
      }
      case ModAbs:
      {
        tAdrResult SrcResult;

        memcpy(BAsmCode + 2, DestResult.Vals, DestResult.Cnt);
        switch (DecodeAdr(&ArgStr[2], MModIReg | MModMReg, &SrcResult))
        {
          case ModIReg:
            CodeLen = 4;
            BAsmCode[0] = 0x94 + (Code << 5);
            BAsmCode[1] = SrcResult.Mode;
            break;
          case ModMReg:
            CodeLen = 4;
            BAsmCode[0] = 0xf6 + Code;
            BAsmCode[1] = SrcResult.Vals[0];
            break;
        }
        break;
      }
    }
  }
}

static void DecodeMOVBS_MOVBZ(Word Code)
{
  LongInt AdrLong;

  if (ChkArgCnt(2, 2))
  {
    tAdrResult DestResult;

    OpSize = eSymbolSize16Bit;
    switch (DecodeAdr(&ArgStr[1], MModReg | MModMReg | MModAbs | M_Dest, &DestResult))
    {
      case ModReg:
      {
        tAdrResult SrcResult;

        OpSize = eSymbolSize8Bit;
        switch (DecodeAdr(&ArgStr[2], MModReg | MModAbs, &SrcResult))
        {
          case ModReg:
            CodeLen = 2;
            BAsmCode[0] = 0xc0 + Code;
            BAsmCode[1] = DestResult.Mode + (SrcResult.Mode << 4);
            break;
          case ModAbs:
            CodeLen = 4;
            BAsmCode[0] = 0xc2 + Code;
            BAsmCode[1] = 0xf0 + DestResult.Mode;
            memcpy(BAsmCode + 2, SrcResult.Vals, 2);
            break;
        }
        break;
      }
      case ModMReg:
      {
        tAdrResult SrcResult;

        BAsmCode[1] = DestResult.Vals[0];
        OpSize = eSymbolSize8Bit;
        switch (DecodeAdr(&ArgStr[2], MModAbs | MModMReg, &SrcResult))
        {
          case ModMReg: /* BAsmCode[1] sicher absolut darstellbar, da Rn vorher */
                        /* abgefangen wird! */
            BAsmCode[0] = 0xc5 + Code;
            AdrLong = SFRStart() + (((Word)BAsmCode[1]) << 1);
            CalcPage(&AdrLong, True);
            BAsmCode[2] = Lo(AdrLong);
            BAsmCode[3] = Hi(AdrLong);
            BAsmCode[1] = SrcResult.Vals[0];
            CodeLen = 4;
            break;
          case ModAbs:
            CodeLen = 2 + SrcResult.Cnt;
            BAsmCode[0] = 0xc2 + Code;
            memcpy(BAsmCode + 2, SrcResult.Vals, SrcResult.Cnt);
            break;
        }
        break;
      }
      case ModAbs:
      {
        tAdrResult SrcResult;

        OpSize = eSymbolSize8Bit;
        memcpy(BAsmCode + 2, DestResult.Vals, DestResult.Cnt);
        switch (DecodeAdr(&ArgStr[2], MModMReg, &SrcResult))
        {
          case ModMReg:
            CodeLen = 4;
            BAsmCode[0] = 0xc5 + Code;
            BAsmCode[1] = SrcResult.Vals[0];
            break;
        }
        break;
      }
    }
  }
}

static void DecodePUSH_POP(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    tAdrResult Result;

    switch (DecodeAdr(&ArgStr[1], MModMReg | ((Code & 0x10) ? M_Dest : 0), &Result))
    {
      case ModMReg:
        CodeLen = 2;
        BAsmCode[0] = Code;
        BAsmCode[1] = Result.Vals[0];
        if (SPChanged) WrXError(ErrNum_Pipeline, RegNames[5]);
        break;
    }
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    tAdrResult Result;

    switch (DecodeAdr(&ArgStr[1], MModMReg | M_Dest, &Result))
    {
      case ModMReg:
        BAsmCode[1] = Result.Vals[0];
        if (DecodeAdr(&ArgStr[2], MModAbs | MModImm, &Result) != ModNone)
        {
          CodeLen = 4; BAsmCode[0] = 0xc6 + (Ord(Result.Type == ModAbs) << 4);
          memcpy(BAsmCode + 2, Result.Vals, 2);
        }
        break;
    }
  }
}

static void DecodeALU2(Word Code)
{
  LongInt AdrLong;

  OpSize = (tSymbolSize)Hi(Code);
  Code = (1 - OpSize) + (Lo(Code)  << 4);

  if (ChkArgCnt(2, 2))
  {
    tAdrResult DestResult;

    switch (DecodeAdr(&ArgStr[1], MModReg | MModMReg | MModAbs | M_Dest, &DestResult))
    {
      case ModReg:
      {
        tAdrResult SrcResult;

        switch (DecodeAdr(&ArgStr[2], MModReg | MModIReg | MModPostInc | MModAbs | MModImm, &SrcResult))
        {
          case ModReg:
            CodeLen = 2;
            BAsmCode[0] = Code;
            BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Mode;
            break;
          case ModIReg:
            if (SrcResult.Mode > 3) WrError(ErrNum_InvAddrMode);
            else
            {
              CodeLen = 2;
              BAsmCode[0] = 0x08 + Code;
              BAsmCode[1] = (DestResult.Mode << 4) + 8 + SrcResult.Mode;
            }
            break;
          case ModPostInc:
            if (SrcResult.Mode > 3) WrError(ErrNum_InvAddrMode);
            else
            {
              CodeLen = 2;
              BAsmCode[0] = 0x08 + Code;
              BAsmCode[1] = (DestResult.Mode << 4) + 12 + SrcResult.Mode;
            }
            break;
          case ModAbs:
            CodeLen = 4;
            BAsmCode[0] = 0x02 + Code;
            BAsmCode[1] = 0xf0 + DestResult.Mode;
            memcpy(BAsmCode + 2, SrcResult.Vals, SrcResult.Cnt);
            break;
          case ModImm:
          {
            Boolean IsShort = WordVal(&SrcResult) <= 7;

            if (!SrcResult.ForceSize)
              SrcResult.ForceSize = IsShort ? eForceShort : eForceLong;
            if (SrcResult.ForceSize == eForceShort)
            {
              if (!IsShort && !mSymbolQuestionable(SrcResult.SymFlags)) WrStrErrorPos(ErrNum_OverRange, &ArgStr[2]);
              else
              {
                CodeLen = 2;
                BAsmCode[0] = 0x08 + Code;
                BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Vals[0];
              }
            }
            else
            {
              CodeLen = 4;
              BAsmCode[0] = 0x06 + Code;
              BAsmCode[1] = 0xf0 + DestResult.Mode;
              memcpy(BAsmCode + 2, SrcResult.Vals, 2);
            }
            break;
          }
        }
        break;
      }
      case ModMReg:
      {
        tAdrResult SrcResult;

        BAsmCode[1] = DestResult.Vals[0];
        switch (DecodeAdr(&ArgStr[2], MModAbs | MModMReg | MModImm, &SrcResult))
        {
          case ModAbs:
            CodeLen = 4;
            BAsmCode[0] = 0x02 + Code;
            memcpy(BAsmCode + 2, SrcResult.Vals, SrcResult.Cnt);
            break;
          case ModMReg: /* BAsmCode[1] sicher absolut darstellbar, da Rn vorher */
                        /* abgefangen wird! */
            BAsmCode[0] = 0x04 + Code;
            AdrLong = SFRStart() + (((Word)BAsmCode[1]) << 1);
            CalcPage(&AdrLong, True);
            BAsmCode[2] = Lo(AdrLong);
            BAsmCode[3] = Hi(AdrLong);
            BAsmCode[1] = SrcResult.Vals[0];
            CodeLen = 4;
            break;
          case ModImm:
            CodeLen = 4;
            BAsmCode[0] = 0x06 + Code;
            memcpy(BAsmCode + 2, SrcResult.Vals, 2);
            break;
        }
        break;
      }
      case ModAbs:
      {
        tAdrResult SrcResult;

        memcpy(BAsmCode + 2, DestResult.Vals, DestResult.Cnt);
        switch (DecodeAdr(&ArgStr[2], MModMReg, &SrcResult))
        {
          case ModMReg:
            CodeLen = 4;
            BAsmCode[0] = 0x04 + Code;
            BAsmCode[1] = SrcResult.Vals[0];
            break;
        }
        break;
      }
    }
  }
}

static void DecodeCPL_NEG(Word Code)
{
  OpSize = (tSymbolSize)Hi(Code);

  Code = Lo(Code) + ((1 - OpSize) << 5);
  if (ChkArgCnt(1, 1))
  {
    tAdrResult Result;

    if (DecodeAdr(&ArgStr[1], MModReg | M_Dest, &Result) == ModReg)
    {
      CodeLen = 2;
      BAsmCode[0] = Code;
      BAsmCode[1] = Result.Mode << 4;
    }
  }
}

static void DecodeDiv(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    tAdrResult Result;

    if (DecodeAdr(&ArgStr[1], MModReg, &Result) == ModReg)
    {
      CodeLen = 2;
      BAsmCode[0] = 0x4b + (Code << 4);
      BAsmCode[1] = Result.Mode * 0x11;
    }
  }
}

static void DecodeLoop(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    tAdrResult Result;

    if (DecodeAdr(&ArgStr[1], MModReg | M_Dest, &Result) == ModReg)
    {
      BAsmCode[1] = Result.Mode;
      switch (DecodeAdr(&ArgStr[2], MModAbs | MModImm, &Result))
      {
        case ModAbs:
          CodeLen = 4;
          BAsmCode[0] = Code + 2;
          BAsmCode[1] += 0xf0;
          memcpy(BAsmCode + 2, Result.Vals, 2);
          break;
        case ModImm:
        {
          Boolean IsShort = WordVal(&Result) < 16;

          if (!Result.ForceSize)
            Result.ForceSize = IsShort ? eForceShort : eForceLong;
          if (Result.ForceSize == eForceShort)
          {
            if (!IsShort && !mSymbolQuestionable(Result.SymFlags)) WrStrErrorPos(ErrNum_OverRange, &ArgStr[2]);
            else
            {
              CodeLen = 2;
              BAsmCode[0] = Code;
              BAsmCode[1] += (WordVal(&Result) << 4);
            }
          }
          else
          {
            CodeLen = 4;
            BAsmCode[0] = Code + 6;
            BAsmCode[1] += 0xf0;
            memcpy(BAsmCode + 2, Result.Vals, 2);
          }
          break;
        }
      }
    }
  }
}

static void DecodeMul(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    tAdrResult DestResult;

    switch (DecodeAdr(&ArgStr[1], MModReg, &DestResult))
    {
      case ModReg:
      {
        tAdrResult SrcResult;

        switch (DecodeAdr(&ArgStr[2], MModReg, &SrcResult))
        {
          case ModReg:
            CodeLen = 2;
            BAsmCode[0] = 0x0b + (Code << 4);
            BAsmCode[1] = (DestResult.Mode << 4) + SrcResult.Mode;
            break;
        }
        break;
      }
    }
  }
}

static void DecodeShift(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    tAdrResult DestResult;

    OpSize = eSymbolSize16Bit;
    switch (DecodeAdr(&ArgStr[1], MModReg | M_Dest, &DestResult))
    {
      case ModReg:
      {
        tAdrResult SrcResult;

        switch (DecodeAdr(&ArgStr[2], MModReg | MModImm | M_Dest, &SrcResult))
        {
          case ModReg:
            BAsmCode[0] = Code;
            BAsmCode[1] = SrcResult.Mode + (DestResult.Mode << 4);
            CodeLen = 2;
            break;
          case ModImm:
            if ((WordVal(&SrcResult) > 15) && !mSymbolQuestionable(SrcResult.SymFlags)) WrStrErrorPos(ErrNum_OverRange, &ArgStr[2]);
            else
            {
              BAsmCode[0] = Code + 0x10;
              BAsmCode[1] = (WordVal(&SrcResult) << 4) + DestResult.Mode;
              CodeLen = 2;
            }
            break;
        }
        break;
      }
    }
  }
}

static void DecodeBit2(Word Code)
{
  Byte BOfs1, BOfs2;
  Word BAdr1, BAdr2;

  if (ChkArgCnt(2, 2)
   && DecodeBitAddr(&ArgStr[1], &BAdr1, &BOfs1, False)
   && DecodeBitAddr(&ArgStr[2], &BAdr2, &BOfs2, False))
  {
    CodeLen = 4;
    BAsmCode[0] = Code;
    BAsmCode[1] = BAdr2;
    BAsmCode[2] = BAdr1;
    BAsmCode[3] = (BOfs2 << 4) + BOfs1;
  }
}

static void DecodeBCLR_BSET(Word Code)
{
  Byte BOfs;
  Word BAdr;

  if (ChkArgCnt(1, 1)
   && DecodeBitAddr(&ArgStr[1], &BAdr, &BOfs, False))
  {
    CodeLen = 2;
    BAsmCode[0] = (BOfs << 4) + Code;
    BAsmCode[1] = BAdr;
  }
}

static void DecodeBFLDH_BFLDL(Word Code)
{
  Byte BOfs;
  Word BAdr;

  if (ChkArgCnt(3, 3))
  {
    strmaxcat(ArgStr[1].str.p_str, ".0", STRINGSIZE);
    if (DecodeBitAddr(&ArgStr[1], &BAdr, &BOfs, False))
    {
      tAdrResult Result;

      OpSize = eSymbolSize8Bit;
      BAsmCode[1] = BAdr;
      if (DecodeAdr(&ArgStr[2], MModImm, &Result) == ModImm)
      {
        BAsmCode[2] = Result.Vals[0];
        if (DecodeAdr(&ArgStr[3], MModImm, &Result) == ModImm)
        {
          BAsmCode[3] = Result.Vals[0];
          CodeLen = 4;
          BAsmCode[0] = Code;
          if (Code & 0x10)
          {
            BAdr = BAsmCode[2];
            BAsmCode[2] = BAsmCode[3];
            BAsmCode[3] = BAdr;
          }
        }
      }
    }
  }
}

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

  if (ChkArgCnt(1, 2))
  {
    Byte cond_code;
    tAdrResult Result;

    if (ArgCnt == 1)
      cond_code = TrueCond;
    else if (!DecodeCondition(ArgStr[1].str.p_str, &cond_code))
    {
      WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
      return;
    }

    switch (DecodeAdr(&ArgStr[ArgCnt], MModAbs | MModLAbs | MModIReg | M_InCode, &Result))
    {
      case ModLAbs:
        if (cond_code != COND_CODE_TRUE) WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
        else
        {
          CodeLen = 2 + Result.Cnt;
          BAsmCode[0] = 0xfa;
          BAsmCode[1] = Result.Mode;
          memcpy(BAsmCode + 2, Result.Vals, Result.Cnt);
        }
        break;
      case ModAbs:
      {
        LongInt AdrDist = WordVal(&Result) - (EProgCounter() + 2);
        Boolean IsShort = (AdrDist <= 254) && (AdrDist >= -256) && ((AdrDist & 1) == 0);

        if (!Result.ForceSize)
          Result.ForceSize = IsShort ? eForceShort : eForceLong;
        if (Result.ForceSize == eForceShort)
        {
          if (!IsShort && !mSymbolQuestionable(Result.SymFlags)) WrStrErrorPos(ErrNum_JmpDistTooBig, &ArgStr[ArgCnt]);
          else
          {
            CodeLen = 2;
            BAsmCode[0] = 0x0d + (cond_code << 4);
            BAsmCode[1] = (AdrDist / 2) & 0xff;
          }
        }
        else
        {
          CodeLen = 2 + Result.Cnt;
          BAsmCode[0] = 0xea;
          BAsmCode[1] = cond_code << 4;
          memcpy(BAsmCode + 2, Result.Vals, Result.Cnt);
        }
        break;
      }
      case ModIReg:
        CodeLen = 2; BAsmCode[0] = 0x9c;
        BAsmCode[1] = (cond_code << 4) + Result.Mode;
        break;
    }
  }
}

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

  if (ChkArgCnt(1, 2))
  {
    Byte cond_code;
    tAdrResult Result;

    if (ArgCnt == 1)
      cond_code = COND_CODE_TRUE;
    else if (!DecodeCondition(ArgStr[1].str.p_str, &cond_code))
    {
      WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
      return;
    }

    switch (DecodeAdr(&ArgStr[ArgCnt], MModAbs | MModLAbs | MModIReg | M_InCode, &Result))
    {
      case ModLAbs:
        if (cond_code != COND_CODE_TRUE) WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
        else
        {
          CodeLen = 2 + Result.Cnt;
          BAsmCode[0] = 0xda;
          BAsmCode[1] = Result.Mode;
          memcpy(BAsmCode + 2, Result.Vals, Result.Cnt);
        }
        break;
      case ModAbs:
      {
        LongInt AdrLong = WordVal(&Result) - (EProgCounter() + 2);
        Boolean IsShort = (AdrLong <= 254) && (AdrLong >= -256) && ((AdrLong & 1) == 0);

        if (!Result.ForceSize && (cond_code == COND_CODE_TRUE))
          Result.ForceSize = IsShort ? eForceShort : eForceLong;
        if (Result.ForceSize == eForceShort)
        {
          if (!IsShort && !mSymbolQuestionable(Result.SymFlags)) WrStrErrorPos(ErrNum_JmpDistTooBig, &ArgStr[ArgCnt]);
          else
          {
            CodeLen = 2;
            BAsmCode[0] = 0xbb;
            BAsmCode[1] = (AdrLong / 2) & 0xff;
          }
        }
        else
        {
          CodeLen = 2 + Result.Cnt;
          BAsmCode[0] = 0xca;
          BAsmCode[1] = 0x00 + (cond_code << 4);
          memcpy(BAsmCode + 2, Result.Vals, Result.Cnt);
        }
        break;
      }
      case ModIReg:
        CodeLen = 2;
        BAsmCode[0] = 0xab;
        BAsmCode[1] = (cond_code << 4) + Result.Mode;
        break;
    }
  }
}

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

  if (ChkArgCnt(1, 2))
  {
    Byte cond_code;
    Boolean OK;
    tSymbolFlags Flags;
    LongInt AdrLong;

    if (ArgCnt == 1)
      cond_code = COND_CODE_TRUE;
    else if (!DecodeCondition(ArgStr[1].str.p_str, &cond_code))
    {
      WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
      return;
    }

    AdrLong = EvalStrIntExpressionWithFlags(&ArgStr[ArgCnt], MemInt, &OK, &Flags) - (EProgCounter() + 2);
    if (OK)
    {
      if (AdrLong & 1) WrError(ErrNum_DistIsOdd);
      else if (!mSymbolQuestionable(Flags) && ((AdrLong > 254) || (AdrLong < -256))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        CodeLen = 2;
        BAsmCode[0] = 0x0d + (cond_code << 4);
        BAsmCode[1] = (AdrLong / 2) & 0xff;
      }
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    tSymbolFlags Flags;
    LongInt AdrLong = EvalStrIntExpressionWithFlags(&ArgStr[ArgCnt], MemInt, &OK, &Flags) - (EProgCounter() + 2);
    if (OK)
    {
      if (AdrLong & 1) WrError(ErrNum_DistIsOdd);
      else if (!mSymbolQuestionable(Flags) && ((AdrLong > 254) || (AdrLong < -256))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        CodeLen = 2;
        BAsmCode[0] = 0xbb;
        BAsmCode[1] = (AdrLong / 2) & 0xff;
      }
    }
  }
}

static void DecodeJMPA_CALLA(Word Code)
{
  if (ChkArgCnt(1, 2))
  {
    Byte cond_code;
    Boolean OK;
    LongInt AdrLong;
    tSymbolFlags Flags;

    if (ArgCnt == 1)
      cond_code = COND_CODE_TRUE;
    else if (!DecodeCondition(ArgStr[1].str.p_str, &cond_code))
    {
      WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
      return;
    }

    AdrLong = EvalStrIntExpressionWithFlags(&ArgStr[ArgCnt], MemInt, &OK, &Flags);
    if (OK && ChkSamePage(AdrLong, EProgCounter(), 16, Flags))
    {
      CodeLen = 4;
      BAsmCode[0] = Code;
      BAsmCode[1] = 0x00 + (cond_code << 4);
      BAsmCode[2] = Lo(AdrLong);
      BAsmCode[3] = Hi(AdrLong);
    }
  }
}

static void DecodeJMPS_CALLS(Word Code)
{
  if (ChkArgCnt(1, 2))
  {
    Boolean OK;
    Word AdrWord;
    Byte AdrBank;
    LongInt AdrLong;

    if (ArgCnt == 1)
    {
      AdrLong = EvalStrIntExpression(&ArgStr[1], MemInt, &OK);
      AdrWord = AdrLong & 0xffff;
      AdrBank = AdrLong >> 16;
    }
    else
    {
      AdrWord = EvalStrIntExpression(&ArgStr[2], UInt16, &OK);
      AdrBank = OK ? EvalStrIntExpression(&ArgStr[1], MemInt2, &OK) : 0;
    }
    if (OK)
    {
      CodeLen = 4;
      BAsmCode[0] = Code;
      BAsmCode[1] = AdrBank;
      BAsmCode[2] = Lo(AdrWord);
      BAsmCode[3] = Hi(AdrWord);
    }
  }
}

static void DecodeJMPI_CALLI(Word Code)
{
  if (ChkArgCnt(1, 2))
  {
    Byte cond_code;
    tAdrResult Result;

    if (ArgCnt == 1)
      cond_code = COND_CODE_TRUE;
    else if (!DecodeCondition(ArgStr[1].str.p_str, &cond_code))
    {
      WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
      return;
    }

    switch (DecodeAdr(&ArgStr[ArgCnt], MModIReg | M_InCode, &Result))
    {
      case ModIReg:
        CodeLen = 2;
        BAsmCode[0] = Code;
        BAsmCode[1] = Result.Mode + (cond_code << 4);
        break;
    }
  }
}

static void DecodeBJmp(Word Code)
{
  Byte BOfs;
  Word BAdr;

  if (ChkArgCnt(2, 2)
   && DecodeBitAddr(&ArgStr[1], &BAdr, &BOfs, False))
  {
    Boolean OK;
    tSymbolFlags Flags;
    LongInt AdrLong = EvalStrIntExpressionWithFlags(&ArgStr[2], MemInt, &OK, &Flags) - (EProgCounter() + 4);
    if (OK)
    {
      if (AdrLong & 1) WrError(ErrNum_DistIsOdd);
      else if (!mSymbolQuestionable(Flags) && ((AdrLong < -256) || (AdrLong > 254))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        CodeLen = 4; BAsmCode[0] = 0x8a + (Code << 4);
        BAsmCode[1] = BAdr;
        BAsmCode[2] = (AdrLong / 2) & 0xff;
        BAsmCode[3] = BOfs << 4;
      }
    }
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    tAdrResult Result;

    switch (DecodeAdr(&ArgStr[1], MModMReg, &Result))
    {
      case ModMReg:
        BAsmCode[1] = Result.Vals[0];
        switch (DecodeAdr(&ArgStr[2], MModAbs | M_InCode, &Result))
        {
          case ModAbs:
            CodeLen = 4;
            BAsmCode[0] = 0xe2;
            memcpy(BAsmCode + 2, Result.Vals, 2);
            break;
        }
        break;
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    tAdrResult Result;

    switch (DecodeAdr(&ArgStr[1], MModMReg, &Result))
    {
      case ModMReg:
        BAsmCode[1] = Result.Vals[0];
        BAsmCode[0] = 0xeb;
        CodeLen = 2;
        if (SPChanged)
          WrXError(ErrNum_Pipeline, RegNames[5]);
        break;
    }
  }
}

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

  if (!ChkArgCnt(1, 1));
  else if (*ArgStr[1].str.p_str != '#') WrError(ErrNum_InvAddrMode);
  else
  {
    Boolean OK;

    BAsmCode[1] = EvalStrIntExpressionOffs(&ArgStr[1], 1, UInt7, &OK) << 1;
    if (OK)
    {
      BAsmCode[0] = 0x9b;
      CodeLen = 2;
    }
  }
}

static void DecodeATOMIC(Word Code)
{
  Byte HReg;

  UNUSED(Code);

  if (ChkArgCnt(1, 1)
   && ChkMinCPU(CPU80C167)
   && DecodePref(&ArgStr[1], &HReg))
  {
    CodeLen = 2;
    BAsmCode[0] = 0xd1;
    BAsmCode[1] = HReg << 4;
  }
}

static void DecodeEXTR(Word Code)
{
  Byte HReg;

  UNUSED(Code);

  if (ChkArgCnt(1, 1)
   && ChkMinCPU(CPU80C167)
   && DecodePref(&ArgStr[1], &HReg))
  {
    CodeLen = 2;
    BAsmCode[0] = 0xd1;
    BAsmCode[1] = 0x80 + (HReg << 4);
    ExtCounter = HReg + 1;
    ExtSFRs = True;
  }
}

static void DecodeEXTP_EXTPR(Word Code)
{
  Byte HReg;

  if (ChkArgCnt(2, 2)
   && ChkMinCPU(CPU80C167)
   && DecodePref(&ArgStr[2], &HReg))
  {
    tAdrResult Result;

    switch (DecodeAdr(&ArgStr[1], MModReg | MModImm, &Result))
    {
      case ModReg:
        CodeLen = 2;
        BAsmCode[0] = 0xdc;
        BAsmCode[1] = Code + 0x40 + (HReg << 4) + Result.Mode;
        ExtCounter = HReg + 1;
        MemMode = MemModeZeroPage;
        break;
      case ModImm:
        CodeLen = 4;
        BAsmCode[0] = 0xd7;
        BAsmCode[1] = Code + 0x40 + (HReg << 4);
        BAsmCode[2] = WordVal(&Result) & 0xff;
        BAsmCode[3] = (WordVal(&Result) >> 8) & 3;
        ExtCounter = HReg + 1;
        MemMode = MemModeFixedPage;
        MemPage = WordVal(&Result) & 0x3ff;
        break;
    }
  }
}

static void DecodeEXTS_EXTSR(Word Code)
{
  Byte HReg;

  OpSize = eSymbolSize8Bit;
  if (ChkArgCnt(2, 2)
   && ChkMinCPU(CPU80C167)
   && DecodePref(&ArgStr[2], &HReg))
  {
    tAdrResult Result;

    switch (DecodeAdr(&ArgStr[1], MModReg | MModImm, &Result))
    {
      case ModReg:
        CodeLen = 2;
        BAsmCode[0] = 0xdc;
        BAsmCode[1] = Code + 0x00 + (HReg << 4) + Result.Mode;
        ExtCounter = HReg + 1;
        MemMode = MemModeNoCheck;
        break;
      case ModImm:
        CodeLen = 4;
        BAsmCode[0] = 0xd7;
        BAsmCode[1] = Code + 0x00 + (HReg << 4);
        BAsmCode[2] = Result.Vals[0];
        BAsmCode[3] = 0;
        ExtCounter = HReg + 1;
        MemMode = MemModeFixedBank;
        MemPage = Result.Vals[0];
        break;
    }
  }
}

static void DecodeBIT(Word Code)
{
  Word Adr;
  Byte Bit;

  UNUSED(Code);

 if (ChkArgCnt(1, 1)
  && DecodeBitAddr(&ArgStr[1], &Adr, &Bit, True))
 {
   PushLocHandle(-1);
   EnterIntSymbol(&LabPart, (Adr << 4) + Bit, SegNone, False);
   PopLocHandle();
   as_snprintf(ListLine, STRINGSIZE, "=%02xH.%1x", (unsigned)Adr, (unsigned)Bit);
 }
}

/*!------------------------------------------------------------------------
 * \fn     update_prefixes(Word index)
 * \brief  necessary operations prior to assembling machine instructions
 * ------------------------------------------------------------------------ */

static void update_prefixes(Word index)
{
  int z;

  UNUSED(index);

  /* Pipeline-Flags weiterschalten */

  SPChanged = N_SPChanged; N_SPChanged = False;
  CPChanged = N_CPChanged; N_CPChanged = False;
  for (z = 0; z < DPPCount; z++)
  {
    DPPChanged[z] = N_DPPChanged[z];
    N_DPPChanged[z] = False;
  }

  /* Praefixe herunterzaehlen */

  if (ExtCounter >= 0)
   if (--ExtCounter < 0)
   {
     MemMode = MemModeStd;
     ExtSFRs = False;
   }
}

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

static void AddBInstTable(const char *NName, Word NCode, InstProc Proc)
{
  char BName[30];

  AddInstTable(InstTable, NName, NCode | (eSymbolSize16Bit << 8), Proc);
  as_snprintf(BName, sizeof(BName), "%sB", NName);
  AddInstTable(InstTable, BName, NCode | (eSymbolSize8Bit << 8), Proc);
}

static void AddFixed(const char *NName, CPUVar NMin, Word NCode1, Word NCode2)
{
  order_array_rsv_end(FixedOrders, BaseOrder);
  FixedOrders[InstrZ].MinCPU = NMin;
  FixedOrders[InstrZ].Code1 = NCode1;
  FixedOrders[InstrZ].Code2 = NCode2;
  AddInstTable(InstTable, NName, InstrZ++, DecodeFixed);
}

static void AddShift(const char *NName, Byte NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeShift);
}

static void AddBit2(const char *NName, Byte NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeBit2);
}

static void AddLoop(const char *NName, Byte NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeLoop);
}

static void AddCondition(const char *NName, Byte NCode)
{
  order_array_rsv_end(Conditions, Condition);
  Conditions[InstrZ].Name = NName;
  Conditions[InstrZ++].Code = NCode;
}

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

  add_null_pseudo(InstTable);

  inst_table_set_prefix_proc(InstTable, update_prefixes, 0);

  AddBInstTable("MOV", 0, DecodeMOV);
  AddInstTable(InstTable, "MOVBS", 0x10, DecodeMOVBS_MOVBZ);
  AddInstTable(InstTable, "MOVBZ", 0x00, DecodeMOVBS_MOVBZ);
  AddInstTable(InstTable, "PUSH", 0xec, DecodePUSH_POP);
  AddInstTable(InstTable, "POP", 0xfc, DecodePUSH_POP);
  AddInstTable(InstTable, "SCXT", 0, DecodeSCXT);
  AddBInstTable("CPL", 0x91, DecodeCPL_NEG);
  AddBInstTable("NEG", 0x81, DecodeCPL_NEG);
  AddInstTable(InstTable, "BCLR", 0x0e, DecodeBCLR_BSET);
  AddInstTable(InstTable, "BSET", 0x0f, DecodeBCLR_BSET);
  AddInstTable(InstTable, "BFLDL", 0x0a, DecodeBFLDH_BFLDL);
  AddInstTable(InstTable, "BFLDH", 0x1a, DecodeBFLDH_BFLDL);
  AddInstTable(InstTable, "JMP", 0, DecodeJMP);
  AddInstTable(InstTable, "CALL", 0, DecodeCALL);
  AddInstTable(InstTable, "JMPR", 0, DecodeJMPR);
  AddInstTable(InstTable, "CALLR", 0, DecodeCALLR);
  AddInstTable(InstTable, "JMPA", 0xea, DecodeJMPA_CALLA);
  AddInstTable(InstTable, "CALLA", 0xca, DecodeJMPA_CALLA);
  AddInstTable(InstTable, "JMPS", 0xfa, DecodeJMPS_CALLS);
  AddInstTable(InstTable, "CALLS", 0xda, DecodeJMPS_CALLS);
  AddInstTable(InstTable, "JMPI", 0x9c, DecodeJMPI_CALLI);
  AddInstTable(InstTable, "CALLI", 0xab, DecodeJMPI_CALLI);
  AddInstTable(InstTable, "PCALL", 0, DecodePCALL);
  AddInstTable(InstTable, "RETP", 0, DecodeRETP);
  AddInstTable(InstTable, "TRAP", 0, DecodeTRAP);
  AddInstTable(InstTable, "ATOMIC", 0, DecodeATOMIC);
  AddInstTable(InstTable, "EXTR", 0, DecodeEXTR);
  AddInstTable(InstTable, "EXTP", 0x00, DecodeEXTP_EXTPR);
  AddInstTable(InstTable, "EXTPR", 0x80, DecodeEXTP_EXTPR);
  AddInstTable(InstTable, "EXTS", 0x00, DecodeEXTS_EXTSR);
  AddInstTable(InstTable, "EXTSR", 0x80, DecodeEXTS_EXTSR);

  InstrZ = 0;
  AddFixed("DISWDT", CPU80C166, 0x5aa5, 0xa5a5);
  AddFixed("EINIT" , CPU80C166, 0x4ab5, 0xb5b5);
  AddFixed("IDLE"  , CPU80C166, 0x7887, 0x8787);
  AddFixed("NOP"   , CPU80C166, 0x00cc, 0x0000);
  AddFixed("PWRDN" , CPU80C166, 0x6897, 0x9797);
  AddFixed("RET"   , CPU80C166, 0x00cb, 0x0000);
  AddFixed("RETI"  , CPU80C166, 0x88fb, 0x0000);
  AddFixed("RETS"  , CPU80C166, 0x00db, 0x0000);
  AddFixed("SRST"  , CPU80C166, 0x48b7, 0xb7b7);
  AddFixed("SRVWDT", CPU80C166, 0x58a7, 0xa7a7);

  InstrZ = 0;
  AddCondition("UC" , COND_CODE_TRUE); AddCondition("Z"  , 0x2);
  AddCondition("NZ" , 0x3); AddCondition("V"  , 0x4);
  AddCondition("NV" , 0x5); AddCondition("N"  , 0x6);
  AddCondition("NN" , 0x7); AddCondition("C"  , 0x8);
  AddCondition("NC" , 0x9); AddCondition("EQ" , 0x2);
  AddCondition("NE" , 0x3); AddCondition("ULT", 0x8);
  AddCondition("ULE", 0xf); AddCondition("UGE", 0x9);
  AddCondition("UGT", 0xe); AddCondition("SLT", 0xc);
  AddCondition("SLE", 0xb); AddCondition("SGE", 0xd);
  AddCondition("SGT", 0xa); AddCondition("NET", 0x1);
  AddCondition(NULL, 0);

  InstrZ = 0;
  AddBInstTable("ADD" , InstrZ++, DecodeALU2);
  AddBInstTable("ADDC", InstrZ++, DecodeALU2);
  AddBInstTable("SUB" , InstrZ++, DecodeALU2);
  AddBInstTable("SUBC", InstrZ++, DecodeALU2);
  AddBInstTable("CMP" , InstrZ++, DecodeALU2);
  AddBInstTable("XOR" , InstrZ++, DecodeALU2);
  AddBInstTable("AND" , InstrZ++, DecodeALU2);
  AddBInstTable("OR"  , InstrZ++, DecodeALU2);

  AddShift("ASHR", 0xac); AddShift("ROL" , 0x0c);
  AddShift("ROR" , 0x2c); AddShift("SHL" , 0x4c);
  AddShift("SHR" , 0x6c);

  AddBit2("BAND", 0x6a); AddBit2("BCMP" , 0x2a);
  AddBit2("BMOV", 0x4a); AddBit2("BMOVN", 0x3a);
  AddBit2("BOR" , 0x5a); AddBit2("BXOR" , 0x7a);

  AddLoop("CMPD1", 0xa0); AddLoop("CMPD2", 0xb0);
  AddLoop("CMPI1", 0x80); AddLoop("CMPI2", 0x90);

  InstrZ = 0;
  AddInstTable(InstTable, "DIV"  , InstrZ++, DecodeDiv);
  AddInstTable(InstTable, "DIVU" , InstrZ++, DecodeDiv);
  AddInstTable(InstTable, "DIVL" , InstrZ++, DecodeDiv);
  AddInstTable(InstTable, "DIVLU", InstrZ++, DecodeDiv);

  InstrZ = 0;
  AddInstTable(InstTable, "JB"   , InstrZ++, DecodeBJmp);
  AddInstTable(InstTable, "JNB"  , InstrZ++, DecodeBJmp);
  AddInstTable(InstTable, "JBC"  , InstrZ++, DecodeBJmp);
  AddInstTable(InstTable, "JNBS" , InstrZ++, DecodeBJmp);

  InstrZ = 0;
  AddInstTable(InstTable, "MUL"  , InstrZ++, DecodeMul);
  AddInstTable(InstTable, "MULU" , InstrZ++, DecodeMul);
  AddInstTable(InstTable, "PRIOR", InstrZ++, DecodeMul);

  inst_table_set_prefix_proc(InstTable, NULL, 0);

  AddInstTable(InstTable, "BIT" , 0, DecodeBIT);
  AddInstTable(InstTable, "REG" , 0, CodeREG);
  AddIntelPseudo(InstTable, eIntPseudoFlag_LittleEndian);
}

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

static void MakeCode_166(void)
{
  OpSize = eSymbolSize16Bit;

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

/*!------------------------------------------------------------------------
 * \fn     InternSymbol_166(char *pArg, TempResult *pResult)
 * \brief  handle built-in symbols on C16x
 * \param  pArg source argument
 * \param  pResult result buffer
 * ------------------------------------------------------------------------ */

static void InternSymbol_166(char *pArg, TempResult *pResult)
{
  tRegInt Erg;
  tSymbolSize Size;

  if (IsRegCore(pArg, &Erg, &Size))
  {
    pResult->Typ = TempReg;
    pResult->DataSize = Size;
    pResult->Contents.RegDescr.Reg = Erg;
    pResult->Contents.RegDescr.Dissect = DissectReg_166;
    pResult->Contents.RegDescr.compare = NULL;
  }
}

static void InitCode_166(void)
{
  int z;

  for (z = 0; z < DPPCount; z++)
  {
    DPPAssumes[z] = z;
    N_DPPChanged[z] = False;
  }
  N_CPChanged = False;
  N_SPChanged = False;

  MemMode = MemModeStd;
  ExtSFRs = False;
  ExtCounter = (-1);
}

static Boolean IsDef_166(void)
{
  return (Memo("BIT")) || (Memo("REG"));
}

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

static void SwitchTo_166(void)
{
  const TFamilyDescr *p_descr = FindFamilyByName("80C166/167");
  Byte z;

  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);
  OpSize = eSymbolSize16Bit;

  PCSymbol = "$";
  HeaderID = p_descr->Id;
  NOPCode = 0xcc00;
  DivideChars = ",";
  HasAttrs = False;

  ValidSegs = (1 << SegCode);
  Grans[SegCode] = 1; ListGrans[SegCode] = 1; SegInits[SegCode] = 0;

  MakeCode = MakeCode_166;
  IsDef = IsDef_166;
  InternSymbol = InternSymbol_166;
  DissectReg = DissectReg_166;
  SwitchFrom = SwitchFrom_166;

  if (MomCPU == CPU80C166)
  {
    MemInt = UInt18;
    MemInt2 = UInt2;
    ASSUME166s[0].max_value = 15;
    SegLimits[SegCode] = 0x3ffffl;
  }
  else
  {
    MemInt = UInt24;
    MemInt2 = UInt8;
    ASSUME166s[0].max_value = 1023;
    SegLimits[SegCode] = 0xffffffl;
  }
  for (z = 1; z < 4; z++)
    ASSUME166s[z].max_value = ASSUME166s[0].max_value;

  assume_set(ASSUME166s, as_array_size(ASSUME166s));

  InitFields();
}

void code166_init(void)
{
  CPU80C166 = AddCPU("80C166", SwitchTo_166);
  CPU80C167 = AddCPU("80C167", SwitchTo_166);
  CPU80C167CS = AddCPU("80C167CS", SwitchTo_166);

  AddInitPassProc(InitCode_166);
}
