/* code17c4x.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator PIC17C4x                                                    */
/*                                                                           */
/*****************************************************************************/

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

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

#include "code17c4x.h"

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

static CPUVar CPU17C42;

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

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

static void DecodeLitt(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Word AdrWord = EvalStrIntExpression(&ArgStr[1], Int8, &OK);
    if (OK)
    {
      WAsmCode[0] = Code + (AdrWord & 0xff);
      CodeLen = 1;
    }
  }
}

static void DecodeAri(Word Code)
{
  Word DefaultDir = (Code >> 7) & 0x100;

  Code &= 0x7fff;
  if (ChkArgCnt(1, 2))
  {
    tEvalResult EvalResult;
    Word AdrWord = EvalStrIntExpressionWithResult(&ArgStr[1], Int8, &EvalResult);

    if (EvalResult.OK)
    {
      ChkSpace(SegData, EvalResult.AddrSpaceMask);
      WAsmCode[0] = Code + (AdrWord & 0xff);
      if (ArgCnt == 1)
      {
        CodeLen = 1;
        WAsmCode[0] += DefaultDir;
      }
      else if (as_strcasecmp(ArgStr[2].str.p_str, "W") == 0)
        CodeLen = 1;
      else if (as_strcasecmp(ArgStr[2].str.p_str, "F") == 0)
      {
        CodeLen = 1;
        WAsmCode[0] += 0x100;
      }
      else
      {
        AdrWord = EvalStrIntExpressionWithResult(&ArgStr[2], UInt1, &EvalResult);
        if (EvalResult.OK)
        {
          CodeLen = 1;
          WAsmCode[0] += (AdrWord << 8);
        }
      }
    }
  }
}

static void DecodeBit(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    tEvalResult EvalResult;
    Word AdrWord = EvalStrIntExpressionWithResult(&ArgStr[2], UInt3, &EvalResult);
    if (EvalResult.OK)
    {
      WAsmCode[0] = EvalStrIntExpressionWithResult(&ArgStr[1], Int8, &EvalResult);
       if (EvalResult.OK)
       {
         CodeLen = 1;
         WAsmCode[0] += Code + (AdrWord << 8);
         ChkSpace(SegData, EvalResult.AddrSpaceMask);
       }
    }
  }
}

static void DecodeF(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word AdrWord = EvalStrIntExpressionWithResult(&ArgStr[1], Int8, &EvalResult);
    if (EvalResult.OK)
    {
      CodeLen = 1;
      WAsmCode[0] = Code + AdrWord;
      ChkSpace(SegData, EvalResult.AddrSpaceMask);
    }
  }
}

static void DecodeMOVFP_MOVPF(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    tStrComp *pArg1 = (Code & 0x2000) ? &ArgStr[2] : &ArgStr[1],
             *pArg2 = (Code & 0x2000) ? &ArgStr[1] : &ArgStr[2];
    Boolean OK;
    Word AdrWord;

    AdrWord = EvalStrIntExpression(pArg1, UInt5, &OK);
    if (OK)
    {
      WAsmCode[0] = EvalStrIntExpression(pArg2, Int8, &OK);
      if (OK)
      {
        WAsmCode[0] = Code + Lo(WAsmCode[0]) + (AdrWord << 8);
        CodeLen = 1;
      }
    }
  }
}

static void DecodeTABLRD_TABLWT(Word Code)
{
  if (ChkArgCnt(3, 3))
  {
    Boolean OK;

    WAsmCode[0] = Lo(EvalStrIntExpression(&ArgStr[3], Int8, &OK));
    if (OK)
    {
      Word AdrWord = EvalStrIntExpression(&ArgStr[2], UInt1, &OK);
      if (OK)
      {
        WAsmCode[0] += AdrWord << 8;
        AdrWord = EvalStrIntExpression(&ArgStr[1], UInt1, &OK);
        if (OK)
        {
          WAsmCode[0] += Code + (AdrWord << 9);
          CodeLen = 1;
        }
      }
    }
  }
}

static void DecodeTLRD_TLWT(Word Code)
{
  if (ChkArgCnt(2, 2))
  {
    Boolean OK;

    WAsmCode[0] = Lo(EvalStrIntExpression(&ArgStr[2], Int8, &OK));
    if (OK)
    {
      Word AdrWord = EvalStrIntExpression(&ArgStr[1], UInt1, &OK);
      if (OK)
      {
        WAsmCode[0] += (AdrWord << 9) + Code;
        CodeLen = 1;
      }
    }
  }
  return;
}

static void DecodeCALL_GOTO(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Word AdrWord;
    tSymbolFlags Flags;

    AdrWord = EvalStrIntExpressionWithFlags(&ArgStr[1], UInt16, &OK, &Flags);
    if (OK && ChkSamePage(ProgCounter(), AdrWord, 13, Flags))
    {
      WAsmCode[0] = Code + (AdrWord & 0x1fff);
      CodeLen = 1;
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    Boolean OK;

    Word AdrWord = EvalStrIntExpression(&ArgStr[1], UInt16, &OK);
    if (OK)
    {
      CodeLen = 3;
      WAsmCode[0] = 0xb000 + Hi(AdrWord);
      WAsmCode[1] = 0x0103;
      WAsmCode[2] = 0xb700 + Lo(AdrWord);
    }
  }
}

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

  code_equate_type(SegData, UInt8);
}

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

  DecodeDATA(Int16, Int8);
}

static void DecodeZERO(Word Code)
{
  Word Size;
  Boolean ValOK;
  tSymbolFlags Flags;

  UNUSED(Code);

  if (ChkArgCnt(1, 1))
  {
    Size = EvalStrIntExpressionWithFlags(&ArgStr[1], Int16, &ValOK, &Flags);
    if (mFirstPassUnknown(Flags)) WrError(ErrNum_FirstPassCalc);
    if (ValOK && !mFirstPassUnknown(Flags))
    {
      if (SetMaxCodeLen(Size << 1)) WrError(ErrNum_CodeOverflow);
      else
      {
        CodeLen = Size;
        memset(WAsmCode, 0, 2 * Size);
      }
    }
  }
}

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

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

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

static void AddAri(const char *NName, Word NDef, Word NCode)
{
  AddInstTable(InstTable, NName, NCode | (NDef << 15), DecodeAri);
}

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

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

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

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "MOVFP", 0x6000, DecodeMOVFP_MOVPF);
  AddInstTable(InstTable, "MOVPF", 0x4000, DecodeMOVFP_MOVPF);
  AddInstTable(InstTable, "TABLRD", 0xa800, DecodeTABLRD_TABLWT);
  AddInstTable(InstTable, "TABLWT", 0xac00, DecodeTABLRD_TABLWT);
  AddInstTable(InstTable, "TLRD", 0xa000, DecodeTLRD_TLWT);
  AddInstTable(InstTable, "TLWT", 0xa400, DecodeTLRD_TLWT);
  AddInstTable(InstTable, "CALL", 0xe000, DecodeCALL_GOTO);
  AddInstTable(InstTable, "GOTO", 0xc000, DecodeCALL_GOTO);
  AddInstTable(InstTable, "LCALL", 0, DecodeLCALL);
  AddInstTable(InstTable, "SFR", 0, DecodeSFR);
  AddInstTable(InstTable, "RES", 0, DecodeRES);
  AddInstTable(InstTable, "DATA", 0, DecodeDATA_17C4x);
  AddInstTable(InstTable, "ZERO", 0, DecodeZERO);

  AddFixed("RETFIE", 0x0005);
  AddFixed("RETURN", 0x0002);
  AddFixed("CLRWDT", 0x0004);
  AddFixed("NOP"   , 0x0000);
  AddFixed("SLEEP" , 0x0003);

  AddLitt("MOVLB", 0xb800);
  AddLitt("ADDLW", 0xb100);
  AddLitt("ANDLW", 0xb500);
  AddLitt("IORLW", 0xb300);
  AddLitt("MOVLW", 0xb000);
  AddLitt("SUBLW", 0xb200);
  AddLitt("XORLW", 0xb400);
  AddLitt("RETLW", 0xb600);

  AddAri("ADDWF" , 0, 0x0e00);
  AddAri("ADDWFC", 0, 0x1000);
  AddAri("ANDWF" , 0, 0x0a00);
  AddAri("CLRF"  , 1, 0x2800);
  AddAri("COMF"  , 1, 0x1200);
  AddAri("DAW"   , 1, 0x2e00);
  AddAri("DECF"  , 1, 0x0600);
  AddAri("INCF"  , 1, 0x1400);
  AddAri("IORWF" , 0, 0x0800);
  AddAri("NEGW"  , 1, 0x2c00);
  AddAri("RLCF"  , 1, 0x1a00);
  AddAri("RLNCF" , 1, 0x2200);
  AddAri("RRCF"  , 1, 0x1800);
  AddAri("RRNCF" , 1, 0x2000);
  AddAri("SETF"  , 1, 0x2a00);
  AddAri("SUBWF" , 0, 0x0400);
  AddAri("SUBWFB", 0, 0x0200);
  AddAri("SWAPF" , 1, 0x1c00);
  AddAri("XORWF" , 0, 0x0c00);
  AddAri("DECFSZ", 1, 0x1600);
  AddAri("DCFSNZ", 1, 0x2600);
  AddAri("INCFSZ", 1, 0x1e00);
  AddAri("INFSNZ", 1, 0x2400);

  AddBit("BCF"  , 0x8800);
  AddBit("BSF"  , 0x8000);
  AddBit("BTFSC", 0x9800);
  AddBit("BTFSS", 0x9000);
  AddBit("BTG"  , 0x3800);

  AddF("MOVWF" , 0x0100);
  AddF("CPFSEQ", 0x3100);
  AddF("CPFSGT", 0x3200);
  AddF("CPFSLT", 0x3000);
  AddF("TSTFSZ", 0x3300);
}

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

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

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

static Boolean IsDef_17c4x(void)
{
  return Memo("SFR");
}

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

static void SwitchTo_17c4x(void)
{
  const TFamilyDescr *p_descr = FindFamilyByName("17C4x");

  TurnWords = False;
  SetIntConstMode(eIntConstModeMoto);

  PCSymbol = "*";
  HeaderID = p_descr->Id;
  NOPCode = 0x0000;
  DivideChars = ",";
  HasAttrs = False;

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

  MakeCode = MakeCode_17c4x;
  IsDef = IsDef_17c4x;
  SwitchFrom = SwitchFrom_17c4x;
  InitFields();
}

void code17c4x_init(void)
{
  CPU17C42 = AddCPU("17C42", SwitchTo_17c4x);
}
