/* code68s12z.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS                                                                        */
/*                                                                           */
/* Code Generator NXP S12Z                                                   */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"

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

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

#include "codes12z.h"

typedef enum
{
  AdrModeNone = -1,
  AdrModeReg = 0,
  AdrModeAReg = 1,
  AdrModeImm = 2,
  AdrModeMemReg = 3
} tAdrMode;

typedef enum
{
  eIndirModeNone,
  eIndirModePar,
  eIndirModeSquare
} tIndirMode;

typedef enum
{
  eIncModeNone,
  eIncModePreInc,
  eIncModePostInc,
  eIncModePreDec,
  eIncModePostDec
} tIncMode;

#define MModeReg (1 << AdrModeReg)
#define MModeAReg (1 << AdrModeAReg)
#define MModeImm (1 << AdrModeImm)
#define MModeMemReg (1 << AdrModeMemReg)

#define OpSizeBitPos8 ((tSymbolSize)16)
#define OpSizeBitPos16 ((tSymbolSize)17)
#define OpSizeBitPos32 ((tSymbolSize)18)
#define OpSizeShiftCount ((tSymbolSize)19)

typedef struct
{
  tAdrMode Mode;
  Byte Arg, Vals[4], ShiftLSB;
  unsigned ValCnt;
} tAdrVals;

static tSymbolSize OpSize, OpSize2;

static const tSymbolSize RegSizes[16] =
{
  eSymbolSize16Bit, eSymbolSize16Bit,   /* D2/D3 */
  eSymbolSize16Bit, eSymbolSize16Bit,   /* D4/D5 */
  eSymbolSize8Bit,  eSymbolSize8Bit,    /* D0/D1 */
  eSymbolSize32Bit, eSymbolSize32Bit,   /* D6/D7 */
  eSymbolSize24Bit, eSymbolSize24Bit,   /* X/Y */
  eSymbolSize24Bit, eSymbolSizeUnknown, /* S/- */
  eSymbolSize8Bit,  eSymbolSize8Bit,    /* CCH/CCL */
  eSymbolSize16Bit, eSymbolSizeUnknown, /* CCR/- */
};

/*--------------------------------------------------------------------------*/
/* Helper Functions */

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

static void AppendAdrVals(const tAdrVals *pVals)
{
  memcpy(BAsmCode + CodeLen, pVals->Vals, pVals->ValCnt);
  CodeLen += pVals->ValCnt;
}

static Boolean DecodeRegStr(const char *pArg, Byte *pRes)
{
  if ((strlen(pArg) == 2)
   && (toupper(*pArg) == 'D')
   && ((pArg[1] >= '0') && (pArg[1] <= '7')))
  {
    static const Byte RegCodes[8] = { 4, 5, 0, 1, 2, 3, 6, 7 };

    *pRes = RegCodes[pArg[1] - '0'];
    return True;
  }
  else
    return False;
}

static Boolean DecodeAdrRegStr(const char *pArg, Byte *pRes)
{
  static const char Regs[4][3] = { "X", "Y", "S", "PC" };
  unsigned z;

  for (z = 0; z < 4; z++)
    if (!as_strcasecmp(pArg, Regs[z]))
    {
      *pRes = z;
      return True;
    }
  return False;
}

static Boolean DecodeRegArg(int ArgNum, Byte *pRes, Byte Mask)
{
  Boolean Result = DecodeRegStr(ArgStr[ArgNum].str.p_str, pRes);

  if (!Result || !((Mask >> *pRes) & 1))
    WrStrErrorPos(ErrNum_InvReg, &ArgStr[ArgNum]);
  return Result;
}

static Boolean DecodeAdrRegArg(int ArgNum, Byte *pRes, Byte Mask)
{
  Boolean Result = DecodeAdrRegStr(ArgStr[ArgNum].str.p_str, pRes);

  if (!Result || !((Mask >> *pRes) & 1))
    WrStrErrorPos(ErrNum_InvReg, &ArgStr[ArgNum]);
  return Result;
}

static Boolean DecodeGenRegArg(int ArgNum, Byte *pRes)
{
  if (DecodeRegStr(ArgStr[ArgNum].str.p_str, pRes))
    return True;
  else if (DecodeAdrRegStr(ArgStr[ArgNum].str.p_str, pRes) && (*pRes != 3))
  {
    *pRes += 8;
    return True;
  }
  else if (!as_strcasecmp(ArgStr[ArgNum].str.p_str, "CCH"))
  {
    *pRes = 12;
    return True;
  }
  else if (!as_strcasecmp(ArgStr[ArgNum].str.p_str, "CCL"))
  {
    *pRes = 13;
    return True;
  }
  else if (!as_strcasecmp(ArgStr[ArgNum].str.p_str, "CCW"))
  {
    *pRes = 14;
    return True;
  }
  else
    return False;
}

static Boolean ShortImm(LongInt Value, ShortInt OpSize, Byte *pShortValue, Byte *pShiftLSB)
{
  if (OpSize == OpSizeShiftCount)
  {
    if ((Value >= 0) && (Value <= 31))
    {
      *pShortValue = (Value >> 1 & 15);
      *pShiftLSB = Value & 1;
      return True;
    }
    else
      return False;
  }
  else if (OpSize < OpSizeBitPos8)
  {
    if (Value == -1)
    {
      *pShortValue = 0;
      return True;
    }
    else if ((Value >= 1) && (Value <= 15))
    {
      *pShortValue = Value;
      return True;
    }
    else if (((Value == (LongInt)0xff) && (OpSize == 0))
          || ((Value == (LongInt)0xffff) && (OpSize == 1))
          || ((Value == (LongInt)0xffffff) && (OpSize == eSymbolSize24Bit))
          || ((Value == (LongInt)0xffffffff) && (OpSize == 2)))
    {
      *pShortValue = 0;
      return True;
    }
    else
      return False;
  }
  else
    return False;
}

static unsigned OpSizeByteLen(ShortInt OpSize)
{
  switch (OpSize)
  {
    case -1: return 0;
    case 1: return 2;
    case 2: return 4;
    case eSymbolSize24Bit: return 3;
    default: return 1;
  }
}

static void ResetAdrVals(tAdrVals *pVals)
{
  pVals->Mode = AdrModeNone;
  pVals->Arg = 0;
  pVals->ValCnt = 0;
  pVals->ShiftLSB = 0;
}

static Boolean IsIncDec(char ch, char *pRes)
{
  *pRes = ((ch == '+') || (ch == '-')) ? ch : '\0';
  return !!*pRes;
}

static void CopyIndirect(tStrComp *pDest, const tStrComp *pSrc)
{
  pDest->Pos.Len = strmemcpy(pDest->str.p_str, STRINGSIZE, pSrc->str.p_str + 1, strlen(pSrc->str.p_str) - 2);
  pDest->Pos.StartCol = pSrc->Pos.StartCol + 1;
}

static Boolean DecodeAdr(int ArgIndex, unsigned ModeMask, tAdrVals *pVals)
{
  String CompStr;
  tStrComp Comp;
  int l;
  tIndirMode IndirMode;
  LargeWord Address;
  Boolean OK;

  ResetAdrVals(pVals);
  StrCompMkTemp(&Comp, CompStr, sizeof(CompStr));

  /* simple register: */

  if (DecodeRegStr(ArgStr[ArgIndex].str.p_str, &pVals->Arg))
  {
    if (ModeMask & MModeReg)
      pVals->Mode = AdrModeReg;
    else
    {
      pVals->Mode = AdrModeMemReg;
      pVals->Arg |= 0xb8;
    }
    goto done;
  }

  if (DecodeAdrRegStr(ArgStr[ArgIndex].str.p_str, &pVals->Arg))
  {
    pVals->Mode = AdrModeAReg;
    goto done;
  }

  /* immediate: */

  if (*ArgStr[ArgIndex].str.p_str == '#')
  {
    Boolean OK;
    LongInt Value;

    /* avoid returning AdrModeMemReg if immediate is forbidden */

    if (!(ModeMask &MModeImm))
      goto error;

    switch ((int)OpSize)
    {
      case eSymbolSize8Bit:
        Value = EvalStrIntExpressionOffs(&ArgStr[ArgIndex], 1, Int8, &OK);
        break;
      case eSymbolSize16Bit:
        Value = EvalStrIntExpressionOffs(&ArgStr[ArgIndex], 1, Int16, &OK);
        break;
      case eSymbolSize32Bit:
        Value = EvalStrIntExpressionOffs(&ArgStr[ArgIndex], 1, Int32, &OK);
        break;
      case eSymbolSize24Bit:
        Value = EvalStrIntExpressionOffs(&ArgStr[ArgIndex], 1, Int24, &OK);
        break;
      case OpSizeBitPos8:
        Value = EvalStrIntExpressionOffs(&ArgStr[ArgIndex], 1, UInt3, &OK);
        break;
      case OpSizeBitPos16:
        Value = EvalStrIntExpressionOffs(&ArgStr[ArgIndex], 1, UInt4, &OK);
        break;
      case OpSizeBitPos32:
      case OpSizeShiftCount:
        Value = EvalStrIntExpressionOffs(&ArgStr[ArgIndex], 1, UInt5, &OK);
        break;
      default:
        WrStrErrorPos(ErrNum_UndefOpSizes, &ArgStr[ArgIndex]);
        goto done;
    }

    if ((ModeMask & MModeMemReg) && (ShortImm(Value, OpSize, &pVals->Arg, &pVals->ShiftLSB)))
    {
      pVals->Mode = AdrModeMemReg;
      pVals->Arg |= 0x70;
    }
    else
    {
      pVals->Mode = AdrModeImm;
      if (OpSize == eSymbolSize32Bit)
        pVals->Vals[pVals->ValCnt++] = (Value >> 24) & 0xff;
      if ((OpSize == eSymbolSize32Bit) || (OpSize == eSymbolSize24Bit))
        pVals->Vals[pVals->ValCnt++] = (Value >> 16) & 0xff;
      if ((OpSize != eSymbolSize8Bit) && (OpSize < OpSizeBitPos8))
        pVals->Vals[pVals->ValCnt++] = (Value >> 8) & 0xff;
      pVals->Vals[pVals->ValCnt++] = Value & 0xff;
    }
    goto done;
  }

  /* indirect () []: */

  l = strlen(ArgStr[ArgIndex].str.p_str);
  if (IsIndirect(ArgStr[ArgIndex].str.p_str))
    IndirMode = eIndirModePar;
  else if ((l >= 2) && (ArgStr[ArgIndex].str.p_str[0] == '[') && (ArgStr[ArgIndex].str.p_str[l - 1] == ']'))
    IndirMode = eIndirModeSquare;
  else
    IndirMode = eIndirModeNone;

  if (IndirMode)
  {
    char *pSep, IncChar;
    LongInt DispAcc = 0;
    Byte DataReg = 0, AdrReg = 0, AdrIncReg = 0;
    Boolean AdrRegPresent = False, DataRegPresent = False, HasDisp = False;
    tIncMode IncMode = eIncModeNone;
    tStrComp Right, RunComp;

    CopyIndirect(&Comp, &ArgStr[ArgIndex]);
    StrCompRefRight(&RunComp, &Comp, 0);

    /* split into components */

    while (True)
    {
      pSep = QuotPos(RunComp.str.p_str, ',');
      if (pSep)
        StrCompSplitRef(&RunComp, &Right, &RunComp, pSep);

      /* remove leading/trailing spaces */

      KillPrefBlanksStrCompRef(&RunComp);
      KillPostBlanksStrComp(&RunComp);
      l = strlen(RunComp.str.p_str);

      if (DecodeRegStr(RunComp.str.p_str, &DataReg))
      {
        if (DataRegPresent)
        {
          WrStrErrorPos(ErrNum_InvAddrMode, &RunComp);
          goto done;
        }
        DataRegPresent = True;
      }
      else if (DecodeAdrRegStr(RunComp.str.p_str, &AdrReg))
      {
        if (AdrRegPresent)
        {
          WrStrErrorPos(ErrNum_InvAddrMode, &RunComp);
          goto done;
        }
        AdrRegPresent = True;
      }
      else if (IsIncDec(*RunComp.str.p_str, &IncChar) && DecodeAdrRegStr(RunComp.str.p_str + 1, &AdrIncReg))
      {
        if (IncMode)
        {
          WrStrErrorPos(ErrNum_InvAddrMode, &RunComp);
          goto done;
        }
        IncMode = (IncChar == '+') ? eIncModePreInc : eIncModePreDec;
      }
      else if (IsIncDec(Comp.str.p_str[l - 1], &IncChar))
      {
        RunComp.str.p_str[l - 1] = '\0';
        if (!DecodeAdrRegStr(RunComp.str.p_str, &AdrIncReg))
        {
          WrStrErrorPos(ErrNum_InvReg, &RunComp);
          goto done;
        }
        if (IncMode)
        {
          WrStrErrorPos(ErrNum_InvAddrMode, &RunComp);
          goto done;
        }
        IncMode = (IncChar == '+') ? eIncModePostInc : eIncModePostDec;
      }
      else
      {
        Boolean OK;
        LongInt Val = EvalStrIntExpression(&RunComp, Int24, &OK);

        if (!OK)
          goto done;
        DispAcc += Val;
        HasDisp = True;
      }

      if (pSep)
        RunComp = Right;
      else
        break;
    }

    /* pre/pos in/decrement */

    if ((IndirMode == eIndirModePar) && IncMode && !DispAcc && !AdrRegPresent && !DataRegPresent)
    {
      switch (AdrIncReg)
      {
        case 0:
        case 1:
          pVals->Arg = 0xc3 | (AdrIncReg << 4);
          if ((IncMode == eIncModePostInc) || (IncMode == eIncModePreInc))
            pVals->Arg |= 0x20;
          if ((IncMode == eIncModePostInc) || (IncMode == eIncModePostDec))
            pVals->Arg |= 0x04;
          pVals->Mode = AdrModeMemReg;
          break;
        case 2:
          if (IncMode == eIncModePreDec)
          {
            pVals->Arg = 0xfb;
            pVals->Mode = AdrModeMemReg;
          }
          else if (IncMode == eIncModePostInc)
          {
            pVals->Arg = 0xff;
            pVals->Mode = AdrModeMemReg;
          }
          else
            goto error;
          break;
        default:
          goto error;
      }
    }

    /* (disp,XYSP) */

    else if ((IndirMode == eIndirModePar) && AdrRegPresent && !DataRegPresent && !IncMode)
    {
      if ((AdrReg == 3) && (HasDisp))
        DispAcc -= EProgCounter();

      if ((DispAcc >= 0) && (DispAcc <= 15) && (AdrReg != 3))
      {
        pVals->Arg = 0x40 | (AdrReg << 4) | (DispAcc & 15);
        pVals->Mode = AdrModeMemReg;
      }
      else if (RangeCheck(DispAcc, SInt9))
      {
        pVals->Arg = 0xc0 | (AdrReg << 4) | ((DispAcc >> 8) & 1);
        pVals->Vals[pVals->ValCnt++] = DispAcc & 0xff;
        pVals->Mode = AdrModeMemReg;
      }
      else
      {
        pVals->Arg = 0xc2 | (AdrReg << 4);
        pVals->Vals[pVals->ValCnt++] = (DispAcc >> 16) & 0xff;
        pVals->Vals[pVals->ValCnt++] = (DispAcc >> 8) & 0xff;
        pVals->Vals[pVals->ValCnt++] = DispAcc & 0xff;
        pVals->Mode = AdrModeMemReg;
      }
    }

    /* (Dn,XYS) */

    else if ((IndirMode == eIndirModePar) && AdrRegPresent && DataRegPresent && !IncMode && !DispAcc)
    {
      if (AdrReg == 3)
        goto error;
      else
      {
        pVals->Arg = 0x88 | (AdrReg << 4) | DataReg;
        pVals->Mode = AdrModeMemReg;
      }
    }

    /* (disp,Dn) */

    else if ((IndirMode == eIndirModePar) && !AdrRegPresent && DataRegPresent && !IncMode)
    {
      if (RangeCheck(DispAcc, UInt18))
      {
        pVals->Arg = 0x80 | DataReg | ((DispAcc >> 12) & 0x30);
        pVals->Vals[pVals->ValCnt++] = (DispAcc >> 8) & 0xff;
        pVals->Vals[pVals->ValCnt++] = DispAcc & 0xff;
        pVals->Mode = AdrModeMemReg;
      }
      else
      {
        pVals->Arg = 0xe8 | DataReg;
        pVals->Vals[pVals->ValCnt++] = (DispAcc >> 16) & 0xff;
        pVals->Vals[pVals->ValCnt++] = (DispAcc >> 8) & 0xff;
        pVals->Vals[pVals->ValCnt++] = DispAcc & 0xff;
        pVals->Mode = AdrModeMemReg;
      }
    }

    /* [Dn,XY] */

    else if ((IndirMode == eIndirModeSquare) && AdrRegPresent && DataRegPresent && !IncMode && !DispAcc)
    {
      if (AdrReg >= 2)
        goto error;
      else
      {
        pVals->Arg = 0xc8 | (AdrReg << 4) | DataReg;
        pVals->Mode = AdrModeMemReg;
      }
    }

    /* [disp,XYSP] */

    else if ((IndirMode == eIndirModeSquare) && AdrRegPresent && !DataRegPresent && !IncMode)
    {
      if ((AdrReg == 3) && (HasDisp))
        DispAcc -= EProgCounter();

      if (RangeCheck(DispAcc, SInt9))
      {
        pVals->Arg = 0xc4 | (AdrReg << 4) | ((DispAcc >> 8) & 1);
        pVals->Vals[pVals->ValCnt++] = DispAcc & 0xff;
        pVals->Mode = AdrModeMemReg;
      }
      else
      {
        pVals->Arg = 0xc6 | (AdrReg << 4);
        pVals->Vals[pVals->ValCnt++] = (DispAcc >> 16) & 0xff;
        pVals->Vals[pVals->ValCnt++] = (DispAcc >> 8) & 0xff;
        pVals->Vals[pVals->ValCnt++] = DispAcc & 0xff;
        pVals->Mode = AdrModeMemReg;
      }
    }

    /* [disp] */

    else if ((IndirMode == eIndirModeSquare) && !AdrRegPresent && !DataRegPresent && !IncMode)
    {
      pVals->Arg = 0xfe;
      pVals->Vals[pVals->ValCnt++] = (DispAcc >> 16) & 0xff;
      pVals->Vals[pVals->ValCnt++] = (DispAcc >> 8) & 0xff;
      pVals->Vals[pVals->ValCnt++] = DispAcc & 0xff;
      pVals->Mode = AdrModeMemReg;
    }

    else
      goto error;

    goto done;
  }

  /* absolute: */

  Address = EvalStrIntExpression(&ArgStr[ArgIndex], UInt24, &OK);
  if (OK)
  {
    if (RangeCheck(Address, UInt14))
    {
      pVals->Arg = 0x00 | ((Address >> 8) & 0x3f);
      pVals->Vals[pVals->ValCnt++] = Address & 0xff;
    }
    else if (RangeCheck(Address, UInt18))
    {
      pVals->Arg = 0xf8 | ((Address >> 16) & 1) | ((Address >> 15) & 4);
      pVals->Vals[pVals->ValCnt++] = (Address >> 8) & 0xff;
      pVals->Vals[pVals->ValCnt++] = Address & 0xff;
    }
    else
    {
      pVals->Arg = 0xfa;
      pVals->Vals[pVals->ValCnt++] = (Address >> 16) & 0xff;
      pVals->Vals[pVals->ValCnt++] = (Address >> 8) & 0xff;
      pVals->Vals[pVals->ValCnt++] = Address & 0xff;
    }
    pVals->Mode = AdrModeMemReg;
  }

done:
  if ((pVals->Mode != AdrModeNone) && !((ModeMask >> pVals->Mode) & 1))
  {
    ResetAdrVals(pVals);
    goto error;
  }
  return (pVals->Mode != AdrModeNone);

error:
  WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[ArgIndex]);
  return False;
}

static Boolean IsImmediate(const tAdrVals *pVals, ShortInt OpSize, Byte *pImmVal)
{
  switch (pVals->Mode)
  {
    case AdrModeImm:
      *pImmVal = pVals->Vals[pVals->ValCnt - 1];
      return True;
    case AdrModeMemReg:
      if ((pVals->Arg & 0xf0) == 0x70)
      {
        *pImmVal = pVals->Arg & 15;
        if (OpSize == OpSizeShiftCount)
          *pImmVal = (*pImmVal << 1) | pVals->ShiftLSB;
        else if (!*pImmVal && (OpSize < OpSizeBitPos8))
          *pImmVal = 0xff;
        return True;
      }
      /* else fall-through */
    default:
      *pImmVal = 0;
      return False;
  }
}

static void ChangeImmediate(tAdrVals *pVals, ShortInt OpSize, Byte ImmVal)
{
  unsigned z;

  pVals->Mode = AdrModeImm;
  pVals->Arg = 0;
  pVals->ValCnt = OpSizeByteLen(OpSize);
  pVals->Vals[pVals->ValCnt - 1] = ImmVal;
  for (z = 1; z < pVals->ValCnt; z++)
    pVals->Vals[z] = (ImmVal & 0x80) ? 0xff : 0x00;
}

static Boolean IsReg(const tAdrVals *pVals, Byte *pReg)
{
  switch (pVals->Mode)
  {
    case AdrModeReg:
      *pReg = pVals->Arg;
      return True;
    case AdrModeMemReg:
      if ((pVals->Arg & 0xf8) == 0xb8)
      {
        *pReg = pVals->Arg & 7;
        return True;
      }
      /* else fall-through */
    default:
      *pReg = 0;
      return False;
  }
}

static Boolean SetOpSize(tSymbolSize NewOpSize)
{
  if ((OpSize == NewOpSize) || (OpSize == eSymbolSizeUnknown))
  {
    OpSize = NewOpSize;
    return True;
  }
  else
  {
    char Str[30];

    as_snprintf(Str, sizeof(Str), "%d -> %d", (int)OpSize, (int)NewOpSize);
    WrXError(ErrNum_ConfOpSizes, Str);
    return False;
  }
}

static Boolean SizeCode2(ShortInt ThisOpSize, Byte *pSizeCode)
{
  switch (ThisOpSize)
  {
    case eSymbolSize8Bit: *pSizeCode = 0; break;
    case eSymbolSize16Bit: *pSizeCode = 1; break;
    case eSymbolSize24Bit: *pSizeCode = 2; break;
    case eSymbolSize32Bit: *pSizeCode = 3; break;
    default: return False;
  }
  return True;
}

static Boolean DecodeImmBitField(tStrComp *pArg, Word *pResult)
{
  char *pSplit = strchr(pArg->str.p_str, ':'), Save;
  tStrComp Left, Right;
  Boolean OK;
  tSymbolFlags Flags;

  if (!pSplit)
  {
    WrError(ErrNum_InvBitPos);
    return False;
  }
  Save = StrCompSplitRef(&Left, &Right, pArg, pSplit);
  *pResult = EvalStrIntExpressionWithFlags(&Left, UInt6, &OK, &Flags);
  if (mFirstPassUnknown(Flags))
    *pResult &= 31;
  *pSplit = Save;
  if (!OK || !ChkRange(*pResult, 1, 32))
    return False;
  *pResult = (*pResult << 5) | (EvalStrIntExpression(&Right, UInt5, &OK) & 31);
  return OK;
}

/*--------------------------------------------------------------------------*/
/* Bit Symbol Handling */

/*
 * Compact representation of bits and bit fields in symbol table:
 * bits 0..2/3/4: (start) bit position
 * bits 3/4/5...14/15/16: register address in I/O space (first 4K)
 * bits 20/21: register size (0/1/2/3 for 8/16/32/24 bits)
 * bits 24..28: length of bit field minus one (0 for individual bit)
 */

/*!------------------------------------------------------------------------
 * \fn     AssembleBitfieldSymbol(Byte BitPos, Byte Width, ShortInt OpSize, Word Address)
 * \brief  build the compact internal representation of a bit field symbol
 * \param  BitPos bit position in word
 * \param  Width width of bit field
 * \param  OpSize operand size (0..2)
 * \param  Address register address
 * \return compact representation
 * ------------------------------------------------------------------------ */

static LongWord AssembleBitfieldSymbol(Byte BitPos, Byte Width, ShortInt OpSize, Word Address)
{
  LongWord CodeOpSize = (OpSize == eSymbolSize24Bit) ? 3 : OpSize;
  int AddrShift = (OpSize == eSymbolSize24Bit) ? 5 : (3 + OpSize);

  return BitPos
       | (((LongWord)Address & 0xfff) << AddrShift)
       | (CodeOpSize << 20)
       | (((LongWord)(Width - 1) & 31) << 24);
}

/*!------------------------------------------------------------------------
 * \fn     AssembleBitSymbol(Byte BitPos, ShortInt OpSize, Word Address)
 * \brief  build the compact internal representation of a bit symbol
 * \param  BitPos bit position in word
 * \param  OpSize operand size (0..2)
 * \param  Address register address
 * \return compact representation
 * ------------------------------------------------------------------------ */

static LongWord AssembleBitSymbol(Byte BitPos, ShortInt OpSize, Word Address)
{
  return AssembleBitfieldSymbol(BitPos, 1, OpSize, Address);
}

/*!------------------------------------------------------------------------
 * \fn     EvalBitPosition(const char *pBitArg, Boolean *pOK, ShortInt OpSize)
 * \brief  evaluate constant bit position, with bit range depending on operand size
 * \param  pBitArg bit position argument
 * \param  pOK returns True if OK
 * \param  OpSize operand size (0,1,2 -> 8,16,32 bits)
 * \return bit position as number
 * ------------------------------------------------------------------------ */

static Byte EvalBitPosition(const tStrComp *pBitArg, Boolean *pOK, ShortInt OpSize)
{
  switch (OpSize)
  {
    case eSymbolSize8Bit:
      return EvalStrIntExpression(pBitArg, UInt3, pOK);
    case eSymbolSize16Bit:
      return EvalStrIntExpression(pBitArg, UInt4, pOK);
    case eSymbolSize24Bit:
    {
      Byte Result;
      tSymbolFlags Flags;

      Result = EvalStrIntExpressionWithFlags(pBitArg, UInt5, pOK, &Flags);
      if (!*pOK)
        return Result;
      if (mFirstPassUnknown(Flags))
        Result &= 15;
      *pOK = ChkRange(Result, 0, 23);
      return Result;
    }
    case eSymbolSize32Bit:
      return EvalStrIntExpression(pBitArg, UInt5, pOK);
    default:
      WrError(ErrNum_InvOpSize);
      *pOK = False;
      return 0;
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitArg2(LongWord *pResult, const tStrComp *pRegArg, const tStrComp *pBitArg, ShortInt OpSize)
 * \brief  encode a bit symbol, address & bit position separated
 * \param  pResult resulting encoded bit
 * \param  pRegArg register argument
 * \param  pBitArg bit argument
 * \param  OpSize register size (0/1/2 = 8/16/32 bit)
 * \return True if success
 * ------------------------------------------------------------------------ */

static Boolean DecodeBitArg2(LongWord *pResult, const tStrComp *pRegArg, const tStrComp *pBitArg, ShortInt OpSize)
{
  Boolean OK;
  tSymbolFlags Flags;
  LongWord Addr;
  Byte BitPos;

  BitPos = EvalBitPosition(pBitArg, &OK, OpSize);
  if (!OK)
    return False;

  /* all I/O registers reside in the first 4K of the address space */

  Addr = EvalStrIntExpressionWithFlags(pRegArg, UInt12, &OK, &Flags);
  if (!OK)
    return False;

  *pResult = AssembleBitSymbol(BitPos, OpSize, Addr);

  return True;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitfieldArg2(LongWord *pResult, const tStrComp *pRegArg, tStrComp *pBitArg, ShortInt OpSize)
 * \brief  encode a bit field symbol, address & bit position separated
 * \param  pResult resulting encoded bit
 * \param  pRegArg register argument
 * \param  pBitArg bit argument
 * \param  OpSize register size (0/1/2 = 8/16/32 bit)
 * \return True if success
 * ------------------------------------------------------------------------ */

static Boolean DecodeBitfieldArg2(LongWord *pResult, const tStrComp *pRegArg, tStrComp *pBitArg, ShortInt OpSize)
{
  Boolean OK;
  LongWord Addr;
  Word BitSpec;

  if (!DecodeImmBitField(pBitArg, &BitSpec))
    return False;

  /* all I/O registers reside in the first 4K of the address space */

  Addr = EvalStrIntExpression(pRegArg, UInt12, &OK);
  if (!OK)
    return False;

  *pResult = AssembleBitfieldSymbol(BitSpec & 31, (BitSpec >> 5) & 31, OpSize, Addr);

  return True;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitArg(LongWord *pResult, int Start, int Stop, ShortInt OpSize)
 * \brief  encode a bit symbol from instruction argument(s)
 * \param  pResult resulting encoded bit
 * \param  Start first argument
 * \param  Stop last argument
 * \param  OpSize register size (0/1/2 = 8/16/32 bit)
 * \return True if success
 * ------------------------------------------------------------------------ */

static Boolean DecodeBitArg(LongWord *pResult, int Start, int Stop, ShortInt OpSize)
{
  *pResult = 0;

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

  if (Start == Stop)
  {
    tEvalResult EvalResult;

    *pResult = EvalStrIntExpressionWithResult(&ArgStr[Start], UInt32, &EvalResult);
    if (EvalResult.OK)
      ChkSpace(SegBData, EvalResult.AddrSpaceMask);
    return EvalResult.OK;
  }

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

  else if (Stop == Start + 1)
    return DecodeBitArg2(pResult, &ArgStr[Start], &ArgStr[Stop], OpSize);

  /* other # of arguments not allowed */

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

/*!------------------------------------------------------------------------
 * \fn     DecodeBitfieldArg(LongWord *pResult, int Start, int Stop, ShortInt OpSize)
 * \brief  encode a bit symbol from instruction argument(s)
 * \param  pResult resulting encoded bit
 * \param  Start first argument
 * \param  Stop last argument
 * \return True if success
 * ------------------------------------------------------------------------ */

static Boolean DecodeBitfieldArg(LongWord *pResult, int Start, int Stop, ShortInt OpSize)
{
  *pResult = 0;

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

  if (Start == Stop)
  {
    tEvalResult EvalResult;

    *pResult = EvalStrIntExpressionWithResult(&ArgStr[Start], UInt32, &EvalResult);
    if (EvalResult.OK)
      ChkSpace(SegBData, EvalResult.AddrSpaceMask);
    return EvalResult.OK;
  }

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

  else if (Stop == Start + 1)
    return DecodeBitfieldArg2(pResult, &ArgStr[Start], &ArgStr[Stop], OpSize);

  /* other # of arguments not allowed */

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

/*!------------------------------------------------------------------------
 * \fn     DissectBitSymbol(LongWord BitSymbol, Word *pAddress, Byte *pBitPos, Byte *pWidth, tSymbolSize *pOpSize)
 * \brief  transform compact represenation of bit (field) symbol into components
 * \param  BitSymbol compact storage
 * \param  pAddress (I/O) register address
 * \param  pBitPos (start) bit position
 * \param  pWidth pWidth width of bit field, always one for individual bit
 * \param  pOpSize returns register size (0/1/2 for 8/16/32 bits)
 * \return constant True
 * ------------------------------------------------------------------------ */

static Boolean DissectBitSymbol(LongWord BitSymbol, Word *pAddress, Byte *pBitPos, Byte *pWidth, tSymbolSize *pOpSize)
{
  *pOpSize = (tSymbolSize)((BitSymbol >> 20) & 3);
  switch (*pOpSize)
  {
    case eSymbolSize8Bit:
      *pAddress = (BitSymbol >> 3) & 0xfff;
      *pBitPos = BitSymbol & 7;
      break;
    case eSymbolSize16Bit:
      *pAddress = (BitSymbol >> 4) & 0xfff;
      *pBitPos = BitSymbol & 15;
      break;
    case 3:
      *pOpSize = eSymbolSize24Bit;
      /* fall-through */
    case eSymbolSize32Bit:
      *pAddress = (BitSymbol >> 5) & 0xfff;
      *pBitPos = BitSymbol & 31;
    default:
      break;
  }
  *pWidth = 1 + ((BitSymbol >> 24) & 31);
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     DissectBit_S12Z(char *pDest, size_t DestSize, LargeWord Inp)
 * \brief  dissect compact storage of bit (field) into readable form for listing
 * \param  pDest destination for ASCII representation
 * \param  DestSize destination buffer size
 * \param  Inp compact storage
 * ------------------------------------------------------------------------ */

static void DissectBit_S12Z(char *pDest, size_t DestSize, LargeWord Inp)
{
  Byte BitPos, BitWidth;
  Word Address;
  tSymbolSize OpSize;
  char Attribute;

  DissectBitSymbol(Inp, &Address, &BitPos, &BitWidth, &OpSize);
  Attribute = (OpSize == eSymbolSize24Bit) ? 'p' : "bwl"[OpSize];

  if (BitWidth > 1)
    as_snprintf(pDest, DestSize, "$%x(%c).%u:%u", (unsigned)Address, Attribute, (unsigned)BitWidth, (unsigned)BitPos);
  else
    as_snprintf(pDest, DestSize, "$%x(%c).%u", (unsigned)Address, Attribute, (unsigned)BitPos);
}

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

static void ExpandS12ZBit(const tStrComp *pVarName, const struct sStructElem *pStructElem, LargeWord Base)
{
  LongWord Address = Base + pStructElem->Offset;

  if (pInnermostNamedStruct)
  {
    PStructElem pElem = CloneStructElem(pVarName, pStructElem);

    if (!pElem)
      return;
    pElem->Offset = Address;
    AddStructElem(pInnermostNamedStruct->StructRec, pElem);
  }
  else
  {
    ShortInt OpSize = (pStructElem->OpSize < 0) ? 0 : pStructElem->OpSize;

    if (!ChkRange(Address, 0, 0xfff)
     || !ChkRange(pStructElem->BitPos, 0, (8 << OpSize) - 1))
      return;

    PushLocHandle(-1);
    EnterIntSymbol(pVarName, AssembleBitSymbol(pStructElem->BitPos, OpSize, Address), SegBData, False);
    PopLocHandle();
    /* TODO: MakeUseList? */
  }
}

/*!------------------------------------------------------------------------
 * \fn     ExpandS12ZBitfield(const tStrComp *pVarName, const struct sStructElem *pStructElem, LargeWord Base)
 * \brief  expands bit field definition when a structure is instantiated
 * \param  pVarName desired symbol name
 * \param  pStructElem element definition
 * \param  Base base address of instantiated structure
 * ------------------------------------------------------------------------ */

static void ExpandS12ZBitfield(const tStrComp *pVarName, const struct sStructElem *pStructElem, LargeWord Base)
{
  LongWord Address = Base + pStructElem->Offset;

  if (pInnermostNamedStruct)
  {
    PStructElem pElem = CloneStructElem(pVarName, pStructElem);

    if (!pElem)
      return;
    pElem->Offset = Address;
    AddStructElem(pInnermostNamedStruct->StructRec, pElem);
  }
  else
  {
    ShortInt OpSize = (pStructElem->OpSize < 0) ? 0 : pStructElem->OpSize;

    if (!ChkRange(Address, 0, 0xfff)
     || !ChkRange(pStructElem->BitPos, 0, (8 << OpSize) - 1)
     || !ChkRange(pStructElem->BitPos + pStructElem->BitWidthM1, 0, (8 << OpSize) - 1))
      return;

    PushLocHandle(-1);
    EnterIntSymbol(pVarName, AssembleBitfieldSymbol(pStructElem->BitPos, pStructElem->BitWidthM1 + 1, OpSize, Address), SegBData, False);
    PopLocHandle();
    /* TODO: MakeUseList? */
  }
}

/*--------------------------------------------------------------------------*/
/* Instruction Decoders */

static void DecodeFixed(Word Code)
{
  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (moto8_chk_no_args())
    PutCode(Code);
}

static Boolean DecodeBranchCore(int ArgIndex)
{
  Boolean OK;
  tSymbolFlags Flags;
  LongInt ShortDist, LongDist;

  /* manual says distance is relative to start of next instruction */

  ShortDist = EvalStrIntExpressionWithFlags(&ArgStr[ArgIndex], UInt24, &OK, &Flags) - (EProgCounter() + CodeLen + 1);
  if (!OK)
    return False;
  LongDist = ShortDist - 1;

  if (OpSize == eSymbolSizeUnknown)
    OpSize = ((ShortDist <= 63) && (ShortDist >= -64)) ? eSymbolSizeFloat32Bit : eSymbolSize32Bit;
  switch (OpSize)
  {
    case eSymbolSize32Bit:
      if (!mSymbolQuestionable(Flags) && !RangeCheck(LongDist, SInt15))
      {
        WrError(ErrNum_JmpDistTooBig);
        return False;
      }
      else
      {
        BAsmCode[CodeLen++] = 0x80 | ((LongDist >> 7) & 0x7f);
        BAsmCode[CodeLen++] = LongDist & 0xff;
      }
      break;
    case eSymbolSizeFloat32Bit:
      if (!mSymbolQuestionable(Flags) && !RangeCheck(ShortDist, SInt7))
      {
        WrError(ErrNum_JmpDistTooBig);
        return False;
      }
      else
      {
        BAsmCode[CodeLen++] = ShortDist & 0x7f;
      }
      break;
    default:
      WrStrErrorPos(ErrNum_InvOpSize, &AttrPart);
      return False;
  }
  return True;
}

static void DecodeBranch(Word Code)
{
  if (!ChkArgCnt(1, 1))
    return;

  PutCode(Code);
  if (!DecodeBranchCore(1))
    CodeLen = 0;
}

static void DecodeReg(Word Code)
{
  Byte Reg;

  if (ChkArgCnt(1, 1)
   && DecodeRegArg(1, &Reg, 0xff)
   && SetOpSize(RegSizes[Reg]))
    PutCode(Code | Reg);
}

static void DecodeTwoReg(Word Code)
{
  Byte SrcReg, DestReg;

  /* TODO: what is the operand order (source/dest)? The manual is
     unclear about this.  Assuming source is first argument, similar to TFR: */

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2)
        && DecodeRegArg(2, &DestReg, 0xff)
        && DecodeRegArg(1, &SrcReg, 0xff))
  {
    PutCode(Code);
    BAsmCode[CodeLen++] = DestReg | (SrcReg << 4);
  }
}

static void DecodeRegMemImm(Word Code)
{
  Byte Reg;
  tAdrVals AdrVals;

  if (ChkArgCnt(2, 2)
   && DecodeRegArg(1, &Reg, 0xff)
   && SetOpSize(RegSizes[Reg])
   && DecodeAdr(2, MModeImm | MModeMemReg, &AdrVals))
  {
    if (AdrVals.Mode == AdrModeImm)
      PutCode(Code | Reg);
    else
    {
      PutCode((Code + 0x10) | Reg);
      BAsmCode[CodeLen++] = AdrVals.Arg;
    }
    AppendAdrVals(&AdrVals);
  }
}

static void DecodeSUB(Word Code)
{
  Byte Reg;
  tAdrVals AdrVals;

  if (ArgCnt == 3)
  {
    if (DecodeRegArg(1, &Reg, 1 << 6)
     && DecodeAdrRegArg(2, &Reg, 3)
     && DecodeAdrRegArg(3, &Reg, 1 << (1 - Reg)))
    {
      BAsmCode[CodeLen++] = 0xfe - Reg;
    }
  }
  else if (ChkArgCnt(2, 3)
   && DecodeRegArg(1, &Reg, 0xff)
   && SetOpSize(RegSizes[Reg])
   && DecodeAdr(2, MModeImm | MModeMemReg, &AdrVals))
  {
    if (AdrVals.Mode == AdrModeImm)
      PutCode(Code | Reg);
    else
    {
      PutCode((Code + 0x10) | Reg);
      BAsmCode[CodeLen++] = AdrVals.Arg;
    }
    AppendAdrVals(&AdrVals);
  }
}

static void DecodeCMP(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    Byte Reg;
    tAdrVals AdrVals;

    DecodeAdr(1, MModeReg | MModeAReg, &AdrVals);
    Reg = AdrVals.Arg;
    switch (AdrVals.Mode)
    {
      case AdrModeReg:
        if (SetOpSize(RegSizes[Reg])
         && DecodeAdr(2, MModeImm | MModeMemReg, &AdrVals))
        {
          if (AdrVals.Mode == AdrModeImm)
            PutCode(Code | Reg);
          else
          {
            PutCode((Code + 0x10) | Reg);
            BAsmCode[CodeLen++] = AdrVals.Arg;
          }
          AppendAdrVals(&AdrVals);
        }
        break;
      case AdrModeAReg:
        if (Reg == 3) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
        else if (SetOpSize(eSymbolSize24Bit))
        {
          DecodeAdr(2, MModeImm | MModeMemReg | MModeAReg, &AdrVals);
          switch (AdrVals.Mode)
          {
            case AdrModeImm:
              PutCode((Reg == 2) ? 0x1b04 : (0xe8 | Reg));
              AppendAdrVals(&AdrVals);
              break;
            case AdrModeMemReg:
              PutCode((Reg == 2) ? 0x1b02 : (0xf8 | Reg));
              BAsmCode[CodeLen++] = AdrVals.Arg;
              AppendAdrVals(&AdrVals);
              break;
            case AdrModeAReg:
              if (Reg != 0) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
              else if (AdrVals.Arg != 1) WrStrErrorPos(ErrNum_InvReg, &ArgStr[2]);
              else
                PutCode(0xfc);
              break;
            default:
              break;
          }
        }
        break;
      default:
        break;
    }
  }
}

static void DecodeImm8(Word Code)
{
  tAdrVals AdrVals;

  if (ChkArgCnt(1, 1) && SetOpSize(eSymbolSize8Bit) && DecodeAdr(1, MModeImm, &AdrVals))
  {
    PutCode(Code);
    AppendAdrVals(&AdrVals);
  }
}

static void DecodeShift(Word Code)
{
  if (ChkArgCnt(2,3))
  {
    tAdrVals CntAdrVals, OpAdrVals;
    tSymbolSize SaveOpSize;
    Boolean IsASL = (Code == 0xc0),
            IsASR = (Code == 0x80);
    Boolean ImmediateCnt, DestIsReg;
    Byte ImmCnt, SizeCode, OpReg, DestReg;

    /* force operand size to 5 bits for count */

    SaveOpSize = OpSize;
    OpSize = OpSizeShiftCount;
    if (!DecodeAdr(ArgCnt, MModeImm | MModeMemReg | MModeReg, &CntAdrVals))
      return;
    ImmediateCnt = IsImmediate(&CntAdrVals, OpSize, &ImmCnt);
    OpSize = SaveOpSize;

    /* source or source-and-dest operand */

    if (!DecodeAdr(ArgCnt - 1, MModeMemReg | MModeReg, &OpAdrVals))
      return;

    /* operand size not yet set - then set from source */

    if (IsReg(&OpAdrVals, &OpReg))
      SetOpSize(RegSizes[OpReg]);
    if (OpSize < 0)
    {
      WrError(ErrNum_UndefOpSizes);
      return;
    }
    else if (!SizeCode2(OpSize, &SizeCode))
    {
      WrError(ErrNum_InvOpSize);
      return;
    }

    /* for three args, destination is always a register */

    if (ArgCnt == 3)
    {
      /* dest reg does not set operand size - opsize is from src
         operand which may be memory */

      if (!DecodeRegArg(1, &DestReg, 0xff))
        return;
      DestIsReg = True;
    }
    else
      DestIsReg = IsReg(&OpAdrVals, &DestReg);

    /* REG-REG-OPR1/2/3 only allowed with ASL: convert to (REG-)OPR1/2/3-OPR1/2/3 for other instructions,
       unless count is immediate: */

    if (!IsASL && DestIsReg && (OpAdrVals.Mode == AdrModeReg) && !ImmediateCnt && (CntAdrVals.Mode == AdrModeMemReg))
    {
      OpAdrVals.Mode = AdrModeMemReg;
      OpAdrVals.Arg |= 0xb8;
    }

    /* REG-REG */

    if (DestIsReg && (OpAdrVals.Mode == AdrModeReg) && (CntAdrVals.Mode == AdrModeReg))
    {
      BAsmCode[CodeLen++] = 0x10 | DestReg;
      BAsmCode[CodeLen++] = Code | (IsASL ? 0x10 : 0x20) | OpAdrVals.Arg;
      BAsmCode[CodeLen++] = 0xb8 | CntAdrVals.Arg;
    }

    /* REG-IMM with n=1..2 */

    else if (DestIsReg && (OpAdrVals.Mode == AdrModeReg) && ImmediateCnt && (ImmCnt >= 1) && (ImmCnt <= 2))
    {
      BAsmCode[CodeLen++] = (IsASR ? 0x00 : 0x10) | DestReg;
      BAsmCode[CodeLen++] = Code | (IsASR ? 0x10 : 0x00) | ((ImmCnt - 1) << 3) | OpAdrVals.Arg;
    }

    /* REG-IMM with arbitrary n */

    else if (DestIsReg && (OpAdrVals.Mode == AdrModeReg) && ImmediateCnt)
    {
      BAsmCode[CodeLen++] = 0x10 | DestReg;
      BAsmCode[CodeLen++] = Code | (IsASL ? 0x00 : 0x10) | ((ImmCnt & 1) << 3) | OpAdrVals.Arg;
      BAsmCode[CodeLen++] = 0x70 | ((ImmCnt >> 1) & 7);
    }

    /* REG-OPR1/2/3 - ASL only */

    else if (IsASL && DestIsReg && (OpAdrVals.Mode == AdrModeReg) && (CntAdrVals.Mode == AdrModeMemReg))
    {
      BAsmCode[CodeLen++] = 0x10 | DestReg;
      BAsmCode[CodeLen++] = Code | (CntAdrVals.ShiftLSB << 3) | OpAdrVals.Arg;
      BAsmCode[CodeLen++] = CntAdrVals.Arg;
      AppendAdrVals(&CntAdrVals);
    }

    /* (REG-)OPR1/2/3-IMM with n=1..2 */

    else if (DestIsReg && ImmediateCnt && (ImmCnt >= 1) && (ImmCnt <= 2))
    {
      BAsmCode[CodeLen++] = 0x10 | DestReg;
      BAsmCode[CodeLen++] = Code | 0x20 | ((ImmCnt - 1) << 3) | SizeCode;
      BAsmCode[CodeLen++] = OpAdrVals.Arg;
      AppendAdrVals(&OpAdrVals);
    }

    /* (REG-)OPR1/2/3-IMM with arbitrary n */

    else if (DestIsReg && ImmediateCnt && (OpAdrVals.Mode == AdrModeMemReg))
    {
      BAsmCode[CodeLen++] = 0x10 | DestReg;
      BAsmCode[CodeLen++] = Code | 0x30 | ((ImmCnt & 1) << 3) | SizeCode;
      BAsmCode[CodeLen++] = OpAdrVals.Arg;
      AppendAdrVals(&OpAdrVals);
      BAsmCode[CodeLen++] = 0x70 | ((ImmCnt >> 1) & 0x0f);
    }

    /* (REG-)OPR1/2/3-OPR1/2/3 */

    else if (DestIsReg && (OpAdrVals.Mode == AdrModeMemReg) && (CntAdrVals.Mode == AdrModeMemReg))
    {
      BAsmCode[CodeLen++] = 0x10 | DestReg;
      BAsmCode[CodeLen++] = Code | 0x30 | (CntAdrVals.ShiftLSB << 3) | SizeCode;
      BAsmCode[CodeLen++] = OpAdrVals.Arg;
      AppendAdrVals(&OpAdrVals);
      BAsmCode[CodeLen++] = CntAdrVals.Arg;
      AppendAdrVals(&CntAdrVals);
    }

    /* (src-)OPR/1/2/3-IMM (n=1..2) */

    else if ((OpAdrVals.Mode == AdrModeMemReg) && ImmediateCnt && (ImmCnt >= 1) && (ImmCnt <= 2))
    {
      BAsmCode[CodeLen++] = 0x10;
      BAsmCode[CodeLen++] = Code | 0x34 | ((ImmCnt - 1) << 3) | SizeCode;
      BAsmCode[CodeLen++] = OpAdrVals.Arg;
      AppendAdrVals(&OpAdrVals);
    }

    else
      WrError(ErrNum_InvAddrMode);
  }
}

static void DecodeBit(Word Code)
{
  tSymbolSize SaveOpSize;
  tAdrVals PosAdrVals, OpAdrVals;
  Byte ImmPos, ImmWidth, SizeCode, OpReg;
  Boolean ImmediatePos;

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

  /* bit operand: */

  if (1 == ArgCnt)
  {
    LongWord BitArg;
    tSymbolSize ThisOpSize;
    Word Address;

    if (DecodeBitArg(&BitArg, 1, 1, OpSize)
     && DissectBitSymbol(BitArg, &Address, &ImmPos, &ImmWidth, &ThisOpSize)
     && SetOpSize(ThisOpSize))
    {
      /* TODO: warn if ImmWidth != 1 */
      if (!SizeCode2(OpSize, &SizeCode) || (SizeCode == 2))
      {
        WrError(ErrNum_InvOpSize);
        return;
      }
      ImmediatePos = True;
      OpAdrVals.Mode = AdrModeMemReg;
      OpAdrVals.Arg = Hi(Address);
      OpAdrVals.Vals[0] = Lo(Address);
      OpAdrVals.ValCnt = 1;
    }
    else
      return;
    OpReg = 0;
  }

  /* other operand */

  else
  {
    if (!DecodeAdr(1, MModeMemReg | MModeReg, &OpAdrVals))
      return;

    /* operand size not yet set - then set from source */

    if (IsReg(&OpAdrVals, &OpReg))
      SetOpSize(RegSizes[OpReg]);
    if (OpSize < 0)
    {
      WrError(ErrNum_UndefOpSizes);
      return;
    }

    else if (!SizeCode2(OpSize, &SizeCode) || (SizeCode == 2))
    {
      WrError(ErrNum_InvOpSize);
      return;
    }

    /* force operand size to 3/4/5 bits for bit position */

    SaveOpSize = OpSize;
    OpSize = OpSizeBitPos8 + OpSize;
    if (!DecodeAdr(2, MModeImm | MModeReg, &PosAdrVals))
      return;
    ImmediatePos = IsImmediate(&PosAdrVals, OpSize, &ImmPos);
    OpSize = SaveOpSize;
  }

  switch (OpAdrVals.Mode)
  {
    case AdrModeReg:
      BAsmCode[CodeLen++] = Code;
      if (ImmediatePos)
        BAsmCode[CodeLen++] = (ImmPos << 3) | OpReg;
      else
      {
        BAsmCode[CodeLen++] = 0x81 | (PosAdrVals.Arg << 4);
        BAsmCode[CodeLen++] = 0xb8 | OpReg;
      }
      break;
    case AdrModeMemReg:
      BAsmCode[CodeLen++] = Code;
      if (ImmediatePos)
      {
        BAsmCode[CodeLen] = 0x80 | ((ImmPos & 7) << 4);
        if (OpSize >= eSymbolSize16Bit)
          BAsmCode[CodeLen] |= (1 << SizeCode) | ((ImmPos >> 3) & SizeCode);
        CodeLen++;
      }
      else
        BAsmCode[CodeLen++] = 0x81 | (PosAdrVals.Arg << 4) | (SizeCode << 2);
      BAsmCode[CodeLen++] = OpAdrVals.Arg;
      AppendAdrVals(&OpAdrVals);
      break;
    default:
      break;
  }
}

static void DecodeBitField(Word Code)
{
  if (!ChkArgCnt(2, 3))
    return;

  /* if two arguments, bit field is symbolic and is
     - the destination (first arg) for BFINS
     - the source (second arg) for BFEXT */

  if (2 == ArgCnt)
  {
    LongWord BitfieldArg;
    Byte Reg, ImmPos, ImmWidth, SizeCode;
    Word FieldSpec, Address;
    tSymbolSize ThisOpSize;
    int RegArg = (Code == 0x80) ? 2 : 1;

    if (DecodeRegArg(RegArg, &Reg, 0xff)
     && DecodeBitfieldArg(&BitfieldArg, 3 - RegArg, 3 - RegArg, OpSize)
     && DissectBitSymbol(BitfieldArg, &Address, &ImmPos, &ImmWidth, &ThisOpSize)
     && SetOpSize(ThisOpSize)
     && SizeCode2(OpSize, &SizeCode))
    {
      FieldSpec = ImmPos | ((ImmWidth < 32) ? ((Word)ImmWidth << 5) : 0);
      BAsmCode[CodeLen++] = 0x1b;
      BAsmCode[CodeLen++] = 0x08 | Reg;
      BAsmCode[CodeLen++] = (Code ? 0xf0: 0x60) | (SizeCode << 2) | Hi(FieldSpec);
      BAsmCode[CodeLen++] = Lo(FieldSpec);
      BAsmCode[CodeLen++] = Hi(Address);
      BAsmCode[CodeLen++] = Lo(Address);
    }
  }
  else
  {
    Byte ParamReg, SizeCode;
    Word ParamImm;
    tAdrVals SrcAdrVals, DestAdrVals;

    if (*ArgStr[3].str.p_str == '#')
    {
      tStrComp Field;

      StrCompRefRight(&Field, &ArgStr[3], 1);
      if (!DecodeImmBitField(&Field, &ParamImm))
        return;
      ParamReg = 16; /* immediate flag */
    }

    /* only D2...D5 allowed as parameter */

    else if (!DecodeRegStr(ArgStr[3].str.p_str, &ParamReg) || (ParamReg >= 4))
    {
      WrStrErrorPos(ErrNum_InvReg, &ArgStr[3]);
      return;
    }

    DecodeAdr(2, MModeReg | MModeImm | MModeMemReg, &SrcAdrVals);
    switch (SrcAdrVals.Mode)
    {
      case AdrModeReg:
        DecodeAdr(1, MModeReg | MModeMemReg, &DestAdrVals);
        switch (DestAdrVals.Mode)
        {
          case AdrModeReg:
            BAsmCode[CodeLen++] = 0x1b;
            BAsmCode[CodeLen++] = 0x08 | DestAdrVals.Arg;
            if (16 == ParamReg)
            {
              BAsmCode[CodeLen++] = Code | 0x20 | (SrcAdrVals.Arg << 2) | Hi(ParamImm);
              BAsmCode[CodeLen++] = Lo(ParamImm);
            }
            else
              BAsmCode[CodeLen++] = Code | (SrcAdrVals.Arg << 2) | ParamReg;
            break;
          case AdrModeMemReg:
            if (OpSize == eSymbolSizeUnknown) WrError(ErrNum_UndefOpSizes);
            else if (!SizeCode2(OpSize, &SizeCode)) WrError(ErrNum_InvOpSize);
            else
            {
              BAsmCode[CodeLen++] = 0x1b;
              BAsmCode[CodeLen++] = 0x08 | SrcAdrVals.Arg;
              if (16 == ParamReg)
              {
                BAsmCode[CodeLen++] = Code | 0x70 | (SizeCode << 2) | Hi(ParamImm);
                BAsmCode[CodeLen++] = Lo(ParamImm);
              }
              else
                BAsmCode[CodeLen++] = Code | 0x50 | (SizeCode << 2) | ParamReg;
              BAsmCode[CodeLen++] = DestAdrVals.Arg;
              AppendAdrVals(&DestAdrVals);
            }
            break;
          default:
            break;
        }
        break;
      case AdrModeMemReg:
        DecodeAdr(1, MModeReg, &DestAdrVals);
        switch (DestAdrVals.Mode)
        {
          case AdrModeReg:
            if (OpSize  == eSymbolSizeUnknown) WrError(ErrNum_UndefOpSizes);
            else if (!SizeCode2(OpSize, &SizeCode)) WrError(ErrNum_InvOpSize);
            else
            {
              BAsmCode[CodeLen++] = 0x1b;
              BAsmCode[CodeLen++] = 0x08 | DestAdrVals.Arg;
              if (16 == ParamReg)
              {
                BAsmCode[CodeLen++] = Code | 0x60 | (SizeCode << 2) | Hi(ParamImm);
                BAsmCode[CodeLen++] = Lo(ParamImm);
              }
              else
                BAsmCode[CodeLen++] = Code | 0x40 | (SizeCode << 2) | ParamReg;
              BAsmCode[CodeLen++] = SrcAdrVals.Arg;
              AppendAdrVals(&SrcAdrVals);
            }
            break;
          default:
            break;
        }
        break;
      case AdrModeImm: /* immediate only allowed for short immediate in MemReg op */
        WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
        break;
      default:
        break;
    }
  }
}

static void DecodeBitRel(Word Code)
{
  if (!ChkArgCnt(3, 3))
    return;

  ArgCnt--;
  DecodeBit(Code);
  if (!CodeLen)
    return;

  /* operand size attribute is consumed by bit operand */

  OpSize = eSymbolSizeUnknown;
  if (!DecodeBranchCore(3))
    CodeLen = 0;
}

static void DecodeCLR(Word Code)
{
  tAdrVals AdrVals;

  UNUSED(Code);

  if (ChkArgCnt(1, 1) && DecodeAdr(1, MModeReg | MModeAReg | MModeMemReg, &AdrVals))
  {
    switch (AdrVals.Mode)
    {
      case AdrModeReg:
        if (SetOpSize(RegSizes[AdrVals.Arg]))
          PutCode(0x0038 | AdrVals.Arg);
        break;
      case AdrModeAReg:
        if (!SetOpSize(eSymbolSize24Bit));
        else if (AdrVals.Arg > 1) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
        else
          PutCode(0x009a | AdrVals.Arg);
        break;
      case AdrModeMemReg:
      {
        Byte SizeCode;

        if (OpSize == eSymbolSizeUnknown) WrError(ErrNum_UndefOpSizes);
        else if (!SizeCode2(OpSize, &SizeCode)) WrError(ErrNum_InvOpSize);
        else
        {
          BAsmCode[CodeLen++] = 0xbc | SizeCode;
          BAsmCode[CodeLen++] = AdrVals.Arg;
          AppendAdrVals(&AdrVals);
        }
        break;
      }
      default:
        break;
    }
  }
}

static void DecodeCOM_NEG(Word Code)
{
  tAdrVals AdrVals;
  Byte OpReg, SizeCode;

  if (!ChkArgCnt(1, 1))
    return;
  DecodeAdr(1, MModeMemReg, &AdrVals);

  /* operand size not yet set - then set from (register) op */

  if (IsReg(&AdrVals, &OpReg))
  {
    if (!SetOpSize(RegSizes[OpReg]))
      return;
  }
  if (OpSize == eSymbolSizeUnknown)
  {
    WrError(ErrNum_UndefOpSizes);
    return;
  }
  if (!SizeCode2(OpSize, &SizeCode) || (OpSize == eSymbolSize24Bit))
  {
    WrError(ErrNum_InvOpSize);
    return;
  }
  PutCode(Code | SizeCode);
  BAsmCode[CodeLen++] = AdrVals.Arg;
  AppendAdrVals(&AdrVals);
}

static void DecodeDBcc(Word Code)
{
  tAdrVals AdrVals;
  Byte OpReg, SizeCode;

  if (!ChkArgCnt(2, 2))
    return;
  DecodeAdr(1, MModeReg | MModeAReg | MModeMemReg, &AdrVals);

  if (IsReg(&AdrVals, &OpReg))
  {
    if (!SetOpSize(RegSizes[OpReg]))
      return;
  }
  else if (AdrVals.Mode == AdrModeAReg)
  {
    if (!SetOpSize(eSymbolSize24Bit))
      return;
  }
  if (OpSize == eSymbolSizeUnknown)
  {
    WrError(ErrNum_UndefOpSizes);
    return;
  }
  if (!SizeCode2(OpSize, &SizeCode))
  {
    WrError(ErrNum_InvOpSize);
    return;
  }
  switch (AdrVals.Mode)
  {
    case AdrModeReg:
      PutCode(Code | AdrVals.Arg);
      break;
    case AdrModeAReg:
      if (AdrVals.Arg > 1) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
      else
        PutCode(Code | 0x0008 | AdrVals.Arg);
      break;
    case AdrModeMemReg:
      PutCode(Code | 0x000c | SizeCode);
      BAsmCode[CodeLen++] = AdrVals.Arg;
      AppendAdrVals(&AdrVals);
      break;
    default:
      break;
  }

  if (CodeLen > 0)
  {
    /* operand size attribute consumed by operand */

    OpSize = eSymbolSizeUnknown;

    if (!DecodeBranchCore(2))
      CodeLen = 0;
  }
}

static void DecodeINC_DEC(Word Code)
{
  tAdrVals AdrVals;

  if (ChkArgCnt(1, 1) && DecodeAdr(1, MModeReg | MModeMemReg, &AdrVals))
  {
    switch (AdrVals.Mode)
    {
      case AdrModeReg:
        if (SetOpSize(RegSizes[AdrVals.Arg]))
          PutCode(Code + AdrVals.Arg);
        break;
      case AdrModeMemReg:
      {
        Byte SizeCode;

        if (OpSize == eSymbolSizeUnknown) WrError(ErrNum_UndefOpSizes);
        else if (!SizeCode2(OpSize, &SizeCode) || (OpSize == eSymbolSize24Bit)) WrError(ErrNum_UndefOpSizes);
        else
        {
          BAsmCode[CodeLen++] = (Code + 0x6c) | SizeCode;
          BAsmCode[CodeLen++] = AdrVals.Arg;
          AppendAdrVals(&AdrVals);
        }
        break;
      }
      default:
        break;
    }
  }
}

static void DecodeDIV_MOD(Word Code)
{
  tAdrVals DividentAdrVals, DivisorAdrVals;
  Byte DividentSizeCode, DivisorSizeCode, DestReg, DivisorReg;
  Word EffCode, LoCode;

  EffCode = Hi(Code) | ((Lo(Code) & 0x01) ? 0x1b00 : 0x0000);
  LoCode = Lo(Code) & 0x80;

  /* destination is always a register */

  if (!ChkArgCnt(3, 3) || !DecodeRegArg(1, &DestReg, 0xff))
    return;

  DecodeAdr(2, MModeImm | MModeReg | MModeMemReg, &DividentAdrVals);
  switch (DividentAdrVals.Mode)
  {
    case AdrModeReg:
      DecodeAdr(3, MModeImm | MModeReg | MModeMemReg, &DivisorAdrVals);
      switch (DivisorAdrVals.Mode)
      {
        case AdrModeReg:
          PutCode(EffCode | DestReg);
          BAsmCode[CodeLen++] = LoCode | (DividentAdrVals.Arg << 3) | DivisorAdrVals.Arg;
          break;
        case AdrModeImm:
          if (!SizeCode2(OpSize, &DivisorSizeCode) || (OpSize == eSymbolSize24Bit)) WrError(ErrNum_UndefOpSizes);
          else
          {
            PutCode(EffCode | DestReg);
            BAsmCode[CodeLen++] = LoCode | 0x44 | (DividentAdrVals.Arg << 3) | DivisorSizeCode;
            AppendAdrVals(&DivisorAdrVals);
          }
          break;
        case AdrModeMemReg:
          if (!SizeCode2(OpSize, &DivisorSizeCode) || (OpSize == eSymbolSize24Bit)) WrError(ErrNum_UndefOpSizes);
          else
          {
            PutCode(EffCode | DestReg);
            BAsmCode[CodeLen++] = LoCode | 0x40 | (DividentAdrVals.Arg << 3) | DivisorSizeCode;
            BAsmCode[CodeLen++] = DivisorAdrVals.Arg;
            AppendAdrVals(&DivisorAdrVals);
          }
          break;
        default:
          break;
      }
      break;
    case AdrModeMemReg:
      /* divident==register is filtered out before, so divident size cannot be set from register */
      if (!SizeCode2(OpSize, &DividentSizeCode) || (OpSize == eSymbolSize24Bit)) WrError(ErrNum_UndefOpSizes);
      else
      {
        OpSize = OpSize2;
        DecodeAdr(3, MModeImm | MModeMemReg, &DivisorAdrVals);
        switch (DivisorAdrVals.Mode)
        {
          case AdrModeMemReg:
            if ((OpSize == eSymbolSizeUnknown) && IsReg(&DivisorAdrVals, &DivisorReg))
              SetOpSize(RegSizes[DivisorReg]);
            if (!SizeCode2(OpSize, &DivisorSizeCode) || (OpSize == eSymbolSize24Bit)) WrError(ErrNum_UndefOpSizes);
            else
            {
              PutCode(EffCode | DestReg);
              BAsmCode[CodeLen++] = LoCode | 0x42 | (DividentSizeCode << 4) | (DivisorSizeCode << 2);
              BAsmCode[CodeLen++] = DividentAdrVals.Arg;
              AppendAdrVals(&DividentAdrVals);
              BAsmCode[CodeLen++] = DivisorAdrVals.Arg;
              AppendAdrVals(&DivisorAdrVals);
            }
            break;
          case AdrModeImm: /* was only allowed for short imm in MemReg */
            WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[3]);
            break;
          default:
            break;
        }
      }
      break;
    case AdrModeImm: /* was only allowed for short imm in MemReg */
      WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
      break;
    default:
      break;
  }
}

static void DecodeEXG_TFR(Word Code)
{
  Byte SrcReg, DestReg;

  if (ChkArgCnt(2, 2)
   && DecodeGenRegArg(1, &SrcReg)
   && DecodeGenRegArg(2, &DestReg))
  {
    if ((OpSizeByteLen(RegSizes[SrcReg]) >= OpSizeByteLen(RegSizes[DestReg])) && Hi(Code))
      WrError(ErrNum_SrcLEThanDest);
    BAsmCode[CodeLen++] = Lo(Code);
    BAsmCode[CodeLen++] = (SrcReg << 4) | DestReg;
  }
}

static void DecodeJMP_JSR(Word Code)
{
  tAdrVals AdrVals;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(1, 1) && DecodeAdr(1, MModeMemReg, &AdrVals))
  {
    Byte Dummy;

    if (IsReg(&AdrVals, &Dummy)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
    else
    {
      if (AdrVals.Arg == 0xfa)
        PutCode(Code | 0x10);
      else
      {
        PutCode(Code);
        BAsmCode[CodeLen++] = AdrVals.Arg;
      }
      AppendAdrVals(&AdrVals);
    }
  }
}

static void DecodeLD_ST(Word Code)
{
  tAdrVals SrcAdrVals, DestAdrVals;

  if (ChkArgCnt(2, 2) && DecodeAdr(1, MModeReg | MModeAReg, &DestAdrVals))
  {
    switch (DestAdrVals.Mode)
    {
      case AdrModeReg:
        if (!SetOpSize(RegSizes[DestAdrVals.Arg]))
          return;
        DecodeAdr(2, (Code ? 0 : MModeImm) | MModeMemReg, &SrcAdrVals);
        switch (SrcAdrVals.Mode)
        {
          case AdrModeMemReg:
          {
            Byte ImmVal;

            if ((OpSize == eSymbolSize8Bit) && IsImmediate(&SrcAdrVals, OpSize, &ImmVal)) /* same instr length for byte, but what people expect? */
            {
              ChangeImmediate(&SrcAdrVals, OpSize, ImmVal);
              goto immediate;
            }
            if (SrcAdrVals.Arg == 0xfa)
              BAsmCode[CodeLen++] = (0xb0 + Code) | DestAdrVals.Arg;
            else
            {
              BAsmCode[CodeLen++] = (0xa0 + Code) | DestAdrVals.Arg;
              BAsmCode[CodeLen++] = SrcAdrVals.Arg;
            }
            AppendAdrVals(&SrcAdrVals);
            break;
          }
          case AdrModeImm:
          immediate:
            BAsmCode[CodeLen++] = 0x90 | DestAdrVals.Arg;
            AppendAdrVals(&SrcAdrVals);
            break;
          default:
            break;
        }
        break;
      case AdrModeAReg:
        if (DestAdrVals.Arg > 2)
        {
          WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
        }
        if (!SetOpSize(eSymbolSize24Bit))
          return;
        DecodeAdr(2, (Code ? 0 : MModeImm) | MModeMemReg, &SrcAdrVals);
        switch (SrcAdrVals.Mode)
        {
          case AdrModeMemReg:
            if ((DestAdrVals.Arg < 2) && (SrcAdrVals.Arg == 0xfa))
              BAsmCode[CodeLen++] = (0xb8 + Code) | DestAdrVals.Arg;
            else if (2 == DestAdrVals.Arg)
            {
              BAsmCode[CodeLen++] = 0x1b;
              BAsmCode[CodeLen++] = 0x00 + !!Code;
              BAsmCode[CodeLen++] = SrcAdrVals.Arg;
            }
            else
            {
              BAsmCode[CodeLen++] = (0xa8 + Code) | DestAdrVals.Arg;
              BAsmCode[CodeLen++] = SrcAdrVals.Arg;
            }
            AppendAdrVals(&SrcAdrVals);
            break;
          case AdrModeImm:
            /* SrcAdrVals.Cnt must be 3 */
            if ((DestAdrVals.Arg < 2) && (SrcAdrVals.Vals[0] < 0x04))
            {
              BAsmCode[CodeLen++] = 0xca | DestAdrVals.Arg | (SrcAdrVals.Vals[0] << 4);
              BAsmCode[CodeLen++] = SrcAdrVals.Vals[1];
              BAsmCode[CodeLen++] = SrcAdrVals.Vals[2];
            }
            else
            {
              PutCode((DestAdrVals.Arg == 2) ? 0x1b03 : (0x98 | DestAdrVals.Arg));
              AppendAdrVals(&SrcAdrVals);
            }
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
  }
}

static void DecodeLEA(Word Code)
{
  tAdrVals DestAdrVals, SrcAdrVals;
  Byte Reg;

  UNUSED(Code);

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2) && DecodeAdr(1, MModeReg | MModeAReg, &DestAdrVals))
  {
    switch (DestAdrVals.Mode)
    {
      case AdrModeReg:
        if (DestAdrVals.Arg < 6) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
        else if (DecodeAdr(2, MModeMemReg, &SrcAdrVals))
        {
          if (IsReg(&SrcAdrVals, &Reg)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
          else
          {
            BAsmCode[CodeLen++] = 0x00 | DestAdrVals.Arg;
            BAsmCode[CodeLen++] = SrcAdrVals.Arg;
            AppendAdrVals(&SrcAdrVals);
          }
        }
        break;
      case AdrModeAReg:
        if (DestAdrVals.Arg > 2) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
        else if (DecodeAdr(2, MModeMemReg, &SrcAdrVals))
        {
          if (IsReg(&SrcAdrVals, &Reg)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
          else
          {
            /* XYS,(i8,XYS) */

            if (((SrcAdrVals.Arg & 0xce) == 0xc0) /* ...,(XYS,i9) */
             && ((SrcAdrVals.Arg & 0x01) == ((SrcAdrVals.Vals[0] >> 7) & 1)) /* i9 is i8 */
             && (DestAdrVals.Arg == ((SrcAdrVals.Arg >> 4) & 3))) /* destreg==srcreg */
            {
              BAsmCode[CodeLen++] = 0x18 | DestAdrVals.Arg;
              BAsmCode[CodeLen++] = SrcAdrVals.Vals[0];
            }
            else
            {
              BAsmCode[CodeLen++] = 0x08 | DestAdrVals.Arg;
              BAsmCode[CodeLen++] = SrcAdrVals.Arg;
              AppendAdrVals(&SrcAdrVals);
            }
          }
        }
        break;
      default:
        break;
    }
  }
}

static void DecodeMIN_MAX(Word Code)
{
  tAdrVals AdrVals;
  Byte Reg;

  if (ChkArgCnt(2, 2)
   && DecodeRegArg(1, &Reg, 0xff)
   && SetOpSize(RegSizes[Reg])
   && DecodeAdr(2, MModeMemReg | MModeImm, &AdrVals))
  {
    switch (AdrVals.Mode)
    {
      case AdrModeMemReg:
        PutCode(Code | Reg);
        BAsmCode[CodeLen++] = AdrVals.Arg;
        AppendAdrVals(&AdrVals);
        break;
      case AdrModeImm: /* was only allowed for short immediate in MemReg */
        WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
        break;
      default:
        break;
    }
  }
}

static void DecodeMOV(Word Code)
{
  tAdrVals SrcAdrVals, DestAdrVals;
  Byte Reg, SizeCode;

  UNUSED(Code);

  if (ChkArgCnt(2, 2)
   && DecodeAdr(2, MModeMemReg, &DestAdrVals))
  {
    /* prefer attribute to destination... */

    if (IsReg(&DestAdrVals, &Reg) && (OpSize == eSymbolSizeUnknown))
      SetOpSize(RegSizes[Reg]);

    if (!DecodeAdr(1, MModeMemReg | MModeImm, &SrcAdrVals))
      return;

    /* ...to source operand size */

    if (IsReg(&SrcAdrVals, &Reg) && (OpSize == eSymbolSizeUnknown))
      SetOpSize(RegSizes[Reg]);

    if (!SizeCode2(OpSize, &SizeCode))
    {
      WrError(ErrNum_InvOpSize);
      return;
    }

    switch (SrcAdrVals.Mode)
    {
      case AdrModeImm:
        PutCode(0x0c + SizeCode);
        AppendAdrVals(&SrcAdrVals);
        BAsmCode[CodeLen++] = DestAdrVals.Arg;
        AppendAdrVals(&DestAdrVals);
        break;
      case AdrModeMemReg:
        PutCode(0x1c | SizeCode);
        BAsmCode[CodeLen++] = SrcAdrVals.Arg;
        AppendAdrVals(&SrcAdrVals);
        BAsmCode[CodeLen++] = DestAdrVals.Arg;
        AppendAdrVals(&DestAdrVals);
        break;
      default:
        break;
    }
  }
}

static void DecodePSH_PUL(Word Code)
{
  Word RegMask = 0, ThisRegMask;
  int z;
  Byte Reg;
  static const Word RegMasks[8] = { 0x0002, 0x0001, 0x2000, 0x1000, 0x0008, 0x004, 0x0800, 0x0400 };

  if (!ChkArgCnt(1, ArgCntMax))
    return;
  for (z = 1; z <= ArgCnt; z++)
  {
    if (!as_strcasecmp(ArgStr[z].str.p_str, "ALL"))
      ThisRegMask = 0x3f3f;
    else if (!as_strcasecmp(ArgStr[z].str.p_str, "ALL16b"))
      ThisRegMask = 0x3003;
    else if (DecodeRegStr(ArgStr[z].str.p_str, &Reg))
      ThisRegMask = RegMasks[Reg];
    else if (!as_strcasecmp(ArgStr[z].str.p_str, "CCH"))
      ThisRegMask = 0x0020;
    else if (!as_strcasecmp(ArgStr[z].str.p_str, "CCL"))
      ThisRegMask = 0x0010;
    else if (DecodeAdrRegStr(ArgStr[z].str.p_str, &Reg) && (Reg < 2))
      ThisRegMask = 0x0200 >> Reg;
    else
    {
      WrStrErrorPos(ErrNum_InvReg, &ArgStr[z]);
      return;
    }
    if (ThisRegMask & RegMask)
    {
      WrStrErrorPos(ErrNum_DoubleReg, &ArgStr[z]);
      return;
    }
    RegMask |= ThisRegMask;
  }
  if (RegMask == 0x3f3f)
    PutCode(Code | 0x00);
  else if (RegMask == 0x3003)
    PutCode(Code | 0x40);
  else if (Hi(RegMask) && !Lo(RegMask))
    PutCode(Code | 0x40 | Hi(RegMask));
  else if (Lo(RegMask) && !Hi(RegMask))
    PutCode(Code | 0x00 | Lo(RegMask));
  else
    WrError(ErrNum_InvRegList);
}

static void DecodeROL_ROR(Word Code)
{
  tAdrVals AdrVals;
  Byte Reg, SizeCode;

  if (ChkArgCnt(1, 1) && DecodeAdr(1, MModeMemReg, &AdrVals))
  {
    if (IsReg(&AdrVals, &Reg) && !SetOpSize(RegSizes[Reg]))
      return;
    if (OpSize == eSymbolSizeUnknown)
    {
      WrError(ErrNum_UndefOpSizes);
      return;
    }
    if (!SizeCode2(OpSize, &SizeCode))
    {
      WrError(ErrNum_InvOpSize);
      return;
    }
    PutCode(Code | SizeCode);
    BAsmCode[CodeLen++] = AdrVals.Arg;
    AppendAdrVals(&AdrVals);
  }
}

static void DecodeTBcc(Word Code)
{
  tAdrVals AdrVals;

  if (!ChkArgCnt(2, 2) || !DecodeAdr(1, MModeReg | MModeAReg | MModeMemReg | MModeImm, &AdrVals))
    return;

  switch (AdrVals.Mode)
  {
    case AdrModeReg:
      if (!SetOpSize(RegSizes[AdrVals.Arg]))
        return;
      PutCode(Code | AdrVals.Arg);
      break;
    case AdrModeAReg:
      if (AdrVals.Arg >= 2)
      {
        WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
        return;
      }
      if (!SetOpSize(eSymbolSize24Bit))
        return;
      PutCode(Code | 0x0008 | AdrVals.Arg);
      break;
    case AdrModeMemReg:
    {
      Byte SizeCode;

      if (OpSize == eSymbolSizeUnknown)
      {
        WrError(ErrNum_UndefOpSizes);
        return;
      }
      if (!SizeCode2(OpSize, &SizeCode))
      {
        WrError(ErrNum_InvOpSize);
        return;
      }
      PutCode(Code | 0x000c | SizeCode);
      BAsmCode[CodeLen++] = AdrVals.Arg;
      AppendAdrVals(&AdrVals);
      break;
    }
    case AdrModeImm: /* was only allowed for short immediate */
      WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
      return;
    default:
      return;
  }

  OpSize = OpSize2;
  if (!DecodeBranchCore(2))
    CodeLen = 0;
}

static void DecodeTRAP(Word Code)
{
  tAdrVals AdrVals;

  UNUSED(Code);

  if (ChkArgCnt(1, 1) && SetOpSize(eSymbolSize8Bit) && DecodeAdr(1, MModeImm, &AdrVals))
  {
    BAsmCode[CodeLen++] = 0x1b;
    BAsmCode[CodeLen++] = AdrVals.Vals[0];
    switch ((AdrVals.Vals[0] >> 4) & 0x0f)
    {
      case 12: case 13: case 14: case 15:
        break;
      case 10: case 11:
        if ((AdrVals.Vals[0] & 0x0f) >= 8)
          break;
        else
          goto warn;
      case 9:
        if ((AdrVals.Vals[0] & 0x0f) >= 2)
          break;
        /* else fall-through */
      default:
      warn:
        WrError(ErrNum_TrapValidInstruction);
    }
  }
}

static void DecodeDEFBIT(Word Code)
{
  LongWord BitSpec;

  UNUSED(Code);

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

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

    if (!ChkArgCnt(2, 2))
      return;
    BitPos = EvalBitPosition(&ArgStr[2], &OK, (OpSize == eSymbolSizeUnknown) ? eSymbolSize32Bit : OpSize);
    if (!OK)
      return;
    pElement = CreateStructElem(&LabPart);
    if (!pElement)
      return;
    pElement->pRefElemName = as_strdup(ArgStr[1].str.p_str);
    pElement->OpSize = OpSize;
    pElement->BitPos = BitPos;
    pElement->ExpandFnc = ExpandS12ZBit;
    AddStructElem(pInnermostNamedStruct->StructRec, pElement);
  }
  else
  {
    if (OpSize == eSymbolSizeUnknown)
      OpSize = eSymbolSize8Bit;
    if (OpSize > eSymbolSize32Bit)
    {
      WrError(ErrNum_InvOpSize);
      return;
    }

    if (DecodeBitArg(&BitSpec, 1, ArgCnt, OpSize))
    {
      *ListLine = '=';
      DissectBit_S12Z(ListLine + 1, STRINGSIZE - 3, BitSpec);
      PushLocHandle(-1);
      EnterIntSymbol(&LabPart, BitSpec, SegBData, False);
      PopLocHandle();
      /* TODO: MakeUseList? */
    }
  }
}

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

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

  if (ActPC == StructSeg)
  {
    Word BitField;
    PStructElem pElement;

    if (!ChkArgCnt(2, 2))
      return;
    if (!DecodeImmBitField(&ArgStr[2], &BitField))
      return;
    pElement = CreateStructElem(&LabPart);
    if (!pElement)
      return;
    pElement->pRefElemName = as_strdup(ArgStr[1].str.p_str);
    pElement->OpSize = OpSize;
    pElement->BitPos = BitField & 31;
    pElement->BitWidthM1 = (BitField >> 5) - 1;
    pElement->ExpandFnc = ExpandS12ZBitfield;
    AddStructElem(pInnermostNamedStruct->StructRec, pElement);
  }
  else
  {
    LongWord BitfieldSpec;

    /* opposed to bit operations, bit field operations also work
        24 bit operands: */

    if (OpSize == eSymbolSizeUnknown)
      OpSize = eSymbolSize8Bit;
    if ((OpSize > eSymbolSize32Bit) && (OpSize != eSymbolSize24Bit))
    {
      WrError(ErrNum_InvOpSize);
      return;
    }

    if (DecodeBitfieldArg(&BitfieldSpec, 1, ArgCnt, OpSize))
    {
      *ListLine = '=';
      DissectBit_S12Z(ListLine + 1, STRINGSIZE - 3, BitfieldSpec);
      PushLocHandle(-1);
      EnterIntSymbol(&LabPart, BitfieldSpec, SegBData, False);
      PopLocHandle();
      /* TODO: MakeUseList? */
    }
  }
}

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

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

static void AddBranch(const char *pName, Word Code)
{
  AddInstTable(InstTable, pName, Code, DecodeBranch);
}

static void AddReg(const char *pName, Word Code)
{
  AddInstTable(InstTable, pName, Code, DecodeReg);
}

static void AddRegMemImm(const char *pName, Word Code)
{
  AddInstTable(InstTable, pName, Code, DecodeRegMemImm);
}

static void AddCondition(const char *pName, Word Code, InstProc Proc)
{
  char InstrName[20];

  as_snprintf(InstrName, sizeof(InstrName), pName, "NE"); AddInstTable(InstTable, InstrName, Code | (0 << 4), Proc);
  as_snprintf(InstrName, sizeof(InstrName), pName, "EQ"); AddInstTable(InstTable, InstrName, Code | (1 << 4), Proc);
  as_snprintf(InstrName, sizeof(InstrName), pName, "PL"); AddInstTable(InstTable, InstrName, Code | (2 << 4), Proc);
  as_snprintf(InstrName, sizeof(InstrName), pName, "MI"); AddInstTable(InstTable, InstrName, Code | (3 << 4), Proc);
  as_snprintf(InstrName, sizeof(InstrName), pName, "GT"); AddInstTable(InstTable, InstrName, Code | (4 << 4), Proc);
  as_snprintf(InstrName, sizeof(InstrName), pName, "LE"); AddInstTable(InstTable, InstrName, Code | (5 << 4), Proc);
}

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

  add_null_pseudo(InstTable);

  AddFixed("NOP",  NOPCode);
  AddFixed("BGND", 0x0000);
  AddFixed("CLC",  0xcefe);
  AddFixed("CLI",  0xceef);
  AddFixed("CLV",  0xcefd);
  AddFixed("RTI",  0x1b90);
  AddFixed("RTS",  0x0005);
  AddFixed("SEC",  0xde01);
  AddFixed("SEI",  0xde10);
  AddFixed("SEV",  0xde02);
  AddFixed("STOP", 0x1b05);
  AddFixed("SWI",  0x00ff);
  AddFixed("SYS",  0x1b07);
  AddFixed("WAI",  0x1b06);
  AddFixed("SPARE", 0x00ef);

  AddBranch("BCC", 0x0024);
  AddBranch("BCS", 0x0025);
  AddBranch("BEQ", 0x0027);
  AddBranch("BGE", 0x002c);
  AddBranch("BGT", 0x002e);
  AddBranch("BHI", 0x0022);
  AddBranch("BHS", 0x0024);
  AddBranch("BLE", 0x002f);
  AddBranch("BLO", 0x0025);
  AddBranch("BLS", 0x0023);
  AddBranch("BLT", 0x002d);
  AddBranch("BMI", 0x002b);
  AddBranch("BNE", 0x0026);
  AddBranch("BPL", 0x002a);
  AddBranch("BRA", 0x0020);
  AddBranch("BSR", 0x0021);
  AddBranch("BVC", 0x0028);
  AddBranch("BVS", 0x0029);

  AddReg("ABS", 0x1b40);
  AddReg("SAT", 0x1ba0);

  AddRegMemImm("ADC",  0x1b50);
  AddRegMemImm("ADD",  0x0050);
  AddRegMemImm("AND",  0x0058);
  AddRegMemImm("BIT",  0x1b58);
  AddRegMemImm("EOR",  0x1b78);
  AddRegMemImm("OR",   0x0078);
  AddRegMemImm("SBC",  0x1b70);

  AddInstTable(InstTable, "SUB", 0x0070, DecodeSUB);
  AddInstTable(InstTable, "CMP", 0x00e0, DecodeCMP);

  AddInstTable(InstTable, "ANDCC", 0x00ce, DecodeImm8);
  AddInstTable(InstTable, "ORCC" , 0x00de, DecodeImm8);
  AddInstTable(InstTable, "TRAP" , 0, DecodeTRAP);

  AddInstTable(InstTable, "ASL", 0xc0, DecodeShift);
  AddInstTable(InstTable, "ASR", 0x80, DecodeShift);
  AddInstTable(InstTable, "LSL", 0x40, DecodeShift);
  AddInstTable(InstTable, "LSR", 0x00, DecodeShift);

  AddInstTable(InstTable, "BCLR", 0xec, DecodeBit);
  AddInstTable(InstTable, "BSET", 0xed, DecodeBit);
  AddInstTable(InstTable, "BTGL", 0xee, DecodeBit);

  AddInstTable(InstTable, "BFEXT", 0x00, DecodeBitField);
  AddInstTable(InstTable, "BFINS", 0x80, DecodeBitField);

  AddInstTable(InstTable, "BRCLR", 0x02, DecodeBitRel);
  AddInstTable(InstTable, "BRSET", 0x03, DecodeBitRel);

  AddInstTable(InstTable, "CLB", 0x1b91, DecodeTwoReg);

  AddInstTable(InstTable, "CLR", 0x0000, DecodeCLR);
  AddInstTable(InstTable, "COM", 0x00cc, DecodeCOM_NEG);
  AddInstTable(InstTable, "NEG", 0x00dc, DecodeCOM_NEG);
  AddCondition("DB%s", 0x0d80, DecodeDBcc);
  AddInstTable(InstTable, "DEC", 0x0040, DecodeINC_DEC);
  AddInstTable(InstTable, "INC", 0x0030, DecodeINC_DEC);

  AddInstTable(InstTable, "DIVS", 0x3081, DecodeDIV_MOD);
  AddInstTable(InstTable, "DIVU", 0x3001, DecodeDIV_MOD);
  AddInstTable(InstTable, "MODS", 0x3881, DecodeDIV_MOD);
  AddInstTable(InstTable, "MODU", 0x3801, DecodeDIV_MOD);
  AddInstTable(InstTable, "MACS", 0x4881, DecodeDIV_MOD);
  AddInstTable(InstTable, "MACU", 0x4801, DecodeDIV_MOD);
  AddInstTable(InstTable, "MULS", 0x4880, DecodeDIV_MOD);
  AddInstTable(InstTable, "MULU", 0x4800, DecodeDIV_MOD);
  AddInstTable(InstTable,"QMULS", 0xb081, DecodeDIV_MOD);
  AddInstTable(InstTable,"QMULU", 0xb001, DecodeDIV_MOD);

  AddInstTable(InstTable, "EXG", 0x00ae, DecodeEXG_TFR);
  AddInstTable(InstTable, "TFR", 0x009e, DecodeEXG_TFR);
  AddInstTable(InstTable, "SEX", 0x01ae, DecodeEXG_TFR);
  AddInstTable(InstTable, "ZEX", 0x019e, DecodeEXG_TFR);

  AddInstTable(InstTable, "JMP", 0x00aa, DecodeJMP_JSR);
  AddInstTable(InstTable, "JSR", 0x00ab, DecodeJMP_JSR);

  AddInstTable(InstTable, "LD" , 0x0000, DecodeLD_ST);
  AddInstTable(InstTable, "ST" , 0x0020, DecodeLD_ST);
  AddInstTable(InstTable, "MOV" , 0x0000, DecodeMOV);
  AddInstTable(InstTable, "LEA" , 0x0000, DecodeLEA);

  AddInstTable(InstTable, "MAXS", 0x1b28, DecodeMIN_MAX);
  AddInstTable(InstTable, "MAXU", 0x1b18, DecodeMIN_MAX);
  AddInstTable(InstTable, "MINS", 0x1b20, DecodeMIN_MAX);
  AddInstTable(InstTable, "MINU", 0x1b10, DecodeMIN_MAX);

  AddInstTable(InstTable, "PSH", 0x0400, DecodePSH_PUL);
  AddInstTable(InstTable, "PUL", 0x0480, DecodePSH_PUL);

  AddInstTable(InstTable, "ROL", 0x1064, DecodeROL_ROR);
  AddInstTable(InstTable, "ROR", 0x1024, DecodeROL_ROR);

  AddCondition("TB%s", 0x0b00, DecodeTBcc);

  AddInstTable(InstTable, "DEFBIT", 0, DecodeDEFBIT);
  AddInstTable(InstTable, "DEFBITFIELD", 0, DecodeDEFBITFIELD);

  add_moto8_pseudo(InstTable, e_moto_pseudo_flags_be);
  add_moto8_comment_onoff();
  AddMoto16Pseudo(InstTable, e_moto_pseudo_flags_be);
  AddInstTable(InstTable, "DB", eIntPseudoFlag_BigEndian | eIntPseudoFlag_AllowInt | eIntPseudoFlag_AllowString | eIntPseudoFlag_MotoRep, DecodeIntelDB);
  AddInstTable(InstTable, "DW", eIntPseudoFlag_BigEndian | eIntPseudoFlag_AllowInt | eIntPseudoFlag_AllowString | eIntPseudoFlag_MotoRep, DecodeIntelDW);
}

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

/*--------------------------------------------------------------------------*/
/* Semiglobal Functions */

static Boolean DecodeAttrPart_S12Z(void)
{
  int z;

  if (strlen(AttrPart.str.p_str) > 2)
  {
    WrStrErrorPos(ErrNum_UndefAttr, &AttrPart);
    return False;
  }

  for (z = 0; z < 2; z++)
  {
    if (AttrPart.str.p_str[z] == '\0')
      break;
    if (!DecodeMoto16AttrSize(AttrPart.str.p_str[z], &AttrPartOpSize[z], True))
      return False;
  }
  return True;
}

static void MakeCode_S12Z(void)
{
  OpSize = (AttrPartOpSize[0] != eSymbolSizeUnknown) ? AttrPartOpSize[0] : eSymbolSizeUnknown;
  OpSize2 = (AttrPartOpSize[1] != eSymbolSizeUnknown) ? AttrPartOpSize[1] : eSymbolSizeUnknown;

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

static Boolean IsDef_S12Z(void)
{
  return Memo("DEFBIT") || Memo("DEFBITFIELD");
}

static void SwitchTo_S12Z(void)
{
  const TFamilyDescr *pDescr = FindFamilyByName("S12Z");
  TurnWords = False;
  SetIntConstMode(eIntConstModeMoto);

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

  ValidSegs = (1 << SegCode);
  Grans[SegCode] = 1; ListGrans[SegCode] = 1; SegInits[SegCode] = 0;
  SegLimits[SegCode] = 0xffffff;
  DecodeAttrPart = DecodeAttrPart_S12Z;
  MakeCode = MakeCode_S12Z;
  IsDef = IsDef_S12Z;
  SwitchFrom = DeinitFields;
  DissectBit = DissectBit_S12Z;
  InitFields();
  AddMoto16PseudoONOFF(False);
}

void codes12z_init(void)
{
  (void)AddCPU("S912ZVC19F0MKH" , SwitchTo_S12Z);
  (void)AddCPU("S912ZVC19F0MLF" , SwitchTo_S12Z);
  (void)AddCPU("S912ZVCA19F0MKH", SwitchTo_S12Z);
  (void)AddCPU("S912ZVCA19F0MLF", SwitchTo_S12Z);
  (void)AddCPU("S912ZVCA19F0WKH", SwitchTo_S12Z);
  (void)AddCPU("S912ZVH128F2CLQ", SwitchTo_S12Z);
  (void)AddCPU("S912ZVH128F2CLL", SwitchTo_S12Z);
  (void)AddCPU("S912ZVH64F2CLQ" , SwitchTo_S12Z);
  (void)AddCPU("S912ZVHY64F1CLQ", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHY32F1CLQ", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHY64F1CLL", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHY32F1CLL", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHL64F1CLQ", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHL32F1CLQ", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHL64F1CLL", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHL32F1CLL", SwitchTo_S12Z);
  (void)AddCPU("S912ZVFP64F1CLQ", SwitchTo_S12Z);
  (void)AddCPU("S912ZVFP64F1CLL", SwitchTo_S12Z);
  (void)AddCPU("S912ZVH128F2VLQ", SwitchTo_S12Z);
  (void)AddCPU("S912ZVH128F2VLL", SwitchTo_S12Z);
  (void)AddCPU("S912ZVH64F2VLQ" , SwitchTo_S12Z);
  (void)AddCPU("S912ZVHY64F1VLQ", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHY32F1VLQ", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHY64F1VL" , SwitchTo_S12Z);
  (void)AddCPU("S912ZVHY32F1VLL", SwitchTo_S12Z);
  (void)AddCPU("S912ZVHL64F1VLQ", SwitchTo_S12Z);
}
