/* codest9.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator SGS-Thomson ST9                                             */
/*                                                                           */
/*****************************************************************************/

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

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

#include "codest9.h"

typedef struct
{
  char *Name;
  Word Code;
} FixedOrder;


#define WorkOfs 0xd0

#define ModNone (-1)
#define ModReg 0
#define MModReg (1l << ModReg)                  /* Rn */
#define ModWReg 1
#define MModWReg (1l << ModWReg)                /* rn */
#define ModRReg 2
#define MModRReg (1l << ModRReg)                /* RRn */
#define ModWRReg 3
#define MModWRReg (1l << ModWRReg)              /* rrn */
#define ModIReg 4
#define MModIReg (1l << ModIReg)                /* (Rn) */
#define ModIWReg 5
#define MModIWReg (1l << ModIWReg)              /* (rn) */
#define ModIRReg 6
#define MModIRReg (1l << ModIRReg)              /* (RRn) */
#define ModIWRReg 7
#define MModIWRReg (1l << ModIWRReg)            /* (rrn) */
#define ModIncWReg 8
#define MModIncWReg (1l << ModIncWReg)          /* (rn)+ */
#define ModIncWRReg 9
#define MModIncWRReg (1l << ModIncWRReg)        /* (rrn)+ */
#define ModDecWRReg 10
#define MModDecWRReg (1l << ModDecWRReg)        /* -(rrn) */
#define ModDisp8WReg 11
#define MModDisp8WReg (1l << ModDisp8WReg)      /* d8(rn) */
#define ModDisp8WRReg 12
#define MModDisp8WRReg (1l << ModDisp8WRReg)    /* d8(rrn) */
#define ModDisp16WRReg 13
#define MModDisp16WRReg (1l << ModDisp16WRReg)  /* d16(rrn) */
#define ModDispRWRReg 14
#define MModDispRWRReg (1l << ModDispRWRReg)    /* rrm(rrn */
#define ModAbs 15
#define MModAbs (1l << ModAbs)                  /* NN */
#define ModImm 16
#define MModImm (1l << ModImm)                  /* #N/#NN */
#define ModDisp8RReg 17
#define MModDisp8RReg (1l << ModDisp8RReg)      /* d8(RRn) */
#define ModDisp16RReg 18
#define MModDisp16RReg (1l << ModDisp16RReg)    /* d16(RRn) */


static CPUVar CPUST9020,CPUST9030,CPUST9040,CPUST9050;

static ShortInt AdrMode,AbsSeg;
static Byte AdrPart,OpSize;
static Byte AdrVals[3];

static LongInt DPAssume;

static as_assume_rec_t ASSUMEST9s[] =
{
  {"DP", &DPAssume, 0,  1, 0x0, NULL}
};

/*--------------------------------------------------------------------------*/
/* helper functions */

static Boolean DecodeReg(char *Asc_O, Byte *Erg, Byte *Size)
{
  char *Asc, *p_end;

  *Size = 0;
  Asc=Asc_O;

  if (strlen(Asc) < 2) return False;
  if (*Asc != 'r')
    return False;
  Asc++;
  if (*Asc == 'r')
  {
    if (strlen(Asc) < 2) return False;
    *Size = 1; Asc++;
  }
  else
    *Size = 0;

  *Erg = strtoul(Asc, &p_end, 10);
  if (*p_end || (*Erg > 15)) return False;
  if ((*Size == 1) && (Odd(*Erg))) return False;

  return True;
}

static LargeInt eval_outer_disp(const tStrComp *p_arg, IntType type, Boolean *p_ok)
{
  if (p_arg->str.p_str[0])
    return EvalStrIntExpression(p_arg, type, p_ok);
  else
  {
    *p_ok = True;
    return 0;
  }
}

static void DecodeAdr(tStrComp *pArg, LongWord Mask)
{
  Word AdrWord;
  int level;
  Byte flg, Size;
  Boolean OK, IsIndirect;
  tEvalResult EvalResult;
  char *p;
  int l;

  AdrMode = ModNone; AdrCnt = 0;

  /* 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);
        AdrVals[0] = Hi(AdrWord);
        AdrVals[1] = Lo(AdrWord);
        break;
    }
    if (OK)
    {
      AdrMode = ModImm;
      AdrCnt = OpSize + 1;
    }
    goto func_exit;
  }

  /* Arbeitsregister */

  if (DecodeReg(pArg->str.p_str, &AdrPart, &Size))
  {
    if (Size == 0)
    {
      if (Mask & MModWReg) AdrMode = ModWReg;
      else
      {
        AdrVals[0] = WorkOfs + AdrPart;
        AdrCnt = 1;
        AdrMode = ModReg;
      }
    }
    else
    {
      if (Mask & MModWRReg) AdrMode = ModWRReg;
      else
      {
        AdrVals[0] = WorkOfs + AdrPart;
        AdrCnt = 1;
        AdrMode = ModRReg;
      }
    }
    goto func_exit;
  }

  l = strlen(pArg->str.p_str);

  /* Postinkrement */

  if ((l > 0) && (pArg->str.p_str[l - 1] == '+'))
  {
    if ((l < 3) || (*pArg->str.p_str != '(') || (pArg->str.p_str[l - 2] != ')')) WrError(ErrNum_InvAddrMode);
    else
    {
      tStrComp RegComp;

      pArg->str.p_str[l - 2] = '\0'; pArg->Pos.Len -= 2;
      StrCompRefRight(&RegComp, pArg, 1);
      if (!DecodeReg(RegComp.str.p_str, &AdrPart, &Size)) WrStrErrorPos(ErrNum_InvReg, &RegComp);
      AdrMode = (Size == 0) ? ModIncWReg : ModIncWRReg;
    }
    goto func_exit;
  }

  /* Predekrement */

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

    pArg->str.p_str[l - 1] = '\0';  pArg->Pos.Len--;
    StrCompRefRight(&RegComp, pArg, 2);
    if (DecodeReg(RegComp.str.p_str, &AdrPart, &Size))
    {
      if (Size == 0) WrError(ErrNum_InvAddrMode); else AdrMode = ModDecWRReg;
      goto func_exit;
    }
  }

  /* indirekt<->direkt */

  if ((l < 3) || (pArg->str.p_str[l - 1] != ')'))
  {
    IsIndirect = False; p = pArg->str.p_str;
  }
  else
  {
    level = 0; p = pArg->str.p_str + l - 2; flg = 0;
    while ((p >= pArg->str.p_str) && (level >= 0))
    {
      switch (*p)
      {
        case '(': if (flg == 0) level--; break;
        case ')': if (flg == 0) level++; break;
        case '\'': if ((!(flg & 2))) flg ^= 1; break;
        case '"': if ((!(flg & 1))) flg ^= 2; break;
      }
      p--;
    }
    IsIndirect = (level == -1) && ((p < pArg->str.p_str) || ((*p == '.') || (*p == '_') || (isdigit(((unsigned int)*p) & 0xff)) || (isalpha(((unsigned int)*p) & 0xff))));
  }

  /* indirekt */

  if (IsIndirect)
  {
    tStrComp RegComp;

    /* discard closing ) at end */

    pArg->str.p_str[--l] = '\0'; pArg->Pos.Len--;

    /* split off part in () */

    StrCompRefRight(&RegComp, pArg, p + 2 - pArg->str.p_str);

    /* truncate arg to everything before ( */

    p[1] = '\0';
    pArg->Pos.Len = p + 1 - pArg->str.p_str;
    if (DecodeReg(RegComp.str.p_str, &AdrPart, &Size))
    {
      if (Size == 0)   /* d(r) */
      {
        AdrVals[0] = eval_outer_disp(pArg, Int8, &OK);
        if (OK)
        {
          if ((Mask & MModIWReg) && (AdrVals[0] == 0)) AdrMode = ModIWReg;
          else if (((Mask & MModIReg) != 0) && (AdrVals[0] == 0))
          {
            AdrVals[0] = WorkOfs + AdrPart; AdrMode = ModIReg; AdrCnt = 1;
          }
          else
          {
            AdrMode = ModDisp8WReg; AdrCnt = 1;
          }
        }
      }
      else            /* ...(rr) */
      {
        if (DecodeReg(pArg->str.p_str, AdrVals, &Size))
        {             /* rr(rr) */
          if (Size != 1) WrError(ErrNum_InvAddrMode);
          else
          {
            AdrMode = ModDispRWRReg; AdrCnt = 1;
          }
        }
        else
        {             /* d(rr) */
          AdrWord = eval_outer_disp(pArg, Int16, &OK);
          if ((AdrWord == 0) && (Mask & (MModIRReg | MModIWRReg)))
          {
            if (Mask & MModIWRReg) AdrMode = ModIWRReg;
            else
            {
              AdrMode = ModIRReg; AdrVals[0] = AdrPart + WorkOfs; AdrCnt = 1;
            }
          }
          else if ((AdrWord < 0x100) && (Mask & (MModDisp8WRReg | MModDisp8RReg)))
          {
            if (Mask & MModDisp8WRReg)
            {
              AdrVals[0] = Lo(AdrWord); AdrCnt = 1; AdrMode = ModDisp8WRReg;
            }
            else
            {
              AdrVals[0] = AdrPart + WorkOfs;
              AdrVals[1] = Lo(AdrWord);
              AdrCnt = 2;
              AdrMode = ModDisp8RReg;
            }
          }
          else if (Mask & MModDisp16WRReg)
          {
            AdrVals[0] = Hi(AdrWord);
            AdrVals[1] = Lo(AdrWord);
            AdrCnt = 2;
            AdrMode = ModDisp16WRReg;
          }
          else
          {
            AdrVals[0] = AdrPart + WorkOfs;
            AdrVals[2] = Hi(AdrWord);
            AdrVals[1] = Lo(AdrWord);
            AdrCnt = 3;
            AdrMode = ModDisp16RReg;
          }
        }
      }
    }
    else             /* ...(RR) */
    {
      AdrWord = EvalStrIntExpressionWithResult(&RegComp, UInt9, &EvalResult);
      if (!(EvalResult.AddrSpaceMask & (1 << SegReg)))  WrError(ErrNum_InvAddrMode);
      else if (AdrWord < 0xff)
      {
        AdrVals[0] = Lo(AdrWord);
        AdrWord = eval_outer_disp(pArg, Int8, &OK);
        if (AdrWord != 0) WrError(ErrNum_OverRange);
        else
        {
          AdrCnt = 1; AdrMode = ModIReg;
        }
      }
      else if ((AdrWord > 0x1ff) || (Odd(AdrWord))) WrError(ErrNum_InvAddrMode);
      else
      {
        AdrVals[0] = Lo(AdrWord);
        AdrWord = eval_outer_disp(pArg, Int16, &OK);
        if ((AdrWord == 0) && (Mask & MModIRReg))
        {
          AdrCnt = 1; AdrMode = ModIRReg;
        }
        else if ((AdrWord < 0x100) && (Mask & MModDisp8RReg))
        {
          AdrVals[1] = Lo(AdrWord); AdrCnt = 2; AdrMode = ModDisp8RReg;
        }
        else
        {
          AdrVals[2] = Hi(AdrWord);
          AdrVals[1] = Lo(AdrWord);
          AdrCnt = 3;
          AdrMode = ModDisp16RReg;
        }
      }
    }
    goto func_exit;
  }

  /* direkt */

  AdrWord = EvalStrIntExpressionWithResult(pArg, UInt16, &EvalResult);
  if (EvalResult.OK)
  {
    if (!(EvalResult.AddrSpaceMask & (1 << SegReg)))
    {
      AdrMode = ModAbs;
      AdrVals[0] = Hi(AdrWord);
      AdrVals[1] = Lo(AdrWord);
      AdrCnt = 2;
      ChkSpace(AbsSeg, EvalResult.AddrSpaceMask);
    }
    else if (AdrWord < 0xff)
    {
      AdrMode = ModReg;
      AdrVals[0] = Lo(AdrWord);
      AdrCnt = 1;
    }
    else if ((AdrWord > 0x1ff) || (Odd(AdrWord))) WrError(ErrNum_InvAddrMode);
    else
    {
      AdrMode = ModRReg;
      AdrVals[0] = Lo(AdrWord);
      AdrCnt = 1;
    }
  }

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

static Boolean SplitBit(tStrComp *pDest, const tStrComp *pSrc, Byte *pErg)
{
  char *p;
  Boolean OK;
  Byte Inv = 0;

  StrCompRefRight(pDest, pSrc, 0);
  p = RQuotPos(pDest->str.p_str, '.');
  if ((!p) || (p == pDest->str.p_str + strlen(pDest->str.p_str) + 1))
  {
    Integer val;

    if (*pDest->str.p_str == '!')
    {
      Inv = 1;
      pDest->str.p_str++;
      pDest->Pos.StartCol++;
      pDest->Pos.Len--;
    }
    val = EvalStrIntExpression(pDest, UInt8, &OK);
    if (OK)
    {
      *pErg = (val & 15) ^ Inv;
      as_snprintf(pDest->str.p_str, STRINGSIZE, "r%d", (int)(val >> 4));
      return True;
    }
    return False;
  }

  Inv = !!(p[1] == '!');
  *pErg = Inv + (EvalStrIntExpressionOffs(pSrc, p + 1 + Inv - pSrc->str.p_str, UInt3, &OK) << 1);
  *p = '\0';
  return OK;
}

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

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

static void DecodeLD(Word IsLDW)
{
  Byte HReg, HPart;
  Word Mask1, Mask2;

  if (ChkArgCnt(2, 2))
  {
    if (IsLDW)
    {
      OpSize = 1; Mask1 = MModWRReg; Mask2 = MModRReg;
    }
    else
    {
      Mask1 = MModWReg; Mask2 = MModReg;
    }
    DecodeAdr(&ArgStr[1], Mask1 | Mask2 | MModIWReg | MModDisp8WReg | MModIncWReg |
                         MModIWRReg | MModIncWRReg | MModDecWRReg | MModDisp8WRReg |
                         MModDisp16WRReg | MModDispRWRReg | MModAbs | MModIRReg);
    switch (AdrMode)
    {
      case ModWReg:
      case ModWRReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask1 | Mask2 | MModIWReg | MModDisp8WReg | MModIWRReg |
                             MModIncWRReg | MModDecWRReg | MModDispRWRReg | MModDisp8WRReg |
                             MModDisp16WRReg | MModAbs | MModImm);
        switch (AdrMode)
        {
          case ModWReg:
            BAsmCode[0] = (HReg << 4) + 8;
            BAsmCode[1] = WorkOfs + AdrPart;
            CodeLen = 2;
            break;
          case ModWRReg:
            BAsmCode[0] = 0xe3;
            BAsmCode[1] = (HReg << 4) + AdrPart;
            CodeLen = 2;
            break;
          case ModReg:
            BAsmCode[0] = (HReg << 4) + 8;
            BAsmCode[1] = AdrVals[0];
            CodeLen = 2;
            break;
          case ModRReg:
            BAsmCode[0] = 0xef;
            BAsmCode[1] = AdrVals[0];
            BAsmCode[2] = HReg + WorkOfs;
            CodeLen = 3;
            break;
          case ModIWReg:
            if (OpSize == 0)
            {
              BAsmCode[0] = 0xe4;
              BAsmCode[1] = (HReg << 4) + AdrPart;
              CodeLen = 2;
            }
            else
            {
              BAsmCode[0] = 0xa6;
              BAsmCode[1] = 0xf0 + AdrPart;
              BAsmCode[2] = WorkOfs + HReg;
              CodeLen = 3;
            }
            break;
          case ModDisp8WReg:
            BAsmCode[0] = 0xb3 + (OpSize * 0x2b);
            BAsmCode[1] = (HReg << 4) + AdrPart;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
          case ModIWRReg:
            BAsmCode[0] = 0xb5 + (OpSize * 0x2e);
            BAsmCode[1] = (HReg << 4) + AdrPart + OpSize;
            CodeLen = 2;
            break;
          case ModIncWRReg:
            BAsmCode[0] = 0xb4 + (OpSize * 0x21);
            BAsmCode[1] = 0xf1 + AdrPart;
            BAsmCode[2] = WorkOfs + HReg;
            CodeLen = 3;
            break;
          case ModDecWRReg:
            BAsmCode[0] = 0xc2 + OpSize;
            BAsmCode[1] = 0xf1 + AdrPart;
            BAsmCode[2] = WorkOfs + HReg;
            CodeLen = 3;
            break;
          case ModDispRWRReg:
            BAsmCode[0] = 0x60;
            BAsmCode[1] = (0x10 * (1 - OpSize)) + (AdrVals[0] << 4) + AdrPart;
            BAsmCode[2] = 0xf0 + HReg;
            CodeLen = 3;
            break;
          case ModDisp8WRReg:
            BAsmCode[0] = 0x7f + (OpSize * 7);
            BAsmCode[1] = 0xf1 + AdrPart;
            BAsmCode[2] = AdrVals[0];
            BAsmCode[3] = HReg + WorkOfs;
            CodeLen = 4;
            break;
          case ModDisp16WRReg:
            BAsmCode[0] = 0x7f + (OpSize * 7);
            BAsmCode[1] = 0xf0 + AdrPart;
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            BAsmCode[2 + AdrCnt] = HReg + WorkOfs;
            CodeLen = 3 + AdrCnt;
            break;
          case ModAbs:
            BAsmCode[0] = 0xc4 + (OpSize * 0x1e);
            BAsmCode[1] = 0xf0 + AdrPart;
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            CodeLen = 2 + AdrCnt;
            break;
          case ModImm:
            if (OpSize == 0)
            {
              BAsmCode[0] = (HReg << 4) + 0x0c;
              memcpy(BAsmCode + 1, AdrVals, AdrCnt);
              CodeLen = 1 + AdrCnt;
            }
            else
            {
              BAsmCode[0] = 0xbf;
              BAsmCode[1] = WorkOfs + HReg;
              memcpy(BAsmCode + 2, AdrVals, AdrCnt);
              CodeLen = 2 + AdrCnt;
            }
            break;
        }
        break;
      case ModReg:
      case ModRReg:
        HReg = AdrVals[0];
        DecodeAdr(&ArgStr[2], Mask1 | Mask2 | MModIWReg | MModIWRReg | MModIncWRReg |
                             MModDecWRReg | MModDispRWRReg | MModDisp8WRReg | MModDisp16WRReg |
                             MModImm);
        switch (AdrMode)
        {
          case ModWReg:
            BAsmCode[0] = (AdrPart << 4) + 0x09;
            BAsmCode[1] = HReg;
            CodeLen = 2;
            break;
          case ModWRReg:
            BAsmCode[0] = 0xef;
            BAsmCode[1] = WorkOfs + AdrPart;
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0xf4 - (OpSize * 5);
            BAsmCode[1] = AdrVals[0];
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
          case ModIWReg:
            BAsmCode[0] = 0xe7 - (0x41 * OpSize);
            BAsmCode[1] = 0xf0 + AdrPart;
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
          case ModIWRReg:
            BAsmCode[0] = 0x72 + (OpSize * 12);
            BAsmCode[1] = 0xf1 + AdrPart - OpSize;
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
          case ModIncWRReg:
            BAsmCode[0] = 0xb4 + (0x21 * OpSize);
            BAsmCode[1] = 0xf1 + AdrPart;
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
          case ModDecWRReg:
            BAsmCode[0] = 0xc2 + OpSize;
            BAsmCode[1] = 0xf1 + AdrPart;
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
          case ModDisp8WRReg:
            BAsmCode[0] = 0x7f + (OpSize * 7);
            BAsmCode[1] = 0xf1 + AdrPart;
            BAsmCode[2] = AdrVals[0];
            BAsmCode[3] = HReg;
            CodeLen = 4;
            break;
          case ModDisp16WRReg:
            BAsmCode[0] = 0x7f + (OpSize * 7);
            BAsmCode[1] = 0xf0 + AdrPart;
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            BAsmCode[2 + AdrCnt] = HReg;
            CodeLen = 3 + AdrCnt;
            break;
          case ModImm:
            BAsmCode[0] = 0xf5 - (OpSize * 0x36);
            BAsmCode[1] = HReg;
            memcpy(BAsmCode + 2, AdrVals, 2);
            CodeLen = 2 + AdrCnt;
            break;
        }
        break;
      case ModIWReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask2 | Mask1);
        switch (AdrMode)
        {
          case ModWReg:
            BAsmCode[0] = 0xe5;
            BAsmCode[1] = (HReg << 4) + AdrPart;
            CodeLen = 2;
            break;
          case ModWRReg:
            BAsmCode[0] = 0x96;
            BAsmCode[1] = WorkOfs + AdrPart;
            BAsmCode[2] = 0xf0 + HReg;
            CodeLen = 3;
            break;
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0xe6 - (0x50 * OpSize);
            BAsmCode[1] = AdrVals[0];
            BAsmCode[2] = 0xf0 + HReg;
            CodeLen = 3;
            break;
        }
        break;
      case ModDisp8WReg:
        BAsmCode[2] = AdrVals[0]; HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask1);
        switch (AdrMode)
        {
          case ModWReg:
          case ModWRReg:
            BAsmCode[0] = 0xb2 + (OpSize * 0x2c);
            BAsmCode[1] = (AdrPart << 4) + (OpSize << 4) + HReg;
            CodeLen = 3;
            break;
        }
        break;
      case ModIncWReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], MModIncWRReg * (1 - OpSize));
        switch (AdrMode)
        {
          case ModIncWRReg:
            BAsmCode[0] = 0xd7;
            BAsmCode[1] = (HReg << 4) + AdrPart + 1;
            CodeLen = 2;
            break;
        }
        break;
      case ModIWRReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], (MModIWReg * (1 - OpSize)) | Mask1 | Mask2 | MModIWRReg | MModImm);
        switch (AdrMode)
        {
          case ModIWReg:
            BAsmCode[0] = 0xb5;
            BAsmCode[1] = (AdrPart << 4) + HReg + 1;
            CodeLen = 2;
            break;
          case ModWReg:
            BAsmCode[0] = 0x72;
            BAsmCode[1] = 0xf0 + HReg;
            BAsmCode[2] = AdrPart + WorkOfs;
            CodeLen = 3;
            break;
          case ModWRReg:
            BAsmCode[0] = 0xe3;
            BAsmCode[1] = (HReg << 4) + 0x10 + AdrPart;
            CodeLen = 2;
            break;
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0x72 + (OpSize * 0x4c);
            BAsmCode[1] = 0xf0 + HReg + OpSize;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
          case ModIWRReg:
            if (OpSize == 0)
            {
              BAsmCode[0] = 0x73;
              BAsmCode[1] = 0xf0 + AdrPart;
              BAsmCode[2] = WorkOfs + HReg;
              CodeLen = 3;
            }
            else
            {
              BAsmCode[0] = 0xe3;
              BAsmCode[1] = 0x11 + (HReg << 4) + AdrPart;
              CodeLen = 2;
            }
            break;
          case ModImm:
            BAsmCode[0] = 0xf3 - (OpSize * 0x35);
            BAsmCode[1] = 0xf0 + HReg;
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            CodeLen = 2 + AdrCnt;
            break;
        }
        break;
      case ModIncWRReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask2 | (MModIncWReg * (1 - OpSize)));
        switch (AdrMode)
        {
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0xb4 + (OpSize * 0x21);
            BAsmCode[1] = 0xf0 + HReg;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
          case ModIncWReg:
            BAsmCode[0] = 0xd7;
            BAsmCode[1] = (AdrPart << 4) + HReg;
            CodeLen = 2;
            break;
        }
        break;
      case ModDecWRReg:
         HReg = AdrPart;
         DecodeAdr(&ArgStr[2], Mask2);
         switch (AdrMode)
         {
           case ModReg:
           case ModRReg:
             BAsmCode[0] = 0xc2 + OpSize;
             BAsmCode[1] = 0xf0 + HReg;
             BAsmCode[2] = AdrVals[0];
             CodeLen = 3;
             break;
         }
         break;
      case ModDispRWRReg:
        HReg = AdrPart; HPart = AdrVals[0];
        DecodeAdr(&ArgStr[2], Mask1);
        switch (AdrMode)
        {
          case ModWReg:
          case ModWRReg:
            BAsmCode[0] = 0x60;
            BAsmCode[1] = (0x10 * (1 - OpSize)) + 0x01 + (HPart << 4) + HReg;
            BAsmCode[2] = 0xf0 + AdrPart;
            CodeLen = 3;
            break;
        }
        break;
      case ModDisp8WRReg:
        BAsmCode[2] = AdrVals[0]; HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask2 | (OpSize * MModImm));
        switch (AdrMode)
        {
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0x26 + (OpSize * 0x60);
            BAsmCode[1] = 0xf1 + HReg;
            BAsmCode[3] = AdrVals[0];
            CodeLen = 4;
            break;
          case ModImm:
            BAsmCode[0] = 0x06;
            BAsmCode[1] = 0xf1 + HReg;
            memcpy(BAsmCode + 3, AdrVals, AdrCnt);
            CodeLen = 3 + AdrCnt;
            break;
        }
        break;
      case ModDisp16WRReg:
        memcpy(BAsmCode + 2, AdrVals, 2); HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask2 | (OpSize * MModImm));
        switch (AdrMode)
        {
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0x26 + (OpSize * 0x60);
            BAsmCode[1] = 0xf0 + HReg;
            BAsmCode[4] = AdrVals[0];
            CodeLen = 5;
            break;
          case ModImm:
            BAsmCode[0] = 0x06;
            BAsmCode[1] = 0xf0 + HReg;
            memcpy(BAsmCode + 4, AdrVals, AdrCnt);
            CodeLen =4 + AdrCnt;
            break;
        }
        break;
      case ModAbs:
        memcpy(BAsmCode + 2, AdrVals, 2);
        DecodeAdr(&ArgStr[2], Mask1 | MModImm);
        switch (AdrMode)
        {
          case ModWReg:
          case ModWRReg:
            BAsmCode[0] = 0xc5 + (OpSize * 0x1d);
            BAsmCode[1] = 0xf0 + AdrPart + OpSize;
            CodeLen = 4;
            break;
          case ModImm:
            memmove(BAsmCode + 2 + AdrCnt, BAsmCode + 2, 2);
            BAsmCode[0] = 0x2f + (OpSize * 7);
            BAsmCode[1] = 0xf1;
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            CodeLen = 4 + AdrCnt;
            break;
        }
        break;
      case ModIRReg:
        HReg = AdrVals[0];
        DecodeAdr(&ArgStr[2], MModIWRReg * (1 - OpSize));
        switch (AdrMode)
        {
          case ModIWRReg:
            BAsmCode[0] = 0x73;
            BAsmCode[1] = 0xf0 + AdrPart;
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
        }
        break;
    }
  }
}

static void DecodeLoad(Word Code)
{
  Byte HReg;

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModIncWRReg);
    if (AdrMode == ModIncWRReg)
    {
      HReg = AdrPart << 4;
      DecodeAdr(&ArgStr[2], MModIncWRReg);
      if (AdrMode == ModIncWRReg)
      {
        BAsmCode[0] = 0xd6;
        BAsmCode[1] = Code + HReg + AdrPart;
        CodeLen = 2;
      }
    }
  }
}

static void DecodePEA_PEAU(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModDisp8RReg | MModDisp16RReg);
    if (AdrMode != ModNone)
    {
      BAsmCode[0] = 0x8f;
      BAsmCode[1] = Code;
      memcpy(BAsmCode + 2, AdrVals, AdrCnt);
      BAsmCode[2] += AdrCnt - 2;
      CodeLen =2 + AdrCnt;
    }
  }
}

static void DecodePUSH_PUSHU(Word IsU)
{
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModReg | MModIReg | MModImm);
    switch (AdrMode)
    {
      case ModReg:
        BAsmCode[0] = 0x66 - (IsU * 0x36);
        BAsmCode[1] = AdrVals[0];
        CodeLen = 2;
        break;
      case ModIReg:
        BAsmCode[0] = 0xf7 - (IsU * 0xc6);
        BAsmCode[1] = AdrVals[0];
        CodeLen = 2;
        break;
      case ModImm:
        BAsmCode[0] = 0x8f;
        BAsmCode[1] = 0xf1 + (IsU * 2);
        BAsmCode[2] = AdrVals[0];
        CodeLen = 3;
        break;
    }
  }
}

static void DecodePUSHW_PUSHUW(Word IsU)
{
  OpSize = 1;
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModRReg | MModImm);
    switch (AdrMode)
    {
      case ModRReg:
        BAsmCode[0] = 0x74 + (IsU * 0x42);
        BAsmCode[1] = AdrVals[0];
        CodeLen = 2;
        break;
      case ModImm:
        BAsmCode[0] = 0x8f;
        BAsmCode[1] = 0xc1 + (IsU * 2);
        memcpy(BAsmCode + 2, AdrVals, AdrCnt);
        CodeLen = 2 + AdrCnt;
        break;
    }
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModReg);
    if (AdrMode == ModReg)
    {
      BAsmCode[2] = AdrVals[0];
      DecodeAdr(&ArgStr[2], MModReg);
      if (AdrMode == ModReg)
      {
        BAsmCode[1] = AdrVals[0];
        BAsmCode[0] = 0x16;
        CodeLen = 3;
      }
    }
  }
}

static void DecodeALU(Word Code)
{
  Word Mask1, Mask2;
  Byte HReg, HPart;
  Byte CodeMask = Lo(Code) << 4;

  if (ChkArgCnt(2, 2))
  {
    if (Hi(Code))
    {
      OpSize = 1; Mask1 = MModWRReg; Mask2 = MModRReg;
    }
    else
    {
      Mask1 = MModWReg; Mask2 = MModReg;
    }
    DecodeAdr(&ArgStr[1], Mask1 | Mask2 | MModIWReg | MModIWRReg | MModIncWRReg |
                         MModDecWRReg | MModDispRWRReg | MModDisp8WRReg | MModDisp16WRReg |
                         MModAbs | MModIRReg);
    switch (AdrMode)
    {
      case ModWReg:
      case ModWRReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask1 | MModIWReg | Mask2 | MModIWRReg | MModIncWRReg |
                             MModDecWRReg | MModDispRWRReg | MModDisp8WRReg | MModDisp16WRReg |
                             MModAbs | MModImm);
        switch (AdrMode)
        {
          case ModWReg:
          case ModWRReg:
            BAsmCode[0] = CodeMask + 2 + (OpSize * 12);
            BAsmCode[1] = (HReg << 4) + AdrPart;
            CodeLen = 2;
            break;
          case ModIWReg:
            if (OpSize == 0)
            {
              BAsmCode[0] = CodeMask + 3;
              BAsmCode[1] = (HReg << 4) + AdrPart;
              CodeLen = 2;
            }
            else
            {
              BAsmCode[0] = 0xa6;
              BAsmCode[1] = CodeMask + AdrPart;
              BAsmCode[2] = WorkOfs + HReg;
              CodeLen = 3;
            }
            break;
          case ModReg:
          case ModRReg:
            BAsmCode[0] = CodeMask + 4 + (OpSize * 3);
            BAsmCode[1] = AdrVals[0];
            BAsmCode[2] = HReg + WorkOfs;
            CodeLen = 3;
            break;
          case ModIWRReg:
            if (OpSize == 0)
            {
              BAsmCode[0] = 0x72;
              BAsmCode[1] = CodeMask + AdrPart + 1;
              BAsmCode[2] = HReg + WorkOfs;
              CodeLen = 3;
            }
            else
            {
              BAsmCode[0] = CodeMask + 0x0e;
              BAsmCode[1] = (HReg << 4) + AdrPart + 1;
              CodeLen = 2;
            }
            break;
          case ModIncWRReg:
            BAsmCode[0] = 0xb4 + (OpSize * 0x21);
            BAsmCode[1] = CodeMask + AdrPart + 1;
            BAsmCode[2] = HReg + WorkOfs;
            CodeLen = 3;
            break;
          case ModDecWRReg:
            BAsmCode[0] = 0xc2 + OpSize;
            BAsmCode[1] = CodeMask + AdrPart + 1;
            BAsmCode[2] = HReg + WorkOfs;
            CodeLen = 3;
            break;
          case ModDispRWRReg:
            BAsmCode[0] = 0x60;
            BAsmCode[1] = 0x10 * (1 - OpSize) + (AdrVals[0] << 4) + AdrPart;
            BAsmCode[2] = CodeMask + HReg;
            CodeLen = 3;
            break;
          case ModDisp8WRReg:
            BAsmCode[0] = 0x7f + (OpSize * 7);
            BAsmCode[1] = CodeMask + AdrPart + 1;
            BAsmCode[2] = AdrVals[0];
            BAsmCode[3] = WorkOfs + HReg;
            CodeLen = 4;
            break;
          case ModDisp16WRReg:
            BAsmCode[0]= 0x7f + (OpSize * 7);
            BAsmCode[1] = CodeMask + AdrPart;
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            BAsmCode[2 + AdrCnt] = WorkOfs + HReg;
            CodeLen = 3 + AdrCnt;
            break;
          case ModAbs:
            BAsmCode[0] = 0xc4 + (OpSize * 30);
            BAsmCode[1] = CodeMask + HReg;
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            CodeLen = 2 + AdrCnt;
            break;
          case ModImm:
            BAsmCode[0] = CodeMask + 5 + (OpSize * 2);
            BAsmCode[1] = WorkOfs + HReg + OpSize;
            memcpy(BAsmCode + 2,AdrVals, AdrCnt);
            CodeLen = 2 + AdrCnt;
            break;
        }
        break;
      case ModReg:
      case ModRReg:
        HReg = AdrVals[0];
        DecodeAdr(&ArgStr[2], Mask2 | MModIWReg | MModIWRReg | MModIncWRReg | MModDecWRReg |
                             MModDisp8WRReg | MModDisp16WRReg | MModImm);
        switch (AdrMode)
        {
          case ModReg:
          case ModRReg:
            BAsmCode[0] = CodeMask + 4 + (OpSize * 3);
            CodeLen = 3;
            BAsmCode[1] = AdrVals[0];
            BAsmCode[2] = HReg;
            break;
          case ModIWReg:
            BAsmCode[0] = 0xa6 + 65 * (1 - OpSize);
            BAsmCode[1] = CodeMask + AdrPart;
            BAsmCode[2] = HReg;
            CodeLen=3;
            break;
          case ModIWRReg:
            BAsmCode[0] = 0x72 + (OpSize * 12);
            BAsmCode[1] = CodeMask + AdrPart + (1 - OpSize);
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
          case ModIncWRReg:
            BAsmCode[0] = 0xb4 + (OpSize * 0x21);
            BAsmCode[1] = CodeMask + AdrPart + 1;
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
          case ModDecWRReg:
            BAsmCode[0] = 0xc2 + OpSize;
            BAsmCode[1] = CodeMask + AdrPart + 1;
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
          case ModDisp8WRReg:
            BAsmCode[0] = 0x7f + (OpSize * 7);
            BAsmCode[1] = CodeMask + AdrPart + 1;
            BAsmCode[2] = AdrVals[0];
            BAsmCode[3] = HReg;
            CodeLen = 4;
            break;
          case ModDisp16WRReg:
            BAsmCode[0] = 0x7f + (OpSize * 7);
            BAsmCode[1] = CodeMask + AdrPart;
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            BAsmCode[2 + AdrCnt] = HReg;
            CodeLen = 3 + AdrCnt;
            break;
          case ModImm:
            BAsmCode[0] = CodeMask + 5 + (OpSize * 2);
            memcpy(BAsmCode + 2, AdrVals, AdrCnt); BAsmCode[1] = HReg + OpSize;
            CodeLen = 2 + AdrCnt;
            break;
        }
        break;
      case ModIWReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask2);
        switch (AdrMode)
        {
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0xe6 - (OpSize * 0x50);
            BAsmCode[1] = AdrVals[0];
            BAsmCode[2] = CodeMask + HReg;
            CodeLen = 3;
            break;
        }
        break;
      case ModIWRReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], (OpSize * MModWRReg) | Mask2 | MModIWRReg | MModImm);
        switch (AdrMode)
        {
          case ModWRReg:
            BAsmCode[0] = CodeMask + 0x0e;
            BAsmCode[1] = (HReg << 4) + 0x10 + AdrPart;
            CodeLen = 2;
            break;
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0x72 + (OpSize * 76);
            BAsmCode[1] = CodeMask + HReg + OpSize;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
          case ModIWRReg:
            if (OpSize == 0)
            {
              BAsmCode[0] = 0x73;
              BAsmCode[1] = CodeMask + AdrPart;
              BAsmCode[2] = HReg + WorkOfs;
              CodeLen = 3;
            }
            else
            {
              BAsmCode[0] = CodeMask + 0x0e;
              BAsmCode[1] = 0x11 + (HReg << 4) + AdrPart;
              CodeLen = 2;
            }
            break;
          case ModImm:
            BAsmCode[0] = 0xf3 - (OpSize * 0x35);
            BAsmCode[1] = CodeMask + HReg;
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            CodeLen = 2 + AdrCnt;
            break;
        }
        break;
      case ModIncWRReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask2);
        switch (AdrMode)
        {
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0xb4 + (OpSize * 0x21);
            BAsmCode[1] = CodeMask + HReg;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
        }
        break;
      case ModDecWRReg:
        HReg = AdrPart;
        DecodeAdr(&ArgStr[2], Mask2);
        switch (AdrMode)
        {
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0xc2 + OpSize;
            BAsmCode[1] = CodeMask + HReg;
            BAsmCode[2] = AdrVals[0];
            CodeLen = 3;
            break;
        }
        break;
      case ModDispRWRReg:
        HReg = AdrPart; HPart = AdrVals[0];
        DecodeAdr(&ArgStr[2], Mask1);
        switch (AdrMode)
        {
          case ModWReg:
          case ModWRReg:
            BAsmCode[0] = 0x60;
            BAsmCode[1] = 0x11 - (OpSize * 0x10) + (HPart << 4) + HReg;
            BAsmCode[2] = CodeMask + AdrPart;
            CodeLen = 3;
            break;
        }
        break;
      case ModDisp8WRReg:
        HReg = AdrPart;
        BAsmCode[2] = AdrVals[0];
        DecodeAdr(&ArgStr[2], Mask2 + (OpSize * MModImm));
        switch (AdrMode)
        {
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0x26 + (OpSize * 0x60);
            BAsmCode[1] = CodeMask + HReg + 1;
            BAsmCode[3] = AdrVals[0] + OpSize;
            CodeLen = 4;
            break;
          case ModImm:
            BAsmCode[0] = 0x06;
            BAsmCode[1] = CodeMask + HReg + 1;
            memcpy(BAsmCode + 3, AdrVals, AdrCnt);
            CodeLen = 3 + AdrCnt;
            break;
        }
        break;
      case ModDisp16WRReg:
        HReg = AdrPart; memcpy(BAsmCode + 2, AdrVals, 2);
        DecodeAdr(&ArgStr[2], Mask2 | (OpSize * MModImm));
        switch (AdrMode)
        {
          case ModReg:
          case ModRReg:
            BAsmCode[0] = 0x26 + (OpSize * 0x60);
            BAsmCode[1] = CodeMask + HReg;
            BAsmCode[4] = AdrVals[0] + OpSize;
            CodeLen = 5;
            break;
          case ModImm:
            BAsmCode[0] = 0x06;
            BAsmCode[1] = CodeMask + HReg;
            memcpy(BAsmCode + 4, AdrVals, AdrCnt);
            CodeLen = 4 + AdrCnt;
            break;
        }
        break;
      case ModAbs:
        memcpy(BAsmCode + 2, AdrVals, 2);
        DecodeAdr(&ArgStr[2], Mask1 | MModImm);
        switch (AdrMode)
        {
          case ModWReg:
          case ModWRReg:
            BAsmCode[0] = 0xc5 + (OpSize * 29);
            BAsmCode[1] = CodeMask + AdrPart + OpSize;
            CodeLen = 4;
            break;
          case ModImm:
            memmove(BAsmCode + 2 + AdrCnt, BAsmCode + 2, 2);
            memcpy(BAsmCode + 2, AdrVals, AdrCnt);
            BAsmCode[0] = 0x2f + (OpSize * 7);
            BAsmCode[1] = CodeMask + 1;
            CodeLen = 4 + AdrCnt;
            break;
        }
        break;
      case ModIRReg:
        HReg = AdrVals[0];
        DecodeAdr(&ArgStr[2], MModIWRReg *(1 - OpSize));
        switch (AdrMode)
        {
          case ModIWRReg:
            BAsmCode[0] = 0x73;
            BAsmCode[1] = CodeMask + AdrPart;
            BAsmCode[2] = HReg;
            CodeLen = 3;
            break;
        }
        break;
    }
  }
}

static void DecodeReg8(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModReg | MModIReg);
    switch (AdrMode)
    {
      case ModReg:
        BAsmCode[0] = Code;
        BAsmCode[1] = AdrVals[0];
        CodeLen = 2;
        break;
      case ModIReg:
        BAsmCode[0] = Code + 1;
        BAsmCode[1] = AdrVals[0];
        CodeLen = 2;
        break;
    }
  }
}

static void DecodeReg16(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModRReg);
    switch (AdrMode)
    {
      case ModRReg:
        BAsmCode[0] = Lo(Code);
        BAsmCode[1] = AdrVals[0] + Hi(Code);
        CodeLen = 2;
        break;
    }
  }
}

static void DecodeDIV_MUL(Word Code)
{
  Byte HReg;

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModWRReg);
    if (AdrMode == ModWRReg)
    {
      HReg = AdrPart;
      DecodeAdr(&ArgStr[2], MModWReg);
      if (AdrMode == ModWReg)
      {
        BAsmCode[0] = Code;
        BAsmCode[1] = (HReg << 4) + AdrPart;
        CodeLen = 2;
      }
    }
  }
}

static void DecodeDIVWS(Word Code)
{
  Byte HReg;

  UNUSED(Code);

  if (ChkArgCnt(3, 3))
  {
    DecodeAdr(&ArgStr[1], MModWRReg);
    if (AdrMode == ModWRReg)
    {
      HReg = AdrPart;
      DecodeAdr(&ArgStr[2], MModWRReg);
      if (AdrMode == ModWRReg)
      {
        BAsmCode[2] = (HReg << 4) + AdrPart;
        DecodeAdr(&ArgStr[3], MModRReg);
        if (AdrMode == ModRReg)
        {
          BAsmCode[0] = 0x56;
          BAsmCode[1] = AdrVals[0];
          CodeLen = 3;
        }
      }
    }
  }
}

static void DecodeBit2(Word Code)
{
  Byte HReg, HPart;
  tStrComp Comp;

  if (ChkArgCnt(2, 2) && SplitBit(&Comp, &ArgStr[1], &HReg))
  {
    if (Odd(HReg)) WrError(ErrNum_InvAddrMode);
    else
    {
      DecodeAdr(&Comp, MModWReg);
      if (AdrMode == ModWReg)
      {
        HReg = (HReg << 4) + AdrPart;
        if (SplitBit(&Comp, &ArgStr[2], &HPart))
        {
          DecodeAdr(&Comp, MModWReg);
          if (AdrMode == ModWReg)
          {
            HPart = (HPart << 4) + AdrPart;
            BAsmCode[0] = Code;
            switch (Code)
            {
              case 0xf2: /* BLD */
                BAsmCode[1] = HPart | 0x10;
                BAsmCode[2] = HReg | (HPart & 0x10);
                break;
              case 0x6f: /* BXOR */
                BAsmCode[1] = 0x10 + HReg;
                BAsmCode[2] = HPart;
                break;
              default:
                BAsmCode[1] = 0x10 + HReg;
                BAsmCode[2] = HPart ^ 0x10;
            }
            CodeLen = 3;
          }
        }
      }
    }
  }
}

static void DecodeBit1(Word Code)
{
  Byte HReg;
  tStrComp Comp;

  if (ChkArgCnt(1, 1) && SplitBit(&Comp, &ArgStr[1], &HReg))
  {
    if (Odd(HReg)) WrError(ErrNum_InvAddrMode);
    else
    {
      DecodeAdr(&Comp, MModWReg + (Hi(Code) * MModIWRReg));
      switch (AdrMode)
      {
        case ModWReg:
          BAsmCode[0] = Lo(Code);
          BAsmCode[1] = (HReg << 4) + AdrPart;
          CodeLen = 2;
          break;
        case ModIWRReg:
          BAsmCode[0] = 0xf6;
          BAsmCode[1] = (HReg << 4) + AdrPart;
          CodeLen = 2;
          break;
      }
    }
  }
}

static void DecodeBTJF_BTJT(Word Code)
{
  Byte HReg;
  Integer AdrInt;
  tStrComp Comp;

  if (ChkArgCnt(2, 2) && SplitBit(&Comp, &ArgStr[1], &HReg))
  {
    if (Odd(HReg)) WrError(ErrNum_InvAddrMode);
    else
    {
      DecodeAdr(&Comp, MModWReg);
      if (AdrMode == ModWReg)
      {
        tEvalResult EvalResult;

        BAsmCode[1] = (HReg << 4) + AdrPart + Code;
        AdrInt = EvalStrIntExpressionWithResult(&ArgStr[2], UInt16, &EvalResult) - (EProgCounter() + 3);
        if (EvalResult.OK)
        {
          if (!mSymbolQuestionable(EvalResult.Flags) && ((AdrInt < -128) || (AdrInt > 127))) WrError(ErrNum_JmpDistTooBig);
          else
          {
            BAsmCode[0] = 0xaf;
            BAsmCode[2] = AdrInt & 0xff;
            CodeLen = 3;
            ChkSpace(SegCode, EvalResult.AddrSpaceMask);
          }
        }
      }
    }
  }
}

static void DecodeJP_CALL(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    AbsSeg = SegCode;
    DecodeAdr(&ArgStr[1], MModIRReg | MModAbs);
    switch (AdrMode)
    {
      case ModIRReg:
        BAsmCode[0] = Hi(Code);
        BAsmCode[1] = AdrVals[0] | ((Code >> 1) & 1);
        CodeLen = 2;
        break;
      case ModAbs:
        BAsmCode[0] = Lo(Code);
        memcpy(BAsmCode + 1, AdrVals, AdrCnt);
        CodeLen =1 + AdrCnt;
        break;
    }
  }
}

static void DecodeCPJFI_CPJTI(Word Code)
{
  Byte HReg;
  Integer AdrInt;

  if (ChkArgCnt(3, 3))
  {
    DecodeAdr(&ArgStr[1], MModWReg);
    if (AdrMode == ModWReg)
    {
      HReg = AdrPart;
      DecodeAdr(&ArgStr[2], MModIWRReg);
      if (AdrMode == ModIWRReg)
      {
        tEvalResult EvalResult;

        BAsmCode[1] = (AdrPart << 4) + Code + HReg;
        AdrInt = EvalStrIntExpressionWithResult(&ArgStr[3], UInt16, &EvalResult) - (EProgCounter() + 3);
        if (EvalResult.OK)
        {
          if (!mSymbolQuestionable(EvalResult.Flags) && ((AdrInt<-128) || (AdrInt>127))) WrError(ErrNum_JmpDistTooBig);
          else
          {
            ChkSpace(SegCode, EvalResult.AddrSpaceMask);
            BAsmCode[0] = 0x9f;
            BAsmCode[2] = AdrInt & 0xff;
            CodeLen = 3;
          }
        }
      }
    }
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModWReg);
    if (AdrMode == ModWReg)
    {
      Integer AdrInt;
      tEvalResult EvalResult;

      BAsmCode[0] = (AdrPart << 4) + 0x0a;
      AdrInt = EvalStrIntExpressionWithResult(&ArgStr[2], UInt16, &EvalResult) - (EProgCounter() + 2);
      if (EvalResult.OK)
      {
        if (!mSymbolQuestionable(EvalResult.Flags) && ((AdrInt < -128) || (AdrInt > 127))) WrError(ErrNum_JmpDistTooBig);
        else
        {
          ChkSpace(SegCode, EvalResult.AddrSpaceMask);
          BAsmCode[1] = AdrInt & 0xff;
          CodeLen = 2;
        }
      }
    }
  }
}

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

  if (ChkArgCnt(2, 2))
  {
    DecodeAdr(&ArgStr[1], MModRReg);
    if (AdrMode == ModRReg)
    {
      Integer AdrInt;
      tEvalResult EvalResult;

      BAsmCode[1] = AdrVals[0];
      AdrInt = EvalStrIntExpressionWithResult(&ArgStr[2], UInt16, &EvalResult) - (EProgCounter() + 3);
      if (EvalResult.OK)
      {
        if (!mSymbolQuestionable(EvalResult.Flags) && ((AdrInt < -128) || (AdrInt > 127))) WrError(ErrNum_JmpDistTooBig);
        else
        {
          ChkSpace(SegCode, EvalResult.AddrSpaceMask);
          BAsmCode[0] = 0xc6;
          BAsmCode[2] = AdrInt & 0xff;
          CodeLen = 3;
        }
      }
    }
  }
}

static void DecodeCondAbs(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word AdrWord = EvalStrIntExpressionWithResult(&ArgStr[1], UInt16, &EvalResult);

    if (EvalResult.OK)
    {
      ChkSpace(SegCode, EvalResult.AddrSpaceMask);
      BAsmCode[0] = 0x0d + Code;
      BAsmCode[1] = Hi(AdrWord);
      BAsmCode[2] = Lo(AdrWord);
      CodeLen = 3;
    }
  }
}

static void DecodeCondRel(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Integer AdrInt = EvalStrIntExpressionWithResult(&ArgStr[1], UInt16, &EvalResult) - (EProgCounter() + 2);

    if (EvalResult.OK)
    {
      if (!mSymbolQuestionable(EvalResult.Flags) && ((AdrInt < -128) || (AdrInt > 127))) WrError(ErrNum_JmpDistTooBig);
      else
      {
        ChkSpace(SegCode, EvalResult.AddrSpaceMask);
        BAsmCode[0] = 0x0b + Code;
        BAsmCode[1] = AdrInt & 0xff;
        CodeLen = 2;
      }
    }
  }
}

static void DecodeSPP(Word Code)
{
  Boolean OK;

  UNUSED(Code);

  if (!ChkArgCnt(1, 1));
  else if (*ArgStr[1].str.p_str != '#') WrError(ErrNum_InvAddrMode);
  else
  {
    BAsmCode[1] = (EvalStrIntExpressionOffs(&ArgStr[1], 1, UInt6, &OK) << 2) + 0x02;
    if (OK)
    {
      BAsmCode[0] = 0xc7;
      CodeLen = 2;
    }
  }
}

static void DecodeSRP(Word Code)
{
  Boolean OK;

  if (!ChkArgCnt(1, 1));
  else if (*ArgStr[1].str.p_str != '#') WrError(ErrNum_InvAddrMode);
  else
  {
    BAsmCode[1] = EvalStrIntExpressionOffs(&ArgStr[1], 1, UInt5, &OK) << 3;
    if (OK)
    {
      BAsmCode[0] = 0xc7;
      BAsmCode[1] += Code;
      CodeLen=2;
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModWReg | MModReg | MModIWRReg);
    switch (AdrMode)
    {
      case ModWReg:
        BAsmCode[0] = 0x42;
        BAsmCode[1] = (AdrPart << 4) + AdrPart;
        CodeLen = 2;
        break;
      case ModReg:
        BAsmCode[0] = 0x44;
        BAsmCode[1] = AdrVals[0];
        BAsmCode[2] = AdrVals[0];
        CodeLen = 3;
        break;
      case ModIWRReg:
        BAsmCode[0] = 0x73;
        BAsmCode[1] = 0x40 + AdrPart;
        BAsmCode[2] = WorkOfs + AdrPart;
        CodeLen = 3;
        break;
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    DecodeAdr(&ArgStr[1], MModWRReg | MModRReg | MModIWRReg);
    switch (AdrMode)
    {
      case ModWRReg:
       BAsmCode[0] = 0x4e;
       BAsmCode[1] = (AdrPart << 4) + AdrPart;
       CodeLen = 2;
       break;
      case ModRReg:
       BAsmCode[0] = 0x47;
       BAsmCode[1] = AdrVals[0];
       BAsmCode[2] = AdrVals[0];
       CodeLen = 3;
       break;
      case ModIWRReg:
       BAsmCode[0] = 0x4e;
       BAsmCode[1] = 0x11 + (AdrPart << 4) + AdrPart;
       CodeLen = 2;
       break;
    }
  }
}

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

  code_equate_type(SegReg, UInt9);
}

static void DecodeBIT(Word Code)
{
  Byte Bit;
  tStrComp Comp;

  UNUSED(Code);

  if (ChkArgCnt(1, 1) && SplitBit(&Comp, &ArgStr[1], &Bit))
  {
    DecodeAdr(&Comp, MModWReg);
    if (AdrMode == ModWReg)
    {
      PushLocHandle(-1);
      EnterIntSymbol(&LabPart, (AdrPart << 4) + Bit, SegNone, False);
      PopLocHandle();
      as_snprintf(ListLine, STRINGSIZE, "=r%d.%s%c",
                  (int)AdrPart,
                  (Odd(Bit)) ? "!" : "", (Bit >> 1) + AscOfs);
    }
  }
}

/*--------------------------------------------------------------------------*/
/* Code Table Handling */

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

static void AddALU(const char *NName8, const char *NName16, Word NCode)
{
  AddInstTable(InstTable, NName8, NCode, DecodeALU);
  AddInstTable(InstTable, NName16, NCode | 0x100, DecodeALU);
}

static void AddReg(const char *NName, Word NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeReg8);
}

static void AddReg16(const char *NName, Word NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeReg16);
}

static void AddBit2(const char *NName, Word NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeBit2);
}

static void AddBit1(const char *NName, Word NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeBit1);
}

static void AddCondition(const char *NameAbs, const char *NameRel, Word NCode)
{
  AddInstTable(InstTable, NameAbs, NCode << 4, DecodeCondAbs);
  AddInstTable(InstTable, NameRel, NCode << 4, DecodeCondRel);
}

static void AddLoad(const char *NName, Word NCode)
{
  AddInstTable(InstTable, NName, NCode, DecodeLoad);
}

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

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "LD", 0, DecodeLD);
  AddInstTable(InstTable, "LDW", 1, DecodeLD);
  AddInstTable(InstTable, "PEA", 0x01, DecodePEA_PEAU);
  AddInstTable(InstTable, "PEAU", 0x03, DecodePEA_PEAU);
  AddInstTable(InstTable, "PUSH", 0, DecodePUSH_PUSHU);
  AddInstTable(InstTable, "PUSHU", 1, DecodePUSH_PUSHU);
  AddInstTable(InstTable, "PUSHW", 0, DecodePUSHW_PUSHUW);
  AddInstTable(InstTable, "PUSHUW", 1, DecodePUSHW_PUSHUW);
  AddInstTable(InstTable, "XCH", 0, DecodeXCH);
  AddInstTable(InstTable, "DIV", 0x5f, DecodeDIV_MUL);
  AddInstTable(InstTable, "MUL", 0x4f, DecodeDIV_MUL);
  AddInstTable(InstTable, "DIVWS", 0, DecodeDIVWS);
  AddInstTable(InstTable, "BTJF", 0x10, DecodeBTJF_BTJT);
  AddInstTable(InstTable, "BTJT", 0, DecodeBTJF_BTJT);
  AddInstTable(InstTable, "JP", 0xd48d, DecodeJP_CALL);
  AddInstTable(InstTable, "CALL", 0x74d2, DecodeJP_CALL);
  AddInstTable(InstTable, "CPJFI", 0, DecodeCPJFI_CPJTI);
  AddInstTable(InstTable, "CPJTI", 0x10, DecodeCPJFI_CPJTI);
  AddInstTable(InstTable, "DJNZ", 0, DecodeDJNZ);
  AddInstTable(InstTable, "DWJNZ", 0, DecodeDWJNZ);
  AddInstTable(InstTable, "SPP", 0, DecodeSPP);
  AddInstTable(InstTable, "SRP", 0x00, DecodeSRP);
  AddInstTable(InstTable, "SRP0", 0x04, DecodeSRP);
  AddInstTable(InstTable, "SRP1", 0x05, DecodeSRP);
  AddInstTable(InstTable, "SLA", 0, DecodeSLA);
  AddInstTable(InstTable, "SLAW", 0, DecodeSLAW);
  AddInstTable(InstTable, "REG", 0, DecodeREG);
  AddInstTable(InstTable, "BIT", 0, DecodeBIT);

  AddFixed("CCF" , 0x0061); AddFixed("DI"  , 0x0010);
  AddFixed("EI"  , 0x0000); AddFixed("HALT", 0xbf01);
  AddFixed("IRET", 0x00d3); AddFixed("NOP" , 0x00ff);
  AddFixed("RCF" , 0x0011); AddFixed("RET" , 0x0046);
  AddFixed("SCF" , 0x0001); AddFixed("SDM" , 0x00fe);
  AddFixed("SPM" , 0x00ee); AddFixed("WFI" , 0xef01);

  AddALU("ADC", "ADCW", 3); AddALU("ADD", "ADDW", 4); AddALU("AND", "ANDW",  1);
  AddALU("CP" , "CPW" , 9); AddALU("OR" , "ORW" , 0); AddALU("SBC", "SBCW",  2);
  AddALU("SUB", "SUBW", 5); AddALU("TCM", "TCMW", 8); AddALU("TM" , "TMW" , 10);
  AddALU("XOR", "XORW", 6);

  AddReg("CLR" , 0x90); AddReg("CPL" , 0x80); AddReg("DA"  , 0x70);
  AddReg("DEC" , 0x40); AddReg("INC" , 0x50); AddReg("POP" , 0x76);
  AddReg("POPU", 0x20); AddReg("RLC" , 0xb0); AddReg("ROL" , 0xa0);
  AddReg("ROR" , 0xc0); AddReg("RRC" , 0xd0); AddReg("SRA" , 0xe0);
  AddReg("SWAP", 0xf0);

  AddReg16("DECW" , 0x00cf); AddReg16("EXT"  , 0x01c6);
  AddReg16("INCW" , 0x00df); AddReg16("POPUW", 0x00b7);
  AddReg16("POPW" , 0x0075); AddReg16("RLCW" , 0x008f);
  AddReg16("RRCW" , 0x0036); AddReg16("SRAW" , 0x002f);

  AddBit2("BAND", 0x1f); AddBit2("BLD" , 0xf2);
  AddBit2("BOR" , 0x0f); AddBit2("BXOR", 0x6f);

  AddBit1("BCPL", 0x006f); AddBit1("BRES" , 0x001f);
  AddBit1("BSET", 0x000f); AddBit1("BTSET", 0x01f2);

  AddCondition("JPF"   , "JRF"   , 0x0); AddCondition("JPT"   , "JRT"   , 0x8);
  AddCondition("JPC"   , "JRC"   , 0x7); AddCondition("JPNC"  , "JRNC"  , 0xf);
  AddCondition("JPZ"   , "JRZ"   , 0x6); AddCondition("JPNZ"  , "JRNZ"  , 0xe);
  AddCondition("JPPL"  , "JRPL"  , 0xd); AddCondition("JPMI"  , "JRMI"  , 0x5);
  AddCondition("JPOV"  , "JROV"  , 0x4); AddCondition("JPNOV" , "JRNOV" , 0xc);
  AddCondition("JPEQ"  , "JREQ"  , 0x6); AddCondition("JPNE"  , "JRNE"  , 0xe);
  AddCondition("JPGE"  , "JRGE"  , 0x9); AddCondition("JPLT"  , "JRLT"  , 0x1);
  AddCondition("JPGT"  , "JRGT"  , 0xa); AddCondition("JPLE"  , "JRLE"  , 0x2);
  AddCondition("JPUGE" , "JRUGE" , 0xf); AddCondition("JPUL"  , "JRUL"  , 0x7);
  AddCondition("JPUGT" , "JRUGT" , 0xb); AddCondition("JPULE" , "JRULE" , 0x3);

  AddLoad("LDPP", 0x00); AddLoad("LDDP", 0x10);
  AddLoad("LDPD", 0x01); AddLoad("LDDD", 0x11);

  AddIntelPseudo(InstTable, eIntPseudoFlag_BigEndian);
}

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

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


static void MakeCode_ST9(void)
{
  OpSize = 0;
  AbsSeg = (DPAssume == 1) ? SegData : SegCode;

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

static void InitCode_ST9(void)
{
  DPAssume = 0;
}

static Boolean IsDef_ST9(void)
{
  return (Memo("REG") || Memo("BIT"));
}

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

static void InternSymbol_ST9(char *Asc, TempResult *Erg)
{
  char *p_end;
  Boolean Pair;
  LargeWord Num;

  as_tempres_set_none(Erg);
  if ((strlen(Asc) < 2) || (*Asc != 'R'))
    return;
  Asc++;

  if (*Asc == 'R')
  {
    if (strlen(Asc) < 2) return;
    Pair = True; Asc++;
  }
  else
    Pair = False;

  Num = strtoul(Asc, &p_end, 10);
  if (*p_end || (Num > 255)) return;
  if ((Num & 0xf0) == 0xd0) return;
  if (Pair && Odd(Num)) return;

  if (Pair) Num += 0x100;
  as_tempres_set_int(Erg, Num); Erg->AddrSpaceMask |= (1 << SegReg);
}

static void SwitchTo_ST9(void)
{
  const TFamilyDescr *p_descr = FindFamilyByName("ST9");

  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);

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

  ValidSegs = (1 << SegCode) | (1 << SegData) | ( 1 << SegReg);
  Grans[SegCode] = 1; ListGrans[SegCode] = 1; SegInits[SegCode] = 0;
  SegLimits[SegCode] = 0xffff;
  Grans[SegData] = 1; ListGrans[SegData] = 1; SegInits[SegData] = 0;
  SegLimits[SegData] = 0xffff;
  Grans[SegReg ] = 1; ListGrans[SegReg ] = 1; SegInits[SegReg ] = 0;
  SegLimits[SegReg ] = 0xff;

  MakeCode=MakeCode_ST9; IsDef=IsDef_ST9;
  SwitchFrom=SwitchFrom_ST9; InternSymbol=InternSymbol_ST9;

  assume_set(ASSUMEST9s, as_array_size(ASSUMEST9s));

  InitFields();
}

void codest9_init(void)
{
  CPUST9020 = AddCPU("ST9020", SwitchTo_ST9);
  CPUST9030 = AddCPU("ST9030", SwitchTo_ST9);
  CPUST9040 = AddCPU("ST9040", SwitchTo_ST9);
  CPUST9050 = AddCPU("ST9050", SwitchTo_ST9);
  AddInitPassProc(InitCode_ST9);
}
