/* code78k0.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator 78K0-Familie                                                */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"
#include <string.h>
#include <ctype.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 "codevars.h"
#include "errmsg.h"
#include "headids.h"

#include "code78k0.h"

enum
{
  ModNone = -1,
  ModReg8 = 0,
  ModReg16 = 1,
  ModImm = 2,
  ModShort = 3,
  ModSFR = 4,
  ModAbs = 5,
  ModIReg = 6,
  ModIndex = 7,
  ModDisp = 8
};

#define MModReg8 (1 << ModReg8)
#define MModReg16 (1 << ModReg16)
#define MModImm (1 << ModImm)
#define MModShort (1 << ModShort)
#define MModSFR (1 << ModSFR)
#define MModAbs (1 << ModAbs)
#define MModIReg (1 << ModIReg)
#define MModIndex (1 << ModIndex)
#define MModDisp (1 << ModDisp)

#define AccReg 1
#define AccReg16 0

static Byte OpSize,AdrPart;
static Byte AdrVals[2];
static ShortInt AdrMode;

static CPUVar CPU78070;

/*-------------------------------------------------------------------------*/
/* Adressausdruck parsen */

static void DecodeAdr(const tStrComp *pArg, Word Mask)
{
  static const char RegNames[8][2] =
  {
    "X","A","C","B","E","D","L","H"
  };

  Word AdrWord;
  int z;
  Boolean OK, LongFlag;
  int ArgLen = strlen(pArg->str.p_str);
  tSymbolFlags Flags;

  AdrMode = ModNone;
  AdrCnt = 0;

  /* Register */

  for (z = 0; z < 8; z++)
    if (!as_strcasecmp(pArg->str.p_str, RegNames[z]))
    {
      AdrMode = ModReg8;
      AdrPart = z;
      goto chk;
    }

  if (as_toupper(*pArg->str.p_str) == 'R')
  {
    if ((strlen(pArg->str.p_str) == 2) && (pArg->str.p_str[1] >= '0') && (pArg->str.p_str[1] <= '7'))
    {
      AdrMode = ModReg8;
      AdrPart = pArg->str.p_str[1] - '0';
      goto chk;
    }
    else if ((ArgLen == 3) && (as_toupper(pArg->str.p_str[1]) == 'P') && (pArg->str.p_str[2] >= '0') && (pArg->str.p_str[2] <= '3'))
    {
      AdrMode = ModReg16;
      AdrPart = pArg->str.p_str[2] - '0';
      goto chk;
    }
  }

  if (ArgLen == 2)
  {
    for (z = 0; z < 4; z++)
      if ((as_toupper(*pArg->str.p_str) == *RegNames[(z << 1) + 1])
       && (as_toupper(pArg->str.p_str[1]) == *RegNames[z << 1]))
      {
        AdrMode = ModReg16;
        AdrPart = z;
        goto chk;
      }
  }

  /* immediate */

  if (*pArg->str.p_str == '#')
  {
    switch (OpSize)
    {
      case 0:
        AdrVals[0] = EvalStrIntExpressionOffs(pArg, 1, Int8, &OK);
        break;
      case 1:
        AdrWord = EvalStrIntExpressionOffs(pArg, 1, Int16, &OK);
        if (OK)
        {
          AdrVals[0] = Lo(AdrWord);
          AdrVals[1] = Hi(AdrWord);
        }
        break;
    }
    if (OK)
    {
      AdrMode = ModImm;
      AdrCnt = OpSize + 1;
    }
    goto chk;
  }

  /* indirekt */

  if ((*pArg->str.p_str == '[') && (pArg->str.p_str[ArgLen - 1] == ']'))
  {
    tStrComp Arg;

    StrCompRefRight(&Arg, pArg, 1);
    StrCompShorten(&Arg, 1);

    if ((!as_strcasecmp(Arg.str.p_str, "DE")) || (!as_strcasecmp(Arg.str.p_str, "RP2")))
    {
      AdrMode = ModIReg;
      AdrPart = 0;
    }
    else if ((!as_strncasecmp(Arg.str.p_str, "HL", 2)) && (!as_strncasecmp(Arg.str.p_str, "RP3", 3))) WrStrErrorPos(ErrNum_InvReg, &Arg);
    else
    {
      StrCompIncRefLeft(&Arg, 2);
      if (*Arg.str.p_str == '3')
        StrCompIncRefLeft(&Arg, 1);
      if ((!as_strcasecmp(Arg.str.p_str, "+B")) || (!as_strcasecmp(Arg.str.p_str, "+R3")))
      {
        AdrMode = ModIndex;
        AdrPart = 1;
      }
      else if ((!as_strcasecmp(Arg.str.p_str, "+C")) || (!as_strcasecmp(Arg.str.p_str, "+R2")))
      {
        AdrMode = ModIndex;
        AdrPart = 0;
      }
      else
      {
        if (Arg.str.p_str[0])
          AdrVals[0] = EvalStrIntExpression(&Arg, UInt8, &OK);
        else
        {
          AdrVals[0] = 0;
          OK = True;
        }
        if (OK)
        {
          if (AdrVals[0] == 0)
          {
            AdrMode = ModIReg;
            AdrPart = 1;
          }
          else
          {
            AdrMode = ModDisp;
            AdrCnt = 1;
          }
        }
      }
    }

    goto chk;
  }

  /* erzwungen lang ? */

  LongFlag = !!(*pArg->str.p_str == '!');

  /* -->absolut */

  AdrWord = EvalStrIntExpressionOffsWithFlags(pArg, LongFlag, UInt16, &OK, &Flags);
  if (mFirstPassUnknown(Flags))
  {
    AdrWord &= 0xffffe;
    if (!(Mask & MModAbs))
      AdrWord = (AdrWord | 0xff00) & 0xff1f;
  }
  if (OK)
  {
    if ((!LongFlag) && (Mask & MModShort) && (AdrWord >= 0xfe20) && (AdrWord <= 0xff1f))
    {
      AdrMode = ModShort;
      AdrCnt = 1;
      AdrVals[0] = Lo(AdrWord);
    }
    else if ((!LongFlag) && (Mask & MModSFR) && (((AdrWord >= 0xff00) && (AdrWord <= 0xffcf)) || (AdrWord >= 0xffe0)))
    {
      AdrMode = ModSFR;
      AdrCnt = 1;
      AdrVals[0] = Lo(AdrWord);
    }
    else
    {
      AdrMode = ModAbs;
      AdrCnt = 2;
      AdrVals[0] = Lo(AdrWord);
      AdrVals[1] = Hi(AdrWord);
    }
  }

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

static void ChkEven(void)
{
  if ((AdrMode==ModAbs) || (AdrMode==ModShort) || (AdrMode==ModSFR))
    if ((AdrVals[0]&1)==1) WrError(ErrNum_AddrNotAligned);
}

static Boolean DecodeBitAdr(const tStrComp *pArg, Byte *Erg)
{
  char *p;
  Boolean OK;
  tStrComp Reg, Bit;

  p = RQuotPos(pArg->str.p_str, '.');
  if (!p)
  {
    WrError(ErrNum_InvBitPos);
    return False;
  }

  StrCompSplitRef(&Reg, &Bit, pArg, p);
  *Erg = EvalStrIntExpression(&Bit, UInt3, &OK) << 4;
  if (!OK)
    return False;

  DecodeAdr(&Reg, MModShort | MModSFR | MModIReg | MModReg8);
  switch (AdrMode)
  {
    case ModReg8:
      if (AdrPart != AccReg)
      {
        WrError(ErrNum_InvAddrMode);
        return False;
      }
      else
      {
        *Erg += 0x88;
        return True;
      }
    case ModShort:
      return True;
    case ModSFR:
      *Erg += 0x08;
      return True;
    case ModIReg:
      if (AdrPart == 0)
      {
        WrError(ErrNum_InvAddrMode);
        return False;
      }
      else
      {
        *Erg += 0x80;
        return True;
      }
    default:
      return False;
  }
}

/*-------------------------------------------------------------------------*/
/* Instruction Decoders */

/* ohne Argument */

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

/* Datentransfer */

static void DecodeMOV(Word Index)
{
  Byte HReg;

  UNUSED(Index);

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg8 | MModShort | MModSFR | MModAbs
                       | MModIReg | MModIndex | MModDisp);
    switch (AdrMode)
    {
      case ModReg8:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], MModImm | MModReg8
                           | ((HReg == AccReg) ? MModShort | MModSFR | MModAbs | MModIReg | MModIndex | MModDisp : 0));
        switch (AdrMode)
        {
          case ModReg8:
            if ((HReg == AccReg) == (AdrPart == AccReg)) WrError(ErrNum_InvAddrMode);
            else if (HReg == AccReg)
            {
              CodeLen = 1;
              BAsmCode[0] = 0x60 + AdrPart;
            }
            else
            {
              CodeLen = 1;
              BAsmCode[0] = 0x70 + HReg;
            }
            break;
          case ModImm:
            CodeLen = 2;
            BAsmCode[0] = 0xa0 + HReg;
            BAsmCode[1] = AdrVals[0];
            break;
          case ModShort:
            CodeLen = 2;
            BAsmCode[0] = 0xf0;
            BAsmCode[1] = AdrVals[0];
            break;
          case ModSFR:
            CodeLen = 2;
            BAsmCode[0] = 0xf4;
            BAsmCode[1] = AdrVals[0];
            break;
          case ModAbs:
            CodeLen = 3;
            BAsmCode[0] = 0x8e;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            break;
          case ModIReg:
            CodeLen = 1;
            BAsmCode[0] = 0x85 + (AdrPart << 1);
            break;
          case ModIndex:
            CodeLen = 1;
            BAsmCode[0] = 0xaa + AdrPart;
            break;
          case ModDisp:
            CodeLen = 2;
            BAsmCode[0] = 0xae;
            BAsmCode[1] = AdrVals[0];
            break;
        }
        break;

      case ModShort:
        BAsmCode[1] = AdrVals[0];
        DecodeAdr(&ArgStr[2], MModReg8 | MModImm);
        switch (AdrMode)
        {
          case ModReg8:
            if (AdrPart != AccReg) WrError(ErrNum_InvAddrMode);
            else
            {
              BAsmCode[0] = 0xf2;
              CodeLen = 2;
            }
            break;
          case ModImm:
            BAsmCode[0] = 0x11;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
        }
        break;

      case ModSFR:
        BAsmCode[1] = AdrVals[0];
        DecodeAdr(&ArgStr[2], MModReg8 | MModImm);
        switch (AdrMode)
        {
          case ModReg8:
            if (AdrPart != AccReg) WrError(ErrNum_InvAddrMode);
            else
            {
              BAsmCode[0] = 0xf6;
              CodeLen = 2;
            }
            break;
          case ModImm:
            BAsmCode[0] = 0x13;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
        }
        break;

      case ModAbs:
        memcpy(BAsmCode + 1, AdrVals, 2);
        DecodeAdr(&ArgStr[2], MModReg8);
        if (AdrMode == ModReg8)
        {
          if (AdrPart != AccReg) WrError(ErrNum_InvAddrMode);
          else
          {
            BAsmCode[0] = 0x9e;
            CodeLen = 3;
          }
        }
        break;

      case ModIReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], MModReg8);
        if (AdrMode == ModReg8)
        {
          if (AdrPart != AccReg) WrError(ErrNum_InvAddrMode);
          else
          {
            BAsmCode[0] = 0x95 | (HReg << 1);
            CodeLen = 1;
          }
        }
        break;

      case ModIndex:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], MModReg8);
        if (AdrMode == ModReg8)
        {
          if (AdrPart != AccReg) WrError(ErrNum_InvAddrMode);
          else
          {
            BAsmCode[0] = 0xba + HReg;
            CodeLen = 1;
          }
        }
        break;

      case ModDisp:
        BAsmCode[1] = AdrVals[0];
        DecodeAdr(&ArgStr[2], MModReg8);
        if (AdrMode == ModReg8)
        {
          if (AdrPart != AccReg) WrError(ErrNum_InvAddrMode);
          else
          {
            BAsmCode[0] = 0xbe;
            CodeLen = 2;
          }
        }
        break;
    }
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    Boolean Swap = (!as_strcasecmp(ArgStr[2].str.p_str, "A")) || (!as_strcasecmp(ArgStr[2].str.p_str, "RP1"));
    tStrComp *pArg1 = Swap ? &ArgStr[2] : &ArgStr[1],
             *pArg2 = Swap ? &ArgStr[1] : &ArgStr[2];

    DecodeAdr(pArg1, MModReg8);
    if (AdrMode != ModNone)
    {
      if (AdrPart != AccReg) WrError(ErrNum_InvAddrMode);
      else
      {
        DecodeAdr(pArg2, MModReg8 | MModShort | MModSFR | MModAbs
                       | MModIReg | MModIndex | MModDisp);
        switch (AdrMode)
        {
          case ModReg8:
            if (AdrPart == AccReg) WrError(ErrNum_InvAddrMode);
            else
            {
              BAsmCode[0] = 0x30 + AdrPart;
              CodeLen = 1;
            }
            break;
          case ModShort:
            BAsmCode[0] = 0x83;
            BAsmCode[1] = AdrVals[0];
            CodeLen = 2;
            break;
          case ModSFR:
            BAsmCode[0] = 0x93;
            BAsmCode[1] = AdrVals[0];
            CodeLen = 2;
            break;
          case ModAbs:
            BAsmCode[0] = 0xce;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            CodeLen = 3;
            break;
          case ModIReg:
            BAsmCode[0] = 0x05 + (AdrPart << 1);
            CodeLen = 1;
            break;
          case ModIndex:
            BAsmCode[0] = 0x31;
            BAsmCode[1] = 0x8a + AdrPart;
            CodeLen = 2;
            break;
          case ModDisp:
            BAsmCode[0] = 0xde;
            BAsmCode[1] = AdrVals[0];
            CodeLen = 2;
            break;
        }
      }
    }
  }
}

static void DecodeMOVW(Word Index)
{
  Byte HReg;

  UNUSED(Index);

  OpSize = 1;

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg16 | MModShort | MModSFR | MModAbs);
    switch (AdrMode)
    {
      case ModReg16:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], MModReg16 | MModImm
                           | ((HReg == AccReg16) ? MModShort | MModSFR | MModAbs : 0));
        switch (AdrMode)
        {
          case ModReg16:
            if ((HReg == AccReg16) == (AdrPart == AccReg16)) WrError(ErrNum_InvAddrMode);
            else if (HReg == AccReg16)
            {
              BAsmCode[0] = 0xc0 + (AdrPart << 1);
              CodeLen = 1;
            }
            else
            {
              BAsmCode[0] = 0xd0 + (HReg << 1);
              CodeLen = 1;
            }
            break;
          case ModImm:
            BAsmCode[0] = 0x10 + (HReg << 1);
            memcpy(BAsmCode + 1, AdrVals, 2);
            CodeLen = 3;
            break;
          case ModShort:
            BAsmCode[0] = 0x89;
            BAsmCode[1] = AdrVals[0];
            CodeLen = 2;
            ChkEven();
            break;
          case ModSFR:
            BAsmCode[0] = 0xa9;
            BAsmCode[1] = AdrVals[0];
            CodeLen = 2;
            ChkEven();
            break;
          case ModAbs:
            BAsmCode[0] = 0x02;
            memcpy(BAsmCode + 1, AdrVals, 2);
            CodeLen = 3;
            ChkEven();
            break;
        }
        break;

      case ModShort:
        ChkEven();
        BAsmCode[1] = AdrVals[0];
        DecodeAdr(&ArgStr[2], MModReg16 | MModImm);
        switch (AdrMode)
        {
          case ModReg16:
            if (AdrPart != AccReg16) WrError(ErrNum_InvAddrMode);
            else
            {
              BAsmCode[0] = 0x99;
              CodeLen = 2;
            }
            break;
          case ModImm:
            BAsmCode[0] = 0xee;
            memcpy(BAsmCode + 2, AdrVals, 2);
            CodeLen = 4;
            break;
        }
        break;

      case ModSFR:
        ChkEven();
        BAsmCode[1] = AdrVals[0];
        DecodeAdr(&ArgStr[2], MModReg16 | MModImm);
        switch (AdrMode)
        {
          case ModReg16:
            if (AdrPart != AccReg16) WrError(ErrNum_InvAddrMode);
            else
            {
              BAsmCode[0] = 0xb9;
              CodeLen = 2;
            }
           break;
          case ModImm:
            BAsmCode[0] = 0xfe;
            memcpy(BAsmCode + 2,AdrVals, 2);
            CodeLen = 4;
            break;
        }
        break;

      case ModAbs:
        ChkEven();
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        DecodeAdr(&ArgStr[2], MModReg16);
        if (AdrMode == ModReg16)
        {
          if (AdrPart != AccReg16) WrError(ErrNum_InvAddrMode);
          else
          {
            BAsmCode[0] = 0x03;
            CodeLen = 3;
          }
        }
        break;
    }
  }
}

static void DecodeXCHW(Word Index)
{
  Byte HReg;

  UNUSED(Index);

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg16);
    if (AdrMode == ModReg16)
    {
      HReg = AdrPart;
      DecodeAdr(&ArgStr[2], MModReg16);
      if (AdrMode == ModReg16)
      {
        if ((HReg == AccReg16) == (AdrPart == AccReg16)) WrError(ErrNum_InvAddrMode);
        else
        {
          BAsmCode[0] = (HReg == AccReg16) ? 0xe0 + (AdrPart << 1) : 0xe0 + (HReg << 1);
          CodeLen = 1;
        }
      }
    }
  }
}

static void DecodeStack(Word Index)
{
  if (!ChkArgCnt(1, 1));
  else if (!as_strcasecmp(ArgStr[1].str.p_str, "PSW"))
  {
    BAsmCode[0] = 0x22 + Index;
    CodeLen = 1;
  }
  else
  {
    DecodeAdr(&ArgStr[1], MModReg16);
    if (AdrMode == ModReg16)
    {
      BAsmCode[0] = 0xb1 - Index + (AdrPart << 1);
      CodeLen = 1;
    }
  }
}

/* Arithmetik */

static void DecodeAri(Word Index)
{
  Byte HReg;

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg8 | MModShort);
    switch (AdrMode)
    {
      case ModReg8:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], MModReg8 | ((HReg == AccReg) ? (MModImm | MModShort | MModAbs | MModIReg | MModIndex | MModDisp) : 0));
        switch (AdrMode)
        {
          case ModReg8:
            if (AdrPart == AccReg)
            {
              BAsmCode[0] = 0x61;
              BAsmCode[1] = (Index << 4) + HReg;
              CodeLen = 2;
            }
            else if (HReg == AccReg)
            {
              BAsmCode[0] = 0x61;
              BAsmCode[1] = 0x08 + (Index << 4) + AdrPart;
              CodeLen = 2;
            }
            else WrError(ErrNum_InvAddrMode);
            break;
          case ModImm:
            BAsmCode[0] = (Index << 4) + 0x0d;
            BAsmCode[1] = AdrVals[0];
            CodeLen = 2;
            break;
          case ModShort:
            BAsmCode[0] = (Index << 4) + 0x0e;
            BAsmCode[1] = AdrVals[0];
            CodeLen = 2;
            break;
          case ModAbs:
            BAsmCode[0] = (Index << 4) + 0x08;
            memcpy(BAsmCode + 1, AdrVals, 2);
            CodeLen = 3;
            break;
          case ModIReg:
            if (AdrPart == 0) WrError(ErrNum_InvAddrMode);
            else
            {
              BAsmCode[0] = (Index << 4) + 0x0f;
              CodeLen = 1;
            }
            break;
          case ModIndex:
            BAsmCode[0] = 0x31;
            BAsmCode[1] = (Index << 4) + 0x0a + AdrPart;
            CodeLen = 2;
            break;
          case ModDisp:
            BAsmCode[0] = (Index << 4) + 0x09;
            BAsmCode[1] = AdrVals[0];
            CodeLen = 2;
            break;
        }
        break;

      case ModShort:
        BAsmCode[1] = AdrVals[0];
        DecodeAdr(&ArgStr[2], MModImm);
        if (AdrMode == ModImm)
        {
          BAsmCode[0] = (Index << 4) + 0x88;
          BAsmCode[2] = AdrVals[0];
          CodeLen = 3;
        }
        break;
    }
  }
}

static void DecodeAri16(Word Index)
{
  if (ChkArgCnt(2, 2))
  {
    OpSize = 1;
    DecodeAdr(&ArgStr[1], MModReg16);
    if (AdrMode == ModReg16)
    {
      DecodeAdr(&ArgStr[2], MModImm);
      if (AdrMode == ModImm)
      {
        BAsmCode[0] = 0xca + (Index << 4);
        memcpy(BAsmCode + 1, AdrVals, 2);
        CodeLen = 3;
      }
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModReg8);
    if (AdrMode == ModReg8)
    {
      if (AdrPart != 0) WrError(ErrNum_InvAddrMode);
      else
      {
        BAsmCode[0] = 0x31;
        BAsmCode[1] = 0x88;
        CodeLen = 2;
      }
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModReg8);
    if (AdrMode == ModReg8)
    {
      if (AdrPart != 2) WrError(ErrNum_InvAddrMode);
      else
      {
        BAsmCode[0] = 0x31;
        BAsmCode[1] = 0x82;
        CodeLen = 2;
      }
    }
  }
}

static void DecodeINCDEC(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModReg8 | MModShort);
    switch (AdrMode)
    {
      case ModReg8:
        BAsmCode[0] = 0x40 + AdrPart + Index;
        CodeLen = 1;
        break;
      case ModShort:
        BAsmCode[0] = 0x81 + Index;
        BAsmCode[1] = AdrVals[0];
        CodeLen = 2;
        break;
    }
  }
}

static void DecodeINCDECW(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModReg16);
    if (AdrMode == ModReg16)
    {
      BAsmCode[0] = 0x80 + Index + (AdrPart << 1);
      CodeLen = 1;
    }
  }
}

static void DecodeShift(Word Index)
{
  Byte HReg;
  Boolean OK;

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg8);
    if (AdrMode == ModReg8)
    {
      if (AdrPart != AccReg) WrError(ErrNum_InvAddrMode);
      else
      {
        HReg = EvalStrIntExpression(&ArgStr[2], UInt1, &OK);
        if (OK)
        {
          if (HReg != 1) WrError(ErrNum_UnderRange);
          else
          {
            BAsmCode[0] = 0x24 + Index;
            CodeLen = 1;
          }
        }
      }
    }
  }
}

static void DecodeRot4(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModIReg);
    if (AdrMode == ModIReg)
    {
      if (AdrPart == 0) WrError(ErrNum_InvAddrMode);
      else
      {
        BAsmCode[0] = 0x31;
        BAsmCode[1] = 0x80 + Index;
        CodeLen = 2;
      }
    }
  }
}

/* Bitoperationen */

static void DecodeMOV1(Word Index)
{
  Byte HReg;

  UNUSED(Index);

  if (ChkArgCnt(2, 2))
  {
    Boolean Swap = !as_strcasecmp(ArgStr[2].str.p_str, "CY");
    tStrComp *pArg1 = Swap ? &ArgStr[2] : &ArgStr[1],
             *pArg2 = Swap ? &ArgStr[1] : &ArgStr[2];
    int z = Swap ? 1 : 4;

    if (as_strcasecmp(pArg1->str.p_str, "CY")) WrError(ErrNum_InvAddrMode);
    else if (DecodeBitAdr(pArg2, &HReg))
    {
      BAsmCode[0] = 0x61 + (Ord((HReg & 0x88) != 0x88) << 4);
      BAsmCode[1] = z + HReg;
      memcpy(BAsmCode + 2, AdrVals, AdrCnt);
      CodeLen = 2 + AdrCnt;
    }
  }
}

static void DecodeBit2(Word Index)
{
  Byte HReg;

  if (!ChkArgCnt(2, 2));
  else if (as_strcasecmp(ArgStr[1].str.p_str, "CY")) WrError(ErrNum_InvAddrMode);
  else if (DecodeBitAdr(&ArgStr[2], &HReg))
  {
    BAsmCode[0] = 0x61 + (Ord((HReg & 0x88) != 0x88) << 4);
    BAsmCode[1] = Index + 5 + HReg;
    memcpy(BAsmCode + 2, AdrVals, AdrCnt);
    CodeLen = 2 + AdrCnt;
  }
}

static void DecodeSETCLR1(Word Index)
{
  Byte HReg;

  if (!ChkArgCnt(1, 1));
  else if (!as_strcasecmp(ArgStr[1].str.p_str, "CY"))
  {
    BAsmCode[0] = 0x20 + Index;
    CodeLen = 1;
  }
  else if (DecodeBitAdr(&ArgStr[1], &HReg))
  {
    if ((HReg & 0x88) == 0)
    {
      BAsmCode[0] = 0x0a + Index + (HReg & 0x70);
      BAsmCode[1] = AdrVals[0];
      CodeLen = 2;
    }
    else
    {
      BAsmCode[0] =0x61 + (Ord((HReg & 0x88) != 0x88) << 4);
      BAsmCode[1] =HReg + 2 + Index;
      memcpy(BAsmCode + 2, AdrVals, AdrCnt);
      CodeLen=2 + AdrCnt;
    }
  }
}

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

  if (!ChkArgCnt(1, 1));
  else if (as_strcasecmp(ArgStr[1].str.p_str, "CY")) WrError(ErrNum_InvAddrMode);
  else
  {
    BAsmCode[0] = 0x01;
    CodeLen = 1;
  }
}

/* Spruenge */

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

  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModAbs);
    if (AdrMode == ModAbs)
    {
      BAsmCode[0] = 0x9a;
      memcpy(BAsmCode + 1, AdrVals, 2);
      CodeLen = 3;
    }
  }
}

static void DecodeCALLF(Word Index)
{
  Word AdrWord;
  Boolean OK;

  UNUSED(Index);

  if (ChkArgCnt(1, 1))
  {
    AdrWord = EvalStrIntExpressionOffs(&ArgStr[1], !!(*ArgStr[1].str.p_str == '!'), UInt11, &OK);
    if (OK)
    {
      BAsmCode[0] = 0x0c | (Hi(AdrWord) << 4);
      BAsmCode[1] = Lo(AdrWord);
      CodeLen = 2;
    }
  }
}

static void DecodeCALLT(Word Index)
{
  Word AdrWord;
  Boolean OK;
  int l = strlen(ArgStr[1].str.p_str);

  UNUSED(Index);

  if (!ChkArgCnt(1, 1));
  else if ((*ArgStr[1].str.p_str != '[') || (ArgStr[1].str.p_str[l - 1] != ']')) WrError(ErrNum_InvAddrMode);
  else
  {
    tSymbolFlags Flags;

    ArgStr[1].str.p_str[l - 1] = '\0';
    AdrWord = EvalStrIntExpressionOffsWithFlags(&ArgStr[1], 1, UInt6, &OK, &Flags);
    if (mFirstPassUnknown(Flags)) AdrWord &= 0xfffe;
    if (OK)
    {
      if (Odd(AdrWord)) WrError(ErrNum_NotAligned);
      else
      {
        BAsmCode[0] = 0xc1 + (AdrWord & 0x3e);
        CodeLen = 1;
      }
    }
  }
}

static void DecodeBR(Word Index)
{
  Word AdrWord;
  Integer AdrInt;
  Boolean OK;
  Byte HReg;

  UNUSED(Index);

  if (!ChkArgCnt(1, 1));
  else if ((!as_strcasecmp(ArgStr[1].str.p_str, "AX")) || (!as_strcasecmp(ArgStr[1].str.p_str, "RP0")))
  {
    BAsmCode[0] = 0x31;
    BAsmCode[1] = 0x98;
    CodeLen = 2;
  }
  else
  {
    unsigned Offset = 0;
    tSymbolFlags Flags;

    if (*ArgStr[1].str.p_str == '!')
    {
      Offset++;
      HReg = 1;
    }
    else if (*ArgStr[1].str.p_str == '$')
    {
      Offset++;
      HReg = 2;
    }
    else HReg = 0;
    AdrWord = EvalStrIntExpressionOffsWithFlags(&ArgStr[1], Offset, UInt16, &OK, &Flags);
    if (OK)
    {
      if (HReg == 0)
      {
        AdrInt = AdrWord - (EProgCounter() - 2);
        HReg = ((AdrInt >= -128) && (AdrInt < 127)) ? 2 : 1;
      }
      switch (HReg)
      {
        case 1:
          BAsmCode[0] = 0x9b;
          BAsmCode[1] = Lo(AdrWord);
          BAsmCode[2] = Hi(AdrWord);
          CodeLen = 3;
          break;
        case 2:
          AdrInt = AdrWord - (EProgCounter() + 2);
          if (((AdrInt < -128) || (AdrInt > 127)) && !mSymbolQuestionable(Flags)) WrError(ErrNum_JmpDistTooBig);
          else
          {
            BAsmCode[0] = 0xfa;
            BAsmCode[1] = AdrInt & 0xff;
            CodeLen = 2;
          }
          break;
      }
    }
  }
}

static void DecodeRel(Word Index)
{
  Integer AdrInt;
  Boolean OK;
  tSymbolFlags Flags;

  if (ChkArgCnt(1, 1))
  {
    AdrInt = EvalStrIntExpressionOffsWithFlags(&ArgStr[1], ('$' == *ArgStr[1].str.p_str), UInt16, &OK, &Flags) - (EProgCounter() + 2);
    if (OK)
    {
      if (((AdrInt < -128) || (AdrInt > 127)) && !mSymbolQuestionable(Flags)) WrError(ErrNum_JmpDistTooBig);
      else
      {
        BAsmCode[0] = 0x8d + (Index << 4);
        BAsmCode[1] = AdrInt & 0xff;
        CodeLen = 2;
      }
    }
  }
}

static void DecodeBRel(Word Index)
{
  Integer AdrInt;
  tSymbolFlags Flags;
  Byte HReg;
  Boolean OK;

  if (!ChkArgCnt(2, 2));
  else if (DecodeBitAdr(&ArgStr[1], &HReg))
  {
    if ((Index == 1) && ((HReg & 0x88) == 0))
    {
      BAsmCode[0] = 0x8c + HReg;
      BAsmCode[1] = AdrVals[0];
      HReg = 2;
    }
    else
    {
      BAsmCode[0] = 0x31;
      switch (HReg & 0x88)
      {
        case 0x00:
          BAsmCode[1] = 0x00;
          break;
        case 0x08:
          BAsmCode[1] = 0x04;
          break;
        case 0x80:
          BAsmCode[1] = 0x84;
          break;
        case 0x88:
          BAsmCode[1] = 0x0c;
          break;
      }
      BAsmCode[1] += (HReg & 0x70) + Index + 1;
      BAsmCode[2] = AdrVals[0];
      HReg = 2 + AdrCnt;
    }
    AdrInt = EvalStrIntExpressionOffsWithFlags(&ArgStr[2], !!(*ArgStr[2].str.p_str == '$'), UInt16, &OK, &Flags) - (EProgCounter() + HReg + 1);
    if (OK)
    {
      if (((AdrInt < -128) || (AdrInt > 127)) & !mSymbolQuestionable(Flags)) WrError(ErrNum_JmpDistTooBig);
      else
      {
        BAsmCode[HReg] = AdrInt & 0xff;
        CodeLen = HReg + 1;
      }
    }
  }
}

static void DecodeDBNZ(Word Index)
{
  Integer AdrInt;
  tSymbolFlags Flags;
  Boolean OK;

  UNUSED(Index);

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg8 + MModShort);
    if ((AdrMode == ModReg8) && ((AdrPart & 6) != 2)) WrError(ErrNum_InvAddrMode);
    else if (AdrMode != ModNone)
    {
      BAsmCode[0] = (AdrMode == ModReg8) ? 0x88 + AdrPart : 0x04;
      BAsmCode[1] = AdrVals[0];
      AdrInt = EvalStrIntExpressionOffsWithFlags(&ArgStr[2], !!(*ArgStr[2].str.p_str == '$'), UInt16, &OK, &Flags) - (EProgCounter() + AdrCnt + 2);
      if (OK)
      {
        if (((AdrInt < -128) || (AdrInt > 127)) && !mSymbolQuestionable(Flags)) WrError(ErrNum_JmpDistTooBig);
        else
        {
          BAsmCode[AdrCnt + 1] = AdrInt & 0xff;
          CodeLen = AdrCnt + 2;
        }
      }
    }
  }
}

/* Steueranweisungen */

static void DecodeSEL(Word Index)
{
  Byte HReg;

  UNUSED(Index);

  if (ArgCnt != 1) WrError(ErrNum_InvAddrMode);
  else if ((strlen(ArgStr[1].str.p_str) != 3) || (as_strncasecmp(ArgStr[1].str.p_str, "RB", 2) != 0)) WrError(ErrNum_InvAddrMode);
  else
  {
    HReg = ArgStr[1].str.p_str[2] - '0';
    if (ChkRange(HReg, 0, 3))
    {
      BAsmCode[0] = 0x61;
      BAsmCode[1] = 0xd0 + ((HReg & 1) << 3) + ((HReg & 2) << 4);
      CodeLen = 2;
    }
  }
}

/*-------------------------------------------------------------------------*/
/* dynamische Codetabellenverwaltung */

static void AddFixed(const char *NewName, Word NewCode)
{
  AddInstTable(InstTable, NewName, NewCode, DecodeFixed);
}

static void AddAri(const char *NewName)
{
  AddInstTable(InstTable, NewName, InstrZ++, DecodeAri);
}

static void AddAri16(const char *NewName)
{
  AddInstTable(InstTable, NewName, InstrZ++, DecodeAri16);
}

static void AddShift(const char *NewName)
{
  AddInstTable(InstTable, NewName, InstrZ++, DecodeShift);
}

static void AddBit2(const char *NewName)
{
  AddInstTable(InstTable, NewName, InstrZ++, DecodeBit2);
}

static void AddRel(const char *NewName)
{
  AddInstTable(InstTable, NewName, InstrZ++, DecodeRel);
}

static void AddBRel(const char *NewName)
{
  AddInstTable(InstTable, NewName, InstrZ++, DecodeBRel);
}

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

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "MOV"  , 0, DecodeMOV);
  AddInstTable(InstTable, "XCH"  , 0, DecodeXCH);
  AddInstTable(InstTable, "MOVW" , 0, DecodeMOVW);
  AddInstTable(InstTable, "XCHW" , 0, DecodeXCHW);
  AddInstTable(InstTable, "PUSH" , 0, DecodeStack);
  AddInstTable(InstTable, "POP"  , 1, DecodeStack);
  AddInstTable(InstTable, "MULU" , 0, DecodeMULU);
  AddInstTable(InstTable, "DIVUW", 0, DecodeDIVUW);
  AddInstTable(InstTable, "INC"  , 0, DecodeINCDEC);
  AddInstTable(InstTable, "DEC"  ,16, DecodeINCDEC);
  AddInstTable(InstTable, "INCW" , 0, DecodeINCDECW);
  AddInstTable(InstTable, "DECW" ,16, DecodeINCDECW);
  AddInstTable(InstTable, "ROL4" , 0, DecodeRot4);
  AddInstTable(InstTable, "ROR4" ,16, DecodeRot4);
  AddInstTable(InstTable, "MOV1" , 0, DecodeMOV1);
  AddInstTable(InstTable, "SET1" , 0, DecodeSETCLR1);
  AddInstTable(InstTable, "CLR1" , 1, DecodeSETCLR1);
  AddInstTable(InstTable, "NOT1" , 1, DecodeNOT1);
  AddInstTable(InstTable, "CALL" , 0, DecodeCALL);
  AddInstTable(InstTable, "CALLF", 0, DecodeCALLF);
  AddInstTable(InstTable, "CALLT", 0, DecodeCALLT);
  AddInstTable(InstTable, "BR"   , 0, DecodeBR);
  AddInstTable(InstTable, "DBNZ" , 0, DecodeDBNZ);
  AddInstTable(InstTable, "SEL"  , 0, DecodeSEL);

  AddFixed("BRK"  , 0x00bf); AddFixed("RET"  , 0x00af);
  AddFixed("RETB" , 0x009f); AddFixed("RETI" , 0x008f);
  AddFixed("HALT" , 0x7110); AddFixed("STOP" , 0x7100);
  AddFixed("NOP"  , 0x0000); AddFixed("EI"   , 0x7a1e);
  AddFixed("DI"   , 0x7b1e); AddFixed("ADJBA", 0x6180);
  AddFixed("ADJBS", 0x6190);

  InstrZ = 0;
  AddAri("ADD" ); AddAri("SUB" ); AddAri("ADDC"); AddAri("SUBC");
  AddAri("CMP" ); AddAri("AND" ); AddAri("OR"  ); AddAri("XOR" );

  InstrZ = 0;
  AddAri16("ADDW"); AddAri16("SUBW"); AddAri16("CMPW");

  InstrZ = 0;
  AddShift("ROR"); AddShift("RORC"); AddShift("ROL"); AddShift("ROLC");

  InstrZ = 0;
  AddBit2("AND1"); AddBit2("OR1"); AddBit2("XOR1");

  InstrZ = 0;
  AddRel("BC"); AddRel("BNC"); AddRel("BZ"); AddRel("BNZ");

  InstrZ = 0;
  AddBRel("BTCLR"); AddBRel("BT"); AddBRel("BF");

  AddIntelPseudo(InstTable, eIntPseudoFlag_LittleEndian);
}

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

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

static void MakeCode_78K0(void)
{
  OpSize = eSymbolSize8Bit;

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

static Boolean IsDef_78K0(void)
{
  return False;
}

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

static void SwitchTo_78K0(void)
{
  const TFamilyDescr *p_descr = FindFamilyByName("78K0");
  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);

  PCSymbol = "PC";
  HeaderID = p_descr->Id;
  NOPCode = 0x00;
  DivideChars = ",";
  HasAttrs = False;

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

  MakeCode = MakeCode_78K0;
  IsDef = IsDef_78K0;
  SwitchFrom = SwitchFrom_78K0;
  InitFields();
}

void code78k0_init(void)
{
  CPU78070 = AddCPU("78070", SwitchTo_78K0);
}
