/* codecp3f.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS                                                                        */
/*                                                                           */
/* Code Generator Olympia CP-3F                                              */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"

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

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

#include "codecp3f.h"

/*---------------------------------------------------------------------------*/
/* Address Decoders */

typedef enum
{
  e_mode_none = -1,
  e_mode_a = 0,
  e_mode_v = 1,
  e_mode_w = 2,
  e_mode_x = 3,
  e_mode_y = 4,
  e_mode_s = 5,
  e_mode_t = 6, 
  e_mode_st = 7,
  e_mode_imm = 8,
  e_mode_imm_short = 9,
  e_mode_dir = 10,
  e_mode_q = 11,
  e_mode_z = 12,
  e_mode_iz = 13
} adr_mode_t;

#define MModeA (1 << e_mode_a)
#define MModeV (1 << e_mode_v)
#define MModeW (1 << e_mode_w)
#define MModeX (1 << e_mode_x)
#define MModeY (1 << e_mode_y)
#define MModeS (1 << e_mode_s)
#define MModeT (1 << e_mode_t)
#define MModeST (1 << e_mode_st)
#define MModeQ (1 << e_mode_q)
#define MModeZ (1 << e_mode_z)
#define MModeIZ (1 << e_mode_iz)
#define MModeImm (1 << e_mode_imm)
#define MModeImmShort (1 << e_mode_imm_short)
#define MModeDir (1 << e_mode_dir)

typedef struct
{
  adr_mode_t mode;
  Byte value;
} adr_vals_t;

/*!------------------------------------------------------------------------
 * \fn     reset_adr_vals(adr_vals_t *p_vals)
 * \brief  reset ecoded address argument to none
 * \param  p_vals argument to reset
 * ------------------------------------------------------------------------ */

static void reset_adr_vals(adr_vals_t *p_vals)
{
  p_vals->mode = e_mode_none;
  p_vals->value = 0;
}

/*!------------------------------------------------------------------------
 * \fn     decode_adr(const tStrComp *p_arg, adr_vals_t *p_vals, unsigned mask, tSymbolSize op_size)
 * \brief  decode address expression
 * \param  p_arg source argument
 * \param  p_vals encoded mode
 * \param  mask bit mask of allowed modes
 * \return deduced address mode
 * ------------------------------------------------------------------------ */

static int chk_xy(const char *p_arg, const char *p_pattern, Byte *p_reg)
{
  for (; *p_arg && *p_pattern; p_arg++, p_pattern++)
  {
    if (*p_pattern == '*')
    {
      switch (as_toupper(*p_arg))
      {
        case 'X': *p_reg = 0; break;
        case 'Y': *p_reg = 1; break;
        default: return 1;
      }
    }
    else if (as_toupper(*p_arg) != as_toupper(*p_pattern))
      return 1;
  }
  return !(!*p_arg && !*p_pattern);
}

static adr_mode_t decode_adr(const tStrComp *p_arg, adr_vals_t *p_vals, unsigned mask, tSymbolSize op_size)
{
  size_t l = strlen(p_arg->str.p_str);
  tEvalResult eval_result;

  reset_adr_vals(p_vals);

  if (l == 1)
  {
    const char reg_names[] = "AVWXYST";
    const char *p_pos = strchr(reg_names, as_toupper(p_arg->str.p_str[0]));

    if (p_pos)
    {
      p_vals->mode = (adr_mode_t)(e_mode_a + (p_pos - reg_names));
      goto fini;
    }
  }

  if (*p_arg->str.p_str == '#')
  {
    int offset = 1;

    switch (op_size)
    {
      case eSymbolSize24Bit:
        p_vals->value = EvalStrIntExpressionOffsWithResult(p_arg, offset, UInt3, &eval_result);
        if (eval_result.OK)
          p_vals->mode = e_mode_imm;
        goto fini;
      case eSymbolSize8Bit:
      {
        Boolean force_short = False,
                force_long = False,
                opt_short = !!(mask & MModeImmShort);

        if (opt_short)
          switch (p_arg->str.p_str[offset])
          {
            case '<':
              force_short = True;
              offset++;
              break;
            case '>':
              force_long = True;
              offset++;
              break;
            default:
              break;
          }
        p_vals->value = EvalStrIntExpressionOffsWithResult(p_arg, offset, force_short ? UInt4 : Int8, &eval_result);
        if (eval_result.OK)
        {
          if (!opt_short)
            p_vals->mode = e_mode_imm;
          else if (force_long)
            p_vals->mode = e_mode_imm;
          else
            p_vals->mode = (p_vals->value < 16) ? e_mode_imm_short : e_mode_imm;
        }
        goto fini;
      }
      default:
        WrStrErrorPos(ErrNum_UndefOpSizes, p_arg);
        goto fini;
    }
  }

  if (!as_strcasecmp(p_arg->str.p_str, "ST"))
  {
    p_vals->mode = e_mode_st;
    goto fini;
  }
  if (!as_strcasecmp(p_arg->str.p_str, "(ST)"))
  {
    p_vals->mode = e_mode_dir;
    p_vals->value = 12;
    goto fini;
  }
  if (!as_strcasecmp(p_arg->str.p_str, "(ST)-"))
  {
    p_vals->mode = e_mode_dir;
    p_vals->value = 13;
    goto fini;
  }
  if (!as_strcasecmp(p_arg->str.p_str, "(ST)+"))
  {
    p_vals->mode = e_mode_dir;
    p_vals->value = 14;
    goto fini;
  }
  if (!chk_xy(p_arg->str.p_str, "Q(*)", &p_vals->value))
  {
    p_vals->mode = e_mode_q;
    goto fini;
  }
  if (!chk_xy(p_arg->str.p_str, "Z(*)", &p_vals->value))
  {
    p_vals->mode = e_mode_z;
    goto fini;
  }
  if (!chk_xy(p_arg->str.p_str, "(Z(*))", &p_vals->value))
  {
    p_vals->mode = e_mode_iz;
    goto fini;
  }

  p_vals->value = EvalStrIntExpressionWithResult(p_arg, UInt4, &eval_result);
  if (mFirstPassUnknownOrQuestionable(eval_result.Flags))
    p_vals->value &= 7;
  if (ChkRange(p_vals->value, 0, 11))
  {
    ChkSpace(SegData, eval_result.AddrSpaceMask);
    p_vals->mode = e_mode_dir;
    goto fini;
  }

fini:
  if ((p_vals->mode != e_mode_none) && !((mask >> p_vals->mode) & 1))
  {
    WrStrErrorPos(ErrNum_InvAddrMode, p_arg);
    reset_adr_vals(p_vals);
  }
  return p_vals->mode;
}

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

/*!------------------------------------------------------------------------
 * \fn     decode_imm_4(Word code)
 * \brief  handle instructions with 4 bit immediate operand
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void decode_imm_4(Word code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean ok;

    BAsmCode[0] = EvalStrIntExpression(&ArgStr[1], UInt4, &ok);
    if (ok)
    {
      BAsmCode[0] = code | (BAsmCode[0] & 0x0f);
      CodeLen = 1;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     decode_imm_3(Word code)
 * \brief  handle instructions with 3 bit immediate operand
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void decode_imm_3(Word code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean ok;

    BAsmCode[0] = EvalStrIntExpression(&ArgStr[1], UInt3, &ok);
    if (ok)
    {
      BAsmCode[0] = code | (BAsmCode[0] & 0x07);
      CodeLen = 1;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     decode_imm_8(Word code)
 * \brief  handle instructions with 8 bit immediate operand
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void decode_imm_8(Word code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean ok;

    BAsmCode[1] = EvalStrIntExpression(&ArgStr[1], Int8, &ok);
    if (ok)
    {
      BAsmCode[0] = code;
      CodeLen = 2;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     decode_fixed(Word code)
 * \brief  handle instructions with no argument
 * \param  code machine code
 * ------------------------------------------------------------------------ */

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

/*!------------------------------------------------------------------------
 * \fn     decode_reg(Word code)
 * \brief  handle instructions with register argument
 * \param  code machine code
 * ------------------------------------------------------------------------ */

/* Original syntax assumes programmer knows that 12..14 refer
   to (ST), (ST)+, (ST)-... */

static void decode_reg(Word code)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult eval_result;

    BAsmCode[0] = EvalStrIntExpressionWithResult(&ArgStr[1], UInt4, &eval_result);
    if (eval_result.OK)
    {
      if (mFirstPassUnknownOrQuestionable(eval_result.Flags))
        BAsmCode[0] = 0;
      if (ChkRange(BAsmCode[0], 0, 14))
      {
        BAsmCode[0] |= code;
        CodeLen = 1;
      }
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     decode_io(Word code)
 * \brief  handle instructions with I/O address operand
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void decode_io(Word code)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult eval_result;

    BAsmCode[0] = EvalStrIntExpressionWithResult(&ArgStr[1], UInt3, &eval_result);
    if (eval_result.OK)
    {
      ChkSpace(SegIO, eval_result.AddrSpaceMask);
      BAsmCode[0] = code | (BAsmCode[0] & 0x07);
      CodeLen = 1;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     decode_port(Word code)
 * \brief  handle PORt instruction
 * ------------------------------------------------------------------------ */

static void decode_port(Word code)
{
  UNUSED(code);

  code_equate_type(SegIO, UInt3);
}

/*!------------------------------------------------------------------------
 * \fn     decode_jmp(Word code)
 * \brief  handle instructions with target address operand
 * \param  code machine code
 * ------------------------------------------------------------------------ */

static void decode_jmp(Word code)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult eval_result;
    Word address = EvalStrIntExpressionWithResult(&ArgStr[1], UInt14, &eval_result);

    if (eval_result.OK
     && ChkSamePage(EProgCounter(), address, 11, eval_result.Flags))
    {
      BAsmCode[0] = code | (Hi(address) & 7);
      BAsmCode[1] = Lo(address);
      CodeLen = 2;
    }
  }
}

/*!------------------------------------------------------------------------
 * \fn     void decode_ld(Word code)
 * \brief  decode virtual LD instruction
 * ------------------------------------------------------------------------ */

static void decode_ld(Word code)
{
  adr_vals_t src_adr_vals, dest_adr_vals;

  UNUSED(code);

  if (ChkArgCnt(2, 2))
    switch (decode_adr(&ArgStr[1], &dest_adr_vals, MModeA | MModeV | MModeW | MModeX | MModeY | MModeS | MModeT | MModeST | MModeDir | MModeQ | MModeZ | MModeIZ, eSymbolSizeUnknown))
    {
      case e_mode_a:
        switch (decode_adr(&ArgStr[2], &src_adr_vals, MModeV | MModeW | MModeX | MModeY | MModeDir | MModeImm | MModeImmShort | MModeIZ, eSymbolSize8Bit))
        {
          case e_mode_v:
          case e_mode_w:
          case e_mode_x:
          case e_mode_y:
            BAsmCode[0] = 0x08 | (src_adr_vals.mode - e_mode_v);
            CodeLen = 1;
            break;
          case e_mode_dir:
            BAsmCode[0] = 0x80 | src_adr_vals.value;
            CodeLen = 1;
            break;
          case e_mode_imm:
            BAsmCode[0] = 0x04;
            BAsmCode[1] = src_adr_vals.value;
            CodeLen = 2;
            break;
          case e_mode_imm_short:
            BAsmCode[0] = 0xf0 | (src_adr_vals.value & 15);
            CodeLen = 1;
            break;
          case e_mode_iz:
            BAsmCode[0] = 0x06 | src_adr_vals.value;
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case e_mode_v:
      case e_mode_w:
      case e_mode_x:
      case e_mode_y:
        switch (decode_adr(&ArgStr[2], &src_adr_vals, MModeA, eSymbolSize8Bit))
        {
          case e_mode_a:
            BAsmCode[0] = 0x18 | (dest_adr_vals.mode - e_mode_v);
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case e_mode_s:
        switch (decode_adr(&ArgStr[2], &src_adr_vals, MModeImm, eSymbolSize24Bit))
        {
          case e_mode_imm:
            BAsmCode[0] = 0x28 | (src_adr_vals.value & 7);
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case e_mode_t:
        switch (decode_adr(&ArgStr[2], &src_adr_vals, MModeA | MModeImm, eSymbolSize24Bit))
        {
          case e_mode_a:
            BAsmCode[0] = 0x01;
            CodeLen = 1;
            break;
          case e_mode_imm:
            BAsmCode[0] = 0x38 | (src_adr_vals.value & 7);
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case e_mode_st:
        switch (decode_adr(&ArgStr[2], &src_adr_vals, MModeA, eSymbolSizeUnknown))
        {
          case e_mode_a:
            BAsmCode[0] = 0x03;
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case e_mode_q:
        switch (decode_adr(&ArgStr[2], &src_adr_vals, MModeA, eSymbolSizeUnknown))
        {
          case e_mode_a:
            BAsmCode[0] = 0x16 | dest_adr_vals.value;
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case e_mode_z:
        switch (decode_adr(&ArgStr[2], &src_adr_vals, MModeA, eSymbolSizeUnknown))
        {
          case e_mode_a:
            BAsmCode[0] = 0x12 | dest_adr_vals.value;
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case e_mode_iz:
        if (dest_adr_vals.value) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
        else switch (decode_adr(&ArgStr[2], &src_adr_vals, MModeA, eSymbolSizeUnknown))
        {
          case e_mode_a:
            BAsmCode[0] = 0x02 | dest_adr_vals.value;
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case e_mode_dir:
        switch (decode_adr(&ArgStr[2], &src_adr_vals, MModeA, eSymbolSize8Bit))
        {
          case e_mode_a:
            BAsmCode[0] = 0x90 | dest_adr_vals.value;
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
}

/*!------------------------------------------------------------------------
 * \fn     void decode_ari(Word code)
 * \brief  decode virtual arithmetic instruction
 * ------------------------------------------------------------------------ */

static void decode_ari(Word code)
{
  adr_vals_t src_adr_vals, dest_adr_vals;
  unsigned mask;
  Byte imm_code = Hi(code), dir_code = Lo(code);

  if (!ChkArgCnt(1, 2))
    return;

  if (ArgCnt == 2)
  {
    if (decode_adr(&ArgStr[1], &dest_adr_vals, MModeA, eSymbolSize8Bit) != e_mode_a)
      return;
  }

  mask = ((imm_code == 0xff) ? 0 : MModeImm)
       | ((dir_code == 0xff) ? 0 : MModeDir);
  switch (decode_adr(&ArgStr[ArgCnt], &src_adr_vals, mask, eSymbolSize8Bit))
  {
    case e_mode_dir:
      BAsmCode[0] = dir_code | src_adr_vals.value;
      CodeLen = 1;
      break;
    case e_mode_imm:
      BAsmCode[0] = imm_code;
      BAsmCode[1] = src_adr_vals.value;
      CodeLen = 2;
      break;
    default:
      break;
  }
}

/*!------------------------------------------------------------------------
 * \fn     decode_srl_sla(Word code)
 * \brief  decode virtual shift instructions
 * \param  code machine code for single bit shift
 * ------------------------------------------------------------------------ */

static void decode_srl_sla(Word code)
{
  int shift_arg_index;
  Byte shift_cnt;

  switch (ArgCnt)
  {
    case 1:
      if (!as_strcasecmp(ArgStr[1].str.p_str, "A"))
        goto acc_1;
      shift_arg_index = 1;
      goto eval_shift;
    case 0:
    acc_1:
      shift_cnt = 1;
      break;
    case 2:
    {
      adr_vals_t adr_vals;

      if (decode_adr(&ArgStr[1], &adr_vals, MModeA, eSymbolSize8Bit) != e_mode_a)
        return;
      shift_arg_index = 2;
      /* FALL-THRU */
    }
    eval_shift:
    {
      tEvalResult eval_result;
      shift_cnt = EvalStrIntExpressionWithResult(&ArgStr[shift_arg_index], UInt8, &eval_result);
      if (!eval_result.OK)
        return;
      if (mFirstPassUnknownOrQuestionable(eval_result.Flags))
        shift_cnt = 1;
      if ((shift_cnt != 1) && (shift_cnt != 4))
      {
        WrStrErrorPos(ErrNum_InvShiftArg, &ArgStr[shift_arg_index]);
        return;
      }
      break;
    }
    default:
      (void)ChkArgCnt(0, 2);
      return;
  }

  BAsmCode[0] = code | ((shift_cnt > 1) ? 2 : 0);
  CodeLen = 1;
}

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

/*!------------------------------------------------------------------------
 * \fn     init_fields(void)
 * \brief  build up instruction hash table
 * ------------------------------------------------------------------------ */

static void init_fields(void)
{
  InstTable = CreateInstTable(101);

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "LAS", 0xf0, decode_imm_4);
  AddInstTable(InstTable, "LSS", 0x28, decode_imm_3);
  AddInstTable(InstTable, "LTS", 0x38, decode_imm_3);
  AddInstTable(InstTable, "LAL", 0x04, decode_imm_8);
  AddInstTable(InstTable, "ANL", 0x05, decode_imm_8);
  AddInstTable(InstTable, "EOL", 0x0c, decode_imm_8);
  AddInstTable(InstTable, "ORL", 0x0d, decode_imm_8);
  AddInstTable(InstTable, "ADL", 0x0e, decode_imm_8);
  AddInstTable(InstTable, "CML", 0x0f, decode_imm_8);

  AddInstTable(InstTable, "LAV", 0x08, decode_fixed);
  AddInstTable(InstTable, "LAW", 0x09, decode_fixed);
  AddInstTable(InstTable, "LAX", 0x0a, decode_fixed);
  AddInstTable(InstTable, "LAY", 0x0b, decode_fixed);
  AddInstTable(InstTable, "SAV", 0x18, decode_fixed);
  AddInstTable(InstTable, "SAW", 0x19, decode_fixed);
  AddInstTable(InstTable, "SAX", 0x1a, decode_fixed);
  AddInstTable(InstTable, "SAY", 0x1b, decode_fixed);
  AddInstTable(InstTable, "SAT", 0x01, decode_fixed);
  AddInstTable(InstTable, "SST", 0x03, decode_fixed);
  AddInstTable(InstTable, "ALS", 0x1c, decode_fixed);
  AddInstTable(InstTable, "ARS", 0x1d, decode_fixed);
  AddInstTable(InstTable, "ALF", 0x1e, decode_fixed);
  AddInstTable(InstTable, "ARF", 0x1f, decode_fixed);
  AddInstTable(InstTable, "RET", 0x00, decode_fixed);
  AddInstTable(InstTable, "SIX", 0x02, decode_fixed);
  AddInstTable(InstTable, "LIX", 0x06, decode_fixed);
  AddInstTable(InstTable, "LIY", 0x07, decode_fixed);
  AddInstTable(InstTable, "SQX", 0x16, decode_fixed);
  AddInstTable(InstTable, "SQY", 0x17, decode_fixed);
  AddInstTable(InstTable, "SZX", 0x12, decode_fixed);
  AddInstTable(InstTable, "SZY", 0x13, decode_fixed);

  AddInstTable(InstTable, "LAR", 0x80, decode_reg);
  AddInstTable(InstTable, "SAR", 0x90, decode_reg);
  AddInstTable(InstTable, "ADR", 0xa0, decode_reg);
  AddInstTable(InstTable, "ANR", 0xb0, decode_reg);
  AddInstTable(InstTable, "EOR", 0xc0, decode_reg);
  AddInstTable(InstTable, "DER", 0xd0, decode_reg);
  AddInstTable(InstTable, "DAR", 0xe0, decode_reg);

  AddInstTable(InstTable, "INP", 0x20, decode_io);
  AddInstTable(InstTable, "OUT", 0x30, decode_io);
  AddInstTable(InstTable, "PORT", 0, decode_port);

  AddInstTable(InstTable, "JMP", 0x40, decode_jmp);
  AddInstTable(InstTable, "JAZ", 0x48, decode_jmp);
  AddInstTable(InstTable, "JAN", 0x50, decode_jmp);
  AddInstTable(InstTable, "JAP", 0x58, decode_jmp);
  AddInstTable(InstTable, "JSD", 0x60, decode_jmp);
  AddInstTable(InstTable, "JCN", 0x68, decode_jmp);
  AddInstTable(InstTable, "JCZ", 0x70, decode_jmp);
  AddInstTable(InstTable, "JSB", 0x78, decode_jmp);
  AddInstTable(InstTable, "GOS", 0x78, decode_jmp);

  AddInstTable(InstTable, "LD", 0, decode_ld);
  AddInstTable(InstTable, "ADD", 0x0ea0, decode_ari);
  AddInstTable(InstTable, "AND", 0x05b0, decode_ari);
  AddInstTable(InstTable, "XOR", 0x0cc0, decode_ari);
  AddInstTable(InstTable, "OR" , 0x0dff, decode_ari);
	AddInstTable(InstTable, "CP" , 0x0fff, decode_ari);
  AddInstTable(InstTable, "DEC", 0xffd0, decode_ari);
  AddInstTable(InstTable, "SLA", 0x1c, decode_srl_sla);
  AddInstTable(InstTable, "SRL", 0x1d, decode_srl_sla);

  AddInstTable(InstTable, "DC", e_moto_pseudo_flags_le, DecodeMotoBYT);
  AddInstTable(InstTable, "DS", e_moto_pseudo_flags_none, DecodeMotoDFS);
}

/*!------------------------------------------------------------------------
 * \fn     deinit_fields(void)
 * \brief  tear down instruction hash table
 * ------------------------------------------------------------------------ */

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

/*---------------------------------------------------------------------------*/
/* Interface Functions */

/*!------------------------------------------------------------------------
 * \fn     make_code_cp3f(void)
 * \brief  machine instruction dispatcher
 * ------------------------------------------------------------------------ */

static void make_code_cp3f(void)
{
  /* pseudo instructions */

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

/*!------------------------------------------------------------------------
 * \fn     switch_from_cp3f(void)
 * \brief  cleanups after switch from target
 * ------------------------------------------------------------------------ */

static void switch_from_cp3f(void)
{
  deinit_fields();
}

/*!------------------------------------------------------------------------
 * \fn     is_def_cp_3f(void)
 * \brief  does instruction use label field?
 * ------------------------------------------------------------------------ */

static Boolean is_def_cp3f(void)
{
  return Memo("PORT");
}

/*!------------------------------------------------------------------------
 * \fn     switch_to_cp_3f(void)
 * \brief  prepare to assemble code for this target
 * ------------------------------------------------------------------------ */

static void switch_to_cp3f(void)
{
  const TFamilyDescr *p_descr = FindFamilyByName("CP-3F");

  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);

  p_descr = FindFamilyByName("CP-3F");
  PCSymbol = "$";
  HeaderID = p_descr->Id;
  NOPCode = 0x00;
  DivideChars = ",";
  HasAttrs = False;

  ValidSegs = (1 << SegCode) | (1 << SegData) | (1 << SegIO);
  Grans[SegCode] = 1; ListGrans[SegCode] = 1; SegLimits[SegCode] = 0x3fff;
  Grans[SegData] = 1; ListGrans[SegData] = 1; SegLimits[SegData] = 0x2f;
  Grans[SegIO  ] = 1; ListGrans[SegIO  ] = 1; SegLimits[SegIO  ] = 0x7;

  MakeCode = make_code_cp3f;
  SwitchFrom = switch_from_cp3f;
  IsDef = is_def_cp3f;
  init_fields();
}

/*!------------------------------------------------------------------------
 * \fn     codecp3f__init(void)
 * \brief  register CP-3F target
 * ------------------------------------------------------------------------ */

void codecp3f_init(void)
{
  (void)AddCPU("CP-3F"    , switch_to_cp3f);
  (void)AddCPU("M380"     , switch_to_cp3f);
  (void)AddCPU("LP8000"   , switch_to_cp3f);
}
