/* codemic8.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* */
/* AS-Portierung */
/* */
/* Codegenerator LatticeMico8 */
/* */
/*****************************************************************************/
#include "stdinc.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "nls.h"
#include "strutil.h"
#include "bpemu.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmitree.h"
#include "asmallg.h"
#include "intpseudo.h"
#include "codevars.h"
#include "headids.h"
#include "errmsg.h"
#include "codepseudo.h"
#include "codemic8.h"
/* define as needed by address space */
#define CodeAddrInt UInt12
#define DataAddrInt UInt5
typedef struct
{
LongWord Code;
} FixedOrder;
typedef struct
{
LongWord Code;
Boolean MayImm;
} ALUOrder;
typedef struct
{
LongWord Code;
Byte Space;
} MemOrder;
static FixedOrder *FixedOrders, *ShortBranchOrders, *RegOrders, *LongBranchOrders;
static MemOrder *MemOrders;
static ALUOrder *ALUOrders;
static CPUVar CPUMico8_05, CPUMico8_V3, CPUMico8_V31;
/*--------------------------------------------------------------------------
* Address Expression Parsing
*--------------------------------------------------------------------------*/
/*!------------------------------------------------------------------------
* \fn IsWRegCore(const char *pArg, LongWord *pValue)
* \brief check whether argument is a CPU register
* \param pArg argument to check
* \param pValue register number if it is a register
* \return True if it is a register
* ------------------------------------------------------------------------ */
static Boolean IsWRegCore(const char *pArg, LongWord *pValue)
{
Boolean OK;
if ((strlen(pArg
) < 2) || (as_toupper
(*pArg
) != 'R'))
return False;
*pValue = ConstLongInt(pArg + 1, &OK, 10);
if (!OK)
return False;
return (*pValue < 32);
}
/*!------------------------------------------------------------------------
* \fn DissectReg_Mico8(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
* \brief dissect register symbols - MICO8 variant
* \param pDest destination buffer
* \param DestSize destination buffer size
* \param Value numeric register value
* \param InpSize register size
* ------------------------------------------------------------------------ */
static void DissectReg_Mico8(char *pDest, size_t DestSize, tRegInt Value, tSymbolSize InpSize)
{
switch (InpSize)
{
case eSymbolSize8Bit:
as_snprintf(pDest, DestSize, "R%u", (unsigned)Value);
break;
default:
as_snprintf(pDest, DestSize, "%d-%u", (int)InpSize, (unsigned)Value);
}
}
/*!------------------------------------------------------------------------
* \fn IsWReg(const tStrComp *pArg, LongWord *pValue, Boolean MustBeReg)
* \brief check whether argument is a CPU register or register alias
* \param pArg argument to check
* \param pValue register number if it is a register
* \param MustBeReg expecting register as arg?
* \return register parse result
* ------------------------------------------------------------------------ */
static tRegEvalResult IsWReg(const tStrComp *pArg, LongWord *pValue, Boolean MustBeReg)
{
tRegDescr RegDescr;
tEvalResult EvalResult;
tRegEvalResult RegEvalResult;
if (IsWRegCore(pArg->str.p_str, pValue))
return eIsReg;
RegEvalResult = EvalStrRegExpressionAsOperand(pArg, &RegDescr, &EvalResult, eSymbolSize8Bit, MustBeReg);
*pValue = RegDescr.Reg;
return RegEvalResult;
}
/*--------------------------------------------------------------------------
* Code Handlers
*--------------------------------------------------------------------------*/
static void DecodePort(Word Index)
{
UNUSED(Index);
CodeEquate(SegIO, 0, SegLimits[SegIO]);
}
static void DecodeFixed(Word Index)
{
FixedOrder *pOrder = FixedOrders + Index;
if (ChkArgCnt(0, 0))
{
DAsmCode[0] = pOrder->Code;
CodeLen = 1;
}
}
static void DecodeALU(Word Index)
{
ALUOrder *pOrder = ALUOrders + Index;
LongWord Src, DReg;
if (ChkArgCnt(2, 2)
&& IsWReg(&ArgStr[1], &DReg, True))
switch (IsWReg(&ArgStr[2], &Src, True))
{
case eIsReg:
DAsmCode[0] = pOrder->Code | (DReg << 8) | (Src << 3);
CodeLen = 1;
break;
case eIsNoReg:
if (!pOrder->MayImm) WrStrErrorPos(ErrNum_InvAddrMode, &ArgStr[2]);
else
{
Boolean OK;
Src = EvalStrIntExpression(&ArgStr[2], Int8, &OK);
if (OK)
{
DAsmCode[0] = pOrder->Code | (1 << 13) | (DReg << 8) | (Src & 0xff);
CodeLen = 1;
}
}
break;
default:
break;
}
}
static void DecodeALUI(Word Index)
{
ALUOrder *pOrder = ALUOrders + Index;
LongWord Src, DReg;
Boolean OK;
if (ChkArgCnt(2, 2)
&& IsWReg(&ArgStr[1], &DReg, True))
{
Src = EvalStrIntExpression(&ArgStr[2], Int8, &OK);
if (OK)
{
DAsmCode[0] = pOrder->Code | (1 << 13) | (DReg << 8) | (Src & 0xff);
CodeLen = 1;
}
}
}
static void DecodeShortBranch(Word Index)
{
FixedOrder *pOrder = ShortBranchOrders + Index;
LongInt Dest;
Boolean OK;
tSymbolFlags Flags;
if (ChkArgCnt(1, 1))
{
Dest = EvalStrIntExpressionWithFlags(&ArgStr[1], CodeAddrInt, &OK, &Flags);
if (OK)
{
Dest -= EProgCounter();
if (((Dest < -512) || (Dest > 511)) && !mSymbolQuestionable(Flags)) WrError(ErrNum_JmpDistTooBig);
else
{
DAsmCode[0] = pOrder->Code | (Dest & 0x3ff);
CodeLen = 1;
}
}
}
}
static void DecodeLongBranch(Word Index)
{
FixedOrder *pOrder = LongBranchOrders + Index;
LongInt Dest;
Boolean OK;
tSymbolFlags Flags;
if (ChkArgCnt(1, 1))
{
Dest = EvalStrIntExpressionWithFlags(&ArgStr[1], CodeAddrInt, &OK, &Flags);
if (OK)
{
Dest -= EProgCounter();
if (((Dest < -2048) || (Dest > 2047)) && !mSymbolQuestionable(Flags)) WrError(ErrNum_JmpDistTooBig);
else
{
DAsmCode[0] = pOrder->Code | (Dest & 0xfff);
CodeLen = 1;
}
}
}
}
static void DecodeMem(Word Index)
{
MemOrder *pOrder = MemOrders + Index;
LongWord DReg, Src;
if (ChkArgCnt(2, 2)
&& IsWReg(&ArgStr[1], &DReg, True))
switch (IsWReg(&ArgStr[2], &Src, False))
{
case eIsReg:
DAsmCode[0] = pOrder->Code | (DReg << 8) | ((Src & 0x1f) << 3) | 2;
CodeLen = 1;
break;
case eIsNoReg:
{
tEvalResult EvalResult;
Src = EvalStrIntExpressionWithResult(&ArgStr[2], DataAddrInt, &EvalResult);
if (EvalResult.OK)
{
ChkSpace(pOrder->Space, EvalResult.AddrSpaceMask);
DAsmCode[0] = pOrder->Code | (DReg << 8) | ((Src & 0x1f) << 3);
CodeLen = 1;
}
break;
}
default:
break;
}
}
static void DecodeMemI(Word Index)
{
MemOrder *pOrder = MemOrders + Index;
LongWord DReg, SReg;
if (ChkArgCnt(2, 2)
&& IsWReg(&ArgStr[1], &DReg, True)
&& IsWReg(&ArgStr[2], &SReg, True))
{
DAsmCode[0] = pOrder->Code | (DReg << 8) | (SReg << 3) | 2;
CodeLen = 1;
}
}
static void DecodeReg(Word Index)
{
FixedOrder *pOrder = RegOrders + Index;
LongWord Reg = 0;
if (!ChkArgCnt(1, 1));
else if (IsWReg(&ArgStr[1], &Reg, True))
{
DAsmCode[0] = pOrder->Code | (Reg << 8);
CodeLen = 1;
}
}
/*--------------------------------------------------------------------------
* Instruction Table Handling
*--------------------------------------------------------------------------*/
static void AddFixed(const char *NName, LongWord NCode)
{
order_array_rsv_end(FixedOrders, FixedOrder);
FixedOrders[InstrZ].Code = NCode;
AddInstTable(InstTable, NName, InstrZ++, DecodeFixed);
}
static void AddALU(const char *NName, const char *NImmName, LongWord NCode)
{
order_array_rsv_end(ALUOrders, ALUOrder);
ALUOrders[InstrZ].Code = NCode;
AddInstTable(InstTable, NName, InstrZ, DecodeALU);
ALUOrders[InstrZ].MayImm = NImmName != NULL;
if (ALUOrders[InstrZ].MayImm)
AddInstTable(InstTable, NImmName, InstrZ, DecodeALUI);
InstrZ++;
}
static void AddShortBranch(const char *NName, LongWord NCode)
{
order_array_rsv_end(ShortBranchOrders, FixedOrder);
ShortBranchOrders[InstrZ].Code = NCode;
AddInstTable(InstTable, NName, InstrZ++, DecodeShortBranch);
}
static void AddLongBranch(const char *NName, LongWord NCode)
{
order_array_rsv_end(LongBranchOrders, FixedOrder);
LongBranchOrders[InstrZ].Code = NCode;
AddInstTable(InstTable, NName, InstrZ++, DecodeLongBranch);
}
static void AddMem(const char *NName, const char *NImmName, LongWord NCode, Byte NSpace)
{
order_array_rsv_end(MemOrders, MemOrder);
MemOrders[InstrZ].Code = NCode;
MemOrders[InstrZ].Space = NSpace;
AddInstTable(InstTable, NName, InstrZ, DecodeMem);
AddInstTable(InstTable, NImmName, InstrZ, DecodeMemI);
InstrZ++;
}
static void AddReg(const char *NName, LongWord NCode)
{
order_array_rsv_end(RegOrders, FixedOrder);
RegOrders[InstrZ].Code = NCode;
AddInstTable(InstTable, NName, InstrZ++, DecodeReg);
}
static void InitFields(void)
{
InstTable = CreateInstTable(97);
InstrZ = 0;
AddFixed("CLRC" , 0x2c000);
AddFixed("SETC" , 0x2c001);
AddFixed("CLRZ" , 0x2c002);
AddFixed("SETZ" , 0x2c003);
AddFixed("CLRI" , 0x2c004);
AddFixed("SETI" , 0x2c005);
if (MomCPU == CPUMico8_05)
{
AddFixed("RET" , 0x3a000);
AddFixed("IRET" , 0x3a001);
}
else if (MomCPU == CPUMico8_V3)
{
AddFixed("RET" , 0x38000);
AddFixed("IRET" , 0x39000);
}
else if (MomCPU == CPUMico8_V31)
{
AddFixed("RET" , 0x39000);
AddFixed("IRET" , 0x3a000);
}
AddFixed("NOP" , 0x10000);
InstrZ = 0;
AddALU("ADD" , "ADDI" , 2UL << 14);
AddALU("ADDC" , "ADDIC" , 3UL << 14);
AddALU("SUB" , "SUBI" , 0UL << 14);
AddALU("SUBC" , "SUBIC" , 1UL << 14);
AddALU("MOV" , "MOVI" , 4UL << 14);
AddALU("AND" , "ANDI" , 5UL << 14);
AddALU("OR" , "ORI" , 6UL << 14);
AddALU("XOR" , "XORI" , 7UL << 14);
AddALU("CMP" , "CMPI" , 8UL << 14);
AddALU("TEST" , "TESTI" , 9UL << 14);
AddALU("ROR" , NULL , (10UL << 14) | 0); /* Note: The User guide (Feb '08) differs */
AddALU("ROL" , NULL , (10UL << 14) | 1); /* from the actual implementation in */
AddALU("RORC" , NULL , (10UL << 14) | 2); /* decoding the last 3 bits of the Rotate */
AddALU("ROLC" , NULL , (10UL << 14) | 3); /* instructions. These values are correct. */
InstrZ = 0;
AddReg("INC" , (2UL << 14) | (1UL << 13) | 1);
AddReg("DEC" , (0UL << 14) | (1UL << 13) | 1);
InstrZ = 0;
if (MomCPU != CPUMico8_V31)
{
AddShortBranch("BZ" , 0x32000);
AddShortBranch("BNZ" , 0x32400);
AddShortBranch("BC" , 0x32800);
AddShortBranch("BNC" , 0x32c00);
AddShortBranch("CALLZ" , 0x36000);
AddShortBranch("CALLNZ", 0x36400);
AddShortBranch("CALLC" , 0x36800);
AddShortBranch("CALLNC", 0x36c00);
}
/* AcQ/MA: a group for unconditional branches, which can support
* larger branches then the conditional branches (not supported
* in the earliest versions of the Mico8 processor). The branch
* range is +2047 to -2048 instead of +511 to -512. */
InstrZ = 0;
if (MomCPU != CPUMico8_05)
{
if (MomCPU == CPUMico8_V31)
{
AddLongBranch("BZ" , 0x30000);
AddLongBranch("BNZ" , 0x31000);
AddLongBranch("BC" , 0x32000);
AddLongBranch("BNC" , 0x33000);
AddLongBranch("CALLZ" , 0x34000);
AddLongBranch("CALLNZ", 0x35000);
AddLongBranch("CALLC" , 0x36000);
AddLongBranch("CALLNC", 0x37000);
AddLongBranch("CALL" , 0x38000);
AddLongBranch("B" , 0x3b000);
}
else
{
AddLongBranch("B" , 0x33000);
AddLongBranch("CALL" , 0x37000);
}
}
InstrZ = 0;
if (MomCPU == CPUMico8_V31)
{
AddMem("INP" , "INPI" , (23UL << 13) | 1, SegIO);
AddMem("IMPORT" , "IMPORTI", (23UL << 13) | 1, SegIO);
AddMem("OUTP" , "OUTPI" , (23UL << 13) | 0, SegIO);
AddMem("EXPORT" , "EXPORTI", (23UL << 13) | 0, SegIO);
AddMem("LSP" , "LSPI" , (23UL << 13) | 5, SegData);
AddMem("SSP" , "SSPI" , (23UL << 13) | 4, SegData);
}
else
{
if (MomCPU == CPUMico8_V3)
{
AddMem("INP" , "INPI" , (15UL << 14) | 1, SegIO);
AddMem("OUTP" , "OUTPI" , (15UL << 14) | 0, SegIO);
}
AddMem("IMPORT" , "IMPORTI", (15UL << 14) | 1, SegIO);
AddMem("EXPORT" , "EXPORTI", (15UL << 14) | 0, SegIO);
AddMem("LSP" , "LSPI" , (15UL << 14) | 5, SegData);
AddMem("SSP" , "SSPI" , (15UL << 14) | 4, SegData);
}
AddInstTable(InstTable, "REG", 0, CodeREG);
AddInstTable(InstTable, "PORT", 0, DecodePort);
}
static void DeinitFields(void)
{
DestroyInstTable(InstTable);
order_array_free(FixedOrders);
order_array_free(ALUOrders);
order_array_free(LongBranchOrders);
order_array_free(ShortBranchOrders);
order_array_free(MemOrders);
order_array_free(RegOrders);
}
/*--------------------------------------------------------------------------
* Semipublic Functions
*--------------------------------------------------------------------------*/
/*!------------------------------------------------------------------------
* \fn InternSymbol_Mico8(char *pArg, TempResult *pResult)
* \brief handle built-in (register) symbols for MICO8
* \param pArg source argument
* \param pResult result buffer
* ------------------------------------------------------------------------ */
static void InternSymbol_Mico8(char *pArg, TempResult *pResult)
{
LongWord RegNum;
if (IsWRegCore(pArg, &RegNum))
{
pResult->Typ = TempReg;
pResult->DataSize = eSymbolSize8Bit;
pResult->Contents.RegDescr.Reg = RegNum;
pResult->Contents.RegDescr.Dissect = DissectReg_Mico8;
pResult->Contents.RegDescr.compare = NULL;
}
}
static Boolean IsDef_Mico8(void)
{
return (Memo("REG")) || (Memo("PORT"));
}
static void SwitchFrom_Mico8(void)
{
DeinitFields();
}
static void MakeCode_Mico8(void)
{
CodeLen = 0; DontPrint = False;
/* zu ignorierendes */
if (Memo("")) return;
/* Pseudoanweisungen */
if (DecodeIntelPseudo(True)) return;
if (!LookupInstTable(InstTable, OpPart.str.p_str))
WrStrErrorPos(ErrNum_UnknownInstruction, &OpPart);
}
static void SwitchTo_Mico8(void)
{
const TFamilyDescr *FoundDescr;
FoundDescr = FindFamilyByName("Mico8");
TurnWords = True;
SetIntConstMode(eIntConstModeC);
PCSymbol = "$"; HeaderID = FoundDescr->Id;
/* NOP = mov R0,R0 */
NOPCode = 0x10000;
DivideChars = ","; HasAttrs = False;
ValidSegs = (1 << SegCode) | (1 << SegData) | (1 << SegXData) | (1 << SegIO);
Grans[SegCode] = 4; ListGrans[SegCode] = 4; SegInits[SegCode] = 0;
SegLimits[SegCode] = IntTypeDefs[CodeAddrInt].Max;
Grans[SegData] = 1; ListGrans[SegData] = 1; SegInits[SegData] = 0;
SegLimits[SegData] = IntTypeDefs[DataAddrInt].Max;
Grans[SegXData] = 1; ListGrans[SegXData] = 1; SegInits[SegXData] = 0;
SegLimits[SegXData] = 0xff;
Grans[SegIO] = 1; ListGrans[SegIO] = 1; SegInits[SegIO] = 0;
SegLimits[SegIO] = 0xff;
MakeCode = MakeCode_Mico8;
IsDef = IsDef_Mico8;
InternSymbol = InternSymbol_Mico8;
DissectReg = DissectReg_Mico8;
SwitchFrom = SwitchFrom_Mico8; InitFields();
}
/*--------------------------------------------------------------------------
* Initialization
*--------------------------------------------------------------------------*/
void codemico8_init(void)
{
CPUMico8_05 = AddCPU("Mico8_05" , SwitchTo_Mico8);
CPUMico8_V3 = AddCPU("Mico8_V3" , SwitchTo_Mico8);
CPUMico8_V31 = AddCPU("Mico8_V31", SwitchTo_Mico8);
}