/*
* Author: Pawel Jablonski
* E-mail: pj@xirx.net
* WWW: xirx.net
* GIT: git.xirx.net
*
* License: You can use this code however you like
* but leave information about the original author.
* Code is free for non-commercial and commercial use.
*/
#include "compiler.h"
/**
* Constructor of the compiler
*
* @param dataList Data list
*/
Compiler::Compiler(DataList &dataList) : dataList(dataList)
{
}
/**
* Add an instruction to the uROM opcode table
*
* @param instruction Instruction to add
*/
void Compiler::addInstruction(const Instruction &instruction)
{
this->instructionList.append(instruction);
}
/**
* Compile the application
*
* @return Status of compiling the application
*/
bool Compiler::compile()
{
QTextStream out(stdout);
for(const QSharedPointer<Data> &data : this->dataList)
{
if(!(this->compileRam(*data)))
{
return(false);
}
if(!(this->compileFlash(*data)))
{
return(false);
}
if(!(this->compileCode(*data)))
{
return(false);
}
}
return(true);
}
/**
* Check the type of the param
*
* @param param String to check
*
* @return Param type
*/
QList<Instruction::Arg> Compiler::checkParamType(const QString ¶m)
{
QList<Instruction::Arg> argList;
if(param.contains(QRegularExpression("^(a|b|x|y|A|B|X|Y)$")))
{
argList.append(Instruction::Arg::ABXY);
if(param.contains(QRegularExpression("^(a|b|A|B)$")))
{
argList.append(Instruction::Arg::AB);
}
}
else if(param.contains(QRegularExpression("^([0-9]+|0b[01]+|0x[0-9abcdefABCDEF]+|'.')$")))
{
argList.append(Instruction::Arg::Val256);
if(param.contains(QRegularExpression("^(0x[0-9abcdefABCDEF]+)$")))
{
argList.append(Instruction::Arg::Addr);
}
}
else if(param.contains(QRegularExpression(R"(^(\.[0-9a-zA-Z_-]+)$)")))
{
argList.append(Instruction::Arg::Addr);
}
else if(param.contains(QRegularExpression(R"(^(\*[0-9a-zA-Z_-]+)$)")))
{
argList.append(Instruction::Arg::Addr);
}
else if(param.contains(QRegularExpression(R"(^(\*[0-9a-zA-Z_-]+\[[0-9]+\])$)")))
{
argList.append(Instruction::Arg::Addr);
}
else if(param.contains(QRegularExpression(R"(^((low|high)\(\.[0-9a-zA-Z_-]+\))$)")))
{
argList.append(Instruction::Arg::Val256);
}
else if(param.contains(QRegularExpression(R"(^((low|high)\(\*[0-9a-zA-Z_-]+\))$)")))
{
argList.append(Instruction::Arg::Val256);
}
else if(param.contains(QRegularExpression(R"(^((low|high)\(\*[0-9a-zA-Z_-]+\[[0-9]+\]\))$)")))
{
argList.append(Instruction::Arg::Val256);
}
return(argList);
}
/**
* Compile the RAM items
*
* @param data Data of the parsed application
*
* @return Status of compiling the RAM items
*/
bool Compiler::compileRam(Data &data)
{
QTextStream out(stdout);
for(const QSharedPointer<RamItem> &ramItem : data.getRamItemList())
{
bool compiled = false;
QString arraySize = ramItem->getArraySize();
if(arraySize.length() > 0)
{
if(arraySize.contains(QRegularExpression("^([0-9]+)$")))
{
bool isOk;
unsigned int size = arraySize.toUInt(&isOk, 10);
if(isOk)
{
ramItem->setDataSize(size);
compiled = true;
}
}
}
else
{
ramItem->setDataSize(1);
compiled = true;
}
if(!compiled)
{
out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(ramItem->getLine()).arg(data.getFileName());
return(false);
}
}
return(true);
}
/**
* Compile the Flash items
*
* @param data Data of the parsed application
*
* @return Status of compiling the Flash items
*/
bool Compiler::compileFlash(Data &data)
{
QTextStream out(stdout);
for(const QSharedPointer<FlashItem> &flashItem : data.getFlashItemList())
{
bool compiled = true;
bool isString = false;
if(flashItem->getValueList().size() == 1)
{
QString string = flashItem->getValueList().at(0);
if(string.contains(QRegularExpression("^(\".+\")$")))
{
isString = true;
for(int j = 1; j < (string.length() - 1); j++)
{
char cChar = string.at(j).toLatin1();
if(cChar > 0)
{
flashItem->getDataVector().append(static_cast<unsigned char>(cChar));
}
else
{
compiled = false;
break;
}
}
flashItem->getDataVector().append(0x00);
}
}
if(!isString)
{
if(!flashItem->getValueList().empty())
{
for(int j = 0; j < flashItem->getValueList().size(); j++)
{
unsigned char value;
if(this->parseParamVal256(flashItem->getValueList().at(j), value))
{
flashItem->getDataVector().append(value);
}
else
{
compiled = false;
break;
}
}
}
else
{
compiled = false;
}
}
if(!compiled)
{
out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(flashItem->getLine()).arg(data.getFileName());
return(false);
}
}
return(true);
}
/**
* Compile the code items
*
* @param data Data of the parsed application
*
* @return Status of compiling the code items
*/
bool Compiler::compileCode(Data &data)
{
QTextStream out(stdout);
for(const QSharedPointer<CodeSection> &codeSection : data.getCodeSectionList())
{
for(const QSharedPointer<CodeItem> &codeItem : codeSection->getCodeItemList())
{
bool compiled = false;
QVector<QList<Instruction::Arg>> arg(3);
if(codeItem->getParamList().length() == 3)
{
arg[0].append(this->checkParamType(codeItem->getParamList().at(0)));
arg[1].append(this->checkParamType(codeItem->getParamList().at(1)));
arg[2].append(this->checkParamType(codeItem->getParamList().at(2)));
for(const Instruction &instruction : this->instructionList)
{
if(!instruction.getName().compare(codeItem->getName(), Qt::CaseInsensitive))
{
if(arg[0].contains(instruction.getArgList().at(0)) && arg[1].contains(instruction.getArgList().at(1)) && arg[2].contains(instruction.getArgList().at(2)))
{
QVector<unsigned char> dataList;
QVector<unsigned char> tmp(2);
unsigned char opCode = instruction.getOpcode();
bool isOk = true;
if(instruction.getArgList().at(0) == Instruction::Arg::AB)
{
isOk &= this->parseParamAB(codeItem->getParamList().at(0), tmp[0]);
opCode |= (tmp[0] << 2);
if(instruction.getArgList().at(1) == Instruction::Arg::ABXY)
{
isOk &= this->parseParamABXY(codeItem->getParamList().at(1), tmp[0]);
opCode |= tmp[0];
}
else
{
isOk = false;
}
}
else if(instruction.getArgList().at(1) == Instruction::Arg::ABXY)
{
isOk &= this->parseParamABXY(codeItem->getParamList().at(1), tmp[0]);
opCode |= tmp[0];
}
dataList.append(opCode);
if(instruction.getArgList().at(0) == Instruction::Arg::Val256)
{
isOk &= this->parseParamVal256(codeItem->getParamList().at(0), tmp[0]);
dataList.append(tmp[0]);
}
if(instruction.getArgList().at(2) == Instruction::Arg::Addr)
{
if(isOk && this->isAddrLabel(codeItem->getParamList().at(2)))
{
codeItem->getToFillUpHash().insert(codeItem->getParamList().at(2), static_cast<unsigned int>(dataList.size()));
}
else
{
isOk &= this->parseParamAddr(codeItem->getParamList().at(2), tmp[0], tmp[1]);
}
dataList.append(tmp[0]);
dataList.append(tmp[1]);
}
if(isOk)
{
codeItem->getDataVector().append(dataList);
compiled = true;
}
break;
}
}
}
}
else if(codeItem->getParamList().length() == 2)
{
arg[0].append(this->checkParamType(codeItem->getParamList().at(0)));
arg[1].append(this->checkParamType(codeItem->getParamList().at(1)));
for(const Instruction &instruction : this->instructionList)
{
if(!instruction.getName().compare(codeItem->getName(), Qt::CaseInsensitive))
{
if(arg[0].contains(instruction.getArgList().at(0)) && arg[1].contains(instruction.getArgList().at(1)) && (instruction.getArgList().at(2) == Instruction::Arg::None))
{
QVector<unsigned char> dataList;
QVector<unsigned char> tmp(2);
unsigned char opCode = instruction.getOpcode();
bool isOk = true;
if(instruction.getArgList().at(0) == Instruction::Arg::AB)
{
isOk &= this->parseParamAB(codeItem->getParamList().at(0), tmp[0]);
opCode |= (tmp[0] << 2);
if(instruction.getArgList().at(1) == Instruction::Arg::ABXY)
{
isOk &= this->parseParamABXY(codeItem->getParamList().at(1), tmp[0]);
opCode |= tmp[0];
}
else
{
isOk = false;
}
}
else if(instruction.getArgList().at(0) == Instruction::Arg::ABXY)
{
isOk &= this->parseParamABXY(codeItem->getParamList().at(0), tmp[0]);
if(instruction.getArgList().at(1) == Instruction::Arg::ABXY)
{
isOk &= this->parseParamABXY(codeItem->getParamList().at(1), tmp[1]);
opCode |= ((tmp[0] << 2) | tmp[1]);
}
else
{
opCode |= tmp[0];
}
}
else if(instruction.getArgList().at(1) == Instruction::Arg::ABXY)
{
isOk &= this->parseParamABXY(codeItem->getParamList().at(1), tmp[0]);
opCode |= tmp[0];
}
dataList.append(opCode);
if(instruction.getArgList().at(0) == Instruction::Arg::Val256)
{
if(isOk && this->isAddrPartLabel(codeItem->getParamList().at(0)))
{
codeItem->getToFillUpHash().insert(codeItem->getParamList().at(0), static_cast<unsigned int>(dataList.size()));
}
else
{
isOk &= this->parseParamVal256(codeItem->getParamList().at(0), tmp[0]);
}
dataList.append(tmp[0]);
}
else if(instruction.getArgList().at(0) == Instruction::Arg::Addr)
{
if(isOk && this->isAddrLabel(codeItem->getParamList().at(0)))
{
codeItem->getToFillUpHash().insert(codeItem->getParamList().at(0), static_cast<unsigned int>(dataList.size()));
}
else
{
isOk &= this->parseParamAddr(codeItem->getParamList().at(0), tmp[0], tmp[1]);
}
dataList.append(tmp[0]);
dataList.append(tmp[1]);
}
if(instruction.getArgList().at(1) == Instruction::Arg::Addr)
{
if(isOk && this->isAddrLabel(codeItem->getParamList().at(1)))
{
codeItem->getToFillUpHash().insert(codeItem->getParamList().at(1), static_cast<unsigned int>(dataList.size()));
}
else
{
isOk &= this->parseParamAddr(codeItem->getParamList().at(1), tmp[0], tmp[1]);
}
dataList.append(tmp[0]);
dataList.append(tmp[1]);
}
if(isOk)
{
if(instruction.getOpcodeSecond() != 0)
{
opCode = instruction.getOpcodeSecond();
if(instruction.getArgSecondList().at(0) == Instruction::Arg::ABXY)
{
isOk &= this->parseParamABXY(codeItem->getParamList().at(0), tmp[0]);
opCode |= tmp[0];
}
dataList.append(opCode);
}
if(isOk)
{
codeItem->getDataVector().append(dataList);
compiled = true;
}
}
break;
}
}
}
}
else if(codeItem->getParamList().length() == 1)
{
arg[0].append(this->checkParamType(codeItem->getParamList().at(0)));
for(const Instruction &instruction : this->instructionList)
{
if(!instruction.getName().compare(codeItem->getName(), Qt::CaseInsensitive))
{
if(arg[0].contains(instruction.getArgList().at(0)) && (instruction.getArgList().at(1) == Instruction::Arg::None) && (instruction.getArgList().at(2) == Instruction::Arg::None))
{
QVector<unsigned char> dataList;
QVector<unsigned char> tmp(2);
unsigned char opCode = instruction.getOpcode();
bool isOk = true;
if(instruction.getArgList().at(0) == Instruction::Arg::ABXY)
{
isOk &= this->parseParamABXY(codeItem->getParamList().at(0), tmp[0]);
opCode |= tmp[0];
}
dataList.append(opCode);
if(instruction.getArgList().at(0) == Instruction::Arg::Val256)
{
if(isOk && this->isAddrPartLabel(codeItem->getParamList().at(0)))
{
codeItem->getToFillUpHash().insert(codeItem->getParamList().at(0), static_cast<unsigned int>(dataList.size()));
}
else
{
isOk &= this->parseParamVal256(codeItem->getParamList().at(0), tmp[0]);
}
dataList.append(tmp[0]);
}
else if(instruction.getArgList().at(0) == Instruction::Arg::Addr)
{
if(isOk && this->isAddrLabel(codeItem->getParamList().at(0)))
{
codeItem->getToFillUpHash().insert(codeItem->getParamList().at(0), static_cast<unsigned int>(dataList.size()));
}
else
{
isOk &= this->parseParamAddr(codeItem->getParamList().at(0), tmp[0], tmp[1]);
}
dataList.append(tmp[0]);
dataList.append(tmp[1]);
}
if(isOk)
{
codeItem->getDataVector().append(dataList);
compiled = true;
}
break;
}
}
}
}
else
{
for(const Instruction &instruction : this->instructionList)
{
if(!instruction.getName().compare(codeItem->getName(), Qt::CaseInsensitive))
{
if((instruction.getArgList().at(0) == Instruction::Arg::None) && (instruction.getArgList().at(1) == Instruction::Arg::None) && (instruction.getArgList().at(2) == Instruction::Arg::None))
{
codeItem->getDataVector().append(instruction.getOpcode());
compiled = true;
break;
}
}
}
}
if(!compiled)
{
out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(codeItem->getLine()).arg(data.getFileName());
return(false);
}
}
}
return(true);
}
/**
* Parse the string and return AB
*
* @param param String to parsing
* @param value Parsed value
*
* @return Status of parsing the string
*/
bool Compiler::parseParamAB(const QString ¶m, unsigned char &value)
{
if(!param.compare("a", Qt::CaseInsensitive))
{
value = 0;
}
else if(!param.compare("b", Qt::CaseInsensitive))
{
value = 1;
}
else
{
return(false);
}
return(true);
}
/**
* Parse the string and return ABXY
*
* @param param String to parsing
* @param value Parsed value
*
* @return Status of parsing the string
*/
bool Compiler::parseParamABXY(const QString ¶m, unsigned char &value)
{
if(!param.compare("a", Qt::CaseInsensitive))
{
value = 0;
}
else if(!param.compare("b", Qt::CaseInsensitive))
{
value = 1;
}
else if(!param.compare("x", Qt::CaseInsensitive))
{
value = 2;
}
else if(!param.compare("y", Qt::CaseInsensitive))
{
value = 3;
}
else
{
return(false);
}
return(true);
}
/**
* Parse the string and return Val256
*
* @param param String to parsing
* @param value Parsed value
*
* @return Status of parsing the string
*/
bool Compiler::parseParamVal256(const QString ¶m, unsigned char &value)
{
bool isOk = false;
unsigned int number = 0;
if(param.contains(QRegularExpression("^([0-9]+)$")))
{
number = param.toUInt(&isOk, 10);
}
else if(param.contains(QRegularExpression("^(0b[01]+)$")))
{
number = param.midRef(2).toUInt(&isOk, 2);
}
else if(param.contains(QRegularExpression("^(0x[0-9abcdefABCDEF]+)$")))
{
number = param.midRef(2).toUInt(&isOk, 16);
}
else if(param.contains(QRegularExpression("^('.')$")))
{
char cChar = param.at(1).toLatin1();
if(cChar > 0)
{
number = static_cast<unsigned int>(cChar);
isOk = true;
}
}
if(isOk)
{
if(number < 256)
{
value = static_cast<unsigned char>(number);
}
else
{
isOk = false;
}
}
return(isOk);
}
/**
* Parse the string and return address
*
* @param param String to parsing
* @param valueLow Parsed lower byte of the value
* @param valueHigh Parsed higher byte of the value
*
* @return Status of parsing the string
*/
bool Compiler::parseParamAddr(const QString ¶m, unsigned char &valueLow, unsigned char &valueHigh)
{
bool isOk = false;
unsigned int number = 0;
if(param.contains(QRegularExpression("^(0x[0-9abcdefABCDEF]+)$")))
{
number = param.midRef(2).toUInt(&isOk, 16);
}
if(isOk)
{
if(number < Data::MEMORY_SIZE)
{
valueLow = static_cast<unsigned char>(number % 256);
valueHigh = static_cast<unsigned char>(number / 256);
}
else
{
isOk = false;
}
}
return(isOk);
}
/**
* Check if the param is an address label
*
* @param param String to check
*
* @return Status of the check
*/
bool Compiler::isAddrLabel(const QString ¶m) const
{
if(param.contains(QRegularExpression(R"(^(\.[0-9a-zA-Z_-]+)$)")))
{
return(true);
}
else if(param.contains(QRegularExpression(R"(^(\*[0-9a-zA-Z_-]+)$)")))
{
return(true);
}
else if(param.contains(QRegularExpression(R"(^(\*[0-9a-zA-Z_-]+\[[0-9]+\])$)")))
{
return(true);
}
return(false);
}
/**
* Check if the param is a low or high address
*
* @param param String to check
*
* @return Status of the check
*/
bool Compiler::isAddrPartLabel(const QString ¶m) const
{
if(param.contains(QRegularExpression(R"(^((low|high)\(\.[0-9a-zA-Z_-]+\))$)")))
{
return(true);
}
else if(param.contains(QRegularExpression(R"(^((low|high)\(\*[0-9a-zA-Z_-]+\))$)")))
{
return(true);
}
else if(param.contains(QRegularExpression(R"(^((low|high)\(\*[0-9a-zA-Z_-]+\[[0-9]+\]\))$)")))
{
return(true);
}
return(false);
}