/* codekenbak.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS                                                                        */
/*                                                                           */
/* Code Generator KENBAK(-1)                                                 */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"

#include <string.h>
#include "stdinc.h"
#include "strutil.h"
#include "intformat.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmallg.h"
#include "asmstructs.h"
#include "headids.h"

#include "asmitree.h"
#include "codevars.h"
#include "codepseudo.h"
#include "intpseudo.h"

#include "codekenbak.h"

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

/* do not change enum values, they match the machine codings: */

enum
{
  ModNone = 0,
  ModImm = 3,
  ModMemory = 4,
  ModIndirect = 5,
  ModIndexed = 6,
  ModIndirectIndexed = 7
};

#define MModImm (1 << ModImm)
#define MModMemory (1 << ModMemory)
#define MModIndirect (1 << ModIndirect)
#define MModIndexed (1 << ModIndexed)
#define MModIndirectIndexed (1 << ModIndirectIndexed)

#define MModAll (MModImm | MModMemory | MModIndirect | MModIndexed | MModIndirectIndexed)

typedef struct
{
  Byte Mode, Val;
} tAdrData;

static const char Regs[5] = "ABXP";

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

/*!------------------------------------------------------------------------
 * \fn     DecodeRegCore(const char *pArg, Byte *pResult)
 * \brief  check whether argument describes a CPU register
 * \param  pArg argument
 * \param  pValue resulting register # if yes
 * \return true if argument is a register
 * ------------------------------------------------------------------------ */

static Boolean DecodeRegCore(const char *pArg, Byte *pResult)
{
  const char *pPos;

  if (strlen(pArg) != 1)
    return False;
  pPos = strchr(Regs, as_toupper(*pArg));
  if (pPos)
    *pResult = pPos - Regs;
  return !!pPos;
}

/*!------------------------------------------------------------------------
 * \fn     DissectReg_KENBAK(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
 * \brief  dissect register symbols - KENBAK variant
 * \param  pDest destination buffer
 * \param  DestSize destination buffer size
 * \param  Value numeric register value
 * \param  InpSize register size
 * ------------------------------------------------------------------------ */

static void DissectReg_KENBAK(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
{
  switch (InpSize)
  {
    case eSymbolSize8Bit:
      if (Value <= 3)
      {
        as_snprintf(pDest, DestSize, "%c", Regs[Value]);
        break;
      }
      /* else fall-thru */
    default:
      as_snprintf(pDest, DestSize, "%d-%u", (int)InpSize, (unsigned)Value);
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeReg(const tStrComp *pArg, Byte *pResult, Word RegMask, Boolean MustBeReg)
 * \brief  check whether argument is CPU register, including register aliases
 * \param  pArg source code argument
 * \param  pResult register # if it's a register
 * \param  RegMask bit mask of allowed registers
 * \param  MustBeReg expecting register anyway
 * \return eval result
 * ------------------------------------------------------------------------ */

static tRegEvalResult DecodeReg(const tStrComp *pArg, Byte *pResult, Word RegMask, Boolean MustBeReg)
{
  tRegDescr RegDescr;
  tEvalResult EvalResult;
  tRegEvalResult RegEvalResult;

  if (DecodeRegCore(pArg->str.p_str, pResult))
    RegEvalResult = eIsReg;
  else
  {
    RegEvalResult = EvalStrRegExpressionAsOperand(pArg, &RegDescr, &EvalResult, eSymbolSize8Bit, MustBeReg);
    *pResult = RegDescr.Reg;
  }

  if ((RegEvalResult == eIsReg)
   && !(1 & (RegMask >> *pResult)))
  {
    WrStrErrorPos(ErrNum_InvReg, pArg);
    RegEvalResult = eRegAbort;
  }

  return RegEvalResult;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeRegWithMemOpt(const tStrComp *pArg, Byte *pResult, Word RegMask)
 * \brief  check whether argument is CPU register, register alias, or memory location representing a register
 * \param  pArg source code argument
 * \param  pResult register # if it's a register
 * \param  RegMask bit mask of allowed registers
 * \return True if argument can be interpreted as register in some way
 * ------------------------------------------------------------------------ */

static Boolean DecodeRegWithMemOpt(const tStrComp *pArg, Byte *pResult, Word RegMask)
{
  switch (DecodeReg(pArg, pResult, RegMask, False))
  {
    case eIsReg:
      return True;
    case eRegAbort:
      return False;
    default:
    {
      tEvalResult EvalResult;

      *pResult = EvalStrIntExpressionWithResult(pArg, UInt8, &EvalResult);
      if (!EvalResult.OK)
        return False;
      if (!(1 & (RegMask >> *pResult)))
      {
        WrStrErrorPos(ErrNum_InvReg, pArg);
        return False;
      }
      return True;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeAdr(const tStrComp *pArg, tAdrData *pAdrData, Word ModeMask)
 * \brief  decode address expression
 * \param  pArg1 1st argument
 * \param  pArg1 optional 2nd argument (may be NULL)
 * \param  pAdrData result buffer
 * \return true if successfully decoded
 * ------------------------------------------------------------------------ */

static Boolean ChkMode(Word ModeMask, tAdrData *pAdrData)
{
  return !!((ModeMask >> pAdrData->Mode) & 1);
}

static Boolean DecodeAdr(tStrComp *pArg1, tStrComp *pArg2, tAdrData *pAdrData, Word ModeMask)
{
  tStrComp Arg;

  pAdrData->Mode = 0;

  if (pArg2)
  {
    Byte IndexReg;

    if (DecodeReg(pArg2, &IndexReg, 4, True) != eIsReg)
      return False;
    pAdrData->Mode |= 2;
  }
  else
  {
    if (*pArg1->str.p_str == '#')
    {
      Boolean OK;

      pAdrData->Mode = 3;
      pAdrData->Val = EvalStrIntExpressionOffs(pArg1, 1, Int8, &OK);
      return OK && ChkMode(ModeMask, pAdrData);
    }
  }

  if ((ModeMask & (MModIndirect | MModIndirectIndexed)) && IsIndirect(pArg1->str.p_str))
  {
    StrCompShorten(pArg1, 1);
    StrCompRefRight(&Arg, pArg1, 1);
    pAdrData->Mode |= 1;
  }
  else
    StrCompRefRight(&Arg, pArg1, 0);
  switch (DecodeReg(&Arg, &pAdrData->Val, 15, False))
  {
    case eIsReg:
      pAdrData->Mode |= 4;
      return ChkMode(ModeMask, pAdrData);
    case eRegAbort:
      return False;
    default:
    {
      Boolean OK;

      pAdrData->Val = EvalStrIntExpression(&Arg, UInt8, &OK);
      pAdrData->Mode |= 4;
      return OK && ChkMode(ModeMask, pAdrData);
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     DecodeAddrKeyword(const char *pKeyword, Byte *pMode)
 * \brief  deduce addressing mode form key word
 * \param  pKeyword keyword from source
 * \param  pMode resulting mode if yes
 * \return True if valid key word
 * ------------------------------------------------------------------------ */

static Boolean DecodeAddrKeyword(const char *pKeyword, Byte *pMode)
{
  if (!as_strcasecmp(pKeyword, "Constant"))
  {
    *pMode = ModImm;
    return True;
  }
  if (!as_strcasecmp(pKeyword, "Memory"))
  {
    *pMode = ModMemory;
    return True;
  }
  if (!as_strcasecmp(pKeyword, "Indexed"))
  {
    *pMode = ModIndexed;
    return True;
  }
  if (!as_strcasecmp(pKeyword, "Indirect"))
  {
    *pMode = ModIndirect;
    return True;
  }
  if (!as_strcasecmp(pKeyword, "Indirect-Indexed"))
  {
    *pMode = ModIndirectIndexed;
    return True;
  }
  return False;
}

/*--------------------------------------------------------------------------*/
/* Bit Symbol Handling */

/*
 * Compact representation of bits in symbol table:
 * Bits 10...3: Absolute Address
 * Bits 0..2: Bit Position
 */

/*!------------------------------------------------------------------------
 * \fn     EvalBitPosition(const tStrComp *pArg, Boolean *pOK)
 * \brief  evaluate bit position
 * \param  bit position argument (with or without #)
 * \param  pOK parsing OK?
 * \return numeric bit position
 * ------------------------------------------------------------------------ */

static LongWord EvalBitPosition(const tStrComp *pArg, Boolean *pOK)
{
  return EvalStrIntExpression(pArg, UInt3, pOK);
}

/*!------------------------------------------------------------------------
 * \fn     AssembleBitSymbol(Byte BitPos, LongWord Address)
 * \brief  build the compact internal representation of a bit symbol
 * \param  BitPos bit position in byte
 * \param  Address register address
 * \return compact representation
 * ------------------------------------------------------------------------ */

static LongWord AssembleBitSymbol(Byte BitPos, Word Address)
{
  return
    (Address << 3)
  | (BitPos << 0);
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitArg2(LongWord *pResult, const tStrComp *pBitArg, tStrComp *pRegArg)
 * \brief  encode a bit symbol, address & bit position separated
 * \param  pResult resulting encoded bit
 * \param  pRegArg register argument
 * \param  pBitArg bit argument
 * \return True if success
 * ------------------------------------------------------------------------ */

static Boolean DecodeBitArg2(LongWord *pResult, const tStrComp *pBitArg, tStrComp *pRegArg)
{
  Boolean OK;
  LongWord BitPos;
  tAdrData AdrData;

  BitPos = EvalBitPosition(pBitArg, &OK);
  if (!OK)
    return False;

  if (!DecodeAdr(pRegArg, NULL, &AdrData, MModMemory))
    return False;

  *pResult = AssembleBitSymbol(BitPos, AdrData.Val);
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     DecodeBitArg(LongWord *pResult, int Start, int Stop)
 * \brief  encode a bit symbol from instruction argument(s)
 * \param  pResult resulting encoded bit
 * \param  Start first argument
 * \param  Stop last argument
 * \return True if success
 * ------------------------------------------------------------------------ */

static Boolean DecodeBitArg(LongWord *pResult, int Start, int Stop)
{
  *pResult = 0;

  /* Just one argument -> parse as bit argument */

  if (Start == Stop)
  {
    tEvalResult EvalResult;

    *pResult = EvalStrIntExpressionWithResult(&ArgStr[Start], UInt32, &EvalResult);
    if (EvalResult.OK)
      ChkSpace(SegBData, EvalResult.AddrSpaceMask);
    return EvalResult.OK;
  }

  /* register & bit position are given as separate arguments */

  else if (Stop == Start + 1)
    return DecodeBitArg2(pResult, &ArgStr[Start], &ArgStr[Stop]);

  /* other # of arguments not allowed */

  else
  {
    WrError(ErrNum_WrongArgCnt);
    return False;
  }
}

/*!------------------------------------------------------------------------
 * \fn     DissectBitSymbol(LongWord BitSymbol, Word *pAddress, Byte *pBitPos)
 * \brief  transform compact representation of bit (field) symbol into components
 * \param  BitSymbol compact storage
 * \param  pAddress register address
 * \param  pBitPos bit position
 * \return constant True
 * ------------------------------------------------------------------------ */

static Boolean DissectBitSymbol(LongWord BitSymbol, Word *pAddress, Byte *pBitPos)
{
  *pAddress = (BitSymbol >> 3) & 0xfful;
  *pBitPos = BitSymbol & 7;
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     DissectBit_KENBAK(char *pDest, size_t DestSize, LargeWord Inp)
 * \brief  dissect compact storage of bit (field) into readable form for listing
 * \param  pDest destination for ASCII representation
 * \param  DestSize destination buffer size
 * \param  Inp compact storage
 * ------------------------------------------------------------------------ */

static void DissectBit_KENBAK(char *pDest, size_t DestSize, LargeWord Inp)
{
  Byte BitPos;
  Word Address;

  DissectBitSymbol(Inp, &Address, &BitPos);

  as_snprintf(pDest, DestSize, "%~02.*u%s,%u",
              ListRadixBase, (unsigned)Address, GetIntConstIntelSuffix(ListRadixBase),
              (unsigned)BitPos);
}

/*!------------------------------------------------------------------------
 * \fn     ExpandBit_KENBAK(const tStrComp *pVarName, const struct sStructElem *pStructElem, LargeWord Base)
 * \brief  expands bit definition when a structure is instantiated
 * \param  pVarName desired symbol name
 * \param  pStructElem element definition
 * \param  Base base address of instantiated structure
 * ------------------------------------------------------------------------ */

static void ExpandBit_KENBAK(const tStrComp *pVarName, const struct sStructElem *pStructElem, LargeWord Base)
{
  LongWord Address = Base + pStructElem->Offset;

  if (pInnermostNamedStruct)
  {
    PStructElem pElem = CloneStructElem(pVarName, pStructElem);

    if (!pElem)
      return;
    pElem->Offset = Address;
    AddStructElem(pInnermostNamedStruct->StructRec, pElem);
  }
  else
  {
    PushLocHandle(-1);
    EnterIntSymbol(pVarName, AssembleBitSymbol(pStructElem->BitPos, Address), SegBData, False);
    PopLocHandle();
    /* TODO: MakeUseList? */
  }
}

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

/*!------------------------------------------------------------------------
 * \fn     CodeGen(Word Code)
 * \brief  decode generic 2-operand instructions
 * \param  Code instruction code in 1st byte
 * ------------------------------------------------------------------------ */

static void CodeGen(Word Code)
{
  if (ChkArgCnt(2, 3))
  {
    int RegArg;
    Boolean AddrOK;
    Byte Reg;
    tAdrData AdrData;

    /* addressing mode is either given by keyword or by addressing syntax: */

    if ((ArgCnt == 3) && DecodeAddrKeyword(ArgStr[1].str.p_str, &AdrData.Mode))
    {
      AdrData.Val = EvalStrIntExpression(&ArgStr[3], Int8, &AddrOK);
      RegArg = 2;
    }
    else
    {
      AddrOK = DecodeAdr(&ArgStr[2], (ArgCnt == 3) ? &ArgStr[3] : NULL, &AdrData, MModAll);
      RegArg = 1;
    }

    if (AddrOK && DecodeRegWithMemOpt(&ArgStr[RegArg], &Reg, Code & 0xc0 ? 1 : 7))
    {
      BAsmCode[CodeLen++] = (Reg << 6) | Code | AdrData.Mode;
      BAsmCode[CodeLen++] = AdrData.Val;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     CodeClear(Word Code)
 * \brief  decode CLEAR instruction
 * ------------------------------------------------------------------------ */

static void CodeClear(Word Code)
{
  Byte Reg;

  UNUSED(Code);

  if (ChkArgCnt(1, 1)
   && DecodeRegWithMemOpt(&ArgStr[1], &Reg, 7))
  {
    /* SUB reg,reg */

    BAsmCode[CodeLen++] = (Reg << 6) | 0x0c;
    BAsmCode[CodeLen++] = Reg;
  }
}

/*!------------------------------------------------------------------------
 * \fn     CodeJump(Word Code)
 * \brief  decode jump instructions (direct/indirect in mnemonic)
 * \param  Code instruction code in 1st byte
 * ------------------------------------------------------------------------ */

static void CodeJumpCommon(Word Code, const tAdrData *pAdrData)
{
  Byte Reg;
  int Cond;
  static const char ShortConds[5][4] = { "NZ", "Z", "N", "P", "PNZ" };
  static const char *LongConds[5] = { "Non-zero", "Zero", "Negative", "Positive", "Positive-Non-zero" };

  if ((ArgCnt == 1) || !as_strcasecmp(ArgStr[1].str.p_str, "Unconditional"))
    Reg = 3;
  else if (!DecodeRegWithMemOpt(&ArgStr[1], &Reg, 7))
    return;

  if (ArgCnt == 1)
    Cond = 0;
  else
  {
    for (Cond = 0; Cond < 5; Cond++)
      if (!as_strcasecmp(ArgStr[ArgCnt - 1].str.p_str, ShortConds[Cond])
       || !as_strcasecmp(ArgStr[ArgCnt - 1].str.p_str, LongConds[Cond]))
        break;
    if (Cond >= 5)
    {
      WrStrErrorPos(ErrNum_UndefCond, &ArgStr[ArgCnt - 1]);
      return;
    }
  }
  BAsmCode[CodeLen++] = (Reg << 6) | Code | (Cond + 3);
  BAsmCode[CodeLen++] = pAdrData->Val;
}

static void CodeJump(Word Code)
{
  tAdrData AdrData;

  if ((ArgCnt != 1) && (ArgCnt != 3))
  {
    WrError(ErrNum_WrongArgCnt);
    return;
  }
  if (DecodeAdr(&ArgStr[ArgCnt], NULL, &AdrData, MModMemory))
    CodeJumpCommon(Code, &AdrData);
}

/*!------------------------------------------------------------------------
 * \fn     CodeJumpGen(Word Code)
 * \brief  decode jump instructions (direct/indirect in addressing mode argument)
 * \param  Code instruction code in 1st byte
 * ------------------------------------------------------------------------ */

static void CodeJumpGen(Word Code)
{
  tAdrData AdrData;

  if ((ArgCnt != 1) && (ArgCnt != 3))
  {
    WrError(ErrNum_WrongArgCnt);
      return;
  }

  /* transport the addressing mode's indirect bit (bit 0) to the
     corresponding instruction code's bit (bit 3): */

  if (DecodeAdr(&ArgStr[ArgCnt], NULL, &AdrData, MModMemory | MModIndirect))
    CodeJumpCommon(Code | ((AdrData.Mode & 1) << 3), &AdrData);
}

/*!------------------------------------------------------------------------
 * \fn     CodeSkip(Word Code)
 * \brief  decode skip instructions
 * \param  Code instruction code in 1st byte
 * ------------------------------------------------------------------------ */

#define NODEST 0x100

static void CodeSkipCore(Word Code, int ArgOffs)
{
  LongWord BitSpec;
  tEvalResult DestEvalResult;
  Word Dest;

  /* For two operands, we do not know whether it's <addr>,<bit>
     or <bitsym>,<dest>.  If the third op is from code segment, we
     assume it's the second: */

  switch (ArgCnt - ArgOffs)
  {
    case 1:
      Dest = NODEST;
      break;
    case 3:
      Dest = EvalStrIntExpressionWithResult(&ArgStr[ArgOffs + 3], UInt8, &DestEvalResult);
      if (!DestEvalResult.OK)
        return;
      break;
    case 2:
    {
      Dest = EvalStrIntExpressionWithResult(&ArgStr[ArgOffs + 2], UInt8, &DestEvalResult);
      if (!DestEvalResult.OK)
        return;
      if (!(DestEvalResult.AddrSpaceMask & (1 << SegCode)))
        Dest = NODEST;
      break;
    }
    default:
      (void)ChkArgCnt(1 + ArgOffs, 3 + ArgOffs);
      return;
  }

  if (DecodeBitArg(&BitSpec, 1 + ArgOffs, ArgCnt - (Dest != NODEST)))
  {
    Word Address;
    Byte BitPos;

    DissectBitSymbol(BitSpec, &Address, &BitPos);

    if (Dest != NODEST)
    {
      if (!(DestEvalResult.Flags & (eSymbolFlag_FirstPassUnknown | eSymbolFlag_Questionable))
       && (Dest != EProgCounter() + 4))
      {
        WrStrErrorPos(ErrNum_SkipTargetMismatch, &ArgStr[ArgCnt]);
        return;
      }
    }
    BAsmCode[CodeLen++] = Code | (BitPos << 3);
    BAsmCode[CodeLen++] = Address;
  }
}

static void CodeSkip(Word Code)
{
  CodeSkipCore(Code, 0);
}

static void CodeSkip2(Word Code)
{
  if (ChkArgCnt(2, 4))
  {
    Boolean OK;
    Byte Value = EvalStrIntExpression(&ArgStr[1], UInt1, &OK);
    if (OK)
      CodeSkipCore(Code | (Value << 6), 1);
  }
}

/*!------------------------------------------------------------------------
 * \fn     CodeReg(Word Code)
 * \brief  decode shift/rotate instructions
 * \param  Code instruction code in 1st byte
 * ------------------------------------------------------------------------ */

static void CodeShiftCore(Word Code, int ArgOffs)
{
  Byte Reg;

  if (DecodeRegWithMemOpt(&ArgStr[ArgCnt], &Reg, 3))
  {
    Byte Count = 1;

    if (ArgCnt > ArgOffs)
    {
      tEvalResult EvalResult;

      Count = EvalStrIntExpressionWithResult(&ArgStr[ArgOffs], UInt3, &EvalResult);
      if (!EvalResult.OK)
        return;
      if (mFirstPassUnknown(EvalResult.Flags))
        Count = 1;
      if ((Count < 1) || (Count > 4))
      {
        WrStrErrorPos(ErrNum_InvShiftArg, &ArgStr[ArgOffs]);
        return;
      }
    }
    BAsmCode[CodeLen++] = Code | (Reg << 5) | ((Count & 3) << 3);
  }
}

static void CodeShift(Word Code)
{
  if (ChkArgCnt(1, 2))
    CodeShiftCore(Code, 1);
}

static void CodeShift2(Word Code)
{
  if (ChkArgCnt(2, 3))
  {
    if (!as_strcasecmp(ArgStr[1].str.p_str, "LEFT"))
      CodeShiftCore(Code | 0x80, 2);
    else if (!as_strcasecmp(ArgStr[1].str.p_str, "RIGHT"))
      CodeShiftCore(Code, 2);
    else
      WrStrErrorPos(ErrNum_InvShiftArg, &ArgStr[1]);
  }
}

/*!------------------------------------------------------------------------
 * \fn     CodeBit(Word Code)
 * \brief  decode bit instructions
 * \param  Code instruction code in 1st byte
 * ------------------------------------------------------------------------ */

static void CodeBitCore(Word Code, int BitArgStart)
{
  LongWord BitSpec;

  if (DecodeBitArg(&BitSpec, BitArgStart, ArgCnt))
  {
    Word Address;
    Byte BitPos;

    DissectBitSymbol(BitSpec, &Address, &BitPos);
    BAsmCode[CodeLen++] = Code | (BitPos << 3);
    BAsmCode[CodeLen++] = Address;
  }
}

static void CodeBit(Word Code)
{
  if (ChkArgCnt(1, 2))
    CodeBitCore(Code, 1);
}

static void CodeBit2(Word Code)
{
  if (ChkArgCnt(2, 3))
  {
    Boolean OK;
    Byte Value = EvalStrIntExpression(&ArgStr[1], UInt1, &OK);
    if (OK)
      CodeBitCore(Code | (Value << 6), 2);
  }
}

/*!------------------------------------------------------------------------
 * \fn     CodeFixed(Word Code)
 * \brief  decode instructions witohut argument
 * \param  Code instruction code in 1st byte
 * ------------------------------------------------------------------------ */

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

/*!------------------------------------------------------------------------
 * \fn     CodeBIT(Word Code)
 * \brief  handle BIT instruction
 * ------------------------------------------------------------------------ */

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

  /* if in structure definition, add special element to structure */

  if (ActPC == StructSeg)
  {
    Boolean OK;
    Byte BitPos;
    PStructElem pElement;

    if (!ChkArgCnt(2, 2))
      return;
    BitPos = EvalBitPosition(&ArgStr[1], &OK);
    if (!OK)
      return;
    pElement = CreateStructElem(&LabPart);
    if (!pElement)
      return;
    pElement->pRefElemName = as_strdup(ArgStr[2].str.p_str);
    pElement->OpSize = eSymbolSize8Bit;
    pElement->BitPos = BitPos;
    pElement->ExpandFnc = ExpandBit_KENBAK;
    AddStructElem(pInnermostNamedStruct->StructRec, pElement);
  }
  else
  {
    LongWord BitSpec;

    if (DecodeBitArg(&BitSpec, 1, ArgCnt))
    {
      *ListLine = '=';
      DissectBit_KENBAK(ListLine + 1, STRINGSIZE - 3, BitSpec);
      PushLocHandle(-1);
      EnterIntSymbol(&LabPart, BitSpec, SegBData, False);
      PopLocHandle();
      /* TODO: MakeUseList? */
    }
  }
}

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

/*!------------------------------------------------------------------------
 * \fn     InitFields(void)
 * \brief  fill instruction hash table
 * ------------------------------------------------------------------------ */

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

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "ADD"  , 0 << 3, CodeGen);
  AddInstTable(InstTable, "SUB"  , 1 << 3, CodeGen);
  AddInstTable(InstTable, "LOAD" , 2 << 3, CodeGen);
  AddInstTable(InstTable, "STORE", 3 << 3, CodeGen);

  AddInstTable(InstTable, "AND"  , 0xd0, CodeGen);
  AddInstTable(InstTable, "OR"   , 0xc0, CodeGen);
  AddInstTable(InstTable, "LNEG" , 0xd8, CodeGen);

  AddInstTable(InstTable, "JPD"  , 0x20, CodeJump);
  AddInstTable(InstTable, "JPI"  , 0x28, CodeJump);
  AddInstTable(InstTable, "JMD"  , 0x30, CodeJump);
  AddInstTable(InstTable, "JMI"  , 0x38, CodeJump);

  AddInstTable(InstTable, "JP"   , 0x20, CodeJumpGen);
  AddInstTable(InstTable, "JM"   , 0x30, CodeJumpGen);

  AddInstTable(InstTable, "SKP0" , 0x82, CodeSkip);
  AddInstTable(InstTable, "SKP1" , 0xc2, CodeSkip);
  AddInstTable(InstTable, "SKP"  , 0x82, CodeSkip2);
  AddInstTable(InstTable, "SKIP" , 0x82, CodeSkip2);

  AddInstTable(InstTable, "SET0" , 0x02, CodeBit);
  AddInstTable(InstTable, "SET1" , 0x42, CodeBit);
  AddInstTable(InstTable, "SET"  , 0x02, CodeBit2);

  AddInstTable(InstTable, "SFTL"  , 0x81, CodeShift);
  AddInstTable(InstTable, "SFTR"  , 0x01, CodeShift);
  AddInstTable(InstTable, "ROTL"  , 0xc1, CodeShift);
  AddInstTable(InstTable, "ROTR"  , 0x41, CodeShift);
  AddInstTable(InstTable, "SHIFT" , 0x01, CodeShift2);
  AddInstTable(InstTable, "ROTATE", 0x41, CodeShift2);

  AddInstTable(InstTable, "CLEAR" , 0x00, CodeClear);

  AddInstTable(InstTable, "NOOP" , 0x80, CodeFixed);
  AddInstTable(InstTable, "HALT" , 0x00, CodeFixed);

  AddInstTable(InstTable, "REG"  , 0   , CodeREG);
  AddInstTable(InstTable, "BIT"  , 0   , CodeBIT);

  AddIntelPseudo(InstTable, eIntPseudoFlag_LittleEndian);
}

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

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

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

static void InternSymbol_KENBAK(char *pArg, TempResult *pResult)
{
  Byte RegNum;

  if (DecodeRegCore(pArg, &RegNum))
  {
    pResult->Typ = TempReg;
    pResult->DataSize = eSymbolSize8Bit;
    pResult->Contents.RegDescr.Reg = RegNum;
    pResult->Contents.RegDescr.Dissect = DissectReg_KENBAK;
    pResult->Contents.RegDescr.compare = NULL;
  }
}

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

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

static Boolean TrueFnc(void)
{
  return True;
}

/*!------------------------------------------------------------------------
 * \fn     SwitchTo_KENBAK(void)
 * \brief  initialize for KENBAK as target
 * ------------------------------------------------------------------------ */

static void SwitchTo_KENBAK(void)
{
  const TFamilyDescr *Descr;

  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);

  Descr = FindFamilyByName("KENBAK");
  PCSymbol = "$";
  HeaderID = Descr->Id;
  NOPCode = 0x80;
  DivideChars = ",";
  HasAttrs = False;
  SetIsOccupiedFnc = TrueFnc;
  ShiftIsOccupied = True;

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

  MakeCode = MakeCode_KENBAK;
  IsDef = IsDef_KENBAK;
  DissectReg = DissectReg_KENBAK;
  DissectBit = DissectBit_KENBAK;
  SwitchFrom = SwitchFrom_KENBAK;
  InternSymbol = InternSymbol_KENBAK;
  InitFields();
}

void codekenbak_init(void)
{
  (void)AddCPU("KENBAK", SwitchTo_KENBAK);
}
