/* codetms1.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator TMS1000-Familie                                             */
/*                                                                           */
/*****************************************************************************/

#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 "codevars.h"
#include "headids.h"
#include "codepseudo.h"
#include "intpseudo.h"
#include "errmsg.h"

#include "codetms1.h"

static CPUVar CPU1000, CPU1100, CPU1200, CPU1300;
static IntType CodeAdrIntType, DataAdrIntType;

/* 2/3/4-bit-operand in instruction is bit-mirrored */

static const Byte BitMirr[16] =
{
   0,  8,  4, 12,  2, 10,  6, 14,
   1,  9,  5, 13,  3, 11,  7, 15
};

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

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

static void DecodeConst4(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    BAsmCode[0] = EvalStrIntExpression(&ArgStr[1], Int4, &OK);
    if (OK)
    {
      BAsmCode[0] = BitMirr[BAsmCode[0] & 0x0f] | (Code & 0xf0);
      CodeLen = 1;
    }
  }
}

static void DecodeConst3(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    BAsmCode[0] = EvalStrIntExpression(&ArgStr[1], UInt3, &OK);
    if (OK)
    {
      BAsmCode[0] = (BitMirr[BAsmCode[0] & 0x07] >> 1) | (Code & 0xf8);
      CodeLen = 1;
    }
  }
}

static void DecodeConst2(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    Boolean OK;
    BAsmCode[0] = EvalStrIntExpression(&ArgStr[1], UInt2, &OK);
    if (OK)
    {
      BAsmCode[0] = (BitMirr[BAsmCode[0] & 0x03] >> 2) | (Code & 0xfc);
      CodeLen = 1;
    }
  }
}

static void DecodeJmp(Word Code)
{
  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Addr = EvalStrIntExpressionWithResult(&ArgStr[1], CodeAdrIntType, &EvalResult);
    if (EvalResult.OK)
    {
      ChkSpace(SegCode, EvalResult.AddrSpaceMask);
      CodeLen = 1;
      BAsmCode[0] = (Code & 0xc0) | (Addr & 0x3f);
    }
  }
}

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

  if (ChkArgCnt(1, 1))
  {
    tEvalResult EvalResult;
    Word Addr = EvalStrIntExpressionWithResult(&ArgStr[1], CodeAdrIntType, &EvalResult);
    if (EvalResult.OK)
    {
      ChkSpace(SegCode, EvalResult.AddrSpaceMask);
      BAsmCode[0] = 0x10 | ((Addr >> 6) & 15); /* LDP... */
      BAsmCode[1] = (Code & 0xc0) | (Addr & 0x3f);
      CodeLen = 2;
    }
  }
}

/*---------------------------------------------------------------------------*/
/* Dynamic Instruction Table Handling */

static void InitFields(void)
{
  Boolean Is1100 = (MomCPU == CPU1100) || (MomCPU == CPU1300);

  InstTable = CreateInstTable(107);

  add_null_pseudo(InstTable);

  AddInstTable(InstTable, "TAY", Is1100 ? 0x20 : 0x24, DecodeFixed);
  AddInstTable(InstTable, "TYA", 0x23, DecodeFixed);
  AddInstTable(InstTable, "CLA", Is1100 ? 0x7f : 0x2f, DecodeFixed);

  AddInstTable(InstTable, "TAM", Is1100 ? 0x27 : 0x03, DecodeFixed);
  if (Is1100)
  {
    AddInstTable(InstTable, "TAMIYC", 0x25, DecodeFixed);
    AddInstTable(InstTable, "TAMDYN", 0x24, DecodeFixed);
  }
  else
  {
    AddInstTable(InstTable, "TAMIY", 0x20, DecodeFixed);
  }
  AddInstTable(InstTable, "TAMZA", Is1100 ? 0x26 : 0x04, DecodeFixed);
  AddInstTable(InstTable, "TMY", 0x22, DecodeFixed);
  AddInstTable(InstTable, "TMA", 0x21, DecodeFixed);
  AddInstTable(InstTable, "XMA", Is1100 ? 0x03 : 0x2e, DecodeFixed);

  AddInstTable(InstTable, "AMAAC", Is1100 ? 0x06 : 0x25, DecodeFixed);
  AddInstTable(InstTable, "SAMAN", Is1100 ? 0x3c : 0x27, DecodeFixed);
  AddInstTable(InstTable, "IMAC", Is1100 ? 0x3e : 0x28, DecodeFixed);
  AddInstTable(InstTable, "DMAN", Is1100 ? 0x07 : 0x2a, DecodeFixed);
  if (Is1100)
    AddInstTable(InstTable, "IAC", 0x70, DecodeFixed);
  else
    AddInstTable(InstTable, "IA", 0x0e, DecodeFixed);
  AddInstTable(InstTable, "IYC", Is1100 ? 0x05 : 0x2b, DecodeFixed);
  AddInstTable(InstTable, "DAN", Is1100 ? 0x77 : 0x07, DecodeFixed);
  AddInstTable(InstTable, "DYN", Is1100 ? 0x04 : 0x2c, DecodeFixed);
  AddInstTable(InstTable, "CPAIZ", Is1100 ? 0x3d : 0x2d, DecodeFixed);
  AddInstTable(InstTable, "A6AAC", Is1100 ? 0x7a : 0x06, DecodeFixed);
  AddInstTable(InstTable, "A8AAC", Is1100 ? 0x7e : 0x01, DecodeFixed);
  AddInstTable(InstTable, "A10AAC", Is1100 ? 0x79 : 0x05, DecodeFixed);
  if (Is1100)
  {
    AddInstTable(InstTable, "A2AAC", 0x78, DecodeFixed);
    AddInstTable(InstTable, "A3AAC", 0x74, DecodeFixed);
    AddInstTable(InstTable, "A4AAC", 0x7c, DecodeFixed);
    AddInstTable(InstTable, "A5AAC", 0x72, DecodeFixed);
    AddInstTable(InstTable, "A7AAC", 0x76, DecodeFixed);
    AddInstTable(InstTable, "A9AAC", 0x71, DecodeFixed);
    AddInstTable(InstTable, "A11AAC", 0x75, DecodeFixed);
    AddInstTable(InstTable, "A12AAC", 0x7d, DecodeFixed);
    AddInstTable(InstTable, "A13AAC", 0x73, DecodeFixed);
    AddInstTable(InstTable, "A14AAC", 0x7b, DecodeFixed);
  }

  AddInstTable(InstTable, "ALEM", Is1100 ? 0x01: 0x29, DecodeFixed);
  if (!Is1100)
  {
    AddInstTable(InstTable, "ALEC", 0x70, DecodeConst4);
  }

  if (Is1100)
  {
    AddInstTable(InstTable, "MNEA", 0x00, DecodeFixed);
  }
  AddInstTable(InstTable, "MNEZ", Is1100 ? 0x3f : 0x26, DecodeFixed);
  AddInstTable(InstTable, "YNEA", Is1100 ? 0x02 : 0x02, DecodeFixed);
  AddInstTable(InstTable, "YNEC", 0x50, DecodeConst4);

  AddInstTable(InstTable, "SBIT", 0x30, DecodeConst2);
  AddInstTable(InstTable, "RBIT", 0x34, DecodeConst2);
  AddInstTable(InstTable, "TBIT1", 0x38, DecodeConst2);

  AddInstTable(InstTable, "TCY", 0x40, DecodeConst4);
  AddInstTable(InstTable, "TCMIY", 0x60, DecodeConst4);

  AddInstTable(InstTable, "KNEZ", Is1100 ? 0x0e : 0x09, DecodeFixed);
  AddInstTable(InstTable, "TKA", 0x08, DecodeFixed);

  AddInstTable(InstTable, "SETR", 0x0d, DecodeFixed);
  AddInstTable(InstTable, "RSTR", 0x0c, DecodeFixed);
  AddInstTable(InstTable, "TDO", 0x0a, DecodeFixed);
  if (!Is1100)
  {
    AddInstTable(InstTable, "CLO", 0x0b, DecodeFixed);
  }

  if (Is1100)
  {
    AddInstTable(InstTable, "LDX", 0x28, DecodeConst3);
  }
  else
  {
    AddInstTable(InstTable, "LDX", 0x3c, DecodeConst2);
  }
  AddInstTable(InstTable, "COMX", Is1100 ? 0x09 : 0x00, DecodeFixed);

  AddInstTable(InstTable, "BR", 0x80, DecodeJmp);
  AddInstTable(InstTable, "CALL", 0xc0, DecodeJmp);
  AddInstTable(InstTable, "BL", 0x80, DecodeJmpL);
  AddInstTable(InstTable, "CALLL", 0xc0, DecodeJmpL);
  AddInstTable(InstTable, "RETN", 0x0f, DecodeFixed);
  AddInstTable(InstTable, "LDP", 0x10, DecodeConst4);
  if (Is1100)
  {
    AddInstTable(InstTable, "COMC", 0x0b, DecodeFixed);
  }
  AddIntelPseudo(InstTable, eIntPseudoFlag_BigEndian);
}

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

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

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

static Boolean IsDef_TMS1(void)
{
  return False;
}

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

static void SwitchTo_TMS1(void)
{
  const TFamilyDescr *pFoundDescr = FindFamilyByName("TMS1000");

  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);

  PCSymbol = "$";
  HeaderID = pFoundDescr->Id;
  NOPCode = 0x100;
  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 == CPU1000) || (MomCPU == CPU1200))
  {
    CodeAdrIntType = UInt10;
    DataAdrIntType = UInt6;
  }
  else if ((MomCPU == CPU1100) || (MomCPU == CPU1300))
  {
    CodeAdrIntType = UInt11;
    DataAdrIntType = UInt7;
  }
  SegLimits[SegCode] = IntTypeDefs[CodeAdrIntType].Max;
  SegLimits[SegData] = IntTypeDefs[DataAdrIntType].Max;

  MakeCode = MakeCode_TMS1;
  IsDef = IsDef_TMS1;
  SwitchFrom = SwitchFrom_TMS1;

  InitFields();
}

void codetms1_init(void)
{
  CPU1000 = AddCPU("TMS1000", SwitchTo_TMS1);
  CPU1100 = AddCPU("TMS1100", SwitchTo_TMS1);
  CPU1200 = AddCPU("TMS1200", SwitchTo_TMS1);
  CPU1300 = AddCPU("TMS1300", SwitchTo_TMS1);
}
