/* code53c8xx.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* Makroassembler AS                                                         */
/*                                                                           */
/* Codegenerator SYM53C8xx                                                   */
/*                                                                           */
/*****************************************************************************/

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

#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmitree.h"
#include "headids.h"
#include "strutil.h"
#include "codevars.h"
#include "codepseudo.h"
#include "intpseudo.h"
#include "errmsg.h"

#include "code53c8xx.h"

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

typedef struct
{
  const char *Name;
  LongWord Code;
  Word Mask;
} TReg, *PReg;

static CPUVar CPU53C810, CPU53C860, CPU53C815, CPU53C825, CPU53C875,
              CPU53C895;

#define M_53C810 0x0001
#define M_53C860 0x0002
#define M_53C815 0x0004
#define M_53C825 0x0008
#define M_53C875 0x0010
#define M_53C895 0x0020

static PReg Regs;

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

static Boolean IsInToken(char Inp)
{
  return ((Inp == '_') || (isalnum(((usint) Inp) & 0xff)));
}

static void GetToken(tStrComp *pSrc, tStrComp *pDest)
{
  char *p;

  /* search token start, by skipping spaces */

  for (p = pSrc->str.p_str; as_isspace(*p); p++)
    if (*p == '\0') break;
  StrCompCutLeft(pSrc, p - pSrc->str.p_str);
  if (*p == '\0')
  {
    StrCompReset(pDest);
    return;
  }
  pDest->Pos.StartCol = pSrc->Pos.StartCol;

  /* geklammerter Ausdruck ? */

  if (*pSrc->str.p_str == '(')
  {
    p = QuotPos(pSrc->str.p_str, ')');

    /* no closing ) -> copy all up to end */

    if (!p)
    {
      StrCompCopy(pDest, pSrc);
      StrCompCutLeft(pSrc, strlen(pSrc->str.p_str));
    }

    /* otherwise, copy (...) */

    else
    {
      pDest->Pos.Len = strmemcpy(pDest->str.p_str, STRINGSIZE, pSrc->str.p_str, p + 1 - pSrc->str.p_str);
      StrCompCutLeft(pSrc, p + 1 - pSrc->str.p_str);
    }
  }

  /* Spezialtoken ? */

  else if (!IsInToken(*pSrc->str.p_str))
  {
    pDest->str.p_str[0] = *pSrc->str.p_str;
    pDest->str.p_str[1] = '\0';
    pDest->Pos.Len = 1;
    StrCompCutLeft(pSrc, 1);
  }

  /* Wort ? */

  else
  {
    for (; IsInToken(*p); p++)
      if (*p == '\0')
        break;
    pDest->Pos.Len = strmemcpy(pDest->str.p_str, STRINGSIZE, pSrc->str.p_str, p - pSrc->str.p_str);
    StrCompCutLeft(pSrc, p - pSrc->str.p_str);
  }
}


static Boolean DecodePhase(char *Name, LongWord *Result)
{
  *Result = 8;
  if (!as_strcasecmp(Name, "DATA_OUT")) *Result = 0;
  else if (!as_strcasecmp(Name, "DATA_IN")) *Result = 1;
  else if (!as_strcasecmp(Name, "CMD")) *Result = 2;
  else if (!as_strcasecmp(Name, "COMMAND")) *Result = 2;
  else if (!as_strcasecmp(Name, "STATUS")) *Result = 3;
  else if (!as_strcasecmp(Name, "RES4")) *Result = 4;
  else if (!as_strcasecmp(Name, "RES5")) *Result = 5;
  else if (!as_strcasecmp(Name, "MSG_OUT")) *Result = 6;
  else if (!as_strcasecmp(Name, "MSG_IN")) *Result = 7;
  else if (!as_strcasecmp(Name, "MESSAGE_OUT")) *Result = 6;
  else if (!as_strcasecmp(Name, "MESSAGE_IN")) *Result = 7;
  return (*Result < 8);
}

static Boolean DecodeReg(char *Name, LongWord *Result)
{
  Integer Mask = 1 << (MomCPU - CPU53C810);
  PReg Reg;

  for (Reg = Regs; Reg->Name; Reg++)
    if (!(as_strcasecmp(Reg->Name, Name)) && (Mask & Reg->Mask))
    {
      *Result = Reg->Code;
      return True;
    }

  return False;
}

static Boolean Err(tErrorNum Num, const char *Msg, const tStrComp *p_arg)
{
  WrXErrorPos(Num, Msg, &p_arg->Pos);
  return False;
}

static Boolean DecodeCond(tStrComp *pSrc, LongWord *Dest)
{
  String TokStr;
  tStrComp Tok;
  Boolean PhaseATNUsed, DataUsed, CarryUsed, MaskUsed;
  LongWord Tmp;
  Boolean OK;

  /* IF/WHEN/Nix-Unterscheidung - TRUE fuer Nix setzen */

  StrCompMkTemp(&Tok, TokStr, sizeof(TokStr));
  GetToken(pSrc, &Tok);
  if (*Tok.str.p_str == '\0')
  {
    *Dest |= 0x00080000;
    return True;
  }

  if (as_strcasecmp(Tok.str.p_str, "WHEN") == 0)
    *Dest |= 0x00010000;
  else if (as_strcasecmp(Tok.str.p_str, "IF") != 0)
    return Err(ErrNum_OpTypeMismatch, Tok.str.p_str, &Tok);

  /* Negierung? */

  GetToken(pSrc, &Tok);
  if (as_strcasecmp(Tok.str.p_str, "NOT") == 0)
    GetToken(pSrc, &Tok);
  else
    *Dest |= 0x00080000;

  /* Bedingungen durchgehen */

  PhaseATNUsed = DataUsed = MaskUsed = CarryUsed = False;
  do
  {
    if (!as_strcasecmp(Tok.str.p_str, "ATN"))
    {
      if (PhaseATNUsed)
        return Err(ErrNum_InvAddrMode, "2 x ATN/Phase", &Tok);
      if (CarryUsed)
        return Err(ErrNum_InvAddrMode, "Carry + ATN/Phase", &Tok);
      if ((*Dest & 0x00010000) != 0)
        return Err(ErrNum_InvAddrMode, "WHEN + ATN", &Tok);
      PhaseATNUsed = True;
      *Dest |= 0x00020000;
    }
    else if (DecodePhase(Tok.str.p_str, &Tmp))
    {
      if (PhaseATNUsed)
        return Err(ErrNum_InvAddrMode, "2 x ATN/Phase", &Tok);
      if (CarryUsed)
        return Err(ErrNum_InvAddrMode, "Carry + ATN/Phase", &Tok);
      PhaseATNUsed = True;
      *Dest |= 0x00020000 + (Tmp << 24);
    }
    else if (!as_strcasecmp(Tok.str.p_str, "CARRY"))
    {
      if (CarryUsed)
        return Err(ErrNum_InvAddrMode, "2 x Carry", &Tok);
      if ((PhaseATNUsed) || (DataUsed))
        return Err(ErrNum_InvAddrMode, "Carry + ...", &Tok);
      CarryUsed = True;
      *Dest |= 0x00200000;
    }
    else if (!as_strcasecmp(Tok.str.p_str, "MASK"))
    {
      if (CarryUsed)
        return Err(ErrNum_InvAddrMode, "Carry + Data", &Tok);
      if (MaskUsed)
        return Err(ErrNum_InvAddrMode, "2 x Mask", &Tok);
      if (!DataUsed)
        return Err(ErrNum_InvAddrMode, "Mask + !Data", &Tok);
      GetToken(pSrc, &Tok);
      Tmp = EvalStrIntExpression(&Tok, UInt8, &OK);
      if (!OK)
        return False;
      MaskUsed = True;
      *Dest |= (Tmp << 8);
    }
    else
    {
      if (CarryUsed)
        return Err(ErrNum_InvAddrMode, "Carry + Data", &Tok);
      if (DataUsed)
        return Err(ErrNum_InvAddrMode, "2 x Data", &Tok);
      Tmp = EvalStrIntExpression(&Tok, UInt8, &OK);
      if (!OK)
        return False;
      DataUsed = True;
      *Dest |= 0x00040000 + Tmp;
    }
    GetToken(pSrc, &Tok);
    if (*Tok.str.p_str != '\0')
    {
      if (as_strcasecmp(Tok.str.p_str, "AND"))
        return Err(ErrNum_InvAddrMode, Tok.str.p_str, &Tok);
      GetToken(pSrc, &Tok);
    }
  }
  while (*Tok.str.p_str != '\0');

  return True;
}

typedef enum
{
  NONE, SFBR, REGISTER, IMM8
} CompType;

static CompType DecodeComp(const tStrComp *pInp, LongWord *Outp)
{
  Boolean OK;

  if (!as_strcasecmp(pInp->str.p_str, "SFBR"))
    return SFBR;
  else if (DecodeReg(pInp->str.p_str, Outp))
    return REGISTER;
  else
  {
    *Outp = EvalStrIntExpression(pInp, Int8, &OK) & 0xff;
    return (OK) ?  IMM8 : NONE;
  }
}

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

static void DecodeFixed(Word Index)
{
  if (ChkArgCnt(0, 0))
  {
    DAsmCode[0] = ((LongWord) Index) << 24;
    DAsmCode[1] = 0x00000000;
    CodeLen = 8;
  }
}

static void DecodeJmps(Word Index)
{
  LongWord Buf;
  LongInt Adr;
  int l;
  Boolean OK;
  tSymbolFlags Flags;

  if (ArgCnt == 0)
  {
    if (Memo("INTFLY"))
    {
      AppendArg(1);
      strcpy(ArgStr[1].str.p_str, "0");
    }
    else if (Memo("RETURN"))
    {
      AppendArg(1);
      *ArgStr[1].str.p_str = '\0';
    }
  }
  if (ChkArgCnt(1, 2))
  {
    if (ArgCnt == 1)
    {
      AppendArg(strlen(ArgStr[1].str.p_str));
      if (Memo("RETURN"))
        StrCompCopy(&ArgStr[2], &ArgStr[1]);
      else
        StrCompReset(&ArgStr[2]);
    }
    Buf = 0;
    if (Memo("RETURN"))
    {
      Adr = 0;
      OK = True;
    }
    else
    {
      l = strlen(ArgStr[1].str.p_str);
      if ((!as_strncasecmp(ArgStr[1].str.p_str, "REL(", 4)) && (ArgStr[1].str.p_str[l - 1] == ')'))
      {
        if (*OpPart.str.p_str == 'I')
        {
          WrError(ErrNum_InvAddrMode);
          OK = False;
        }
        Buf |= 0x00800000;
        strmov(ArgStr[1].str.p_str, ArgStr[1].str.p_str + 4);
        ArgStr[1].str.p_str[l - 5] = '\0';
      }
      Adr = EvalStrIntExpressionWithFlags(&ArgStr[1], UInt32, &OK, &Flags);
      if ((OK) && (Buf != 0))
      {
        Adr -= EProgCounter() + 8;
        if (!mSymbolQuestionable(Flags) && ((Adr > 0x7fffff) || (Adr < -0x800000)))
        {
          WrError(ErrNum_JmpDistTooBig);
          OK = False;
        }
      }
    }
    if ((OK) && (DecodeCond(&ArgStr[2], &Buf)))
    {
      DAsmCode[0] = 0x80000000 + (((LongWord) Index) << 27) + Buf;
      if (Memo("INTFLY")) DAsmCode[0] |= 0x00100000;
      DAsmCode[1] = Adr;
      CodeLen = 8;
    }
  }
}

static void DecodeCHMOV(Word Index)
{
  String TokenStr;
  tStrComp Token, *pAdrArg = NULL;
  LongWord Phase;
  Boolean OK;

  UNUSED(Index);
  StrCompMkTemp(&Token, TokenStr, sizeof(TokenStr));

  if ((ChkExactCPUList(ErrNum_InstructionNotSupported, CPU53C825, CPU53C875, CPU53C895, CPUNone) >= 0)
   && ChkArgCnt(2, 3))
  {
    GetToken(&ArgStr[ArgCnt], &Token);
    if (!as_strcasecmp(Token.str.p_str, "WITH"))
      DAsmCode[0] = 0x08000000;
    else if (!as_strcasecmp(Token.str.p_str, "WHEN"))
      DAsmCode[0] = 0x00000000;
    else
    {
      WrStrErrorPos(ErrNum_InvAddrMode, &Token);
      return;
    }
    KillPrefBlanksStrComp(&ArgStr[ArgCnt]);
    KillPostBlanksStrComp(&ArgStr[ArgCnt]);
    if (!DecodePhase(ArgStr[ArgCnt].str.p_str, &Phase)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[ArgCnt]);
    else
    {
      DAsmCode[0] |= Phase << 24;
      OK = False;
      if (ArgCnt == 2)
      {
        GetToken(&ArgStr[1], &Token);
        if (as_strcasecmp(Token.str.p_str, "FROM")) WrError(ErrNum_InvAddrMode);
        else
        {
          pAdrArg = &ArgStr[1];
          DAsmCode[0] |= 0x10000000;
          OK = True;
        }
      }
      else
      {
        Phase = EvalStrIntExpression(&ArgStr[1], UInt24, &OK);
        if (OK)
        {
          DAsmCode[0] |= Phase;
          if (!as_strncasecmp(ArgStr[2].str.p_str, "PTR", 3))
          {
            StrCompCutLeft(&ArgStr[2], 4);
            DAsmCode[0] |= 0x20000000;
          }
          pAdrArg = &ArgStr[2];
        }
      }
      if (OK)
      {
        KillPrefBlanksStrComp(pAdrArg);
        DAsmCode[1] = EvalStrIntExpression(pAdrArg, UInt32, &OK);
        if (OK)
          CodeLen = 8;
      }
    }
  }
}

static Boolean TrueFnc(void)
{
  return True;
}

static void DecodeFlags(Word Index)
{
  Boolean OK;
  String TokenStr;
  tStrComp Token;

  StrCompMkTemp(&Token, TokenStr, sizeof(TokenStr));
  if (ChkArgCnt(1, 1))
  {
    OK = True;
    DAsmCode[0] = ((LongWord) Index) << 24;
    DAsmCode[1] = 0;
    while ((OK) && (*ArgStr[1].str.p_str != '\0'))
    {
      GetToken(&ArgStr[1], &Token);
      if (!as_strcasecmp(Token.str.p_str, "ACK"))
        DAsmCode[0] |= 0x00000040;
      else if (!as_strcasecmp(Token.str.p_str, "ATN"))
        DAsmCode[0] |= 0x00000008;
      else if (!as_strcasecmp(Token.str.p_str, "TARGET"))
        DAsmCode[0] |= 0x00000200;
      else if (!as_strcasecmp(Token.str.p_str, "CARRY"))
        DAsmCode[0] |= 0x00000400;
      else
      {
        OK = False;
        WrStrErrorPos(ErrNum_InvAddrMode, &Token);
      }
      if (OK && (*ArgStr[1].str.p_str != '\0'))
      {
        GetToken(&ArgStr[1], &Token);
        if (as_strcasecmp(Token.str.p_str, "AND"))
        {
          OK = False;
          WrStrErrorPos(ErrNum_InvAddrMode, &Token);
        }
      }
    }
    if (OK)
      CodeLen = 8;
  }
}

static void DecodeRegTrans(Word Index)
{
  LongWord Reg, Cnt;
  Boolean OK;

  if (!ChkArgCnt(3, 3));
  else if (!ChkExcludeCPU(CPU53C815));
  else if (!DecodeReg(ArgStr[1].str.p_str, &Reg)) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
  else
  {
    tSymbolFlags Flags;

    Cnt = EvalStrIntExpressionWithFlags(&ArgStr[2], UInt3, &OK, &Flags);
    if (mFirstPassUnknown(Flags))
      Cnt = 1;
    if ((OK) && (ChkRange(Cnt, 1, 4)))
    {
      int l = strlen(ArgStr[3].str.p_str);
      DAsmCode[0] = 0xe0000000 + (((LongInt) Index) << 24) + (Reg << 16) + Cnt;
      if ((!as_strncasecmp(ArgStr[3].str.p_str, "DSAREL(", 7))
       && (ArgStr[3].str.p_str[l - 1] == ')'))
      {
        ArgStr[3].str.p_str[--l] = '\0';
        DAsmCode[0] |= 0x10000000;
        DAsmCode[1] = EvalStrIntExpressionOffs(&ArgStr[3], 7, SInt24, &OK) & 0xffffff;
      }
      else
        DAsmCode[1] = EvalStrIntExpression(&ArgStr[3], UInt32, &OK);
      if (OK)
        CodeLen = 8;
    }
  }
}

static void DecodeMOVE(Word Index)
{
#define MAXPARTS 8
  Boolean WithCarry;
  String TokenStr;
  tStrComp Token;
  LongWord Tmp, DReg , AriOp = 0xff, ImmVal = 0x100;
  Boolean OK;
  int z;
  Word BigCPUMask = (1 << (CPU53C825 - CPU53C810))
                  | (1 << (CPU53C875 - CPU53C810))
                  | (1 << (CPU53C895 - CPU53C810));

  UNUSED(Index);
  StrCompMkTemp(&Token, TokenStr, sizeof(TokenStr));

  if (!ChkArgCnt(1, 3));
  else if (ArgCnt == 1) /* MOVE Register */
  {
    String PartStr[MAXPARTS];
    tStrComp Parts[MAXPARTS];
    int PartCnt = 0;

    for (z = 0; z < MAXPARTS; z++)
      StrCompMkTemp(&Parts[z], PartStr[z], sizeof(PartStr[z]));
    do
    {
      GetToken(&ArgStr[1], &Parts[PartCnt++]);
    }
    while ((*ArgStr[1].str.p_str != '\0') && (PartCnt < MAXPARTS));
    if ((PartCnt > 1) && (!as_strcasecmp(Parts[PartCnt - 1].str.p_str, "CARRY")) && (!as_strcasecmp(Parts[PartCnt - 1].str.p_str, "TO")))
    {
      WithCarry = True;
      PartCnt -= 2;
    }
    else
      WithCarry = False;
    DAsmCode[0] = 0x40000000;
    DAsmCode[1] = 0;
    if (PartCnt == 3)
    {
      if (WithCarry) WrError(ErrNum_InvAddrMode);
      else if (!as_strcasecmp(Parts[1].str.p_str, "TO")) /* MOVE */
      {
        switch (DecodeComp(&Parts[0], &ImmVal))
        {
          case SFBR:
            switch (DecodeComp(&Parts[2], &ImmVal))
            {
              case SFBR:
                ImmVal = 8;
                /* fall-through */
              case REGISTER: /* --> 0x00 OR SFBR to reg */
                DAsmCode[0] += 0x2a000000 + (ImmVal << 16);
                CodeLen = 8;
                break;
              default:
                WrError(ErrNum_InvAddrMode);
            }
            break;
          case REGISTER:
            DReg = ImmVal;
            switch (DecodeComp(&Parts[2], &ImmVal))
            {
              case SFBR: /* --> 0x00 OR reg to SFBR */
                DAsmCode[0] += 0x32000000 + (DReg << 16);
                CodeLen = 8;
                break;
              case REGISTER:
                if (ImmVal != DReg) WrError(ErrNum_InvAddrMode);
                else
                {
                  DAsmCode[0] += 0x3a000000 + (DReg << 16);
                  CodeLen = 8;
                }
                break;
              default:
                WrError(ErrNum_InvAddrMode);
            }
            break;
          case IMM8:
            switch (DecodeComp(&Parts[2], &DReg))
            {
              case SFBR:
                DReg = 8;
                /* fall-through */
              case REGISTER: /* --> imm to reg */
                DAsmCode[0] += 0x38000000 + (DReg << 16) + (ImmVal << 8);
                CodeLen = 8;
                break;
              default:
                WrError(ErrNum_InvAddrMode);
            }
            break;
          default:
            WrError(ErrNum_InvAddrMode);
            break;
        }
      } /* ... TO ... */
      else if ((!as_strcasecmp(Parts[1].str.p_str, "SHL")) || (!as_strcasecmp(Parts[1].str.p_str, "SHR")))
      {
        AriOp = 1 + (Ord(as_toupper(Parts[1].str.p_str[2]) == 'R') << 2);
        switch (DecodeComp(&Parts[0], &DReg))
        {
           case SFBR:
             switch (DecodeComp(&Parts[2], &DReg))
             {
               case SFBR:
                 DReg = 8;
                 /* fall-through */
               case REGISTER:
                 DAsmCode[0] += 0x28000000 + (AriOp << 24) + (DReg << 16);
                 CodeLen = 8;
                 break;
               default:
                 WrError(ErrNum_InvAddrMode);
             }
             break;
           case REGISTER:
             ImmVal = DReg;
             switch (DecodeComp(&Parts[2], &DReg))
             {
               case SFBR:
                 DAsmCode[0] += 0x30000000 + (AriOp << 24) + (ImmVal << 16);
                 CodeLen = 8;
                 break;
               case REGISTER:
                 if (DReg != ImmVal) WrError(ErrNum_InvAddrMode);
                 else
                 {
                   DAsmCode[0] += 0x38000000 + (AriOp << 24) + (ImmVal << 16);
                   CodeLen = 8;
                 }
                 break;
               default:
                 WrError(ErrNum_InvAddrMode);
             }
             break;
          default:
            WrError(ErrNum_InvAddrMode);
        }
      } /* ... SHx ... */
    } /* PartCnt == 3 */
    else if (PartCnt == 5)
    {
      if (as_strcasecmp(Parts[3].str.p_str, "TO")) WrStrErrorPos(ErrNum_InvAddrMode, &Parts[3]);
      else
      {
        if ((!as_strcasecmp(Parts[1].str.p_str, "XOR"))
         || (!as_strcasecmp(Parts[1].str.p_str, "^")))
          AriOp = 3;
        else if ((!as_strcasecmp(Parts[1].str.p_str, "OR"))
              || (!as_strcasecmp(Parts[1].str.p_str, "|")))
          AriOp = 2;
        else if ((!as_strcasecmp(Parts[1].str.p_str, "AND"))
              || (!as_strcasecmp(Parts[1].str.p_str, "&")))
          AriOp = 4;
        else if (!strcmp(Parts[1].str.p_str, "+"))
          AriOp = 6;
        if (WithCarry)
          AriOp = (AriOp == 6) ? 7 : 0xff;
        if (AriOp == 0xff) WrStrErrorPos(ErrNum_InvAddrMode, &Parts[1]);
        else
        {
          DAsmCode[0] |= (AriOp << 24);
          switch (DecodeComp(&Parts[0], &ImmVal))
          {
            case SFBR:
              switch (DecodeComp(&Parts[2], &ImmVal))
              {
                case SFBR:
                  switch (DecodeComp(&Parts[4], &ImmVal))
                  {
                    case SFBR:
                      ImmVal = 8;
                      /* fall-through */
                    case REGISTER:
                      if (ChkExactCPUMask(BigCPUMask, CPU53C810) >= 0)
                      {
                        DAsmCode[0] |= 0x28800000 + (ImmVal << 16);
                        CodeLen = 8;
                      }
                      break;
                    default:
                      WrStrErrorPos(ErrNum_InvAddrMode, &Parts[4]);
                  }
                  break;
                case IMM8:
                  switch (DecodeComp(&Parts[4], &DReg))
                  {
                    case SFBR:
                      DReg = 8;
                      /* fall-through */
                    case REGISTER:
                      DAsmCode[0] |= 0x28000000 + (DReg << 16) + (ImmVal << 8);
                      CodeLen = 8;
                      break;
                    default:
                      WrStrErrorPos(ErrNum_InvAddrMode, &Parts[4]);
                  }
                  break;
                default:
                  WrStrErrorPos(ErrNum_InvAddrMode, &Parts[2]);
              }
              break;
            case REGISTER:
              DAsmCode[0] |= ImmVal << 16;
              DReg = ImmVal;
              switch (DecodeComp(&Parts[2], &ImmVal))
              {
                case SFBR:
                  switch (DecodeComp(&Parts[4], &ImmVal))
                  {
                    case SFBR:
                      if (ChkExactCPUMask(BigCPUMask, CPU53C810) >= 0)
                      {
                        DAsmCode[0] |= 0x30800000;
                        CodeLen = 8;
                      }
                      break;
                    case REGISTER:
                      if (DReg != ImmVal) WrError(ErrNum_InvAddrMode);
                      else if (ChkExactCPUMask(BigCPUMask, CPU53C810) >= 0)
                      {
                        DAsmCode[0] |= 0x38800000;
                        CodeLen = 8;
                      }
                      break;
                    default:
                      WrStrErrorPos(ErrNum_InvAddrMode, &Parts[4]);
                  }
                  break;
                case IMM8:
                  DAsmCode[0] |= (ImmVal << 8);
                  switch (DecodeComp(&Parts[4], &Tmp))
                  {
                    case SFBR:
                      DAsmCode[0] |= 0x30000000;
                      CodeLen = 8;
                      break;
                    case REGISTER:
                      if (DReg != Tmp) WrStrErrorPos(ErrNum_InvAddrMode, &Parts[4]);
                      else
                      {
                        DAsmCode[0] |= 0x38000000;
                        CodeLen = 8;
                      }
                      break;
                    default:
                      WrStrErrorPos(ErrNum_InvAddrMode, &Parts[4]);
                  }
                  break;
                default:
                  WrStrErrorPos(ErrNum_InvAddrMode, &Parts[2]);
              }
              break;
            default:
              WrStrErrorPos(ErrNum_InvAddrMode, &Parts[0]);
          }
        }
      }
    }  /* PartCnt == 5 */
    else
      WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
  }
  else if (ArgCnt == 2)
  {
    GetToken(&ArgStr[1], &Token);
    if (as_strcasecmp(Token.str.p_str, "FROM")) WrError(ErrNum_InvAddrMode);
    else
    {
      DAsmCode[0] = 0x00000000;
      DAsmCode[1] = EvalStrIntExpression(&ArgStr[1], Int32, &OK);
      if (OK)
      {
        GetToken(&ArgStr[2], &Token);
        OK = True;
        if (!as_strcasecmp(Token.str.p_str, "WHEN"))
          DAsmCode[0] |= 0x08000000;
        else if (as_strcasecmp(Token.str.p_str, "WITH"))
          OK = False;
        if (!OK) WrError(ErrNum_InvAddrMode);
        else
        {
          KillPrefBlanksStrComp(&ArgStr[2]);
          KillPostBlanksStrComp(&ArgStr[2]);
          if (!DecodePhase(ArgStr[2].str.p_str, &ImmVal)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
          else
          {
            DAsmCode[0] |= ImmVal << 24;
            CodeLen = 8;
          }
        }
      }
    }
  }
  else if (ArgCnt == 3)
  {
    if (!as_strncasecmp(ArgStr[1].str.p_str, "MEMORY", 6))
    {
      StrCompCutLeft(&ArgStr[1], 7);
      if (!as_strncasecmp(ArgStr[1].str.p_str, "NO FLUSH", 8))
      {
        DAsmCode[0] = 0xc1000000;
        StrCompCutLeft(&ArgStr[1], 9);
      }
      else DAsmCode[0] = 0xc0000000;
      DAsmCode[0] |= EvalStrIntExpression(&ArgStr[1], UInt24, &OK);
      if (OK)
      {
        DAsmCode[1] = EvalStrIntExpression(&ArgStr[2], Int32, &OK);
        if (OK)
        {
          DAsmCode[2] = EvalStrIntExpression(&ArgStr[3], Int32, &OK);
          if (OK) CodeLen = 12;
        }
      }
    }
    else
    {
      DAsmCode[0] = EvalStrIntExpression(&ArgStr[1], UInt24, &OK);
      if (OK)
      {
        GetToken(&ArgStr[3], &Token);
        OK = True;
        if (!as_strcasecmp(Token.str.p_str, "WHEN")) DAsmCode[0] |= 0x08000000;
        else if (as_strcasecmp(Token.str.p_str, "WITH")) OK = False;
        if (!OK) WrError(ErrNum_InvAddrMode);
        else
        {
          KillPrefBlanksStrComp(&ArgStr[3]);
          KillPostBlanksStrComp(&ArgStr[3]);
          if (!DecodePhase(ArgStr[3].str.p_str, &ImmVal)) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[3]);
          else
          {
            DAsmCode[0] |= ImmVal << 24;
            if (!as_strncasecmp(ArgStr[2].str.p_str, "PTR", 3))
            {
              StrCompCutLeft(&ArgStr[2], 4);
              DAsmCode[0] |= 0x20000000;
            }
            DAsmCode[1] = EvalStrIntExpression(&ArgStr[2], UInt32, &OK);
            if (OK) CodeLen = 8;
          }
        }
      }
    }
  }
}

static void DecodeSELECT(Word MayATN)
{
  Boolean OK;
  LongInt Dist;
  tSymbolFlags Flags;
  int l;

  if (ChkArgCnt(2, 2))
  {
    DAsmCode[0] = 0x40000000;
    OK = True;
    if (!as_strncasecmp(ArgStr[1].str.p_str, "ATN ", 4))
    {
      StrCompCutLeft(&ArgStr[1], 4);
      KillPrefBlanksStrComp(&ArgStr[1]);
      if (!MayATN)
        OK = False;
      else
        DAsmCode[0] |= 0x01000000;
    }
    if (!OK) WrError(ErrNum_InvAddrMode);
    else
    {
      if (!as_strncasecmp(ArgStr[1].str.p_str, "FROM ", 5))
      {
        StrCompCutLeft(&ArgStr[1], 5);
        KillPrefBlanksStrComp(&ArgStr[1]);
        DAsmCode[0] |= 0x02000000 + EvalStrIntExpression(&ArgStr[1], UInt24, &OK);
      }
      else
        DAsmCode[0] |= EvalStrIntExpression(&ArgStr[1], UInt4, &OK) << 16;
      if (OK)
      {
        l = strlen(ArgStr[2].str.p_str);
        if ((!as_strncasecmp(ArgStr[2].str.p_str, "REL(", 4)) && (ArgStr[2].str.p_str[l - 1] == ')'))
        {
          DAsmCode[0] |= 0x04000000;
          ArgStr[2].str.p_str[l - 1] = '\0';
          Dist = EvalStrIntExpressionOffsWithFlags(&ArgStr[2], 4, UInt32, &OK, &Flags) - (EProgCounter() + 8);
          if (OK)
          {
            if (!mSymbolQuestionable(Flags) && ((Dist > 0x7fffff) || (Dist < -0x800000))) WrError(ErrNum_JmpDistTooBig);
            else DAsmCode[1] = Dist & 0xffffff;
          }
        }
        else DAsmCode[1] = EvalStrIntExpression(&ArgStr[2], UInt32, &OK);
        if (OK) CodeLen = 8;
      }
    }
  }
}

static void DecodeWAIT(Word Index)
{
  String TokenStr;
  tStrComp Token;
  Boolean OK;
  LongInt Dist;
  tSymbolFlags Flags;

  UNUSED(Index);
  StrCompMkTemp(&Token, TokenStr, sizeof(TokenStr));

  if (ChkArgCnt(1, 1))
  {
    GetToken(&ArgStr[1], &Token);
    KillPrefBlanksStrComp(&ArgStr[1]);
    if (!as_strcasecmp(Token.str.p_str, "DISCONNECT"))
    {
      if (*ArgStr[1].str.p_str != '\0') WrError(ErrNum_InvAddrMode);
      else
      {
        DAsmCode[0] = 0x48000000;
        DAsmCode[1] = 0;
        CodeLen = 8;
      }
    }
    else if ((!as_strcasecmp(Token.str.p_str, "RESELECT")) || (!as_strcasecmp(Token.str.p_str, "SELECT")))
    {
      if ((!as_strncasecmp(ArgStr[1].str.p_str, "REL(", 4)) && (ArgStr[1].str.p_str[strlen(ArgStr[1].str.p_str) - 1] == ')'))
      {
        StrCompShorten(&ArgStr[1], 1);
        DAsmCode[0] = 0x54000000;
        Dist = EvalStrIntExpressionOffsWithFlags(&ArgStr[1], 4, UInt32, &OK, &Flags) - (EProgCounter() + 8);
        if (OK)
        {
          if (mSymbolQuestionable(Flags) && ((Dist > 0x7fffff) || (Dist < -0x800000))) WrError(ErrNum_JmpDistTooBig);
          else
            DAsmCode[1] = Dist & 0xffffff;
        }
      }
      else
      {
        DAsmCode[0] = 0x50000000;
        DAsmCode[1] = EvalStrIntExpression(&ArgStr[1], UInt32, &OK);
      }
      if (OK)
      {
        if (as_toupper(*Token.str.p_str) == 'S')
          DAsmCode[0] |= 0x00000200;
        CodeLen = 8;
      }
    }
  }
}

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

static void AddReg(const char *NName, LargeWord Adr, Word Mask)
{
  order_array_rsv_end(Regs, TReg);
  Regs[InstrZ].Name = NName;
  Regs[InstrZ].Code = Adr;
  Regs[InstrZ++].Mask = Mask;
}

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

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "NOP"     , 0x80, DecodeFixed);
  AddInstTable(InstTable, "DISCONNECT" , 0x48, DecodeFixed);
  AddInstTable(InstTable, "JUMP"    , 0,    DecodeJmps);
  AddInstTable(InstTable, "CALL"    , 1,    DecodeJmps);
  AddInstTable(InstTable, "RETURN"  , 2,    DecodeJmps);
  AddInstTable(InstTable, "INT"     , 3,    DecodeJmps);
  AddInstTable(InstTable, "INTFLY"  , 3,    DecodeJmps);
  AddInstTable(InstTable, "CHMOV"   , 0,    DecodeCHMOV);
  AddInstTable(InstTable, "CLEAR"   , 0x60, DecodeFlags);
  AddInstTable(InstTable, "SET"     , 0x58, DecodeFlags);
  AddInstTable(InstTable, "LOAD"    , 1,    DecodeRegTrans);
  AddInstTable(InstTable, "STORE"   , 0,    DecodeRegTrans);
  AddInstTable(InstTable, "MOVE"    , 0,    DecodeMOVE);
  AddInstTable(InstTable, "RESELECT", 0,    DecodeSELECT);
  AddInstTable(InstTable, "SELECT"  , 1,    DecodeSELECT);
  AddInstTable(InstTable, "WAIT"    , 0,    DecodeWAIT);

  InstrZ = 0;
  AddReg("SCNTL0"   , 0x00, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SCNTL1"   , 0x01, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SCNTL2"   , 0x02, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SCNTL3"   , 0x03, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SCID"     , 0x04, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SXFER"    , 0x05, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SDID"     , 0x06, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("GPREG"    , 0x07, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SFBR"     , 0x08, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SOCL"     , 0x09, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SSID"     , 0x0a, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SBCL"     , 0x0b, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DSTAT"    , 0x0c, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SSTAT0"   , 0x0d, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SSTAT1"   , 0x0e, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SSTAT2"   , 0x0f, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DSA"      , 0x10, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("ISTAT"    , 0x14, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("CTEST0"   , 0x18, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("CTEST1"   , 0x19, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("CTEST2"   , 0x1a, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("CTEST3"   , 0x1b, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("TEMP"     , 0x1c, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DFIFO"    , 0x20, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("CTEST4"   , 0x21, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("CTEST5"   , 0x22, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("CTEST6"   , 0x23, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DBC"      , 0x24, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DCMD"     , 0x27, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DNAD"     , 0x28, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DSP"      , 0x2c, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DSPS"     , 0x30, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SCRATCHA" , 0x34, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DMODE"    , 0x38, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("DIEN"     , 0x39, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SBR"      , 0x3a, M_53C810                       + M_53C860 + M_53C875 + M_53C895);
  AddReg("DWT"      , 0x3a,            M_53C815                                            );
  AddReg("DCNTL"    , 0x3b, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("ADDER"    , 0x3c, M_53C810            + M_53C825 + M_53C860            + M_53C895);
  AddReg("SIEN0"    , 0x40, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SIEN1"    , 0x41, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SIST0"    , 0x42, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SIST1"    , 0x43, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SLPAR"    , 0x44, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SWIDE"    , 0x45,                       M_53C825            + M_53C875 + M_53C895);
  AddReg("MACNTL"   , 0x46, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("GPCNTL"   , 0x47, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("STIME0"   , 0x48, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("STIME1"   , 0x49, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("RESPID"   , 0x4a, M_53C810 + M_53C815 + M_53C825 + M_53C860                      );
  AddReg("RESPID0"  , 0x4a,                                             M_53C875 + M_53C895);
  AddReg("RESPID1"  , 0x4b,                                             M_53C875 + M_53C895);
  AddReg("STEST0"   , 0x4c, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("STEST1"   , 0x4d, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("STEST2"   , 0x4e, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("STEST3"   , 0x4f, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SIDL"     , 0x50, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("STEST4"   , 0x52,                                                      + M_53C895);
  AddReg("SODL"     , 0x54, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SBDL"     , 0x58, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SCRATCHB" , 0x5c, M_53C810 + M_53C815 + M_53C825 + M_53C860 + M_53C875 + M_53C895);
  AddReg("SCRATCHC" , 0x60,                                             M_53C875 + M_53C895);
  AddReg("SCRATCHD" , 0x64,                                             M_53C875 + M_53C895);
  AddReg("SCRATCHE" , 0x68,                                             M_53C875 + M_53C895);
  AddReg("SCRATCHF" , 0x6c,                                             M_53C875 + M_53C895);
  AddReg("SCRATCHG" , 0x70,                                             M_53C875 + M_53C895);
  AddReg("SCRATCHH" , 0x74,                                             M_53C875 + M_53C895);
  AddReg("SCRATCHI" , 0x78,                                             M_53C875 + M_53C895);
  AddReg("SCRATCHJ" , 0x7c,                                             M_53C875 + M_53C895);
  AddReg(NULL       , 0x00, 0);

  AddIntelPseudo(InstTable, eIntPseudoFlag_LittleEndian);
}

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

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

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

static Boolean IsDef_53c8xx(void)
{
  return False;
}

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

static void SwitchTo_53c8xx(void)
{
  const TFamilyDescr *FoundDescr;

  FoundDescr = FindFamilyByName("SYM53C8xx");

  TurnWords = False;
  SetIntConstMode(eIntConstModeC);
  SetIsOccupiedFnc = TrueFnc;
  PCSymbol="$";
  HeaderID = FoundDescr->Id;
  NOPCode = 0;
  DivideChars = ",";
  HasAttrs = False;

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

  MakeCode = MakeCode_53c8xx;
  IsDef = IsDef_53c8xx;
  SwitchFrom = SwitchFrom_53c8xx;

  InitFields();
}

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

void code53c8xx_init(void)
{
  CPU53C810 = AddCPU("SYM53C810", SwitchTo_53c8xx);
  CPU53C860 = AddCPU("SYM53C860", SwitchTo_53c8xx);
  CPU53C815 = AddCPU("SYM53C815", SwitchTo_53c8xx);
  CPU53C825 = AddCPU("SYM53C825", SwitchTo_53c8xx);
  CPU53C875 = AddCPU("SYM53C875", SwitchTo_53c8xx);
  CPU53C895 = AddCPU("SYM53C895", SwitchTo_53c8xx);
}
