/* codeavr.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator Atmel AVR                                                   */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"

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

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

#include "codeavr.h"

#define RegBankSize 32
#define IOAreaStdSize 64
#define IOAreaExtSize (IOAreaStdSize + 160)
#define IOAreaExt2Size (IOAreaExtSize + 256)

#define BitFlag_Data 0x800000ul
#define BitFlag_IO 0x400000ul

typedef enum
{
  eCoreNone,
  eCoreMinTiny,
  eCore90S1200,
  eCoreClassic, /* AT90Sxxxx */
  eCoreTiny, /* ATtiny up to 8KB flash */
  eCoreTiny16K,
  eCoreMega
} tCPUCore;

#define MinCoreMask(c) ((Word)(0xffffu << (c)))

typedef struct
{
  Word Code;
  Word CoreMask;
} FixedOrder;

/* Data types are a bit squeezed to make struct fit into fewer bytes:
   lower four bits of FlashEnd are always 0xf and are not stored: */

typedef struct
{
  const char *pName;
  Word FlashEndD16, RAMSize, EESize, IOAreaSize;
  Boolean RegistersMapped;
  Byte Core;
} tCPUProps;

static FixedOrder *FixedOrders, *Reg1Orders, *Reg2Orders;

static Boolean WrapFlag;
static LongInt ORMask, SignMask, CodeSegSize;
static const tCPUProps *pCurrCPUProps;

static IntType CodeAdrIntType,
               DataAdrIntType;

static const char WrapFlagName[] = "WRAPMODE";

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

static LongInt CutAdr(LongInt Adr)
{
  if ((Adr & SignMask) != 0)
    return (Adr | ORMask);
  else
    return (Adr & SegLimits[SegCode]);
}

static Boolean ChkMinCore(tCPUCore MinCore)
{
  if (pCurrCPUProps->Core < MinCore)
  {
    WrError(ErrNum_InstructionNotSupported);
    return False;
  }
  return True;
}

static Boolean ChkCoreMask(Word CoreMask)
{
  if ((1 << pCurrCPUProps->Core) & CoreMask)
    return True;
  WrError(ErrNum_InstructionNotSupported);
  return False;
}

static void DissectBit_AVR(char *pDest, size_t DestSize, LargeWord Inp)
{
  LongWord BitSpec = Inp;

  as_snprintf(pDest, DestSize, "0x%0*x(%c).%d",
              (BitSpec & BitFlag_IO) ? 2 : 3,
              (unsigned)((BitSpec >> 3) & 0xffff),
              (BitSpec & BitFlag_Data) ? SegShorts[SegData]
                                   : ((BitSpec & BitFlag_IO) ? SegShorts[SegIO] : SegShorts[SegNone]),
              (int)(BitSpec & 7));
}

/*---------------------------------------------------------------------------*/
/* Argument Decoders                                                         */

/*!------------------------------------------------------------------------
 * \fn     DecodeRegCore(const char *pArg, Word *pResult)
 * \brief  check wether argument is CPU register
 * \param  pArg source code argument
 * \param  pResult register # if it's a register
 * \return True if it's a register
 * ------------------------------------------------------------------------ */

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

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

  *pResult = strtoul(pArg + 1, &p_end, 10);
  return (!*p_end
       && ((*pResult >= 16) || (pCurrCPUProps->Core != eCoreMinTiny))
       && (*pResult < 32));
}

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

static void DissectReg_AVR(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
{
  switch (InpSize)
  {
    case eSymbolSize8Bit:
      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)
 * \brief  check wether argument is CPU register, including register aliases
 * \param  pArg source code argument
 * \param  pResult register # if it's a register
 * \return True if it's a register
 * ------------------------------------------------------------------------ */

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

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

  RegEvalResult = EvalStrRegExpressionAsOperand(pArg, &RegDescr, &EvalResult, eSymbolSize8Bit, True);
  *pResult = RegDescr.Reg;
  return (RegEvalResult == eIsReg);
}

static Boolean DecodeMem(char * Asc, Word *Erg)
{
  if (as_strcasecmp(Asc, "X") == 0) *Erg = 0x1c;
  else if (as_strcasecmp(Asc, "X+") == 0) *Erg = 0x1d;
  else if (as_strcasecmp(Asc, "-X") == 0) *Erg = 0x1e;
  else if (as_strcasecmp(Asc, "Y" ) == 0) *Erg = 0x08;
  else if (as_strcasecmp(Asc, "Y+") == 0) *Erg = 0x19;
  else if (as_strcasecmp(Asc, "-Y") == 0) *Erg = 0x1a;
  else if (as_strcasecmp(Asc, "Z" ) == 0) *Erg = 0x00;
  else if (as_strcasecmp(Asc, "Z+") == 0) *Erg = 0x11;
  else if (as_strcasecmp(Asc, "-Z") == 0) *Erg = 0x12;
  else return False;
  return True;
}

static Boolean DecodeBitArg2(const tStrComp *pRegArg, const tStrComp *pBitArg, LongWord *pResult, tSymbolFlags *p_flags)
{
  tEvalResult EvalResult;
  LongWord Addr;

  *p_flags = eSymbolFlag_None;
  *pResult = EvalStrIntExpressionWithResult(pBitArg, UInt3, &EvalResult);
  if (!EvalResult.OK)
    return False;
  *p_flags |= EvalResult.Flags;

  Addr = EvalStrIntExpressionWithResult(pRegArg, DataAdrIntType, &EvalResult);
  if (!EvalResult.OK)
    return False;
  *p_flags |= EvalResult.Flags;

  if (EvalResult.AddrSpaceMask & (1 << SegIO))
  {
    if (!mFirstPassUnknown(EvalResult.Flags) && !ChkRange(Addr, 0, IOAreaStdSize - 1))
      return False;
    *pResult |= BitFlag_IO | (Addr & 0x3f) << 3;
    return True;
  }
  else
  {
    ChkSpace(SegData, EvalResult.AddrSpaceMask);

    if (!mFirstPassUnknown(EvalResult.Flags) && !ChkRange(Addr, 0, SegLimits[SegData]))
      return False;
    *pResult |= ((EvalResult.AddrSpaceMask & (1 << SegData)) ? BitFlag_Data : 0) | (Addr & 0x1ff) << 3;
    return True;
  }
}

static Boolean DecodeBitArg(int Start, int Stop, LongWord *pResult, tSymbolFlags *p_flags)
{
  if (Start == Stop)
  {
    char *pPos = QuotPos(ArgStr[Start].str.p_str, '.');
    tEvalResult EvalResult;

    if (pPos)
    {
      tStrComp RegArg, BitArg;

      StrCompSplitRef(&RegArg, &BitArg, &ArgStr[Start], pPos);
      return DecodeBitArg2(&RegArg, &BitArg, pResult, p_flags);
    }
    *pResult = EvalStrIntExpressionWithResult(&ArgStr[Start], UInt16, &EvalResult);
    if (EvalResult.OK)
    {
      ChkSpace(SegBData, EvalResult.AddrSpaceMask);
      *p_flags = EvalResult.Flags;
    }
    return EvalResult.OK;
  }
  else if (Stop == Start + 1)
    return DecodeBitArg2(&ArgStr[Start], &ArgStr[Stop], pResult, p_flags);
  else
  {
    WrError(ErrNum_WrongArgCnt);
    return False;
  }
}

static const LongWord
       AllRegMask = 0xfffffffful,
       UpperHalfRegMask = 0xffff0000ul,
       Reg16_23Mask = 0x00ff0000ul,
       EvenRegMask = 0x55555555ul,
       UpperEightEvenRegMask = 0x55000000ul;

static Boolean DecodeArgReg(unsigned ArgIndex, Word *pReg, LongWord RegMask)
{
  Boolean Result;

  Result = DecodeReg(&ArgStr[ArgIndex], pReg);
  if (Result && !((RegMask >> *pReg) & 1))
  {
    WrStrErrorPos(ErrNum_InvReg, &ArgStr[ArgIndex]);
    return False;
  }
  return Result;
}

/*!------------------------------------------------------------------------
 * \fn     GetWordCodeAddress(tStrComp *pArg, tEvalResult *EvalResult)
 * \brief  decode argument as code address and divide by two if in byte mode
 * \param  pArg address argument
 * \param  pEvalResult returns OK/failure
 * \return (word) address
 * ------------------------------------------------------------------------ */

static LongInt GetWordCodeAddress(tStrComp *pArg, tEvalResult *pEvalResult)
{
  LongInt Result;

  Result = EvalStrIntExpressionWithResult(pArg, CodeAdrIntType, pEvalResult);
  if (pEvalResult->OK)
  {
    ChkSpace(SegCode, pEvalResult->AddrSpaceMask);
    if (!CodeSegSize)
    {
      if (mFirstPassUnknown(pEvalResult->Flags))
        Result &= ~1ul;
      if (Result & 1)
      {
        WrStrErrorPos(ErrNum_NotAligned, pArg);
        pEvalResult->OK = False;
      }
      else
        Result >>= 1;
    }
  }
  return Result;
}

/*!------------------------------------------------------------------------
 * \fn     GetNextCodeAddress(void)
 * \brief  retrieve (word) address of next instruction
 * \return (word) address
 * ------------------------------------------------------------------------ */

static LongInt GetNextCodeAddress(void)
{
  LongInt Result = EProgCounter();

  if (!CodeSegSize)
    Result >>= 1;
  return Result + 1;
}

/*---------------------------------------------------------------------------*/
/* Individual Decoders                                                       */

/* Pseudo Instructions */

static void DecodePORT(Word Index)
{
  UNUSED(Index);

  code_equate_type(SegIO, UInt6);
}

static void DecodeSFR(Word Index)
{
  LargeWord Start = (pCurrCPUProps->RegistersMapped ? RegBankSize : 0);
  UNUSED(Index);

  code_equate_range(SegData, Start, Start + pCurrCPUProps->IOAreaSize);
}

static void AppendCode(Word Code)
{
  if (CodeSegSize)
    WAsmCode[CodeLen++] = Code;
  else
  {
    BAsmCode[CodeLen++] = Lo(Code);
    BAsmCode[CodeLen++] = Hi(Code);
  }
}

static void append_code_with_guess_flags(Word code, tSymbolFlags flags, Word guess_mask)
{
  if (CodeSegSize)
    set_w_guessed(flags, CodeLen, 1, guess_mask);
  else
  {
    set_b_guessed(flags, CodeLen    , 1, Lo(guess_mask));
    set_b_guessed(flags, CodeLen + 1, 1, Hi(guess_mask));
  }
  AppendCode(code);
}

/* No Argument */

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

  if (ChkArgCnt(0, 0) && ChkCoreMask(pOrder->CoreMask))
    AppendCode(pOrder->Code);
}

static void DecodeRES(Word Index)
{
  Boolean OK;
  Integer Size;
  tSymbolFlags Flags;

  UNUSED(Index);

  Size = EvalStrIntExpressionWithFlags(&ArgStr[1], Int16, &OK, &Flags);
  if (mFirstPassUnknown(Flags)) WrError(ErrNum_FirstPassCalc);
  if (OK && !mFirstPassUnknown(Flags))
  {
    DontPrint = True;
    if (!Size) WrError(ErrNum_NullResMem);
    CodeLen = Size;
    BookKeeping();
  }
}

typedef struct
{
  Word data_acc, guess_acc;
  Boolean word_acc_full;
} value_ctx_t;

static void PlaceValue(Word Value, tSymbolFlags flags, Boolean IsByte, value_ctx_t *p_ctx)
{
  if (ActPC != SegCode)
  {
    set_b_guessed(flags, CodeLen, 1, 0xff);
    BAsmCode[CodeLen++] = Value;
    p_ctx->word_acc_full = False;
  }
  else if (IsByte)
  {
    if (CodeSegSize)
    {
      Word this_guess_value = mFirstPassUnknownOrQuestionable(flags) ? 0xff : 0x00;
      Value &= 0xff;
      if (p_ctx->word_acc_full)
      {
        set_wasmcode_guessed(CodeLen, 1, p_ctx->guess_acc |= (this_guess_value << 8));
        AppendCode(p_ctx->data_acc |= (Value << 8));
      }
      else
      {
        p_ctx->data_acc = Value;
        p_ctx->guess_acc = this_guess_value;
      }
      p_ctx->word_acc_full = !p_ctx->word_acc_full;
    }
    else
    {
      set_b_guessed(flags, CodeLen, 1, 0xff);
      BAsmCode[CodeLen++] = Value;
      p_ctx->word_acc_full = False;
    }
  }
  else
  {
    if (CodeSegSize)
      append_code_with_guess_flags(Value, flags, 0xffff);
    else
    {
      set_b_guessed(flags, CodeLen, 2, 0xff);
      BAsmCode[CodeLen++] = Lo(Value);
      BAsmCode[CodeLen++] = Hi(Value);
    }
    p_ctx->word_acc_full = False;
  }
}

static void DecodeDATA_AVR(Word Index)
{
  Integer Trans;
  TempResult t;
  LongInt MinV, MaxV;
  value_ctx_t ctx;

  UNUSED(Index);

  as_tempres_ini(&t);
  MaxV = ((ActPC == SegCode) && !Packing) ? 65535 : 255;
  MinV = (-((MaxV + 1) >> 1));
  ctx.data_acc = ctx.guess_acc = 0;
  ctx.word_acc_full = False;
  if (ChkArgCnt(1, ArgCntMax))
  {
    Boolean OK = True;
    const tStrComp *pArg;

    forallargs(pArg, OK)
    {
      EvalStrExpression(pArg, &t);
      if (mFirstPassUnknown(t.Flags) && (t.Typ == TempInt)) t.Contents.Int &= MaxV;
      switch (t.Typ)
      {
        case TempString:
        {
          int z2;

          if (MultiCharToInt(&t, 2))
            goto ToInt;

          if (as_chartrans_xlate_nonz_dynstr(CurrTransTable->p_table, &t.Contents.str, pArg))
            OK = False;
          else
            for (z2 = 0; z2 < (int)t.Contents.str.len; z2++)
            {
              Trans = ((usint) t.Contents.str.p_str[z2]) & 0xff;
              PlaceValue(Trans, t.Flags, True, &ctx);
            }
          break;
        }
        ToInt:
        case TempInt:
          if (ChkRange(t.Contents.Int, MinV, MaxV))
            PlaceValue(t.Contents.Int, t.Flags, Packing, &ctx);
          break;
        case TempFloat:
          WrStrErrorPos(ErrNum_StringOrIntButFloat, pArg);
          /* fall-through */
        default:
          OK = False;
      }
    }
    if (!OK)
      CodeLen = 0;
    else if (ctx.word_acc_full)
    {
      WrError(ErrNum_PaddingAdded);
      PlaceValue(0x00, eSymbolFlag_None, True, &ctx);
    }
  }
  as_tempres_free(&t);
}

/* one register 0..31 */

static void DecodeReg1(Word Index)
{
  const FixedOrder *pOrder = Reg1Orders + Index;
  Word Reg;

  if (ChkArgCnt(1, 1)
   && ChkCoreMask(pOrder->CoreMask)
   && DecodeArgReg(1, &Reg, AllRegMask))
    AppendCode(pOrder->Code | (Reg << 4));
}

/* two registers 0..31 */

static void DecodeReg2(Word Index)
{
  const FixedOrder *pOrder = Reg2Orders + Index;
  Word Reg1, Reg2;

  if (ChkArgCnt(2, 2)
   && ChkCoreMask(pOrder->CoreMask)
   && DecodeArgReg(1, &Reg1, AllRegMask)
   && DecodeArgReg(2, &Reg2, AllRegMask))
    AppendCode(pOrder->Code | (Reg2 & 15) | (Reg1 << 4) | ((Reg2 & 16) << 5));
}

/* one register 0..31 with itself */

static void DecodeReg3(Word Code)
{
  Word Reg;

  if (ChkArgCnt(1, 1) && DecodeArgReg(1, &Reg, AllRegMask))
    AppendCode(Code | (Reg & 15) | (Reg << 4) | ((Reg & 16) << 5));
}

/* immediate with register */

static void DecodeImm(Word Code)
{
  Word Reg;

  if (ChkArgCnt(2, 2) && DecodeArgReg(1, &Reg, UpperHalfRegMask))
  {
    Boolean OK;
    tSymbolFlags flags;
    Word Const = EvalStrIntExpressionWithFlags(&ArgStr[2], Int8, &OK, &flags);
    if (OK)
      append_code_with_guess_flags(Code | ((Const & 0xf0) << 4) | (Const & 0x0f) | ((Reg & 0x0f) << 4), flags, 0x0f0f);
  }
}

static void DecodeADIW(Word Index)
{
  Word Reg;

  if (ChkArgCnt(2, 2)
   && ChkMinCore(eCoreClassic)
   && DecodeArgReg(1, &Reg, UpperEightEvenRegMask))
  {
    Boolean OK;
    tSymbolFlags flags;
    Word Const = EvalStrIntExpressionWithFlags(&ArgStr[2], UInt6, &OK, &flags);
    if (OK)
      append_code_with_guess_flags(0x9600 | Index | ((Reg & 6) << 3) | (Const & 15) | ((Const & 0x30) << 2), flags, 0x00cf);
  }
}

/* transfer operations */

static void DecodeLDST(Word Index)
{
  int RegI, MemI;
  Word Reg, Mem;

  if (ChkArgCnt(2, 2))
  {
    RegI = Index ? 2 : 1; /* ST */
    MemI = 3 - RegI;
    if (!DecodeArgReg(RegI, &Reg, AllRegMask));
    else if (!DecodeMem(ArgStr[MemI].str.p_str, &Mem)) WrError(ErrNum_InvAddrMode);
    else if ((pCurrCPUProps->Core == eCore90S1200) && (Mem != 0)) WrError(ErrNum_AddrMustBeAligned);
    else
    {
      AppendCode(0x8000 | Index | (Reg << 4) | (Mem & 0x0f) | ((Mem & 0x10) << 8));
      if (((Mem >= 0x1d) && (Mem <= 0x1e) && (Reg >= 26) && (Reg <= 27))  /* X+/-X with X */
       || ((Mem >= 0x19) && (Mem <= 0x1a) && (Reg >= 28) && (Reg <= 29))  /* Y+/-Y with Y */
       || ((Mem >= 0x11) && (Mem <= 0x12) && (Reg >= 30) && (Reg <= 31))) /* Z+/-Z with Z */
        WrError(ErrNum_Unpredictable);
    }
  }
}

static void DecodeLDDSTD(Word Index)
{
  if (ChkArgCnt(2, 2)
   && ChkMinCore(eCoreClassic))
  {
    Word Reg;
    char RegChar;
    Boolean OK;
    int RegI = Index ? 2 : 1, /* STD */
        MemI = 3 - RegI;

    RegChar = *ArgStr[MemI].str.p_str;
    OK = True;
    if (as_toupper(RegChar) == 'Y') Index += 8;
    else if (as_toupper(RegChar) == 'Z');
    else OK = False;
    if (!OK) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[MemI]);
    else if (DecodeArgReg(RegI, &Reg, AllRegMask))
    {
      tSymbolFlags flags;
      Word Disp;

      *ArgStr[MemI].str.p_str = '0';
      Disp = EvalStrIntExpressionWithFlags(&ArgStr[MemI], UInt6, &OK, &flags);
      *ArgStr[MemI].str.p_str = RegChar;
      if (OK)
        append_code_with_guess_flags(0x8000 | Index | (Reg << 4) | (Disp & 7) | ((Disp & 0x18) << 7) | ((Disp & 0x20) << 8), flags, 0x2c07);
    }
  }
}

static void DecodeINOUT(Word Index)
{
  int RegI, MemI;
  Word Reg, Mem;

  if (ChkArgCnt(2, 2))
  {
    RegI = Index ? 2 : 1; /* OUT */
    MemI = 3 - RegI;
    if (DecodeArgReg(RegI, &Reg, AllRegMask))
    {
      tEvalResult EvalResult;

      Mem = EvalStrIntExpressionWithResult(&ArgStr[MemI], UInt6, &EvalResult);
      if (EvalResult.OK)
      {
        ChkSpace(SegIO, EvalResult.AddrSpaceMask);
        append_code_with_guess_flags(0xb000 | Index | (Reg << 4) | (Mem & 0x0f) | ((Mem & 0xf0) << 5), EvalResult.Flags, 0x060f);
      }
    }
  }
}

static void DecodeLDSSTS(Word Index)
{
  int RegI, MemI;
  Word Reg;

  if (ChkArgCnt(2, 2)
   && ChkCoreMask(MinCoreMask(eCoreClassic) | (1 << eCoreMinTiny)))
  {
    RegI = Index ? 2 : 1; /* STS */
    MemI = 3 - RegI;
    if (DecodeArgReg(RegI, &Reg, AllRegMask))
    {
      tEvalResult EvalResult;
      Word Address = EvalStrIntExpressionWithResult(&ArgStr[MemI], UInt16, &EvalResult);
      if (EvalResult.OK)
      {
        ChkSpace(SegData, EvalResult.AddrSpaceMask);
        AppendCode(0x9000 | Index | (Reg << 4));
        append_code_with_guess_flags(Address, EvalResult.Flags, 0xffff);
      }
    }
  }
}

/* bit operations */

static void DecodeBCLRSET(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    tSymbolFlags flags;
    Word Bit = EvalStrIntExpressionWithFlags(&ArgStr[1], UInt3, &OK, &flags);

    if (OK)
      append_code_with_guess_flags(0x9408 | (Bit << 4) | Index, flags, 0x0070);
  }
}

static void DecodeBit(Word Code)
{
  Word Reg, Bit;
  Boolean OK;

  if (!ChkArgCnt(2, 2));
  else if (DecodeArgReg(1, &Reg, AllRegMask))
  {
    Bit = EvalStrIntExpression(&ArgStr[2], UInt3, &OK);
    if (OK)
      AppendCode(Code | (Reg << 4) | Bit);
  }
}

static void DecodeCBR(Word Index)
{
  Word Reg, Mask;
  Boolean OK;

  UNUSED(Index);

  if (ChkArgCnt(2, 2) && DecodeArgReg(1, &Reg, UpperHalfRegMask))
  {
    Mask = EvalStrIntExpression(&ArgStr[2], Int8, &OK) ^ 0xff;
    if (OK)
      AppendCode(0x7000 | ((Mask & 0xf0) << 4) | (Mask & 0x0f) | ((Reg & 0x0f) << 4));
  }
}

static void DecodeSER(Word Index)
{
  Word Reg;

  UNUSED(Index);

  if (ChkArgCnt(1, 1) && DecodeArgReg(1, &Reg, UpperHalfRegMask))
    AppendCode(0xef0f | ((Reg & 0x0f) << 4));
}

static void DecodePBit(Word Code)
{
  LongWord BitSpec;
  tSymbolFlags flags;

  if (DecodeBitArg(1, ArgCnt, &BitSpec, &flags))
  {
    Word Bit = BitSpec & 7,
         Adr = (BitSpec >> 3) & 0xffff;

    if (BitSpec & BitFlag_Data) WrError(ErrNum_WrongSegment);
    if (ChkRange(Adr, 0, 31))
      append_code_with_guess_flags(Code | Bit | (Adr << 3), flags, 0x00ff);
  }
}

/* branches */

static void DecodeRel(Word Code)
{
  LongInt AdrInt;
  tEvalResult EvalResult;

  if (ChkArgCnt(1, 1))
  {
    AdrInt = GetWordCodeAddress(&ArgStr[1], &EvalResult) - GetNextCodeAddress();
    if (EvalResult.OK)
    {
      if (WrapFlag) AdrInt = CutAdr(AdrInt);
      if (!mSymbolQuestionable(EvalResult.Flags) && ((AdrInt < -64) || (AdrInt > 63))) WrError(ErrNum_JmpDistTooBig);
      else
        append_code_with_guess_flags(Code | ((AdrInt & 0x7f) << 3), EvalResult.Flags, 0x03f8);
    }
  }
}

static void DecodeBRBSBC(Word Index)
{
  Word Bit;
  LongInt AdrInt;
  tEvalResult EvalResult;

  if (ChkArgCnt(2, 2))
  {
    Bit = EvalStrIntExpressionWithResult(&ArgStr[1], UInt3, &EvalResult);
    if (EvalResult.OK)
    {
      AdrInt = GetWordCodeAddress(&ArgStr[2], &EvalResult) - GetNextCodeAddress();
      if (EvalResult.OK)
      {
        if (WrapFlag) AdrInt = CutAdr(AdrInt);
        if (!mSymbolQuestionable(EvalResult.Flags) && ((AdrInt < -64) || (AdrInt > 63))) WrError(ErrNum_JmpDistTooBig);
        else
          append_code_with_guess_flags(0xf000 | Index | ((AdrInt & 0x7f) << 3) | Bit, EvalResult.Flags, 0x03f8);
      }
    }
  }
}

static void DecodeJMPCALL(Word Index)
{
  LongInt AdrInt;
  tEvalResult EvalResult;

  if (ChkArgCnt(1, 1)
   && ChkMinCore(eCoreTiny16K))
  {
    AdrInt = GetWordCodeAddress(&ArgStr[1], &EvalResult);
    if (EvalResult.OK)
    {
      append_code_with_guess_flags(0x940c | Index | ((AdrInt & 0x3e0000) >> 13) | ((AdrInt & 0x10000) >> 16), EvalResult.Flags, 0x01f1);
      append_code_with_guess_flags(AdrInt & 0xffff, EvalResult.Flags, 0xffff);
    }
  }
}

static void DecodeRJMPCALL(Word Index)
{
  LongInt AdrInt;
  tEvalResult EvalResult;

  if (ChkArgCnt(1, 1))
  {
    AdrInt = GetWordCodeAddress(&ArgStr[1], &EvalResult) - GetNextCodeAddress();
    if (EvalResult.OK)
    {
      if (WrapFlag) AdrInt = CutAdr(AdrInt);
      if (!mSymbolQuestionable(EvalResult.Flags) && ((AdrInt < -2048) || (AdrInt > 2047))) WrError(ErrNum_JmpDistTooBig);
      else
        append_code_with_guess_flags(0xc000 | Index | (AdrInt & 0xfff), EvalResult.Flags, 0xfff);
    }
  }
}

static void DecodeMULS(Word Index)
{
  Word Reg1, Reg2;

  UNUSED(Index);

  if (ChkArgCnt(2, 2)
   && ChkMinCore(eCoreMega)
   && DecodeArgReg(1, &Reg1, UpperHalfRegMask)
   && DecodeArgReg(2, &Reg2, UpperHalfRegMask))
    AppendCode(0x0200 | ((Reg1 & 15) << 4) | (Reg2 & 15));
}

static void DecodeMegaMUL(Word Index)
{
  Word Reg1, Reg2;

  if (ChkArgCnt(2, 2)
   && ChkMinCore(eCoreMega)
   && DecodeArgReg(1, &Reg1, Reg16_23Mask)
   && DecodeArgReg(2, &Reg2, Reg16_23Mask))
    AppendCode(Index | ((Reg1 & 7) << 4) | (Reg2 & 7));
}

static void DecodeMOVW(Word Index)
{
  Word Reg1, Reg2;

  UNUSED(Index);

  if (ChkArgCnt(2, 2)
   && ChkMinCore(eCoreTiny)
   && DecodeArgReg(1, &Reg1, EvenRegMask)
   && DecodeArgReg(2, &Reg2, EvenRegMask))
    AppendCode(0x0100 | ((Reg1 >> 1) << 4) | (Reg2 >> 1));
}

static void DecodeLPM(Word Index)
{
  Word Reg, Adr;

  UNUSED(Index);

  if (!ArgCnt)
  {
    if (ChkMinCore(eCoreClassic))
      AppendCode(0x95c8);
  }
  else if (ArgCnt == 2)
  {
    if (!ChkMinCore(eCoreTiny));
    else if (!DecodeArgReg(1, &Reg, AllRegMask));
    else if (!DecodeMem(ArgStr[2].str.p_str, &Adr)) WrError(ErrNum_InvAddrMode);
    else if ((Adr != 0x00) && (Adr != 0x11)) WrError(ErrNum_InvAddrMode);
    else
    {
      if (((Reg == 30) || (Reg == 31)) && (Adr == 0x11)) WrError(ErrNum_Unpredictable);
      AppendCode(0x9004 | (Reg << 4) | (Adr & 1));
    }
  }
  else
    (void)ChkArgCnt(2, 2);
}

static void DecodeELPM(Word Index)
{
  Word Reg, Adr;

  UNUSED(Index);

  if (!ChkMinCore(eCoreMega));
  else if (!ArgCnt)
    AppendCode(0x95d8);
  else if (!ChkArgCnt(2, 2));
  else if (!DecodeArgReg(1, &Reg, AllRegMask));
  else if (!DecodeMem(ArgStr[2].str.p_str, &Adr)) WrError(ErrNum_InvAddrMode);
  else if ((Adr != 0x00) && (Adr != 0x11)) WrError(ErrNum_InvAddrMode);
  else
  {
    if (((Reg == 30) || (Reg == 31)) && (Adr == 0x11)) WrError(ErrNum_Unpredictable);
    AppendCode(0x9006 | (Reg << 4) | (Adr & 1));
  }
}

static void DecodeBIT(Word Code)
{
  LongWord BitSpec;
  tSymbolFlags flags;

  UNUSED(Code);

  if (DecodeBitArg(1, ArgCnt, &BitSpec, &flags))
  {
    *ListLine = '=';
    DissectBit_AVR(ListLine + 1, STRINGSIZE - 3, BitSpec);
    PushLocHandle(-1);
    EnterIntSymbol(&LabPart, BitSpec, SegBData, False);
    PopLocHandle();
    if (MakeUseList)
    {
      if (AddChunk(SegChunks + SegBData, BitSpec, 1, False))
        WrError(ErrNum_Overlap);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     make_pc_even(Word index)
 * \brief  assure PC is even before assembling machine instructions
 * ------------------------------------------------------------------------ */

static void make_pc_even(Word index)
{
  UNUSED(index);

  /* All other instructions must be on an even address in byte mode.
     In other words, they may not cross flash word boundaries: */

  if (!CodeSegSize && Odd(EProgCounter()))
  {
    if (DoPadding)
      InsertPadding(1, False);
    else
      WrError(ErrNum_AddrNotAligned);
  }
}

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

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

static void AddReg1(const char *NName, Word NMin, Word NCode)
{
  order_array_rsv_end(Reg1Orders, FixedOrder);
  Reg1Orders[InstrZ].Code = NCode;
  Reg1Orders[InstrZ].CoreMask = NMin;
  AddInstTable(InstTable, NName, InstrZ++, DecodeReg1);
}

static void AddReg2(const char *NName, Word NMin, Word NCode)
{
  order_array_rsv_end(Reg2Orders, FixedOrder);
  Reg2Orders[InstrZ].Code = NCode;
  Reg2Orders[InstrZ].CoreMask = NMin;
  AddInstTable(InstTable, NName, InstrZ++, DecodeReg2);
}

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

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

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

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

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

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

  add_null_pseudo(InstTable);

  inst_table_set_prefix_proc(InstTable, make_pc_even, 0);

  InstrZ = 0;
  AddFixed("IJMP" , MinCoreMask(eCoreClassic) | (1 << eCoreMinTiny), 0x9409);
  AddFixed("ICALL", MinCoreMask(eCoreClassic) | (1 << eCoreMinTiny), 0x9509);
  AddFixed("RET"  , MinCoreMask(eCoreMinTiny), 0x9508); AddFixed("RETI"  , MinCoreMask(eCoreMinTiny), 0x9518);
  AddFixed("SEC"  , MinCoreMask(eCoreMinTiny), 0x9408);
  AddFixed("CLC"  , MinCoreMask(eCoreMinTiny), 0x9488); AddFixed("SEN"   , MinCoreMask(eCoreMinTiny), 0x9428);
  AddFixed("CLN"  , MinCoreMask(eCoreMinTiny), 0x94a8); AddFixed("SEZ"   , MinCoreMask(eCoreMinTiny), 0x9418);
  AddFixed("CLZ"  , MinCoreMask(eCoreMinTiny), 0x9498); AddFixed("SEI"   , MinCoreMask(eCoreMinTiny), 0x9478);
  AddFixed("CLI"  , MinCoreMask(eCoreMinTiny), 0x94f8); AddFixed("SES"   , MinCoreMask(eCoreMinTiny), 0x9448);
  AddFixed("CLS"  , MinCoreMask(eCoreMinTiny), 0x94c8); AddFixed("SEV"   , MinCoreMask(eCoreMinTiny), 0x9438);
  AddFixed("CLV"  , MinCoreMask(eCoreMinTiny), 0x94b8); AddFixed("SET"   , MinCoreMask(eCoreMinTiny), 0x9468);
  AddFixed("CLT"  , MinCoreMask(eCoreMinTiny), 0x94e8); AddFixed("SEH"   , MinCoreMask(eCoreMinTiny), 0x9458);
  AddFixed("CLH"  , MinCoreMask(eCoreMinTiny), 0x94d8); AddFixed("NOP"   , MinCoreMask(eCoreMinTiny), 0x0000);
  AddFixed("SLEEP", MinCoreMask(eCoreMinTiny), 0x9588); AddFixed("WDR"   , MinCoreMask(eCoreMinTiny), 0x95a8);
  AddFixed("EIJMP", MinCoreMask(eCoreMega   ), 0x9419); AddFixed("EICALL", MinCoreMask(eCoreMega   ), 0x9519);
  AddFixed("SPM"  , MinCoreMask(eCoreTiny   ), 0x95e8);
  AddFixed("BREAK" , MinCoreMask(eCoreTiny   ) | (1 << eCoreMinTiny), 0x9598);

  InstrZ = 0;
  AddReg1("COM"  , MinCoreMask(eCoreMinTiny), 0x9400); AddReg1("NEG"  , MinCoreMask(eCoreMinTiny), 0x9401);
  AddReg1("INC"  , MinCoreMask(eCoreMinTiny), 0x9403); AddReg1("DEC"  , MinCoreMask(eCoreMinTiny), 0x940a);
  AddReg1("PUSH" , MinCoreMask(eCoreClassic) | (1 << eCoreMinTiny), 0x920f);
  AddReg1("POP"  , MinCoreMask(eCoreClassic) | (1 << eCoreMinTiny), 0x900f);
  AddReg1("LSR"  , MinCoreMask(eCoreMinTiny), 0x9406); AddReg1("ROR"  , MinCoreMask(eCoreMinTiny), 0x9407);
  AddReg1("ASR"  , MinCoreMask(eCoreMinTiny), 0x9405); AddReg1("SWAP" , MinCoreMask(eCoreMinTiny), 0x9402);

  InstrZ = 0;
  AddReg2("ADD"  , MinCoreMask(eCoreMinTiny), 0x0c00); AddReg2("ADC"  , MinCoreMask(eCoreMinTiny), 0x1c00);
  AddReg2("SUB"  , MinCoreMask(eCoreMinTiny), 0x1800); AddReg2("SBC"  , MinCoreMask(eCoreMinTiny), 0x0800);
  AddReg2("AND"  , MinCoreMask(eCoreMinTiny), 0x2000); AddReg2("OR"   , MinCoreMask(eCoreMinTiny), 0x2800);
  AddReg2("EOR"  , MinCoreMask(eCoreMinTiny), 0x2400); AddReg2("CPSE" , MinCoreMask(eCoreMinTiny), 0x1000);
  AddReg2("CP"   , MinCoreMask(eCoreMinTiny), 0x1400); AddReg2("CPC"  , MinCoreMask(eCoreMinTiny), 0x0400);
  AddReg2("MOV"  , MinCoreMask(eCoreMinTiny), 0x2c00); AddReg2("MUL"  , MinCoreMask(eCoreMega   ), 0x9c00);

  AddReg3("CLR"  , 0x2400); AddReg3("TST"  , 0x2000); AddReg3("LSL"  , 0x0c00);
  AddReg3("ROL"  , 0x1c00);

  AddImm("SUBI" , 0x5000); AddImm("SBCI" , 0x4000); AddImm("ANDI" , 0x7000);
  AddImm("ORI"  , 0x6000); AddImm("SBR"  , 0x6000); AddImm("CPI"  , 0x3000);
  AddImm("LDI"  , 0xe000);

  AddRel("BRCC" , 0xf400); AddRel("BRCS" , 0xf000); AddRel("BREQ" , 0xf001);
  AddRel("BRGE" , 0xf404); AddRel("BRSH" , 0xf400); AddRel("BRID" , 0xf407);
  AddRel("BRIE" , 0xf007); AddRel("BRLO" , 0xf000); AddRel("BRLT" , 0xf004);
  AddRel("BRMI" , 0xf002); AddRel("BRNE" , 0xf401); AddRel("BRHC" , 0xf405);
  AddRel("BRHS" , 0xf005); AddRel("BRPL" , 0xf402); AddRel("BRTC" , 0xf406);
  AddRel("BRTS" , 0xf006); AddRel("BRVC" , 0xf403); AddRel("BRVS" , 0xf003);

  AddBit("BLD"  , 0xf800); AddBit("BST"  , 0xfa00);
  AddBit("SBRC" , 0xfc00); AddBit("SBRS" , 0xfe00);

  AddPBit("CBI" , 0x9800); AddPBit("SBI" , 0x9a00);
  AddPBit("SBIC", 0x9900); AddPBit("SBIS", 0x9b00);

  AddInstTable(InstTable, "ADIW", 0x0000, DecodeADIW);
  AddInstTable(InstTable, "SBIW", 0x0100, DecodeADIW);

  AddInstTable(InstTable, "LD", 0x0000, DecodeLDST);
  AddInstTable(InstTable, "ST", 0x0200, DecodeLDST);

  AddInstTable(InstTable, "LDD", 0x0000, DecodeLDDSTD);
  AddInstTable(InstTable, "STD", 0x0200, DecodeLDDSTD);

  AddInstTable(InstTable, "IN" , 0x0000, DecodeINOUT);
  AddInstTable(InstTable, "OUT", 0x0800, DecodeINOUT);

  AddInstTable(InstTable, "LDS", 0x0000, DecodeLDSSTS);
  AddInstTable(InstTable, "STS", 0x0200, DecodeLDSSTS);

  AddInstTable(InstTable, "BCLR", 0x0080, DecodeBCLRSET);
  AddInstTable(InstTable, "BSET", 0x0000, DecodeBCLRSET);

  AddInstTable(InstTable, "CBR", 0, DecodeCBR);
  AddInstTable(InstTable, "SER", 0, DecodeSER);

  AddInstTable(InstTable, "BRBC", 0x0400, DecodeBRBSBC);
  AddInstTable(InstTable, "BRBS", 0x0000, DecodeBRBSBC);

  AddInstTable(InstTable, "JMP" , 0, DecodeJMPCALL);
  AddInstTable(InstTable, "CALL", 2, DecodeJMPCALL);

  AddInstTable(InstTable, "RJMP" , 0x0000, DecodeRJMPCALL);
  AddInstTable(InstTable, "RCALL", 0x1000, DecodeRJMPCALL);

  AddInstTable(InstTable, "MULS", 0, DecodeMULS);

  AddInstTable(InstTable, "MULSU" , 0x0300, DecodeMegaMUL);
  AddInstTable(InstTable, "FMUL"  , 0x0308, DecodeMegaMUL);
  AddInstTable(InstTable, "FMULS" , 0x0380, DecodeMegaMUL);
  AddInstTable(InstTable, "FMULSU", 0x0388, DecodeMegaMUL);

  AddInstTable(InstTable, "MOVW", 0, DecodeMOVW);

  AddInstTable(InstTable, "LPM" , 0, DecodeLPM);
  AddInstTable(InstTable, "ELPM", 0, DecodeELPM);

  inst_table_set_prefix_proc(InstTable, NULL, 0);

  /* instructions that do not require word alignment in byte mode */

  AddInstTable(InstTable, "PORT", 0, DecodePORT);
  AddInstTable(InstTable, "SFR" , 0, DecodeSFR);
  AddInstTable(InstTable, "RES" , 0, DecodeRES);
  AddInstTable(InstTable, "REG" , 0, CodeREG);
  AddInstTable(InstTable, "BIT" , 0, DecodeBIT);
  AddInstTable(InstTable, "DATA", 0, DecodeDATA_AVR);
  AddIntelPseudo(InstTable, eIntPseudoFlag_LittleEndian);
}

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

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

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

static Boolean IsDef_AVR(void)
{
  return (Memo("PORT")
       || Memo("REG")
       || Memo("SFR")
       || Memo("BIT"));
}

/*!------------------------------------------------------------------------
 * \fn     InternSymbol_AVR(char *pArg, TempResult *pResult)
 * \brief  parse for built-in symbols
 * \param  pArg source argument
 * \param  pResult possible result
 * ------------------------------------------------------------------------ */

static void InternSymbol_AVR(char *pArg, TempResult *pResult)
{
  Word RegValue;

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

static Boolean ChkZeroArg(void)
{
  return (0 == ArgCnt);
}

static void SwitchTo_AVR(void *pUser)
{
  const TFamilyDescr *p_descr = FindFamilyByName(CodeSegSize ? "AVR" : "AVR(CSEG8)");

  pCurrCPUProps = (const tCPUProps*)pUser;

  TurnWords = False;
  SetIntConstMode(eIntConstModeC);
  SetIsOccupiedFnc = ChkZeroArg;

  PCSymbol = "*";
  HeaderID = p_descr->Id;
  NOPCode = 0x0000;
  DivideChars = ",";
  HasAttrs = False;

  ValidSegs = (1 << SegCode) | (1 << SegData) | (1 << SegIO);
  Grans[SegCode] = ListGrans[SegCode] = 1 << CodeSegSize; SegInits[SegCode] = 0;
  Grans[SegData] = 1; ListGrans[SegData] = 1; SegInits[SegData] = 32;
  Grans[SegIO  ] = 1; ListGrans[SegIO  ] = 1; SegInits[SegIO  ] = 0;  SegLimits[SegIO] = 0x3f;
  if (pCurrCPUProps->EESize)
  {
    ValidSegs |= (1 << SegEEData);
    SegLimits[SegEEData] = pCurrCPUProps->EESize;
    Grans[SegEEData] = 1; ListGrans[SegEEData] = 1; SegInits[SegEEData] = 0;
  }

  SegLimits[SegCode] = ((LongWord)pCurrCPUProps->FlashEndD16) << 4 | 0xf;
  if (!CodeSegSize)
  {
    SegLimits[SegCode] = (SegLimits[SegCode] << 1) + 1;
    AddONOFF(DoPaddingName, &DoPadding, DoPaddingName, False);
  }
  SegLimits[SegData] = (pCurrCPUProps->RegistersMapped ? RegBankSize : 0)
                     + pCurrCPUProps->IOAreaSize
                     + pCurrCPUProps->RAMSize
                     - 1;

  CodeAdrIntType = GetSmallestUIntType(SegLimits[SegCode]);
  DataAdrIntType = GetSmallestUIntType(SegLimits[SegData]);

  SignMask = (SegLimits[SegCode] + 1) >> 1;
  ORMask = ((LongInt) - 1) - SegLimits[SegCode];

  onoff_packing_add(False);
  AddONOFF("WRAPMODE", &WrapFlag, WrapFlagName, False);
  SetFlag(&WrapFlag, WrapFlagName, False);

  MakeCode = MakeCode_AVR;
  IsDef = IsDef_AVR;
  InternSymbol = InternSymbol_AVR;
  DissectReg = DissectReg_AVR;
  SwitchFrom = DeinitFields;
  DissectBit = DissectBit_AVR;
  InitFields();
}

static const tCPUProps CPUProps[] =
{
  { "AT90S1200"      ,  0x01f, 0x0000, 0x003f, IOAreaStdSize , True , eCore90S1200   },
  { "AT90S2313"      ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreClassic   },
  { "AT90S2323"      ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreClassic   },
  { "AT90S2333"      ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreClassic   }, /* == ATtiny22 */
  { "AT90S2343"      ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreClassic   },
  { "AT90S4414"      ,  0x07f, 0x0100, 0x00ff, IOAreaStdSize , True , eCoreClassic   },
  { "AT90S4433"      ,  0x07f, 0x0080, 0x00ff, IOAreaStdSize , True , eCoreClassic   },
  { "AT90S4434"      ,  0x07f, 0x0100, 0x00ff, IOAreaStdSize , True , eCoreClassic   },
  { "AT90S8515"      ,  0x0ff, 0x0200, 0x01ff, IOAreaStdSize , True , eCoreClassic   },
  { "AT90C8534"      ,  0x0ff, 0x0100, 0x01ff, IOAreaStdSize , True , eCoreClassic   },
  { "AT90S8535"      ,  0x0ff, 0x0200, 0x01ff, IOAreaStdSize , True , eCoreClassic   },
  { "AT90USB646"     ,  0x7ff, 0x1000, 0x07ff, IOAreaExtSize , True , eCoreMega      },
  { "AT90USB647"     ,  0x7ff, 0x1000, 0x07ff, IOAreaExtSize , True , eCoreMega      },
  { "AT90USB1286"    ,  0xfff, 0x2000, 0x0fff, IOAreaExtSize , True , eCoreMega      },
  { "AT90USB1287"    ,  0xfff, 0x2000, 0x0fff, IOAreaExtSize , True , eCoreMega      },

  { "AT43USB355"     ,  0x2ff, 0x0400, 0x0000, 0x2000-RegBankSize, True , eCoreClassic   }, /* allow USB registers @ 0x1fxx */

  { "ATTINY4"        ,  0x00f, 0x0020, 0x0000, IOAreaStdSize , False, eCoreMinTiny   },
  { "ATTINY5"        ,  0x00f, 0x0020, 0x0000, IOAreaStdSize , False, eCoreMinTiny   },
  { "ATTINY9"        ,  0x01f, 0x0020, 0x0000, IOAreaStdSize , False, eCoreMinTiny   },
  { "ATTINY10"       ,  0x01f, 0x0020, 0x0000, IOAreaStdSize , False, eCoreMinTiny   },
  { "ATTINY11"       ,  0x01f, 0x0000, 0x0000, IOAreaStdSize , True , eCore90S1200   },
  { "ATTINY12"       ,  0x01f, 0x0000, 0x003f, IOAreaStdSize , True , eCore90S1200   },
  { "ATTINY13"       ,  0x01f, 0x0040, 0x003f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY13A"      ,  0x01f, 0x0040, 0x003f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY15"       ,  0x01f, 0x0000, 0x003f, IOAreaStdSize , True , eCore90S1200   },
  { "ATTINY20"       ,  0x03f, 0x0080, 0x0000, IOAreaStdSize , False, eCoreMinTiny   },
  { "ATTINY24"       ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY24A"      ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY25"       ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY26"       ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY28"       ,  0x03f, 0x0000, 0x0000, IOAreaStdSize , True , eCore90S1200   },
  { "ATTINY40"       ,  0x07f, 0x0100, 0x0000, IOAreaStdSize , False, eCoreMinTiny   },
  { "ATTINY44"       ,  0x07f, 0x0100, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY44A"      ,  0x07f, 0x0100, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY45"       ,  0x07f, 0x0100, 0x00ff, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY48"       ,  0x07f, 0x0100, 0x003f, IOAreaExtSize , True , eCoreTiny      },
  { "ATTINY84"       ,  0x0ff, 0x0200, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY84A"      ,  0x0ff, 0x0200, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY85"       ,  0x0ff, 0x0200, 0x00ff, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY87"       ,  0x0ff, 0x0200, 0x01ff, IOAreaExtSize , True , eCoreTiny16K   },
  { "ATTINY88"       ,  0x0ff, 0x0200, 0x003f, IOAreaExtSize , True , eCoreTiny      },
  { "ATTINY102"      ,  0x01f, 0x0020, 0x0000, IOAreaStdSize , False, eCoreMinTiny   },
  { "ATTINY104"      ,  0x01f, 0x0020, 0x0000, IOAreaStdSize , False, eCoreMinTiny   },
  { "ATTINY167"      ,  0x1ff, 0x0200, 0x01ff, IOAreaExtSize , True , eCoreTiny16K   },
  { "ATTINY261"      ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY261A"     ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY43U"      ,  0x07f, 0x0100, 0x003f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY441"      ,  0x07f, 0x0100, 0x00ff, IOAreaExtSize , True , eCoreTiny      },
  { "ATTINY461"      ,  0x07f, 0x0100, 0x00ff, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY461A"     ,  0x07f, 0x0100, 0x00ff, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY828"      ,  0x0ff, 0x0200, 0x00ff, IOAreaExtSize , True , eCoreTiny      },
  { "ATTINY841"      ,  0x0ff, 0x0200, 0x00ff, IOAreaExtSize , True , eCoreTiny      },
  { "ATTINY861"      ,  0x0ff, 0x0200, 0x01ff, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY861A"     ,  0x0ff, 0x0200, 0x01ff, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY1634"     ,  0x1ff, 0x0400, 0x00ff, IOAreaExtSize , True , eCoreTiny16K   },
  { "ATTINY2313"     ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY2313A"    ,  0x03f, 0x0080, 0x007f, IOAreaStdSize , True , eCoreTiny      },
  { "ATTINY4313"     ,  0x07f, 0x0100, 0x00ff, IOAreaStdSize , True , eCoreTiny      },

  { "ATMEGA48"       ,  0x07f, 0x0200, 0x00ff, IOAreaExtSize , True , eCoreMega      },

  { "ATMEGA8"        ,  0x0ff, 0x0400, 0x01ff, IOAreaStdSize , True , eCoreMega      },
  { "ATMEGA8515"     ,  0x0ff, 0x0200, 0x01ff, IOAreaStdSize , True , eCoreMega      },
  { "ATMEGA8535"     ,  0x0ff, 0x0200, 0x01ff, IOAreaStdSize , True , eCoreMega      },
  { "ATMEGA88"       ,  0x0ff, 0x0200, 0x01ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA8U2"      ,  0x0ff, 0x0200, 0x01ff, IOAreaExtSize , True , eCoreMega      },

  { "ATMEGA16"       ,  0x1ff, 0x0400, 0x01ff, IOAreaStdSize , True , eCoreMega      },
  { "ATMEGA161"      ,  0x1ff, 0x0400, 0x01ff, IOAreaStdSize , True , eCoreMega      },
  { "ATMEGA162"      ,  0x1ff, 0x0400, 0x01ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA163"      ,  0x1ff, 0x0400, 0x01ff, IOAreaStdSize , True , eCoreMega      },
  { "ATMEGA164"      ,  0x1ff, 0x0400, 0x01ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA165"      ,  0x1ff, 0x0200, 0x01ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA168"      ,  0x1ff, 0x0400, 0x01ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA169"      ,  0x1ff, 0x0400, 0x01ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA16U2"     ,  0x1ff, 0x0200, 0x01ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA16U4"     ,  0x1ff, 0x0500, 0x01ff, IOAreaExtSize , True , eCoreMega      },

  { "ATMEGA32"       ,  0x3ff, 0x0800, 0x03ff, IOAreaStdSize , True , eCoreMega      },
  { "ATMEGA323"      ,  0x3ff, 0x0800, 0x03ff, IOAreaStdSize , True , eCoreMega      },
  { "ATMEGA324"      ,  0x3ff, 0x0800, 0x03ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA325"      ,  0x3ff, 0x0800, 0x03ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA3250"     ,  0x3ff, 0x0800, 0x03ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA328"      ,  0x3ff, 0x0800, 0x03ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA329"      ,  0x3ff, 0x0800, 0x03ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA3290"     ,  0x3ff, 0x0800, 0x03ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA32U2"     ,  0x3ff, 0x0400, 0x03ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA32U4"     ,  0x3ff, 0x0a00, 0x03ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA32U6"     ,  0x3ff, 0x0a00, 0x03ff, IOAreaExtSize , True , eCoreMega      },

  { "ATMEGA406"      ,  0x4ff, 0x0800, 0x01ff, IOAreaExtSize , True , eCoreMega      },

  { "ATMEGA64"       ,  0x7ff, 0x1000, 0x07ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA640"      ,  0x7ff, 0x2000, 0x0fff, IOAreaExt2Size, True , eCoreMega      },
  { "ATMEGA644"      ,  0x7ff, 0x1000, 0x07ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA644RFR2"  ,  0x7ff, 0x2000, 0x07ff, IOAreaExt2Size, True , eCoreMega      },
  { "ATMEGA645"      ,  0x7ff, 0x1000, 0x07ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA6450"     ,  0x7ff, 0x1000, 0x07ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA649"      ,  0x7ff, 0x1000, 0x07ff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA6490"     ,  0x7ff, 0x1000, 0x07ff, IOAreaExtSize , True , eCoreMega      },

  { "ATMEGA103"      ,  0xfff, 0x1000, 0x0fff, IOAreaStdSize , True , eCoreMega      },
  { "ATMEGA128"      ,  0xfff, 0x1000, 0x0fff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA1280"     ,  0xfff, 0x2000, 0x0fff, IOAreaExt2Size, True , eCoreMega      },
  { "ATMEGA1281"     ,  0xfff, 0x2000, 0x0fff, IOAreaExt2Size, True , eCoreMega      },
  { "ATMEGA1284"     ,  0xfff, 0x4000, 0x0fff, IOAreaExtSize , True , eCoreMega      },
  { "ATMEGA1284RFR2" ,  0xfff, 0x4000, 0x0fff, IOAreaExt2Size, True , eCoreMega      },

  { "ATMEGA2560"     , 0x1fff, 0x2000, 0x0fff, IOAreaExt2Size, True , eCoreMega      },
  { "ATMEGA2561"     , 0x1fff, 0x2000, 0x0fff, IOAreaExt2Size, True , eCoreMega      },
  { "ATMEGA2564RFR2" , 0x1fff, 0x8000, 0x1fff, IOAreaExt2Size, True , eCoreMega      },
  { NULL             ,    0x0, 0     , 0     , 0             , False, eCoreNone      },
};

void codeavr_init(void)
{
  const tCPUProps *pProp;
  static const tCPUArg AVRArgs[] =
  {
    { "CODESEGSIZE", 0, 1, 1, &CodeSegSize },
    { NULL         , 0, 0, 0, NULL         }
  };

  for (pProp = CPUProps; pProp->pName; pProp++)
    (void)AddCPUUserWithArgs(pProp->pName, SwitchTo_AVR, (void*)pProp, NULL, AVRArgs);
}
