/* code68rs08.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator 68RS08                                                      */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"

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

#include "bpemu.h"
#include "strutil.h"

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

#include "code68rs08.h"

typedef struct
{
  char *Name;
  CPUVar MinCPU;
  Byte Code;
} BaseOrder;

typedef struct
{
  char *Name;
  CPUVar MinCPU;
  Byte Code;
  Word Mask;
} ALUOrder;

typedef struct
{
  char *Name;
  CPUVar MinCPU;
  Byte Code;
  Byte DCode;
  Word Mask;
} RMWOrder;

enum
{
  ModNone = -1,
  ModImm = 0,
  ModDir = 1,
  ModExt = 2,
  ModSrt = 3,
  ModTny = 4,
  ModIX = 5,
  ModX = 6
};

#define MModImm (1 << ModImm)
#define MModDir (1 << ModDir)
#define MModExt (1 << ModExt)
#define MModSrt (1 << ModSrt)
#define MModTny (1 << ModTny)
#define MModIX (1 << ModIX)
#define MModX (1 << ModX)

static ShortInt AdrMode;
static Byte AdrVals[2];

static IntType AdrIntType;

static CPUVar CPU68RS08;

static BaseOrder *FixedOrders;
static BaseOrder *RelOrders;
static RMWOrder *RMWOrders;
static ALUOrder *ALUOrders;

/*--------------------------------------------------------------------------*/
/* address parser */


static unsigned ChkZero(char *s, Byte *pErg)
{
  if (*s == '<') /* short / tiny */
  {
    *pErg = 2;
    return 1;
  }
  else if (*s == '>') /* direct */
  {
    *pErg = 1;
    return 1;
  }
  else /* let the assembler make the choice */
  {
    *pErg = 0;
    return 0;
  }
}

static void DecodeAdr(Byte Start, Byte Stop, Word Mask)
{
  Boolean OK;
  tSymbolFlags Flags;
  Word AdrWord;
  Byte ZeroMode;
  unsigned Offset;

  AdrMode = ModNone;
  AdrCnt = 0;

  if (Stop - Start == 1)
  {
    if (*(ArgStr[Start].str.p_str) == 0 && (!as_strcasecmp(ArgStr[Stop].str.p_str, "X")))
    {
      AdrMode = ModIX;
    }
    else
    {
      WrStrErrorPos(ErrNum_InvReg, &ArgStr[Stop]);
      goto chk;
    }
  }

  else if (Stop == Start)
  {
    /* X-indirekt */

    if (!as_strcasecmp(ArgStr[Start].str.p_str, "X"))
    {
      AdrMode = ModX;
      goto chk;
    }

    if (!as_strcasecmp(ArgStr[Start].str.p_str, "D[X]"))
    {
      AdrMode = ModIX;
      goto chk;
    }

    /* immediate */

    if (*ArgStr[Start].str.p_str == '#')
    {
      AdrVals[0] = EvalStrIntExpressionOffs(&ArgStr[Start], 1, Int8, &OK);
      if (OK)
      {
        AdrCnt = 1;
        AdrMode = ModImm;
      }
      goto chk;
    }

    /* absolut */

    Offset = ChkZero(ArgStr[Start].str.p_str, &ZeroMode);
    AdrWord = EvalStrIntExpressionOffsWithFlags(&ArgStr[Start], Offset, (ZeroMode == 2) ? UInt8 : AdrIntType, &OK, &Flags);

    if (OK)
    {
      if (((Mask & MModExt) == 0) || (ZeroMode == 2) || ((ZeroMode == 0) && (Hi(AdrWord) == 0)))
      {
        if (mFirstPassUnknown(Flags))
          AdrWord &= 0xff;
        if (Hi(AdrWord) != 0) WrError(ErrNum_NoShortAddr);
        else
        {
          AdrCnt = 1;
          AdrVals[0] = Lo(AdrWord);
          AdrMode = ModDir;
          if (ZeroMode == 0)
          {
            if ((Mask & MModTny) && (AdrVals[0] <= 0x0f))
              AdrMode = ModTny;
            if ((Mask & MModSrt) && (AdrVals[0] <= 0x1f))
              AdrMode = ModSrt;
          }
          if (ZeroMode == 2)
          {
            if (Mask & MModTny)
            {
              if (AdrVals[0] <= 0x0f)
                AdrMode = ModTny;
              else
                WrError(ErrNum_ConfOpSizes);
              return;
            }
            else if (Mask & MModSrt)
            {
              if (AdrVals[0] <= 0x1f)
                AdrMode = ModSrt;
              else
                WrError(ErrNum_ConfOpSizes);
              return;
            }
            else
            {
              AdrMode = ModNone;
              WrError(ErrNum_NoShortAddr);
              return;
            }
          }
        }
      }
      else
      {
        AdrVals[0] = Hi(AdrWord);
        AdrVals[1] = Lo(AdrWord);
        AdrCnt = 2;
        AdrMode = ModExt;
      }
      goto chk;
    }
  }

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

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

/*--------------------------------------------------------------------------*/
/* instruction parsers */

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

  if (moto8_chk_no_args()
   && ChkMinCPU(pOrder->MinCPU))
  {
    CodeLen = 1;
    BAsmCode[0] = pOrder->Code;
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(1, 1, MModImm | MModDir | MModIX);
    switch (AdrMode)
    {
      case ModImm:
        BAsmCode[1] = AdrVals[0];
        DecodeAdr(2, 2, MModDir | MModIX);
        switch (AdrMode)
        {
          case ModDir:
            BAsmCode[0] = 0x3e;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
          case ModIX:
            BAsmCode[0] = 0x3e;
            BAsmCode[2] = 0x0e;
            CodeLen = 3;
            break;
        }
        break;
      case ModDir:
        BAsmCode[1] = AdrVals[0];
        DecodeAdr(2, 2, MModDir | MModIX);
        switch (AdrMode)
        {
          case ModDir:
            BAsmCode[0] = 0x4e;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
          case ModIX:
            BAsmCode[0] = 0x4e;
            BAsmCode[2] = 0x0e;
            CodeLen = 3;
            break;
        }
        break;
      case ModIX:
        DecodeAdr(2, 2, MModDir);
        if (AdrMode == ModDir)
        {
          BAsmCode[0] = 0x4e;
          BAsmCode[1] = 0x0e;
          BAsmCode[2] = AdrVals[0];
          CodeLen = 3;
        }
        break;
    }
  }
}

static void DecodeRel(Word Index)
{
  BaseOrder *pOrder = RelOrders + Index;
  Boolean OK;
  tSymbolFlags Flags;
  LongInt AdrInt;

  if (ChkArgCnt(1, 1)
   && ChkMinCPU(pOrder->MinCPU))
  {
    AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[1], AdrIntType, &OK, &Flags) - (EProgCounter() + 2);
    if (OK)
    {
      if (!mSymbolQuestionable(Flags) && ((AdrInt < -128) || (AdrInt>127))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        CodeLen = 2;
        BAsmCode[0] = pOrder->Code;
        BAsmCode[1] = Lo(AdrInt);
	if (BAsmCode[0] == 0x00) /* BRN pseudo op */
	{
	  BAsmCode[0] = 0x30;
	  BAsmCode[1] = 0x00;
	}
      }
    }
  }
}

static void DecodeCBEQx(Word Index)
{
  Boolean OK;
  tSymbolFlags Flags;
  LongInt AdrInt;

  UNUSED(Index);

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(1, 1, MModImm);
    if (AdrMode == ModImm)
    {
      BAsmCode[1] = AdrVals[0];
      AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[2], AdrIntType, &OK, &Flags) - (EProgCounter() + 3);
      if (OK)
      {
        if (!mSymbolQuestionable(Flags) && ((AdrInt < -128) || (AdrInt > 127))) WrError(ErrNum_JmpDistTooBig);
        else
        {
          BAsmCode[0] = 0x41;
          BAsmCode[2] = AdrInt & 0xff;
          CodeLen = 3;
        }
      }
    }
  }
}

static void DecodeCBEQ(Word Index)
{
  Boolean OK;
  tSymbolFlags Flags;
  LongInt AdrInt;

  UNUSED(Index);

  if (ArgCnt == 2)
  {
    DecodeAdr(1, 1, MModDir | MModX);
    switch (AdrMode)
    {
      case ModDir:
        BAsmCode[1] = AdrVals[0];
        break;
      case ModX:
        BAsmCode[1] = 0x0f;
        break;
    }
    if (AdrMode != ModNone)
    {
      BAsmCode[0] = 0x31;
      AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[2], AdrIntType, &OK, &Flags) - (EProgCounter() + 3);
      if (OK)
      {
        if (!mSymbolQuestionable(Flags) && ((AdrInt < -128) || (AdrInt > 127))) WrError(ErrNum_JmpDistTooBig);
        else
        {
          BAsmCode[2] = AdrInt & 0xff;
          CodeLen = 3;
        }
      }
    }
  }
  else if (ArgCnt == 3)
  {
    if ((*(ArgStr[1].str.p_str) != 0) || (as_strcasecmp(ArgStr[2].str.p_str, "X"))) WrStrErrorPos(ErrNum_InvReg, &ArgStr[2]);
    else
    {
      BAsmCode[0] = 0x31; BAsmCode[1] = 0x0e;
      AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[3], AdrIntType, &OK, &Flags) - (EProgCounter() + 3);
      if (OK)
      {
        if (!mSymbolQuestionable(Flags) && ((AdrInt < -128) || (AdrInt > 127))) WrError(ErrNum_JmpDistTooBig);
        else
        {
          BAsmCode[2] = AdrInt & 0xff;
          CodeLen = 3;
        }
      }
    }
  }
  else
    (void)ChkArgCnt(2, 3);
}

static void DecodeDBNZx(Word Index)
{
  Boolean OK;
  tSymbolFlags Flags;
  LongInt AdrInt;

  if (ChkArgCnt(1, 1))
  {
    AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[1], AdrIntType, &OK, &Flags) - (EProgCounter() + ((Index == 0) ? 3 : 2));
    if (OK)
    {
      if (!mSymbolQuestionable(Flags) && ((AdrInt < -128) || (AdrInt > 127))) WrError(ErrNum_JmpDistTooBig);
      else if (Index == 0)
      {
        BAsmCode[0] = 0x3b;
	BAsmCode[1] = 0x0f;
        BAsmCode[2] = AdrInt & 0xff;
        CodeLen = 3;
      }
      else
      {
        BAsmCode[0] = 0x4b;
        BAsmCode[1] = AdrInt & 0xff;
        CodeLen = 2;
      }
    }
  }
}

static void DecodeDBNZ(Word Index)
{
  Boolean OK;
  tSymbolFlags Flags;
  LongInt AdrInt;
  Byte Disp = 0;

  UNUSED(Index);

  if (ChkArgCnt(2, 3))
  {
    DecodeAdr(1, ArgCnt - 1, MModDir | MModIX);
    switch (AdrMode)
    {
      case ModDir:
        BAsmCode[0] = 0x3b;
        BAsmCode[1] = AdrVals[0];
        Disp = 3;
        break;
      case ModIX:
        BAsmCode[0] = 0x3b;
        BAsmCode[1] = 0x0e;
        Disp = 3;
        break;
    }
    if (AdrMode != ModNone)
    {
      AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[ArgCnt], AdrIntType, &OK, &Flags) - (EProgCounter() + Disp);
      if (OK)
      {
        if (!mSymbolQuestionable(Flags) && ((AdrInt < -128) || (AdrInt > 127))) WrError(ErrNum_JmpDistTooBig);
        else
        {
          BAsmCode[Disp - 1] = AdrInt & 0xff;
          CodeLen = Disp;
        }
      }
    }
  }
}


static void DecodeLDX(Word Index)
{

  BAsmCode[0] = 0x4e;

  if (ChkArgCnt(1, 2))
  {
    DecodeAdr(1, ArgCnt, (Index == 0) ? (MModImm | MModDir | MModIX) : MModDir);
    if (AdrMode != ModNone)
    {
      switch (AdrMode)
      {
        case ModImm:
          BAsmCode[0] = 0x3e;
          BAsmCode[1] = AdrVals[0];
          BAsmCode[2] = 0x0f;
	  break;	
	case ModDir:
	  if (Index == 0)
	  {
            BAsmCode[1] = AdrVals[0];
            BAsmCode[2] = 0x0f;
	  }
	  else
	  {
            BAsmCode[1] = 0x0f;
            BAsmCode[2] = AdrVals[0];
	  }
          break;
	case ModIX:
          BAsmCode[1] = 0x0e;
          BAsmCode[2] = 0x0e;
	  break;
      }
      CodeLen = 3;
    }
  }
}

static void DecodeTST(Word Index)
{

  BAsmCode[0] = 0x4e;

  if (Index == 1)
  {
    if (moto8_chk_no_args())
    {
      BAsmCode[1] = 0x0f;
      BAsmCode[2] = 0x0f;
      CodeLen = 3;
    }
  }
  else if (Index == 2)
  {
    BAsmCode[0] = 0xaa;
    BAsmCode[1] = 0x00;
    CodeLen = 2;
  }
  else
  {
    DecodeAdr(1, ArgCnt, MModDir | MModX | MModIX);
    if (AdrMode != ModNone)
    {
      switch (AdrMode)
      {
        case ModDir:
          BAsmCode[1] = AdrVals[0];
          BAsmCode[2] = AdrVals[0];
          break;
        case ModIX:
          BAsmCode[1] = 0x0e;
          BAsmCode[2] = 0x0e;
          break;
        case ModX:
          BAsmCode[1] = 0x0f;
          BAsmCode[2] = 0x0f;
          break;
      }
      CodeLen = 3;
    }
  }
}

static void DecodeALU(Word Index)
{
  ALUOrder *pOrder = ALUOrders + Index;

  if (ChkMinCPU(pOrder->MinCPU))
  {
    DecodeAdr(1, ArgCnt, pOrder->Mask);
    if (AdrMode != ModNone)
    {
      switch (AdrMode)
      {
        case ModImm:
          BAsmCode[0] = 0xa0 | pOrder->Code;
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
          CodeLen = 1 + AdrCnt;
          break;
        case ModDir:
          BAsmCode[0] = 0xb0 | pOrder->Code;
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
          CodeLen = 1 + AdrCnt;
          break;
        case ModIX:
          BAsmCode[0] = 0xb0 | pOrder->Code;
          BAsmCode[1] = 0x0e;
          CodeLen = 2;
          break;
        case ModX:
          BAsmCode[0] = 0xb0 | pOrder->Code;
          BAsmCode[1] = 0x0f;
          CodeLen = 2;
          break;
	case ModExt:
          BAsmCode[0] = pOrder->Code;
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
          CodeLen = 1 + AdrCnt;
      }
    }
  }
}

static void DecodeRMW(Word Index)
{
  RMWOrder *pOrder = RMWOrders + Index;

  if (ChkMinCPU(pOrder->MinCPU))
  {
    DecodeAdr(1, ArgCnt, pOrder->Mask);
    if (AdrMode != ModNone)
    {
      switch (AdrMode)
      {
        case ModImm :
          BAsmCode[0] = 0xa0 | pOrder->Code;
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
          CodeLen = 1 + AdrCnt;
          break;
        case ModDir :
          BAsmCode[0] = 0xb0 ^ pOrder->Code;
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
          CodeLen = 1+ AdrCnt;
          break;
        case ModTny :
          BAsmCode[0] = AdrVals[0] | pOrder->DCode;
          CodeLen = 1;
          break;
        case ModSrt :
          BAsmCode[0] = AdrVals[0] | pOrder->DCode;
          CodeLen = 1;
          break;
        case ModIX  :
          BAsmCode[0] = 0x0e | pOrder->DCode;
          CodeLen = 1;
          break;
        case ModX :
          BAsmCode[0] = 0x0f | pOrder->DCode;
          CodeLen = 1;
          break;
      }
    }
  }
}

static void decode_bset_bclr_core(Word code, int arg_index)
{
  Boolean ok = True;

  if (!as_strcasecmp(ArgStr[arg_index].str.p_str, "D[X]")) BAsmCode[1] = 0x0e;
  else if  (!as_strcasecmp(ArgStr[arg_index].str.p_str, "X")) BAsmCode[1] = 0x0f;
  else BAsmCode[1] = EvalStrIntExpression(&ArgStr[2], Int8, &ok);

  if (ok)
  {
    CodeLen = 2;
    BAsmCode[0] = 0x10 | code;
  }
}

static void decode_bset_bclr_1(Word code)
{
  if (ChkArgCnt(1, 1))
    decode_bset_bclr_core(code, 1);
}

static void decode_bset_bclr_2(Word code)
{
  if (ChkArgCnt(2, 2))
  {
    Boolean ok;

    code |= EvalStrIntExpression(&ArgStr[1], UInt3, &ok) << 1;
    if (ok)
      decode_bset_bclr_core(code, 2);
  }
}

static void decode_brset_brclr_core(Word code, int arg_index)
{
  Boolean ok = True;

  if (!as_strcasecmp(ArgStr[2].str.p_str, "D[X]"))
    BAsmCode[1] = 0x0e;
  else if (!as_strcasecmp(ArgStr[2].str.p_str, "X"))
    BAsmCode[1] = 0x0f;
  else
    BAsmCode[1] = EvalStrIntExpression(&ArgStr[arg_index], Int8, &ok);

  if (ok)
  {
    tSymbolFlags flags;
    LongInt address;

    address = EvalStrIntExpressionWithFlags(&ArgStr[arg_index + 1], AdrIntType, &ok, &flags) - (EProgCounter() + 3);
    if (ok)
    {
      if (!mSymbolQuestionable(flags) && ((address < -128) || (address > 127))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        CodeLen = 3;
        BAsmCode[0] = code;
        BAsmCode[2] = Lo(address);
      }
    }
  }
}

static void decode_brset_brclr_2(Word code)
{
  if (ChkArgCnt(2, 2))
    decode_brset_brclr_core(code, 1);
}

static void decode_brset_brclr_3(Word code)
{
  if (ChkArgCnt(3, 3))
  {
    Boolean ok;

    code |= EvalStrIntExpression(&ArgStr[1], UInt3, &ok) << 1;
    if (ok)
      decode_brset_brclr_core(code, 2);
  }
}

/*--------------------------------------------------------------------------*/
/* dynamic code table handling */

static void AddFixed(const char *NName, CPUVar NMin, Byte NCode)
{
  order_array_rsv_end(FixedOrders, BaseOrder);
  FixedOrders[InstrZ].MinCPU = NMin;
  FixedOrders[InstrZ].Code = NCode;
  AddInstTable(InstTable, NName, InstrZ++, DecodeFixed);
}

static void AddRel(const char *NName, CPUVar NMin, Byte NCode)
{
  order_array_rsv_end(RelOrders, BaseOrder);
  RelOrders[InstrZ].MinCPU = NMin;
  RelOrders[InstrZ].Code = NCode;
  AddInstTable(InstTable, NName, InstrZ++, DecodeRel);
}

static void AddALU(const char *NName, CPUVar NMin, Byte NCode, Word NMask)
{
  order_array_rsv_end(ALUOrders, ALUOrder);
  ALUOrders[InstrZ].MinCPU = NMin;
  ALUOrders[InstrZ].Code = NCode;
  ALUOrders[InstrZ].Mask = NMask;
  AddInstTable(InstTable, NName, InstrZ++, DecodeALU);
}

static void AddRMW(const char *NName, CPUVar NMin, Byte NCode, Byte DCode, Word NMask)
{
  order_array_rsv_end(RMWOrders, RMWOrder);
  RMWOrders[InstrZ].MinCPU = NMin;
  RMWOrders[InstrZ].Code = NCode;
  RMWOrders[InstrZ].DCode = DCode;
  RMWOrders[InstrZ].Mask = NMask;
  AddInstTable(InstTable, NName, InstrZ++, DecodeRMW);
}

static void add_bset_bclr(const char *p_name, Word code)
{
  char name[10];
  unsigned bit;

  AddInstTable(InstTable, p_name, code, decode_bset_bclr_2);
  for (bit = 0; bit < 8; bit++)
  {
    as_snprintf(name, sizeof(name), "%s%c", p_name, bit + '0');
    AddInstTable(InstTable, name, code | (bit << 1), decode_bset_bclr_1);
  }
}

static void add_brset_brclr(const char *p_name, Word code)
{
  char name[10];
  unsigned bit;

  AddInstTable(InstTable, p_name, code, decode_brset_brclr_3);
  for (bit = 0; bit < 8; bit++)
  {
    as_snprintf(name, sizeof(name), "%s%c", p_name, bit + '0');
    AddInstTable(InstTable, name, code | (bit << 1), decode_brset_brclr_2);
  }
}

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

  add_null_pseudo(InstTable);

  InstrZ = 0;
  AddFixed("SHA" , CPU68RS08, 0x45); AddFixed("SLA" , CPU68RS08, 0x42);
  AddFixed("RTS" , CPU68RS08, 0xbe); AddFixed("TAX" , CPU68RS08, 0xef);
  AddFixed("CLC" , CPU68RS08, 0x38); AddFixed("SEC" , CPU68RS08, 0x39);
  AddFixed("NOP" , CPU68RS08, 0xac); AddFixed("TXA" , CPU68RS08, 0xcf);
  AddFixed("COMA", CPU68RS08, 0x43); AddFixed("LSRA", CPU68RS08, 0x44);
  AddFixed("RORA", CPU68RS08, 0x46); AddFixed("ASLA", CPU68RS08, 0x48);
  AddFixed("LSLA", CPU68RS08, 0x48); AddFixed("ROLA", CPU68RS08, 0x49);
  AddFixed("DECA", CPU68RS08, 0x4a); AddFixed("DECX", CPU68RS08, 0x5f);
  AddFixed("INCA", CPU68RS08, 0x4c); AddFixed("INCX", CPU68RS08, 0x2f);
  AddFixed("CLRA", CPU68RS08, 0x4f); AddFixed("CLRX", CPU68RS08, 0x8f);
  AddFixed("STOP", CPU68RS08, 0xae); AddFixed("WAIT", CPU68RS08, 0xaf);
  AddFixed("BGND", CPU68RS08, 0xbf);

  InstrZ = 0;
  AddRel("BRA" , CPU68RS08, 0x30); AddRel("BRN" , CPU68RS08, 0x00);
  AddRel("BCC" , CPU68RS08, 0x34); AddRel("BCS" , CPU68RS08, 0x35);
  AddRel("BHS" , CPU68RS08, 0x34); AddRel("BLO" , CPU68RS08, 0x35);
  AddRel("BNE" , CPU68RS08, 0x36); AddRel("BEQ" , CPU68RS08, 0x37);
  AddRel("BSR" , CPU68RS08, 0xad);

  InstrZ = 0;
  AddALU("ADC" , CPU68RS08, 0x09, MModImm | MModDir | MModIX  | MModX);
  AddALU("AND" , CPU68RS08, 0x04, MModImm | MModDir | MModIX  | MModX);
  AddALU("CMP" , CPU68RS08, 0x01, MModImm | MModDir | MModIX  | MModX);
  AddALU("EOR" , CPU68RS08, 0x08, MModImm | MModDir | MModIX  | MModX);
  AddALU("ORA" , CPU68RS08, 0x0a, MModImm | MModDir | MModIX  | MModX);
  AddALU("SBC" , CPU68RS08, 0x02, MModImm | MModDir | MModIX  | MModX);
  AddALU("JMP" , CPU68RS08, 0xbc, MModExt);
  AddALU("JSR" , CPU68RS08, 0xbd, MModExt);

  InstrZ = 0;
  AddRMW("ADD" , CPU68RS08, 0x0b, 0x60, MModImm | MModDir | MModTny           | MModIX | MModX );
  AddRMW("SUB" , CPU68RS08, 0x00, 0x70, MModImm | MModDir | MModTny           | MModIX | MModX );
  AddRMW("DEC" , CPU68RS08, 0x8a, 0x50,           MModDir | MModTny           | MModIX | MModX );
  AddRMW("INC" , CPU68RS08, 0x8c, 0x20,           MModDir | MModTny           | MModIX | MModX );
  AddRMW("CLR" , CPU68RS08, 0x8f, 0x80,           MModDir           | MModSrt | MModIX | MModX );
  AddRMW("LDA" , CPU68RS08, 0x06, 0xc0, MModImm | MModDir           | MModSrt | MModIX | MModX );
  AddRMW("STA" , CPU68RS08, 0x07, 0xe0,           MModDir           | MModSrt | MModIX | MModX );


  AddInstTable(InstTable, "CBEQA", 1, DecodeCBEQx);

  AddInstTable(InstTable, "CBEQ" , 0, DecodeCBEQ);

  AddInstTable(InstTable, "DBNZA", 1, DecodeDBNZx);
  AddInstTable(InstTable, "DBNZX", 0, DecodeDBNZx);

  AddInstTable(InstTable, "DBNZ" , 0, DecodeDBNZ);

  AddInstTable(InstTable, "MOV"  , 0, DecodeMOV);

  AddInstTable(InstTable, "TST"  , 0, DecodeTST);
  AddInstTable(InstTable, "TSTX" , 1, DecodeTST);
  AddInstTable(InstTable, "TSTA" , 2, DecodeTST);

  AddInstTable(InstTable, "LDX"  , 0, DecodeLDX);
  AddInstTable(InstTable, "STX"  , 1, DecodeLDX);

  add_bset_bclr("BCLR" , 0x01);
  add_bset_bclr("BSET" , 0x00);
  add_brset_brclr("BRCLR", 0x01);
  add_brset_brclr("BRSET", 0x00);

  add_moto8_pseudo(InstTable, e_moto_pseudo_flags_be);
  add_moto8_comment_onoff();
  AddMoto16Pseudo(InstTable, e_moto_pseudo_flags_be);
  AddInstTable(InstTable, "DB", eIntPseudoFlag_BigEndian | eIntPseudoFlag_AllowInt | eIntPseudoFlag_AllowString | eIntPseudoFlag_MotoRep, DecodeIntelDB);
  AddInstTable(InstTable, "DW", eIntPseudoFlag_BigEndian | eIntPseudoFlag_AllowInt | eIntPseudoFlag_AllowString | eIntPseudoFlag_MotoRep, DecodeIntelDW);
}

static void DeinitFields(void)
{
  DestroyInstTable(InstTable);
  order_array_free(FixedOrders);
  order_array_free(RelOrders);
  order_array_free(ALUOrders);
  order_array_free(RMWOrders);
}

/*--------------------------------------------------------------------------*/
/* Main Functions */

static Boolean DecodeAttrPart_68rs08(void)
{
  if (strlen(AttrPart.str.p_str) > 1)
  {
    WrStrErrorPos(ErrNum_UndefAttr, &AttrPart);
    return False;
  }

  /* Deduce operand size.  No size is zero-length string -> '\0' */

  return DecodeMoto16AttrSize(*AttrPart.str.p_str, &AttrPartOpSize[0], False);
}

static void MakeCode_68rs08(void)
{
  if (AttrPartOpSize[0] == eSymbolSizeUnknown)
    AttrPartOpSize[0] = eSymbolSize8Bit;

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

static Boolean IsDef_68rs08(void)
{
  return False;
}

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

static void SwitchTo_68rs08(void)
{
  const TFamilyDescr *p_descr = FindFamilyByName("68RS08");

  TurnWords = False;
  SetIntConstMode(eIntConstModeMoto);

  PCSymbol = "*";
  HeaderID = p_descr->Id;
  NOPCode = 0xac;
  DivideChars = ",";
  HasAttrs = True;
  AttrChars = ".";

  ValidSegs = (1 << SegCode);
  Grans[SegCode] = 1; ListGrans[SegCode] = 1; SegInits[SegCode] = 0;
  SegLimits[SegCode] = 0x3fff;
  AdrIntType = UInt14;

  DecodeAttrPart = DecodeAttrPart_68rs08;
  MakeCode = MakeCode_68rs08;
  IsDef = IsDef_68rs08;
  SwitchFrom = SwitchFrom_68rs08;
  InitFields();
  AddMoto16PseudoONOFF(False);
}

void code68rs08_init(void)
{
  CPU68RS08 = AddCPU("68RS08", SwitchTo_68rs08);
  AddCopyright("68RS08-Generator (C) 2006 Andreas Bolsch");
}
