#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <map>
#include <vector>
#include <assert.h>
#include <errno.h>
#include <sstream>
#include <algorithm>
#include <unistd.h> // for getopt()
using namespace std;
enum token_t {
END=-1,
WORD,
DOT,
COMMA,
SEMI,
COLON,
};
struct QPUreg {
enum { A, B, ACCUM, SMALL } file;
int num;
};
struct relocation {
string label;
int pc;
};
struct context {
const char *stream;
map<string, int> labels;
int pc;
vector<relocation> relocations;
};
static string addOps[] = {
"nop", "fadd", "fsub", "fmin", "fmax", "fminabs", "fmaxabs",
"ftoi", "itof", "XXX", "XXX", "XXX", "add", "sub", "shr",
"asr", "ror", "shl", "min", "max", "and", "or", "xor", "not",
"clz", "XXX", "XXX", "XXX", "XXX", "XXX", "v8adds", "v8subs" };
static string mulOps[] = {
"nop", "fmul", "mul24", "v8muld", "v8min", "v8max", "v8adds",
"v8subs" };
static uint8_t addOpCode(const string& word)
{
for (int i=0; i < 32; i++) {
if (word == addOps[i])
return i;
}
return 0xFF;
}
string printAddOpCode(uint8_t opcode) {
assert((opcode >= 0) && (opcode < 32));
return addOps[opcode];
}
static uint8_t mulOpCode(const string& word)
{
for (int i=0; i < 8; i++) {
if (word == mulOps[i])
return i;
}
return 0xFF;
}
string printMulOpCode(uint8_t opcode) {
assert((opcode >= 0) && (opcode < 8));
return mulOps[opcode];
}
bool isRegisterWord(const string& word) { return word[0] == 'r'; }
string printRegister(const QPUreg& reg)
{
char buffer[32];
if (reg.file == QPUreg::A || reg.file == QPUreg::B) {
snprintf(buffer, 32, "r%c%d", (reg.file == QPUreg::A) ? 'a' : 'b',
reg.num);
}
else if (reg.file == QPUreg::ACCUM) {
snprintf(buffer, 32, "r%d", reg.num);
}
else {
snprintf(buffer, 32, ".0x%x.", reg.num);
}
return buffer;
}
void parsePossibleNumber(const char* possibleNumber, int base, int* outNumber, bool* outIsNumber) {
char *endOfNumber;
*outNumber = strtol(possibleNumber, &endOfNumber, base);
*outIsNumber = (!(endOfNumber == possibleNumber || *endOfNumber != '\0' || errno == ERANGE));
}
bool parseRegister(const string& word, QPUreg& reg)
{
if (word[0] != 'r')
return false;
int offset = 0;
switch (word[1]) {
case 'a': reg.file = QPUreg::A; offset = 2; break;
case 'b': reg.file = QPUreg::B; offset = 2; break;
default:
reg.file = QPUreg::ACCUM;
offset = 1;
}
const char* possibleNumber = (word.c_str() + offset);
bool isNumber;
int number;
parsePossibleNumber(possibleNumber, 10, &number, &isNumber);
if (!isNumber) {
cerr << "Warning - couldn't interpret '" << word << "' as a register" << endl;
return false;
}
reg.num = number;
if ((reg.file == QPUreg::ACCUM) && (reg.num >= 6)) {
fprintf(stderr, "Warning - accumulator out of range\n");
return false;
}
return true;
}
bool parseFullImmediate(const string& str, uint32_t* outResult, uint32_t* outType)
{
bool isNumber;
if (str[0] == '[') {
bool areAnyNegative = false;
std:string cleanedString(str);
cleanedString.erase(std::remove(cleanedString.begin(), cleanedString.end(), '['), cleanedString.end());
cleanedString.erase(std::remove(cleanedString.begin(), cleanedString.end(), ']'), cleanedString.end());
std::stringstream ss(cleanedString);
std::string item;
int itemCount = 0;
int itemValues[16];
while (std::getline(ss, item, ',')) {
if (itemCount >= 16) {
break;
}
bool isItemNumber;
int itemValue;
parsePossibleNumber(item.c_str(), 10, &itemValues[itemCount], &isItemNumber);
if (!isItemNumber) {
cerr << "Couldn't understand '" << item << "' as an entry in an immediate list" << endl;
return false;
}
if (itemValues[itemCount] < 0) {
areAnyNegative = true;
}
itemCount += 1;
}
if (itemCount < 16) {
cerr << "Found too few items in the immediate array - expected 16 but had " << itemCount << endl;
return false;
}
if (areAnyNegative) {
*outType = 0x02;
} else {
*outType = 0x06;
}
uint32_t result = 0;
for (int index = 0; index < 16; index += 1) {
int value = itemValues[index];
if (areAnyNegative) {
if ((value < -1) || (value > 1)) {
cerr << "Found an out-of-range signed value in the immediate array - expected -1, 0, or 1 but found " << value << endl;
return false;
}
} else {
if (value > 3) {
cerr << "Found an out-of-range unsigned value in the immediate array - expected 0, 1, 2, or 3 but found " << value << endl;
return false;
}
}
uint32_t msb;
uint32_t lsb;
if (areAnyNegative) {
msb = ((value & 0x80000000) >> 31);
lsb = (value & 0x1);
} else {
msb = ((value & 0x2) >> 1);
lsb = (value & 0x1);
}
result = (result | (lsb << (index + 0)));
result = (result | (msb << (index + 16)));
}
*outResult = result;
isNumber = true;
} else {
*outType = 0x00; // A full 32-bit immediate
// if there is an 'x' we assume it's hex.
if (str.find_first_of("x") != string::npos) {
int signedResult;
parsePossibleNumber(str.c_str(), 16, &signedResult, &isNumber);
*outResult = signedResult;
} else if (str.find_first_of(".f") != string::npos) {
float f = strtof(str.c_str(), NULL);
*outResult = *(uint32_t*)&f;
isNumber = true;
} else {
int signedResult;
parsePossibleNumber(str.c_str(), 10, &signedResult, &isNumber);
*outResult = signedResult;
}
}
return isNumber;
}
int32_t parseSmallImmediate(const string& str)
{
int32_t result;
if (str.find_first_of("x") != string::npos) {
result = strtoul(str.c_str(), NULL, 16);
if (result >= 16) {
cerr << "Immediate out of range: " << str << endl;
result = -1;
}
} else if (str.find_first_of("<<") != string::npos) {
uint32_t shift = strtoul(str.c_str() + 2, NULL, 10);
result = (48 + shift);
} else if (str.find_first_of("-") != string::npos) {
uint32_t value = strtoul(str.c_str() + 1, NULL, 10);
if ((value < 1) || (value > 16)) {
cerr << "Negative immediate out of range: " << str << endl;
result = -1;
} else {
result = (32 + value);
}
} else {
result = strtoul(str.c_str(), NULL, 10);
if (result >= 16) {
cerr << "Immediate out of range: " << str << endl;
result = -1;
}
}
return result;
}
uint8_t parseBranchCond(const string& str)
{
if (str == "zf") // all z flags set ("z full")
return 0x0;
if (str == "ze") // all z flags clear ("z empty")
return 0x1;
if (str == "zs") // any z flags set ("z set")
return 0x2;
if (str == "zc") // any z flags clear ("z clear")
return 0x3;
if (str == "nf") // all N flags set ("N full")
return 0x4;
if (str == "ne") // all N flags clear ("N empty")
return 0x5;
if (str == "ns") // any N flags set ("N set")
return 0x6;
if (str == "nc") // any N flags clear ("N clear")
return 0x7;
if (str == "cf") // all C flags set ("C full")
return 0x8;
if (str == "ce") // all C flags clear ("C empty")
return 0x9;
if (str == "cs")