/* codepdk.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* AS - Target Padauk MCUs                                                   */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"
#include <string.h>
#include "bpemu.h"
#include "strutil.h"
#include "cpulist.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmitree.h"
#include "asmstructs.h"
#include "codevars.h"
#include "codepseudo.h"
#include "fourpseudo.h"
#include "headids.h"

#include "codepdk.h"

typedef enum
{
  eCoreNone,
  eCorePDK13,
  eCorePDK14,
  eCorePDK15,
  eCorePDK16
} tCPUCore;

enum
{
  eInstCapCOMP_AM_MA = 1 << 0,
  eInstCapNADD_AM_MA = 1 << 1,
  eInstCapMUL = 1 << 2,
  eInstCapXOR_IOA = 1 << 3,
  eInstCap_SWAPM = 1 << 4,
  eInstCapNMOV_AM_MA = 1 << 5,
  eInstCapPUSHW_POPW = 1 << 6,
  eInstCapPMODE = 1 << 7
};

typedef struct
{
  const char *pName;
  Word FlashEndD16;
  Byte RAMEnd, IOAreaEnd;
  tCPUCore Core;
  Word InstCaps;
} tCPUProps;

typedef enum
{
  ModNone = 0,
  ModMem = 1,
  ModIO = 2,
  ModImm = 3,
  ModAcc = 4
} tAdrMode;

#define MModMem (1 << ModMem)
#define MModIO (1 << ModIO)
#define MModImm (1 << ModImm)
#define MModAcc (1 << ModAcc)

static const tCPUProps *pCurrCPUProps;

static IntType CodeAdrIntType,
               CodeWordIntType,
               DataAdrIntType,
               DataBitAdrIntType,
               IOAdrIntType;
static Byte DataMemBits,
            DataBitMemBits,
            CodeMemBits,
            IOMemBits;
static Word AccInstOffs;
static ShortInt OpSize;

/*!------------------------------------------------------------------------
 * \fn     DecodeAdr(tStrComp *pArg, Word Mask, IntType MemIntType, Word *pResult)
 * \brief  decode address expression
 * \param  pArg string argument in source
 * \param  Mask bit mask of allowed modes
 * \param  MemIntType integer range for memory addresses
 * \param  pResult resulting address for memory/IO
 * \return decoded address mode or none
 * ------------------------------------------------------------------------ */

static tAdrMode DecodeAdr(tStrComp *pArg, Word Mask, IntType MemIntType, Word *pResult)
{
  tAdrMode AdrMode = ModNone;
  Boolean OK;
  IntType AutoIntType;
  LongInt AddrOrImm;
  int ArgLen;
  tEvalResult EvalResult;

  *pResult = 0;
  if (!as_strcasecmp(pArg->str.p_str, "A"))
  {
    AdrMode = ModAcc;
    goto check;
  }

  /* explicit memory: disp[addr], [addr], addr[idx] */

  ArgLen = strlen(pArg->str.p_str);
  if ((ArgLen >= 2) && (pArg->str.p_str[ArgLen - 1] == ']'))
  {
    tStrComp Part1, Part2;
    LongInt Num1, Num2;
    Boolean EitherUnknown;
    char *pSep;
    tEvalResult EvalResult1, EvalResult2;

    StrCompShorten(pArg, 1);
    pSep = RQuotPos(pArg->str.p_str, '[');
    if (!pSep)
    {
      WrStrErrorPos(ErrNum_BrackErr, pArg);
      goto check;
    }

    StrCompSplitRef(&Part1, &Part2, pArg, pSep);
    EitherUnknown = False;

    if (*Part1.str.p_str)
    {
      Num1 = EvalStrIntExpressionWithResult(&Part1, MemIntType, &EvalResult1);
      if (!EvalResult1.OK)
        goto check;
      EitherUnknown = EitherUnknown || mFirstPassUnknown(EvalResult1.Flags);
    }
    else
    {
      EvalResult1.OK = False;
      EvalResult1.Flags = eSymbolFlag_None;
      EvalResult1.AddrSpaceMask = 0;
      Num1 = 0;
    }

    Num2 = EvalStrIntExpressionWithResult(&Part2, MemIntType, &EvalResult2);
    if (!EvalResult2.OK)
      goto check;
    EitherUnknown = EitherUnknown || mFirstPassUnknown(EvalResult2.Flags);
    Num1 += Num2;

    if (EitherUnknown)
    {
      Num1 &= SegLimits[SegData];
      if (OpSize == eSymbolSize16Bit)
        Num1 &= ~1;
    }

    if ((OpSize == eSymbolSize16Bit) && (Num1 & 1)) WrStrErrorPos(ErrNum_NotAligned, pArg);
    else if (ChkRange(Num1, 0, SegLimits[SegData]))
    {
      ChkSpace(SegData, EvalResult1.AddrSpaceMask | EvalResult2.AddrSpaceMask);
      AdrMode = ModMem;
      *pResult = Num1;
    }
    goto check;
  }

  /* explicit I/O */

  if (!as_strncasecmp(pArg->str.p_str, "IO", 2) && IsIndirect(pArg->str.p_str + 2))
  {
    *pResult = EvalStrIntExpressionOffsWithResult(pArg, 2, IOAdrIntType, &EvalResult);
    if (EvalResult.OK)
    {
      ChkSpace(SegIO, EvalResult.AddrSpaceMask);
      AdrMode = ModIO;
    }
    goto check;
  }

  /* explicit immediate */

  if (*pArg->str.p_str == '#')
  {
    *pResult = EvalStrIntExpressionOffs(pArg, 1, Int8, &OK) & 0xff;
    if (OK)
      AdrMode = ModImm;
    goto check;
  }

  /* OK, guess what is meant... */

  AutoIntType = Int8;
  if (Lo(IntTypeDefs[IOAdrIntType].SignAndWidth) > Lo(IntTypeDefs[AutoIntType].SignAndWidth))
    AutoIntType = IOAdrIntType;
  if (Lo(IntTypeDefs[MemIntType].SignAndWidth) > Lo(IntTypeDefs[AutoIntType].SignAndWidth))
    AutoIntType = MemIntType;

  AddrOrImm = EvalStrIntExpressionWithResult(pArg, AutoIntType, &EvalResult);
  if (EvalResult.OK)
  {
    if (EvalResult.AddrSpaceMask == 1 << SegIO)
    {
      if (mFirstPassUnknown(EvalResult.Flags) && (AddrOrImm > (LongInt)SegLimits[SegIO]))
        AddrOrImm &= SegLimits[SegIO];
      if (AddrOrImm > (LongInt)SegLimits[SegIO])
        WrStrErrorPos(ErrNum_OverRange, pArg);
      else
      {
        AdrMode = ModIO;
        *pResult = AddrOrImm;
      }
    }
    else if (EvalResult.AddrSpaceMask == 1 << SegData)
    {
      if (mFirstPassUnknown(EvalResult.Flags) && (AddrOrImm > (LongInt)SegLimits[SegData]))
        AddrOrImm &= SegLimits[SegData];
      if (AddrOrImm > (LongInt)SegLimits[SegData])
        WrStrErrorPos(ErrNum_OverRange, pArg);
      else
      {
        AdrMode = ModMem;
        *pResult = AddrOrImm;
      }
    }
    else
    {
      if (mFirstPassUnknown(EvalResult.Flags) && ((AddrOrImm > 0xff) || (AddrOrImm < -128)))
        AddrOrImm &= 0xff;
      if (!ChkRange(AddrOrImm, -128, 255))
        WrStrErrorPos(ErrNum_OverRange, pArg);
      else
      {
        AdrMode = ModImm;
        *pResult = AddrOrImm & 0xff;
      }
    }
  }

check:
  if ((AdrMode != ModNone) && (!(Mask & (1 << AdrMode))))
  {
    WrStrErrorPos(ErrNum_InvAddrMode, pArg);
    AdrMode = ModNone;
  }
  return AdrMode;
}

static Boolean CoreMask(Word Mask)
{
  return !!(Mask & (1 << pCurrCPUProps->Core));
}

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

/*
 * Compact representation of bits in symbol table:
 * bits 0..2: bit position
 * bits 3...n: I/O or memory address
 * bit 15: 1 for I/O, 0 for memory
 */

/*!------------------------------------------------------------------------
 * \fn     EvalBitPosition(const tStrComp *pArg, Boolean *pOK)
 * \brief  evaluate bit position
 * \param  bit position argument (with or without #)
 * \param  pOK parsing OK?
 * \return numeric bit position
 * ------------------------------------------------------------------------ */

static LongWord EvalBitPosition(const tStrComp *pArg, Boolean *pOK)
{
  return EvalStrIntExpression(pArg, UInt3, pOK);
}

/*!------------------------------------------------------------------------
 * \fn     AssembleBitSymbol(Byte BitPos, Word Address, Boolean IsIO)
 * \brief  build the compact internal representation of a bit symbol
 * \param  BitPos bit position in word
 * \param  Address register address
 * \param  IsIO true for bit in I/O space
 * \return compact representation
 * ------------------------------------------------------------------------ */

static LongWord AssembleBitSymbol(Byte BitPos, Word Address, Boolean IsIO)
{
  return (BitPos & 7)
       | (((LongWord)Address & 0x1ff) << 3)
       | (IsIO ? 0x8000u : 0x0000);
}

/*!------------------------------------------------------------------------
 * \fn     DissectBitSymbol(LongWord BitSymbol, Word *pAddress, Byte *pBitPos, Boolean *pIO)
 * \brief  transform compact representation of bit symbol into components
 * \param  BitSymbol compact storage
 * \param  pAddress I/O or memory address
 * \param  pBitPos bit position
 * \param  pIO 1 for I/O, 0 for memory address
 * \return constant True
 * ------------------------------------------------------------------------ */

static Boolean DissectBitSymbol(LongWord BitSymbol, Word *pAddress, Byte *pBitPos, Boolean *pIO)
{
  *pAddress = (BitSymbol >> 3) & 0x1ff;
  *pBitPos = BitSymbol & 7;
  *pIO = !!(BitSymbol & 0x8000u);
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitArg2(LongWord *pResult, tStrComp *pAddrArg, const tStrComp *pBitArg)
 * \brief  encode a bit symbol, address & bit position separated
 * \param  pResult resulting encoded bit
 * \param  pAddrArg memory/IO address argument
 * \param  pBitArg bit argument
 * \return True if success
 * ------------------------------------------------------------------------ */

static Boolean DecodeBitArg2(LongWord *pResult, tStrComp *pAddrArg, const tStrComp *pBitArg)
{
  Boolean OK;
  Word Address;
  Byte BitPos;
  Boolean IsIO;

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

  switch (DecodeAdr(pAddrArg, MModMem | MModIO, DataBitAdrIntType, &Address))
  {
    case ModMem:
      IsIO = False;
      break;
    case ModIO:
      IsIO = True;
      break;
    default:
      return False;
  }

  *pResult = AssembleBitSymbol(BitPos, Address, IsIO);

  return True;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitArg(LongWord *pResult, int Start, int Stop)
 * \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 DecodeBitArg(LongWord *pResult, int Start, int Stop)
{
  *pResult = 0;

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

  if (Start == Stop)
  {
    char *pSep = strchr(ArgStr[Start].str.p_str, '.');

    if (pSep)
    {
      tStrComp AddressComp, PosComp;

      StrCompSplitRef(&AddressComp, &PosComp, &ArgStr[Start], pSep);
      return DecodeBitArg2(pResult, &AddressComp, &PosComp);
    }
    else
    {
      tEvalResult EvalResult;

      *pResult = EvalStrIntExpressionWithResult(&ArgStr[Start], UInt16, &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]);

  /* other # of arguments not allowed */

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

/*!------------------------------------------------------------------------
 * \fn     DissectBit_Padauk(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_Padauk(char *pDest, size_t DestSize, LargeWord Inp)
{
  Byte BitPos;
  Word Address;
  Boolean IsIO;

  DissectBitSymbol(Inp, &Address, &BitPos, &IsIO);

  if (IsIO)
    as_snprintf(pDest, DestSize, "IO(0x%x).%u", (unsigned)Address, (unsigned)BitPos);
  else
    as_snprintf(pDest, DestSize, "[0x%x].%u", (unsigned)Address, (unsigned)BitPos);
}

/*!------------------------------------------------------------------------
 * \fn     ExpandBit_Padauk(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 ExpandBit_Padauk(const tStrComp *pVarName, const struct sStructElem *pStructElem, LargeWord Base)
{
  LongWord Address = Base + pStructElem->Offset;
  Boolean IsIO = (ActPC == SegIO);

  if (!ChkRange(Address, 0, IntTypeDefs[IsIO ? IOAdrIntType : DataBitAdrIntType].Max)
   || !ChkRange(pStructElem->BitPos, 0, 7))
    return;

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

/*!------------------------------------------------------------------------
 * \fn     ImmCode(Word Code13)
 * \brief  translate PDK13 arithmetic op code to actual code code for immediate src
 * \param  Code13 PDK13 code
 * \return Immediate Opcode
 * ------------------------------------------------------------------------ */

static Word ImmCode(Word Code13)
{
  switch (pCurrCPUProps->Core)
  {
    case eCorePDK14:
      return 0x20 | ((Code13 & 0x10) >> 1) | (Code13 & 7);
    case eCorePDK15:
      return 0x40 | Code13;
    case eCorePDK16:
      return 0x08 | Code13;
    default:
      return Code13;
  }
}

/*!------------------------------------------------------------------------
 * \fn     AMCode(Word Code13, Boolean AccDest)
 * \brief  translate PDK13 arithmetic op code to actual code code for Acc<->[M]
 * \param  Code13 PDK13 code
 * \param  AccDest: true if A<-M
 * \return Opcode
 * ------------------------------------------------------------------------ */

static Word AMCode(Word Code13, Boolean AccDest)
{
  switch (pCurrCPUProps->Core)
  {
    case eCorePDK16:
      return (Code13 << 1) | (AccDest ? 1 : 0);
    default:
      return Code13 | (AccDest ? 8 : 0);
  }
}

/*!------------------------------------------------------------------------
 * \fn     MCode(Word Code13)
 * \brief  translate PDK13 arithmetic op code to actual code code for [M]
 * \param  Code13 PDK13 code
 * \return Opcode
 * ------------------------------------------------------------------------ */

static Word MCode(Word Code13)
{
  switch (pCurrCPUProps->Core)
  {
    case eCorePDK16:
      return 0x30 | Code13;
    default:
      return 0x20 | Code13;
  }
}

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

static void DecodeFixed(Word Code)
{
  if (ChkArgCnt(0, 0))
    WAsmCode[CodeLen++] = Code;
}

static void DecodeACC(Word Code)
{
  Word Address;

  if (ChkArgCnt(1, 1)
   && DecodeAdr(&ArgStr[1], MModAcc, DataAdrIntType, &Address))
    WAsmCode[CodeLen++] = Code;
}

static void DecodeSWAP(Word Code)
{
  Word Address;

  if (ChkArgCnt(1, 1))
    switch (DecodeAdr(&ArgStr[1], MModAcc | ((pCurrCPUProps->InstCaps & eInstCap_SWAPM) ? MModMem : 0), DataAdrIntType, &Address))
    {
      case ModAcc:
        WAsmCode[CodeLen++] = Code;
        break;
      case ModMem:
        WAsmCode[CodeLen++] = ((pCurrCPUProps->Core == eCorePDK16 ? 0x3e: 0x0a) << DataMemBits) | Address;
        break;
      default:
        break;
    }
}

static void DecodeADDC_SUBC(Word Code)
{
  Word Address;

  switch (ArgCnt)
  {
    case 1:
      switch (DecodeAdr(&ArgStr[1], MModAcc | MModMem, DataAdrIntType, &Address))
      {
        case ModAcc:
          WAsmCode[CodeLen++] = (AccInstOffs + 0) | Code;
          break;
        case ModMem:
          WAsmCode[CodeLen++] = (MCode(Code) << DataMemBits) | Address;
          break;
        default:
          break;
      }
      break;
    case 2:
      if (!as_strcasecmp(ArgStr[1].str.p_str, "A"))
      {
        if (DecodeAdr(&ArgStr[2], MModMem, DataAdrIntType, &Address))
          WAsmCode[CodeLen++] = (AMCode(0x12 + Code, True) << DataMemBits) | Address;
      }
      else if (!as_strcasecmp(ArgStr[2].str.p_str, "A"))
      {
        if (DecodeAdr(&ArgStr[1], MModMem, DataAdrIntType, &Address))
          WAsmCode[CodeLen++] = (AMCode(0x12 + Code, False) << DataMemBits) | Address;
      }
      break;
    default:
      (void)ChkArgCnt(1, 2);
  }
}

static void DecodeACCOrM(Word Code)
{
  Word Address;

  if (ChkArgCnt(1, 1))
    switch (DecodeAdr(&ArgStr[1], MModMem | MModAcc, DataAdrIntType, &Address))
    {
      case ModAcc:
        WAsmCode[CodeLen++] = AccInstOffs + Code;
        break;
      case ModMem:
        WAsmCode[CodeLen++] = (MCode(Code) << DataMemBits) | Address;
        break;
      default:
        break;
    }
}

static void DecodeDELAY(Word Code)
{
  Word Address;

  if (ChkArgCnt(1, 1))
    switch (DecodeAdr(&ArgStr[1], MModMem | MModAcc | MModImm, DataAdrIntType, &Address))
    {
      case ModAcc:
        WAsmCode[CodeLen++] = AccInstOffs + Code;
        break;
      case ModMem:
        WAsmCode[CodeLen++] = (MCode(Code) << DataMemBits) | Address;
        break;
      case ModImm:
        WAsmCode[CodeLen++] = 0x0e00 | Address;
        break;
      default:
        break;
    }
}

static void DecodeM(Word Code)
{
  Word Address;

  if (ChkArgCnt(1, 1)
   && DecodeAdr(&ArgStr[1], MModMem, DataAdrIntType, &Address))
    WAsmCode[CodeLen++] = (MCode(Code) << DataMemBits) | Address;
}

static void DecodeRET(Word Code)
{
  Word Value;

  UNUSED(Code);

  switch (ArgCnt)
  {
    case 0:
      WAsmCode[CodeLen++] = (AccInstOffs == 0x60) ? 0x7a : 0x3a;
      break;
    case 1:
      if (DecodeAdr(&ArgStr[1], MModImm, DataAdrIntType, &Value))
        WAsmCode[CodeLen++] = Code | Value;
      break;
    default:
      (void)ChkArgCnt(0, 1);
  }
}

static void DecodeXOR(Word Code)
{
  Word DestAddress, SrcAddress,
       OpCode = Lo(Code),
       IOCode = Hi(Code);

  if (ChkArgCnt(2,2))
    switch (DecodeAdr(&ArgStr[1], MModAcc | MModMem | ((pCurrCPUProps->InstCaps & eInstCapXOR_IOA) ? MModIO : 0), DataAdrIntType, &DestAddress))
    {
      case ModAcc:
        switch (DecodeAdr(&ArgStr[2], MModMem | MModImm | ((pCurrCPUProps->Core >= eCorePDK16) ? MModIO : 0), DataAdrIntType, &SrcAddress))
        {
          case ModMem:
            WAsmCode[CodeLen++] = (AMCode(OpCode, True) << DataMemBits) | SrcAddress;
            break;
          case ModImm:
            WAsmCode[CodeLen++] = (ImmCode(OpCode) << 8) | SrcAddress;
            break;
          case ModIO:
            WAsmCode[CodeLen++] = ((IOCode + 1) << IOMemBits) | SrcAddress;
            break;
          default:
            break;
        }
        break;
      case ModMem:
        if (DecodeAdr(&ArgStr[2], MModAcc, DataAdrIntType, &SrcAddress))
          WAsmCode[CodeLen++] = (AMCode(OpCode, False) << DataMemBits) | DestAddress;
        break;
      case ModIO:
        if (DecodeAdr(&ArgStr[2], MModAcc, DataAdrIntType, &SrcAddress))
          WAsmCode[CodeLen++] = (IOCode << IOMemBits) | DestAddress;
        break;
      default:
        break;
    }
}

static void DecodeAccToM(Word Code)
{
  Word DestAddress, SrcAddress;

  if (ChkArgCnt(2, 2))
    switch (DecodeAdr(&ArgStr[1], MModAcc | MModMem, DataAdrIntType, &DestAddress))
    {
      case ModAcc:
        switch (DecodeAdr(&ArgStr[2], MModMem | MModImm, DataAdrIntType, &SrcAddress))
        {
          case ModMem:
            WAsmCode[CodeLen++] = (AMCode(Code, True) << DataMemBits) | SrcAddress;
            break;
          case ModImm:
            WAsmCode[CodeLen++] = (ImmCode(Code) << 8) | SrcAddress;
            break;
          default:
            break;
        }
        break;
      case ModMem:
        if (DecodeAdr(&ArgStr[2], MModAcc, DataAdrIntType, &SrcAddress))
          WAsmCode[CodeLen++] = (AMCode(Code, False) << DataMemBits) | DestAddress;
        break;
      default:
        break;
    }
}

static void DecodeCOMP_NADD_NMOV(Word Code)
{
  Word DestAddress, SrcAddress;

  UNUSED(Code);

  if (ChkArgCnt(2, 2))
    switch (DecodeAdr(&ArgStr[1], MModAcc | MModMem, DataAdrIntType, &DestAddress))
    {
      case ModAcc:
        if (DecodeAdr(&ArgStr[2], MModMem, DataAdrIntType, &SrcAddress))
          WAsmCode[CodeLen++] = (((Code << 1) + 0) << DataMemBits) | SrcAddress;
        break;
      case ModMem:
        if (DecodeAdr(&ArgStr[2], MModAcc, DataAdrIntType, &SrcAddress))
          WAsmCode[CodeLen++] = (((Code << 1) + 1) << DataMemBits) | DestAddress;
        break;
      default:
        break;
    }
}

static void DecodeMOV(Word Code)
{
  Word DestAddress, SrcAddress,
       OpCode = Lo(Code),
       IOCode = Hi(Code) & 15;

  if (ChkArgCnt(2,2))
    switch (DecodeAdr(&ArgStr[1], MModAcc | MModMem | MModIO, DataAdrIntType, &DestAddress))
    {
      case ModAcc:
        switch (DecodeAdr(&ArgStr[2], MModMem | MModImm | MModIO, DataAdrIntType, &SrcAddress))
        {
          case ModMem:
            WAsmCode[CodeLen++] = (AMCode(OpCode, True) << DataMemBits) | SrcAddress;
            break;
          case ModImm:
            WAsmCode[CodeLen++] = (ImmCode(OpCode) << 8) | SrcAddress;
            break;
          case ModIO:
            WAsmCode[CodeLen++] = ((IOCode + 1) << IOMemBits) | SrcAddress;
            break;
          default:
            break;
        }
        break;
      case ModMem:
        if (DecodeAdr(&ArgStr[2], MModAcc, DataAdrIntType, &SrcAddress))
          WAsmCode[CodeLen++] = (AMCode(OpCode, False) << DataMemBits) | DestAddress;
        break;
      case ModIO:
        if (DecodeAdr(&ArgStr[2], MModAcc, DataAdrIntType, &SrcAddress))
          WAsmCode[CodeLen++] = (IOCode << IOMemBits) | DestAddress;
        break;
      default:
        break;
    }
}

static void DecodeGOTO_CALL(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Address = EvalStrIntExpressionWithResult(&ArgStr[1], CodeAdrIntType, &EvalResult);

    if (EvalResult.OK)
    {
      ChkSpace(SegCode, EvalResult.AddrSpaceMask);
      WAsmCode[CodeLen++] = (Code << CodeMemBits) | Address;
    }
  }
}

static void DecodeCnEQSN(Word Code)
{
  Word SrcAddress, DestAddress;

  if (ChkArgCnt(2, 2))
   switch (DecodeAdr(&ArgStr[1], MModAcc | ((pCurrCPUProps->Core >= eCorePDK16) ? MModMem : 0), DataAdrIntType, &DestAddress))
   {
     case ModAcc:
       switch (DecodeAdr(&ArgStr[2], MModMem | MModImm, DataAdrIntType, &SrcAddress))
       {
         case ModMem:
           WAsmCode[CodeLen++] = (Code << DataMemBits) | SrcAddress;
           break;
         case ModImm:
           WAsmCode[CodeLen++] = (ImmCode(0x12 + (Code & 1)) << 8) | SrcAddress;
           break;
         default:
           break;
       }
       break;
     case ModMem:
       if (DecodeAdr(&ArgStr[2], MModAcc, DataAdrIntType, &SrcAddress))
         WAsmCode[CodeLen++] = ((Code ^ 1) << DataMemBits) | DestAddress;
       break;
     default:
       break;
   }
}

static void DecodeEvenAddr(tStrComp *pArg, Word Code1, Word Code2)
{
  Word Address;

  OpSize = eSymbolSize16Bit;

  if (DecodeAdr(pArg, MModMem, (pCurrCPUProps->Core == eCorePDK13) ? UInt5 : DataAdrIntType, &Address))
    WAsmCode[CodeLen++] = (Code1 << ((pCurrCPUProps->Core == eCorePDK13) ? 5 : DataMemBits)) | Code2 | Address;
}

static void DecodeSTT16_LDT16(Word Code)
{
  if (ChkArgCnt(1, 1))
    DecodeEvenAddr(&ArgStr[1], Hi(Code), Lo(Code));
}

static void DecodeLDTAB(Word Code)
{
  if (ChkArgCnt(1, 1))
    DecodeEvenAddr(&ArgStr[1], 0x05, Code);
}

static void DecodeIDXM(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    if (!as_strcasecmp(ArgStr[1].str.p_str, "A"))
      DecodeEvenAddr(&ArgStr[2], Code, 1);
    else if (!as_strcasecmp(ArgStr[2].str.p_str, "A"))
      DecodeEvenAddr(&ArgStr[1], Code, 0);
  }
}

static void DecodeBitOp(Word Code)
{
  LongWord BitSpec;
  Word OpCode = Lo(Code);

  if (DecodeBitArg(&BitSpec, 1, ArgCnt))
  {
    Word Address;
    Byte BitPos;
    Boolean IsIO;

    DissectBitSymbol(BitSpec, &Address, &BitPos, &IsIO);
    if (IsIO)
    {
      if (pCurrCPUProps->Core == eCorePDK16)
        WAsmCode[CodeLen++] = 0x2000 | (OpCode << (3 + IOMemBits)) | (BitPos << IOMemBits) | Address;
      else
        WAsmCode[CodeLen++] = (3 << (5 + IOMemBits)) | (OpCode << (3 + IOMemBits)) | (BitPos << IOMemBits) | Address;
    }
    else if (pCurrCPUProps->Core == eCorePDK13)
      WAsmCode[CodeLen++] = (Code & 0xff00u) | ((OpCode & 2) << (4 - 1 + DataBitMemBits)) | (BitPos << (1 + DataBitMemBits)) | ((Code & 1) << DataBitMemBits) | Address;
    else
      WAsmCode[CodeLen++] = (Code & 0xff00u) | (OpCode << (3 + DataBitMemBits)) | (BitPos << DataBitMemBits) | Address;
  }
}

static void DecodeBitOpIO(Word Code)
{
  LongWord BitSpec;

  if (DecodeBitArg(&BitSpec, 1, ArgCnt))
  {
    Word Address;
    Byte BitPos;
    Boolean IsIO;

    DissectBitSymbol(BitSpec, &Address, &BitPos, &IsIO);
    if (IsIO)
      WAsmCode[CodeLen++] = (Code << (3 + IOMemBits)) | (BitPos << IOMemBits) | Address;
    else
      WrError(ErrNum_InvAddrMode);
  }
}

static void DecodeConstU5(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Word Num = EvalStrIntExpressionOffs(&ArgStr[1], !!(*ArgStr[1].str.p_str == '#'), UInt5, &OK);

    if (OK)
      WAsmCode[CodeLen++] = Code | (Num & 0x1f);
  }
}

static void DecodeConstU4(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Word Num = EvalStrIntExpressionOffs(&ArgStr[1], !!(*ArgStr[1].str.p_str == '#'), UInt4, &OK);

    if (OK)
      WAsmCode[CodeLen++] = Code | (Num & 0x0f);
  }
}

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

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

  if (ActPC == StructSeg)
  {
    Boolean OK;
    Byte BitPos;
    PStructElem pElement;
    tStrComp BitComp, *pBitComp, AddrComp, *pAddrComp;

    switch (ArgCnt)
    {
      case 1:
      {
        char *pSep = strchr(ArgStr[1].str.p_str, '.');
        if (!pSep)
          goto fail;
        StrCompSplitRef(&AddrComp, &BitComp, &ArgStr[1], pSep);
        pBitComp = &BitComp;
        pAddrComp = &AddrComp;
        break;
      }
      case 2:
        pAddrComp = &ArgStr[1];
        pBitComp = &ArgStr[2];
        break;
      default:
      fail:
        WrError(ErrNum_WrongArgCnt);
        return;
    }

    BitPos = EvalBitPosition(pBitComp, &OK);
    if (!OK)
      return;
    pElement = CreateStructElem(&LabPart);
    pElement->pRefElemName = as_strdup(pAddrComp->str.p_str);
    pElement->OpSize = eSymbolSize8Bit;
    pElement->BitPos = BitPos;
    pElement->ExpandFnc = ExpandBit_Padauk;
    AddStructElem(pInnermostNamedStruct->StructRec, pElement);
  }
  else
  {
    LongWord BitSpec;

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

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

  DecodeDATA(CodeWordIntType, DataAdrIntType);
}

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

  code_equate_segment(SegIO);
}

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

static void InitFields(void)
{
  Word Base, MovIOCode, RETCode, SWAPCCode, BitOpCode, XORIOCode, STT16_LDT16Code, IDXMCode, CEQSNCode, CNEQSNCode;
  InstTable = CreateInstTable(203);

  switch (pCurrCPUProps->Core)
  {
    case eCorePDK13:
      MovIOCode = 0x0400;
      RETCode = 0x0100;
      SWAPCCode = 0;
      BitOpCode = 0x0200;
      XORIOCode = 0x0300;
      STT16_LDT16Code = 0x0600;
      IDXMCode = 7;
      CEQSNCode = 0x2e;
      CNEQSNCode = 0x2f;
      break;
    case eCorePDK14:
      MovIOCode = 0x0600;
      RETCode = 0x0200;
      SWAPCCode = 2;
      BitOpCode = 0x2000;
      XORIOCode = 0x0300;
      STT16_LDT16Code = 0x0600;
      IDXMCode = 7;
      CEQSNCode = 0x2e;
      CNEQSNCode = 0x2f;
      break;
    case eCorePDK15:
      MovIOCode = 0x0200;
      RETCode = 0x0200;
      SWAPCCode = 0x17;
      BitOpCode = 0x4000;
      XORIOCode = 0x0100;
      STT16_LDT16Code = 0x0600;
      IDXMCode = 7;
      CEQSNCode = 0x2e;
      CNEQSNCode = 0x2f;
      break;
    case eCorePDK16:
      MovIOCode = 0x0200;
      RETCode = 0x0f00;
      SWAPCCode = 0x17;
      BitOpCode = 0x8000;
      XORIOCode = 0x4000;
      STT16_LDT16Code = 0x0100;
      IDXMCode = 4;
      CNEQSNCode = 0x0b;
      CEQSNCode = 0x1c;
      break;
    default:
      WrError(ErrNum_InternalError);
      exit(255);
  }

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "NOP", NOPCode, DecodeFixed);
  if (pCurrCPUProps->Core <= eCorePDK14)
  {
    AddInstTable(InstTable, "LDSPTL", 0x0006, DecodeFixed);
    AddInstTable(InstTable, "LDSPTH", 0x0007, DecodeFixed);
  }
  else
  {
    AddInstTable(InstTable, "LDTABL", 0, DecodeLDTAB);
    AddInstTable(InstTable, "LDTABH", 1, DecodeLDTAB);
  }
  Base = (AccInstOffs == 0x60) ? 0x70 : 0x30;
  AddInstTable(InstTable, "WDRESET", Base + 0x00, DecodeFixed);
  AddInstTable(InstTable, "PUSHAF" , Base + 0x02, DecodeFixed);
  AddInstTable(InstTable, "POPAF"  , Base + 0x03, DecodeFixed);
  AddInstTable(InstTable, "RESET"  , Base + 0x05, DecodeFixed);
  AddInstTable(InstTable, "STOPSYS", Base + 0x06, DecodeFixed);
  AddInstTable(InstTable, "STOPEXE", Base + 0x07, DecodeFixed);
  AddInstTable(InstTable, "ENGINT" , Base + 0x08, DecodeFixed);
  AddInstTable(InstTable, "DISGINT", Base + 0x09, DecodeFixed);
  AddInstTable(InstTable, "RETI"   , Base + 0x0b, DecodeFixed);
  if (pCurrCPUProps->InstCaps & eInstCapMUL)
    AddInstTable(InstTable, "MUL"    , Base + 0x0c, DecodeFixed);

  AddInstTable(InstTable, "RET"    , RETCode, DecodeRET);

  AddInstTable(InstTable, "ADDC", 0, DecodeADDC_SUBC);
  AddInstTable(InstTable, "SUBC", 1, DecodeADDC_SUBC);
  AddInstTable(InstTable, "IZSN", 2, DecodeACCOrM);
  AddInstTable(InstTable, "DZSN", 3, DecodeACCOrM);
  AddInstTable(InstTable, "NOT" , 8, DecodeACCOrM);
  AddInstTable(InstTable, "NEG" , 9, DecodeACCOrM);
  AddInstTable(InstTable, "SR"  ,10, DecodeACCOrM);
  AddInstTable(InstTable, "SL"  ,11, DecodeACCOrM);
  AddInstTable(InstTable, "SRC" ,12, DecodeACCOrM);
  AddInstTable(InstTable, "SLC" ,13, DecodeACCOrM);
  if (pCurrCPUProps->Core >= eCorePDK16)
    AddInstTable(InstTable, "DELAY", 15, DecodeDELAY);

  AddInstTable(InstTable, "PCADD", AccInstOffs +  7, DecodeACC);
  AddInstTable(InstTable, "SWAP",  AccInstOffs + 14, DecodeSWAP);

  AddInstTable(InstTable, "INC"  , 4, DecodeM);
  AddInstTable(InstTable, "DEC"  , 5, DecodeM);
  AddInstTable(InstTable, "CLEAR", 6, DecodeM);
  AddInstTable(InstTable, "XCH"  , 7, DecodeM);

  if (pCurrCPUProps->InstCaps & eInstCapNADD_AM_MA)
    AddInstTable(InstTable, "COMP", pCurrCPUProps->Core >= eCorePDK16 ? 0x0f : 0x06, DecodeCOMP_NADD_NMOV);
  if (pCurrCPUProps->InstCaps & eInstCapCOMP_AM_MA)
    AddInstTable(InstTable, "NADD", pCurrCPUProps->Core >= eCorePDK16 ? 0x0d : 0x07, DecodeCOMP_NADD_NMOV);
  if (pCurrCPUProps->InstCaps & eInstCapNMOV_AM_MA)
    AddInstTable(InstTable, "NMOV", pCurrCPUProps->Core >= eCorePDK16 ? 0x0c : 0x04, DecodeCOMP_NADD_NMOV);
  AddInstTable(InstTable, "ADD", 0x10, DecodeAccToM);
  AddInstTable(InstTable, "SUB", 0x11, DecodeAccToM);
  AddInstTable(InstTable, "AND", 0x14, DecodeAccToM);
  AddInstTable(InstTable, "OR" , 0x15, DecodeAccToM);
  AddInstTable(InstTable, "XOR", XORIOCode | 0x16, DecodeXOR);
  AddInstTable(InstTable, "MOV", MovIOCode | 0x17, DecodeMOV);
  AddInstTable(InstTable, "CEQSN", CEQSNCode, DecodeCnEQSN);
  if (pCurrCPUProps->Core >= eCorePDK14)
    AddInstTable(InstTable, "CNEQSN", CNEQSNCode | 1, DecodeCnEQSN);

  AddInstTable(InstTable, "STT16", STT16_LDT16Code | 0, DecodeSTT16_LDT16);
  AddInstTable(InstTable, "LDT16", STT16_LDT16Code | 1, DecodeSTT16_LDT16);
  if (pCurrCPUProps->InstCaps & eInstCapPUSHW_POPW)
  {
    AddInstTable(InstTable, "POPW", 0x0200, DecodeSTT16_LDT16);
    AddInstTable(InstTable, "PUSHW", 0x0201, DecodeSTT16_LDT16);
  }
  if (pCurrCPUProps->Core >= eCorePDK16)
  {
    AddInstTable(InstTable, "IGOTO", 0x0300, DecodeSTT16_LDT16);
    AddInstTable(InstTable, "ICALL", 0x0301, DecodeSTT16_LDT16);
  }
  AddInstTable(InstTable, "IDXM" , IDXMCode, DecodeIDXM);

  AddInstTable(InstTable, "GOTO", 6, DecodeGOTO_CALL);
  AddInstTable(InstTable, "CALL", 7, DecodeGOTO_CALL);

  AddInstTable(InstTable, "T0SN", BitOpCode | 0, DecodeBitOp);
  AddInstTable(InstTable, "T1SN", BitOpCode | 1, DecodeBitOp);
  AddInstTable(InstTable, "SET0", BitOpCode | 2, DecodeBitOp);
  AddInstTable(InstTable, "SET1", BitOpCode | 3, DecodeBitOp);
  if (pCurrCPUProps->Core >= eCorePDK16)
  {
    AddInstTable(InstTable, "TOG", 0x14, DecodeBitOpIO);
    AddInstTable(InstTable, "WAIT0", 0x15, DecodeBitOpIO);
    AddInstTable(InstTable, "WAIT1", 0x16, DecodeBitOpIO);
  }
  if (SWAPCCode)
    AddInstTable(InstTable, "SWAPC", SWAPCCode, DecodeBitOpIO);

  if (pCurrCPUProps->InstCaps & eInstCapPMODE)
    AddInstTable(InstTable, "PMODE", 0x0040, DecodeConstU5);
  if (pCurrCPUProps->InstCaps & eInstCapPUSHW_POPW)
  {
    AddInstTable(InstTable, "POPWPC" , 0x0060, DecodeConstU4);
    AddInstTable(InstTable, "PUSHWPC", 0x0070, DecodeConstU4);
  }

  AddInstTable(InstTable, "BIT", 0, DecodeBIT);
  AddInstTable(InstTable, "DATA", 0, DecodeDATA_Padauk);
  AddInstTable(InstTable, "SFR", 0, DecodeSFR);
  AddInstTable(InstTable, "RES", 0, DecodeRES);
}

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

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

static void MakeCode_Padauk(void)
{
  OpSize = eSymbolSize8Bit;

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

static Boolean IsDef_Padauk(void)
{
  return Memo("BIT")
      || Memo("SFR");
}

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

static void SwitchTo_Padauk(void *pUser)
{
  const TFamilyDescr *p_descr;
  static const char CommentLeadIn[] = { ';', '\0', '/', '/', '\0', '\0' };

  pCurrCPUProps = (const tCPUProps*)pUser;

  TurnWords = False;
  SetIntConstMode(eIntConstModeC);

  switch (pCurrCPUProps->Core)
  {
    case eCorePDK13:
      p_descr = FindFamilyByName("PDK13");
      DataBitAdrIntType = UInt4;
      IOMemBits = 5;
      break;
    case eCorePDK14:
      p_descr = FindFamilyByName("PDK14");
      DataBitAdrIntType = UInt6;
      IOMemBits = 6;
      break;
    case eCorePDK15:
      p_descr = FindFamilyByName("PDK15");
      DataBitAdrIntType = UInt7;
      IOMemBits = 7;
      break;
    case eCorePDK16:
      p_descr = FindFamilyByName("PDK16");
      DataBitAdrIntType = UInt9;
      IOMemBits = 6;
      break;
    default:
      p_descr = NULL;
  }

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

  ValidSegs = (1 << SegCode) | (1 << SegData) | (1 << SegIO);
  Grans[SegCode] = 2; ListGrans[SegCode] = 2; SegInits[SegCode] = 0;
  Grans[SegData] = 1; ListGrans[SegData] = 1; SegInits[SegData] = 0; SegLimits[SegData] = pCurrCPUProps->RAMEnd;
  Grans[SegIO  ] = 1; ListGrans[SegIO  ] = 1; SegInits[SegIO  ] = 0; SegLimits[SegIO  ] = pCurrCPUProps->IOAreaEnd;

  SegLimits[SegCode] = ((LongWord)pCurrCPUProps->FlashEndD16) << 4 | 0xf;
  CodeAdrIntType = GetSmallestUIntType(SegLimits[SegCode]);
  CodeWordIntType = GetUIntTypeByBits(13 + (pCurrCPUProps->Core - eCorePDK13));
  DataAdrIntType = GetSmallestUIntType(SegLimits[SegData]);
  IOAdrIntType   = GetSmallestUIntType(SegLimits[SegIO  ]);
  DataBitMemBits = Lo(IntTypeDefs[DataBitAdrIntType].SignAndWidth);
  DataMemBits = 6 + (pCurrCPUProps->Core - eCorePDK13);
  CodeMemBits = 10 + (pCurrCPUProps->Core - eCorePDK13);

#if 0
  fprintf(stderr, "Data 0x%lx DataBit 0x%lx Code 0x%lx IO 0x%lx\n",
          IntTypeDefs[DataAdrIntType].Max,
          IntTypeDefs[DataBitAdrIntType].Max,
          IntTypeDefs[CodeAdrIntType].Max,
          IntTypeDefs[IOAdrIntType].Max);
#endif

  AccInstOffs = CoreMask((1 << eCorePDK14) | (1 << eCorePDK15)) ? 0x0060 : 0x0010;

  MakeCode = MakeCode_Padauk;
  IsDef = IsDef_Padauk;
  SwitchFrom = SwitchFrom_Padauk;
  DissectBit = DissectBit_Padauk;
  InitFields();
}

static const tCPUProps CPUProps[] =
{
  { "PMC150"  , 0x3f, 0x3f, 0x1f, eCorePDK13,                                                         eInstCapXOR_IOA                                                                            },
  { "PMS150"  , 0x3f, 0x3f, 0x1f, eCorePDK13,                                                         eInstCapXOR_IOA                                                                            },
  { "PFS154"  , 0x7f, 0x7f, 0x3f, eCorePDK14,                                                         eInstCapXOR_IOA                                                                            },
  { "PMC131"  , 0x5f, 0x57, 0x3f, eCorePDK14, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA                                                                                              },
  { "PMS130"  , 0x5f, 0x57, 0x3f, eCorePDK14, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA                                                                                              },
  { "PMS131"  , 0x5f, 0x57, 0x3f, eCorePDK14, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA                                                                                              },
  { "PMS132"  , 0x7f, 0x7f, 0x3f, eCorePDK14, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA | eInstCapXOR_IOA                                                                            },
  { "PMS132B" , 0x7f, 0x7f, 0x3f, eCorePDK14, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA | eInstCapXOR_IOA                                                                            },
  { "PMS152"  , 0x4f, 0x4f, 0x3f, eCorePDK14, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA                                                                            },
  { "PMS154B" , 0x7f, 0x7f, 0x3f, eCorePDK14, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA                                                                            },
  { "PMS154C" , 0x7f, 0x7f, 0x3f, eCorePDK14, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA                                                                            },
  { "PFS173"  , 0xbf, 0xff, 0x7f, eCorePDK15, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA                                                                                              },
  { "PMS133"  , 0xbf, 0xff, 0x7f, eCorePDK15, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM                                      },
  { "PMS134"  , 0xff, 0xff, 0x7f, eCorePDK15, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM                                      },
  { "DF69"    , 0xff, 0xcf, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM | eInstCapPUSHW_POPW | eInstCapPMODE },
  { "MCS11"   , 0xff, 0xcf, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM | eInstCapPUSHW_POPW | eInstCapPMODE },
  { "PMC232"  , 0x7f, 0x9f, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM                                      },
  { "PMC234"  , 0xff, 0xcf, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM                                      },
  { "PMC251"  , 0x3f, 0x3f, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM                                      },
  { "PMC271"  , 0x3f, 0x3f, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM                                      },
  { "PMC884"  , 0xff, 0xff, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA | eInstCapMUL | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM | eInstCapPUSHW_POPW | eInstCapPMODE },
  { "PMS232"  , 0x7f, 0x9f, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM                                      },
  { "PMS234"  , 0xff, 0xcf, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM                                      },
  { "PMS271"  , 0x3f, 0x3f, 0x3f, eCorePDK16, eInstCapCOMP_AM_MA               | eInstCapNADD_AM_MA | eInstCapXOR_IOA | eInstCapNMOV_AM_MA | eInstCap_SWAPM                                      },
  { NULL      , 0x00, 0x00, 0x00, eCoreNone , 0 }
};

void codepdk_init(void)
{
  const tCPUProps *pProp;

  for (pProp = CPUProps; pProp->pName; pProp++)
    (void)AddCPUUser(pProp->pName, SwitchTo_Padauk, (void*)pProp, NULL);
}
