/* code75xx.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator NEC 75xx                                                    */
/*                                                                           */
/*****************************************************************************/

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

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

#include "code75xx.h"

static CPUVar CPU7566, CPU7508;
static IntType CodeIntType, DataIntType;

/*-------------------------------------------------------------------------*/
/* code generation */

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

static void DecodeFixed(Word Index)
{
  if (ChkArgCnt(0, 0))
    PutCode(Index);
}

static void DecodeImm4(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Value = EvalStrIntExpressionWithResult(&ArgStr[1], Int4, &EvalResult);

    if (EvalResult.OK)
    {
      PutCode(Index | (Value & 15));
      if (Memo("OP"))
        ChkSpace(SegIO, EvalResult.AddrSpaceMask);
    }
  }
}

static void DecodeImm5(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Word Value;

    Value = EvalStrIntExpression(&ArgStr[1], Int5, &OK);
    if (OK)
      PutCode(Index | (Value & 31));
  }
}

static void DecodeImm8(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Word Value;

    Value = EvalStrIntExpression(&ArgStr[1], Int8, &OK);
    if (OK)
    {
      PutCode(Index);
      BAsmCode[CodeLen++] = Value;
    }
  }
}

static void DecodeImm2(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    Word Value;

    Value = EvalStrIntExpression(&ArgStr[1], UInt2, &OK);
    if (OK)
      PutCode(Index | (Value & 3));
  }
}

static void DecodeImm3(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Value = EvalStrIntExpressionWithResult(&ArgStr[1], UInt3, &EvalResult);
    if (EvalResult.OK)
    {
      PutCode(Index | (Value & 7));
      if (Memo("OP"))
        ChkSpace(SegIO, EvalResult.AddrSpaceMask);
    }
  }
}

static void DecodePR(Word Index)
{
  if (!ChkArgCnt(1, 1));
  else if (!as_strcasecmp(ArgStr[1].str.p_str, "HL-"))
    PutCode(Index | 0x10);
  else if (!as_strcasecmp(ArgStr[1].str.p_str, "HL+"))
    PutCode(Index | 0x11);
  else if (!as_strcasecmp(ArgStr[1].str.p_str, "HL"))
    PutCode(Index | 0x12);
  else if ((MomCPU == CPU7508) && (!as_strcasecmp(ArgStr[1].str.p_str, "DL")))
    PutCode(Index | 0x00);
  else if ((MomCPU == CPU7508) && (!as_strcasecmp(ArgStr[1].str.p_str, "DE")))
    PutCode(Index | 0x01);
  else
    WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[1]);
}

static void DecodeDataMem(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Value = EvalStrIntExpressionWithResult(&ArgStr[1], DataIntType, &EvalResult);
    if (EvalResult.OK)
    {
      PutCode(Index);
      BAsmCode[CodeLen++] = Value & SegLimits[SegData];
      ChkSpace(SegData, EvalResult.AddrSpaceMask);
    }
  }
}

static void DecodeAbs(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Address;
    IntType Type = CodeIntType;
    Word SegLimit = SegLimits[SegCode];

    /* CALL can only address first 2K on 7508! */

    if ((Memo("CALL")) && (MomCPU == CPU7508))
    {
      Type = UInt11;
      SegLimit = 0x1fff;
    }

    Address = EvalStrIntExpressionWithResult(&ArgStr[1], Type, &EvalResult);
    if (EvalResult.OK)
    {
      PutCode(Index | (Address & SegLimit));
      ChkSpace(SegCode, EvalResult.AddrSpaceMask);
    }
  }
}

static void DecodeJCP(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Address = EvalStrIntExpressionWithResult(&ArgStr[1], CodeIntType, &EvalResult);

    if (EvalResult.OK && ChkSamePage(Address, EProgCounter() + 1, 6, EvalResult.Flags))
    {
      PutCode(Index | (Address & 0x3f));
      ChkSpace(SegCode, EvalResult.AddrSpaceMask);
    }
  }
}

static void DecodeCAL(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Address = EvalStrIntExpressionWithResult(&ArgStr[1], CodeIntType, &EvalResult);

    if (mFirstPassUnknown(EvalResult.Flags))
      Address = (Address & 0x1c7) | 0x100;
    if (EvalResult.OK)
    {
      if ((Address & 0x338) != 0x100) WrError(ErrNum_OverRange);
      else
        PutCode(Index | ((Address & 0x0c0) >> 3) | (Address & 7));
      ChkSpace(SegCode, EvalResult.AddrSpaceMask);
    }
  }
}

static void DecodeLHLT(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Address = EvalStrIntExpressionWithResult(&ArgStr[1], CodeIntType, &EvalResult);

    if (mFirstPassUnknown(EvalResult.Flags))
      Address = (Address & 0x00cf) | 0x00c0;
    if (EvalResult.OK)
    {
      if ((Address < 0xc0) || (Address > 0xcf)) WrError(ErrNum_OverRange);
      else
        PutCode(Index | (Address & 0xf));
      ChkSpace(SegCode, EvalResult.AddrSpaceMask);
    }
  }
}

static void DecodeCALT(Word Index)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Address = EvalStrIntExpressionWithResult(&ArgStr[1], CodeIntType, &EvalResult);

    if (mFirstPassUnknown(EvalResult.Flags))
      Address = (Address & 0x00ff) | 0x00c0;
    if (EvalResult.OK)
    {
      if ((Address < 0xd0) || (Address > 0xff)) WrError(ErrNum_OverRange);
      else
        PutCode(Index | (Address & 0x3f));
      ChkSpace(SegCode, EvalResult.AddrSpaceMask);
    }
  }
}

static void DecodeLogPort(Word Index)
{
  if (ChkArgCnt(2, 2))
  {
    tEvalResult EvalResult;
    Word Port, Mask;

    Port = EvalStrIntExpressionWithResult(&ArgStr[1], UInt4, &EvalResult);
    if (EvalResult.OK)
    {
      ChkSpace(SegIO, EvalResult.AddrSpaceMask);
      Mask = EvalStrIntExpressionWithResult(&ArgStr[2], UInt4, &EvalResult);
      if (EvalResult.OK)
        PutCode(Index | ((Mask & 15) << 4) | (Port & 15));
    }
  }
}

/*-------------------------------------------------------------------------*/
/* dynamische Codetabellenverwaltung */

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

static void AddImm4(const char *NewName, Word NewCode)
{
  AddInstTable(InstTable, NewName, NewCode, DecodeImm4);
}

static void AddImm2(const char *NewName, Word NewCode)
{
  AddInstTable(InstTable, NewName, NewCode, DecodeImm2);
}

static void AddImm5(const char *NewName, Word NewCode)
{
  AddInstTable(InstTable, NewName, NewCode, DecodeImm5);
}

static void AddImm8(const char *NewName, Word NewCode)
{
  AddInstTable(InstTable, NewName, NewCode, DecodeImm8);
}

static void AddImm3(const char *NewName, Word NewCode)
{
  AddInstTable(InstTable, NewName, NewCode, DecodeImm3);
}

static void AddPR(const char *NewName, Word NewCode)
{
  AddInstTable(InstTable, NewName, NewCode, DecodePR);
}

static void AddDataMem(const char *NewName, Word NewCode)
{
  AddInstTable(InstTable, NewName, NewCode, DecodeDataMem);
}

static void AddAbs(const char *NewName, Word NewCode)
{
  AddInstTable(InstTable, NewName, NewCode, DecodeAbs);
}

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

  add_null_pseudo(InstTable);

  AddFixed("ST"    , 0x57);
  AddFixed("XAL"   , 0x7b);
  AddFixed("ASC"   , 0x7d);
  AddFixed("ACSC"  , 0x7c);
  AddFixed("EXL"   , 0x7e);
  AddFixed("CMA"   , 0x7f);
  AddFixed("RC"    , 0x78);
  AddFixed("SC"    , 0x79);
  AddFixed("ILS"   , 0x59);
  AddFixed("DLS"   , 0x58);
  AddFixed("RT"    , 0x53);
  AddFixed("RTS"   , 0x5b);
  AddFixed("TAMSP" , 0x3f31);
  AddFixed("SKC"   , 0x5a);
  AddFixed("SKAEM" , 0x5f);
  AddFixed("TAMSIO", 0x3f3e);
  AddFixed("TSIOAM", 0x3f3a);
  AddFixed("SIO"   , 0x3f33);
  AddFixed("TIMER" , 0x3f32);
  AddFixed("TCNTAM", 0x3f3b);
  AddFixed("IPL"   , 0x70);
  AddFixed("OPL"   , 0x72);
  AddFixed("HALT"  , 0x3f36);
  AddFixed("STOP"  , 0x3f37);
  AddFixed("NOP"  , 0x00);
  if (MomCPU == CPU7566)
  {
    AddFixed("RPBL"  , 0x5c);
    AddFixed("SPBL"  , 0x5d);
  }
  if (MomCPU == CPU7508)
  {
    AddFixed("LAMTL", 0x3f34);
    AddFixed("TAD", 0x3eaa);
    AddFixed("TAE", 0x3e8a);
    AddFixed("TAH", 0x3eba);
    AddFixed("TAL", 0x3e9a);
    AddFixed("TDA", 0x3eab);
    AddFixed("TEA", 0x3e8b);
    AddFixed("THA", 0x3ebb);
    AddFixed("TLA", 0x3e9b);
    AddFixed("XAD", 0x4a);
    AddFixed("XAE", 0x4b);
    AddFixed("XAH", 0x7a);
    AddFixed("ANL", 0x3fb2);
    AddFixed("ORL", 0x3fb6);
    AddFixed("RAR", 0x3fb3);
    AddFixed("IES", 0x49);
    AddFixed("DES", 0x48);
    AddFixed("RTPSW", 0x43);
    AddFixed("PSHDE", 0x3e8e);
    AddFixed("PSHHL", 0x3e9e);
    AddFixed("POPDE", 0x3e8f);
    AddFixed("POPHL", 0x3e9f);
    AddFixed("TSPAM", 0x3f35);
    AddFixed("TAMMOD", 0x3f3f);
    AddFixed("IP1", 0x71);
    AddFixed("IP54", 0x3f38);
    AddFixed("OP3", 0x73);
    AddFixed("OP54", 0x3f3c);
  }

  AddImm4("LAI", 0x10);
  AddImm4("AISC", 0x00);
  AddImm4("SKAEI", 0x3f60);
  if (MomCPU == CPU7566)
  {
    AddImm4("STII", 0x40);
  }
  if (MomCPU == CPU7508)
  {
    AddImm4("LDI", 0x3e20);
    AddImm4("LEI", 0x3e00);
    AddImm4("LHI", 0x3e30);
    AddImm4("LLI", 0x3e10);
    AddImm4("JAM", 0x3f10);
    AddImm4("SKDEI", 0x3e60);
    AddImm4("SKEEI", 0x3e40);
    AddImm4("SKHEI", 0x3e70);
    AddImm4("SKLEI", 0x3e50);
    AddImm4("OP", 0x3fe0);
  }

  AddImm2("RMB", 0x68);
  AddImm2("SMB", 0x6c);
  AddImm2("SKABT", 0x74);
  AddImm2("SKMBT", 0x64);
  AddImm2("SKMBF", 0x60);
  if (MomCPU == CPU7566)
  {
    AddImm2("SKI", 0x3d40);
    AddImm2("LHI", 0x28);
  }

  if (MomCPU == CPU7508)
  {
    AddImm3("EI", 0x3f90);
    AddImm3("DI", 0x3f80);
    AddImm3("SKI", 0x3f40);
    AddImm3("IP", 0x3fc0);
  }

  if (MomCPU == CPU7566)
  {
    AddImm5("LHLI", 0xc0);
  }

  if (MomCPU == CPU7508)
  {
    AddImm8("LDEI", 0x4f);
    AddImm8("LHLI", 0x4e);
  }

  AddPR("LAM", 0x40);
  AddPR("XAM", 0x44);

  AddDataMem("IDRS", 0x3d);
  AddDataMem("DDRS", 0x3c);
  if (MomCPU == CPU7508)
  {
    AddDataMem("LADR", 0x38);
    AddDataMem("XADR", 0x39);
    AddDataMem("XHDR", 0x3a);
    AddDataMem("XLDR", 0x3b);
  }

  AddAbs("JMP" , 0x2000);
  AddAbs("CALL", 0x3000);
  AddInstTable(InstTable, "JCP", 0x80, DecodeJCP);
  if (MomCPU == CPU7566)
  {
    AddInstTable(InstTable, "CAL", 0xe0, DecodeCAL);
  }
  if (MomCPU == CPU7508)
  {
    AddInstTable(InstTable, "LHLT", 0xc0, DecodeLHLT);
    AddInstTable(InstTable, "CALT", 0xc0, DecodeCALT);
    AddInstTable(InstTable, "ANP", 0x4c00, DecodeLogPort);
    AddInstTable(InstTable, "ORP", 0x4d00, DecodeLogPort);
  }

  AddIntelPseudo(InstTable, eIntPseudoFlag_BigEndian);
}

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

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

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

static Boolean IsDef_75xx(void)
{
  return False;
}

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

static void SwitchTo_75xx(void)
{
  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);

  PCSymbol = "PC"; HeaderID = FindFamilyByName("75xx")->Id; NOPCode = 0x00;
  DivideChars = ","; HasAttrs = False;

  ValidSegs = (1 << SegCode) | (1 << SegData);
  Grans[SegCode] = 1; ListGrans[SegCode] = 1; SegInits[SegCode] = 0;
  Grans[SegData] = 1; ListGrans[SegData] = 1; SegInits[SegData] = 0;
  if (MomCPU == CPU7508)
  {
    ValidSegs |= (1 << SegIO);
    Grans[SegIO] = 1; ListGrans[SegIO] = 1; SegInits[SegIO] = 0; SegLimits[SegIO] = 15;

    SegLimits[SegData] = 0xff;
    DataIntType = UInt8;
    SegLimits[SegCode] = 0xfff;
    CodeIntType = UInt12;
  }
  else
  {
    SegLimits[SegData] = 0x3f;
    DataIntType = UInt6;
    SegLimits[SegCode] = 0x3ff;
    CodeIntType = UInt10;
  }

  MakeCode = MakeCode_75xx; IsDef = IsDef_75xx;
  SwitchFrom = SwitchFrom_75xx; InitFields();
}

void code75xx_init(void)
{
  CPU7566 = AddCPU("7566", SwitchTo_75xx);
  CPU7508 = AddCPU("7508", SwitchTo_75xx);
}
