/* code7700.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* AS-Codegeneratormodul MELPS-7700                                          */
/*                                                                           */
/*****************************************************************************/

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

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

#include "code7700.h"

typedef struct
{
  Word Code;
  ShortInt Disp8, Disp16;
} RelOrder;

typedef struct
{
  Byte CodeImm, CodeAbs8, CodeAbs16, CodeIdxX8, CodeIdxX16,
       CodeIdxY8, CodeIdxY16;
} XYOrder;

enum
{
  ModNone =   (-1),
  ModImm =     0,
  ModAbs8 =    1,
  ModAbs16 =   2,
  ModAbs24 =   3,
  ModIdxX8 =   4,
  ModIdxX16 =  5,
  ModIdxX24 =  6,
  ModIdxY8 =   7,
  ModIdxY16 =  8,
  ModIdxY24 =  9,
  ModInd8 =   10,
  ModInd16 =  11,
  ModInd24 =  12,
  ModIndX8 =  13,
  ModIndX16 = 14,
  ModIndX24 = 15,
  ModIndY8 =  16,
  ModIndY16 = 17,
  ModIndY24 = 18,
  ModIdxS8 =  19,
  ModIndS8 =  20
};

#define MModImm      (1l << ModImm)
#define MModAbs8     (1l << ModAbs8)
#define MModAbs16    (1l << ModAbs16)
#define MModAbs24    (1l << ModAbs24)
#define MModIdxX8    (1l << ModIdxX8)
#define MModIdxX16   (1l << ModIdxX16)
#define MModIdxX24   (1l << ModIdxX24)
#define MModIdxY8    (1l << ModIdxY8)
#define MModIdxY16   (1l << ModIdxY16)
#define MModIdxY24   (1l << ModIdxY24)
#define MModInd8     (1l << ModInd8)
#define MModInd16    (1l << ModInd16)
#define MModInd24    (1l << ModInd24)
#define MModIndX8    (1l << ModIndX8)
#define MModIndX16   (1l << ModIndX16)
#define MModIndX24   (1l << ModIndX24)
#define MModIndY8    (1l << ModIndY8)
#define MModIndY16   (1l << ModIndY16)
#define MModIndY24   (1l << ModIndY24)
#define MModIdxS8    (1l << ModIdxS8)
#define MModIndS8    (1l << ModIndS8)

#define PushRegCnt 10
static const char PushRegNames[PushRegCnt][4] =
{
  "A", "B", "X", "Y", "DPR", "DT", "DBR", "PG", "PBR", "PS"
};
static const Byte PushRegCodes[PushRegCnt] =
{
  0  ,1  ,2  ,3  ,4    ,5   ,5    ,6   ,6    ,7
};

#define PrefAccB 0x42

static LongInt Reg_PG, Reg_DT, Reg_X, Reg_M, Reg_DPR, BankReg;

static Boolean WordSize;
static Byte AdrVals[3];
static ShortInt AdrType;

static RelOrder *RelOrders;
static XYOrder *XYOrders;

static CPUVar CPU65816, CPUM7700, CPUM7750, CPUM7751;

static as_assume_rec_t ASSUME7700s[] =
{
  { "PG" , &Reg_PG , 0,   0xff,   0x100, NULL },
  { "DT" , &Reg_DT , 0,   0xff,   0x100, NULL },
  { "PBR", &Reg_PG , 0,   0xff,   0x100, NULL },
  { "DBR", &Reg_DT , 0,   0xff,   0x100, NULL },
  { "X"  , &Reg_X  , 0,      1,      -1, NULL },
  { "M"  , &Reg_M  , 0,      1,      -1, NULL },
  { "DPR", &Reg_DPR, 0, 0xffff, 0x10000, NULL }
};

/*---------------------------------------------------------------------------*/
/* Address Parsing */

static void CodeDisp(const tStrComp *pArg, LongInt Start, LongWord Mask)
{
  Boolean OK;
  tSymbolFlags Flags;
  LongInt Adr;
  ShortInt DType;
  int ArgLen = strlen(pArg->str.p_str);
  unsigned Offset = 0;

  if ((ArgLen > 1) && (pArg->str.p_str[Offset] == '<'))
  {
    Offset = 1;
    DType = 0;
  }
  else if ((ArgLen > 1) && (pArg->str.p_str[Offset] == '>'))
  {
    if ((ArgLen > 2) && (pArg->str.p_str[Offset + 1] == '>'))
    {
      Offset = 2;
      DType = 2;
    }
    else
    {
      Offset = 1;
      DType = 1;
    }
  }
  else
    DType = -1;

  Adr = EvalStrIntExpressionOffsWithFlags(pArg, Offset, UInt24, &OK, &Flags);

  if (!OK)
    return;

  if (DType == -1)
  {
    if ((Mask & (1l << Start))
     && (Adr >= Reg_DPR)
     && (Adr < Reg_DPR + 0x100))
      DType = 0;
    else if ((Mask & (2l << Start))
          && ((Adr >> 16) == BankReg))
      DType = 1;
    else
      DType = 2;
  }

  if (!(Mask & (1l << (Start + DType)))) WrError(ErrNum_InvAddrMode);
  else
  {
    switch (DType)
    {
      case 0:
        if (mFirstPassUnknown(Flags) || (ChkRange(Adr, Reg_DPR, Reg_DPR + 0xff)))
        {
          AdrCnt = 1;
          AdrType = Start;
          AdrVals[0] = Lo(Adr - Reg_DPR);
        }
        break;
      case 1:
        if (!mFirstPassUnknown(Flags) && ((Adr >> 16) != BankReg)) WrError(ErrNum_OverRange);
        else
        {
          AdrCnt = 2;
          AdrType = Start + 1;
          AdrVals[0] = Lo(Adr);
          AdrVals[1] = Hi(Adr);
        }
        break;
      case 2:
        AdrCnt = 3;
        AdrType = Start + 2;
        AdrVals[0] = Lo(Adr);
        AdrVals[1] = Hi(Adr);
        AdrVals[2] = Adr >> 16;
        break;
    }
  }
}

static Integer SplitArg(const tStrComp *pSrc, tStrComp *pDest)
{
  tStrComp IArg;
  char *p;

  StrCompRefRight(&IArg, pSrc, 1);
  StrCompShorten(&IArg, 1);
  p = QuotPos(IArg.str.p_str, ',');
  if (!p)
  {
    StrCompCopy(&pDest[0], &IArg);
    return 1;
  }
  else
  {
    StrCompSplitCopy(&pDest[0], &pDest[1], &IArg, p);
    return 2;
  }
}

static void DecodeAdr(Integer Start, LongWord Mask)
{
  Word AdrWord;
  Boolean OK;
  Integer HCnt;
  String HStr[2];
  tStrComp HArg[2];

  StrCompMkTemp(&HArg[0], HStr[0], sizeof(HStr[0]));
  StrCompMkTemp(&HArg[1], HStr[1], sizeof(HStr[1]));
  AdrType = ModNone;
  AdrCnt = 0;

  /* I. 1 Parameter */

  if (Start == ArgCnt)
  {
    /* I.1. immediate */

    if (*ArgStr[Start].str.p_str == '#')
    {
      if (WordSize)
      {
        AdrWord = EvalStrIntExpressionOffs(&ArgStr[Start], 1, Int16, &OK);
        AdrVals[0] = Lo(AdrWord);
        AdrVals[1] = Hi(AdrWord);
      }
      else
        AdrVals[0] = EvalStrIntExpressionOffs(&ArgStr[Start], 1, Int8, &OK);
      if (OK)
      {
        AdrCnt = 1 + Ord(WordSize);
        AdrType = ModImm;
      }
      goto chk;
    }

    /* I.2. indirekt */

    if (IsIndirect(ArgStr[Start].str.p_str))
    {
      HCnt = SplitArg(&ArgStr[Start], HArg);

      /* I.2.i. einfach indirekt */

      if (HCnt == 1)
      {
        CodeDisp(&HArg[0], ModInd8, Mask);
        goto chk;
      }

      /* I.2.ii indirekt mit Vorindizierung */

      else if (!as_strcasecmp(HArg[1].str.p_str, "X"))
      {
        CodeDisp(&HArg[0], ModIndX8, Mask);
        goto chk;
      }

      else
      {
        WrError(ErrNum_InvAddrMode);
        goto chk;
      }
    }

    /* I.3. absolut */

    else
    {
      CodeDisp(&ArgStr[Start], ModAbs8, Mask);
      goto chk;
    }
  }

  /* II. 2 Parameter */

  else if (Start + 1 == ArgCnt)
  {
    /* II.1 indirekt mit Nachindizierung */

    if (IsIndirect(ArgStr[Start].str.p_str))
    {
      if (as_strcasecmp(ArgStr[Start + 1].str.p_str, "Y")) WrError(ErrNum_InvAddrMode);
      else
      {
        HCnt = SplitArg(&ArgStr[Start], HArg);

        /* II.1.i. (d),Y */

        if (HCnt == 1)
        {
          CodeDisp(&HArg[0], ModIndY8, Mask);
          goto chk;
        }

        /* II.1.ii. (d,S),Y */

        else if (!as_strcasecmp(HArg[1].str.p_str, "S"))
        {
          AdrVals[0] = EvalStrIntExpression(&HArg[0], Int8, &OK);
          if (OK)
          {
            AdrType = ModIndS8;
            AdrCnt = 1;
          }
          goto chk;
        }

        else WrError(ErrNum_InvAddrMode);
      }
      goto chk;
    }

    /* II.2. einfach indiziert */

    else
    {
      /* II.2.i. d,X */

      if (!as_strcasecmp(ArgStr[Start + 1].str.p_str, "X"))
      {
        CodeDisp(&ArgStr[Start], ModIdxX8, Mask);
        goto chk;
      }

      /* II.2.ii. d,Y */

      else if (!as_strcasecmp(ArgStr[Start + 1].str.p_str, "Y"))
      {
        CodeDisp(&ArgStr[Start], ModIdxY8, Mask);
        goto chk;
      }

      /* II.2.iii. d,S */

      else if (!as_strcasecmp(ArgStr[Start + 1].str.p_str, "S"))
      {
        AdrVals[0] = EvalStrIntExpression(&ArgStr[Start], Int8, &OK);
        if (OK)
        {
          AdrType = ModIdxS8;
          AdrCnt = 1;
        }
        goto chk;
      }

      else WrError(ErrNum_InvAddrMode);
    }
  }

  else
    (void)ChkArgCnt(Start, Start + 1);

chk:
  if ((AdrType != ModNone)
   && (!(Mask & (1l << ((LongWord)AdrType)))))
   {
     AdrType = ModNone;
     AdrCnt = 0;
     WrError(ErrNum_InvAddrMode);
   }
}

static Boolean CPUMatch(Byte Mask)
{
  return ((Mask >> (MomCPU - CPU65816)) & 1);
}

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

/* see my rant about BRK in code65.c */

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

  if (ChkArgCnt(0, 1))
  {
    BAsmCode[0] = 0x00;
    if (ArgCnt > 0)
    {
      Boolean OK;

      BAsmCode[1] = EvalStrIntExpressionOffs(&ArgStr[1], !!(*ArgStr[1].str.p_str == '#'), Int8, &OK);
      if (OK)
        CodeLen = 2;
    }
    else
      CodeLen = 1;
  }
}

static void DecodeFixed(Word Code)
{
  if (ChkArgCnt(0, 0))
  {
    CodeLen = 1 + Ord(Hi(Code) != 0);
    if (CodeLen == 2)
      BAsmCode[0] = Hi(Code);
    BAsmCode[CodeLen - 1] = Lo(Code);
  }
}

static void DecodePHB_PLB(Word Code)
{
  if (ChkArgCnt(0, 0))
  {
    if (MomCPU >= CPUM7700)
    {
      CodeLen = 2;
      BAsmCode[0] = PrefAccB;
      BAsmCode[1] = 0x48;
    }
    else
    {
      CodeLen = 1;
      BAsmCode[0] = 0x8b;
    }
    BAsmCode[CodeLen - 1] += Code;
  }
}

static void DecodeRel(Word Index)
{
  const RelOrder *pOrder = RelOrders + Index;
  LongInt AdrLong;
  Boolean OK;
  tSymbolFlags Flags;

  if (ChkArgCnt(1, 1))
  {
    AdrLong = EvalStrIntExpressionOffsWithFlags(&ArgStr[1], !!(*ArgStr[1].str.p_str == '#'), Int32, &OK, &Flags);
    if (OK)
    {
      OK = pOrder->Disp8 == -1;
      if (OK)
        AdrLong -= EProgCounter() + pOrder->Disp16;
      else
      {
        AdrLong -= EProgCounter() + pOrder->Disp8;
        if (((AdrLong > 127) || (AdrLong < -128)) && !mSymbolQuestionable(Flags) && (pOrder->Disp16 != -1))
        {
          OK = True;
          AdrLong -= pOrder->Disp16 - pOrder->Disp8;
        }
      }
      if (OK)            /* d16 */
      {
        if (((AdrLong < -32768) || (AdrLong > 32767)) && !mSymbolQuestionable(Flags)) WrError(ErrNum_DistTooBig);
        else
        {
          CodeLen = 3;
          BAsmCode[0] = Hi(pOrder->Code);
          BAsmCode[1] = Lo(AdrLong);
          BAsmCode[2] = Hi(AdrLong);
        }
      }
      else               /* d8 */
      {
        if (((AdrLong < -128) || (AdrLong > 127)) && !mSymbolQuestionable(Flags)) WrError(ErrNum_JmpDistTooBig);
        else
        {
          CodeLen = 2;
          BAsmCode[0] = Lo(pOrder->Code);
          BAsmCode[1] = Lo(AdrLong);
        }
      }
    }
  }
}

static void DecodeAcc(Word Code)
{
  int Start;
  Boolean LFlag = Hi(Code);
  Code = Lo(Code);

  if (ChkArgCnt(1, 3))
  {
    WordSize = (Reg_M == 0);
    if (!as_strcasecmp(ArgStr[1].str.p_str, "A"))
      Start = 2;
    else if (!as_strcasecmp(ArgStr[1].str.p_str, "B"))
    {
      Start = 2;
      BAsmCode[0] = PrefAccB;
      CodeLen++;
      if (!ChkExcludeCPUExt(CPU65816, ErrNum_AddrModeNotSupported))
        return;
    }
    else
      Start = 1;
    DecodeAdr(Start,
              MModAbs8 | MModAbs16 | MModAbs24
            | MModIdxX8 | MModIdxX16 | MModIdxX24
            | MModIdxY16
            | MModInd8 | MModIndX8 | MModIndY8
            | MModIdxS8 | MModIndS8
            | ((Code == 0x80) ? 0 : MModImm)); /* STA */
    if (AdrType != ModNone)
    {
      if ((LFlag) && (AdrType != ModInd8) && (AdrType != ModIndY8)) WrError(ErrNum_InvAddrMode);
      else
      {
        switch (AdrType)
        {
          case ModImm:
            BAsmCode[CodeLen] = Code + 0x09;
            break;
          case ModAbs8:
            BAsmCode[CodeLen] = Code + 0x05;
            break;
          case ModAbs16:
            BAsmCode[CodeLen] = Code + 0x0d;
            break;
          case ModAbs24:
            BAsmCode[CodeLen] = Code + 0x0f;
            break;
          case ModIdxX8:
            BAsmCode[CodeLen] = Code + 0x15;
            break;
          case ModIdxX16:
            BAsmCode[CodeLen] = Code + 0x1d;
            break;
          case ModIdxX24:
            BAsmCode[CodeLen] = Code + 0x1f;
            break;
          case ModIdxY16:
            BAsmCode[CodeLen] = Code + 0x19;
            break;
          case ModInd8:
            BAsmCode[CodeLen] = LFlag ? Code + 0x07 : Code + 0x12;
            break;
          case ModIndX8:
            BAsmCode[CodeLen] = Code + 0x01;
            break;
          case ModIndY8:
            BAsmCode[CodeLen] = (LFlag) ? Code + 0x17 : Code + 0x11;
            break;
          case ModIdxS8:
            BAsmCode[CodeLen] = Code + 0x03;
            break;
          case ModIndS8:
            BAsmCode[CodeLen] = Code + 0x13;
            break;
        }
        memcpy(BAsmCode + CodeLen + 1, AdrVals, AdrCnt);
        CodeLen += 1 + AdrCnt;
      }
    }
  }
}

static void DecodeEXTS_EXTZ(Word Code)
{
  if (ArgCnt == 0)
  {
    const char AccArg[] = "A";

    AppendArg(strlen(AccArg));
    strmaxcpy(ArgStr[ArgCnt].str.p_str, AccArg, STRINGSIZE);
  }

  if (ChkArgCnt(1, 1)
   && ChkMinCPU(CPUM7750))
  {
    BAsmCode[1] = Code;
    BAsmCode[0] = 0;
    if (!as_strcasecmp(ArgStr[1].str.p_str, "A"))
      BAsmCode[0] = 0x89;
    else if (!as_strcasecmp(ArgStr[1].str.p_str, "B"))
      BAsmCode[0] = 0x42;
    else WrError(ErrNum_InvAddrMode);
    if (BAsmCode[0] != 0)
      CodeLen = 2;
  }
}

static void DecodeRMW(Word Code)
{
  if ((ArgCnt == 0) || ((ArgCnt == 1) && (!as_strcasecmp(ArgStr[1].str.p_str, "A"))))
  {
    CodeLen = 1;
    BAsmCode[0] = Hi(Code);
  }
  else if ((ArgCnt == 1) && (!as_strcasecmp(ArgStr[1].str.p_str, "B")))
  {
    CodeLen = 2;
    BAsmCode[0] = PrefAccB;
    BAsmCode[1] = Hi(Code);
    if (!ChkExcludeCPUExt(CPU65816, ErrNum_AddrModeNotSupported))
      return;
  }
  else if (!ChkArgCnt(0, 2));
  else
  {
    DecodeAdr(1, MModAbs8 | MModAbs16 | MModIdxX8 | MModIdxX16);
    if (AdrType != ModNone)
    {
      switch (AdrType)
      {
        case ModAbs8:
          BAsmCode[0] = Lo(Code);
          break;
        case ModAbs16:
          BAsmCode[0] = Lo(Code) + 8;
          break;
        case ModIdxX8:
          BAsmCode[0] = Lo(Code) + 16;
          break;
        case ModIdxX16:
          BAsmCode[0] = Lo(Code) + 24;
          break;
      }
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
      CodeLen = 1 + AdrCnt;
    }
  }
}

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

  if (!ChkMinCPU(CPUM7750));
  else if ((ArgCnt == 0) || ((ArgCnt == 1) && (!as_strcasecmp(ArgStr[1].str.p_str, "A"))))
  {
    BAsmCode[0] = 0x89;
    BAsmCode[1] = 0x08;
    CodeLen = 2;
  }
  else if ((ArgCnt == 1) && (!as_strcasecmp(ArgStr[1].str.p_str, "B")))
  {
    BAsmCode[0] = 0x42;
    BAsmCode[1] = 0x08;
    CodeLen = 2;
  }
  else if (ChkArgCnt(1, 2))
  {
    DecodeAdr(1, MModAbs8 | MModIdxX8 | MModAbs16 | MModIdxX16);
    if (AdrType != ModNone)
    {
      BAsmCode[0] = 0x89;
      switch (AdrType)
      {
        case ModAbs8:
          BAsmCode[1] = 0x06;
          break;
        case ModIdxX8:
          BAsmCode[1] = 0x16;
          break;
        case ModAbs16:
          BAsmCode[1] = 0x0e;
          break;
        case ModIdxX16:
          BAsmCode[1] = 0x1e;
          break;
      }
      memcpy(BAsmCode + 2, AdrVals, AdrCnt);
      CodeLen = 2 + AdrCnt;
    }
  }
}

static void DecodeBBC_BBS(Word Code)
{
  if (ChkArgCnt(3, 3)
   && ChkMinCPU(CPUM7700))
  {
    WordSize = (Reg_M == 0);
    ArgCnt = 2;
    DecodeAdr(2, MModAbs8 + MModAbs16);
    if (AdrType != ModNone)
    {
      BAsmCode[0] = Code + ((AdrType == ModAbs16) ? 8 : 0);
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
      CodeLen = 1 + AdrCnt;
      ArgCnt = 1;
      DecodeAdr(1, MModImm);
      if (AdrType == ModNone)
        CodeLen = 0;
      else
      {
        LongInt AdrLong;
        Boolean OK;
        tSymbolFlags Flags;

        memcpy(BAsmCode + CodeLen, AdrVals, AdrCnt);
        CodeLen += AdrCnt;
        AdrLong = EvalStrIntExpressionWithFlags(&ArgStr[3], UInt24, &OK, &Flags) - (EProgCounter() + CodeLen + 1);
        if (!OK)
          CodeLen = 0;
        else if (!mSymbolQuestionable(Flags) && ((AdrLong < -128) || (AdrLong > 127)))
        {
          WrError(ErrNum_JmpDistTooBig);
          CodeLen = 0;
        }
        else
        {
          BAsmCode[CodeLen] = Lo(AdrLong);
          CodeLen++;
        }
      }
    }
  }
}

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

  if (ChkArgCnt(1, 2)
   && ChkExactCPU(CPU65816))
  {
    WordSize = (Reg_M == 0);
    DecodeAdr(1, MModAbs8 | MModAbs16 | MModIdxX8 | MModIdxX16 | MModImm);
    if (AdrType != ModNone)
    {
      switch (AdrType)
      {
        case ModAbs8:
          BAsmCode[0] = 0x24;
          break;
        case ModAbs16:
          BAsmCode[0] = 0x2c;
          break;
        case ModIdxX8:
          BAsmCode[0] = 0x34;
          break;
        case ModIdxX16:
          BAsmCode[0] = 0x3c;
          break;
        case ModImm:
          BAsmCode[0] = 0x89;
          break;
      }
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
      CodeLen = 1 + AdrCnt;
    }
  }
}

static void DecodeCLB_SEB(Word Code)
{
  if (ChkArgCnt(2, 2)
   && ChkMinCPU(CPUM7700))
  {
    WordSize = (Reg_M == 0);
    DecodeAdr(2, MModAbs8 | MModAbs16);
    if (AdrType != ModNone)
    {
      BAsmCode[0] = Code + ((AdrType == ModAbs16) ? 8 : 0);
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
      CodeLen = 1 + AdrCnt;
      ArgCnt = 1;
      DecodeAdr(1, MModImm);
      if (AdrType == ModNone)
        CodeLen = 0;
      else
      {
        memcpy(BAsmCode + CodeLen, AdrVals, AdrCnt);
        CodeLen += AdrCnt;
      }
    }
  }
}

static void DecodeTSB_TRB(Word Code)
{
  if (MomCPU == CPU65816)
  {
    if (ChkArgCnt(1, 1))
    {
      DecodeAdr(1, MModAbs8 + MModAbs16);
      if (AdrType != ModNone)
      {
        BAsmCode[0] = Code + ((AdrType == ModAbs16) ? 8 : 0);
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        CodeLen = 1 + AdrCnt;
      }
    }
  }
  else if (Code == 0x14) (void)ChkExactCPU(CPU65816); /* TRB */
  else if (ChkArgCnt(0, 0))
  {
    CodeLen = 2;
    BAsmCode[0] = 0x42;
    BAsmCode[1] = 0x3b;
  }
}

static void DecodeImm8(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    WordSize = False;
    DecodeAdr(1, MModImm);
    if (AdrType == ModImm)
    {
      CodeLen = 1 + Ord(Hi(Code) != 0);
      if (CodeLen == 2)
        BAsmCode[0] = Hi(Code);
      BAsmCode[CodeLen-1] = Lo(Code);
      memcpy(BAsmCode + CodeLen, AdrVals, AdrCnt);
      CodeLen += AdrCnt;
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    WordSize = (Reg_M == 0);
    DecodeAdr(1, MModImm);
    if (AdrType != ModNone)
    {
      CodeLen = 2 + AdrCnt;
      BAsmCode[0] = 0x89;
      BAsmCode[1] = 0x49;
      memcpy(BAsmCode + 2, AdrVals, AdrCnt);
    }
  }
}

static void DecodeXY(Word Index)
{
  const XYOrder *pOrder = XYOrders + Index;

  if (ChkArgCnt(1, 2))
  {
    WordSize = (Reg_X == 0);
    DecodeAdr(1, ((pOrder->CodeImm    != 0xff) ? MModImm : 0)
               | ((pOrder->CodeAbs8   != 0xff) ? MModAbs8 : 0)
               | ((pOrder->CodeAbs16  != 0xff) ? MModAbs16 : 0)
               | ((pOrder->CodeIdxX8  != 0xff) ? MModIdxX8 : 0)
               | ((pOrder->CodeIdxX16 != 0xff) ? MModIdxX16 : 0)
               | ((pOrder->CodeIdxY8  != 0xff) ? MModIdxY8 : 0)
               | ((pOrder->CodeIdxY16 != 0xff) ? MModIdxY16 : 0));
    if (AdrType != ModNone)
    {
      switch (AdrType)
      {
        case ModImm:
          BAsmCode[0] = pOrder->CodeImm;
          break;
        case ModAbs8:
          BAsmCode[0] = pOrder->CodeAbs8;
          break;
        case ModAbs16:
          BAsmCode[0] = pOrder->CodeAbs16;
          break;
        case ModIdxX8:
          BAsmCode[0] = pOrder->CodeIdxX8;
          break;
        case ModIdxY8:
          BAsmCode[0] = pOrder->CodeIdxY8;
          break;
        case ModIdxX16:
          BAsmCode[0] = pOrder->CodeIdxX16;
          break;
        case ModIdxY16:
          BAsmCode[0] = pOrder->CodeIdxY16;
          break;
      }
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
      CodeLen = 1 + AdrCnt;
    }
  }
}

static void DecodeMulDiv(Word Code)
{
  Byte LFlag = Hi(Code);
  Code = Lo(Code);

  if (ChkArgCnt(1, 2))
  {
    WordSize = (Reg_M == 0);
    DecodeAdr(1, MModImm | MModAbs8 | MModAbs16 | MModAbs24 | MModIdxX8 | MModIdxX16
               | MModIdxX24 | MModIdxY16 | MModInd8 | MModIndX8 | MModIndY8
               | MModIdxS8 | MModIndS8);
    if (AdrType != ModNone)
    {
      if ((LFlag) && (AdrType != ModInd8) && (AdrType != ModIndY8)) WrError(ErrNum_InvAddrMode);
      else
      {
        BAsmCode[0] = 0x89;
        switch (AdrType)
        {
          case ModImm:
            BAsmCode[1] = 0x09;
            break;
          case ModAbs8:
            BAsmCode[1] = 0x05;
            break;
          case ModAbs16:
            BAsmCode[1] = 0x0d;
            break;
          case ModAbs24:
            BAsmCode[1] = 0x0f;
            break;
          case ModIdxX8:
            BAsmCode[1] = 0x15;
            break;
          case ModIdxX16:
            BAsmCode[1] = 0x1d;
            break;
          case ModIdxX24:
            BAsmCode[1] = 0x1f;
            break;
          case ModIdxY16:
            BAsmCode[1] = 0x19;
            break;
          case ModInd8:
            BAsmCode[1] = (LFlag) ? 0x07 : 0x12;
            break;
          case ModIndX8:
            BAsmCode[1] = 0x01;
            break;
          case ModIndY8:
            BAsmCode[1] = (LFlag) ? 0x17 : 0x11;
            break;
          case ModIdxS8:
            BAsmCode[1] = 0x03;
            break;
          case ModIndS8:
            BAsmCode[1] = 0x13;
            break;
        }
        BAsmCode[1] += Code;
        memcpy(BAsmCode + 2, AdrVals, AdrCnt);
        CodeLen = 2 + AdrCnt;
      }
    }
  }
}

static void DecodeJML_JSL(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    LongInt AdrLong = EvalStrIntExpression(&ArgStr[1], UInt24, &OK);

    if (OK)
    {
      CodeLen = 4;
      BAsmCode[0] = Code;
      BAsmCode[3] = AdrLong >> 16;
      BAsmCode[2] = Hi(AdrLong);
      BAsmCode[1] = Lo(AdrLong);
    }
  }
}

static void DecodeJMP_JSR(Word IsJSR)
{
  Byte LFlag = Hi(IsJSR);
  IsJSR = Lo(IsJSR);

  if (ChkArgCnt(1, 1))
  {
    BankReg = Reg_PG;
    DecodeAdr(1, MModAbs24 | MModIndX16
               | (LFlag ? 0: MModAbs16)
               | (IsJSR ? 0 : MModInd16));
    if (AdrType != ModNone)
    {
      switch (AdrType)
      {
        case ModAbs16:
          BAsmCode[0] = IsJSR ? 0x20 : 0x4c;
          break;
        case ModAbs24:
          BAsmCode[0] = IsJSR ? 0x22 : 0x5c;
          break;
        case ModIndX16:
          BAsmCode[0] = IsJSR ? 0xfc : 0x7c;
          break;
        case ModInd16:
          BAsmCode[0] = LFlag ? 0xdc : 0x6c;
          break;
      }
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
      CodeLen = 1 + AdrCnt;
    }
  }
}

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

  if (ChkArgCnt(2, 3)
   && ChkMinCPU(CPUM7700))
  {
    DecodeAdr(2, MModAbs8 | MModAbs16 | MModIdxX8 | MModIdxX16);
    if (AdrType != ModNone)
    {
      switch (AdrType)
      {
        case ModAbs8:
          BAsmCode[0] = 0x64;
          break;
        case ModAbs16:
          BAsmCode[0] = 0x9c;
          break;
        case ModIdxX8:
          BAsmCode[0] = 0x74;
          break;
        case ModIdxX16:
          BAsmCode[0] = 0x9e;
          break;
      }
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
      CodeLen = 1 + AdrCnt;
      WordSize = (Reg_M == 0);
      ArgCnt = 1;
      DecodeAdr(1, MModImm);
      if (AdrType == ModNone)
        CodeLen = 0;
      else
      {
        memcpy(BAsmCode + CodeLen, AdrVals, AdrCnt);
        CodeLen += AdrCnt;
      }
    }
  }
}

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

  if (ChkArgCnt(1, 2)
   && ChkMinCPU(CPU65816))
  {
    DecodeAdr(1, MModAbs8 | MModAbs16 | MModIdxX8 | MModIdxX16);
    if (AdrType != ModNone)
    {
      switch (AdrType)
      {
        case ModAbs8:
          BAsmCode[0] = 0x64;
          break;
        case ModAbs16:
          BAsmCode[0] = 0x9c;
          break;
        case ModIdxX8:
          BAsmCode[0] = 0x74;
          break;
        case ModIdxX16:
          BAsmCode[0] = 0x9e;
          break;
      }
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
      CodeLen = 1 + AdrCnt;
    }
  }
}

static void DecodeMVN_MVP(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    Boolean OK;
    LongInt Src = EvalStrIntExpression(&ArgStr[1], UInt24, &OK);

    if (OK)
    {
      LongInt Dest = EvalStrIntExpression(&ArgStr[2], UInt24, &OK);

      if (OK)
      {
        BAsmCode[0] = Code;
        BAsmCode[1] = Dest >> 16;
        BAsmCode[2] = Src >> 16;
        CodeLen = 3;
      }
    }
  }
}

static void DecodePSH_PUL(Word Code)
{
  if (ChkArgCnt(0, 0)
   && ChkMinCPU(CPUM7700))
  {
    Boolean OK;
    int z, Start;

    BAsmCode[0] = Code;
    BAsmCode[1] = 0;
    OK = True;
    z = 1;
    while ((z <= ArgCnt) && (OK))
    {
      Boolean OK;

      if (*ArgStr[z].str.p_str == '#')
        BAsmCode[1] |= EvalStrIntExpressionOffs(&ArgStr[z], 1, Int8, &OK);
      else
      {
        Start = 0;
        while ((Start < PushRegCnt) && (as_strcasecmp(PushRegNames[Start], ArgStr[z].str.p_str)))
          Start++;
        OK = (Start < PushRegCnt);
        if (OK)
          BAsmCode[1] |= 1l << PushRegCodes[Start];
        else
          WrStrErrorPos(ErrNum_InvRegName, &ArgStr[z]);
      }
      z++;
    }
    if (OK)
      CodeLen = 2;
  }
}

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

  WordSize = True;
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(1, MModAbs16);
    if (AdrType != ModNone)
    {
      CodeLen = 1 + AdrCnt;
      BAsmCode[0] = 0xf4;
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(1, MModInd8);
    if (AdrType != ModNone)
    {
      CodeLen = 1 + AdrCnt; BAsmCode[0] = 0xd4;
      memcpy(BAsmCode + 1, AdrVals, AdrCnt);
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    Boolean OK, Rel = !(*ArgStr[1].str.p_str == '#');
    tSymbolFlags Flags;

    BAsmCode[0] = 0x62;
    if (Rel)
    {
      LongInt AdrLong = EvalStrIntExpressionWithFlags(&ArgStr[1], UInt24, &OK, &Flags) - (EProgCounter() + 3);

      if (OK)
      {
        if (((AdrLong < -32768) || (AdrLong > 32767)) && !mSymbolQuestionable(Flags)) WrError(ErrNum_JmpDistTooBig);
        else
        {
          CodeLen = 3;
          BAsmCode[1] = AdrLong & 0xff;
          BAsmCode[2] = (AdrLong >> 8) & 0xff;
        }
      }
    }
    else
    {
      Word AdrWord = EvalStrIntExpressionOffs(&ArgStr[1], 1, Int16, &OK);

      if (OK)
      {
        CodeLen = 3;
        BAsmCode[1] = Lo(AdrWord);
        BAsmCode[2] = Hi(AdrWord);
      }
    }
  }
}

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

static void AddFixed(const char *NName, Word NCode, Byte NAllowed)
{
  if (CPUMatch(NAllowed))
    AddInstTable(InstTable, NName, NCode, DecodeFixed);
}

static void AddRel(const char *NName, Word NCode, ShortInt NDisp8, ShortInt NDisp16)
{
  order_array_rsv_end(RelOrders, RelOrder);
  RelOrders[InstrZ].Code = NCode;
  RelOrders[InstrZ].Disp8 = NDisp8;
  RelOrders[InstrZ].Disp16 = NDisp16;
  AddInstTable(InstTable, NName, InstrZ++, DecodeRel);
}

static void AddAcc(const char *NName, Byte NCode)
{
  char Tmp[30];

  AddInstTable(InstTable, NName, NCode, DecodeAcc);
  as_snprintf(Tmp, sizeof(Tmp), "%sL", NName);
  AddInstTable(InstTable, Tmp, 0x0100 | NCode, DecodeAcc);
}

static void AddRMW(const char *NName, Word NACode, Word NMCode)
{
  AddInstTable(InstTable, NName, NACode << 8 | NMCode, DecodeRMW);
}

static void AddImm8(const char *NName, Word NCode, Byte NAllowed)
{
  if (CPUMatch(NAllowed))
    AddInstTable(InstTable, NName, NCode, DecodeImm8);
}

static void AddXY(const char *NName, Byte NCodeImm, Byte NCodeAbs8, Byte NCodeAbs16,
                  Byte NCodeIdxX8, Byte NCodeIdxX16, Byte NCodeIdxY8,
                  Byte NCodeIdxY16)
{
  order_array_rsv_end(XYOrders, XYOrder);
  XYOrders[InstrZ].CodeImm = NCodeImm;
  XYOrders[InstrZ].CodeAbs8 = NCodeAbs8;
  XYOrders[InstrZ].CodeAbs16 = NCodeAbs16;
  XYOrders[InstrZ].CodeIdxX8 = NCodeIdxX8;
  XYOrders[InstrZ].CodeIdxX16 = NCodeIdxX16;
  XYOrders[InstrZ].CodeIdxY8 = NCodeIdxY8;
  XYOrders[InstrZ].CodeIdxY16 = NCodeIdxY16;
  AddInstTable(InstTable, NName, InstrZ++, DecodeXY);
}

static void AddMulDiv(const char *NName, Word NCode, Byte NAllowed)
{
  if (CPUMatch(NAllowed))
  {
    char Tmp[30];

    AddInstTable(InstTable, NName, NCode, DecodeMulDiv);
    as_snprintf(Tmp, sizeof(Tmp), "%sL", NName);
    AddInstTable(InstTable, Tmp, 0x0100 | NCode, DecodeMulDiv);
  }
}

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

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "BRK", 0, DecodeBRK);
  AddInstTable(InstTable, "PHB", 0, DecodePHB_PLB);
  AddInstTable(InstTable, "PLB", 0x20, DecodePHB_PLB);
  AddInstTable(InstTable, "EXTS", 0x8b, DecodeEXTS_EXTZ);
  AddInstTable(InstTable, "EXTZ", 0xab, DecodeEXTS_EXTZ);
  AddInstTable(InstTable, "ASR", 0, DecodeASR);
  AddInstTable(InstTable, "BBC", 0x34, DecodeBBC_BBS);
  AddInstTable(InstTable, "BBS", 0x24, DecodeBBC_BBS);
  AddInstTable(InstTable, "BIT", 0, DecodeBIT);
  AddInstTable(InstTable, "CLB", 0x14, DecodeCLB_SEB);
  AddInstTable(InstTable, "SEB", 0x04, DecodeCLB_SEB);
  AddInstTable(InstTable, "TSB", 0x04, DecodeTSB_TRB);
  AddInstTable(InstTable, "TRB", 0x14, DecodeTSB_TRB);
  AddInstTable(InstTable, "RLA", 0, DecodeRLA);
  AddInstTable(InstTable, "JML", 0x5c, DecodeJML_JSL);
  AddInstTable(InstTable, "JSL", 0x22, DecodeJML_JSL);
  AddInstTable(InstTable, "JMP", 0x0000, DecodeJMP_JSR);
  AddInstTable(InstTable, "JSR", 0x0001, DecodeJMP_JSR);
  AddInstTable(InstTable, "JMPL", 0x0100, DecodeJMP_JSR);
  AddInstTable(InstTable, "JSRL", 0x0101, DecodeJMP_JSR);
  AddInstTable(InstTable, "LDM", 0, DecodeLDM);
  AddInstTable(InstTable, "STZ", 0, DecodeSTZ);
  AddInstTable(InstTable, "MVN", 0x54, DecodeMVN_MVP);
  AddInstTable(InstTable, "MVP", 0x44, DecodeMVN_MVP);
  AddInstTable(InstTable, "PSH", 0xeb, DecodePSH_PUL);
  AddInstTable(InstTable, "PUL", 0xfb, DecodePSH_PUL);
  AddInstTable(InstTable, "PEA", 0, DecodePEA);
  AddInstTable(InstTable, "PEI", 0, DecodePEI);
  AddInstTable(InstTable, "PER", 0, DecodePER);

  AddFixed("CLC", 0x0018, 15); AddFixed("CLI", 0x0058, 15);
  AddFixed("CLM", 0x00d8, 14); AddFixed("CLV", 0x00b8, 15);
  AddFixed("DEX", 0x00ca, 15); AddFixed("DEY", 0x0088, 15);
  AddFixed("INX", 0x00e8, 15); AddFixed("INY", 0x00c8, 15);
  AddFixed("NOP", 0x00ea, 15); AddFixed("PHA", 0x0048, 15);
  AddFixed("PHD", 0x000b, 15); AddFixed("PHG", 0x004b, 14);
  AddFixed("PHP", 0x0008, 15); AddFixed("PHT", 0x008b, 14);
  AddFixed("PHX", 0x00da, 15); AddFixed("PHY", 0x005a, 15);
  AddFixed("PLA", 0x0068, 15); AddFixed("PLD", 0x002b, 15);
  AddFixed("PLP", 0x0028, 15); AddFixed("PLT", 0x00ab, 14);
  AddFixed("PLX", 0x00fa, 15); AddFixed("PLY", 0x007a, 15);
  AddFixed("RTI", 0x0040, 15); AddFixed("RTL", 0x006b, 15);
  AddFixed("RTS", 0x0060, 15); AddFixed("SEC", 0x0038, 15);
  AddFixed("SEI", 0x0078, 15); AddFixed("SEM", 0x00f8, 14);
  AddFixed("STP", 0x00db, 15); AddFixed("TAD", 0x005b, 15);
  AddFixed("TAS", 0x001b, 15); AddFixed("TAX", 0x00aa, 15);
  AddFixed("TAY", 0x00a8, 15); AddFixed("TBD", 0x425b, 14);
  AddFixed("TBS", 0x421b, 14); AddFixed("TBX", 0x42aa, 14);
  AddFixed("TBY", 0x42a8, 14); AddFixed("TDA", 0x007b, 15);
  AddFixed("TDB", 0x427b, 14); AddFixed("TSA", 0x003b, 15);
  AddFixed("TSX", 0x00ba, 15); AddFixed("TXA", 0x008a, 15);
  AddFixed("TXB", 0x428a, 14); AddFixed("TXS", 0x009a, 15);
  AddFixed("TXY", 0x009b, 15); AddFixed("TYA", 0x0098, 15);
  AddFixed("TYB", 0x4298, 15); AddFixed("TYX", 0x00bb, 15);
  AddFixed("WIT", 0x00cb, 14); AddFixed("XAB", 0x8928, 14);
  AddFixed("CLD", 0x00d8,  1); AddFixed("SED", 0x00f8,  1);
  AddFixed("TCS", 0x001b, 15); AddFixed("TSC", 0x003b, 15);
  AddFixed("TCD", 0x005b, 15); AddFixed("TDC", 0x007b, 15);
  AddFixed("PHK", 0x004b,  1); AddFixed("WAI", 0x00cb,  1);
  AddFixed("XBA", 0x00eb,  1); AddFixed("SWA", 0x00eb,  1);
  AddFixed("XCE", 0x00fb,  1);
  AddFixed("DEA", (MomCPU >= CPUM7700) ? 0x001a : 0x003a, 15);
  AddFixed("INA", (MomCPU >= CPUM7700) ? 0x003a : 0x001a, 15);

  InstrZ = 0;
  AddRel("BCC" , 0x0090,  2, -1);
  AddRel("BLT" , 0x0090,  2, -1);
  AddRel("BCS" , 0x00b0,  2, -1);
  AddRel("BGE" , 0x00b0,  2, -1);
  AddRel("BEQ" , 0x00f0,  2, -1);
  AddRel("BMI" , 0x0030,  2, -1);
  AddRel("BNE" , 0x00d0,  2, -1);
  AddRel("BPL" , 0x0010,  2, -1);
  AddRel("BRA" , 0x8280,  2,  3);
  AddRel("BVC" , 0x0050,  2, -1);
  AddRel("BVS" , 0x0070,  2, -1);
  AddRel("BRL" , 0x8200, -1,  3);
  AddRel("BRAL", 0x8200, -1,  3);

  AddAcc("ADC", 0x60);
  AddAcc("AND", 0x20);
  AddAcc("CMP", 0xc0);
  AddAcc("CPA", 0xc0);
  AddAcc("EOR", 0x40);
  AddAcc("LDA", 0xa0);
  AddAcc("ORA", 0x00);
  AddAcc("SBC", 0xe0);
  AddAcc("STA", 0x80);

  AddRMW("ASL", 0x0a, 0x06);
  AddRMW("DEC", (MomCPU >= CPUM7700) ? 0x1a : 0x3a, 0xc6);
  AddRMW("ROL", 0x2a, 0x26);
  AddRMW("INC", (MomCPU >= CPUM7700) ? 0x3a : 0x1a, 0xe6);
  AddRMW("LSR", 0x4a, 0x46);
  AddRMW("ROR", 0x6a, 0x66);

  AddImm8("CLP", 0x00c2, 15);
  AddImm8("REP", 0x00c2, 15);
  AddImm8("LDT", 0x89c2, 14);
  AddImm8("SEP", 0x00e2, 15);
  AddImm8("RMPA", 0x89e2, 8);
  AddImm8("COP", 0x0002, 1);

  InstrZ = 0;
  AddXY("CPX", 0xe0, 0xe4, 0xec, 0xff, 0xff, 0xff, 0xff);
  AddXY("CPY", 0xc0, 0xc4, 0xcc, 0xff, 0xff, 0xff, 0xff);
  AddXY("LDX", 0xa2, 0xa6, 0xae, 0xff, 0xff, 0xb6, 0xbe);
  AddXY("LDY", 0xa0, 0xa4, 0xac, 0xb4, 0xbc, 0xff, 0xff);
  AddXY("STX", 0xff, 0x86, 0x8e, 0xff, 0xff, 0x96, 0xff);
  AddXY("STY", 0xff, 0x84, 0x8c, 0x94, 0xff, 0xff, 0xff);

  AddMulDiv("MPY", 0x0000, 14); AddMulDiv("MPYS", 0x0080, 12);
  AddMulDiv("DIV", 0x0020, 14); AddMulDiv("DIVS", 0x00a0, 12); /*???*/

  add_moto8_pseudo(InstTable, e_moto_pseudo_flags_le | e_moto_pseudo_flags_ddb);
  AddIntelPseudo(InstTable, eIntPseudoFlag_LittleEndian);
}

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

  order_array_free(RelOrders);
  order_array_free(XYOrders);
}

static void MakeCode_7700(void)
{
  BankReg = Reg_DT;

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

static void InitCode_7700(void)
{
  Reg_PG = 0;
  Reg_DT = 0;
  Reg_X = 0;
  Reg_M = 0;
  Reg_DPR = 0;
}

static Boolean IsDef_7700(void)
{
  return False;
}

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

static void SwitchTo_7700(void)
{
  TurnWords = False;
  SetIntConstMode(eIntConstModeMoto);

  PCSymbol = "*"; HeaderID = 0x19; NOPCode = 0xea;
  DivideChars = ","; HasAttrs = False;

  ValidSegs = 1 << SegCode;
  Grans[SegCode] = 1; ListGrans[SegCode] = 1; SegInits[SegCode] = 0;
  SegLimits[SegCode] = 0xffffffl;

  assume_set(ASSUME7700s, as_array_size(ASSUME7700s));

  MakeCode = MakeCode_7700; IsDef = IsDef_7700;
  SwitchFrom = SwitchFrom_7700; InitFields();
}

void code7700_init(void)
{
  CPU65816 = AddCPU("65816"    , SwitchTo_7700);
  CPUM7700 = AddCPU("MELPS7700", SwitchTo_7700);
  CPUM7750 = AddCPU("MELPS7750", SwitchTo_7700);
  CPUM7751 = AddCPU("MELPS7751", SwitchTo_7700);

  AddInitPassProc(InitCode_7700);
}
