/* codenano.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator HP Nanoprocessor                                            */
/*                                                                           */
/*****************************************************************************/

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

#include "nls.h"
#include "strutil.h"
#include "chunks.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmitree.h"
#include "codevars.h"
#include "codepseudo.h"
#include "headids.h"
#include "errmsg.h"
#include "onoff_common.h"

#include "codenano.h"

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

static CPUVar CPUNANO;

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

static void DecodeBit(Word Index)
{
	if (ChkArgCnt(1,1))
	{
		Word val;
		Boolean OK;

		val = EvalStrIntExpression(&ArgStr[1], UInt3, &OK);
		if (!OK) return;

		BAsmCode[0] = Index | val;
		CodeLen = 1;
	}
}

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

static void DecodeImm(Word Index)
{
	if (ChkArgCnt(1,1))
	{
		Word val;
		Boolean OK;

		val = EvalStrIntExpression(&ArgStr[1], UInt8, &OK);
		if (!OK) return;

		BAsmCode[0] = Index;
		BAsmCode[1] = val;
		CodeLen = 2;
	}
}

static void DecodeReg(Word Index)
{
	if (ChkArgCnt(1,1))
	{
		Word reg;
		Boolean OK;

		reg = EvalStrIntExpression(&ArgStr[1], UInt4, &OK);
		if (!OK) return;

		BAsmCode[0] = Index | reg;
		CodeLen = 1;
	}
}

static void DecodeRegImm(Word Index)
{
	if (ChkArgCnt(2,2))
	{
		Word reg;
		Word val;
		Boolean OK;

		reg = EvalStrIntExpression(&ArgStr[1], UInt4, &OK);
		if (!OK) return;

		val = EvalStrIntExpression(&ArgStr[2], UInt8, &OK);
		if (!OK) return;

		BAsmCode[0] = Index | reg;
		BAsmCode[1] = val;
		CodeLen = 2;
	}
}

static void DecodeDev(Word Index)
{
	if (ChkArgCnt(1,1))
	{
		Word dev;
		Boolean OK;

		dev = EvalStrIntExpression(&ArgStr[1], UInt4, &OK);
		if (!OK) return;
		if (Index == 0x50 && dev == 15)	/* OTA 15 not allowed */
		{
			WrError(ErrNum_OverRange);
			return;
		}

		BAsmCode[0] = Index | dev;
		CodeLen = 1;
	}
}

static void DecodeDevImm(Word Index)
{
	if (ChkArgCnt(2,2))
	{
		Word dev;
		Word val;
		Boolean OK;

		dev = EvalStrIntExpression(&ArgStr[1], UInt4, &OK);
		if (!OK) return;

		val = EvalStrIntExpression(&ArgStr[2], UInt8, &OK);
		if (!OK) return;

		if (dev == 15)	/* OTR 15,xx not allowed */
		{
			WrError(ErrNum_OverRange);
			return;
		}

		BAsmCode[0] = Index | dev;
		BAsmCode[1] = val;
		CodeLen = 2;
	}
}

static void DecodeDirect(Word Index)
{
	if (ChkArgCnt(1,1))
	{
		Word val;
		Boolean OK;

		val = EvalStrIntExpression(&ArgStr[1], UInt3, &OK);
		if (!OK) return;
		if (val == 7)
		{
			WrError(ErrNum_OverRange);
			return;
		}

		BAsmCode[0] = Index | val;
		CodeLen = 1;
	}
}

static void DecodeAddress(Word Index)
{
	if (ChkArgCnt(1,1))
	{
		Word val;
		Boolean OK;

		val = EvalStrIntExpression(&ArgStr[1], UInt11, &OK);
		if (!OK) return;

		BAsmCode[0] = Index | (val >> 8);
		BAsmCode[1] = val & 0xff;
		CodeLen = 2;
	}
}


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

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

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

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

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

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

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

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

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

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

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

  add_null_pseudo(InstTable);

	/* A. Accumulator Group */
	AddBit("SBS", 0x10);
	AddBit("SBZ", 0x30);
	AddBit("SBN", 0x20);
	AddBit("CBN", 0xA0);
	AddFixed("INB", 0x00);
	AddFixed("IND", 0x02);
	AddFixed("DEB", 0x01);
	AddFixed("DED", 0x03);
	AddFixed("CLA", 0x04);
	AddFixed("CMA", 0x05);
	AddFixed("LSA", 0x07);
	AddFixed("RSA", 0x06);
	AddFixed("SES", 0x1f);
	AddFixed("SEZ", 0x3f);
	AddImm("LDR", 0xcf);

	/* B. Register Transfer Group */
	AddReg("LDA", 0x60);
	AddReg("STA", 0x70);
	AddReg("LDI", 0xe0);
	AddReg("STI", 0xf0);
	AddRegImm("STR", 0xd0);

	/* C. Extend Register Group */
	AddFixed("STE", 0xb4);
	AddFixed("CLE", 0xb5);

	/* D. Interrupt Group */
	AddFixed("DSI", 0xaf);
	AddFixed("ENI", 0x2f);

	/* E. Comparator Group */
	AddFixed("SLT", 0x09);
	AddFixed("SEQ", 0x0a);
	AddFixed("SAZ", 0x0b);
	AddFixed("SLE", 0x0c);
	AddFixed("SGE", 0x0d);
	AddFixed("SNE", 0x0e);
	AddFixed("SAN", 0x0f);
	AddFixed("SGT", 0x08);

	/* F. Input/Output Group */
	AddDev("INA", 0x40);
	AddDev("OTA", 0x50);
	AddDevImm("OTR", 0xc0);
	AddDirect("STC", 0x28);
	AddDirect("CLC", 0xa8);
	AddDirect("SFS", 0x18);
	AddDirect("SFZ", 0x38);
	AddFixed("RTI", 0x90);
	AddFixed("RTE", 0xb1);
	AddFixed("NOP", 0x5f);
	AddReg("JAI", 0x90);
	AddReg("JAS", 0x98);

	/* G. Program Control Group */
	AddAddress("JMP", 0x80);
	AddAddress("JSB", 0x88);
	AddFixed("RTS", 0xb8);
	AddFixed("RSE", 0xb9);
}

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

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

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

static Boolean IsDef_NANO(void)
{
	return False;
}

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

static void SwitchTo_NANO(void)
{
	const TFamilyDescr *pDescr;

	TurnWords = False;
	SetIntConstMode(eIntConstModeC);

	pDescr = FindFamilyByName("NANO");
	PCSymbol = "*";
	HeaderID = pDescr->Id;
	NOPCode = 0x5f;
	DivideChars = ",";
	HasAttrs = False;

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

	MakeCode = MakeCode_NANO;
	IsDef = IsDef_NANO;
	SwitchFrom = SwitchFrom_NANO;
	InitFields();
}

void codenano_init(void)
{
	CPUNANO = AddCPU("NANO", SwitchTo_NANO);

	AddCopyright("HP Nanoprocessor Generator (C) 2022 Haruo Asano");
}
