/* code2650.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* */
/* AS-Portierung */
/* */
/* Codegenerator Signetics 2650 */
/* */
/*****************************************************************************/
#include "stdinc.h"
#include <string.h>
#include <ctype.h>
#include "nls.h"
#include "chunks.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 "intpseudo.h"
#include "errmsg.h"
#include "onoff_common.h"
#include "code2650.h"
#define ADDR_INT UInt15
/*--------------------------------------------------------------------------*/
/* Local Variables */
static CPUVar CPU2650;
/*--------------------------------------------------------------------------*/
/* Expression Parsers */
static Boolean DecodeReg(const char *pAsc, Byte *pRes)
{
Boolean Result;
Result
= ((strlen(pAsc
) == 2) && (as_toupper
(pAsc
[0]) == 'R') && (pAsc
[1] >= '0') && (pAsc
[1] <= '3'));
if (Result)
*pRes = pAsc[1] - '0';
return Result;
}
/*!------------------------------------------------------------------------
* \fn DecodeCondition(const tStrComp *p_arg, Byte *p_ret)
* \brief decode condition code
* \param p_arg source argument
* \param p_ret binary encoded argument
* \return true if success
* ------------------------------------------------------------------------ */
static Boolean DecodeCondition(const tStrComp *p_arg, Byte *p_ret)
{
const char *p_asc = p_arg->str.p_str;
if (!as_strcasecmp(p_asc, "EQ") || !as_strcasecmp(p_asc, "Z"))
{
*p_ret = 0;
return True;
}
else if (!as_strcasecmp(p_asc, "GT") || !as_strcasecmp(p_asc, "P"))
{
*p_ret = 1;
return True;
}
else if (!as_strcasecmp(p_asc, "LT") || !as_strcasecmp(p_asc, "N"))
{
*p_ret = 2;
return True;
}
else if ((!as_strcasecmp(p_asc, "ALWAYS")) || (!as_strcasecmp(p_asc, "UN")))
{
*p_ret = 3;
return True;
}
else
{
Boolean ok;
*p_ret = EvalStrIntExpression(p_arg, UInt2, &ok);
return ok;
}
}
/*!------------------------------------------------------------------------
* \fn page_rel_ok(Word dest, Word src, tSymbolFlags flags, Word *p_dist, Boolean is_branch)
* \brief check whether relative addressing in same 8K page is possible
* \param dest target address
* \param src current (source) address
* \param flags expression evaluation flags
* \param p_dist resulting distance
* \param is_branch code or data access?
* \return True if distance is in range
* ------------------------------------------------------------------------ */
#define PAGE_MASK 0x1fff
static Boolean page_rel_ok(Word dest, Word src, tSymbolFlags flags, Word *p_dist, Boolean is_branch)
{
if (((src & ~PAGE_MASK) != (dest & ~PAGE_MASK)) && !mSymbolQuestionable(flags))
{
WrError(is_branch ? ErrNum_JmpTargOnDiffPage : ErrNum_TargOnDiffPage);
return False;
}
*p_dist = (dest - src) & PAGE_MASK;
if (((*p_dist < 0x1fc0) && (*p_dist > 0x3f)) && !mSymbolQuestionable(flags))
{
WrError(is_branch ? ErrNum_JmpDistTooBig : ErrNum_DistTooBig);
return False;
}
return True;
}
/*!------------------------------------------------------------------------
* \fn Boolean page_abs_ok(Word dest, Word src, tSymbolFlags flags, Word *p_dest, Boolean is_branch)
* \brief check whether absolute address is in same 8K page
* \param dest target address
* \param src current (source) address
* \param flags expression evaluation flags
* \param p_dest resulting address in machine instruction
* \param is_branch code or data access?
* \return True if distance is in range
* ------------------------------------------------------------------------ */
static Boolean page_abs_ok(Word dest, Word src, tSymbolFlags flags, Word *p_dest, Boolean is_branch)
{
if (((src & ~PAGE_MASK) != (dest & ~PAGE_MASK)) && !mSymbolQuestionable(flags))
{
WrError(is_branch ? ErrNum_JmpTargOnDiffPage : ErrNum_TargOnDiffPage);
return False;
}
*p_dest = dest & PAGE_MASK;
return True;
}
/*--------------------------------------------------------------------------*/
/* Instruction Decoders */
static void DecodeFixed(Word Index)
{
if (ChkArgCnt(0, 0))
{
BAsmCode[0] = Index; CodeLen = 1;
}
}
/*!------------------------------------------------------------------------
* \fn DecodeOneReg(Word Index)
* \brief decode instructions taking a single register as argument
* \param Index machine code of instruction when register 0 is used
* ------------------------------------------------------------------------ */
static void DecodeOneReg(Word Index)
{
Byte Reg;
if (!ChkArgCnt(1, 1));
else if (!DecodeReg(ArgStr[1].str.p_str, &Reg)) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
else
{
BAsmCode[0] = Index | Reg;
CodeLen = 1;
}
}
/*!------------------------------------------------------------------------
* \fn DecodeOneReg_NoZero(Word Index)
* \brief decode instructions taking a single register except R0 as argument
* \param Index machine code of instruction register # is added to
* ------------------------------------------------------------------------ */
static void DecodeOneReg_NoZero(Word Index)
{
Byte Reg;
if (!ChkArgCnt(1, 1));
else if (!DecodeReg(ArgStr[1].str.p_str, &Reg)) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
else if (!Reg) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
else
{
BAsmCode[0] = Index | Reg;
CodeLen = 1;
}
}
/*!------------------------------------------------------------------------
* \fn DecodeLODZ(Word Index)
* \brief decode LODZ instruction
* \param Index machine code of instruction when register 0 is used
* ------------------------------------------------------------------------ */
static void DecodeLODZ(Word Index)
{
Byte Reg;
if (!ChkArgCnt(1, 1));
else if (!DecodeReg(ArgStr[1].str.p_str, &Reg)) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
else
{
/* LODZ R0 shall be encoded as IORZ R0 */
BAsmCode[0] = Reg ? (Index | Reg) : 0x60;
CodeLen = 1;
}
}
static void DecodeImm(Word Index)
{
Boolean OK;
if (ChkArgCnt(1, 1))
{
BAsmCode[1] = EvalStrIntExpression(&ArgStr[1], Int8, &OK);
if (OK)
{
BAsmCode[0] = Index; CodeLen = 2;
}
}
}
static void DecodeRegImm(Word Index)
{
Byte Reg;
Boolean OK;
if (!ChkArgCnt(2, 2));
else if (!DecodeReg(ArgStr[1].str.p_str, &Reg)) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
else
{
BAsmCode[1] = EvalStrIntExpression(&ArgStr[2], Int8, &OK);
if (OK)
{
BAsmCode[0] = Index | Reg; CodeLen = 2;
}
}
}
/*!------------------------------------------------------------------------
* \fn void DecodeRegAbs(Word code)
* \brief handle instruction with register & absolute address
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void DecodeRegAbs(Word code)
{
Byte dest_reg;
if (!ChkArgCnt(2, 4));
else if (!DecodeReg(ArgStr[1].str.p_str, &dest_reg)) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
else
{
Boolean ok, ind_flag = *ArgStr[2].str.p_str == '*';
Word abs_val;
tSymbolFlags flags;
Byte index_reg;
abs_val = EvalStrIntExpressionOffsWithFlags(&ArgStr[2], ind_flag, ADDR_INT, &ok, &flags);
if (ok && page_abs_ok(abs_val, EProgCounter() + 3, flags, &abs_val, False))
{
BAsmCode[0] = code;
BAsmCode[1] = Hi(abs_val);
BAsmCode[2] = Lo(abs_val);
if (ind_flag)
BAsmCode[1] |= 0x80;
if (ArgCnt == 2)
{
BAsmCode[0] |= dest_reg;
CodeLen = 3;
}
else
{
if (!DecodeReg(ArgStr[3].str.p_str, &index_reg)) WrStrErrorPos(ErrNum_InvReg, &ArgStr[3]);
else if (dest_reg != 0) WrError(ErrNum_InvAddrMode);
else
{
BAsmCode[0] |= index_reg;
if (ArgCnt == 3)
{
BAsmCode[1] |= 0x60;
CodeLen = 3;
}
else if (!strcmp(ArgStr
[4].
str.
p_str, "-"))
{
BAsmCode[1] |= 0x40;
CodeLen = 3;
}
else if (!strcmp(ArgStr
[4].
str.
p_str, "+"))
{
BAsmCode[1] |= 0x20;
CodeLen = 3;
}
else
WrError(ErrNum_InvAddrMode);
}
}
}
}
}
/*!------------------------------------------------------------------------
* \fn DecodeRegRel(Word code)
* \brief handle instructions with register & relative argument
* \param code instruction machine code
* ------------------------------------------------------------------------ */
static void DecodeRegRel(Word code)
{
Byte reg;
if (!ChkArgCnt(2, 2));
else if (!DecodeReg(ArgStr[1].str.p_str, ®)) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
else
{
Word dest, dist;
tSymbolFlags flags;
Boolean ind_flag, ok;
BAsmCode[0] = code | reg;
ind_flag = *ArgStr[2].str.p_str == '*';
dest = EvalStrIntExpressionOffsWithFlags(&ArgStr[2], ind_flag, ADDR_INT, &ok, &flags);
if (ok && page_rel_ok(dest, EProgCounter() + 2, flags, &dist, False))
{
BAsmCode[1] = dist & 0x7f;
if (ind_flag)
BAsmCode[1] |= 0x80;
CodeLen = 2;
}
}
}
static void DecodeCondAbs(Word Index)
{
Byte Cond;
Word Address;
Boolean OK, IndFlag;
if (ChkArgCnt(2, 2)
&& DecodeCondition(&ArgStr[1], &Cond))
{
IndFlag = *ArgStr[2].str.p_str == '*';
Address = EvalStrIntExpressionOffs(&ArgStr[2], IndFlag, ADDR_INT, &OK);
if (OK)
{
BAsmCode[0] = Index | Cond;
BAsmCode[1] = Hi(Address);
if (IndFlag)
BAsmCode[1] |= 0x80;
BAsmCode[2] = Lo(Address);
CodeLen = 3;
}
}
}
/*!------------------------------------------------------------------------
* \fn DecodeCondRel(Word code)
* \brief decode relative branches
* \param code machine code
* ------------------------------------------------------------------------ */
static void DecodeCondRel(Word code)
{
Byte cond;
if (ChkArgCnt(2, 2)
&& DecodeCondition(&ArgStr[1], &cond))
{
Boolean ind_flag, ok;
tSymbolFlags flags;
Word dist, dest;
BAsmCode[0] = code | cond;
ind_flag = *ArgStr[2].str.p_str == '*';
dest = EvalStrIntExpressionOffsWithFlags(&ArgStr[2], ind_flag, ADDR_INT, &ok, &flags);
if (ok && page_rel_ok(dest, EProgCounter() + 2, flags, &dist, True))
{
BAsmCode[1] = dist & 0x7f;
if (ind_flag)
BAsmCode[1] |= 0x80;
CodeLen = 2;
}
}
}
static void DecodeRegAbs2(Word Index)
{
Byte Reg;
Word AbsVal;
Boolean IndFlag, OK;
if (!ChkArgCnt(2, 2));
else if (!DecodeReg(ArgStr[1].str.p_str, &Reg)) WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
else
{
BAsmCode[0] = Index | Reg;
IndFlag = *ArgStr[2].str.p_str == '*';
AbsVal = EvalStrIntExpressionOffs(&ArgStr[2], IndFlag, ADDR_INT, &OK);
if (OK)
{
BAsmCode[1] = Hi(AbsVal);
if (IndFlag)
BAsmCode[1] |= 0x80;
BAsmCode[2] = Lo(AbsVal);
CodeLen = 3;
}
}
}
static void DecodeBrAbs(Word Index)
{
Byte Reg = 3;
Word AbsVal;
Boolean IndFlag, OK;
if (!ChkArgCnt(1, 2));
else if ((ArgCnt == 2) && (!DecodeReg(ArgStr[2].str.p_str, &Reg))) WrStrErrorPos(ErrNum_InvReg, &ArgStr[2]);
else if (Reg != 3) WrError(ErrNum_InvAddrMode);
else
{
BAsmCode[0] = Index | Reg;
IndFlag = *ArgStr[1].str.p_str == '*';
AbsVal = EvalStrIntExpressionOffs(&ArgStr[1], IndFlag, ADDR_INT, &OK);
if (OK)
{
BAsmCode[1] = Hi(AbsVal);
if (IndFlag)
BAsmCode[1] |= 0x80;
BAsmCode[2] = Lo(AbsVal);
CodeLen = 3;
}
}
}
static void DecodeCond(Word Index)
{
Byte Cond;
if (ChkArgCnt(1, 1)
&& DecodeCondition(&ArgStr[1], &Cond))
{
BAsmCode[0] = Index | Cond;
CodeLen = 1;
}
}
/*!------------------------------------------------------------------------
* \fn DecodeZero(Word code)
* \brief decode zero page branch instructions
* \param code machine code
* ------------------------------------------------------------------------ */
static void DecodeZero(Word code)
{
if (ChkArgCnt(1, 1))
{
Boolean ind_flag, ok;
tSymbolFlags flags;
Word dest, dist;
BAsmCode[0] = code;
ind_flag = *ArgStr[1].str.p_str == '*';
dest = EvalStrIntExpressionOffsWithFlags(&ArgStr[1], ind_flag, ADDR_INT, &ok, &flags);
if (ok && page_rel_ok(dest, 0x0000, flags, &dist, True))
{
BAsmCode[1] = dist & 0x7f;
if (ind_flag)
BAsmCode[1] |= 0x80;
CodeLen = 2;
}
}
}
/*--------------------------------------------------------------------------*/
/* Code Table Handling */
static void AddFixed(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeFixed);
}
static void AddOneReg(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeOneReg);
}
static void AddImm(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeImm);
}
static void AddRegImm(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeRegImm);
}
static void AddRegAbs(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeRegAbs);
}
static void AddRegRel(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeRegRel);
}
static void AddCondAbs(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeCondAbs);
}
static void AddCondRel(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeCondRel);
}
static void AddRegAbs2(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeRegAbs2);
}
static void AddBrAbs(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeBrAbs);
}
static void AddCond(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeCond);
}
static void AddZero(const char *pName, Word Code)
{
AddInstTable(InstTable, pName, Code, DecodeZero);
}
static void InitFields(void)
{
InstTable = CreateInstTable(203);
AddFixed("NOP", 0xc0);
AddFixed("HALT", 0x40);
AddFixed("LPSL", 0x93);
AddFixed("LPSU", 0x92);
AddFixed("SPSL", 0x13);
AddFixed("SPSU", 0x12);
AddOneReg("ADDZ", 0x80);
/* ANDZ R0 is not allowed and decodes as HALT */
AddInstTable(InstTable, "ANDZ", 0x40, DecodeOneReg_NoZero);
AddOneReg("COMZ", 0xe0);
AddOneReg("DAR", 0x94);
AddOneReg("EORZ", 0x20);
AddOneReg("IORZ", 0x60);
AddInstTable(InstTable, "LODZ", 0x00, DecodeLODZ);
AddOneReg("REDC", 0x30);
AddOneReg("REDD", 0x70);
AddOneReg("RRL", 0xd0);
AddOneReg("RRR", 0x50);
/* STRZ R0 is not allowed and decodes as NOP */
AddInstTable(InstTable, "STRZ", 0xc0, DecodeOneReg_NoZero);
AddOneReg("SUBZ", 0xa0);
AddOneReg("WRTC", 0xb0);
AddOneReg("WRTD", 0xf0);
AddImm("CPSL", 0x75);
AddImm("CPSU", 0x74);
AddImm("PPSL", 0x77);
AddImm("PPSU", 0x76);
AddImm("TPSL", 0xb5);
AddImm("TPSU", 0xb4);
AddRegImm("ADDI", 0x84);
AddRegImm("ANDI", 0x44);
AddRegImm("COMI", 0xe4);
AddRegImm("EORI", 0x24);
AddRegImm("IORI", 0x64);
AddRegImm("LODI", 0x04);
AddRegImm("REDE", 0x54);
AddRegImm("SUBI", 0xa4);
AddRegImm("TMI", 0xf4);
AddRegImm("WRTE", 0xd4);
AddRegAbs("ADDA", 0x8c);
AddRegAbs("ANDA", 0x4c);
AddRegAbs("COMA", 0xec);
AddRegAbs("EORA", 0x2c);
AddRegAbs("IORA", 0x6c);
AddRegAbs("LODA", 0x0c);
AddRegAbs("STRA", 0xcc);
AddRegAbs("SUBA", 0xac);
AddRegRel("ADDR", 0x88);
AddRegRel("ANDR", 0x48);
AddRegRel("BDRR", 0xf8);
AddRegRel("BIRR", 0xd8);
AddRegRel("BRNR", 0x58);
AddRegRel("BSNR", 0x78);
AddRegRel("COMR", 0xe8);
AddRegRel("EORR", 0x28);
AddRegRel("IORR", 0x68);
AddRegRel("LODR", 0x08);
AddRegRel("STRR", 0xc8);
AddRegRel("SUBR", 0xa8);
AddCondAbs("BCFA", 0x9c);
AddCondAbs("BCTA", 0x1c);
AddCondAbs("BSFA", 0xbc);
AddCondAbs("BSTA", 0x3c);
AddCondRel("BCFR", 0x98);
AddCondRel("BCTR", 0x18);
AddCondRel("BSFR", 0xb8);
AddCondRel("BSTR", 0x38);
AddRegAbs2("BDRA", 0xfc);
AddRegAbs2("BIRA", 0xdc);
AddRegAbs2("BRNA", 0x5c);
AddRegAbs2("BSNA", 0x7c);
AddBrAbs("BSXA", 0xbf);
AddBrAbs("BXA", 0x9f);
AddCond("RETC", 0x14);
AddCond("RETE", 0x34);
AddZero("ZBRR", 0x9b);
AddZero("ZBSR", 0xbb);
AddInstTable(InstTable, "RES", 1, DecodeIntelDS);
AddInstTable(InstTable, "ACON", eIntPseudoFlag_BigEndian | eIntPseudoFlag_AllowInt | eIntPseudoFlag_AllowString, DecodeIntelDW);
}
static void DeinitFields(void)
{
DestroyInstTable(InstTable);
}
/*--------------------------------------------------------------------------*/
/* Callbacks */
static void MakeCode_2650(void)
{
char *pPos;
CodeLen = 0;
DontPrint = False;
/* Nullanweisung */
if ((*OpPart.str.p_str == '\0') && (ArgCnt == 0))
return;
/* Pseudoanweisungen */
if (DecodeIntelPseudo(TargetBigEndian)) return;
/* try to split off first (register) operand from instruction */
pPos
= strchr(OpPart.
str.
p_str, ',');
if (pPos)
{
InsertArg
(1, strlen(OpPart.
str.
p_str));
StrCompSplitRight(&OpPart, &ArgStr[1], pPos);
}
/* alles aus der Tabelle */
if (!LookupInstTable(InstTable, OpPart.str.p_str))
WrStrErrorPos(ErrNum_UnknownInstruction, &OpPart);
}
static Boolean IsDef_2650(void)
{
return FALSE;
}
static void SwitchFrom_2650(void)
{
DeinitFields();
}
static void SwitchTo_2650(void)
{
const TFamilyDescr *pDescr;
TurnWords = False;
SetIntConstMode(eIntConstModeMoto);
pDescr = FindFamilyByName("2650");
PCSymbol = "$"; HeaderID = pDescr->Id; NOPCode = 0xc0;
DivideChars = ","; HasAttrs = False;
ValidSegs = (1 << SegCode);
Grans[SegCode] = 1; ListGrans[SegCode] = 1; SegInits[SegCode] = 0;
SegLimits[SegCode] = IntTypeDefs[ADDR_INT].Max;
MakeCode = MakeCode_2650; IsDef = IsDef_2650;
SwitchFrom = SwitchFrom_2650; InitFields();
onoff_bigendian_add();
}
/*--------------------------------------------------------------------------*/
/* Initialisierung */
void code2650_init(void)
{
CPU2650 = AddCPU("2650", SwitchTo_2650);
}