/* code97c241.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator TLCS-9000                                                   */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"

#include <string.h>
#include <ctype.h>
#include <assert.h>

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

#include "code97c241.h"

typedef struct
{
  Byte Code;
  Byte Mask;     /* B0..2=OpSizes, B4=-MayImm, B5=-MayReg */
} RMWOrder;

static CPUVar CPU97C241;

static int OpSize, OpSize2;
static Integer LowLim4, LowLim8;

static Boolean AdrOK;
static Word AdrMode, AdrMode2;
static Byte AdrCnt2;
static Word AdrVals[2], AdrVals2[2];
static int AdrInc;
static Word Prefs[2];
static Boolean PrefUsed[2];
static char Format;
static Boolean MinOneIs0;

static RMWOrder *RMWOrders;

static const char Conditions[][4] =
{
  "C",   "NC",
  "Z",   "NZ",
  "OV",  "NOV",
  "MI",  "PL",
  "LE",  "GT",
  "LT",  "GE",
  "ULE", "UGT",
  "N",   "A",
  "ULT", "UGE",
  "EQ",  "NE"
};

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

static int CheckForcePrefix(const char *pArg, Boolean *pForce)
{
  if (*pArg == '>')
  {
    *pForce = True;
    return 1;
  }
  return 0;
}

static void AddSignedPrefix(Byte Index, Byte MaxBits, LongInt Value, Boolean Force)
{
  LongInt Max;

  Max = 1l << (MaxBits -1);
  if (Force || ((Value < -Max) || (Value >= Max)))
  {
    PrefUsed[Index] = True;
    Prefs[Index] = (Value >> MaxBits) & 0x7ff;
  }
}

static Boolean AddRelPrefix(Byte Index, Byte MaxBits, LongInt *Value, Boolean Force)
{
  LongInt Max1,Max2;

  Max1 = 1l << (MaxBits - 1);
  Max2 = 1l << (MaxBits + 10);
  if ((*Value < -Max2) || (*Value >= Max2)) WrError(ErrNum_JmpDistTooBig);
  else
  {
    if (Force || ((*Value < -Max1) || (*Value >= Max1)))
    {
      PrefUsed[Index] = True;
      Prefs[Index] = ((*Value) >> MaxBits) & 0x7ff;
    }
    return True;
  }
  return False;
}

static void AddAbsPrefix(Byte Index, Byte MaxBits, LongInt Value, Boolean Force)
{
  LongInt Dist;

  Dist = 1l << (MaxBits - 1);
  if (Force || ((Value >= Dist) && (Value < 0x1000000 - Dist)))
  {
    PrefUsed[Index] = True;
    Prefs[Index] = (Value >> MaxBits) & 0x7ff;
  }
}

static void InsertSinglePrefix(Byte Index)
{
  if (PrefUsed[Index])
  {
    memmove(WAsmCode + 1, WAsmCode + 0, CodeLen);
    WAsmCode[0] = Prefs[Index] + 0xd000 + (((Word)Index) << 11);
    CodeLen += 2;
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeRegCore(const char *pArg, Byte *pResult, tSymbolSize *pSize)
 * \brief  check whether argument is a CPU register
 * \param  pArg source argument
 * \param  pResult register number if yes
 * \param  pSize register size if yes
 * \return True if yes
 * ------------------------------------------------------------------------ */

static Boolean DecodeRegCore(const char *pArg, Byte *pResult, tSymbolSize *pSize)
{
  char *p_end;
  int l = strlen(pArg);

  if (as_toupper(*pArg) != 'R')
    return False;
  l = strlen(pArg);
  if ((l > 4) || (l < 3))
    return False;

  switch (as_toupper(pArg[1]))
  {
    case 'B':
      *pSize = eSymbolSize8Bit;
      break;
    case 'W':
      *pSize = eSymbolSize16Bit;
      break;
    case 'D':
      *pSize = eSymbolSize32Bit;
      break;
    default:
      return False;
  }
  *pResult = strtoul(pArg + 2, &p_end, 10);
  if (*p_end || (*pResult > 15))
    return False;
  if ((*pSize == eSymbolSize32Bit) && Odd(*pResult))
    return False;
  return True;
}

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

static void DissectReg_97C241(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
{
  switch (InpSize)
  {
    case eSymbolSize8Bit:
      as_snprintf(pDest, DestSize, "RB%u", (unsigned)Value);
      break;
    case eSymbolSize16Bit:
      as_snprintf(pDest, DestSize, "RW%u", (unsigned)Value);
      break;
    case eSymbolSize32Bit:
      as_snprintf(pDest, DestSize, "RD%u", (unsigned)Value);
      break;
    default:
      as_snprintf(pDest, DestSize, "%d-%u", (int)InpSize, (unsigned)Value);
  }
}

/*!------------------------------------------------------------------------
 * \fn     tRegEvalResult DecodeReg(const tStrComp *pArg, Byte *pResult, tSymbolSize *pSize, Boolean MustBeReg)
 * \brief  check whether argument is a CPU register or register alias
 * \param  pArg source argument
 * \param  pResult register number if yes
 * \param  pSize register size if yes
 * \param  MustBeReg True if register is expected
 * \return True if yes
 * ------------------------------------------------------------------------ */

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

  if (DecodeRegCore(pArg->str.p_str, pResult, pSize))
    return eIsReg;

  RegEvalResult = EvalStrRegExpressionAsOperand(pArg, &RegDescr, &EvalResult, eSymbolSizeUnknown, MustBeReg);
  if (eIsReg == RegEvalResult)
  {
    *pResult = RegDescr.Reg;
    *pSize = EvalResult.DataSize;
  }
  else
  {
    *pResult = 0;
    *pSize = eSymbolSizeUnknown;
  }
  return RegEvalResult;
}

static Boolean DecodeSpecReg(char *Asc, Byte *Result)
{
  if (!as_strcasecmp(Asc, "SP")) *Result = 0x8c;
  else if (!as_strcasecmp(Asc, "ISP")) *Result = 0x81;
  else if (!as_strcasecmp(Asc, "ESP")) *Result = 0x83;
  else if (!as_strcasecmp(Asc, "PBP")) *Result = 0x05;
  else if (!as_strcasecmp(Asc, "CBP")) *Result = 0x07;
  else if (!as_strcasecmp(Asc, "PSW")) *Result = 0x89;
  else if (!as_strcasecmp(Asc, "IMC")) *Result = 0x0b;
  else if (!as_strcasecmp(Asc, "CC"))  *Result = 0x0e;
  else return False;
  return True;
}

static Boolean DecodeRegAdr(const tStrComp *pArg, Byte *pResult)
{
  tSymbolSize Size;

  if (DecodeReg(pArg, pResult, &Size, True) != eIsReg)
    return False;
  if (OpSize == eSymbolSizeUnknown)
    OpSize = Size;
  if (Size != OpSize)
  {
    WrError(ErrNum_ConfOpSizes);
    return False;
  }
  *pResult &= 0x3f;
  return True;
}

typedef enum
{
  eImmNo,
  eImmYes,
  eImmAsAbs
} tImmAllow;

#define FreeReg 0xff
#define SPReg 0xfe
#define PCReg 0xfd

typedef struct
{
  as_eval_cb_data_t cb_data;
  Byte base_reg, ind_reg, scale_fact;
  tSymbolSize base_reg_size, ind_reg_size;
} tlcs9000_eval_cb_data_t;

DECLARE_AS_EVAL_CB(tlcs9000_eval_cb)
{
  tlcs9000_eval_cb_data_t *p_eval_cb_data = (tlcs9000_eval_cb_data_t*)p_data;
  Byte this_reg;
  tSymbolSize size;

  UNUSED(p_res);

  switch (DecodeReg(p_arg, &this_reg, &size, False))
  {
    case eIsReg:
      break;
    case eRegAbort:
      return e_eval_fail;
    default:
      if (!as_strcasecmp(p_arg->str.p_str, "PC"))
      {
        this_reg = PCReg;
        size = eSymbolSize32Bit;
      }
      else if (!as_strcasecmp(p_arg->str.p_str, "SP"))
      {
        this_reg = SPReg;
        size = eSymbolSize32Bit;
      }
      else
        return e_eval_none;
  }

  if (size == eSymbolSize8Bit)
  {
    WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
    return e_eval_fail;
  }

  /* I.4.c. Basisregister */

  if (as_eval_cb_data_stack_plain_add(p_data->p_stack))
  {
    if (p_eval_cb_data->base_reg == FreeReg)
    {
      p_eval_cb_data->base_reg = this_reg;
      p_eval_cb_data->base_reg_size = size;
    }
    else if (p_eval_cb_data->ind_reg == FreeReg)
    {
      p_eval_cb_data->ind_reg = this_reg;
      p_eval_cb_data->ind_reg_size = size;
      p_eval_cb_data->scale_fact = 0;
    }
    else
    {
      WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
      return e_eval_fail;
    }
    as_tempres_set_int(p_res, 0);
    return e_eval_ok;
  }

  /* I.4.b. Indexregister mit Skalierung */

  else if (p_data->p_stack
        && as_eval_cb_data_stackelem_mul(p_data->p_stack)
        && as_eval_cb_data_stack_plain_add(p_data->p_stack->p_next))
  {
    if (!p_data->p_other_arg || (p_data->p_other_arg->Typ != TempInt))
    {
      WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
      return e_eval_fail;
    }
    for (p_eval_cb_data->scale_fact = 0;
         p_eval_cb_data->scale_fact < 4;
         p_eval_cb_data->scale_fact++)
      if (p_data->p_other_arg->Contents.Int == 1 << p_eval_cb_data->scale_fact)
        break;
    if (p_eval_cb_data->scale_fact >= 4)
    {
      WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
      return e_eval_fail;
    }
    if (p_eval_cb_data->ind_reg == FreeReg)
    {
      p_eval_cb_data->ind_reg = this_reg;
      p_eval_cb_data->ind_reg_size = size;
    }
    else
    {
      WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
      return e_eval_fail;
    }
    as_tempres_set_int(p_res, 0);
    return e_eval_ok;
  }
  else
  {
    WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
    return e_eval_fail;
  }
}

static Boolean force_long_op(TempResult *pErg, TempResult *pLVal, TempResult *pRVal)
{
  UNUSED(pLVal);
  if (!pRVal)
    return False;

  /* clone value as-is */

  as_tempres_copy_value(pErg, pRVal);
  pErg->Flags |= (pLVal->Flags & eSymbolFlags_Promotable) | eSymbolFlag_UserLong;
  pErg->DataSize = pLVal->DataSize;
  return True;
}

/* NOTE: as unary operator, '>' binds stronger than as binary operator, similar
   to '~' : */

static const as_operator_t tlcs9000_operators[] =
{
  { ">" ,1 , e_op_monadic, 1, { TempInt | (TempInt << 4), 0, 0, 0 }, force_long_op},
  {NULL, 0 , e_op_monadic, 0, { 0, 0, 0, 0, 0 }, NULL}
};

static void DecodeAdr(const tStrComp *pArg, Byte PrefInd, tImmAllow MayImm, Boolean MayReg)
{
  Byte Reg;
  String AdrPartStr;
  tStrComp AdrPart;
  Boolean OK;
  int ArgLen;
  tSymbolSize Size;
  tRegEvalResult RegEvalResult;

  AdrCnt = 0; AdrOK = False;
  StrCompMkTemp(&AdrPart, AdrPartStr, sizeof(AdrPartStr));

   /* I. Speicheradresse */

  if (IsIndirect(pArg->str.p_str))
  {
    tStrComp Arg, TmpComp;
    String Tmp;
    tlcs9000_eval_cb_data_t eval_cb_data;
    tEvalResult eval_result;
    LongInt DispAcc;
    Boolean force_prefix;

    StrCompMkTemp(&TmpComp, Tmp, sizeof(Tmp));

    /* I.1. vorkonditionieren */

    StrCompRefRight(&Arg, pArg, 1);
    StrCompShorten(&Arg, 1);
    KillPrefBlanksStrCompRef(&Arg);
    KillPostBlanksStrComp(&Arg);

    /* I.2. Predekrement */

    if ((*Arg.str.p_str == '-') && (Arg.str.p_str[1] == '-'))
    {
      tStrComp RegComp;

      StrCompRefRight(&RegComp, &Arg, 2);
      if (DecodeReg(&RegComp, &Reg, &Size, True) == eIsReg)
      {
        switch (Size)
        {
          case eSymbolSize16Bit:
            AdrMode = 0x50 + (Reg & 15);
            AdrOK = True;
            break;
          case eSymbolSize32Bit:
            AdrMode = 0x71 + (Reg & 14);
            AdrOK = True;
            break;
          default:
            WrStrErrorPos(ErrNum_InvAddrMode, &Arg);
            break;
        }
      }
      return;
    }

    /* I.3. Postinkrement */

    ArgLen = strlen(Arg.str.p_str);
    if ((Arg.str.p_str[ArgLen - 1] == '+') && (Arg.str.p_str[ArgLen - 2] == '+'))
    {
      StrCompCopySub(&AdrPart, &Arg, 0, Arg.Pos.Len - 2);
      if (DecodeReg(&AdrPart, &Reg, &Size, True) == eIsReg)
      {
        switch (Size)
        {
          case eSymbolSize16Bit:
            AdrMode = 0x40 + (Reg & 15);
            AdrOK = True;
            break;
          case eSymbolSize32Bit:
            AdrMode = 0x70 + (Reg & 14);
            AdrOK = True;
            break;
          default:
            WrError(ErrNum_InvAddrMode);
            break;
        }
        return;
      }
    }

    /* I.4. Adresskomponenten zerlegen */

    as_eval_cb_data_ini(&eval_cb_data.cb_data, tlcs9000_eval_cb);
    eval_cb_data.cb_data.p_operators = tlcs9000_operators;
    eval_cb_data.base_reg = eval_cb_data.ind_reg = FreeReg;
    eval_cb_data.base_reg_size = eval_cb_data.ind_reg_size = eSymbolSizeUnknown;
    eval_cb_data.scale_fact = 0;
    DispAcc = AdrInc + EvalStrIntExprWithResultAndCallback(&Arg, Int32, &eval_result, &eval_cb_data.cb_data);
    if (!eval_result.OK)
      return;
    force_prefix = !!(eval_result.Flags & eSymbolFlag_UserLong);

    /* I.5. Indexregister mit Skalierung 1 als Basis behandeln */

    if ((eval_cb_data.base_reg == FreeReg) && (eval_cb_data.ind_reg != FreeReg) && (eval_cb_data.scale_fact == 0))
    {
      eval_cb_data.base_reg = eval_cb_data.ind_reg;
      eval_cb_data.base_reg_size = eval_cb_data.ind_reg_size;
      eval_cb_data.ind_reg = FreeReg;
    }

    /* I.6. absolut */

    if ((eval_cb_data.base_reg == FreeReg) && (eval_cb_data.ind_reg == FreeReg))
    {
      AdrMode = 0x20; /* 0x60 should be equivalent: adding 0 as RW0 or RD0 is irrelvant */
      AdrVals[0] = 0xe000 + (DispAcc & 0x1fff); AdrCnt = 2;
      AddAbsPrefix(PrefInd, 13, DispAcc, force_prefix);
      AdrOK = True;
      return;
    }

    /* I.7. Basis [mit Displacement] */

    if ((eval_cb_data.base_reg != FreeReg) && (eval_cb_data.ind_reg == FreeReg))
    {
      /* I.7.a. Basis ohne Displacement */

      if (DispAcc == 0)
      {
        if (eval_cb_data.base_reg_size == eSymbolSize16Bit)
          AdrMode = 0x10 + (eval_cb_data.base_reg & 15);
        else
          AdrMode = 0x61 + (eval_cb_data.base_reg & 14);
        AdrOK = True;
        return;
      }

      /* I.7.b. Nullregister mit Displacement muss in Erweiterungswort */

      else if (eval_cb_data.base_reg == 0)
      {
        if (DispAcc > 0x7ffff) WrError(ErrNum_OverRange);
        else if (DispAcc < -0x80000) WrError(ErrNum_UnderRange);
        else
        {
          AdrMode = 0x20;
          if (eval_cb_data.base_reg_size == eSymbolSize16Bit)
            AdrVals[0] = ((Word)eval_cb_data.base_reg & 15) << 11;
          else
            AdrVals[0] = (((Word)eval_cb_data.base_reg & 14) << 11) + 0x8000;
          AdrVals[0] += DispAcc & 0x1ff;
          AdrCnt = 2;
          AddSignedPrefix(PrefInd, 9, DispAcc, force_prefix);
          AdrOK = True;
        }
        return;
      }

      /* I.7.c. Stack mit Displacement: Optimierung moeglich */

      else if (eval_cb_data.base_reg == SPReg)
      {
        if (DispAcc > 0x7ffff) WrError(ErrNum_OverRange);
        else if (DispAcc < -0x80000) WrError(ErrNum_UnderRange);
        else if ((DispAcc >= 0) && (DispAcc <= 127))
        {
          AdrMode = 0x80 + (DispAcc & 0x7f);
          AdrOK = True;
        }
        else
        {
          AdrMode = 0x20;
          AdrVals[0] = 0xd000 + (DispAcc & 0x1ff); AdrCnt = 2;
          AddSignedPrefix(PrefInd, 9, DispAcc, force_prefix);
          AdrOK = True;
        }
        return;
      }

      /* I.7.d. Programmzaehler mit Displacement: keine Optimierung */

      else if (eval_cb_data.base_reg == PCReg)
      {
        if (DispAcc > 0x7ffff) WrError(ErrNum_OverRange);
        else if (DispAcc < -0x80000) WrError(ErrNum_UnderRange);
        else
        {
          AdrMode = 0x20;
          AdrVals[0] = 0xd800 + (DispAcc & 0x1ff);
          AdrCnt = 2;
          AddSignedPrefix(PrefInd, 9, DispAcc, force_prefix);
          AdrOK = True;
        }
        return;
      }

      /* I.7.e. einfaches Basisregister mit Displacement */

      else
      {
        if (DispAcc > 0x7fffff) WrError(ErrNum_OverRange);
        else if (DispAcc < -0x800000) WrError(ErrNum_UnderRange);
        else
        {
          if (eval_cb_data.base_reg_size == eSymbolSize16Bit)
            AdrMode = 0x20 + (eval_cb_data.base_reg & 15);
          else
            AdrMode = 0x60 + (eval_cb_data.base_reg & 14);
          AdrVals[0] = 0xe000 + (DispAcc & 0x1fff);
          AdrCnt = 2;
          AddSignedPrefix(PrefInd, 13, DispAcc, force_prefix);
          AdrOK = True;
        }
        return;
      }
    }

    /* I.8. Index- [und Basisregister] */

    else
    {
      if (DispAcc > 0x7ffff) WrError(ErrNum_OverRange);
      else if (DispAcc < -0x80000) WrError(ErrNum_UnderRange);
      else if ((eval_cb_data.ind_reg & 15) == 0) WrError(ErrNum_InvAddrMode);
      else
      {
        if (eval_cb_data.ind_reg_size == eSymbolSize16Bit)
          AdrMode = 0x20 + (eval_cb_data.ind_reg & 15);
        else
          AdrMode = 0x60 + (eval_cb_data.ind_reg & 14);
        switch (eval_cb_data.base_reg)
        {
          case FreeReg:
            AdrVals[0] = 0xc000; break;
          case SPReg:
            AdrVals[0] = 0xd000; break;
          case PCReg:
            AdrVals[0] = 0xd800; break;
          default:
            if (eval_cb_data.base_reg_size == eSymbolSize16Bit)
              AdrVals[0] = ((Word)eval_cb_data.base_reg & 15) << 11;
            else
              AdrVals[0] = 0x8000 + (((Word)eval_cb_data.base_reg & 14) << 10);
        }
        AdrVals[0] += (((Word)eval_cb_data.scale_fact) << 9) + (DispAcc & 0x1ff);
        AdrCnt = 2;
        AddSignedPrefix(PrefInd, 9, DispAcc, force_prefix);
        AdrOK = True;
      }
      return;
    }
  }

  /* II. Arbeitsregister */

  else if ((RegEvalResult = DecodeReg(pArg, &Reg, &Size, False)) != eIsNoReg)
  {
    if (RegEvalResult == eRegAbort)
      return;
    if (!MayReg) WrStrErrorPos(ErrNum_InvAddrMode, pArg);
    else
    {
      if (OpSize == eSymbolSizeUnknown)
        OpSize = Size;
      if (Size != OpSize) WrError(ErrNum_ConfOpSizes);
      else
      {
        AdrMode = Reg & 15;
        AdrOK = True;
      }
    }
    return;
  }

  /* III. Spezialregister */

  else if (DecodeSpecReg(pArg->str.p_str, &Reg))
  {
    if (!MayReg) WrError(ErrNum_InvAddrMode);
    else
    {
      if (OpSize == -1)
        OpSize=Reg >> 6;
      if ((Reg >> 6) != OpSize) WrError(ErrNum_ConfOpSizes);
      else
      {
        AdrMode = 0x30 + (Reg & 15);
        AdrOK = True;
      }
    }
    return;
  }

  else switch (MayImm)
  {
    case eImmNo:
      WrError(ErrNum_InvAddrMode);
      break;
    case eImmAsAbs:
    {
      Boolean ForcePrefix = False;
      LongInt DispAcc = EvalStrIntExpressionOffs(pArg, CheckForcePrefix(pArg->str.p_str, &ForcePrefix), Int32, &AdrOK);

      if (AdrOK)
      {
        AdrMode = 0x20; /* 0x60 should be equivalent: adding 0 as RW0 or RD0 is irrelvant */
        AdrVals[0] = 0xe000 + (DispAcc & 0x1fff); AdrCnt = 2;
        AddAbsPrefix(PrefInd, 13, DispAcc, ForcePrefix);
        AdrOK = True;
      }
      break;
    }
    case eImmYes:
      if ((OpSize == -1) && (MinOneIs0))
        OpSize = 0;
      if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
      else
      {
        AdrMode = 0x30;
        switch (OpSize)
        {
          case 0:
            AdrVals[0] = EvalStrIntExpression(pArg, Int8, &OK) & 0xff;
            if (OK)
            {
              AdrCnt = 2;
              AdrOK = True;
            }
            break;
          case 1:
            AdrVals[0] = EvalStrIntExpression(pArg, Int16, &OK);
            if (OK)
            {
              AdrCnt = 2;
              AdrOK = True;
            }
            break;
          case 2:
          {
            LongInt DispAcc = EvalStrIntExpression(pArg, Int32, &OK);

            if (OK)
            {
              AdrVals[0] = DispAcc & 0xffff;
              AdrVals[1] = DispAcc >> 16;
              AdrCnt = 4;
              AdrOK = True;
            }
            break;
          }
        }
      }
      break;
  }
}

static void CopyAdr(void)
{
  OpSize2 = OpSize;
  AdrMode2 = AdrMode;
  AdrCnt2 = AdrCnt;
  memcpy(AdrVals2, AdrVals, AdrCnt);
}

static Boolean IsReg(void)
{
  return (AdrMode <= 15);
}

static Boolean Is2Reg(void)
{
  return (AdrMode2 <= 15);
}

static Boolean IsImmediate(void)
{
  return (AdrMode == 0x30);
}

static Boolean Is2Immediate(void)
{
  return (AdrMode2 == 0x30);
}

static LongInt ImmVal(void)
{
  LongInt Tmp1;
  Integer Tmp2;
  ShortInt Tmp3;

  switch (OpSize)
  {
    case 0:
      Tmp3 = AdrVals[0] & 0xff;
      return Tmp3;
    case 1:
      Tmp2 = AdrVals[0];
      return Tmp2;
    case 2:
      Tmp1 = (((LongInt)AdrVals[1]) << 16) + AdrVals[0];
      return Tmp1;
    default:
      WrError(ErrNum_InternalError);
      return 0;
  }
}

static LongInt ImmVal2(void)
{
  LongInt Tmp1;
  Integer Tmp2;
  ShortInt Tmp3;

  switch (OpSize)
  {
    case 0:
      Tmp3 = AdrVals2[0] & 0xff;
      return Tmp3;
    case 1:
      Tmp2 = AdrVals2[0];
      return Tmp2;
    case 2:
      Tmp1 = (((LongInt)AdrVals2[1]) << 16) + AdrVals2[0];
      return Tmp1;
    default:
      WrError(ErrNum_InternalError);
      return 0;
  }
}

static Boolean IsAbsolute(void)
{
  return (((AdrMode == 0x20) || (AdrMode == 0x60))
       && (AdrCnt == 2)
       && ((AdrVals[0] & 0xe000) == 0xe000));
}

static Boolean Is2Absolute(void)
{
  return (((AdrMode2 == 0x20) || (AdrMode2 == 0x60))
       && (AdrCnt2 == 2)
       && ((AdrVals2[0] & 0xe000) == 0xe000));
}

static Boolean IsShort(void)
{
  if (AdrMode < 0x30)
    return True;
  else if (AdrMode == 0x30)
  {
    LongInt ImmValue = ImmVal();

    return ((ImmValue >= LowLim4) && (ImmValue <= 7));
  }
  else
    return False;
}

static Boolean Is2Short(void)
{
  if (AdrMode2 < 0x30)
    return True;
  else if (AdrMode2 == 0x30)
  {
    LongInt ImmValue = ImmVal2();

    return ((ImmValue >= LowLim4) && (ImmValue <= 7));
  }
  else
    return False;
}

static void ConvertShort(void)
{
  if (AdrMode == 0x30)
  {
    AdrMode += ImmVal() & 15;
    AdrCnt = 0;
  }
}

static void Convert2Short(void)
{
  if (AdrMode2 == 0x30)
  {
    AdrMode2 += ImmVal2() & 15;
    AdrCnt2 = 0;
  }
}

static void SetULowLims(void)
{
  LowLim4 = 0;
  LowLim8 = 0;
}

static void AddPrefixes(void)
{
  if (CodeLen != 0)
  {
    InsertSinglePrefix(1);
    InsertSinglePrefix(0);
  }
}

static Boolean DecodeCondition(const char *pAsc, Word *pCondition)
{
  size_t z;

  for (z = 0; z < as_array_size(Conditions); z++)
    if (!as_strcasecmp(pAsc, Conditions[z]))
    {
      *pCondition = z;
      return True;
    }
  return False;
}

static char DecideGA(void)
{
  if (((IsShort()) && (Is2Absolute()))
   || ((Is2Short()) && (IsAbsolute())))
    return 'A';
  else
    return 'G';
}

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

static void DecodeFixed(Word Code)
{
  if (!ChkArgCnt(0, 0));
  else if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else
  {
    WAsmCode[0] = Code;
    CodeLen = 2;
  }
}

static void DecodeRMW(Word Index)
{
  const RMWOrder *pOrder = RMWOrders + Index;

  if ((OpSize == -1) && (pOrder->Mask & 0x20))
    OpSize = 2;
  if (ChkArgCnt(1, 1))
  {
    tImmAllow AllowImm = (pOrder->Mask & 0x10) ? eImmNo : eImmYes;

    if (!IsIndirect(ArgStr[1].str.p_str) && (pOrder->Mask & 0x20))
      AllowImm = eImmAsAbs;
    DecodeAdr(&ArgStr[1], 0, AllowImm, !(pOrder->Mask & 0x20));
    if (AdrOK)
    {
      if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
      else if (!(pOrder->Mask & (1 << OpSize))) WrError(ErrNum_InvOpSize);
      else
      {
        WAsmCode[0] = (((Word)OpSize + 1) << 14) + (((Word)pOrder->Code) << 8) + AdrMode;
        memcpy(WAsmCode + 1, AdrVals, AdrCnt);
        CodeLen = 2 + AdrCnt;
      }
    }
  }
}

static void DecodeGASI1(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], 1, eImmNo, True);
    if (AdrOK)
    {
      CopyAdr();
      DecodeAdr(&ArgStr[2], 0, eImmYes, True);
      if (AdrOK)
      {
        if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
        else
        {
          LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

          if (Format == ' ')
          {
            if (((IsReg()) && (Is2Short()))
             || ((Is2Reg()) && (IsShort())))
              Format = 'S';
            else if ((IsImmediate()) && (OpSize > 0) && ((ImmValue > 127) || (ImmValue < -128)))
              Format = 'I';
            else
              Format = DecideGA();
          }
          switch (Format)
          {
            case 'G':
              WAsmCode[0] = 0x700 + (((Word)OpSize + 1) << 14);
              if ((IsImmediate()) && (ImmValue <= 127) && (ImmValue >= -128))
              {
                AdrMode = ImmValue & 0xff;
                AdrCnt = 0;
              }
              else
                WAsmCode[0] += 0x800;
              WAsmCode[0] += AdrMode;
              memcpy(WAsmCode + 1, AdrVals, AdrCnt);
              WAsmCode[1 + (AdrCnt >> 1)] = 0x8400 + (Code << 8) + AdrMode2;
              memcpy(WAsmCode + 2 + (AdrCnt >> 1), AdrVals2, AdrCnt2);
              CodeLen = 4 + AdrCnt + AdrCnt2;
              break;
            case 'A':
              if ((IsShort()) && (Is2Absolute()))
              {
                ConvertShort();
                WAsmCode[0] = 0x3900
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode & 0xf0) << 5)
                            + (AdrMode & 15);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = (AdrVals2[0] & 0x1fff) + (Code << 13);
                CodeLen = 4 + AdrCnt;
              }
              else if ((Is2Short()) && (IsAbsolute()))
              {
                Convert2Short();
                WAsmCode[0] = 0x3980
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode2 & 0xf0) << 5)
                            + (AdrMode2 & 15);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                WAsmCode[1 + (AdrCnt2 >> 1)] = (AdrVals[0] & 0x1fff) + (Code << 13);
                CodeLen = 4 + AdrCnt2;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            case 'S':
              if ((IsShort()) && (Is2Reg()))
              {
                ConvertShort();
                WAsmCode[0] = 0x0000
                            + (((Word)OpSize + 1) << 14)
                            + (AdrMode & 15)
                            + ((AdrMode & 0xf0) << 5)
                            + ((AdrMode2 & 1) << 12)
                            + ((AdrMode2 & 14) << 4)
                            + ((Code & 1) << 4)
                            + ((Code & 2) << 10);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                CodeLen = 2 + AdrCnt;
              }
              else if ((Is2Short()) && (IsReg()))
              {
                Convert2Short();
                WAsmCode[0] = 0x0100
                            + (((Word)OpSize + 1) << 14)
                            + (AdrMode2 & 15)
                            + ((AdrMode2 & 0xf0) << 5)
                            + ((AdrMode & 1) << 12)
                            + ((AdrMode & 14) << 4)
                            + ((Code & 1) << 4)
                            + ((Code & 2) << 11);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                CodeLen = 2 + AdrCnt2;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            case 'I':
              if ((!IsImmediate()) || (OpSize == 0)) WrError(ErrNum_InvAddrMode);
              else
              {
                WAsmCode[0] = AdrMode2 + (((Word)OpSize-1) << 11) + (Code << 8);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                memcpy(WAsmCode + 1 + (AdrCnt2 >> 1), AdrVals, AdrCnt);
                CodeLen = 2 + AdrCnt + AdrCnt2;
              }
              break;
            default:
               WrError(ErrNum_InvFormat);
          }
        }
      }
    }
  }
}

static void DecodeGASI2(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], 1, eImmNo, True);
    if (AdrOK)
    {
      CopyAdr();
      DecodeAdr(&ArgStr[2], 0, eImmYes, True);
      if (AdrOK)
      {
        if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
        else
        {
          LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

          if (Format == ' ')
          {
            if ((IsReg()) && (Is2Reg()))
              Format = 'S';
            else if ((IsImmediate()) && (OpSize > 0) && ((ImmValue > 127) || (ImmValue < -128)))
              Format = 'I';
            else
              Format = DecideGA();
          }
          switch (Format)
          {
            case 'G':
              WAsmCode[0] = 0x700 + (((Word)OpSize + 1) << 14);
              if ((IsImmediate()) && (ImmValue <= 127) && (ImmValue >= -128))
              {
                AdrMode = ImmValue & 0xff;
                AdrCnt = 0;
              }
              else
                WAsmCode[0] += 0x800;
              WAsmCode[0] += AdrMode;
              memcpy(WAsmCode + 1, AdrVals, AdrCnt);
              WAsmCode[1 + (AdrCnt >> 1)] = 0xc400 + (Code << 8) + AdrMode2;
              memcpy(WAsmCode + 2 + (AdrCnt >> 1), AdrVals2, AdrCnt2);
              CodeLen = 4 + AdrCnt + AdrCnt2;
              break;
            case 'A':
              if ((IsShort()) && (Is2Absolute()))
              {
                ConvertShort();
                WAsmCode[0] = 0x3940
                            + (((Word)OpSize+1) << 14)
                            + ((AdrMode & 0xf0) << 5)
                            + (AdrMode & 15);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = (AdrVals2[0] & 0x1fff) + (Code << 13);
                CodeLen = 4 + AdrCnt;
              }
              else if ((Is2Short()) && (IsAbsolute()))
              {
                Convert2Short();
                WAsmCode[0] = 0x39c0
                            + (((Word)OpSize+1) << 14)
                            + ((AdrMode2 & 0xf0) << 5)
                            + (AdrMode2 & 15);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                WAsmCode[1 + (AdrCnt2 >> 1)] = (AdrVals[0] & 0x1fff) + (Code << 13);
                CodeLen = 4 + AdrCnt2;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            case 'S':
              if ((IsReg()) && (Is2Reg()))
              {
                WAsmCode[0] = 0x3800
                            + (((Word)OpSize+1) << 14)
                            + (AdrMode & 15)
                            + (AdrMode2 << 4)
                            + (Code << 9);
                CodeLen = 2;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            case 'I':
              if ((!IsImmediate()) || (OpSize == 0)) WrError(ErrNum_InvAddrMode);
              else
              {
                WAsmCode[0] = 0x400 + AdrMode2 + (((Word)OpSize-1) << 11) + (Code << 8);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                memcpy(WAsmCode + 1 + (AdrCnt2 >> 1), AdrVals, AdrCnt);
                CodeLen = 2 + AdrCnt + AdrCnt2;
              }
              break;
            default:
              WrError(ErrNum_InvFormat);
          }
        }
      }
    }
  }
}

static void DecodeTrinom(Word Code)
{
  int Cnt;
  Byte Reg;

  if (Code == 2) /* MAC */
    LowLim8 = 0;
  if (!ChkArgCnt(3, 3));
  else if (DecodeRegAdr(&ArgStr[1], &Reg))
  {
    if (Code >= 2)
      OpSize--;
    if (OpSize < 0) WrError(ErrNum_InvOpSize);
    else
    {
      DecodeAdr(&ArgStr[3], 0, eImmYes, True);
      if (AdrOK)
      {
        LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

        WAsmCode[0] = 0x700;
        if ((IsImmediate()) && (ImmValue < 127) && (ImmValue > LowLim8))
        {
          AdrMode = ImmValue & 0xff;
          AdrCnt = 0;
        }
        else
          WAsmCode[0] += 0x800;
        WAsmCode[0] += (((Word)OpSize + 1) << 14) + AdrMode;
        memcpy(WAsmCode + 1, AdrVals, AdrCnt);
        Cnt = AdrCnt;
        DecodeAdr(&ArgStr[2], 1, eImmNo, True);
        if (AdrOK)
        {
          WAsmCode[1 + (Cnt >> 1)] = AdrMode + (Code << 8) + (((Word)Reg) << 11);
          memcpy(WAsmCode + 2 + (Cnt >> 1), AdrVals, AdrCnt);
          CodeLen = 4 + Cnt + AdrCnt;
        }
      }
    }
  }
}

static void DecodeRLM_RRM(Word Code)
{
  int Cnt;
  Byte Reg;
  tSymbolSize Size;

  if (!ChkArgCnt(3, 3));
  else if (!DecodeReg(&ArgStr[2], &Reg, &Size, True));
  else if (Size != eSymbolSize16Bit) WrStrErrorPos(ErrNum_InvOpSize, &ArgStr[2]);
  else
  {
    Reg &= 0x3f;
    DecodeAdr(&ArgStr[3], 0, eImmYes, True);
    if (AdrOK)
    {
      LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

      WAsmCode[0] = 0x700;
      if ((IsImmediate()) && (ImmValue < 127) && (ImmValue > -128))
      {
        AdrMode = ImmValue & 0xff; AdrCnt = 0;
      }
      else
        WAsmCode[0] += 0x800;
      WAsmCode[0] += AdrMode;
      memcpy(WAsmCode + 1, AdrVals, AdrCnt);
      Cnt = AdrCnt;
      DecodeAdr(&ArgStr[1], 1, eImmNo, True);
      if (AdrOK)
      {
        if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
        else
        {
          WAsmCode[0] += ((Word)OpSize + 1) << 14;
          WAsmCode[1 + (Cnt >> 1)] = Code + (((Word)Reg) << 11)+AdrMode;
          memcpy(WAsmCode + 2 + (Cnt >> 1), AdrVals, AdrCnt);
          CodeLen = 4 + AdrCnt + Cnt;
        }
      }
    }
  }
}

static void DecodeBit(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], 1, eImmNo, True);
    if (AdrOK)
    {
      if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
      else
      {
        CopyAdr();
        OpSize = -1;
        MinOneIs0 = True;
        DecodeAdr(&ArgStr[2], 0, eImmYes, True);
        if (AdrOK)
        {
          LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

          OpSize = OpSize2;
          if (Format==' ')
          {
            if ((Is2Reg()) && (IsImmediate())
             && (ImmValue > 0)
             && (ImmValue < (1 << (OpSize + 3))))
              Format = 'S';
            else
              Format = DecideGA();
          }
          switch (Format)
          {
            case 'G':
              WAsmCode[0] = 0x700 + (((Word)OpSize + 1) << 14);
              if ((IsImmediate()) && (ImmValue >= LowLim8) && (ImmValue < 127))
              {
                AdrMode = ImmValue & 0xff;
                AdrCnt = 0;
              }
              else
                WAsmCode[0] += 0x800;
              WAsmCode[0] += AdrMode;
              memcpy(WAsmCode + 1, AdrVals, AdrCnt);
              WAsmCode[1 + (AdrCnt >> 1)] = 0xd400 + (Code << 8) + AdrMode2;
              memcpy(WAsmCode + 2 + (AdrCnt >> 1), AdrVals2, AdrCnt2);
              CodeLen = 4 + AdrCnt + AdrCnt2;
              break;
            case 'A':
              if ((IsAbsolute()) && (Is2Short()))
              {
                Convert2Short();
                WAsmCode[0] = 0x39d0
                            + (((Word)OpSize+1) << 14)
                            + ((AdrMode2 & 0xf0) << 5)
                            + (AdrMode2 & 15);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                WAsmCode[1 + (AdrCnt2 >> 1)] = (AdrVals[0] & 0x1fff) + (Code << 13);
                CodeLen = 4 + AdrCnt2;
              }
              else if ((Is2Absolute()) && (IsShort()))
              {
                ConvertShort();
                WAsmCode[0] = 0x3950
                            + (((Word)OpSize+1) << 14)
                            + ((AdrMode & 0xf0) << 5)
                            + (AdrMode & 15);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = (AdrVals2[0] & 0x1fff) + (Code << 13);
                CodeLen = 4 + AdrCnt;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            case 'S':
              if ((Is2Reg())
               && (IsImmediate())
               && (ImmVal() >= 0)
               && (ImmVal() < (1 << (3 + OpSize))))
              {
                if (OpSize == 2)
                {
                  if (ImmVal() >= 16)
                  {
                    AdrVals[0] -= 16;
                    AdrMode2++;
                  }
                  OpSize = 1;
                }
                if (OpSize == 1)
                {
                  if (ImmVal() < 8)
                    OpSize=0;
                  else
                    AdrVals[0] -= 8;
                }
                WAsmCode[0] = 0x1700
                            + (((Word)OpSize + 1) << 14)
                            + ((Code & 1) << 7)
                            + ((Code & 2) << 10)
                            + (ImmVal() << 4)
                            + AdrMode2;
                CodeLen = 2;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            default:
              WrError(ErrNum_InvFormat);
          }
        }
      }
    }
  }
}

static void DecodeShift(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], 1, eImmNo, True);
    if (AdrOK)
    {
      if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
      else
      {
        CopyAdr();
        OpSize = -1;
        MinOneIs0 = True;
        DecodeAdr(&ArgStr[2], 0, eImmYes, True);
        if (AdrOK)
        {
          LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

          OpSize = OpSize2;
          if (Format==' ')
          {
            if ((IsImmediate()) && (ImmValue == 1))
              Format = 'S';
            else
              Format = DecideGA();
          }
          switch (Format)
          {
            case 'G':
              WAsmCode[0] = 0x700 + (((Word)OpSize + 1) << 14);
              if ((IsImmediate()) && (ImmValue >= LowLim8) && (ImmVal() < 127))
              {
                AdrMode = ImmValue & 0xff;
                AdrCnt = 0;
              }
              else
                WAsmCode[0] += 0x800;
              WAsmCode[0] += AdrMode;
              memcpy(WAsmCode + 1, AdrVals, AdrCnt);
              WAsmCode[1 + (AdrCnt >> 1)] = 0xb400
                                          + ((Code & 3) << 8)
                                          + ((Code & 4) << 9)
                                          + AdrMode2;
              memcpy(WAsmCode + 2 + (AdrCnt >> 1), AdrVals2, AdrCnt2);
              CodeLen = 4 + AdrCnt + AdrCnt2;
              break;
            case 'A':
              if ((IsAbsolute()) && (Is2Short()))
              {
                Convert2Short();
                WAsmCode[0] = 0x39b0
                            + (((Word)OpSize+1) << 14)
                            + ((AdrMode2 & 0xf0) << 5)
                            + (AdrMode2 & 15);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                WAsmCode[1 + (AdrCnt2 >> 1)] = (AdrVals[0] & 0x1fff) + (Code << 13);
                CodeLen = 4 + AdrCnt2;
              }
              else if ((Is2Absolute()) && (IsShort()))
              {
                ConvertShort();
                WAsmCode[0] = 0x3930
                            + (((Word)OpSize+1) << 14)
                            + ((AdrMode & 0xf0) << 5)
                            + (AdrMode & 15);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = (AdrVals2[0] & 0x1fff)+ (Code << 13);
                CodeLen = 4 + AdrCnt;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            case 'S':
              if ((IsImmediate()) && (ImmValue == 1))
              {
                WAsmCode[0] = 0x2400
                            + (((Word)OpSize+1) << 14)
                            + AdrMode2
                            + ((Code & 3) << 8)
                            + ((Code & 4) << 9);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                CodeLen =2 + AdrCnt2;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            default:
              WrError(ErrNum_InvFormat);
          }
        }
      }
    }
  }
}

static void DecodeBField(Word Code)
{
  Byte Reg, Num1, Num2;
  Boolean OK;
  tSymbolFlags Flags;
  tSymbolSize Size;

  if (ChkArgCnt(4, 4))
  {
    tStrComp *pArg1 = (Code == 2) ? &ArgStr[2] : &ArgStr[1],
             *pArg2 = (Code == 2) ? &ArgStr[1] : &ArgStr[2];

    if (!DecodeReg(pArg1, &Reg, &Size, True));
    else if (Size != eSymbolSize16Bit) WrStrErrorPos(ErrNum_InvOpSize, pArg1);
    else
    {
      Reg &= 0x3f;
      Num2 = EvalStrIntExpressionWithFlags(&ArgStr[4], Int5, &OK, &Flags);
      if (OK)
      {
        if (mFirstPassUnknown(Flags))
          Num2 &= 15;
        Num2--;
        if (Num2 > 15) WrError(ErrNum_OverRange);
        else if ((OpSize == -1) && (!DecodeRegAdr(pArg2, &Num1)));
        else
        {
          switch (OpSize)
          {
            case 0: Num1 = EvalStrIntExpression(&ArgStr[3], UInt3, &OK) & 7; break;
            case 1: Num1 = EvalStrIntExpression(&ArgStr[3], Int4, &OK) & 15; break;
            case 2: Num1 = EvalStrIntExpression(&ArgStr[3], Int5, &OK) & 31; break;
            default: abort();
          }
          if (OK)
          {
            if ((OpSize == 2) && (Num1 > 15))
              AdrInc = 2;
            DecodeAdr(pArg2, 1, eImmNo, True);
            if (AdrOK)
            {
              if ((OpSize == 2) && (Num1 > 15))
              {
                Num1 -= 16;
                OpSize--;
                if (!(AdrMode & 0xf0))
                  AdrMode++;
              }
              WAsmCode[0] = 0x7000 + (((Word)OpSize + 1) << 8) + AdrMode;
              memcpy(WAsmCode + 1, AdrVals, AdrCnt);
              WAsmCode[1 + (AdrCnt >> 1)] = (((Word)Reg) << 11)
                                          + Num2
                                          + (((Word)Num1) << 5)
                                          + ((Code & 1) << 10)
                                          + ((Code & 2) << 14);
              CodeLen = 4 + AdrCnt;
            }
          }
        }
      }
    }
  }
}

static void DecodeGAEq(Word Code)
{
  if (Hi(Code))
    SetULowLims();
  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], 1, eImmNo, True);
    if (AdrOK)
    {
      CopyAdr();
      DecodeAdr(&ArgStr[2], 0, eImmYes, True);
      if (AdrOK)
      {
        if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
        else
        {
          if (OpSize == 0)
            LowLim8 = -128;
          if (Format == ' ')
            Format = DecideGA();
          switch (Format)
          {
            case 'G':
            {
              LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

              WAsmCode[0] = 0x700 + (((Word)OpSize + 1) << 14);
              if ((IsImmediate()) && (ImmValue < 127) && (ImmValue > LowLim8))
              {
                AdrMode = ImmValue & 0xff;
                AdrCnt = 0;
              }
              else
                WAsmCode[0] += 0x800;
              WAsmCode[0] += AdrMode;
              memcpy(WAsmCode + 1, AdrVals, AdrCnt);
              WAsmCode[1 + (AdrCnt >> 1)] = 0x8400
                                          + AdrMode2
                                          + ((Code & 0xf0) << 8)
                                          + ((Code & 4) << 9)
                                          + ((Code & 3) << 8);
              memcpy(WAsmCode + 2 + (AdrCnt >> 1), AdrVals2, AdrCnt2);
              CodeLen = 4 + AdrCnt + AdrCnt2;
              break;
            }
            case 'A':
              if ((IsAbsolute()) && (Is2Short()))
              {
                Convert2Short();
                WAsmCode[0] = 0x3980
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode2 & 0xf0) << 5)
                            + (AdrMode2 & 15)
                            + (Code & 0xf0);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                WAsmCode[1 + (AdrCnt2 >> 1)] = (AdrVals[0] & 0x1fff)
                                             + ((Code & 15) << 13);
                CodeLen = 4 + AdrCnt2;
              }
              else if ((Is2Absolute()) && (IsShort()))
              {
                ConvertShort();
                WAsmCode[0] = 0x3900
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode & 0xf0) << 5)
                            + (AdrMode & 15)
                            + (Code & 0xf0);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = (AdrVals2[0] & 0x1fff)
                                            + ((Code & 15) << 13);
                CodeLen = 4 + AdrCnt;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            default:
              WrError(ErrNum_InvFormat);
          }
        }
      }
    }
  }
}

static void DecodeGAHalf(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], 1, eImmNo, True);
    if (AdrOK)
    {
      if (OpSize == 0) WrError(ErrNum_InvOpSize);
      else
      {
        if (OpSize != -1)
          OpSize--;
        CopyAdr();
        DecodeAdr(&ArgStr[2], 0, eImmYes, True);
        if (AdrOK)
        {
          if (OpSize == 2) WrError(ErrNum_InvOpSize);
          else if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
          else
          {
            if (Format == ' ')
              Format = DecideGA();
            switch (Format)
            {
              case 'G':
              {
                LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

                WAsmCode[0] = 0x700 + (((Word)OpSize + 1) << 14);
                if ((IsImmediate()) && (ImmValue < 127) && (ImmValue > LowLim8))
                {
                  AdrMode = ImmValue & 0xff;
                  AdrCnt = 0;
                }
                else
                  WAsmCode[0] += 0x800;
                WAsmCode[0] += AdrMode;
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = 0x8400
                                            + AdrMode2
                                            + ((Code & 0xf0) << 8)
                                            + ((Code & 4) << 9)
                                            + ((Code & 3) << 8);
                memcpy(WAsmCode + 2 + (AdrCnt >> 1), AdrVals2, AdrCnt2);
                CodeLen = 4 + AdrCnt + AdrCnt2;
                break;
              }
              case 'A':
                if ((IsAbsolute()) && (Is2Short()))
                {
                  Convert2Short();
                  WAsmCode[0] = 0x3980
                              + (((Word)OpSize + 1) << 14)
                              + ((AdrMode2 & 0xf0) << 5)
                              + (AdrMode2 & 15)
                              + (Code & 0xf0);
                  memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                  WAsmCode[1 + (AdrCnt2 >> 1)] = (AdrVals[0] & 0x1fff)
                                               + ((Code & 15) << 13);
                  CodeLen = 4 + AdrCnt2;
                }
                else if ((Is2Absolute()) && (IsShort()))
                {
                  ConvertShort();
                  WAsmCode[0] = 0x3900
                              + (((Word)OpSize + 1) << 14)
                              + ((AdrMode & 0xf0) << 5)
                              + (AdrMode & 15)
                              + (Code & 0xf0);
                  memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                  WAsmCode[1 + (AdrCnt >> 1)] = (AdrVals2[0] & 0x1fff)
                                              + ((Code & 15) << 13);
                  CodeLen = 4 + AdrCnt;
                }
                else WrError(ErrNum_InvAddrMode);
                break;
              default:
                WrError(ErrNum_InvFormat);
            }
          }
        }
      }
    }
  }
}

static void DecodeGAFirst(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], 1, (Memo("STCF") || Memo("TSET")) ? eImmNo : eImmYes, True);
    if (AdrOK)
    {
      if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
      else
      {
        CopyAdr();
        OpSize = -1;
        MinOneIs0 = True;
        DecodeAdr(&ArgStr[2], 0, eImmYes, True);
        OpSize = OpSize2;
        if (AdrOK)
        {
          if (Format == ' ')
            Format = DecideGA();
          switch (Format)
          {
            case 'G':
            {
              LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

              WAsmCode[0] = 0x700
                          + (((Word)OpSize + 1) << 14);
              if ((IsImmediate()) && (ImmValue < 127) && (ImmValue > LowLim8))
              {
                AdrMode = ImmValue & 0xff;
                AdrCnt = 0;
              }
              else WAsmCode[0] += 0x800;
              WAsmCode[0] += AdrMode;
              memcpy(WAsmCode + 1, AdrVals, AdrCnt);
              WAsmCode[1 + (AdrCnt >> 1)] = 0x8400
                                          + AdrMode2
                                          + ((Code & 0xf0) << 8)
                                          + ((Code & 4) << 9)
                                          + ((Code & 3) << 8);
              memcpy(WAsmCode + 2 + (AdrCnt >> 1), AdrVals2, AdrCnt2);
              CodeLen = 4 + AdrCnt + AdrCnt2;
              break;
            }
            case 'A':
              if ((IsAbsolute()) && (Is2Short()))
              {
                Convert2Short();
                WAsmCode[0] = 0x3980
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode2 & 0xf0) << 5)
                            + (AdrMode2 & 15)
                            + (Code & 0xf0);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                WAsmCode[1 + (AdrCnt2 >> 1)] = (AdrVals[0] & 0x1fff)
                                             + ((Code & 15) << 13);
                CodeLen = 4 + AdrCnt2;
              }
              else if ((Is2Absolute()) && (IsShort()))
              {
                ConvertShort();
                WAsmCode[0] = 0x3900
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode & 0xf0) << 5)
                            + (AdrMode & 15)
                            + (Code & 0xf0);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = (AdrVals2[0] & 0x1fff)
                                            + ((Code & 15) << 13);
                CodeLen = 4 + AdrCnt;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            default:
              WrError(ErrNum_InvFormat);
          }
        }
      }
    }
  }
}

static void DecodeGASecond(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[2], 0, eImmYes, True);
    if (AdrOK)
    {
      if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
      else
      {
        CopyAdr();
        OpSize = -1;
        DecodeAdr(&ArgStr[1], 1, eImmNo, True);
        OpSize = OpSize2;
        if (AdrOK)
        {
          if (Format == ' ')
            Format = DecideGA();
          switch (Format)
          {
            case 'G':
              WAsmCode[0] = 0x700 + (((Word)OpSize + 1) << 14);
              if ((Is2Immediate()) && (ImmVal2() < 127) && (ImmVal2() > LowLim8))
              {
                AdrMode2 = ImmVal2() & 0xff;
                AdrCnt = 0;
              }
              else
                WAsmCode[0] += 0x800;
              WAsmCode[0] += AdrMode2;
              memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
              WAsmCode[1 + (AdrCnt2 >> 1)] = 0x8400
                                           + AdrMode
                                           + ((Code & 0xf0) << 8)
                                           + ((Code & 4) << 9)
                                           + ((Code & 3) << 8);
              memcpy(WAsmCode + 2 + (AdrCnt2 >> 1), AdrVals, AdrCnt);
              CodeLen = 4 + AdrCnt + AdrCnt2;
              break;
            case 'A':
              if ((IsAbsolute()) && (Is2Short()))
              {
                Convert2Short();
                WAsmCode[0] = 0x3900
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode2 & 0xf0) << 5)
                            + (AdrMode2 & 15)
                            + (Code & 0xf0);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                WAsmCode[1 + (AdrCnt2 >> 1)] = (AdrVals[0] & 0x1fff)
                                             + ((Code & 15) << 13);
                CodeLen = 4 + AdrCnt2;
              }
              else if ((Is2Absolute()) && (IsShort()))
              {
                ConvertShort();
                WAsmCode[0] = 0x3980 + (((Word)OpSize + 1) << 14)
                                     + ((AdrMode & 0xf0) << 5)
                                     + (AdrMode & 15)
                                     + (Code & 0xf0);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = (AdrVals2[0] & 0x1fff)
                                            + ((Code & 15) << 13);
                CodeLen = 4 + AdrCnt;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            default:
              WrError(ErrNum_InvFormat);
          }
        }
      }
    }
  }
}

static void DecodeCHK_CHKS(Word IsSigned)
{
  if (!IsSigned)
    SetULowLims();
  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[2], 1, eImmNo, True);
    if (AdrOK)
    {
      if ((OpSize != 1) && (OpSize != 2)) WrError(ErrNum_InvOpSize);
      else if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
      else
      {
        CopyAdr();
        DecodeAdr(&ArgStr[1], 0, eImmNo, False);
        if (AdrOK)
        {
          if (OpSize == 0)
            LowLim8 = -128;
          if (Format == ' ')
            Format = DecideGA();
          switch (Format)
          {
            case 'G':
              WAsmCode[0] = 0xf00 + (((Word)OpSize + 1) << 14) + AdrMode2;
              memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
              WAsmCode[1 + (AdrCnt2 >> 1)] = 0xa600 + AdrMode
                                           + (IsSigned << 8);
              memcpy(WAsmCode + 2 + (AdrCnt2 >> 1), AdrVals, AdrCnt);
              CodeLen = 4 + AdrCnt + AdrCnt2;
              break;
            case 'A':
              if ((IsAbsolute()) && (Is2Short()))
              {
                Convert2Short();
                WAsmCode[0] = 0x3920
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode2 & 0xf0) << 5)
                            + (AdrMode2 & 15);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                WAsmCode[1 + (AdrCnt2 >> 1)] = 0x4000
                                             + (AdrVals[0] & 0x1fff)
                                             + (IsSigned << 13);
                CodeLen = 4 + AdrCnt2;
              }
              else if ((Is2Absolute()) && (IsShort()))
              {
                ConvertShort();
                WAsmCode[0] = 0x39a0
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode & 0xf0) << 5)
                            + (AdrMode & 15);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = 0x4000
                                            + (AdrVals2[0] & 0x1fff)
                                            + (IsSigned << 13);
                CodeLen = 4 + AdrCnt;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            default:
              WrError(ErrNum_InvFormat);
          }
        }
      }
    }
  }
}

static void DecodeString(Word Code)
{
  Byte Reg;
  int Cnt;
  tSymbolSize Size;

  if (!ChkArgCnt(3, 3));
  else if (!DecodeReg(&ArgStr[3], &Reg, &Size, True));
  else if (Size != eSymbolSize16Bit) WrStrErrorPos(ErrNum_InvOpSize, &ArgStr[3]);
  else
  {
    Reg &= 0x3f;
    DecodeAdr(&ArgStr[2], 0, eImmYes, True);
    if (AdrOK)
    {
      LongInt ImmValue = IsImmediate() ? ImmVal() : 0;

      WAsmCode[0] = 0x700;
      if ((IsImmediate()) && (ImmValue < 127) && (ImmValue > LowLim8))
      {
        AdrMode = ImmValue & 0xff;
        AdrCnt = 0;
      }
      else
        WAsmCode[0] += 0x800;
      WAsmCode[0] += AdrMode;
      memcpy(WAsmCode + 1, AdrVals, AdrCnt);
      Cnt = AdrCnt;
      DecodeAdr(&ArgStr[1], 1, eImmYes, True);
      if (AdrOK)
      {
        if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
        else
        {
          WAsmCode[0] += ((Word)OpSize + 1) << 14;
          WAsmCode[1 + (Cnt >> 1)] = 0x8000 + AdrMode + (Code << 8) + (((Word)Reg) << 11);
          memcpy(WAsmCode + 2 + (Cnt >> 1), AdrVals, AdrCnt);
          CodeLen = 4 + AdrCnt + Cnt;
        }
      }
    }
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], 1, eImmNo, True);
    if (AdrOK)
    {
      CopyAdr();
      DecodeAdr(&ArgStr[2], 0, eImmNo, True);
      if (AdrOK)
      {
        if (OpSize == -1) WrError(ErrNum_UndefOpSizes);
        else
        {
          if (Format == ' ')
          {
            if ((IsReg()) && (Is2Reg()))
              Format = 'S';
            else
              Format = DecideGA();
          }
          switch (Format)
          {
            case 'G':
              WAsmCode[0] = 0x0f00 + (((Word)OpSize + 1) << 14) + AdrMode;
              memcpy(WAsmCode + 1, AdrVals, AdrCnt);
              WAsmCode[1 + (AdrCnt >> 1)] = 0x8f00 + AdrMode2;
              memcpy(WAsmCode + 2 + (AdrCnt >> 1), AdrVals2, AdrCnt2);
              CodeLen = 4 + AdrCnt + AdrCnt2;
              break;
            case 'A':
              if ((IsAbsolute()) && (Is2Short()))
              {
                Convert2Short();
                WAsmCode[0] = 0x3980
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode2 & 0xf0) << 5)
                            + (AdrMode2 & 15);
                memcpy(WAsmCode + 1, AdrVals2, AdrCnt2);
                WAsmCode[1 + (AdrCnt2 >> 1)] = AdrVals[0];
                CodeLen = 4 + AdrCnt2;
              }
              else if ((Is2Absolute()) && (IsShort()))
              {
                ConvertShort();
                WAsmCode[0] = 0x3900
                            + (((Word)OpSize + 1) << 14)
                            + ((AdrMode & 0xf0) << 5)
                            + (AdrMode & 15);
                memcpy(WAsmCode + 1, AdrVals, AdrCnt);
                WAsmCode[1 + (AdrCnt >> 1)] = AdrVals2[0];
                CodeLen = 4 + AdrCnt;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            case 'S':
              if ((IsReg()) && (Is2Reg()))
              {
                WAsmCode[0] = 0x3e00
                            + (((Word)OpSize + 1) << 14)
                            + (AdrMode2 << 4)
                            + AdrMode;
                CodeLen = 2;
              }
              else WrError(ErrNum_InvAddrMode);
              break;
            default:
              WrError(ErrNum_InvFormat);
          }
        }
      }
    }
  }
}

static void DecodeCALR_JR(Word Code)
{
  if (!ChkArgCnt(1, 1));
  else if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else
  {
    LongInt AdrInt;
    Boolean OK, ForcePrefix = False;

    AdrInt = EvalStrIntExpressionOffs(&ArgStr[1], CheckForcePrefix(ArgStr[1].str.p_str, &ForcePrefix), Int32, &OK) - EProgCounter();
    if ((OK) && (AddRelPrefix(0, 13, &AdrInt, ForcePrefix)))
    {
      if (Odd(AdrInt)) WrError(ErrNum_DistIsOdd);
      else
      {
        WAsmCode[0] = Code + (AdrInt & 0x1ffe);
        CodeLen = 2;
      }
    }
  }
}

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

  if (!ChkArgCnt(2, 2));
  else if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else
  {
    Word Condition;

    if (!DecodeCondition(ArgStr[1].str.p_str, &Condition)) WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
    else
    {
      LongInt AdrInt;
      Boolean OK, ForcePrefix = False;

      Condition %= 16;
      AdrInt = EvalStrIntExpressionOffs(&ArgStr[2], CheckForcePrefix(ArgStr[2].str.p_str, &ForcePrefix), Int32, &OK) - EProgCounter();
      if ((OK) && (AddRelPrefix(0, 9, &AdrInt, ForcePrefix)))
      {
        if (Odd(AdrInt)) WrError(ErrNum_DistIsOdd);
        else
        {
          WAsmCode[0] = 0x1000 + ((Condition & 14) << 8) + (AdrInt & 0x1fe) + (Condition & 1);
          CodeLen = 2;
        }
      }
    }
  }
}

static void DecodeJRBC_JRBS(Word Code)
{
  if (!ChkArgCnt(3, 3));
  else if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else
  {
    int z;
    Boolean OK;

    z = EvalStrIntExpression(&ArgStr[1], UInt3, &OK);
    if (OK)
    {
      Boolean AdrLongPrefix = False;
      LongInt AdrLong;

      AdrLong = EvalStrIntExpressionOffs(&ArgStr[2], CheckForcePrefix(ArgStr[2].str.p_str, &AdrLongPrefix), Int24, &OK);
      if (OK)
      {
        LongInt AdrInt;
        Boolean AdrIntPrefix = False;

        AddAbsPrefix(1, 13, AdrLong, AdrLongPrefix);
        AdrInt = EvalStrIntExpressionOffs(&ArgStr[3], CheckForcePrefix(ArgStr[3].str.p_str, &AdrIntPrefix), Int32, &OK) - EProgCounter();
        if ((OK) && (AddRelPrefix(0, 9, &AdrInt, AdrIntPrefix)))
        {
          if (Odd(AdrInt)) WrError(ErrNum_DistIsOdd);
          else
          {
            CodeLen = 4;
            WAsmCode[1] = (z << 13) + (AdrLong & 0x1fff);
            WAsmCode[0] = Code + (AdrInt & 0x1fe);
          }
        }
      }
    }
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], 0, eImmNo, True);
    if (AdrOK)
    {
      if ((OpSize != 1) && (OpSize != 2)) WrError(ErrNum_InvOpSize);
      else
      {
        LongInt AdrInt;
        Boolean OK, ForcePrefix = False;

        AdrInt = EvalStrIntExpressionOffs(&ArgStr[2], CheckForcePrefix(ArgStr[2].str.p_str, &ForcePrefix), Int32, &OK) - (EProgCounter() + 4 + AdrCnt +2 * Ord(PrefUsed[0]));
        if ((OK) && (AddRelPrefix(1, 13, &AdrInt, ForcePrefix)))
        {
          if (Odd(AdrInt)) WrError(ErrNum_DistIsOdd);
          else
          {
            WAsmCode[0] = 0x3700 + (((Word)OpSize + 1) << 14) + AdrMode;
            memcpy(WAsmCode + 1, AdrVals, AdrCnt);
            WAsmCode[1 + (AdrCnt >> 1)] = 0xe000 + (AdrInt & 0x1ffe);
            CodeLen = 4 + AdrCnt;
          }
        }
      }
    }
  }
}

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

  if (ChkArgCnt(3, 3))
  {
    Word Condition;

    if (!DecodeCondition(ArgStr[2].str.p_str, &Condition)) WrStrErrorPos(ErrNum_UndefCond, &ArgStr[2]);
    else
    {
      Condition %= 16;
      DecodeAdr(&ArgStr[1], 0, eImmNo, True);
      if (AdrOK)
      {
        if ((OpSize != 1) && (OpSize != 2)) WrError(ErrNum_InvOpSize);
        else
        {
          Boolean OK, ForcePrefix = False;
          LongInt AdrInt;

          AdrInt = EvalStrIntExpressionOffs(&ArgStr[3], CheckForcePrefix(ArgStr[3].str.p_str, &ForcePrefix), Int32, &OK) - EProgCounter();
          if ((OK) && (AddRelPrefix(1, 13, &AdrInt, ForcePrefix)))
          {
            if (Odd(AdrInt)) WrError(ErrNum_DistIsOdd);
            else
            {
              WAsmCode[0] = 0x3700 + (((Word)OpSize+1) << 14) + AdrMode;
              memcpy(WAsmCode + 1, AdrVals, AdrCnt);
              WAsmCode[1 + (AdrCnt >> 1)] = ((Condition & 14) << 12) + (AdrInt & 0x1ffe) + (Condition & 1);
              CodeLen =4 + AdrCnt;
            }
          }
        }
      }
    }
  }
}

static void DecodeLINK_RETD(Word Code)
{
  if (!ChkArgCnt(1, 1));
  else if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else
  {
    LongInt AdrInt;
    Boolean OK, ForcePrefix = False;
    tSymbolFlags Flags;

    AdrInt = EvalStrIntExpressionOffsWithFlags(&ArgStr[1], CheckForcePrefix(ArgStr[1].str.p_str, &ForcePrefix), Int32, &OK, &Flags);
    if (mFirstPassUnknown(Flags))
      AdrInt &= 0x1fe;
    if (ChkRange(AdrInt, -0x80000, 0x7ffff))
    {
      if (Odd(AdrInt)) WrError(ErrNum_NotAligned);
      else
      {
        WAsmCode[0] = Code + (AdrInt & 0x1fe);
        AddSignedPrefix(0, 9, AdrInt, ForcePrefix);
        CodeLen = 2;
      }
    }
  }
}

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

  if (!ChkArgCnt(1, 1));
  else if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else
  {
    Boolean OK;

    WAsmCode[0] = EvalStrIntExpression(&ArgStr[1], Int4, &OK) + 0x7f90;
    if (OK)
      CodeLen = 2;
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[2], 0, eImmNo, False);
    if (AdrOK)
    {
      int z;

      WAsmCode[0] = 0x3000 + AdrMode;
      z = AdrCnt;
      memcpy(WAsmCode + 1, AdrVals, z);
      DecodeAdr(&ArgStr[1], 1, eImmNo, True);
      if (AdrOK)
      {
        if ((OpSize != 1) && (OpSize != 2)) WrError(ErrNum_InvOpSize);
        else
        {
          WAsmCode[0] += ((Word)OpSize) << 14;
          WAsmCode[1 + (z >> 1)] = 0x9700 + AdrMode;
          memcpy(WAsmCode + 2 + (z >> 1), AdrVals, AdrCnt);
          CodeLen = 4 + z + AdrCnt;
        }
      }
    }
  }
}

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

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

static void AddRMW(const char *NName, Byte NCode, Byte NMask)
{
  order_array_rsv_end(RMWOrders, RMWOrder);
  RMWOrders[InstrZ].Mask = NMask;
  RMWOrders[InstrZ].Code = NCode;
  AddInstTable(InstTable, NName, InstrZ++, DecodeRMW);
}

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

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

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

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

static void InitFields(void)
{
  InstTable = CreateInstTable(301);

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "RLM", 0x0400, DecodeRLM_RRM);
  AddInstTable(InstTable, "RRM", 0x0500, DecodeRLM_RRM);
  AddInstTable(InstTable, "CHK", 0, DecodeCHK_CHKS);
  AddInstTable(InstTable, "CHKS", 1, DecodeCHK_CHKS);
  AddInstTable(InstTable, "EX", 0, DecodeEX);
  AddInstTable(InstTable, "CALR", 0x2001, DecodeCALR_JR);
  AddInstTable(InstTable, "JR", 0x2000, DecodeCALR_JR);
  AddInstTable(InstTable, "JRC", 0, DecodeJRC);
  AddInstTable(InstTable, "JRBC", 0x1e00, DecodeJRBC_JRBS);
  AddInstTable(InstTable, "JRBS", 0x1e01, DecodeJRBC_JRBS);
  AddInstTable(InstTable, "DJNZ", 0, DecodeDJNZ);
  AddInstTable(InstTable, "DJNZC", 0, DecodeDJNZC);
  AddInstTable(InstTable, "LINK", 0xc001, DecodeLINK_RETD);
  AddInstTable(InstTable, "RETD", 0xc801, DecodeLINK_RETD);
  AddInstTable(InstTable, "SWI", 0, DecodeSWI);
  AddInstTable(InstTable, "LDA", 0, DecodeLDA);
  AddInstTable(InstTable, "REG", 0, CodeREG);

  AddFixed("CCF" , 0x7f82);
  AddFixed("CSF" , 0x7f8a);
  AddFixed("CVF" , 0x7f86);
  AddFixed("CZF" , 0x7f8e);
  AddFixed("DI"  , 0x7fa1);
  AddFixed("EI"  , 0x7fa3);
  AddFixed("HALT", 0x7fa5);
  AddFixed("NOP" , 0x7fa0);
  AddFixed("RCF" , 0x7f80);
  AddFixed("RET" , 0x7fa4);
  AddFixed("RETI", 0x7fa9);
  AddFixed("RETS", 0x7fab);
  AddFixed("RSF" , 0x7f88);
  AddFixed("RVF" , 0x7f84);
  AddFixed("RZF" , 0x7f8c);
  AddFixed("SCF" , 0x7f81);
  AddFixed("SSF" , 0x7f89);
  AddFixed("SVF" , 0x7f85);
  AddFixed("SZF" , 0x7f8b);
  AddFixed("UNLK", 0x7fa2);

  InstrZ = 0;
  AddRMW("CALL" , 0x35, 0x36);
  AddRMW("CLR"  , 0x2b, 0x17);
  AddRMW("CPL"  , 0x28, 0x17);
  AddRMW("EXTS" , 0x33, 0x16);
  AddRMW("EXTZ" , 0x32, 0x16);
  AddRMW("JP"   , 0x34, 0x36);
  AddRMW("MIRR" , 0x23, 0x17);
  AddRMW("NEG"  , 0x29, 0x17);
  AddRMW("POP"  , 0x20, 0x17);
  AddRMW("PUSH" , 0x21, 0x07);
  AddRMW("PUSHA", 0x31, 0x36);
  AddRMW("RVBY" , 0x22, 0x17);
  AddRMW("TJP"  , 0x36, 0x16);
  AddRMW("TST"  , 0x2a, 0x17);

  InstrZ = 0;
  AddInstTable(InstTable, "ADD", InstrZ++, DecodeGASI1);
  AddInstTable(InstTable, "SUB", InstrZ++, DecodeGASI1);
  AddInstTable(InstTable, "CP" , InstrZ++, DecodeGASI1);
  AddInstTable(InstTable, "LD" , InstrZ++, DecodeGASI1);

  InstrZ = 0;
  AddInstTable(InstTable, "AND", InstrZ++, DecodeGASI2);
  AddInstTable(InstTable, "OR" , InstrZ++, DecodeGASI2);
  AddInstTable(InstTable, "XOR", InstrZ++, DecodeGASI2);

  InstrZ = 0;
  AddInstTable(InstTable, "ADD3", InstrZ++, DecodeTrinom);
  AddInstTable(InstTable, "SUB3", InstrZ++, DecodeTrinom);
  AddInstTable(InstTable, "MAC" , InstrZ++, DecodeTrinom);
  AddInstTable(InstTable, "MACS", InstrZ++, DecodeTrinom);

  InstrZ = 0;
  AddInstTable(InstTable, "BRES", InstrZ++, DecodeBit);
  AddInstTable(InstTable, "BSET", InstrZ++, DecodeBit);
  AddInstTable(InstTable, "BCHG", InstrZ++, DecodeBit);
  AddInstTable(InstTable, "BTST", InstrZ++, DecodeBit);

  InstrZ = 0;
  AddInstTable(InstTable, "SLL", InstrZ++, DecodeShift);
  AddInstTable(InstTable, "SRL", InstrZ++, DecodeShift);
  AddInstTable(InstTable, "SLA", InstrZ++, DecodeShift);
  AddInstTable(InstTable, "SRA", InstrZ++, DecodeShift);
  AddInstTable(InstTable, "RL" , InstrZ++, DecodeShift);
  AddInstTable(InstTable, "RR" , InstrZ++, DecodeShift);
  AddInstTable(InstTable, "RLC", InstrZ++, DecodeShift);
  AddInstTable(InstTable, "RRC", InstrZ++, DecodeShift);

  InstrZ = 0;
  AddInstTable(InstTable, "BFEX" , InstrZ++, DecodeBField);
  AddInstTable(InstTable, "BFEXS", InstrZ++, DecodeBField);
  AddInstTable(InstTable, "BFIN" , InstrZ++, DecodeBField);

  AddGAEq("ABCD" , 0x0110);
  AddGAEq("ADC"  , 0x0004);
  AddGAEq("CBCD" , 0x0112);
  AddGAEq("CPC"  , 0x0006);
  AddGAEq("MAX"  , 0x0116);
  AddGAEq("MAXS" , 0x0017);
  AddGAEq("MIN"  , 0x0114);
  AddGAEq("MINS" , 0x0015);
  AddGAEq("SBC"  , 0x0105);
  AddGAEq("SBCD" , 0x0111);

  AddGAHalf("DIV"  , 0x26);
  AddGAHalf("DIVS" , 0x27);
  AddGAHalf("MUL"  , 0x24);
  AddGAHalf("MULS" , 0x25);

  AddGAFirst("ANDCF", 0x44);
  AddGAFirst("LDCF" , 0x47);
  AddGAFirst("ORCF" , 0x45);
  AddGAFirst("STCF" , 0x43);
  AddGAFirst("TSET" , 0x70);
  AddGAFirst("XORCF", 0x46);

  AddGASecond("BS0B" , 0x54);
  AddGASecond("BS0F" , 0x55);
  AddGASecond("BS1B" , 0x56);
  AddGASecond("BS1F" , 0x57);

  AddInstTable(InstTable, "CPSZ", 0, DecodeString);
  AddInstTable(InstTable, "CPSN", 1, DecodeString);
  AddInstTable(InstTable, "LDS" , 3, DecodeString);

  AddIntelPseudo(InstTable, eIntPseudoFlag_LittleEndian);
}

static void DeinitFields(void)
{
  DestroyInstTable(InstTable);

  order_array_free(RMWOrders);
}

static Boolean DecodeAttrPart_97C241(void)
{
  char *p;

  switch (AttrSplit)
  {
    case '.':
      p = strchr(AttrPart.str.p_str, ':');
      if (p)
      {
        Format = (p < AttrPart.str.p_str + strlen(AttrPart.str.p_str) - 1) ? p[1] : ' ';
        *p = '\0';
      }
      else
        Format = ' ';
      break;
    case ':':
      p = strchr(AttrPart.str.p_str, '.');
      if (!p)
      {
        Format = (*AttrPart.str.p_str);
        *AttrPart.str.p_str = '\0';
      }
      else
      {
        Format = (p == AttrPart.str.p_str) ? ' ' : *AttrPart.str.p_str;
        strmov(AttrPart.str.p_str, p + 1);
      }
      break;
    default:
      Format = ' ';
  }
  Format = as_toupper(Format);

  if (*AttrPart.str.p_str)
    switch (as_toupper(*AttrPart.str.p_str))
    {
      case 'B':
        AttrPartOpSize[0] = eSymbolSize8Bit;
        break;
      case 'W':
        AttrPartOpSize[0] = eSymbolSize16Bit;
        break;
      case 'D':
        AttrPartOpSize[0] = eSymbolSize32Bit;
        break;
      default:
       WrStrErrorPos(ErrNum_UndefAttr, &AttrPart);
       return False;
    }
  return True;
}

static void MakeCode_97C241(void)
{
  PrefUsed[0] = False;
  PrefUsed[1] = False;
  AdrInc = 0;
  MinOneIs0 = False;
  LowLim4 = -8; LowLim8 = -128;

  OpSize = AttrPartOpSize[0];

  if (LookupInstTable(InstTable, OpPart.str.p_str))
  {
    AddPrefixes();
    return;
  }

  WrStrErrorPos(ErrNum_UnknownInstruction, &OpPart);
}

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

static void InternSymbol_97C241(char *pArg, TempResult *pResult)
{
  Byte Reg;
  tSymbolSize Size;

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

static Boolean IsDef_97C241(void)
{
  return Memo("REG");
}

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

static void SwitchTo_97C241(void)
{
  const TFamilyDescr *p_descr = FindFamilyByName("TLCS-9000");

  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);

  PCSymbol = "$";
  HeaderID = p_descr->Id;
  NOPCode = 0x7fa0;
  DivideChars = ",";
  HasAttrs = True;
  AttrChars = ".:";

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

  DecodeAttrPart = DecodeAttrPart_97C241;
  MakeCode = MakeCode_97C241;
  IsDef = IsDef_97C241;
  InternSymbol = InternSymbol_97C241;
  DissectReg = DissectReg_97C241;
  SwitchFrom = SwitchFrom_97C241;
  InitFields();
}

void code97c241_init(void)
{
  CPU97C241 = AddCPU("97C241", SwitchTo_97C241);
}
