/* * 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 : 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 Compiler::checkParamType(const QString ¶m) { QList 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 : 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 : 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(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 : data.getCodeSectionList()) { for(const QSharedPointer &codeItem : codeSection->getCodeItemList()) { bool compiled = false; QVector> 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 dataList; QVector 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(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 dataList; QVector 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(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(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(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 dataList; QVector 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(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(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(cChar); isOk = true; } } if(isOk) { if(number < 256) { value = static_cast(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(number % 256); valueHigh = static_cast(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); }