/* code90c141.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator Toshiba TLCS-90                                             */
/*                                                                           */
/*****************************************************************************/

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

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

#include "code90c141.h"

typedef struct
{
  const char *Name;
  Byte Code;
} Condition;

#define AccReg 6
#define HLReg 2

#define COND_CODE_TRUE 8

enum
{
  ModNone = -1,
  ModReg8 = 0,
  ModReg16 = 1,
  ModIReg16 = 2,
  ModIndReg = 3,
  ModIdxReg = 4,
  ModDir = 5,
  ModMem = 6,
  ModImm = 7
};

#define MModReg8   (1 << ModReg8)
#define MModReg16  (1 << ModReg16)
#define MModIReg16 (1 << ModIReg16)
#define MModIndReg (1 << ModIndReg)
#define MModIdxReg (1 << ModIdxReg)
#define MModDir    (1 << ModDir)
#define MModMem    (1 << ModMem)
#define MModImm    (1 << ModImm)
#define MAssumeInd (1 << 12)

static ShortInt AdrType;
static Byte AdrMode;
static ShortInt OpSize;
static Byte AdrVals[10];
static Boolean MinOneIs0;

static Condition *Conditions;

static CPUVar CPU90C141;

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

static void SetOpSize(ShortInt New)
{
  if (OpSize == -1)
    OpSize = New;
  else if (OpSize != New)
  {
    WrError(ErrNum_ConfOpSizes);
    AdrType = ModNone;
    AdrCnt = 0;
  }
}

  static const char Reg16Names[][3] = { "BC", "DE", "HL", "\0", "IX", "IY", "SP" };

typedef struct
{
  as_eval_cb_data_t cb_data;
  Byte occ_flag, base_reg;
} tlcs90_eval_cb_data_t;

DECLARE_AS_EVAL_CB(tlcs90_eval_cb)
{
  tlcs90_eval_cb_data_t *p_tlcs90_eval_cb_data = (tlcs90_eval_cb_data_t*)p_data;
  size_t z;

  if (!as_strcasecmp(p_arg->str.p_str, "A"))
  {
    if ((p_tlcs90_eval_cb_data->occ_flag & 1)
     || !as_eval_cb_data_stack_plain_add(p_data->p_stack))
    {
      WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
      return e_eval_fail;
    }
    p_tlcs90_eval_cb_data->occ_flag |= 1;
    as_tempres_set_int(p_res, 0);
    return e_eval_ok;
  }

  for (z = 0; z < as_array_size(Reg16Names); z++)
    if (!as_strcasecmp(p_arg->str.p_str, Reg16Names[z]))
    {
      if ((p_tlcs90_eval_cb_data->occ_flag & 2)
       || !as_eval_cb_data_stack_plain_add(p_data->p_stack))
      {
        WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
        return e_eval_fail;
      }
      p_tlcs90_eval_cb_data->occ_flag |= 2;
      p_tlcs90_eval_cb_data->base_reg = z;
      as_tempres_set_int(p_res, 0);
      return e_eval_ok;
    }
  return e_eval_none;
}

static void DecodeAdr(const tStrComp *pArg, Word Erl)
{
  static const char Reg8Names[][2] = { "B", "C", "D", "E", "H", "L", "A" };
  static const int Reg8Cnt = sizeof(Reg8Names) / sizeof(*Reg8Names);
  static const int Reg16Cnt = sizeof(Reg16Names) / sizeof(*Reg16Names);
  static const char IReg16Names[][3] =  {"IX", "IY", "SP" };
  static const int IReg16Cnt = sizeof(IReg16Names) / sizeof(*IReg16Names);

  int z;
  Boolean ok, is_indirect;

  AdrType = ModNone; AdrCnt = 0;

  /* 1. 8-Bit-Register */

  for (z = 0; z < Reg8Cnt; z++)
    if (!as_strcasecmp(pArg->str.p_str, Reg8Names[z]))
    {
      AdrType = ModReg8;
      AdrMode = z;
      SetOpSize(0);
      goto chk;
    }

  /* 2. 16-Bit-Register, indiziert */

  if (Erl & MModIReg16)
  {
    for (z = 0; z < IReg16Cnt; z++)
      if (!as_strcasecmp(pArg->str.p_str, IReg16Names[z]))
      {
        AdrType = ModIReg16;
        AdrMode = z;
        SetOpSize(1);
        goto chk;
      }
  }

  /* 3. 16-Bit-Register, normal */

  if (Erl & MModReg16)
  {
    for (z = 0; z < Reg16Cnt; z++)
     if (!as_strcasecmp(pArg->str.p_str, Reg16Names[z]))
     {
       AdrType = ModReg16;
       AdrMode = z;
       SetOpSize(1);
       goto chk;
     }
  }

  /* Speicheradresse */

  is_indirect = IsIndirect(pArg->str.p_str);
  if (is_indirect || (Erl & MAssumeInd))
  {
    tlcs90_eval_cb_data_t tlcs90_eval_cb_data;
    LongInt DispAcc;
    tStrComp Arg;
    tEvalResult eval_result;

    StrCompRefRight(&Arg, pArg, !!is_indirect);
    if (is_indirect)
      StrCompShorten(&Arg, 1);
    KillPrefBlanksStrCompRef(&Arg);
    KillPostBlanksStrComp(&Arg);

    as_eval_cb_data_ini(&tlcs90_eval_cb_data.cb_data, tlcs90_eval_cb);
    tlcs90_eval_cb_data.occ_flag = 0;
    tlcs90_eval_cb_data.base_reg = 0;
    DispAcc = EvalStrIntExprWithResultAndCallback(&Arg, Int32, &eval_result, &tlcs90_eval_cb_data.cb_data);
    if (!eval_result.OK)
      return;
    if (mFirstPassUnknown(eval_result.Flags))
      DispAcc &= 0x7f;

    switch (tlcs90_eval_cb_data.occ_flag)
    {
      case 1:
        WrError(ErrNum_InvAddrMode);
        break;
      case 3:
        if ((tlcs90_eval_cb_data.base_reg != 2) || DispAcc) WrError(ErrNum_InvAddrMode);
        else
        {
          AdrType = ModIdxReg;
          AdrMode = 3;
        }
        break;
      case 2:
        if ((DispAcc > 127) || (DispAcc < -128)) WrError(ErrNum_OverRange);
        else if (DispAcc == 0)
        {
          AdrType = ModIndReg;
          AdrMode = tlcs90_eval_cb_data.base_reg;
        }
        else if (tlcs90_eval_cb_data.base_reg < 4) WrError(ErrNum_InvAddrMode);
        else
        {
          AdrType = ModIdxReg;
          AdrMode = tlcs90_eval_cb_data.base_reg - 4;
          AdrCnt = 1;
          AdrVals[0] = DispAcc & 0xff;
        }
        break;
      case 0:
        if (DispAcc > 0xffff) WrError(ErrNum_AdrOverflow);
        else if ((Hi(DispAcc) == 0xff) && (Erl & MModDir))
        {
          AdrType = ModDir;
          AdrCnt = 1;
          AdrVals[0] = Lo(DispAcc);
        }
        else
        {
          AdrType = ModMem;
          AdrCnt = 2;
          AdrVals[0] = Lo(DispAcc);
          AdrVals[1] = Hi(DispAcc);
        }
        break;
    }
  }

  /* immediate */

  else
  {
    if ((OpSize == -1) && (MinOneIs0))
      OpSize = 0;
    switch (OpSize)
    {
      case -1:
        WrError(ErrNum_InvOpSize);
        break;
      case 0:
        AdrVals[0] = EvalStrIntExpression(pArg, Int8, &ok);
        if (ok)
        {
          AdrType = ModImm;
          AdrCnt = 1;
        }
        break;
      case 1:
      {
        LongInt DispVal = EvalStrIntExpression(pArg, Int16, &ok);
        if (ok)
        {
          AdrType = ModImm;
          AdrCnt = 2;
          AdrVals[0] = Lo(DispVal);
          AdrVals[1] = Hi(DispVal);
        }
        break;
      }
    }
  }

  /* gefunden */

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

static Boolean ArgPair(const char *Arg1, const char *Arg2)
{
  return  (((!as_strcasecmp(ArgStr[1].str.p_str, Arg1)) && (!as_strcasecmp(ArgStr[2].str.p_str, Arg2)))
        || ((!as_strcasecmp(ArgStr[1].str.p_str, Arg2)) && (!as_strcasecmp(ArgStr[2].str.p_str, Arg1))));
}

/*!------------------------------------------------------------------------
 * \fn     decode_condition(const char *p_cond_str, Byte *p_cond_code)
 * \brief  parse condition code
 * \param  p_cond_str source argument
 * \param  p_cond_code returns machine code of condition if found
 * \return True if found
 * ------------------------------------------------------------------------ */

static Boolean decode_condition(const char *p_cond_str, Byte *p_cond_code)
{
  int z;

  for (z = 0; Conditions[z].Name; z++)
    if (!as_strcasecmp(p_cond_str, Conditions[z].Name))
    {
      *p_cond_code = Conditions[z].Code;
      return True;
    }
  return False;
}

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

/* ohne Argument */

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

static void DecodeMove(Word Code)
{
  if (ChkArgCnt(0, 0))
  {
    CodeLen = 2;
    BAsmCode[0] = 0xfe;
    BAsmCode[1] = Code;
  }
}

static void DecodeShift(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], (Hi(Code) ? MModReg8 : 0) | MModIndReg | MModIdxReg | MModMem | MModDir);
    switch (AdrType)
    {
      case ModReg8:
        CodeLen = 2;
        BAsmCode[0] = 0xf8 + AdrMode;
        BAsmCode[1] = Lo(Code);
        if (AdrMode == AccReg)
          WrError(ErrNum_ShortAddrPossible);
        break;
      case ModIndReg:
        CodeLen = 2;
        BAsmCode[0] = 0xe0 + AdrMode;
        BAsmCode[1] = Lo(Code);
        break;
      case ModIdxReg:
        CodeLen = 2 + AdrCnt;
        BAsmCode[0] = 0xf0 + AdrMode;
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        BAsmCode[1 + AdrCnt] = Lo(Code);
        break;
      case ModDir:
        CodeLen = 3;
        BAsmCode[0] = 0xe7;
        BAsmCode[1] = AdrVals[0];
        BAsmCode[2] = Lo(Code);
        break;
      case ModMem:
        CodeLen = 4;
        BAsmCode[0] = 0xe3;
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        BAsmCode[3] = Lo(Code);
        break;
    }
  }
}

/* Logik */

static void DecodeBit(Word Code)
{
  Byte BitPos;
  Boolean OK;

  if (ChkArgCnt(2, 2))
  {
    BitPos = EvalStrIntExpression(&ArgStr[1], UInt3, &OK);
    if (OK)
    {
      DecodeAdr(&ArgStr[2], MModReg8 | MModIndReg | MModIdxReg | MModMem | MModDir);
      switch (AdrType)
      {
        case ModReg8:
          CodeLen = 2;
          BAsmCode[0] = 0xf8 + AdrMode;
          BAsmCode[1] = Code + BitPos;
          break;
        case ModIndReg:
          CodeLen = 2;
          BAsmCode[0] = 0xe0 + AdrMode;
          BAsmCode[1] = Code + BitPos;
          break;
        case ModIdxReg:
          CodeLen = 2 + AdrCnt;
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
          BAsmCode[0] = 0xf0 + AdrMode;
          BAsmCode[1 + AdrCnt] = Code + BitPos;
          break;
        case ModMem:
          CodeLen = 4;
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
          BAsmCode[0] = 0xe3;
          BAsmCode[1 + AdrCnt] = Code + BitPos;
          break;
        case ModDir:
          BAsmCode[1] = AdrVals[0];
          if (Code == 0x18)
          {
            BAsmCode[0] = 0xe7;
            BAsmCode[2] = Code + BitPos;
            CodeLen = 3;
          }
          else
          {
            BAsmCode[0] = Code + BitPos;
            CodeLen = 2;
          }
          break;
      }
    }
  }
}

static void DecodeAcc(Word Code)
{
  if (!ChkArgCnt(1, 1));
  else if (as_strcasecmp(ArgStr[1].str.p_str, "A")) WrError(ErrNum_InvAddrMode);
  else
  {
    CodeLen = 1;
    BAsmCode[0] = Code;
  }
}

static void DecodeALU2(Word Code)
{
  Byte HReg;

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg8 | MModReg16 | MModIdxReg | MModIndReg | MModDir | MModMem);
    switch (AdrType)
    {
      case ModReg8:
        DecodeAdr(&ArgStr[2], MModImm | (((HReg = AdrMode) == AccReg) ? MModReg8 | MModIndReg | MModIdxReg | MModDir | MModMem:0));
        switch(AdrType)
        {
          case ModReg8:
            CodeLen = 2;
            BAsmCode[0] = 0xf8 | AdrMode;
            BAsmCode[1] = 0x60 | Code;
            break;
          case ModIndReg:
            CodeLen = 2;
            BAsmCode[0] = 0xe0 | AdrMode; BAsmCode[1] = 0x60 | Code;
            break;
          case ModIdxReg:
            CodeLen = 2 + AdrCnt;
            BAsmCode[0] = 0xf0 | AdrMode;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            BAsmCode[1 + AdrCnt] = 0x60 | Code;
            break;
          case ModDir:
            CodeLen = 2;
            BAsmCode[0] = 0x60 | Code;
            BAsmCode[1] = AdrVals[0];
            break;
          case ModMem:
            CodeLen = 4;
            BAsmCode[0] = 0xe3;
            BAsmCode[3] = 0x60 | Code;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            break;
          case ModImm:
            if (HReg == AccReg)
            {
              CodeLen = 2;
              BAsmCode[0] = 0x68 | Code;
              BAsmCode[1] = AdrVals[0];
            }
            else
            {
              CodeLen = 3;
              BAsmCode[0] = 0xf8 | HReg;
              BAsmCode[1] = 0x68 | Code;
              BAsmCode[2] = AdrVals[0];
            }
            break;
        }
        break;
      case ModReg16:
        if ((AdrMode == 2) || ((Code == 0) && (AdrMode >= 4)))
        {
          HReg = AdrMode;
          DecodeAdr(&ArgStr[2], MModReg16 | MModIndReg | MModIdxReg | MModDir | MModMem | MModImm);
          switch (AdrType)
          {
            case ModReg16:
              CodeLen = 2;
              BAsmCode[0] = 0xf8 | AdrMode;
              BAsmCode[1] = (HReg >= 4) ? 0x14 + HReg - 4 : 0x70 + Code;
              break;
            case ModIndReg:
              CodeLen = 2;
              BAsmCode[0] = 0xe0 | AdrMode;
              BAsmCode[1] = (HReg >= 4) ? 0x14 + HReg - 4 : 0x70 + Code;
              break;
            case ModIdxReg:
              CodeLen = 2 + AdrCnt;
              BAsmCode[0] = 0xf0 | AdrMode;
              memcpy(BAsmCode + 1,AdrVals, AdrCnt);
              BAsmCode[1 + AdrCnt] = (HReg >= 4) ? 0x14 + HReg - 4 : 0x70 + Code;
              break;
            case ModDir:
              if (HReg >= 4)
              {
                CodeLen = 3;
                BAsmCode[0] = 0xe7;
                BAsmCode[1] = AdrVals[0];
                BAsmCode[2] = 0x10 | HReg;
              }
              else
              {
                CodeLen = 2;
                BAsmCode[0] = 0x70 | Code;
                BAsmCode[1] = AdrVals[0];
              }
              break;
            case ModMem:
              CodeLen = 4;
              BAsmCode[0] = 0xe3;
              memcpy(BAsmCode + 1, AdrVals, 2);
              BAsmCode[3] = (HReg >= 4) ? 0x14 + HReg - 4 : 0x70 + Code;
              break;
            case ModImm:
              CodeLen = 3;
              BAsmCode[0] = (HReg >= 4) ? 0x14 + HReg - 4 : 0x78 + Code;
              memcpy(BAsmCode + 1, AdrVals, AdrCnt);
              break;
          }
        }
        else WrError(ErrNum_InvAddrMode);
        break;
      case ModIndReg:
      case ModIdxReg:
      case ModDir:
      case ModMem:
        OpSize = 0;
        switch (AdrType)
        {
          case ModIndReg:
            HReg = 3;
            BAsmCode[0] = 0xe8 | AdrMode;
            BAsmCode[1] = 0x68 | Code;
            break;
          case ModIdxReg:
            HReg = 3 + AdrCnt;
            BAsmCode[0] = 0xf4 | AdrMode;
            BAsmCode[1 + AdrCnt] = 0x68 | Code;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            break;
          case ModDir:
            HReg = 4;
            BAsmCode[0] = 0xef;
            BAsmCode[1] = AdrVals[0];
            BAsmCode[2] = 0x68 | Code;
            break;
          case ModMem:
            HReg = 5;
            BAsmCode[0] = 0xeb;
            memcpy(BAsmCode + 1, AdrVals, 2);
            BAsmCode[3] = 0x68 | Code;
            break;
          default:
            HReg = 0;
        }
        DecodeAdr(&ArgStr[2], MModImm);
        if (AdrType == ModImm)
        {
          BAsmCode[HReg-1] = AdrVals[0];
          CodeLen = HReg;
        }
        break;
    }
  }
}

static void DecodeLD(Word Code)
{
  Byte HReg;

  if (Hi(Code))
    SetOpSize(1);

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg8 | MModReg16 | MModIndReg | MModIdxReg | MModDir | MModMem);
    switch (AdrType)
    {
      case ModReg8:
        HReg = AdrMode;
        DecodeAdr(&ArgStr[2], MModReg8 | MModIndReg | MModIdxReg | MModDir | MModMem | MModImm);
        switch (AdrType)
        {
          case ModReg8:
            if (HReg == AccReg)
            {
              CodeLen = 1;
              BAsmCode[0] = 0x20 | AdrMode;
            }
            else if (AdrMode == AccReg)
            {
              CodeLen = 1;
              BAsmCode[0] = 0x28 | HReg;
            }
            else
            {
              CodeLen = 2;
              BAsmCode[0] = 0xf8 | AdrMode;
              BAsmCode[1] = 0x30 | HReg;
            }
            break;
          case ModIndReg:
            CodeLen = 2;
            BAsmCode[0] = 0xe0 | AdrMode;
            BAsmCode[1] = 0x28 | HReg;
            break;
          case ModIdxReg:
            CodeLen = 2 + AdrCnt;
            BAsmCode[0] = 0xf0 | AdrMode;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            BAsmCode[1 + AdrCnt] = 0x28 | HReg;
            break;
          case ModDir:
            if (HReg == AccReg)
            {
              CodeLen = 2;
              BAsmCode[0] = 0x27;
              BAsmCode[1] = AdrVals[0];
            }
            else
            {
              CodeLen = 3;
              BAsmCode[0] = 0xe7;
              BAsmCode[1] = AdrVals[0];
              BAsmCode[2] = 0x28 | HReg;
            }
            break;
          case ModMem:
            CodeLen = 4;
            BAsmCode[0] = 0xe3;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            BAsmCode[3] = 0x28 | HReg;
            break;
          case ModImm:
            CodeLen = 2;
            BAsmCode[0] = 0x30 | HReg;
            BAsmCode[1] = AdrVals[0];
            break;
        }
        break;
      case ModReg16:
        HReg = AdrMode;
        DecodeAdr(&ArgStr[2], MModReg16 | MModIndReg | MModIdxReg | MModDir | MModMem | MModImm);
        switch (AdrType)
        {
          case ModReg16:
            if (HReg == HLReg)
            {
              CodeLen = 1;
              BAsmCode[0] = 0x40 | AdrMode;
            }
            else if (AdrMode == HLReg)
            {
              CodeLen = 1;
              BAsmCode[0] = 0x48 | HReg;
            }
            else
            {
              CodeLen = 2;
              BAsmCode[0] = 0xf8 | AdrMode;
              BAsmCode[1] = 0x38 | HReg;
            }
            break;
          case ModIndReg:
            CodeLen = 2;
            BAsmCode[0] = 0xe0 | AdrMode;
            BAsmCode[1] = 0x48 | HReg;
            break;
          case ModIdxReg:
            CodeLen = 2 + AdrCnt;
            BAsmCode[0] = 0xf0 | AdrMode;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            BAsmCode[1 + AdrCnt] = 0x48 | HReg;
            break;
          case ModDir:
            if (HReg == HLReg)
            {
              CodeLen = 2;
              BAsmCode[0] = 0x47;
              BAsmCode[1] = AdrVals[0];
            }
            else
            {
              CodeLen = 3;
              BAsmCode[0] = 0xe7;
              BAsmCode[1] = AdrVals[0];
              BAsmCode[2] = 0x48 | HReg;
            }
            break;
          case ModMem:
            CodeLen = 4;
            BAsmCode[0] = 0xe3;
            BAsmCode[3] = 0x48 | HReg;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            break;
          case ModImm:
            CodeLen = 3;
            BAsmCode[0] = 0x38 | HReg;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            break;
        }
        break;
      case ModIndReg:
      case ModIdxReg:
      case ModDir:
      case ModMem:
        MinOneIs0 = True;
        HReg = AdrCnt;
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        switch (AdrType)
        {
          case ModIndReg:
            BAsmCode[0] = 0xe8 | AdrMode;
            break;
          case ModIdxReg:
            BAsmCode[0] = 0xf4 | AdrMode;
            break;
          case ModMem:
            BAsmCode[0] = 0xeb;
            break;
          case ModDir:
            BAsmCode[0] = 0x0f;
            break;
        }
        DecodeAdr(&ArgStr[2], MModReg16 | MModReg8 | MModImm);
        if (BAsmCode[0] == 0x0f)
         switch (AdrType)
         {
           case ModReg8:
             if (AdrMode == AccReg)
             {
               CodeLen = 2;
               BAsmCode[0] = 0x2f;
             }
             else
             {
               CodeLen = 3;
               BAsmCode[0] = 0xef;
               BAsmCode[2] = 0x20 | AdrMode;
             }
             break;
           case ModReg16:
             if (AdrMode == HLReg)
             {
               CodeLen = 2;
               BAsmCode[0] = 0x4f;
             }
             else
             {
               CodeLen = 3;
               BAsmCode[0] = 0xef;
               BAsmCode[2] = 0x40 | AdrMode;
             }
             break;
           case ModImm:
             CodeLen = 3 + OpSize;
             BAsmCode[0] = 0x37 | (OpSize << 3);
             memcpy(BAsmCode + 2, AdrVals, AdrCnt);
             break;
         }
         else
         {
           switch (AdrType)
           {
             case ModReg8:
               BAsmCode[1 + HReg] = 0x20 | AdrMode;
               break;
             case ModReg16:
               BAsmCode[1 + HReg] = 0x40 | AdrMode;
               break;
             case ModImm:
               BAsmCode[1 + HReg] = 0x37 | (OpSize << 3);
               break;
           }
           memcpy(BAsmCode + 2 + HReg, AdrVals, AdrCnt);
           CodeLen = 1 + HReg + 1 + AdrCnt;
         }
        break;
    }
  }
}

static void DecodePUSH_POP(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    if (!as_strcasecmp(ArgStr[1].str.p_str, "AF"))
    {
      CodeLen = 1;
      BAsmCode[0] = 0x56 | Code;
    }
    else
    {
      DecodeAdr(&ArgStr[1], MModReg16);
      if (AdrType == ModReg16)
      {
        if (AdrMode == 6) WrError(ErrNum_InvAddrMode);
        else
        {
          CodeLen = 1;
          BAsmCode[0] = 0x50 | Code | AdrMode;
        }
      }
    }
  }
}

static void DecodeLDA(Word Code)
{
  Byte HReg;

  UNUSED(Code);

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg16);
    if (AdrType == ModReg16)
    {
      HReg = 0x38 + AdrMode;
      DecodeAdr(&ArgStr[2], MModIndReg | MModIdxReg | MAssumeInd);
      switch (AdrType)
      {
        case ModIndReg:
          if (AdrMode < 4) WrError(ErrNum_InvAddrMode);
          else
          {
            CodeLen = 3;
            BAsmCode[0] = 0xf0 | AdrMode;
            BAsmCode[1] = 0;
            BAsmCode[2] = HReg;
          }
          break;
        case ModIdxReg:
          CodeLen = 2 + AdrCnt;
          BAsmCode[0] = 0xf4 + AdrMode;
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
          BAsmCode[1 + AdrCnt] = HReg;
          break;
      }
    }
  }
}

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

  if (!ChkArgCnt(2, 2));
  else if (as_strcasecmp(ArgStr[1].str.p_str, "HL")) WrError(ErrNum_InvAddrMode);
  else
  {
    Boolean OK;
    Integer AdrInt = EvalStrIntExpression(&ArgStr[2], Int16, &OK) - (EProgCounter() + 2);
    if (OK)
    {
      CodeLen = 3;
      BAsmCode[0] = 0x17;
      BAsmCode[1] = Lo(AdrInt);
      BAsmCode[2] = Hi(AdrInt);
    }
  }
}

static void DecodeEX(Word Code)
{
  Byte HReg;

  UNUSED(Code);

  /* work around the parser problem related to the ' character */

  if (!as_strncasecmp(ArgStr[2].str.p_str, "AF\'", 3))
    ArgStr[2].str.p_str[3] = '\0';

  if (!ChkArgCnt(2, 2));
  else if (ArgPair("DE", "HL"))
  {
    CodeLen = 1;
    BAsmCode[0] = 0x08;
  }
  else if ((ArgPair("AF", "AF\'")) || (ArgPair("AF", "AF`")))
  {
    CodeLen = 1;
    BAsmCode[0] = 0x09;
  }
  else
  {
    DecodeAdr(&ArgStr[1], MModReg16 | MModIndReg | MModIdxReg | MModMem | MModDir);
    switch (AdrType)
    {
      case ModReg16:
        HReg = 0x50 | AdrMode;
        DecodeAdr(&ArgStr[2], MModIndReg | MModIdxReg | MModMem | MModDir);
        switch (AdrType)
        {
          case ModIndReg:
            CodeLen = 2;
            BAsmCode[0] = 0xe0 | AdrMode;
            BAsmCode[1] = HReg;
            break;
          case ModIdxReg:
            CodeLen = 2 + AdrCnt;
            BAsmCode[0] = 0xf0 | AdrMode;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            BAsmCode[1 + AdrCnt] = HReg;
            break;
          case ModDir:
            CodeLen = 3;
            BAsmCode[0] = 0xe7;
            BAsmCode[1] = AdrVals[0];
            BAsmCode[2] = HReg;
            break;
          case ModMem:
            CodeLen = 4;
            BAsmCode[0] = 0xe3;
            memcpy(BAsmCode + 1, AdrVals, AdrCnt);
            BAsmCode[3] = HReg;
            break;
        }
        break;
      case ModIndReg:
      case ModIdxReg:
      case ModDir:
      case ModMem:
        switch (AdrType)
        {
          case ModIndReg:
            BAsmCode[0] = 0xe0 | AdrMode;
            break;
          case ModIdxReg:
            BAsmCode[0] = 0xf0 | AdrMode;
            break;
          case ModDir:
            BAsmCode[0] = 0xe7;
            break;
          case ModMem:
            BAsmCode[0] = 0xe3;
            break;
        }
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        HReg = 2 + AdrCnt;
        DecodeAdr(&ArgStr[2], MModReg16);
        if (AdrType == ModReg16)
        {
          BAsmCode[HReg - 1] = 0x50 | AdrMode;
          CodeLen = HReg;
        }
        break;
    }
  }
}

static void DecodeINC_DEC(Word Code)
{
  if (Hi(Code))
    SetOpSize(1);

  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModReg8 | MModReg16 | MModIndReg | MModIdxReg | MModDir | MModMem);
    if (OpSize==-1)
      OpSize = 0;
    switch (AdrType)
    {
      case ModReg8:
        CodeLen = 1;
        BAsmCode[0] = 0x80 | Lo(Code) | AdrMode;
        break;
      case ModReg16:
        CodeLen = 1;
        BAsmCode[0] = 0x90 | Lo(Code) | AdrMode;
        break;
      case ModIndReg:
        CodeLen = 2;
        BAsmCode[0] = 0xe0 | AdrMode;
        BAsmCode[1] = 0x87 | (OpSize << 4) | Lo(Code);
        break;
      case ModIdxReg:
        CodeLen = 2 + AdrCnt;
        BAsmCode[0] = 0xf0 | AdrMode;
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        BAsmCode[1 + AdrCnt] = 0x87 | (OpSize << 4) | Lo(Code);
        break;
      case ModDir:
        CodeLen = 2;
        BAsmCode[0] = 0x87 | (OpSize << 4) | Lo(Code);
        BAsmCode[1] = AdrVals[0];
        break;
      case ModMem:
        CodeLen = 4;
        BAsmCode[0] = 0xe3;
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        BAsmCode[3] = 0x87 | (OpSize << 4) | Lo(Code);
        BAsmCode[1] = AdrVals[0];
        break;
    }
  }
}

static void DecodeINCX_DECX(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModDir);
    if (AdrType == ModDir)
    {
      CodeLen = 2;
      BAsmCode[0] = Code;
      BAsmCode[1] = AdrVals[0];
    }
  }
}

static void DecodeMUL_DIV(Word Code)
{
  if (!ChkArgCnt(2, 2));
  else if (as_strcasecmp(ArgStr[1].str.p_str, "HL")) WrError(ErrNum_InvAddrMode);
  else
  {
    OpSize = 0;
    DecodeAdr(&ArgStr[2], MModReg8 | MModIndReg | MModIdxReg | MModDir | MModMem | MModImm);
    switch (AdrType)
    {
      case ModReg8:
        CodeLen = 2;
        BAsmCode[0] = 0xf8 + AdrMode;
        BAsmCode[1] = Code;
        break;
      case ModIndReg:
        CodeLen = 2;
        BAsmCode[0] = 0xe0 + AdrMode;
        BAsmCode[1] = Code;
        break;
      case ModIdxReg:
        CodeLen = 2 + AdrCnt;
        BAsmCode[0] = 0xf0 + AdrMode;
        BAsmCode[1 + AdrCnt] = Code;
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        break;
      case ModDir:
        CodeLen = 3;
        BAsmCode[0] = 0xe7;
        BAsmCode[1] = AdrVals[0];
        BAsmCode[2] = Code;
        break;
      case ModMem:
        CodeLen = 4;
        BAsmCode[0] = 0xe3;
        BAsmCode[3] = Code;
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        break;
      case ModImm:
        CodeLen = 2;
        BAsmCode[0] = Code;
        BAsmCode[1] = AdrVals[0];
        break;
    }
  }
}

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

  if (ChkArgCnt(1, 2))
  {
    Byte cond_code;
    Boolean OK;
    tSymbolFlags Flags;
    Integer AdrInt;

    if (ArgCnt == 1)
      cond_code = COND_CODE_TRUE;
    else if (!decode_condition(ArgStr[1].str.p_str, &cond_code))
    {
      WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
      return;
    }

    AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[ArgCnt], Int16, &OK, &Flags) - (EProgCounter() + 2);
    if (OK)
    {
      if (!mSymbolQuestionable(Flags) && ((AdrInt > 127) || (AdrInt < -128))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        CodeLen = 2;
        BAsmCode[0] = 0xc0 | cond_code;
        BAsmCode[1] = AdrInt & 0xff;
      }
    }
  }
}

static void DecodeCALL_JP(Word Code)
{
  if (ChkArgCnt(1, 2))
  {
    Byte cond_code;

    if (ArgCnt == 1)
      cond_code = COND_CODE_TRUE;
    else if (!decode_condition(ArgStr[1].str.p_str, &cond_code))
    {
      WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
      return;
    }

    OpSize = 1;
    DecodeAdr(&ArgStr[ArgCnt], MModIndReg | MModIdxReg | MModMem | MAssumeInd);
    switch (AdrType)
    {
      case ModIndReg:
        CodeLen = 2;
        BAsmCode[0] = 0xe8 | AdrMode;
        BAsmCode[1] = 0xc0 | (Code << 4) | cond_code;
        break;
      case ModIdxReg:
        CodeLen = 2 + AdrCnt;
        BAsmCode[0] = 0xf4 | AdrMode;
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        BAsmCode[1 + AdrCnt] = 0xc0 | (Code << 4) | cond_code;
        break;
      case ModMem:
        if (cond_code == COND_CODE_TRUE)
        {
          CodeLen = 3;
          BAsmCode[0] = 0x1a + (Code << 1);
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        }
        else
        {
          CodeLen = 4;
          BAsmCode[0] = 0xeb;
          memcpy(BAsmCode + 1, AdrVals, AdrCnt);
          BAsmCode[3] = 0xc0 | (Code << 4) | cond_code;
        }
        break;
    }
  }
}

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

  if (ChkArgCnt(0, 1))
  {
    Byte cond_code;

    if (ArgCnt == 0)
      cond_code = COND_CODE_TRUE;
    else if (!decode_condition(ArgStr[1].str.p_str, &cond_code))
    {
      WrStrErrorPos(ErrNum_UndefCond, &ArgStr[1]);
      return;
    }

    if (cond_code == COND_CODE_TRUE)
    {
      CodeLen = 1;
      BAsmCode[0] = 0x1e;
    }
    else
    {
      CodeLen = 2;
      BAsmCode[0] = 0xfe;
      BAsmCode[1] = 0xd0 | cond_code;
    }
  }
}

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

  if (ChkArgCnt(1, 2))
  {
    if (ArgCnt == 1)
    {
      AdrType = ModReg8;
      AdrMode = 0;
      OpSize = 0;
    }
    else
      DecodeAdr(&ArgStr[1], MModReg8 | MModReg16);
    if (AdrType != ModNone)
    {
      if (AdrMode != 0) WrError(ErrNum_InvAddrMode);
      else
      {
        Boolean OK;
        tSymbolFlags Flags;
        Integer AdrInt = EvalStrIntExpressionWithFlags(&ArgStr[ArgCnt], Int16, &OK, &Flags) - (EProgCounter() + 2);

        if (OK)
        {
          if (!mSymbolQuestionable(Flags) && ((AdrInt > 127) || (AdrInt < -128))) WrError(ErrNum_JmpDistTooBig);
          else
          {
            CodeLen = 2;
            BAsmCode[0] = 0x18 | OpSize;
            BAsmCode[1] = AdrInt & 0xff;
          }
        }
      }
    }
  }
}

static void DecodeJRL_CALR(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Integer AdrInt = EvalStrIntExpression(&ArgStr[1], Int16, &OK) - (EProgCounter() + 2);
    if (OK)
    {
      CodeLen = 3;
      BAsmCode[0] = Code;
      if ((Code == 0x1b)
       && (AdrInt >= -128)
       && (AdrInt <= 127))
        WrError(ErrNum_ShortJumpPossible);
      BAsmCode[1] = Lo(AdrInt);
      BAsmCode[2] = Hi(AdrInt);
    }
  }
}

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

static void AddW(const char *Name, Word Code, InstProc Proc)
{
  char Str[20];

  AddInstTable(InstTable, Name, Code, Proc);
  as_snprintf(Str, sizeof(Str), "%sW", Name);
  AddInstTable(InstTable, Str, Code | 0x100, Proc);
}

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

static void AddMove(const char *NName, Byte NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeMove);
}

static void AddShift(const char *NName, Word NCode, Boolean NMay)
{
  AddInstTable(InstTable, NName, NCode | (NMay ? 0x100 : 0), DecodeShift);
}

static void AddBit(const char *NName, Byte NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeBit);
}

static void AddAcc(const char *NName, Byte NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeAcc);
}

static void AddCondition(const char *NName, Byte NCode)
{
  order_array_rsv_end(Conditions, Condition);
  Conditions[InstrZ].Name = NName;
  Conditions[InstrZ++].Code = NCode;
}

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

  add_null_pseudo(InstTable);

  AddW("LD", 0, DecodeLD);
  AddInstTable(InstTable, "PUSH", 0, DecodePUSH_POP);
  AddInstTable(InstTable, "POP" , 8, DecodePUSH_POP);
  AddInstTable(InstTable, "LDA", 0, DecodeLDA);
  AddInstTable(InstTable, "LDAR", 0, DecodeLDAR);
  AddInstTable(InstTable, "EX", 0, DecodeEX);
  AddW("INC", 0, DecodeINC_DEC);
  AddW("DEC", 8, DecodeINC_DEC);
  AddInstTable(InstTable, "INCX", 0x07, DecodeINCX_DECX);
  AddInstTable(InstTable, "DECX", 0x0f, DecodeINCX_DECX);
  AddInstTable(InstTable, "MUL", 0x12, DecodeMUL_DIV);
  AddInstTable(InstTable, "DIV", 0x13, DecodeMUL_DIV);
  AddInstTable(InstTable, "JR", 0, DecodeJR);
  AddInstTable(InstTable, "CALL", 1, DecodeCALL_JP);
  AddInstTable(InstTable, "JP", 0, DecodeCALL_JP);
  AddInstTable(InstTable, "RET", 0, DecodeRET);
  AddInstTable(InstTable, "DJNZ", 0, DecodeDJNZ);
  AddInstTable(InstTable, "JRL", 0x1b, DecodeJRL_CALR);
  AddInstTable(InstTable, "CALR", 0x1d, DecodeJRL_CALR);

  AddFixed("EXX" , 0x0a); AddFixed("CCF" , 0x0e);
  AddFixed("SCF" , 0x0d); AddFixed("RCF" , 0x0c);
  AddFixed("NOP" , 0x00); AddFixed("HALT", 0x01);
  AddFixed("DI"  , 0x02); AddFixed("EI"  , 0x03);
  AddFixed("SWI" , 0xff); AddFixed("RLCA", 0xa0);
  AddFixed("RRCA", 0xa1); AddFixed("RLA" , 0xa2);
  AddFixed("RRA" , 0xa3); AddFixed("SLAA", 0xa4);
  AddFixed("SRAA", 0xa5); AddFixed("SLLA", 0xa6);
  AddFixed("SRLA", 0xa7); AddFixed("RETI", 0x1f);

  AddMove("LDI" , 0x58);
  AddMove("LDIR", 0x59);
  AddMove("LDD" , 0x5a);
  AddMove("LDDR", 0x5b);
  AddMove("CPI" , 0x5c);
  AddMove("CPIR", 0x5d);
  AddMove("CPD" , 0x5e);
  AddMove("CPDR", 0x5f);

  AddShift("RLC", 0xa0, True );
  AddShift("RRC", 0xa1, True );
  AddShift("RL" , 0xa2, True );
  AddShift("RR" , 0xa3, True );
  AddShift("SLA", 0xa4, True );
  AddShift("SRA", 0xa5, True );
  AddShift("SLL", 0xa6, True );
  AddShift("SRL", 0xa7, True );
  AddShift("RLD", 0x10, False);
  AddShift("RRD", 0x11, False);

  AddBit("BIT" , 0xa8);
  AddBit("SET" , 0xb8);
  AddBit("RES" , 0xb0);
  AddBit("TSET", 0x18);

  AddAcc("DAA", 0x0b);
  AddAcc("CPL", 0x10);
  AddAcc("NEG", 0x11);

  InstrZ = 0;
  AddInstTable(InstTable, "ADD", InstrZ++, DecodeALU2);
  AddInstTable(InstTable, "ADC", InstrZ++, DecodeALU2);
  AddInstTable(InstTable, "SUB", InstrZ++, DecodeALU2);
  AddInstTable(InstTable, "SBC", InstrZ++, DecodeALU2);
  AddInstTable(InstTable, "AND", InstrZ++, DecodeALU2);
  AddInstTable(InstTable, "XOR", InstrZ++, DecodeALU2);
  AddInstTable(InstTable, "OR" , InstrZ++, DecodeALU2);
  AddInstTable(InstTable, "CP" , InstrZ++, DecodeALU2);

  InstrZ = 0;
  AddCondition("F"  ,  0); AddCondition("T"  , COND_CODE_TRUE);
  AddCondition("Z"  ,  6); AddCondition("NZ" , 14);
  AddCondition("C"  ,  7); AddCondition("NC" , 15);
  AddCondition("PL" , 13); AddCondition("MI" ,  5);
  AddCondition("P"  , 13); AddCondition("M"  ,  5);
  AddCondition("NE" , 14); AddCondition("EQ" ,  6);
  AddCondition("OV" ,  4); AddCondition("NOV", 12);
  AddCondition("PE" ,  4); AddCondition("PO" , 12);
  AddCondition("GE" ,  9); AddCondition("LT" ,  1);
  AddCondition("GT" , 10); AddCondition("LE" ,  2);
  AddCondition("UGE", 15); AddCondition("ULT",  7);
  AddCondition("UGT", 11); AddCondition("ULE",  3);
  AddCondition(NULL ,  0);

  AddIntelPseudo(InstTable, eIntPseudoFlag_LittleEndian);
}

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

  order_array_free(Conditions);
}

static void MakeCode_90C141(void)
{
  OpSize = eSymbolSizeUnknown;

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

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

static Boolean IsDef_90C141(void)
{
  return False;
}

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

static Boolean ChkMoreOneArg(void)
{
  return (ArgCnt > 1);
}

static void SwitchTo_90C141(void)
{
  const TFamilyDescr *p_descr = FindFamilyByName("TLCS-90");

  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);
  SetIsOccupiedFnc = ChkMoreOneArg;

  PCSymbol = "$";
  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_90C141;
  IsDef = IsDef_90C141;
  SwitchFrom = SwitchFrom_90C141;
  InitFields();
}

void code90c141_init(void)
{
  CPU90C141 = AddCPU("90C141", SwitchTo_90C141);
}
