DataHoarder
93a5ae1c13
All checks were successful
continuous-integration/drone/push Build is passing
301 lines
15 KiB
C++
301 lines
15 KiB
C++
/*****************************************************************************
|
|
* Copyright (c) 2020, rrcc FM10K-Documentation Contributors
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the copyright holder nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*****************************************************************************/
|
|
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
#include "Instruction.h"
|
|
|
|
#include "Write.h"
|
|
#include "Copy.h"
|
|
#include "Load.h"
|
|
#include "Init.h"
|
|
#include "Calc.h"
|
|
#include "CalcImm.h"
|
|
#include "Branch.h"
|
|
#include "Poll.h"
|
|
#include "Loop.h"
|
|
#include "Jump.h"
|
|
#include "Return.h"
|
|
#include "Set.h"
|
|
#include "Wait.h"
|
|
#include "End.h"
|
|
|
|
#include "virtual/Stack.h"
|
|
#include "virtual/Call.h"
|
|
#include "virtual/FastCall.h"
|
|
#include "virtual/Container.h"
|
|
|
|
#include "../Declaration.h"
|
|
|
|
namespace Instruction {
|
|
|
|
std::vector<struct Instruction::knownOperator> Instruction::knownOperators{};
|
|
|
|
void Instruction::addDefaultOperators() {
|
|
|
|
addOperator("POP", 1, Stack::Pop::fromTokens);
|
|
addOperator("POPI", 0, Stack::PopImm::fromTokens);
|
|
addOperator("POPP", 1, Stack::PopPeek::fromTokens);
|
|
addOperator("PUSH", 1, Stack::Push::fromTokens);
|
|
|
|
addOperator("GET", 1, Stack::Get::fromTokens);
|
|
addOperator("PUT", 1, Stack::Put::fromTokens);
|
|
|
|
addOperator("CALL", 1, Call::fromTokens);
|
|
addOperator("RET", 0, Ret::fromTokens);
|
|
addOperator("FASTCALL", 1, FastCall::fromTokens);
|
|
|
|
addOperator("MOV", 2, [](const parseOperatorResult &op, const std::vector<Token> &tokens) -> std::unique_ptr<Instruction> {
|
|
if (tokens.at(1).getType() == Token::Type::RegisterLocation) {
|
|
return std::make_unique<Copy>(AddressWithOffset{tokens.at(0).getNumericValue(), op.offset1},
|
|
AddressWithOffset{tokens.at(1).getNumericValue(), op.offset2}, 1);
|
|
} else {
|
|
return std::make_unique<Write>(AddressWithOffset{tokens.at(0).getNumericValue(), op.offset1},
|
|
std::vector<uint32_t>{tokens.at(1).getNumericValue()});
|
|
}
|
|
});
|
|
|
|
addOperator("JUMP", 1, Jump::fromTokens);
|
|
addOperator("RETURN", 1, Return::fromTokens);
|
|
|
|
addOperator("WRITE", 2, Write::fromTokens);
|
|
|
|
addOperator("COPY", 2, Copy::fromTokens);
|
|
|
|
// INIT <R> <count> [data] [data_add]
|
|
addOperator("INIT", 2, Init::fromTokens);
|
|
addOperator("INIT_DIRECT", 2, Init::fromDirectTokens);
|
|
|
|
addOperator("LOAD", 2, Load::fromTokens);
|
|
addOperator("LOAD_DIRECT", 2, Load::fromDirectTokens);
|
|
|
|
// NOT dest, source
|
|
//Using NOT x = 255 - x.
|
|
addOperator("NOT", 2, [](const parseOperatorResult &op, const std::vector<Token> &tokens) -> std::unique_ptr<Instruction> {
|
|
if(op.offset1 != 0){
|
|
auto c = std::make_unique<Container>("NOT op with offset");
|
|
c->addInstruction(std::make_unique<Copy>(AddressWithOffset{(uint32_t) MgmtRegisters::std_SCRATCH_0, 0}, AddressWithOffset{tokens.at(1).getNumericValue(), op.offset1}, 1));
|
|
c->addInstruction(std::make_unique<Calc>(Calc::Operation::SUB, 0,
|
|
(uint32_t) MgmtRegisters::std_SCRATCH_0,
|
|
(uint32_t)MgmtRegisters::INTERNAL_REGISTER_ALWAYS_FFFFFFFF,
|
|
(uint32_t) MgmtRegisters::std_SCRATCH_0));
|
|
c->addInstruction(std::make_unique<Copy>(AddressWithOffset{tokens.at(1).getNumericValue(), op.offset2}, AddressWithOffset{(uint32_t) MgmtRegisters::std_SCRATCH_0, 0}, 1));
|
|
return std::move(c);
|
|
}
|
|
auto i = std::make_unique<Calc>(Calc::Operation::SUB, 0,
|
|
tokens.at(0).getNumericValue(),
|
|
(uint32_t)MgmtRegisters::INTERNAL_REGISTER_ALWAYS_FFFFFFFF,
|
|
tokens.at(1).getNumericValue());
|
|
i->comment = "NOT op";
|
|
return std::move(i);
|
|
});
|
|
|
|
//XOR P1, P2 = A, P3 = B
|
|
addOperator("XOR", 3, [](const parseOperatorResult &op, const std::vector<Token> &tokens) -> std::unique_ptr<Instruction> {
|
|
if(op.offset1 != 0){
|
|
throw std::runtime_error("XOR cannot use addressing offsets");
|
|
}
|
|
auto c = std::make_unique<Container>("XOR op = a + b - (a & b) - (a & b)");
|
|
|
|
/*
|
|
* Non-offset version
|
|
* AND S1, A, B
|
|
* ADD S0, A, B
|
|
* SUB S0, S0, S1
|
|
* SUB P1, S0, S1
|
|
*/
|
|
|
|
if(tokens.at(2).getType() == Token::Type::Immediate){
|
|
c->addInstruction(std::make_unique<CalcImm>(Calc::Operation::AND, 0, (uint32_t) MgmtRegisters::std_SCRATCH_1, tokens.at(1).getNumericValue(), tokens.at(2).getNumericValue()), "XOR, s1 = a & b");
|
|
c->addInstruction(std::make_unique<CalcImm>(Calc::Operation::ADD, 0, (uint32_t) MgmtRegisters::std_SCRATCH_0, tokens.at(1).getNumericValue(), tokens.at(2).getNumericValue()), "XOR, s0 = a + b");
|
|
}else{
|
|
c->addInstruction(std::make_unique<Calc>(Calc::Operation::AND, 0, (uint32_t) MgmtRegisters::std_SCRATCH_1, tokens.at(1).getNumericValue(), tokens.at(2).getNumericValue()), "XOR, s1 = a & b");
|
|
c->addInstruction(std::make_unique<Calc>(Calc::Operation::ADD, 0, (uint32_t) MgmtRegisters::std_SCRATCH_0, tokens.at(1).getNumericValue(), tokens.at(2).getNumericValue()), "XOR, s0 = a + b");
|
|
}
|
|
|
|
c->addInstruction(std::make_unique<Calc>(Calc::Operation::SUB, 0, (uint32_t) MgmtRegisters::std_SCRATCH_0, (uint32_t) MgmtRegisters::std_SCRATCH_0, (uint32_t) MgmtRegisters::std_SCRATCH_1), "XOR, s0 = s0 - s1");
|
|
c->addInstruction(std::make_unique<Calc>(Calc::Operation::SUB, 0, tokens.at(0).getNumericValue(), (uint32_t) MgmtRegisters::std_SCRATCH_0, (uint32_t) MgmtRegisters::std_SCRATCH_1), "XOR, result = s0 - s1");
|
|
|
|
return std::move(c);
|
|
});
|
|
|
|
addOperator("AND", 3, CalcImm::fromAndTokens);
|
|
|
|
addOperator("OR", 3, CalcImm::fromOrTokens);
|
|
|
|
addOperator("ADD", 3, CalcImm::fromAddTokens);
|
|
|
|
addOperator("SUB", 3, CalcImm::fromSubTokens);
|
|
|
|
addOperator("LSHIFT", 3, CalcImm::fromLShiftTokens);
|
|
addOperator("SHL", 3, CalcImm::fromLShiftTokens);
|
|
|
|
addOperator("RSHIFT", 3, CalcImm::fromRShiftTokens);
|
|
addOperator("SHR", 3, CalcImm::fromRShiftTokens);
|
|
|
|
addOperator("ARSHIFT", 3, CalcImm::fromARShiftTokens);
|
|
addOperator("SAR", 3, CalcImm::fromARShiftTokens);
|
|
|
|
addOperator("BRANCH_EQUALS", 4, Branch::fromEqualTokens);
|
|
addOperator("BEQ", 4, Branch::fromEqualTokens); //TODO: redo
|
|
addOperator("BRANCH_NOTEQUALS", 4, Branch::fromNonEqualTokens);
|
|
addOperator("BNE", 4, Branch::fromNonEqualTokens); //TODO: redo
|
|
|
|
addOperator("POLL_EQUALS", 6, Poll::fromEqualTokens);
|
|
addOperator("PEQ", 6, Poll::fromEqualTokens); //TODO: redo
|
|
addOperator("POLL_NOTEQUALS", 6, Poll::fromNonEqualTokens);
|
|
addOperator("PNE", 6, Poll::fromNonEqualTokens); //TODO: redo
|
|
|
|
//This allows both static and dynamic calls, with multiple offsets
|
|
addOperator("SET", 3, [](const parseOperatorResult &op, const std::vector<Token> &tokens) -> std::unique_ptr<Instruction> {
|
|
if (tokens.at(1).getType() == Token::Type::Immediate && tokens.at(2).getType() == Token::Type::Immediate) {
|
|
return Set::fromTokens(op, tokens);
|
|
} else {
|
|
auto c = std::make_unique<Container>("SET op, extended with registers");
|
|
|
|
//Get register if needed due to offset
|
|
if (op.offset1) {
|
|
c->addInstruction(std::make_unique<Copy>(AddressWithOffset{(uint32_t) MgmtRegisters::std_SCRATCH_0, 0},
|
|
AddressWithOffset{tokens.at(0).getNumericValue(), op.offset1}));
|
|
}
|
|
|
|
//S0 = (Register & ~maskValue)
|
|
if (tokens.at(2).getType() == Token::Type::Immediate) {
|
|
c->addInstruction(std::make_unique<CalcImm>(Calc::Operation::AND, 0, (uint32_t) MgmtRegisters::std_SCRATCH_0,
|
|
op.offset1 ? (uint32_t) MgmtRegisters::std_SCRATCH_0 : tokens.at(
|
|
0).getNumericValue(), ~tokens.at(2).getNumericValue()));
|
|
} else {
|
|
if (op.offset2) {
|
|
c->addInstruction(std::make_unique<Copy>(AddressWithOffset{(uint32_t) MgmtRegisters::std_SCRATCH_1, 0},
|
|
AddressWithOffset{tokens.at(2).getNumericValue(), op.offset2}));
|
|
}
|
|
//NOT without offset
|
|
c->addInstruction(std::make_unique<Calc>(Calc::Operation::SUB, 0,
|
|
(uint32_t) MgmtRegisters::std_SCRATCH_1,
|
|
(uint32_t) MgmtRegisters::INTERNAL_REGISTER_ALWAYS_FFFFFFFF,
|
|
op.offset2 ? (uint32_t) MgmtRegisters::std_SCRATCH_1 : tokens.at(
|
|
2).getNumericValue()));
|
|
c->addInstruction(std::make_unique<Calc>(Calc::Operation::AND, 0, (uint32_t) MgmtRegisters::std_SCRATCH_0,
|
|
op.offset1 ? (uint32_t) MgmtRegisters::std_SCRATCH_0 : tokens.at(
|
|
0).getNumericValue(), (uint32_t) MgmtRegisters::std_SCRATCH_1));
|
|
}
|
|
|
|
//Register = S0 | value
|
|
if (tokens.at(1).getType() == Token::Type::Immediate) {
|
|
c->addInstruction(std::make_unique<CalcImm>(Calc::Operation::OR, 0,
|
|
op.offset1 ? (uint32_t) MgmtRegisters::std_SCRATCH_0 : tokens.at(
|
|
0).getNumericValue(), (uint32_t) MgmtRegisters::std_SCRATCH_0,
|
|
tokens.at(1).getNumericValue()));
|
|
} else {
|
|
if (op.offset3) {
|
|
c->addInstruction(std::make_unique<Copy>(AddressWithOffset{(uint32_t) MgmtRegisters::std_SCRATCH_1, 0},
|
|
AddressWithOffset{tokens.at(1).getNumericValue(), op.offset2}));
|
|
}
|
|
c->addInstruction(std::make_unique<Calc>(Calc::Operation::OR, 0,
|
|
op.offset1 ? (uint32_t) MgmtRegisters::std_SCRATCH_0 : tokens.at(
|
|
0).getNumericValue(), (uint32_t) MgmtRegisters::std_SCRATCH_0,
|
|
op.offset3 ? (uint32_t) MgmtRegisters::std_SCRATCH_1 : tokens.at(
|
|
1).getNumericValue()));
|
|
}
|
|
|
|
//set register if needed due to offset
|
|
if (op.offset1) {
|
|
c->addInstruction(std::make_unique<Copy>(AddressWithOffset{tokens.at(0).getNumericValue(), op.offset1},
|
|
AddressWithOffset{(uint32_t) MgmtRegisters::std_SCRATCH_0, 0}));
|
|
}
|
|
|
|
return std::move(c);
|
|
}
|
|
});
|
|
|
|
addOperator("LOOP", 1, Loop::fromTokens);
|
|
|
|
addOperator("WAIT", 1, Wait::fromTokens);
|
|
|
|
addOperator("END", 0, End::fromTokens);
|
|
}
|
|
|
|
std::unique_ptr<Instruction> Instruction::getCommandForDeclaration(const Declaration &declaration) {
|
|
size_t offset = 0;
|
|
for (auto &t : declaration.tokens) {
|
|
if (t.getType() == Token::Type::Operator) {
|
|
break;
|
|
}
|
|
++offset;
|
|
}
|
|
|
|
if (offset == declaration.tokens.size()) {
|
|
return nullptr;
|
|
}
|
|
|
|
offset++;
|
|
|
|
std::string operatorValue = declaration.tokens.at(offset - 1).getTextValue();
|
|
|
|
auto operatorData = parseOperator(operatorValue);
|
|
|
|
std::vector<Token> tokens(declaration.tokens.begin() + offset, declaration.tokens.end());
|
|
|
|
std::string opName = operatorData.name;
|
|
for (auto & c: opName){
|
|
c = std::toupper(c);
|
|
}
|
|
|
|
for (auto &e : knownOperators) {
|
|
if (e.name == opName && tokens.size() >= e.minParams) {
|
|
return e.callable(operatorData, tokens);
|
|
}
|
|
}
|
|
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Instruction::parseOperatorResult Instruction::parseOperator(const std::string &op) {
|
|
std::stringstream s(op);
|
|
std::vector<std::string> segments;
|
|
std::string segment;
|
|
|
|
while (std::getline(s, segment, '~')) {
|
|
segments.push_back(segment);
|
|
}
|
|
|
|
uint8_t offset1 = segments.size() > 1 ? std::stoul(segments.at(1).substr(0, 1), nullptr, 10) : 0;
|
|
uint8_t offset2 = (segments.size() > 1 && segments.at(1).size() > 1) ? std::stoul(
|
|
segments.at(1).substr(1, 1), nullptr, 10) : 0;
|
|
|
|
return Instruction::parseOperatorResult{
|
|
segments.at(0),
|
|
offset1,
|
|
offset2
|
|
};
|
|
}
|
|
|
|
|
|
}
|
|
|