/* codemcore.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator MCORE-Familie                                               */
/*                                                                           */
/*****************************************************************************/

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

#include "nls.h"
#include "be_le.h"
#include "strutil.h"
#include "bpemu.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmallg.h"
#include "onoff_common.h"
#include "codepseudo.h"
#include "motpseudo.h"
#include "asmitree.h"
#include "codevars.h"
#include "errmsg.h"
#include "headids.h"

#include "codemcore.h"

/*--------------------------------------------------------------------------*/
/* Variablen */

#define REG_SP 0
#define REG_LR 15

typedef struct
{
  Word Code;
  Boolean Priv;
} FixedOrder;

typedef struct
{
  Word Code;
  Word Min,Ofs;
} ImmOrder;

typedef struct
{
  const char *Name;
  Word Code;
} CReg;

static CPUVar CPUMCORE;
static tSymbolSize OpSize;

static FixedOrder *FixedOrders;
static FixedOrder *OneRegOrders;
static FixedOrder *TwoRegOrders;
static ImmOrder *UImm5Orders;
static FixedOrder *LJmpOrders;
static CReg *CRegs;

/*--------------------------------------------------------------------------*/
/* Hilfsdekoder */

static const Word AllRegMask = 0xffff;

/*!------------------------------------------------------------------------
 * \fn     DecodeRegCore(const char *pArg, Word *pResult)
 * \brief  check whether argument is a register
 * \param  pArg argument
 * \param  pResult register number if it is
 * \return True if it is
 * ------------------------------------------------------------------------ */

static Boolean DecodeRegCore(const char *pArg, Word *pResult)
{
  if (!as_strcasecmp(pArg, "SP"))
    *pResult = REGSYM_FLAG_ALIAS | REG_SP;
  else if (!as_strcasecmp(pArg, "LR"))
    *pResult = REGSYM_FLAG_ALIAS | REG_LR;
  else if (as_toupper(*pArg) != 'R')
    return False;
  else
  {
    char *endptr;

    *pResult = strtol(pArg + 1, &endptr, 10);
    if (*endptr || (*pResult > 15))
      return False;
  }
  return True;
}

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

static void DissectReg_MCORE(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
{
  switch (InpSize)
  {
    case eSymbolSize32Bit:
      switch (Value)
      {
        case REGSYM_FLAG_ALIAS | REG_SP:
          as_snprintf(pDest, DestSize, "SP");
          break;
        case REGSYM_FLAG_ALIAS | REG_LR:
          as_snprintf(pDest, DestSize, "LR");
          break;
        default:
          as_snprintf(pDest, DestSize, "R%u", (unsigned)Value);
      }
      break;
    default:
      as_snprintf(pDest, DestSize, "%d-%u", (int)InpSize, (unsigned)Value);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeReg(const tStrComp *pArg, Word *pResult, Word Mask, Boolean MustBeReg)
 * \brief  check whether argument is a CPU register or register alias
 * \param  pArg argument
 * \param  pResult register number if it is
 * \param  Mask bit mask of allowed registers
 * \param  MustBeReg operand is expected to be a register
 * \return True if it is an allowed register
 * ------------------------------------------------------------------------ */

static tRegEvalResult DecodeReg(const tStrComp *pArg, Word *pResult, Word Mask, Boolean MustBeReg)
{
  tRegEvalResult RegEvalResult;

  if (DecodeRegCore(pArg->str.p_str, pResult))
    RegEvalResult = eIsReg;
  else
  {
    tRegDescr RegDescr;
    tEvalResult EvalResult;

    RegEvalResult = EvalStrRegExpressionAsOperand(pArg, &RegDescr, &EvalResult, eSymbolSize32Bit, MustBeReg);
    *pResult = RegDescr.Reg;
  }
  *pResult &= ~REGSYM_FLAG_ALIAS;
  if ((RegEvalResult == eIsReg) && !(Mask & (1 << *pResult)))
  {
    RegEvalResult = MustBeReg ? eRegAbort : eIsNoReg;
    WrStrErrorPos(ErrNum_InvReg, pArg);
  }
  return RegEvalResult;
}

static Boolean DecodeArgReg(int Index, Word *pErg, Word Mask)
{
  return (DecodeReg(&ArgStr[Index], pErg, Mask, True) == eIsReg);
}

static Boolean DecodeArgIReg(int Index, Word *pErg, Word Mask)
{
  tStrComp RegComp;
  const char *pArg = ArgStr[Index].str.p_str;
  int l = strlen(pArg);

  if ((l <= 3) || (pArg[0] != '(') || (pArg[l - 1] != ')'))
  {
    WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[Index]);
    return False;
  }
  StrCompRefRight(&RegComp, &ArgStr[Index], 1);
  StrCompShorten(&RegComp, 1);
  return (DecodeReg(&RegComp, pErg, Mask, True) == eIsReg);
}

static Boolean DecodeArgRegPair(int Index, Word *pFrom, Word FromMask, Word *pTo, Word ToMask)
{
  tStrComp FromComp, ToComp;
  char *pSep = strchr(ArgStr[Index].str.p_str, '-');

  if (!pSep)
  {
    WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[Index]);
    return False;
  }

  StrCompSplitRef(&FromComp, &ToComp, &ArgStr[Index], pSep);
  return (DecodeReg(&FromComp, pFrom, FromMask, True) == eIsReg)
      && (DecodeReg(&ToComp, pTo, ToMask, True) == eIsReg);
}

static Boolean DecodeCReg(char *Asc, Word *Erg)
{
  char *endptr;
  int z;

  for (z = 0; CRegs[z].Name; z++)
    if (!as_strcasecmp(Asc, CRegs[z].Name))
    {
      *Erg = CRegs[z].Code;
      return True;
    }

  if ((as_toupper(*Asc) != 'C') || (as_toupper(Asc[1]) != 'R'))
    return False;
  else
  {
    *Erg = strtol(Asc + 2, &endptr, 10);
    return ((*endptr == '\0') && (*Erg <= 31));
  }
}

static Boolean DecodeArgCReg(int Index, Word *pErg)
{
  Boolean Result = DecodeCReg(ArgStr[Index].str.p_str, pErg);

  if (!Result)
    WrStrErrorPos(ErrNum_InvCtrlReg, &ArgStr[Index]);
  return Result;
}

static Boolean DecodeAdr(const tStrComp *pArg, Word *Erg)
{
  Word Base = 0xff, Tmp;
  LongInt DispAcc = 0, DMask = (1 << OpSize) - 1, DMax = 15 << OpSize;
  Boolean OK, FirstFlag = False;
  tSymbolFlags Flags;
  char *Pos;
  tStrComp Arg, Remainder;

  if (!IsIndirect(pArg->str.p_str))
  {
    WrError(ErrNum_InvAddrMode);
    return False;
  }

  StrCompRefRight(&Arg,pArg, 1);
  StrCompShorten(&Arg, 1);
  do
  {
    Pos = QuotPos(Arg.str.p_str,',');
    if (Pos)
      StrCompSplitRef(&Arg, &Remainder, &Arg, Pos);
    switch (DecodeReg(&Arg, &Tmp, AllRegMask, False))
    {
      case eIsReg:
        if (Base == 0xff) Base = Tmp;
        else
        {
          WrError(ErrNum_InvAddrMode);
          return False;
        }
        break;
      case eIsNoReg:
        DispAcc += EvalStrIntExpressionWithFlags(&Arg, Int32, &OK, &Flags);
        if (mFirstPassUnknown(Flags)) FirstFlag = True;
        if (!OK)
          return False;
        break;
      default:
        return False;
    }
    if (Pos)
      Arg = Remainder;
  }
  while (Pos);

  if (Base == 0xff)
  {
    WrError(ErrNum_InvAddrMode);
    return False;
  }

  if (FirstFlag)
  {
    DispAcc -= DispAcc & DMask;
    if (DispAcc < 0) DispAcc = 0;
    if (DispAcc > DMax) DispAcc = DMax;
  }

  if ((DispAcc & DMask) != 0)
  {
    WrError(ErrNum_NotAligned);
    return False;
  }
  if (!ChkRange(DispAcc, 0, DMax))
    return False;
  *Erg = Base + ((DispAcc >> OpSize) << 4);
  return True;
}

static void DecodeFixed(Word Index)
{
  FixedOrder *Instr = FixedOrders + Index;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(0, 0))
  {
    if ((Instr->Priv) && (!SupAllowed)) WrError(ErrNum_PrivOrder);
    WAsmCode[0] = Instr->Code;
    CodeLen = 2;
  }
}

static void DecodeOneReg(Word Index)
{
  FixedOrder *Instr = OneRegOrders + Index;
  Word RegX;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(1, 1) && DecodeArgReg(1, &RegX, AllRegMask))
  {
    if ((Instr->Priv) && (!SupAllowed)) WrError(ErrNum_PrivOrder);
    WAsmCode[0] = Instr->Code + RegX;
    CodeLen = 2;
  }
}

static void DecodeTwoReg(Word Index)
{
  FixedOrder *Instr = TwoRegOrders + Index;
  Word RegX, RegY;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2)
        && DecodeArgReg(1, &RegX, AllRegMask)
        && DecodeArgReg(2, &RegY, AllRegMask))
  {
    if ((Instr->Priv) && (!SupAllowed)) WrError(ErrNum_PrivOrder);
    WAsmCode[0] = Instr->Code + (RegY << 4) + RegX;
    CodeLen = 2;
  }
}

static void DecodeUImm5(Word Index)
{
  ImmOrder *Instr = UImm5Orders + Index;
  Word RegX, ImmV;
  Boolean OK;
  tSymbolFlags Flags;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2) && DecodeArgReg(1, &RegX, AllRegMask))
  {
    ImmV = EvalStrIntExpressionWithFlags(&ArgStr[2], (Instr->Ofs > 0) ? UInt6 : UInt5, &OK, &Flags);
    if ((Instr->Min > 0) && (ImmV < Instr->Min))
    {
      if (mFirstPassUnknown(Flags)) ImmV = Instr->Min;
      else
      {
        WrError(ErrNum_UnderRange); OK = False;
      }
    }
    if ((Instr->Ofs > 0) && ((ImmV < Instr->Ofs) || (ImmV > 31 + Instr->Ofs)))
    {
      if (mFirstPassUnknown(Flags)) ImmV = Instr->Ofs;
      else
      {
        WrError((ImmV < Instr->Ofs) ? ErrNum_UnderRange : ErrNum_OverRange);
        OK = False;
      }
    }
    if (OK)
    {
      WAsmCode[0] = Instr->Code + ((ImmV - Instr->Ofs) << 4) + RegX;
      CodeLen = 2;
    }
  }
}

static void DecodeLJmp(Word Index)
{
  FixedOrder *Instr = LJmpOrders + Index;
  LongInt Dest;
  Boolean OK;
  tSymbolFlags Flags;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(1, 1))
  {
    Dest = EvalStrIntExpressionWithFlags(&ArgStr[1], UInt32, &OK, &Flags) - (EProgCounter() + 2);
    if (OK)
    {
      if (!mSymbolQuestionable(Flags) && (Dest & 1)) WrError(ErrNum_DistIsOdd);
      else if (!mSymbolQuestionable(Flags) && ((Dest > 2046) || (Dest < -2048))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        if ((Instr->Priv) && (!SupAllowed)) WrError(ErrNum_PrivOrder);
        WAsmCode[0] = Instr->Code + ((Dest >> 1) & 0x7ff);
        CodeLen = 2;
      }
    }
  }
}

static void DecodeSJmp(Word Index)
{
  LongInt Dest;
  Boolean OK;
  tSymbolFlags Flags;
  int l = 0;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (!ChkArgCnt(1, 1));
  else if ((*ArgStr[1].str.p_str != '[') || (ArgStr[1].str.p_str[l = strlen(ArgStr[1].str.p_str) - 1] != ']')) WrError(ErrNum_InvAddrMode);
  else
  {
    ArgStr[1].str.p_str[l] = '\0';
    Dest = EvalStrIntExpressionOffsWithFlags(&ArgStr[1], 1, UInt32, &OK, &Flags);
    if (OK)
    {
      if (!mSymbolQuestionable(Flags) && (Dest & 3)) WrError(ErrNum_NotAligned);
      else
      {
        Dest = (Dest - (EProgCounter() + 2)) >> 2;
        if ((EProgCounter() & 3) < 2) Dest++;
        if (!mSymbolQuestionable(Flags) && ((Dest < 0) || (Dest > 255))) WrError(ErrNum_JmpDistTooBig);
        else
        {
          WAsmCode[0] = 0x7000 + (Index << 8) + (Dest & 0xff);
          CodeLen = 2;
        }
      }
    }
  }
}

static void DecodeBGENI(Word Index)
{
  Word RegX, ImmV;
  Boolean OK;
  UNUSED(Index);

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2) && DecodeArgReg(1, &RegX, AllRegMask))
  {
    ImmV = EvalStrIntExpression(&ArgStr[2], UInt5, &OK);
    if (OK)
    {
      if (ImmV > 6)
        WAsmCode[0] = 0x3200 + (ImmV << 4) + RegX;
      else
        WAsmCode[0] = 0x6000 + (1 << (4 + ImmV)) + RegX;
      CodeLen = 2;
    }
  }
}

static void DecodeBMASKI(Word Index)
{
  Word RegX, ImmV;
  Boolean OK;
  tSymbolFlags Flags;

  UNUSED(Index);

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2) && DecodeArgReg(1, &RegX, AllRegMask))
  {
    ImmV = EvalStrIntExpressionWithFlags(&ArgStr[2], UInt6, &OK, &Flags);
    if (mFirstPassUnknown(Flags) && ((ImmV < 1) || (ImmV > 32))) ImmV = 8;
    if (OK)
    {
      if (ChkRange(ImmV, 1, 32))
      {
        ImmV &= 31;
        if ((ImmV < 1) || (ImmV > 7))
          WAsmCode[0] = 0x2c00 + (ImmV << 4) + RegX;
        else
          WAsmCode[0] = 0x6000 + (((1 << ImmV) - 1) << 4) + RegX;
        CodeLen = 2;
      }
    }
  }
}

static void DecodeLdSt(Word Index)
{
  Word RegX, RegZ, NSize;

  if (*AttrPart.str.p_str && (Lo(Index) != 0xff)) WrError(ErrNum_UseLessAttr);
  else if (!ChkArgCnt(2, 2));
  else if (OpSize > eSymbolSize32Bit) WrError(ErrNum_InvOpSize);
  else
  {
    if (Lo(Index) != 0xff) OpSize = (tSymbolSize)Lo(Index);
    if (DecodeArgReg(1, &RegZ, AllRegMask) && DecodeAdr(&ArgStr[2], &RegX))
    {
      NSize = (OpSize == eSymbolSize32Bit) ? 0 : OpSize + 1;
      WAsmCode[0] = 0x8000 + (NSize << 13) + (Hi(Index) << 12) + (RegZ << 8) + RegX;
      CodeLen = 2;
    }
  }
}

static void DecodeLdStm(Word Index)
{
  Word RegF, RegL, RegI;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2)
        && DecodeArgIReg(2, &RegI, 0x0001)
        && DecodeArgRegPair(1, &RegF, 0x7ffe, &RegL, 0x8000))
  {
    WAsmCode[0] = 0x0060 + (Index << 4) + RegF;
    CodeLen = 2;
  }
}

static void DecodeLdStq(Word Index)
{
  Word RegF, RegL, RegX;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2)
        && DecodeArgIReg(2, &RegX, 0xff0f)
        && DecodeArgRegPair(1, &RegF, 0x0010, &RegL, 0x0080))
  {
    WAsmCode[0] = 0x0040 + (Index << 4) + RegX;
    CodeLen = 2;
  }
}

static void DecodeLoopt(Word Index)
{
  Word RegY;
  LongInt Dest;
  Boolean OK;
  tSymbolFlags Flags;
  UNUSED(Index);

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2) && DecodeArgReg(1, &RegY, AllRegMask))
  {
    Dest = EvalStrIntExpressionWithFlags(&ArgStr[2], UInt32, &OK, &Flags) - (EProgCounter() + 2);
    if (OK)
    {
      if (!mSymbolQuestionable(Flags) && (Dest & 1)) WrError(ErrNum_DistIsOdd);
      else if (!mSymbolQuestionable(Flags) && ((Dest > -2) || (Dest <- 32))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        WAsmCode[0] = 0x0400 + (RegY << 4) + ((Dest >> 1) & 15);
        CodeLen = 2;
      }
    }
  }
}

static void DecodeLrm(Word Index)
{
  LongInt Dest;
  Word RegZ;
  Boolean OK;
  tSymbolFlags Flags;
  int l = 0;
  UNUSED(Index);

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (!ChkArgCnt(2, 2));
  else if (!DecodeArgReg(1, &RegZ, 0x7ffe));
  else if ((*ArgStr[2].str.p_str != '[') || (ArgStr[2].str.p_str[l = strlen(ArgStr[2].str.p_str) - 1] != ']')) WrError(ErrNum_InvAddrMode);
  else
  {
    ArgStr[2].str.p_str[l] = '\0';
    Dest = EvalStrIntExpressionOffsWithFlags(&ArgStr[2], 1, UInt32, &OK, &Flags);
    if (OK)
    {
      if (!mSymbolQuestionable(Flags) && (Dest & 3)) WrError(ErrNum_NotAligned);
      else
      {
        Dest = (Dest - (EProgCounter() + 2)) >> 2;
        if ((EProgCounter() & 3) < 2) Dest++;
        if (!mSymbolQuestionable(Flags) && ((Dest < 0) || (Dest > 255))) WrError(ErrNum_JmpDistTooBig);
        else
        {
          WAsmCode[0] = 0x7000 + (RegZ << 8) + (Dest & 0xff);
          CodeLen = 2;
        }
      }
    }
  }
}

static void DecodeMcr(Word Index)
{
  Word RegX,CRegY;

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2)
        && DecodeArgReg(1, &RegX, AllRegMask)
        && DecodeArgCReg(2, &CRegY))
  {
    if (!SupAllowed) WrError(ErrNum_PrivOrder);
    WAsmCode[0] = 0x1000 + (Index << 11) + (CRegY << 4) + RegX;
    CodeLen = 2;
  }
}

static void DecodeMovi(Word Index)
{
  Word RegX, ImmV;
  Boolean OK;
  UNUSED(Index);

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (ChkArgCnt(2, 2) && DecodeArgReg(1, &RegX, AllRegMask))
  {
    ImmV = EvalStrIntExpression(&ArgStr[2], UInt7, &OK);
    if (OK)
    {
      WAsmCode[0] = 0x6000 + ((ImmV & 127) << 4) + RegX;
      CodeLen = 2;
    }
  }
}

static void DecodeTrap(Word Index)
{
  Word ImmV;
  Boolean OK;
  UNUSED(Index);

  if (*AttrPart.str.p_str) WrError(ErrNum_UseLessAttr);
  else if (!ChkArgCnt(1, 1));
  else if (*ArgStr[1].str.p_str != '#') WrError(ErrNum_OnlyImmAddr);
  else
  {
    ImmV = EvalStrIntExpressionOffs(&ArgStr[1], 1, UInt2, &OK);
    if (OK)
    {
      WAsmCode[0] = 0x0008 + ImmV;
      CodeLen = 2;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     check_pc_even(Word index)
 * \brief  assure machine instruction  is at even address
 * ------------------------------------------------------------------------ */

static void check_pc_even(Word index)
{
  UNUSED(index);
  if (Odd(EProgCounter())) WrError(ErrNum_AddrNotAligned);
}

/*--------------------------------------------------------------------------*/
/* Codetabellenverwaltung */

static void AddFixed(const char *NName, Word NCode, Boolean NPriv)
{
  order_array_rsv_end(FixedOrders, FixedOrder);
  FixedOrders[InstrZ].Code = NCode;
  FixedOrders[InstrZ].Priv = NPriv;
  AddInstTable(InstTable, NName, InstrZ++, DecodeFixed);
}

static void AddOneReg(const char *NName, Word NCode, Boolean NPriv)
{
  order_array_rsv_end(OneRegOrders, FixedOrder);
  OneRegOrders[InstrZ].Code = NCode;
  OneRegOrders[InstrZ].Priv = NPriv;
  AddInstTable(InstTable, NName, InstrZ++, DecodeOneReg);
}

static void AddTwoReg(const char *NName, Word NCode, Boolean NPriv)
{
  order_array_rsv_end(TwoRegOrders, FixedOrder);
  TwoRegOrders[InstrZ].Code = NCode;
  TwoRegOrders[InstrZ].Priv = NPriv;
  AddInstTable(InstTable, NName, InstrZ++, DecodeTwoReg);
}

static void AddUImm5(const char *NName, Word NCode, Word NMin, Word NOfs)
{
  order_array_rsv_end(UImm5Orders, ImmOrder);
  UImm5Orders[InstrZ].Code = NCode;
  UImm5Orders[InstrZ].Min = NMin;
  UImm5Orders[InstrZ].Ofs = NOfs;
  AddInstTable(InstTable, NName, InstrZ++, DecodeUImm5);
}

static void AddLJmp(const char *NName, Word NCode, Boolean NPriv)
{
  order_array_rsv_end(LJmpOrders, FixedOrder);
  LJmpOrders[InstrZ].Code = NCode;
  LJmpOrders[InstrZ].Priv = NPriv;
  AddInstTable(InstTable, NName, InstrZ++, DecodeLJmp);
}

static void AddCReg(const char *NName, Word NCode)
{
  order_array_rsv_end(CRegs, CReg);
  CRegs[InstrZ].Name = NName;
  CRegs[InstrZ++].Code = NCode;
}

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

  AddInstTable(InstTable, "REG", 0, CodeREG);
  AddMoto16Pseudo(InstTable, e_moto_pseudo_flags_be);

  inst_table_set_prefix_proc(InstTable, check_pc_even, 0);

  InstrZ = 0;
  AddFixed("BKPT" , 0x0000, False);
  AddFixed("DOZE" , 0x0006, True );
  AddFixed("RFI"  , 0x0003, True );
  AddFixed("RTE"  , 0x0002, True );
  AddFixed("STOP" , 0x0004, True );
  AddFixed("SYNC" , 0x0001, False);
  AddFixed("WAIT" , 0x0005, True );

  InstrZ = 0;
  AddOneReg("ABS"   , 0x01e0, False);  AddOneReg("ASRC" , 0x3a00, False);
  AddOneReg("BREV"  , 0x00f0, False);  AddOneReg("CLRF" , 0x01d0, False);
  AddOneReg("CLRT"  , 0x01c0, False);  AddOneReg("DECF" , 0x0090, False);
  AddOneReg("DECGT" , 0x01a0, False);  AddOneReg("DECLT", 0x0180, False);
  AddOneReg("DECNE" , 0x01b0, False);  AddOneReg("DECT" , 0x0080, False);
  AddOneReg("DIVS"  , 0x3210, False);  AddOneReg("DIVU" , 0x2c10, False);
  AddOneReg("FF1"   , 0x00e0, False);  AddOneReg("INCF" , 0x00b0, False);
  AddOneReg("INCT"  , 0x00a0, False);  AddOneReg("JMP"  , 0x00c0, False);
  AddOneReg("JSR"   , 0x00d0, False);  AddOneReg("LSLC" , 0x3c00, False);
  AddOneReg("LSRC"  , 0x3e00, False);  AddOneReg("MVC"  , 0x0020, False);
  AddOneReg("MVCV"  , 0x0030, False);  AddOneReg("NOT"  , 0x01f0, False);
  AddOneReg("SEXTB" , 0x0150, False);  AddOneReg("SEXTH", 0x0170, False);
  AddOneReg("TSTNBZ", 0x0190, False);  AddOneReg("XSR"  , 0x3800, False);
  AddOneReg("XTRB0" , 0x0130, False);  AddOneReg("XTRB1", 0x0120, False);
  AddOneReg("XTRB2" , 0x0110, False);  AddOneReg("XTRB3", 0x0100, False);
  AddOneReg("ZEXTB" , 0x0140, False);  AddOneReg("ZEXTH", 0x0160, False);

  InstrZ = 0;
  AddTwoReg("ADDC" , 0x0600, False);  AddTwoReg("ADDU" , 0x1c00, False);
  AddTwoReg("AND"  , 0x1600, False);  AddTwoReg("ANDN" , 0x1f00, False);
  AddTwoReg("ASR"  , 0x1a00, False);  AddTwoReg("BGENR", 0x1300, False);
  AddTwoReg("CMPHS", 0x0c00, False);  AddTwoReg("CMPLT", 0x0d00, False);
  AddTwoReg("CMPNE", 0x0f00, False);  AddTwoReg("IXH"  , 0x1d00, False);
  AddTwoReg("IXW"  , 0x1500, False);  AddTwoReg("LSL"  , 0x1b00, False);
  AddTwoReg("LSR"  , 0x0b00, False);  AddTwoReg("MOV"  , 0x1200, False);
  AddTwoReg("MOVF" , 0x0a00, False);  AddTwoReg("MOVT" , 0x0200, False);
  AddTwoReg("MULT" , 0x0300, False);  AddTwoReg("OR"   , 0x1e00, False);
  AddTwoReg("RSUB" , 0x1400, False);  AddTwoReg("SUBC" , 0x0700, False);
  AddTwoReg("SUBU" , 0x0500, False);  AddTwoReg("TST"  , 0x0e00, False);
  AddTwoReg("XOR"  , 0x1700, False);

  InstrZ = 0;
  AddUImm5("ADDI"  , 0x2000, 0, 1);  AddUImm5("ANDI"  , 0x2e00, 0, 0);
  AddUImm5("ASRI"  , 0x3a00, 1, 0);  AddUImm5("BCLRI" , 0x3000, 0, 0);
  AddUImm5("BSETI" , 0x3400, 0, 0);  AddUImm5("BTSTI" , 0x3600, 0, 0);
  AddUImm5("CMPLTI", 0x2200, 0, 1);  AddUImm5("CMPNEI", 0x2a00, 0, 0);
  AddUImm5("LSLI"  , 0x3c00, 1, 0);  AddUImm5("LSRI"  , 0x3e00, 1, 0);
  AddUImm5("ROTLI" , 0x3800, 1, 0);  AddUImm5("RSUBI" , 0x2800, 0, 0);
  AddUImm5("SUBI"  , 0x2400, 0, 1);

  InstrZ = 0;
  AddLJmp("BF"   , 0xe800, False);  AddLJmp("BR"   , 0xf000, False);
  AddLJmp("BSR"  , 0xf800, False);  AddLJmp("BT"   , 0xe000, False);

  InstrZ = 0;
  AddCReg("PSR" , 0); AddCReg("VBR" , 1);
  AddCReg("EPSR", 2); AddCReg("FPSR", 3);
  AddCReg("EPC" , 4); AddCReg("FPC",  5);
  AddCReg("SS0",  6); AddCReg("SS1",  7);
  AddCReg("SS2",  8); AddCReg("SS3",  9);
  AddCReg("SS4", 10); AddCReg("GCR", 11);
  AddCReg("GSR", 12); AddCReg(NULL , 0);

  AddInstTable(InstTable, "BGENI" , 0, DecodeBGENI);
  AddInstTable(InstTable, "BMASKI", 0, DecodeBMASKI);
  AddInstTable(InstTable, "JMPI"  , 0, DecodeSJmp);
  AddInstTable(InstTable, "JSRI"  , 0, DecodeSJmp);
  AddInstTable(InstTable, "LD"    , 0x0ff, DecodeLdSt);
  AddInstTable(InstTable, "LDB"   , 0x000, DecodeLdSt);
  AddInstTable(InstTable, "LDH"   , 0x001, DecodeLdSt);
  AddInstTable(InstTable, "LDW"   , 0x002, DecodeLdSt);
  AddInstTable(InstTable, "ST"    , 0x1ff, DecodeLdSt);
  AddInstTable(InstTable, "STB"   , 0x100, DecodeLdSt);
  AddInstTable(InstTable, "STH"   , 0x101, DecodeLdSt);
  AddInstTable(InstTable, "STW"   , 0x102, DecodeLdSt);
  AddInstTable(InstTable, "LDM"   , 0, DecodeLdStm);
  AddInstTable(InstTable, "STM"   , 1, DecodeLdStm);
  AddInstTable(InstTable, "LDQ"   , 0, DecodeLdStq);
  AddInstTable(InstTable, "STQ"   , 1, DecodeLdStq);
  AddInstTable(InstTable, "LOOPT" , 0, DecodeLoopt);
  AddInstTable(InstTable, "LRM"   , 0, DecodeLrm);
  AddInstTable(InstTable, "MFCR"  , 0, DecodeMcr);
  AddInstTable(InstTable, "MTCR"  , 1, DecodeMcr);
  AddInstTable(InstTable, "MOVI"  , 0, DecodeMovi);
  AddInstTable(InstTable, "TRAP"  , 0, DecodeTrap);
}

static void DeinitFields(void)
{
  DestroyInstTable(InstTable);
  order_array_free(FixedOrders);
  order_array_free(OneRegOrders);
  order_array_free(TwoRegOrders);
  order_array_free(UImm5Orders);
  order_array_free(LJmpOrders);
  order_array_free(CRegs);
}

/*--------------------------------------------------------------------------*/
/* Callbacks */

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

static void InternSymbol_MCORE(char *pArg, TempResult *pResult)
{
  Word RegNum;

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

static Boolean DecodeAttrPart_MCORE(void)
{
  if (strlen(AttrPart.str.p_str) > 1)
  {
    WrStrErrorPos(ErrNum_UndefAttr, &AttrPart);
    return False;
  }
  /* operand size identifiers slightly differ from '68K Standard': */

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

static void MakeCode_MCORE(void)
{
  if (AttrPartOpSize[0] == eSymbolSizeUnknown)
    AttrPartOpSize[0] = eSymbolSize32Bit;
  OpSize = AttrPartOpSize[0];

  /* Nullanweisung */

  if ((*OpPart.str.p_str == '\0') && !*AttrPart.str.p_str && (ArgCnt == 0)) return;

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

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

static void SwitchTo_MCORE(void)
{
  const TFamilyDescr *p_descr = FindFamilyByName("M-CORE");

  TurnWords = True;
  SetIntConstMode(eIntConstModeMoto);

  PCSymbol = "*";
  HeaderID = p_descr->Id;
  NOPCode = 0x1200; /* ==MOV r0,r0 */
  DivideChars = ",";
  HasAttrs = True;
  AttrChars = ".";

  ValidSegs = (1 << SegCode);
  Grans[SegCode] = 1; ListGrans[SegCode] = 2; SegInits[SegCode] = 0;
  SegLimits[SegCode] = (LargeWord)IntTypeDefs[UInt32].Max;

  DecodeAttrPart = DecodeAttrPart_MCORE;
  MakeCode = MakeCode_MCORE;
  IsDef = IsDef_MCORE;
  InternSymbol = InternSymbol_MCORE;
  DissectReg = DissectReg_MCORE;

  SwitchFrom = DeinitFields; InitFields();
  onoff_supmode_add();
  AddMoto16PseudoONOFF(True);
}

/*--------------------------------------------------------------------------*/
/* Initialisierung */

void codemcore_init(void)
{
  CPUMCORE = AddCPU("MCORE", SwitchTo_MCORE);
}
