mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Always use braces with InsertBraces: true in .clang-format
This commit is contained in:
@@ -61,6 +61,7 @@ IndentPPDirectives: BeforeHash
|
|||||||
IndentRequires: true
|
IndentRequires: true
|
||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
IndentWrappedFunctionNames: true
|
IndentWrappedFunctionNames: true
|
||||||
|
InsertBraces: true
|
||||||
InsertNewlineAtEOF: true
|
InsertNewlineAtEOF: true
|
||||||
# Only support for Javascript as of clang-format 14...
|
# Only support for Javascript as of clang-format 14...
|
||||||
# InsertTrailingCommas: Wrapped
|
# InsertTrailingCommas: Wrapped
|
||||||
|
|||||||
@@ -52,16 +52,19 @@ bool charmap_ForEach(
|
|||||||
auto [nodeIdx, mapping] = std::move(prefixes.top());
|
auto [nodeIdx, mapping] = std::move(prefixes.top());
|
||||||
prefixes.pop();
|
prefixes.pop();
|
||||||
CharmapNode const &node = charmap.nodes[nodeIdx];
|
CharmapNode const &node = charmap.nodes[nodeIdx];
|
||||||
if (node.isTerminal())
|
if (node.isTerminal()) {
|
||||||
mappings[nodeIdx] = mapping;
|
mappings[nodeIdx] = mapping;
|
||||||
|
}
|
||||||
for (unsigned c = 0; c < 256; c++) {
|
for (unsigned c = 0; c < 256; c++) {
|
||||||
if (size_t nextIdx = node.next[c]; nextIdx)
|
if (size_t nextIdx = node.next[c]; nextIdx) {
|
||||||
prefixes.push({nextIdx, mapping + static_cast<char>(c)});
|
prefixes.push({nextIdx, mapping + static_cast<char>(c)});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mapFunc(charmap.name);
|
mapFunc(charmap.name);
|
||||||
for (auto [nodeIdx, mapping] : mappings)
|
for (auto [nodeIdx, mapping] : mappings) {
|
||||||
charFunc(mapping, charmap.nodes[nodeIdx].value);
|
charFunc(mapping, charmap.nodes[nodeIdx].value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return !charmapList.empty();
|
return !charmapList.empty();
|
||||||
}
|
}
|
||||||
@@ -70,10 +73,11 @@ void charmap_New(std::string const &name, std::string const *baseName) {
|
|||||||
size_t baseIdx = SIZE_MAX;
|
size_t baseIdx = SIZE_MAX;
|
||||||
|
|
||||||
if (baseName != nullptr) {
|
if (baseName != nullptr) {
|
||||||
if (auto search = charmapMap.find(*baseName); search == charmapMap.end())
|
if (auto search = charmapMap.find(*baseName); search == charmapMap.end()) {
|
||||||
error("Base charmap '%s' doesn't exist\n", baseName->c_str());
|
error("Base charmap '%s' doesn't exist\n", baseName->c_str());
|
||||||
else
|
} else {
|
||||||
baseIdx = search->second;
|
baseIdx = search->second;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charmapMap.find(name) != charmapMap.end()) {
|
if (charmapMap.find(name) != charmapMap.end()) {
|
||||||
@@ -85,10 +89,11 @@ void charmap_New(std::string const &name, std::string const *baseName) {
|
|||||||
charmapMap[name] = charmapList.size();
|
charmapMap[name] = charmapList.size();
|
||||||
Charmap &charmap = charmapList.emplace_back();
|
Charmap &charmap = charmapList.emplace_back();
|
||||||
|
|
||||||
if (baseIdx != SIZE_MAX)
|
if (baseIdx != SIZE_MAX) {
|
||||||
charmap.nodes = charmapList[baseIdx].nodes; // Copies `charmapList[baseIdx].nodes`
|
charmap.nodes = charmapList[baseIdx].nodes; // Copies `charmapList[baseIdx].nodes`
|
||||||
else
|
} else {
|
||||||
charmap.nodes.emplace_back(); // Zero-init the root node
|
charmap.nodes.emplace_back(); // Zero-init the root node
|
||||||
|
}
|
||||||
|
|
||||||
charmap.name = name;
|
charmap.name = name;
|
||||||
|
|
||||||
@@ -96,10 +101,11 @@ void charmap_New(std::string const &name, std::string const *baseName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Set(std::string const &name) {
|
void charmap_Set(std::string const &name) {
|
||||||
if (auto search = charmapMap.find(name); search == charmapMap.end())
|
if (auto search = charmapMap.find(name); search == charmapMap.end()) {
|
||||||
error("Charmap '%s' doesn't exist\n", name.c_str());
|
error("Charmap '%s' doesn't exist\n", name.c_str());
|
||||||
else
|
} else {
|
||||||
currentCharmap = &charmapList[search->second];
|
currentCharmap = &charmapList[search->second];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Push() {
|
void charmap_Push() {
|
||||||
@@ -149,8 +155,9 @@ void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
|
|||||||
|
|
||||||
CharmapNode &node = charmap.nodes[nodeIdx];
|
CharmapNode &node = charmap.nodes[nodeIdx];
|
||||||
|
|
||||||
if (node.isTerminal())
|
if (node.isTerminal()) {
|
||||||
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping\n");
|
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping\n");
|
||||||
|
}
|
||||||
|
|
||||||
std::swap(node.value, value);
|
std::swap(node.value, value);
|
||||||
}
|
}
|
||||||
@@ -162,8 +169,9 @@ bool charmap_HasChar(std::string const &input) {
|
|||||||
for (char c : input) {
|
for (char c : input) {
|
||||||
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
||||||
|
|
||||||
if (!nodeIdx)
|
if (!nodeIdx) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return charmap.nodes[nodeIdx].isTerminal();
|
return charmap.nodes[nodeIdx].isTerminal();
|
||||||
@@ -188,8 +196,9 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
for (size_t nodeIdx = 0; inputIdx < input.length();) {
|
for (size_t nodeIdx = 0; inputIdx < input.length();) {
|
||||||
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(input[inputIdx])];
|
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(input[inputIdx])];
|
||||||
|
|
||||||
if (!nodeIdx)
|
if (!nodeIdx) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
inputIdx++; // Consume that char
|
inputIdx++; // Consume that char
|
||||||
|
|
||||||
@@ -209,8 +218,9 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
if (matchIdx) { // A match was found, use it
|
if (matchIdx) { // A match was found, use it
|
||||||
std::vector<int32_t> const &value = charmap.nodes[matchIdx].value;
|
std::vector<int32_t> const &value = charmap.nodes[matchIdx].value;
|
||||||
|
|
||||||
if (output)
|
if (output) {
|
||||||
output->insert(output->end(), RANGE(value));
|
output->insert(output->end(), RANGE(value));
|
||||||
|
}
|
||||||
|
|
||||||
matchLen = value.size();
|
matchLen = value.size();
|
||||||
} else if (inputIdx < input.length()) { // No match found, but there is some input left
|
} else if (inputIdx < input.length()) { // No match found, but there is some input left
|
||||||
@@ -218,18 +228,20 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
|||||||
// This will write the codepoint's value to `output`, little-endian
|
// This will write the codepoint's value to `output`, little-endian
|
||||||
size_t codepointLen = readUTF8Char(output, input.data() + inputIdx);
|
size_t codepointLen = readUTF8Char(output, input.data() + inputIdx);
|
||||||
|
|
||||||
if (codepointLen == 0)
|
if (codepointLen == 0) {
|
||||||
error("Input string is not valid UTF-8\n");
|
error("Input string is not valid UTF-8\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Warn if this character is not mapped but any others are
|
// Warn if this character is not mapped but any others are
|
||||||
if (charmap.nodes.size() > 1)
|
if (charmap.nodes.size() > 1) {
|
||||||
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s\n", printChar(firstChar));
|
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s\n", printChar(firstChar));
|
||||||
else if (charmap.name != DEFAULT_CHARMAP_NAME)
|
} else if (charmap.name != DEFAULT_CHARMAP_NAME) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_UNMAPPED_CHAR_2,
|
WARNING_UNMAPPED_CHAR_2,
|
||||||
"Unmapped character %s not in " DEFAULT_CHARMAP_NAME " charmap\n",
|
"Unmapped character %s not in " DEFAULT_CHARMAP_NAME " charmap\n",
|
||||||
printChar(firstChar)
|
printChar(firstChar)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
inputIdx += codepointLen;
|
inputIdx += codepointLen;
|
||||||
matchLen = codepointLen;
|
matchLen = codepointLen;
|
||||||
|
|||||||
@@ -25,10 +25,12 @@ static double fix2double(int32_t i, int32_t q) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int32_t double2fix(double d, int32_t q) {
|
static int32_t double2fix(double d, int32_t q) {
|
||||||
if (isnan(d))
|
if (isnan(d)) {
|
||||||
return 0;
|
return 0;
|
||||||
if (isinf(d))
|
}
|
||||||
|
if (isinf(d)) {
|
||||||
return d < 0 ? INT32_MIN : INT32_MAX;
|
return d < 0 ? INT32_MIN : INT32_MAX;
|
||||||
|
}
|
||||||
return static_cast<int32_t>(round(d * pow(2.0, q)));
|
return static_cast<int32_t>(round(d * pow(2.0, q)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +77,9 @@ int32_t fix_Mul(int32_t i, int32_t j, int32_t q) {
|
|||||||
int32_t fix_Div(int32_t i, int32_t j, int32_t q) {
|
int32_t fix_Div(int32_t i, int32_t j, int32_t q) {
|
||||||
double dividend = fix2double(i, q);
|
double dividend = fix2double(i, q);
|
||||||
double divisor = fix2double(j, q);
|
double divisor = fix2double(j, q);
|
||||||
if (fpclassify(divisor) == FP_ZERO)
|
if (fpclassify(divisor) == FP_ZERO) {
|
||||||
return dividend < 0 ? INT32_MIN : dividend > 0 ? INT32_MAX : 0;
|
return dividend < 0 ? INT32_MIN : dividend > 0 ? INT32_MAX : 0;
|
||||||
|
}
|
||||||
return double2fix(dividend / divisor, q);
|
return double2fix(dividend / divisor, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,8 +93,9 @@ int32_t fix_Pow(int32_t i, int32_t j, int32_t q) {
|
|||||||
|
|
||||||
int32_t fix_Log(int32_t i, int32_t j, int32_t q) {
|
int32_t fix_Log(int32_t i, int32_t j, int32_t q) {
|
||||||
double divisor = log(fix2double(j, q));
|
double divisor = log(fix2double(j, q));
|
||||||
if (fpclassify(divisor) == FP_ZERO)
|
if (fpclassify(divisor) == FP_ZERO) {
|
||||||
return INT32_MAX;
|
return INT32_MAX;
|
||||||
|
}
|
||||||
return double2fix(log(fix2double(i, q)) / divisor, q);
|
return double2fix(log(fix2double(i, q)) / divisor, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,39 +13,44 @@
|
|||||||
#include "asm/warning.hpp"
|
#include "asm/warning.hpp"
|
||||||
|
|
||||||
void FormatSpec::useCharacter(int c) {
|
void FormatSpec::useCharacter(int c) {
|
||||||
if (state == FORMAT_INVALID)
|
if (state == FORMAT_INVALID) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
// sign
|
// sign
|
||||||
case ' ':
|
case ' ':
|
||||||
case '+':
|
case '+':
|
||||||
if (state > FORMAT_SIGN)
|
if (state > FORMAT_SIGN) {
|
||||||
goto invalid;
|
goto invalid;
|
||||||
|
}
|
||||||
state = FORMAT_EXACT;
|
state = FORMAT_EXACT;
|
||||||
sign = c;
|
sign = c;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// exact
|
// exact
|
||||||
case '#':
|
case '#':
|
||||||
if (state > FORMAT_EXACT)
|
if (state > FORMAT_EXACT) {
|
||||||
goto invalid;
|
goto invalid;
|
||||||
|
}
|
||||||
state = FORMAT_ALIGN;
|
state = FORMAT_ALIGN;
|
||||||
exact = true;
|
exact = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// align
|
// align
|
||||||
case '-':
|
case '-':
|
||||||
if (state > FORMAT_ALIGN)
|
if (state > FORMAT_ALIGN) {
|
||||||
goto invalid;
|
goto invalid;
|
||||||
|
}
|
||||||
state = FORMAT_WIDTH;
|
state = FORMAT_WIDTH;
|
||||||
alignLeft = true;
|
alignLeft = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// pad, width, and prec values
|
// pad, width, and prec values
|
||||||
case '0':
|
case '0':
|
||||||
if (state < FORMAT_WIDTH)
|
if (state < FORMAT_WIDTH) {
|
||||||
padZero = true;
|
padZero = true;
|
||||||
|
}
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case '1':
|
case '1':
|
||||||
case '2':
|
case '2':
|
||||||
@@ -72,16 +77,18 @@ void FormatSpec::useCharacter(int c) {
|
|||||||
|
|
||||||
// width
|
// width
|
||||||
case '.':
|
case '.':
|
||||||
if (state > FORMAT_WIDTH)
|
if (state > FORMAT_WIDTH) {
|
||||||
goto invalid;
|
goto invalid;
|
||||||
|
}
|
||||||
state = FORMAT_FRAC;
|
state = FORMAT_FRAC;
|
||||||
hasFrac = true;
|
hasFrac = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// prec
|
// prec
|
||||||
case 'q':
|
case 'q':
|
||||||
if (state > FORMAT_PREC)
|
if (state > FORMAT_PREC) {
|
||||||
goto invalid;
|
goto invalid;
|
||||||
|
}
|
||||||
state = FORMAT_PREC;
|
state = FORMAT_PREC;
|
||||||
hasPrec = true;
|
hasPrec = true;
|
||||||
break;
|
break;
|
||||||
@@ -95,8 +102,9 @@ void FormatSpec::useCharacter(int c) {
|
|||||||
case 'o':
|
case 'o':
|
||||||
case 'f':
|
case 'f':
|
||||||
case 's':
|
case 's':
|
||||||
if (state >= FORMAT_DONE)
|
if (state >= FORMAT_DONE) {
|
||||||
goto invalid;
|
goto invalid;
|
||||||
|
}
|
||||||
state = FORMAT_DONE;
|
state = FORMAT_DONE;
|
||||||
valid = true;
|
valid = true;
|
||||||
type = c;
|
type = c;
|
||||||
@@ -110,8 +118,9 @@ invalid:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FormatSpec::finishCharacters() {
|
void FormatSpec::finishCharacters() {
|
||||||
if (!isValid())
|
if (!isValid()) {
|
||||||
state = FORMAT_INVALID;
|
state = FORMAT_INVALID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string escapeString(std::string const &str) {
|
static std::string escapeString(std::string const &str) {
|
||||||
@@ -151,16 +160,21 @@ void FormatSpec::appendString(std::string &str, std::string const &value) const
|
|||||||
useType = 's';
|
useType = 's';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sign)
|
if (sign) {
|
||||||
error("Formatting string with sign flag '%c'\n", sign);
|
error("Formatting string with sign flag '%c'\n", sign);
|
||||||
if (padZero)
|
}
|
||||||
|
if (padZero) {
|
||||||
error("Formatting string with padding flag '0'\n");
|
error("Formatting string with padding flag '0'\n");
|
||||||
if (hasFrac)
|
}
|
||||||
|
if (hasFrac) {
|
||||||
error("Formatting string with fractional width\n");
|
error("Formatting string with fractional width\n");
|
||||||
if (hasPrec)
|
}
|
||||||
|
if (hasPrec) {
|
||||||
error("Formatting string with fractional precision\n");
|
error("Formatting string with fractional precision\n");
|
||||||
if (useType != 's')
|
}
|
||||||
|
if (useType != 's') {
|
||||||
error("Formatting string as type '%c'\n", useType);
|
error("Formatting string as type '%c'\n", useType);
|
||||||
|
}
|
||||||
|
|
||||||
std::string useValue = exact ? escapeString(value) : value;
|
std::string useValue = exact ? escapeString(value) : value;
|
||||||
size_t valueLen = useValue.length();
|
size_t valueLen = useValue.length();
|
||||||
@@ -187,22 +201,27 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f'
|
if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f'
|
||||||
&& useExact)
|
&& useExact) {
|
||||||
error("Formatting type '%c' with exact flag '#'\n", useType);
|
error("Formatting type '%c' with exact flag '#'\n", useType);
|
||||||
if (useType != 'f' && hasFrac)
|
}
|
||||||
|
if (useType != 'f' && hasFrac) {
|
||||||
error("Formatting type '%c' with fractional width\n", useType);
|
error("Formatting type '%c' with fractional width\n", useType);
|
||||||
if (useType != 'f' && hasPrec)
|
}
|
||||||
|
if (useType != 'f' && hasPrec) {
|
||||||
error("Formatting type '%c' with fractional precision\n", useType);
|
error("Formatting type '%c' with fractional precision\n", useType);
|
||||||
if (useType == 's')
|
}
|
||||||
|
if (useType == 's') {
|
||||||
error("Formatting number as type 's'\n");
|
error("Formatting number as type 's'\n");
|
||||||
|
}
|
||||||
|
|
||||||
char signChar = sign; // 0 or ' ' or '+'
|
char signChar = sign; // 0 or ' ' or '+'
|
||||||
|
|
||||||
if (useType == 'd' || useType == 'f') {
|
if (useType == 'd' || useType == 'f') {
|
||||||
if (int32_t v = value; v < 0) {
|
if (int32_t v = value; v < 0) {
|
||||||
signChar = '-';
|
signChar = '-';
|
||||||
if (v != INT32_MIN)
|
if (v != INT32_MIN) {
|
||||||
value = -v;
|
value = -v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,10 +269,11 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double fval = fabs(value / pow(2.0, usePrec));
|
double fval = fabs(value / pow(2.0, usePrec));
|
||||||
if (int fracWidthArg = static_cast<int>(useFracWidth); useExact)
|
if (int fracWidthArg = static_cast<int>(useFracWidth); useExact) {
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "%.*fq%zu", fracWidthArg, fval, usePrec);
|
snprintf(valueBuf, sizeof(valueBuf), "%.*fq%zu", fracWidthArg, fval, usePrec);
|
||||||
else
|
} else {
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "%.*f", fracWidthArg, fval);
|
snprintf(valueBuf, sizeof(valueBuf), "%.*f", fracWidthArg, fval);
|
||||||
|
}
|
||||||
} else if (useType == 'd') {
|
} else if (useType == 'd') {
|
||||||
// Decimal numbers may be formatted with a '-' sign by `snprintf`, so `abs` prevents that,
|
// Decimal numbers may be formatted with a '-' sign by `snprintf`, so `abs` prevents that,
|
||||||
// with a special case for `INT32_MIN` since `labs(INT32_MIN)` is UB. The sign will be
|
// with a special case for `INT32_MIN` since `labs(INT32_MIN)` is UB. The sign will be
|
||||||
@@ -278,27 +298,33 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
|||||||
|
|
||||||
str.reserve(str.length() + totalLen);
|
str.reserve(str.length() + totalLen);
|
||||||
if (alignLeft) {
|
if (alignLeft) {
|
||||||
if (signChar)
|
if (signChar) {
|
||||||
str += signChar;
|
str += signChar;
|
||||||
if (prefixChar)
|
}
|
||||||
|
if (prefixChar) {
|
||||||
str += prefixChar;
|
str += prefixChar;
|
||||||
|
}
|
||||||
str.append(valueBuf);
|
str.append(valueBuf);
|
||||||
str.append(padLen, ' ');
|
str.append(padLen, ' ');
|
||||||
} else {
|
} else {
|
||||||
if (padZero) {
|
if (padZero) {
|
||||||
// sign, then prefix, then zero padding
|
// sign, then prefix, then zero padding
|
||||||
if (signChar)
|
if (signChar) {
|
||||||
str += signChar;
|
str += signChar;
|
||||||
if (prefixChar)
|
}
|
||||||
|
if (prefixChar) {
|
||||||
str += prefixChar;
|
str += prefixChar;
|
||||||
|
}
|
||||||
str.append(padLen, '0');
|
str.append(padLen, '0');
|
||||||
} else {
|
} else {
|
||||||
// space padding, then sign, then prefix
|
// space padding, then sign, then prefix
|
||||||
str.append(padLen, ' ');
|
str.append(padLen, ' ');
|
||||||
if (signChar)
|
if (signChar) {
|
||||||
str += signChar;
|
str += signChar;
|
||||||
if (prefixChar)
|
}
|
||||||
|
if (prefixChar) {
|
||||||
str += prefixChar;
|
str += prefixChar;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
str.append(valueBuf);
|
str.append(valueBuf);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,8 +90,9 @@ std::shared_ptr<std::string> fstk_GetUniqueIDStr() {
|
|||||||
std::shared_ptr<std::string> &str = contextStack.top().uniqueIDStr;
|
std::shared_ptr<std::string> &str = contextStack.top().uniqueIDStr;
|
||||||
|
|
||||||
// If a unique ID is allowed but has not been generated yet, generate one now.
|
// If a unique ID is allowed but has not been generated yet, generate one now.
|
||||||
if (str && str->empty())
|
if (str && str->empty()) {
|
||||||
*str = "_u"s + std::to_string(nextUniqueID++);
|
*str = "_u"s + std::to_string(nextUniqueID++);
|
||||||
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
@@ -103,27 +104,32 @@ MacroArgs *fstk_GetCurrentMacroArgs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fstk_AddIncludePath(std::string const &path) {
|
void fstk_AddIncludePath(std::string const &path) {
|
||||||
if (path.empty())
|
if (path.empty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string &includePath = includePaths.emplace_back(path);
|
std::string &includePath = includePaths.emplace_back(path);
|
||||||
if (includePath.back() != '/')
|
if (includePath.back() != '/') {
|
||||||
includePath += '/';
|
includePath += '/';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_SetPreIncludeFile(std::string const &path) {
|
void fstk_SetPreIncludeFile(std::string const &path) {
|
||||||
if (!preIncludeName.empty())
|
if (!preIncludeName.empty()) {
|
||||||
warnx("Overriding pre-included filename %s", preIncludeName.c_str());
|
warnx("Overriding pre-included filename %s", preIncludeName.c_str());
|
||||||
|
}
|
||||||
preIncludeName = path;
|
preIncludeName = path;
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("Pre-included filename %s\n", preIncludeName.c_str());
|
printf("Pre-included filename %s\n", preIncludeName.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printDep(std::string const &path) {
|
static void printDep(std::string const &path) {
|
||||||
if (dependFile) {
|
if (dependFile) {
|
||||||
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), path.c_str());
|
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), path.c_str());
|
||||||
if (generatePhonyDeps)
|
if (generatePhonyDeps) {
|
||||||
fprintf(dependFile, "%s:\n", path.c_str());
|
fprintf(dependFile, "%s:\n", path.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,20 +147,22 @@ std::optional<std::string> fstk_FindFile(std::string const &path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
if (generatedMissingIncludes)
|
if (generatedMissingIncludes) {
|
||||||
printDep(path);
|
printDep(path);
|
||||||
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool yywrap() {
|
bool yywrap() {
|
||||||
uint32_t ifDepth = lexer_GetIFDepth();
|
uint32_t ifDepth = lexer_GetIFDepth();
|
||||||
|
|
||||||
if (ifDepth != 0)
|
if (ifDepth != 0) {
|
||||||
fatalerror(
|
fatalerror(
|
||||||
"Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
"Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
||||||
ifDepth,
|
ifDepth,
|
||||||
ifDepth == 1 ? "" : "s"
|
ifDepth == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (Context &context = contextStack.top(); context.fileInfo->type == NODE_REPT) {
|
if (Context &context = contextStack.top(); context.fileInfo->type == NODE_REPT) {
|
||||||
// The context is a REPT or FOR block, which may loop
|
// The context is a REPT or FOR block, which may loop
|
||||||
@@ -177,8 +185,9 @@ bool yywrap() {
|
|||||||
Symbol *sym = sym_AddVar(context.forName, context.forValue);
|
Symbol *sym = sym_AddVar(context.forName, context.forValue);
|
||||||
|
|
||||||
// This error message will refer to the current iteration
|
// This error message will refer to the current iteration
|
||||||
if (sym->type != SYM_VAR)
|
if (sym->type != SYM_VAR) {
|
||||||
fatalerror("Failed to update FOR symbol value\n");
|
fatalerror("Failed to update FOR symbol value\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Advance to the next iteration
|
// Advance to the next iteration
|
||||||
fileInfoIters.front()++;
|
fileInfoIters.front()++;
|
||||||
@@ -199,8 +208,9 @@ bool yywrap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void checkRecursionDepth() {
|
static void checkRecursionDepth() {
|
||||||
if (contextStack.size() > maxRecursionDepth)
|
if (contextStack.size() > maxRecursionDepth) {
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool newFileContext(std::string const &filePath, bool updateStateNow) {
|
static bool newFileContext(std::string const &filePath, bool updateStateNow) {
|
||||||
@@ -298,8 +308,9 @@ void fstk_RunInclude(std::string const &path, bool preInclude) {
|
|||||||
|
|
||||||
if (!fullPath) {
|
if (!fullPath) {
|
||||||
if (generatedMissingIncludes && !preInclude) {
|
if (generatedMissingIncludes && !preInclude) {
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n", path.c_str(), strerror(errno));
|
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n", path.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
failedOnMissingInclude = true;
|
failedOnMissingInclude = true;
|
||||||
} else {
|
} else {
|
||||||
error("Unable to open included file '%s': %s\n", path.c_str(), strerror(errno));
|
error("Unable to open included file '%s': %s\n", path.c_str(), strerror(errno));
|
||||||
@@ -307,18 +318,20 @@ void fstk_RunInclude(std::string const &path, bool preInclude) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newFileContext(*fullPath, false))
|
if (!newFileContext(*fullPath, false)) {
|
||||||
fatalerror("Failed to set up lexer for file include\n");
|
fatalerror("Failed to set up lexer for file include\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs) {
|
void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs) {
|
||||||
Symbol *macro = sym_FindExactSymbol(macroName);
|
Symbol *macro = sym_FindExactSymbol(macroName);
|
||||||
|
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
if (sym_IsPurgedExact(macroName))
|
if (sym_IsPurgedExact(macroName)) {
|
||||||
error("Macro \"%s\" not defined; it was purged\n", macroName.c_str());
|
error("Macro \"%s\" not defined; it was purged\n", macroName.c_str());
|
||||||
else
|
} else {
|
||||||
error("Macro \"%s\" not defined\n", macroName.c_str());
|
error("Macro \"%s\" not defined\n", macroName.c_str());
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (macro->type != SYM_MACRO) {
|
if (macro->type != SYM_MACRO) {
|
||||||
@@ -330,8 +343,9 @@ void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macr
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span) {
|
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span) {
|
||||||
if (count == 0)
|
if (count == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
newReptContext(reptLineNo, span, count);
|
newReptContext(reptLineNo, span, count);
|
||||||
}
|
}
|
||||||
@@ -344,24 +358,28 @@ void fstk_RunFor(
|
|||||||
int32_t reptLineNo,
|
int32_t reptLineNo,
|
||||||
ContentSpan const &span
|
ContentSpan const &span
|
||||||
) {
|
) {
|
||||||
if (Symbol *sym = sym_AddVar(symName, start); sym->type != SYM_VAR)
|
if (Symbol *sym = sym_AddVar(symName, start); sym->type != SYM_VAR) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
if (step > 0 && start < stop)
|
if (step > 0 && start < stop) {
|
||||||
count = (static_cast<int64_t>(stop) - start - 1) / step + 1;
|
count = (static_cast<int64_t>(stop) - start - 1) / step + 1;
|
||||||
else if (step < 0 && stop < start)
|
} else if (step < 0 && stop < start) {
|
||||||
count = (static_cast<int64_t>(start) - stop - 1) / -static_cast<int64_t>(step) + 1;
|
count = (static_cast<int64_t>(start) - stop - 1) / -static_cast<int64_t>(step) + 1;
|
||||||
else if (step == 0)
|
} else if (step == 0) {
|
||||||
error("FOR cannot have a step value of 0\n");
|
error("FOR cannot have a step value of 0\n");
|
||||||
|
}
|
||||||
|
|
||||||
if ((step > 0 && start > stop) || (step < 0 && start < stop))
|
if ((step > 0 && start > stop) || (step < 0 && start < stop)) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n", start, stop, step
|
WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n", start, stop, step
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Context &context = newReptContext(reptLineNo, span, count);
|
Context &context = newReptContext(reptLineNo, span, count);
|
||||||
context.isForLoop = true;
|
context.isForLoop = true;
|
||||||
@@ -385,17 +403,20 @@ bool fstk_Break() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fstk_NewRecursionDepth(size_t newDepth) {
|
void fstk_NewRecursionDepth(size_t newDepth) {
|
||||||
if (contextStack.size() > newDepth + 1)
|
if (contextStack.size() > newDepth + 1) {
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", newDepth);
|
fatalerror("Recursion limit (%zu) exceeded\n", newDepth);
|
||||||
|
}
|
||||||
maxRecursionDepth = newDepth;
|
maxRecursionDepth = newDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_Init(std::string const &mainPath, size_t maxDepth) {
|
void fstk_Init(std::string const &mainPath, size_t maxDepth) {
|
||||||
if (!newFileContext(mainPath, true))
|
if (!newFileContext(mainPath, true)) {
|
||||||
fatalerror("Failed to open main file\n");
|
fatalerror("Failed to open main file\n");
|
||||||
|
}
|
||||||
|
|
||||||
maxRecursionDepth = maxDepth;
|
maxRecursionDepth = maxDepth;
|
||||||
|
|
||||||
if (!preIncludeName.empty())
|
if (!preIncludeName.empty()) {
|
||||||
fstk_RunInclude(preIncludeName, true);
|
fstk_RunInclude(preIncludeName, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,8 +81,9 @@ static char *mapFile(int fd, std::string const &path, size_t size) {
|
|||||||
// The implementation may not support MAP_PRIVATE; try again with MAP_SHARED
|
// The implementation may not support MAP_PRIVATE; try again with MAP_SHARED
|
||||||
// instead, offering, I believe, weaker guarantees about external modifications to
|
// instead, offering, I believe, weaker guarantees about external modifications to
|
||||||
// the file while reading it. That's still better than not opening it at all, though.
|
// the file while reading it. That's still better than not opening it at all, though.
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("mmap(%s, MAP_PRIVATE) failed, retrying with MAP_SHARED\n", path.c_str());
|
printf("mmap(%s, MAP_PRIVATE) failed, retrying with MAP_SHARED\n", path.c_str());
|
||||||
|
}
|
||||||
mappingAddr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
mappingAddr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
}
|
}
|
||||||
return mappingAddr != MAP_FAILED ? static_cast<char *>(mappingAddr) : nullptr;
|
return mappingAddr != MAP_FAILED ? static_cast<char *>(mappingAddr) : nullptr;
|
||||||
@@ -119,8 +120,9 @@ struct CaseInsensitive {
|
|||||||
size_t operator()(std::string const &str) const {
|
size_t operator()(std::string const &str) const {
|
||||||
size_t hash = 0x811C9DC5;
|
size_t hash = 0x811C9DC5;
|
||||||
|
|
||||||
for (char const &c : str)
|
for (char const &c : str) {
|
||||||
hash = (hash ^ toupper(c)) * 16777619;
|
hash = (hash ^ toupper(c)) * 16777619;
|
||||||
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,8 +377,9 @@ void lexer_IncIFDepth() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lexer_DecIFDepth() {
|
void lexer_DecIFDepth() {
|
||||||
if (lexerState->ifStack.empty())
|
if (lexerState->ifStack.empty()) {
|
||||||
fatalerror("Found ENDC outside of an IF construct\n");
|
fatalerror("Found ENDC outside of an IF construct\n");
|
||||||
|
}
|
||||||
|
|
||||||
lexerState->ifStack.pop_front();
|
lexerState->ifStack.pop_front();
|
||||||
}
|
}
|
||||||
@@ -405,8 +408,9 @@ bool LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
|
|||||||
if (filePath == "-") {
|
if (filePath == "-") {
|
||||||
path = "<stdin>";
|
path = "<stdin>";
|
||||||
content.emplace<BufferedContent>(STDIN_FILENO);
|
content.emplace<BufferedContent>(STDIN_FILENO);
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("Opening stdin\n");
|
printf("Opening stdin\n");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
struct stat statBuf;
|
struct stat statBuf;
|
||||||
if (stat(filePath.c_str(), &statBuf) != 0) {
|
if (stat(filePath.c_str(), &statBuf) != 0) {
|
||||||
@@ -430,8 +434,9 @@ bool LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
|
|||||||
content.emplace<ViewedContent>(
|
content.emplace<ViewedContent>(
|
||||||
std::shared_ptr<char[]>(mappingAddr, FileUnmapDeleter(size)), size
|
std::shared_ptr<char[]>(mappingAddr, FileUnmapDeleter(size)), size
|
||||||
);
|
);
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("File \"%s\" is mmap()ped\n", path.c_str());
|
printf("File \"%s\" is mmap()ped\n", path.c_str());
|
||||||
|
}
|
||||||
isMmapped = true;
|
isMmapped = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,10 +457,11 @@ bool LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
clear(0);
|
clear(0);
|
||||||
if (updateStateNow)
|
if (updateStateNow) {
|
||||||
lexerState = this;
|
lexerState = this;
|
||||||
else
|
} else {
|
||||||
lexerStateEOL = this;
|
lexerStateEOL = this;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,8 +507,9 @@ BufferedContent::~BufferedContent() {
|
|||||||
void BufferedContent::advance() {
|
void BufferedContent::advance() {
|
||||||
assume(offset < std::size(buf));
|
assume(offset < std::size(buf));
|
||||||
offset++;
|
offset++;
|
||||||
if (offset == std::size(buf))
|
if (offset == std::size(buf)) {
|
||||||
offset = 0; // Wrap around if necessary
|
offset = 0; // Wrap around if necessary
|
||||||
|
}
|
||||||
assume(size > 0);
|
assume(size > 0);
|
||||||
size--;
|
size--;
|
||||||
}
|
}
|
||||||
@@ -519,16 +526,19 @@ void BufferedContent::refill() {
|
|||||||
size_t nbReadChars = readMore(startIndex, nbExpectedChars);
|
size_t nbReadChars = readMore(startIndex, nbExpectedChars);
|
||||||
|
|
||||||
startIndex += nbReadChars;
|
startIndex += nbReadChars;
|
||||||
if (startIndex == std::size(buf))
|
if (startIndex == std::size(buf)) {
|
||||||
startIndex = 0;
|
startIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// If the read was incomplete, don't perform a second read
|
// If the read was incomplete, don't perform a second read
|
||||||
target -= nbReadChars;
|
target -= nbReadChars;
|
||||||
if (nbReadChars < nbExpectedChars)
|
if (nbReadChars < nbExpectedChars) {
|
||||||
target = 0;
|
target = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (target != 0)
|
if (target != 0) {
|
||||||
readMore(startIndex, target);
|
readMore(startIndex, target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t BufferedContent::readMore(size_t startIndex, size_t nbChars) {
|
size_t BufferedContent::readMore(size_t startIndex, size_t nbChars) {
|
||||||
@@ -536,8 +546,9 @@ size_t BufferedContent::readMore(size_t startIndex, size_t nbChars) {
|
|||||||
assume(startIndex + nbChars <= std::size(buf));
|
assume(startIndex + nbChars <= std::size(buf));
|
||||||
ssize_t nbReadChars = read(fd, &buf[startIndex], nbChars);
|
ssize_t nbReadChars = read(fd, &buf[startIndex], nbChars);
|
||||||
|
|
||||||
if (nbReadChars == -1)
|
if (nbReadChars == -1) {
|
||||||
fatalerror("Error while reading \"%s\": %s\n", lexerState->path.c_str(), strerror(errno));
|
fatalerror("Error while reading \"%s\": %s\n", lexerState->path.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
size += nbReadChars;
|
size += nbReadChars;
|
||||||
|
|
||||||
@@ -556,19 +567,22 @@ void lexer_ToggleStringExpansion(bool enable) {
|
|||||||
// Functions for the actual lexer to obtain characters
|
// Functions for the actual lexer to obtain characters
|
||||||
|
|
||||||
static void beginExpansion(std::shared_ptr<std::string> str, std::optional<std::string> name) {
|
static void beginExpansion(std::shared_ptr<std::string> str, std::optional<std::string> name) {
|
||||||
if (name)
|
if (name) {
|
||||||
lexer_CheckRecursionDepth();
|
lexer_CheckRecursionDepth();
|
||||||
|
}
|
||||||
|
|
||||||
// Do not expand empty strings
|
// Do not expand empty strings
|
||||||
if (str->empty())
|
if (str->empty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lexerState->expansions.push_front({.name = name, .contents = str, .offset = 0});
|
lexerState->expansions.push_front({.name = name, .contents = str, .offset = 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
void lexer_CheckRecursionDepth() {
|
void lexer_CheckRecursionDepth() {
|
||||||
if (lexerState->expansions.size() > maxRecursionDepth + 1)
|
if (lexerState->expansions.size() > maxRecursionDepth + 1) {
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isMacroChar(char c) {
|
static bool isMacroChar(char c) {
|
||||||
@@ -619,10 +633,11 @@ static uint32_t readBracketedMacroArgNum() {
|
|||||||
Symbol const *sym = sym_FindScopedValidSymbol(symName);
|
Symbol const *sym = sym_FindScopedValidSymbol(symName);
|
||||||
|
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
if (sym_IsPurgedScoped(symName))
|
if (sym_IsPurgedScoped(symName)) {
|
||||||
error("Bracketed symbol \"%s\" does not exist; it was purged\n", symName.c_str());
|
error("Bracketed symbol \"%s\" does not exist; it was purged\n", symName.c_str());
|
||||||
else
|
} else {
|
||||||
error("Bracketed symbol \"%s\" does not exist\n", symName.c_str());
|
error("Bracketed symbol \"%s\" does not exist\n", symName.c_str());
|
||||||
|
}
|
||||||
num = 0;
|
num = 0;
|
||||||
symbolError = true;
|
symbolError = true;
|
||||||
} else if (!sym->isNumeric()) {
|
} else if (!sym->isNumeric()) {
|
||||||
@@ -707,21 +722,25 @@ static std::shared_ptr<std::string> readMacroArg(char name) {
|
|||||||
int LexerState::peekChar() {
|
int LexerState::peekChar() {
|
||||||
// This is `.peekCharAhead()` modified for zero lookahead distance
|
// This is `.peekCharAhead()` modified for zero lookahead distance
|
||||||
for (Expansion &exp : expansions) {
|
for (Expansion &exp : expansions) {
|
||||||
if (exp.offset < exp.size())
|
if (exp.offset < exp.size()) {
|
||||||
return static_cast<uint8_t>((*exp.contents)[exp.offset]);
|
return static_cast<uint8_t>((*exp.contents)[exp.offset]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.holds<ViewedContent>()) {
|
if (content.holds<ViewedContent>()) {
|
||||||
auto &view = content.get<ViewedContent>();
|
auto &view = content.get<ViewedContent>();
|
||||||
if (view.offset < view.span.size)
|
if (view.offset < view.span.size) {
|
||||||
return static_cast<uint8_t>(view.span.ptr[view.offset]);
|
return static_cast<uint8_t>(view.span.ptr[view.offset]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
auto &cbuf = content.get<BufferedContent>();
|
auto &cbuf = content.get<BufferedContent>();
|
||||||
if (cbuf.size == 0)
|
if (cbuf.size == 0) {
|
||||||
cbuf.refill();
|
cbuf.refill();
|
||||||
|
}
|
||||||
assume(cbuf.offset < std::size(cbuf.buf));
|
assume(cbuf.offset < std::size(cbuf.buf));
|
||||||
if (cbuf.size > 0)
|
if (cbuf.size > 0) {
|
||||||
return static_cast<uint8_t>(cbuf.buf[cbuf.offset]);
|
return static_cast<uint8_t>(cbuf.buf[cbuf.offset]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there aren't enough chars, give up
|
// If there aren't enough chars, give up
|
||||||
@@ -736,22 +755,26 @@ int LexerState::peekCharAhead() {
|
|||||||
// An expansion that has reached its end will have `exp.offset` == `exp.size()`,
|
// An expansion that has reached its end will have `exp.offset` == `exp.size()`,
|
||||||
// and `.peekCharAhead()` will continue with its parent
|
// and `.peekCharAhead()` will continue with its parent
|
||||||
assume(exp.offset <= exp.size());
|
assume(exp.offset <= exp.size());
|
||||||
if (exp.offset + distance < exp.size())
|
if (exp.offset + distance < exp.size()) {
|
||||||
return static_cast<uint8_t>((*exp.contents)[exp.offset + distance]);
|
return static_cast<uint8_t>((*exp.contents)[exp.offset + distance]);
|
||||||
|
}
|
||||||
distance -= exp.size() - exp.offset;
|
distance -= exp.size() - exp.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.holds<ViewedContent>()) {
|
if (content.holds<ViewedContent>()) {
|
||||||
auto &view = content.get<ViewedContent>();
|
auto &view = content.get<ViewedContent>();
|
||||||
if (view.offset + distance < view.span.size)
|
if (view.offset + distance < view.span.size) {
|
||||||
return static_cast<uint8_t>(view.span.ptr[view.offset + distance]);
|
return static_cast<uint8_t>(view.span.ptr[view.offset + distance]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
auto &cbuf = content.get<BufferedContent>();
|
auto &cbuf = content.get<BufferedContent>();
|
||||||
assume(distance < std::size(cbuf.buf));
|
assume(distance < std::size(cbuf.buf));
|
||||||
if (cbuf.size <= distance)
|
if (cbuf.size <= distance) {
|
||||||
cbuf.refill();
|
cbuf.refill();
|
||||||
if (cbuf.size > distance)
|
}
|
||||||
|
if (cbuf.size > distance) {
|
||||||
return static_cast<uint8_t>(cbuf.buf[(cbuf.offset + distance) % std::size(cbuf.buf)]);
|
return static_cast<uint8_t>(cbuf.buf[(cbuf.offset + distance) % std::size(cbuf.buf)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there aren't enough chars, give up
|
// If there aren't enough chars, give up
|
||||||
@@ -765,8 +788,9 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth);
|
|||||||
static int peek() {
|
static int peek() {
|
||||||
int c = lexerState->peekChar();
|
int c = lexerState->peekChar();
|
||||||
|
|
||||||
if (lexerState->macroArgScanDistance > 0)
|
if (lexerState->macroArgScanDistance > 0) {
|
||||||
return c;
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
lexerState->macroArgScanDistance++; // Do not consider again
|
lexerState->macroArgScanDistance++; // Do not consider again
|
||||||
|
|
||||||
@@ -809,8 +833,9 @@ static int peek() {
|
|||||||
|
|
||||||
static void shiftChar() {
|
static void shiftChar() {
|
||||||
if (lexerState->capturing) {
|
if (lexerState->capturing) {
|
||||||
if (lexerState->captureBuf)
|
if (lexerState->captureBuf) {
|
||||||
lexerState->captureBuf->push_back(peek());
|
lexerState->captureBuf->push_back(peek());
|
||||||
|
}
|
||||||
lexerState->captureSize++;
|
lexerState->captureSize++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -840,14 +865,16 @@ static int nextChar() {
|
|||||||
int c = peek();
|
int c = peek();
|
||||||
|
|
||||||
// If not at EOF, advance read position
|
// If not at EOF, advance read position
|
||||||
if (c != EOF)
|
if (c != EOF) {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleCRLF(int c) {
|
static void handleCRLF(int c) {
|
||||||
if (c == '\r' && peek() == '\n')
|
if (c == '\r' && peek() == '\n') {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto scopedDisableExpansions() {
|
static auto scopedDisableExpansions() {
|
||||||
@@ -870,13 +897,15 @@ uint32_t lexer_GetColNo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lexer_DumpStringExpansions() {
|
void lexer_DumpStringExpansions() {
|
||||||
if (!lexerState)
|
if (!lexerState) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (Expansion &exp : lexerState->expansions) {
|
for (Expansion &exp : lexerState->expansions) {
|
||||||
// Only register EQUS expansions, not string args
|
// Only register EQUS expansions, not string args
|
||||||
if (exp.name)
|
if (exp.name) {
|
||||||
fprintf(stderr, "while expanding symbol \"%s\"\n", exp.name->c_str());
|
fprintf(stderr, "while expanding symbol \"%s\"\n", exp.name->c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,8 +925,9 @@ static void discardBlockComment() {
|
|||||||
handleCRLF(c);
|
handleCRLF(c);
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case '\n':
|
case '\n':
|
||||||
if (lexerState->expansions.empty())
|
if (lexerState->expansions.empty()) {
|
||||||
nextLine();
|
nextLine();
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
case '/':
|
case '/':
|
||||||
if (peek() == '*') {
|
if (peek() == '*') {
|
||||||
@@ -921,8 +951,9 @@ static void discardComment() {
|
|||||||
for (;; shiftChar()) {
|
for (;; shiftChar()) {
|
||||||
int c = peek();
|
int c = peek();
|
||||||
|
|
||||||
if (c == EOF || c == '\r' || c == '\n')
|
if (c == EOF || c == '\r' || c == '\n') {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -936,8 +967,9 @@ static void discardLineContinuation() {
|
|||||||
shiftChar();
|
shiftChar();
|
||||||
// Handle CRLF before nextLine() since shiftChar updates colNo
|
// Handle CRLF before nextLine() since shiftChar updates colNo
|
||||||
handleCRLF(c);
|
handleCRLF(c);
|
||||||
if (lexerState->expansions.empty())
|
if (lexerState->expansions.empty()) {
|
||||||
nextLine();
|
nextLine();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
} else if (c == ';') {
|
} else if (c == ';') {
|
||||||
discardComment();
|
discardComment();
|
||||||
@@ -968,12 +1000,14 @@ static uint32_t readNumber(int radix, uint32_t baseValue) {
|
|||||||
for (;; shiftChar()) {
|
for (;; shiftChar()) {
|
||||||
int c = peek();
|
int c = peek();
|
||||||
|
|
||||||
if (c == '_')
|
if (c == '_') {
|
||||||
continue;
|
continue;
|
||||||
else if (c < '0' || c > '0' + radix - 1)
|
} else if (c < '0' || c > '0' + radix - 1) {
|
||||||
break;
|
break;
|
||||||
if (value > (UINT32_MAX - (c - '0')) / radix)
|
}
|
||||||
|
if (value > (UINT32_MAX - (c - '0')) / radix) {
|
||||||
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
||||||
|
}
|
||||||
value = value * radix + (c - '0');
|
value = value * radix + (c - '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1005,8 +1039,9 @@ static uint32_t readFractionalPart(uint32_t integer) {
|
|||||||
warning(WARNING_LARGE_CONSTANT, "Precision of fixed-point constant is too large\n");
|
warning(WARNING_LARGE_CONSTANT, "Precision of fixed-point constant is too large\n");
|
||||||
// Discard any additional digits
|
// Discard any additional digits
|
||||||
shiftChar();
|
shiftChar();
|
||||||
while (c = peek(), (c >= '0' && c <= '9') || c == '_')
|
while (c = peek(), (c >= '0' && c <= '9') || c == '_') {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
value = value * 10 + (c - '0');
|
value = value * 10 + (c - '0');
|
||||||
@@ -1023,16 +1058,18 @@ static uint32_t readFractionalPart(uint32_t integer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (precision == 0) {
|
if (precision == 0) {
|
||||||
if (state >= READFRACTIONALPART_PRECISION)
|
if (state >= READFRACTIONALPART_PRECISION) {
|
||||||
error("Invalid fixed-point constant, no significant digits after 'q'\n");
|
error("Invalid fixed-point constant, no significant digits after 'q'\n");
|
||||||
|
}
|
||||||
precision = fixPrecision;
|
precision = fixPrecision;
|
||||||
} else if (precision > 31) {
|
} else if (precision > 31) {
|
||||||
error("Fixed-point constant precision must be between 1 and 31\n");
|
error("Fixed-point constant precision must be between 1 and 31\n");
|
||||||
precision = fixPrecision;
|
precision = fixPrecision;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (integer >= (1ULL << (32 - precision)))
|
if (integer >= (1ULL << (32 - precision))) {
|
||||||
warning(WARNING_LARGE_CONSTANT, "Magnitude of fixed-point constant is too large\n");
|
warning(WARNING_LARGE_CONSTANT, "Magnitude of fixed-point constant is too large\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Cast to unsigned avoids undefined overflow behavior
|
// Cast to unsigned avoids undefined overflow behavior
|
||||||
uint32_t fractional =
|
uint32_t fractional =
|
||||||
@@ -1051,16 +1088,18 @@ static uint32_t readBinaryNumber() {
|
|||||||
int bit;
|
int bit;
|
||||||
|
|
||||||
// Check for '_' after digits in case one of the digits is '_'
|
// Check for '_' after digits in case one of the digits is '_'
|
||||||
if (c == binDigits[0])
|
if (c == binDigits[0]) {
|
||||||
bit = 0;
|
bit = 0;
|
||||||
else if (c == binDigits[1])
|
} else if (c == binDigits[1]) {
|
||||||
bit = 1;
|
bit = 1;
|
||||||
else if (c == '_')
|
} else if (c == '_') {
|
||||||
continue;
|
continue;
|
||||||
else
|
} else {
|
||||||
break;
|
break;
|
||||||
if (value > (UINT32_MAX - bit) / 2)
|
}
|
||||||
|
if (value > (UINT32_MAX - bit) / 2) {
|
||||||
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
||||||
|
}
|
||||||
value = value * 2 + bit;
|
value = value * 2 + bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1074,26 +1113,29 @@ static uint32_t readHexNumber() {
|
|||||||
for (;; shiftChar()) {
|
for (;; shiftChar()) {
|
||||||
int c = peek();
|
int c = peek();
|
||||||
|
|
||||||
if (c >= 'a' && c <= 'f')
|
if (c >= 'a' && c <= 'f') {
|
||||||
c = c - 'a' + 10;
|
c = c - 'a' + 10;
|
||||||
else if (c >= 'A' && c <= 'F')
|
} else if (c >= 'A' && c <= 'F') {
|
||||||
c = c - 'A' + 10;
|
c = c - 'A' + 10;
|
||||||
else if (c >= '0' && c <= '9')
|
} else if (c >= '0' && c <= '9') {
|
||||||
c = c - '0';
|
c = c - '0';
|
||||||
else if (c == '_' && !empty)
|
} else if (c == '_' && !empty) {
|
||||||
continue;
|
continue;
|
||||||
else
|
} else {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (value > (UINT32_MAX - c) / 16)
|
if (value > (UINT32_MAX - c) / 16) {
|
||||||
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
||||||
|
}
|
||||||
value = value * 16 + c;
|
value = value * 16 + c;
|
||||||
|
|
||||||
empty = false;
|
empty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty)
|
if (empty) {
|
||||||
error("Invalid integer constant, no digits after '$'\n");
|
error("Invalid integer constant, no digits after '$'\n");
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -1109,34 +1151,37 @@ static uint32_t readGfxConstant() {
|
|||||||
uint32_t pixel;
|
uint32_t pixel;
|
||||||
|
|
||||||
// Check for '_' after digits in case one of the digits is '_'
|
// Check for '_' after digits in case one of the digits is '_'
|
||||||
if (c == gfxDigits[0])
|
if (c == gfxDigits[0]) {
|
||||||
pixel = 0;
|
pixel = 0;
|
||||||
else if (c == gfxDigits[1])
|
} else if (c == gfxDigits[1]) {
|
||||||
pixel = 1;
|
pixel = 1;
|
||||||
else if (c == gfxDigits[2])
|
} else if (c == gfxDigits[2]) {
|
||||||
pixel = 2;
|
pixel = 2;
|
||||||
else if (c == gfxDigits[3])
|
} else if (c == gfxDigits[3]) {
|
||||||
pixel = 3;
|
pixel = 3;
|
||||||
else if (c == '_' && width > 0)
|
} else if (c == '_' && width > 0) {
|
||||||
continue;
|
continue;
|
||||||
else
|
} else {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (width < 8) {
|
if (width < 8) {
|
||||||
bitPlaneLower = bitPlaneLower << 1 | (pixel & 1);
|
bitPlaneLower = bitPlaneLower << 1 | (pixel & 1);
|
||||||
bitPlaneUpper = bitPlaneUpper << 1 | (pixel >> 1);
|
bitPlaneUpper = bitPlaneUpper << 1 | (pixel >> 1);
|
||||||
}
|
}
|
||||||
if (width < 9)
|
if (width < 9) {
|
||||||
width++;
|
width++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width == 0)
|
if (width == 0) {
|
||||||
error("Invalid graphics constant, no digits after '`'\n");
|
error("Invalid graphics constant, no digits after '`'\n");
|
||||||
else if (width == 9)
|
} else if (width == 9) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_LARGE_CONSTANT,
|
WARNING_LARGE_CONSTANT,
|
||||||
"Graphics constant is too long, only first 8 pixels considered\n"
|
"Graphics constant is too long, only first 8 pixels considered\n"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return bitPlaneUpper << 8 | bitPlaneLower;
|
return bitPlaneUpper << 8 | bitPlaneLower;
|
||||||
}
|
}
|
||||||
@@ -1164,8 +1209,9 @@ static Token readIdentifier(char firstChar, bool raw) {
|
|||||||
identifier += c;
|
identifier += c;
|
||||||
|
|
||||||
// If the char was a dot, mark the identifier as local
|
// If the char was a dot, mark the identifier as local
|
||||||
if (c == '.')
|
if (c == '.') {
|
||||||
tokenType = T_(LOCAL_ID);
|
tokenType = T_(LOCAL_ID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to check for a keyword if the identifier is not raw
|
// Attempt to check for a keyword if the identifier is not raw
|
||||||
@@ -1179,8 +1225,9 @@ static Token readIdentifier(char firstChar, bool raw) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
||||||
if (identifier.find_first_not_of('.') == identifier.npos)
|
if (identifier.find_first_not_of('.') == identifier.npos) {
|
||||||
tokenType = T_(ID);
|
tokenType = T_(ID);
|
||||||
|
}
|
||||||
|
|
||||||
return Token(tokenType, identifier);
|
return Token(tokenType, identifier);
|
||||||
}
|
}
|
||||||
@@ -1188,8 +1235,9 @@ static Token readIdentifier(char firstChar, bool raw) {
|
|||||||
// Functions to read strings
|
// Functions to read strings
|
||||||
|
|
||||||
static std::shared_ptr<std::string> readInterpolation(size_t depth) {
|
static std::shared_ptr<std::string> readInterpolation(size_t depth) {
|
||||||
if (depth > maxRecursionDepth)
|
if (depth > maxRecursionDepth) {
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||||
|
}
|
||||||
|
|
||||||
std::string fmtBuf;
|
std::string fmtBuf;
|
||||||
FormatSpec fmt{};
|
FormatSpec fmt{};
|
||||||
@@ -1217,11 +1265,13 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth) {
|
|||||||
break;
|
break;
|
||||||
} else if (c == ':' && !fmt.isFinished()) { // Format spec, only once
|
} else if (c == ':' && !fmt.isFinished()) { // Format spec, only once
|
||||||
shiftChar();
|
shiftChar();
|
||||||
for (char f : fmtBuf)
|
for (char f : fmtBuf) {
|
||||||
fmt.useCharacter(f);
|
fmt.useCharacter(f);
|
||||||
|
}
|
||||||
fmt.finishCharacters();
|
fmt.finishCharacters();
|
||||||
if (!fmt.isValid())
|
if (!fmt.isValid()) {
|
||||||
error("Invalid format spec '%s'\n", fmtBuf.c_str());
|
error("Invalid format spec '%s'\n", fmtBuf.c_str());
|
||||||
|
}
|
||||||
fmtBuf.clear(); // Now that format has been set, restart at beginning of string
|
fmtBuf.clear(); // Now that format has been set, restart at beginning of string
|
||||||
} else {
|
} else {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
@@ -1248,10 +1298,11 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth) {
|
|||||||
Symbol const *sym = sym_FindScopedValidSymbol(fmtBuf);
|
Symbol const *sym = sym_FindScopedValidSymbol(fmtBuf);
|
||||||
|
|
||||||
if (!sym || !sym->isDefined()) {
|
if (!sym || !sym->isDefined()) {
|
||||||
if (sym_IsPurgedScoped(fmtBuf))
|
if (sym_IsPurgedScoped(fmtBuf)) {
|
||||||
error("Interpolated symbol \"%s\" does not exist; it was purged\n", fmtBuf.c_str());
|
error("Interpolated symbol \"%s\" does not exist; it was purged\n", fmtBuf.c_str());
|
||||||
else
|
} else {
|
||||||
error("Interpolated symbol \"%s\" does not exist\n", fmtBuf.c_str());
|
error("Interpolated symbol \"%s\" does not exist\n", fmtBuf.c_str());
|
||||||
|
}
|
||||||
} else if (sym->type == SYM_EQUS) {
|
} else if (sym->type == SYM_EQUS) {
|
||||||
auto buf = std::make_shared<std::string>();
|
auto buf = std::make_shared<std::string>();
|
||||||
fmt.appendString(*buf, *sym->getEqus());
|
fmt.appendString(*buf, *sym->getEqus());
|
||||||
@@ -1335,8 +1386,9 @@ static std::string readString(bool raw) {
|
|||||||
case '"':
|
case '"':
|
||||||
if (multiline) {
|
if (multiline) {
|
||||||
// Only """ ends a multi-line string
|
// Only """ ends a multi-line string
|
||||||
if (peek() != '"')
|
if (peek() != '"') {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
shiftChar();
|
shiftChar();
|
||||||
if (peek() != '"') {
|
if (peek() != '"') {
|
||||||
str += '"';
|
str += '"';
|
||||||
@@ -1347,8 +1399,9 @@ static std::string readString(bool raw) {
|
|||||||
return str;
|
return str;
|
||||||
|
|
||||||
case '\\': // Character escape or macro arg
|
case '\\': // Character escape or macro arg
|
||||||
if (raw)
|
if (raw) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
c = peek();
|
c = peek();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\\':
|
case '\\':
|
||||||
@@ -1415,8 +1468,9 @@ static std::string readString(bool raw) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case '{': // Symbol interpolation
|
case '{': // Symbol interpolation
|
||||||
if (raw)
|
if (raw) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
// We'll be exiting the string scope, so re-enable expansions
|
// We'll be exiting the string scope, so re-enable expansions
|
||||||
// (Not interpolations, since they're handled by the function itself...)
|
// (Not interpolations, since they're handled by the function itself...)
|
||||||
lexerState->disableMacroArgs = false;
|
lexerState->disableMacroArgs = false;
|
||||||
@@ -1477,12 +1531,14 @@ static void appendStringLiteral(std::string &str, bool raw) {
|
|||||||
case '"':
|
case '"':
|
||||||
if (multiline) {
|
if (multiline) {
|
||||||
// Only """ ends a multi-line string
|
// Only """ ends a multi-line string
|
||||||
if (peek() != '"')
|
if (peek() != '"') {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
str += '"';
|
str += '"';
|
||||||
shiftChar();
|
shiftChar();
|
||||||
if (peek() != '"')
|
if (peek() != '"') {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
str += '"';
|
str += '"';
|
||||||
shiftChar();
|
shiftChar();
|
||||||
}
|
}
|
||||||
@@ -1490,8 +1546,9 @@ static void appendStringLiteral(std::string &str, bool raw) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case '\\': // Character escape or macro arg
|
case '\\': // Character escape or macro arg
|
||||||
if (raw)
|
if (raw) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
c = peek();
|
c = peek();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
// Character escape
|
// Character escape
|
||||||
@@ -1549,8 +1606,9 @@ static void appendStringLiteral(std::string &str, bool raw) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case '{': // Symbol interpolation
|
case '{': // Symbol interpolation
|
||||||
if (raw)
|
if (raw) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
// We'll be exiting the string scope, so re-enable expansions
|
// We'll be exiting the string scope, so re-enable expansions
|
||||||
// (Not interpolations, since they're handled by the function itself...)
|
// (Not interpolations, since they're handled by the function itself...)
|
||||||
lexerState->disableMacroArgs = false;
|
lexerState->disableMacroArgs = false;
|
||||||
@@ -1849,12 +1907,14 @@ static Token yylex_NORMAL() {
|
|||||||
|
|
||||||
// An ELIF after a taken IF needs to not evaluate its condition
|
// An ELIF after a taken IF needs to not evaluate its condition
|
||||||
if (token.type == T_(POP_ELIF) && lexerState->lastToken == T_(NEWLINE)
|
if (token.type == T_(POP_ELIF) && lexerState->lastToken == T_(NEWLINE)
|
||||||
&& lexer_GetIFDepth() > 0 && lexer_RanIFBlock() && !lexer_ReachedELSEBlock())
|
&& lexer_GetIFDepth() > 0 && lexer_RanIFBlock() && !lexer_ReachedELSEBlock()) {
|
||||||
return yylex_SKIP_TO_ENDC();
|
return yylex_SKIP_TO_ENDC();
|
||||||
|
}
|
||||||
|
|
||||||
// If a keyword, don't try to expand
|
// If a keyword, don't try to expand
|
||||||
if (token.type != T_(ID) && token.type != T_(LOCAL_ID))
|
if (token.type != T_(ID) && token.type != T_(LOCAL_ID)) {
|
||||||
return token;
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
// `token` is either an `ID` or a `LOCAL_ID`, and both have a `std::string` value.
|
// `token` is either an `ID` or a `LOCAL_ID`, and both have a `std::string` value.
|
||||||
assume(token.value.holds<std::string>());
|
assume(token.value.holds<std::string>());
|
||||||
@@ -1885,8 +1945,9 @@ static Token yylex_NORMAL() {
|
|||||||
// character *immediately* follows the identifier. Thus, at the beginning of a line,
|
// character *immediately* follows the identifier. Thus, at the beginning of a line,
|
||||||
// "Label:" and "mac:" are treated as label definitions, but "Label :" and "mac :"
|
// "Label:" and "mac:" are treated as label definitions, but "Label :" and "mac :"
|
||||||
// are treated as macro invocations.
|
// are treated as macro invocations.
|
||||||
if (token.type == T_(ID) && peek() == ':')
|
if (token.type == T_(ID) && peek() == ':') {
|
||||||
token.type = T_(LABEL);
|
token.type = T_(LABEL);
|
||||||
|
}
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -1916,8 +1977,9 @@ static Token yylex_RAW() {
|
|||||||
shiftChar();
|
shiftChar();
|
||||||
c = peek();
|
c = peek();
|
||||||
// If not a line continuation, handle as a normal char
|
// If not a line continuation, handle as a normal char
|
||||||
if (!isWhitespace(c) && c != '\n' && c != '\r')
|
if (!isWhitespace(c) && c != '\n' && c != '\r') {
|
||||||
goto backslash;
|
goto backslash;
|
||||||
|
}
|
||||||
// Line continuations count as "whitespace"
|
// Line continuations count as "whitespace"
|
||||||
discardLineContinuation();
|
discardLineContinuation();
|
||||||
} else {
|
} else {
|
||||||
@@ -1963,18 +2025,21 @@ static Token yylex_RAW() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ',': // End of macro arg
|
case ',': // End of macro arg
|
||||||
if (parenDepth == 0)
|
if (parenDepth == 0) {
|
||||||
goto finish;
|
goto finish;
|
||||||
|
}
|
||||||
goto append;
|
goto append;
|
||||||
|
|
||||||
case '(': // Open parentheses inside macro args
|
case '(': // Open parentheses inside macro args
|
||||||
if (parenDepth < UINT_MAX)
|
if (parenDepth < UINT_MAX) {
|
||||||
parenDepth++;
|
parenDepth++;
|
||||||
|
}
|
||||||
goto append;
|
goto append;
|
||||||
|
|
||||||
case ')': // Close parentheses inside macro args
|
case ')': // Close parentheses inside macro args
|
||||||
if (parenDepth > 0)
|
if (parenDepth > 0) {
|
||||||
parenDepth--;
|
parenDepth--;
|
||||||
|
}
|
||||||
goto append;
|
goto append;
|
||||||
|
|
||||||
case '\\': // Character escape
|
case '\\': // Character escape
|
||||||
@@ -2054,8 +2119,9 @@ finish:
|
|||||||
// an empty raw string before it). This will not be treated as a
|
// an empty raw string before it). This will not be treated as a
|
||||||
// macro argument. To pass an empty last argument, use a second
|
// macro argument. To pass an empty last argument, use a second
|
||||||
// trailing comma.
|
// trailing comma.
|
||||||
if (!str.empty())
|
if (!str.empty()) {
|
||||||
return Token(T_(STRING), str);
|
return Token(T_(STRING), str);
|
||||||
|
}
|
||||||
lexer_SetMode(LEXER_NORMAL);
|
lexer_SetMode(LEXER_NORMAL);
|
||||||
|
|
||||||
if (c == '\r' || c == '\n') {
|
if (c == '\r' || c == '\n') {
|
||||||
@@ -2087,8 +2153,9 @@ static Token skipIfBlock(bool toEndc) {
|
|||||||
|
|
||||||
for (;; shiftChar()) {
|
for (;; shiftChar()) {
|
||||||
c = peek();
|
c = peek();
|
||||||
if (!isWhitespace(c))
|
if (!isWhitespace(c)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startsIdentifier(c)) {
|
if (startsIdentifier(c)) {
|
||||||
@@ -2099,23 +2166,28 @@ static Token skipIfBlock(bool toEndc) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_(POP_ELIF):
|
case T_(POP_ELIF):
|
||||||
if (lexer_ReachedELSEBlock())
|
if (lexer_ReachedELSEBlock()) {
|
||||||
fatalerror("Found ELIF after an ELSE block\n");
|
fatalerror("Found ELIF after an ELSE block\n");
|
||||||
if (!toEndc && lexer_GetIFDepth() == startingDepth)
|
}
|
||||||
|
if (!toEndc && lexer_GetIFDepth() == startingDepth) {
|
||||||
return token;
|
return token;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_(POP_ELSE):
|
case T_(POP_ELSE):
|
||||||
if (lexer_ReachedELSEBlock())
|
if (lexer_ReachedELSEBlock()) {
|
||||||
fatalerror("Found ELSE after an ELSE block\n");
|
fatalerror("Found ELSE after an ELSE block\n");
|
||||||
|
}
|
||||||
lexer_ReachELSEBlock();
|
lexer_ReachELSEBlock();
|
||||||
if (!toEndc && lexer_GetIFDepth() == startingDepth)
|
if (!toEndc && lexer_GetIFDepth() == startingDepth) {
|
||||||
return token;
|
return token;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_(POP_ENDC):
|
case T_(POP_ENDC):
|
||||||
if (lexer_GetIFDepth() == startingDepth)
|
if (lexer_GetIFDepth() == startingDepth) {
|
||||||
return token;
|
return token;
|
||||||
|
}
|
||||||
lexer_DecIFDepth();
|
lexer_DecIFDepth();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2172,8 +2244,9 @@ static Token yylex_SKIP_TO_ENDR() {
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = peek();
|
c = peek();
|
||||||
if (!isWhitespace(c))
|
if (!isWhitespace(c)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
shiftChar();
|
shiftChar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2187,8 +2260,9 @@ static Token yylex_SKIP_TO_ENDR() {
|
|||||||
|
|
||||||
case T_(POP_ENDR):
|
case T_(POP_ENDR):
|
||||||
depth--;
|
depth--;
|
||||||
if (!depth)
|
if (!depth) {
|
||||||
return Token(T_(YYEOF)); // yywrap() will finish the REPT/FOR loop
|
return Token(T_(YYEOF)); // yywrap() will finish the REPT/FOR loop
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_(POP_IF):
|
case T_(POP_IF):
|
||||||
@@ -2234,11 +2308,13 @@ yy::parser::symbol_type yylex() {
|
|||||||
lexerState = lexerStateEOL;
|
lexerState = lexerStateEOL;
|
||||||
lexerStateEOL = nullptr;
|
lexerStateEOL = nullptr;
|
||||||
}
|
}
|
||||||
if (lexerState->lastToken == T_(EOB) && yywrap())
|
if (lexerState->lastToken == T_(EOB) && yywrap()) {
|
||||||
return yy::parser::make_YYEOF();
|
return yy::parser::make_YYEOF();
|
||||||
|
}
|
||||||
// Newlines read within an expansion should not increase the line count
|
// Newlines read within an expansion should not increase the line count
|
||||||
if (lexerState->atLineStart && lexerState->expansions.empty())
|
if (lexerState->atLineStart && lexerState->expansions.empty()) {
|
||||||
nextLine();
|
nextLine();
|
||||||
|
}
|
||||||
|
|
||||||
static Token (* const lexerModeFuncs[NB_LEXER_MODES])() = {
|
static Token (* const lexerModeFuncs[NB_LEXER_MODES])() = {
|
||||||
yylex_NORMAL,
|
yylex_NORMAL,
|
||||||
@@ -2299,8 +2375,9 @@ static Capture startCapture() {
|
|||||||
static void endCapture(Capture &capture) {
|
static void endCapture(Capture &capture) {
|
||||||
// This being `nullptr` means we're capturing from the capture buffer, which is reallocated
|
// This being `nullptr` means we're capturing from the capture buffer, which is reallocated
|
||||||
// during the whole capture process, and so MUST be retrieved at the end
|
// during the whole capture process, and so MUST be retrieved at the end
|
||||||
if (!capture.span.ptr)
|
if (!capture.span.ptr) {
|
||||||
capture.span.ptr = lexerState->makeSharedCaptureBufPtr();
|
capture.span.ptr = lexerState->makeSharedCaptureBufPtr();
|
||||||
|
}
|
||||||
capture.span.size = lexerState->captureSize;
|
capture.span.size = lexerState->captureSize;
|
||||||
|
|
||||||
// ENDR/ENDM or EOF puts us past the start of the line
|
// ENDR/ENDM or EOF puts us past the start of the line
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ std::shared_ptr<std::string> MacroArgs::getArg(uint32_t i) const {
|
|||||||
std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
|
std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
|
||||||
size_t nbArgs = args.size();
|
size_t nbArgs = args.size();
|
||||||
|
|
||||||
if (shift >= nbArgs)
|
if (shift >= nbArgs) {
|
||||||
return std::make_shared<std::string>("");
|
return std::make_shared<std::string>("");
|
||||||
|
}
|
||||||
|
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
|
||||||
for (uint32_t i = shift; i < nbArgs; i++)
|
for (uint32_t i = shift; i < nbArgs; i++) {
|
||||||
len += args[i]->length() + 1; // 1 for comma
|
len += args[i]->length() + 1; // 1 for comma
|
||||||
|
}
|
||||||
|
|
||||||
auto str = std::make_shared<std::string>();
|
auto str = std::make_shared<std::string>();
|
||||||
str->reserve(len + 1); // 1 for comma
|
str->reserve(len + 1); // 1 for comma
|
||||||
@@ -34,16 +36,18 @@ std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
|
|||||||
str->append(*arg);
|
str->append(*arg);
|
||||||
|
|
||||||
// Commas go between args and after a last empty arg
|
// Commas go between args and after a last empty arg
|
||||||
if (i < nbArgs - 1 || arg->empty())
|
if (i < nbArgs - 1 || arg->empty()) {
|
||||||
str->push_back(','); // no space after comma
|
str->push_back(','); // no space after comma
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroArgs::appendArg(std::shared_ptr<std::string> arg) {
|
void MacroArgs::appendArg(std::shared_ptr<std::string> arg) {
|
||||||
if (arg->empty())
|
if (arg->empty()) {
|
||||||
warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument\n");
|
warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument\n");
|
||||||
|
}
|
||||||
args.push_back(arg);
|
args.push_back(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
100
src/asm/main.cpp
100
src/asm/main.cpp
@@ -37,8 +37,9 @@ static std::string make_escape(std::string &str) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
// All dollars needs to be doubled
|
// All dollars needs to be doubled
|
||||||
size_t nextPos = str.find("$", pos);
|
size_t nextPos = str.find("$", pos);
|
||||||
if (nextPos == std::string::npos)
|
if (nextPos == std::string::npos) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
escaped.append(str, pos, nextPos - pos);
|
escaped.append(str, pos, nextPos - pos);
|
||||||
escaped.append("$$");
|
escaped.append("$$");
|
||||||
pos = nextPos + literal_strlen("$");
|
pos = nextPos + literal_strlen("$");
|
||||||
@@ -111,12 +112,14 @@ int main(int argc, char *argv[]) {
|
|||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
// Support SOURCE_DATE_EPOCH for reproducible builds
|
// Support SOURCE_DATE_EPOCH for reproducible builds
|
||||||
// https://reproducible-builds.org/docs/source-date-epoch/
|
// https://reproducible-builds.org/docs/source-date-epoch/
|
||||||
if (char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH"); sourceDateEpoch)
|
if (char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH"); sourceDateEpoch) {
|
||||||
now = static_cast<time_t>(strtoul(sourceDateEpoch, nullptr, 0));
|
now = static_cast<time_t>(strtoul(sourceDateEpoch, nullptr, 0));
|
||||||
|
}
|
||||||
|
|
||||||
Defer closeDependFile{[&] {
|
Defer closeDependFile{[&] {
|
||||||
if (dependFile)
|
if (dependFile) {
|
||||||
fclose(dependFile);
|
fclose(dependFile);
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
// Perform some init for below
|
// Perform some init for below
|
||||||
@@ -133,18 +136,20 @@ int main(int argc, char *argv[]) {
|
|||||||
std::unordered_map<std::string, std::vector<StateFeature>> stateFileSpecs;
|
std::unordered_map<std::string, std::vector<StateFeature>> stateFileSpecs;
|
||||||
std::string newTarget;
|
std::string newTarget;
|
||||||
// Maximum of 100 errors only applies if rgbasm is printing errors to a terminal.
|
// Maximum of 100 errors only applies if rgbasm is printing errors to a terminal.
|
||||||
if (isatty(STDERR_FILENO))
|
if (isatty(STDERR_FILENO)) {
|
||||||
maxErrors = 100;
|
maxErrors = 100;
|
||||||
|
}
|
||||||
|
|
||||||
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
case 'b':
|
case 'b':
|
||||||
if (strlen(musl_optarg) == 2)
|
if (strlen(musl_optarg) == 2) {
|
||||||
opt_B(musl_optarg);
|
opt_B(musl_optarg);
|
||||||
else
|
} else {
|
||||||
errx("Must specify exactly 2 characters for option 'b'");
|
errx("Must specify exactly 2 characters for option 'b'");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
char *equals;
|
char *equals;
|
||||||
@@ -163,10 +168,11 @@ int main(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'g':
|
case 'g':
|
||||||
if (strlen(musl_optarg) == 4)
|
if (strlen(musl_optarg) == 4) {
|
||||||
opt_G(musl_optarg);
|
opt_G(musl_optarg);
|
||||||
else
|
} else {
|
||||||
errx("Must specify exactly 4 characters for option 'g'");
|
errx("Must specify exactly 4 characters for option 'g'");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -178,8 +184,9 @@ int main(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
if (dependFile)
|
if (dependFile) {
|
||||||
warnx("Overriding dependfile %s", dependFileName);
|
warnx("Overriding dependfile %s", dependFileName);
|
||||||
|
}
|
||||||
if (strcmp("-", musl_optarg)) {
|
if (strcmp("-", musl_optarg)) {
|
||||||
dependFile = fopen(musl_optarg, "w");
|
dependFile = fopen(musl_optarg, "w");
|
||||||
dependFileName = musl_optarg;
|
dependFileName = musl_optarg;
|
||||||
@@ -187,8 +194,9 @@ int main(int argc, char *argv[]) {
|
|||||||
dependFile = stdout;
|
dependFile = stdout;
|
||||||
dependFileName = "<stdout>";
|
dependFileName = "<stdout>";
|
||||||
}
|
}
|
||||||
if (dependFile == nullptr)
|
if (dependFile == nullptr) {
|
||||||
err("Failed to open dependfile \"%s\"", dependFileName);
|
err("Failed to open dependfile \"%s\"", dependFileName);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
@@ -203,11 +211,13 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'p':
|
case 'p':
|
||||||
padByte = strtoul(musl_optarg, &endptr, 0);
|
padByte = strtoul(musl_optarg, &endptr, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *endptr != '\0')
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
errx("Invalid argument for option 'p'");
|
errx("Invalid argument for option 'p'");
|
||||||
|
}
|
||||||
|
|
||||||
if (padByte > 0xFF)
|
if (padByte > 0xFF) {
|
||||||
errx("Argument for option 'p' must be between 0 and 0xFF");
|
errx("Argument for option 'p' must be between 0 and 0xFF");
|
||||||
|
}
|
||||||
|
|
||||||
opt_P(padByte);
|
opt_P(padByte);
|
||||||
break;
|
break;
|
||||||
@@ -216,15 +226,18 @@ int main(int argc, char *argv[]) {
|
|||||||
char const *precisionArg;
|
char const *precisionArg;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
precisionArg = musl_optarg;
|
precisionArg = musl_optarg;
|
||||||
if (precisionArg[0] == '.')
|
if (precisionArg[0] == '.') {
|
||||||
precisionArg++;
|
precisionArg++;
|
||||||
|
}
|
||||||
precision = strtoul(precisionArg, &endptr, 0);
|
precision = strtoul(precisionArg, &endptr, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *endptr != '\0')
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
errx("Invalid argument for option 'Q'");
|
errx("Invalid argument for option 'Q'");
|
||||||
|
}
|
||||||
|
|
||||||
if (precision < 1 || precision > 31)
|
if (precision < 1 || precision > 31) {
|
||||||
errx("Argument for option 'Q' must be between 1 and 31");
|
errx("Argument for option 'Q' must be between 1 and 31");
|
||||||
|
}
|
||||||
|
|
||||||
opt_Q(precision);
|
opt_Q(precision);
|
||||||
break;
|
break;
|
||||||
@@ -232,35 +245,41 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'r':
|
case 'r':
|
||||||
maxDepth = strtoul(musl_optarg, &endptr, 0);
|
maxDepth = strtoul(musl_optarg, &endptr, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *endptr != '\0')
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
errx("Invalid argument for option 'r'");
|
errx("Invalid argument for option 'r'");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's': {
|
case 's': {
|
||||||
// Split "<features>:<name>" so `musl_optarg` is "<features>" and `name` is "<name>"
|
// Split "<features>:<name>" so `musl_optarg` is "<features>" and `name` is "<name>"
|
||||||
char *name = strchr(musl_optarg, ':');
|
char *name = strchr(musl_optarg, ':');
|
||||||
if (!name)
|
if (!name) {
|
||||||
errx("Invalid argument for option 's'");
|
errx("Invalid argument for option 's'");
|
||||||
|
}
|
||||||
*name++ = '\0';
|
*name++ = '\0';
|
||||||
|
|
||||||
std::vector<StateFeature> features;
|
std::vector<StateFeature> features;
|
||||||
for (char *feature = musl_optarg; feature;) {
|
for (char *feature = musl_optarg; feature;) {
|
||||||
// Split "<feature>,<rest>" so `feature` is "<feature>" and `next` is "<rest>"
|
// Split "<feature>,<rest>" so `feature` is "<feature>" and `next` is "<rest>"
|
||||||
char *next = strchr(feature, ',');
|
char *next = strchr(feature, ',');
|
||||||
if (next)
|
if (next) {
|
||||||
*next++ = '\0';
|
*next++ = '\0';
|
||||||
|
}
|
||||||
// Trim whitespace from the beginning of `feature`...
|
// Trim whitespace from the beginning of `feature`...
|
||||||
feature += strspn(feature, " \t");
|
feature += strspn(feature, " \t");
|
||||||
// ...and from the end
|
// ...and from the end
|
||||||
if (char *end = strpbrk(feature, " \t"); end)
|
if (char *end = strpbrk(feature, " \t"); end) {
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
|
}
|
||||||
// A feature must be specified
|
// A feature must be specified
|
||||||
if (*feature == '\0')
|
if (*feature == '\0') {
|
||||||
errx("Empty feature for option 's'");
|
errx("Empty feature for option 's'");
|
||||||
|
}
|
||||||
// Parse the `feature` and update the `features` list
|
// Parse the `feature` and update the `features` list
|
||||||
if (!strcasecmp(feature, "all")) {
|
if (!strcasecmp(feature, "all")) {
|
||||||
if (!features.empty())
|
if (!features.empty()) {
|
||||||
warnx("Redundant feature before \"%s\" for option 's'", feature);
|
warnx("Redundant feature before \"%s\" for option 's'", feature);
|
||||||
|
}
|
||||||
features.assign({STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO});
|
features.assign({STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO});
|
||||||
} else {
|
} else {
|
||||||
StateFeature value = !strcasecmp(feature, "equ") ? STATE_EQU
|
StateFeature value = !strcasecmp(feature, "equ") ? STATE_EQU
|
||||||
@@ -280,10 +299,12 @@ int main(int argc, char *argv[]) {
|
|||||||
feature = next;
|
feature = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stateFileSpecs.find(name) != stateFileSpecs.end())
|
if (stateFileSpecs.find(name) != stateFileSpecs.end()) {
|
||||||
warnx("Overriding state filename %s", name);
|
warnx("Overriding state filename %s", name);
|
||||||
if (verbose)
|
}
|
||||||
|
if (verbose) {
|
||||||
printf("State filename %s\n", name);
|
printf("State filename %s\n", name);
|
||||||
|
}
|
||||||
stateFileSpecs.emplace(name, std::move(features));
|
stateFileSpecs.emplace(name, std::move(features));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -308,11 +329,13 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'X':
|
case 'X':
|
||||||
maxValue = strtoul(musl_optarg, &endptr, 0);
|
maxValue = strtoul(musl_optarg, &endptr, 0);
|
||||||
|
|
||||||
if (musl_optarg[0] == '\0' || *endptr != '\0')
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
errx("Invalid argument for option 'X'");
|
errx("Invalid argument for option 'X'");
|
||||||
|
}
|
||||||
|
|
||||||
if (maxValue > UINT_MAX)
|
if (maxValue > UINT_MAX) {
|
||||||
errx("Argument for option 'X' must be between 0 and %u", UINT_MAX);
|
errx("Argument for option 'X' must be between 0 and %u", UINT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
maxErrors = maxValue;
|
maxErrors = maxValue;
|
||||||
break;
|
break;
|
||||||
@@ -331,10 +354,12 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'Q':
|
case 'Q':
|
||||||
case 'T':
|
case 'T':
|
||||||
newTarget = musl_optarg;
|
newTarget = musl_optarg;
|
||||||
if (depType == 'Q')
|
if (depType == 'Q') {
|
||||||
newTarget = make_escape(newTarget);
|
newTarget = make_escape(newTarget);
|
||||||
if (!targetFileName.empty())
|
}
|
||||||
|
if (!targetFileName.empty()) {
|
||||||
targetFileName += ' ';
|
targetFileName += ' ';
|
||||||
|
}
|
||||||
targetFileName += newTarget;
|
targetFileName += newTarget;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -347,8 +372,9 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetFileName.empty() && !objectFileName.empty())
|
if (targetFileName.empty() && !objectFileName.empty()) {
|
||||||
targetFileName = objectFileName;
|
targetFileName = objectFileName;
|
||||||
|
}
|
||||||
|
|
||||||
if (argc == musl_optind) {
|
if (argc == musl_optind) {
|
||||||
fputs(
|
fputs(
|
||||||
@@ -364,13 +390,15 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
std::string mainFileName = argv[musl_optind];
|
std::string mainFileName = argv[musl_optind];
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("Assembling %s\n", mainFileName.c_str());
|
printf("Assembling %s\n", mainFileName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
if (dependFile) {
|
if (dependFile) {
|
||||||
if (targetFileName.empty())
|
if (targetFileName.empty()) {
|
||||||
errx("Dependency files can only be created if a target file is specified with either "
|
errx("Dependency files can only be created if a target file is specified with either "
|
||||||
"-o, -MQ or -MT");
|
"-o, -MQ or -MT");
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), mainFileName.c_str());
|
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), mainFileName.c_str());
|
||||||
}
|
}
|
||||||
@@ -381,8 +409,9 @@ int main(int argc, char *argv[]) {
|
|||||||
fstk_Init(mainFileName, maxDepth);
|
fstk_Init(mainFileName, maxDepth);
|
||||||
|
|
||||||
// Perform parse (`yy::parser` is auto-generated from `parser.y`)
|
// Perform parse (`yy::parser` is auto-generated from `parser.y`)
|
||||||
if (yy::parser parser; parser.parse() != 0 && nbErrors == 0)
|
if (yy::parser parser; parser.parse() != 0 && nbErrors == 0) {
|
||||||
nbErrors = 1;
|
nbErrors = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!failedOnMissingInclude) {
|
if (!failedOnMissingInclude) {
|
||||||
sect_CheckUnionClosed();
|
sect_CheckUnionClosed();
|
||||||
@@ -394,17 +423,20 @@ int main(int argc, char *argv[]) {
|
|||||||
sect_CheckStack();
|
sect_CheckStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbErrors != 0)
|
if (nbErrors != 0) {
|
||||||
errx("Assembly aborted (%u error%s)!", nbErrors, nbErrors == 1 ? "" : "s");
|
errx("Assembly aborted (%u error%s)!", nbErrors, nbErrors == 1 ? "" : "s");
|
||||||
|
}
|
||||||
|
|
||||||
// If parse aborted due to missing an include, and `-MG` was given, exit normally
|
// If parse aborted due to missing an include, and `-MG` was given, exit normally
|
||||||
if (failedOnMissingInclude)
|
if (failedOnMissingInclude) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
out_WriteObject();
|
out_WriteObject();
|
||||||
|
|
||||||
for (auto [name, features] : stateFileSpecs)
|
for (auto [name, features] : stateFileSpecs) {
|
||||||
out_WriteState(name, features);
|
out_WriteState(name, features);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,17 +53,19 @@ void opt_W(char const *flag) {
|
|||||||
void opt_Parse(char const *s) {
|
void opt_Parse(char const *s) {
|
||||||
switch (s[0]) {
|
switch (s[0]) {
|
||||||
case 'b':
|
case 'b':
|
||||||
if (strlen(&s[1]) == 2)
|
if (strlen(&s[1]) == 2) {
|
||||||
opt_B(&s[1]);
|
opt_B(&s[1]);
|
||||||
else
|
} else {
|
||||||
error("Must specify exactly 2 characters for option 'b'\n");
|
error("Must specify exactly 2 characters for option 'b'\n");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'g':
|
case 'g':
|
||||||
if (strlen(&s[1]) == 4)
|
if (strlen(&s[1]) == 4) {
|
||||||
opt_G(&s[1]);
|
opt_G(&s[1]);
|
||||||
else
|
} else {
|
||||||
error("Must specify exactly 4 characters for option 'g'\n");
|
error("Must specify exactly 4 characters for option 'g'\n");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
@@ -72,12 +74,13 @@ void opt_Parse(char const *s) {
|
|||||||
unsigned int padByte;
|
unsigned int padByte;
|
||||||
|
|
||||||
result = sscanf(&s[1], "%x", &padByte);
|
result = sscanf(&s[1], "%x", &padByte);
|
||||||
if (result != 1)
|
if (result != 1) {
|
||||||
error("Invalid argument for option 'p'\n");
|
error("Invalid argument for option 'p'\n");
|
||||||
else if (padByte > 0xFF)
|
} else if (padByte > 0xFF) {
|
||||||
error("Argument for option 'p' must be between 0 and 0xFF\n");
|
error("Argument for option 'p' must be between 0 and 0xFF\n");
|
||||||
else
|
} else {
|
||||||
opt_P(padByte);
|
opt_P(padByte);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error("Invalid argument for option 'p'\n");
|
error("Invalid argument for option 'p'\n");
|
||||||
}
|
}
|
||||||
@@ -86,19 +89,21 @@ void opt_Parse(char const *s) {
|
|||||||
char const *precisionArg;
|
char const *precisionArg;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
precisionArg = &s[1];
|
precisionArg = &s[1];
|
||||||
if (precisionArg[0] == '.')
|
if (precisionArg[0] == '.') {
|
||||||
precisionArg++;
|
precisionArg++;
|
||||||
|
}
|
||||||
if (strlen(precisionArg) <= 2) {
|
if (strlen(precisionArg) <= 2) {
|
||||||
int result;
|
int result;
|
||||||
unsigned int precision;
|
unsigned int precision;
|
||||||
|
|
||||||
result = sscanf(precisionArg, "%u", &precision);
|
result = sscanf(precisionArg, "%u", &precision);
|
||||||
if (result != 1)
|
if (result != 1) {
|
||||||
error("Invalid argument for option 'Q'\n");
|
error("Invalid argument for option 'Q'\n");
|
||||||
else if (precision < 1 || precision > 31)
|
} else if (precision < 1 || precision > 31) {
|
||||||
error("Argument for option 'Q' must be between 1 and 31\n");
|
error("Argument for option 'Q' must be between 1 and 31\n");
|
||||||
else
|
} else {
|
||||||
opt_Q(precision);
|
opt_Q(precision);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error("Invalid argument for option 'Q'\n");
|
error("Invalid argument for option 'Q'\n");
|
||||||
}
|
}
|
||||||
@@ -106,8 +111,9 @@ void opt_Parse(char const *s) {
|
|||||||
|
|
||||||
case 'r': {
|
case 'r': {
|
||||||
++s; // Skip 'r'
|
++s; // Skip 'r'
|
||||||
while (isblank(*s))
|
while (isblank(*s)) {
|
||||||
++s; // Skip leading whitespace
|
++s; // Skip leading whitespace
|
||||||
|
}
|
||||||
|
|
||||||
if (s[0] == '\0') {
|
if (s[0] == '\0') {
|
||||||
error("Missing argument to option 'r'\n");
|
error("Missing argument to option 'r'\n");
|
||||||
@@ -128,10 +134,11 @@ void opt_Parse(char const *s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'W':
|
case 'W':
|
||||||
if (strlen(&s[1]) > 0)
|
if (strlen(&s[1]) > 0) {
|
||||||
opt_W(&s[1]);
|
opt_W(&s[1]);
|
||||||
else
|
} else {
|
||||||
error("Must specify an argument for option 'W'\n");
|
error("Must specify an argument for option 'W'\n");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -63,11 +63,13 @@ void out_RegisterNode(std::shared_ptr<FileStackNode> node) {
|
|||||||
|
|
||||||
// Return a section's ID, or UINT32_MAX if the section is not in the list
|
// Return a section's ID, or UINT32_MAX if the section is not in the list
|
||||||
static uint32_t getSectIDIfAny(Section *sect) {
|
static uint32_t getSectIDIfAny(Section *sect) {
|
||||||
if (!sect)
|
if (!sect) {
|
||||||
return UINT32_MAX;
|
return UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto search = sectionMap.find(sect->name); search != sectionMap.end())
|
if (auto search = sectionMap.find(sect->name); search != sectionMap.end()) {
|
||||||
return static_cast<uint32_t>(search->second);
|
return static_cast<uint32_t>(search->second);
|
||||||
|
}
|
||||||
|
|
||||||
fatalerror("Unknown section '%s'\n", sect->name.c_str());
|
fatalerror("Unknown section '%s'\n", sect->name.c_str());
|
||||||
}
|
}
|
||||||
@@ -109,8 +111,9 @@ static void writeSection(Section const §, FILE *file) {
|
|||||||
fwrite(sect.data.data(), 1, sect.size, file);
|
fwrite(sect.data.data(), 1, sect.size, file);
|
||||||
putLong(sect.patches.size(), file);
|
putLong(sect.patches.size(), file);
|
||||||
|
|
||||||
for (Patch const &patch : sect.patches)
|
for (Patch const &patch : sect.patches) {
|
||||||
writePatch(patch, file);
|
writePatch(patch, file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,8 +165,9 @@ static void writeRpn(std::vector<uint8_t> &rpnexpr, std::vector<uint8_t> const &
|
|||||||
symName.clear();
|
symName.clear();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uint8_t c = rpn[offset++];
|
uint8_t c = rpn[offset++];
|
||||||
if (c == 0)
|
if (c == 0) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
symName += c;
|
symName += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,8 +192,9 @@ static void writeRpn(std::vector<uint8_t> &rpnexpr, std::vector<uint8_t> const &
|
|||||||
symName.clear();
|
symName.clear();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uint8_t c = rpn[offset++];
|
uint8_t c = rpn[offset++];
|
||||||
if (c == 0)
|
if (c == 0) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
symName += c;
|
symName += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,14 +303,16 @@ static void writeFileStackNode(FileStackNode const &node, FILE *file) {
|
|||||||
|
|
||||||
putLong(nodeIters.size(), file);
|
putLong(nodeIters.size(), file);
|
||||||
// Iters are stored by decreasing depth, so reverse the order for output
|
// Iters are stored by decreasing depth, so reverse the order for output
|
||||||
for (uint32_t i = nodeIters.size(); i--;)
|
for (uint32_t i = nodeIters.size(); i--;) {
|
||||||
putLong(nodeIters[i], file);
|
putLong(nodeIters[i], file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void out_WriteObject() {
|
void out_WriteObject() {
|
||||||
if (objectFileName.empty())
|
if (objectFileName.empty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *file;
|
FILE *file;
|
||||||
if (objectFileName != "-") {
|
if (objectFileName != "-") {
|
||||||
@@ -315,8 +322,9 @@ void out_WriteObject() {
|
|||||||
(void)setmode(STDOUT_FILENO, O_BINARY);
|
(void)setmode(STDOUT_FILENO, O_BINARY);
|
||||||
file = stdout;
|
file = stdout;
|
||||||
}
|
}
|
||||||
if (!file)
|
if (!file) {
|
||||||
err("Failed to open object file '%s'", objectFileName.c_str());
|
err("Failed to open object file '%s'", objectFileName.c_str());
|
||||||
|
}
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
// Also write symbols that weren't written above
|
// Also write symbols that weren't written above
|
||||||
@@ -335,33 +343,39 @@ void out_WriteObject() {
|
|||||||
writeFileStackNode(node, file);
|
writeFileStackNode(node, file);
|
||||||
|
|
||||||
// The list is supposed to have decrementing IDs
|
// The list is supposed to have decrementing IDs
|
||||||
if (it + 1 != fileStackNodes.end() && it[1]->ID != node.ID - 1)
|
if (it + 1 != fileStackNodes.end() && it[1]->ID != node.ID - 1) {
|
||||||
fatalerror(
|
fatalerror(
|
||||||
"Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
|
"Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
|
||||||
". Please report this to the developers!\n",
|
". Please report this to the developers!\n",
|
||||||
it[1]->ID,
|
it[1]->ID,
|
||||||
node.ID
|
node.ID
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Symbol const *sym : objectSymbols)
|
for (Symbol const *sym : objectSymbols) {
|
||||||
writeSymbol(*sym, file);
|
writeSymbol(*sym, file);
|
||||||
|
}
|
||||||
|
|
||||||
for (Section const § : sectionList)
|
for (Section const § : sectionList) {
|
||||||
writeSection(sect, file);
|
writeSection(sect, file);
|
||||||
|
}
|
||||||
|
|
||||||
putLong(assertions.size(), file);
|
putLong(assertions.size(), file);
|
||||||
|
|
||||||
for (Assertion const &assert : assertions)
|
for (Assertion const &assert : assertions) {
|
||||||
writeAssert(assert, file);
|
writeAssert(assert, file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void out_SetFileName(std::string const &name) {
|
void out_SetFileName(std::string const &name) {
|
||||||
if (!objectFileName.empty())
|
if (!objectFileName.empty()) {
|
||||||
warnx("Overriding output filename %s", objectFileName.c_str());
|
warnx("Overriding output filename %s", objectFileName.c_str());
|
||||||
|
}
|
||||||
objectFileName = name;
|
objectFileName = name;
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("Output filename %s\n", objectFileName.c_str());
|
printf("Output filename %s\n", objectFileName.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dumpString(std::string const &escape, FILE *file) {
|
static void dumpString(std::string const &escape, FILE *file) {
|
||||||
@@ -397,8 +411,9 @@ static bool dumpEquConstants(FILE *file) {
|
|||||||
equConstants.clear();
|
equConstants.clear();
|
||||||
|
|
||||||
sym_ForEach([](Symbol &sym) {
|
sym_ForEach([](Symbol &sym) {
|
||||||
if (!sym.isBuiltin && sym.type == SYM_EQU)
|
if (!sym.isBuiltin && sym.type == SYM_EQU) {
|
||||||
equConstants.push_back(&sym);
|
equConstants.push_back(&sym);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Constants are ordered by file, then by definition order
|
// Constants are ordered by file, then by definition order
|
||||||
std::sort(RANGE(equConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
std::sort(RANGE(equConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
@@ -418,8 +433,9 @@ static bool dumpVariables(FILE *file) {
|
|||||||
variables.clear();
|
variables.clear();
|
||||||
|
|
||||||
sym_ForEach([](Symbol &sym) {
|
sym_ForEach([](Symbol &sym) {
|
||||||
if (!sym.isBuiltin && sym.type == SYM_VAR)
|
if (!sym.isBuiltin && sym.type == SYM_VAR) {
|
||||||
variables.push_back(&sym);
|
variables.push_back(&sym);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Variables are ordered by file, then by definition order
|
// Variables are ordered by file, then by definition order
|
||||||
std::sort(RANGE(variables), [](Symbol *sym1, Symbol *sym2) -> bool {
|
std::sort(RANGE(variables), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
@@ -439,8 +455,9 @@ static bool dumpEqusConstants(FILE *file) {
|
|||||||
equsConstants.clear();
|
equsConstants.clear();
|
||||||
|
|
||||||
sym_ForEach([](Symbol &sym) {
|
sym_ForEach([](Symbol &sym) {
|
||||||
if (!sym.isBuiltin && sym.type == SYM_EQUS)
|
if (!sym.isBuiltin && sym.type == SYM_EQUS) {
|
||||||
equsConstants.push_back(&sym);
|
equsConstants.push_back(&sym);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Constants are ordered by file, then by definition order
|
// Constants are ordered by file, then by definition order
|
||||||
std::sort(RANGE(equsConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
std::sort(RANGE(equsConstants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
@@ -467,8 +484,9 @@ static bool dumpCharmaps(FILE *file) {
|
|||||||
fputs("charmap \"", charmapFile);
|
fputs("charmap \"", charmapFile);
|
||||||
dumpString(mapping, charmapFile);
|
dumpString(mapping, charmapFile);
|
||||||
putc('"', charmapFile);
|
putc('"', charmapFile);
|
||||||
for (int32_t v : value)
|
for (int32_t v : value) {
|
||||||
fprintf(charmapFile, ", $%" PRIx32, v);
|
fprintf(charmapFile, ", $%" PRIx32, v);
|
||||||
|
}
|
||||||
putc('\n', charmapFile);
|
putc('\n', charmapFile);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -479,8 +497,9 @@ static bool dumpMacros(FILE *file) {
|
|||||||
macros.clear();
|
macros.clear();
|
||||||
|
|
||||||
sym_ForEach([](Symbol &sym) {
|
sym_ForEach([](Symbol &sym) {
|
||||||
if (!sym.isBuiltin && sym.type == SYM_MACRO)
|
if (!sym.isBuiltin && sym.type == SYM_MACRO) {
|
||||||
macros.push_back(&sym);
|
macros.push_back(&sym);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Macros are ordered by file, then by definition order
|
// Macros are ordered by file, then by definition order
|
||||||
std::sort(RANGE(macros), [](Symbol *sym1, Symbol *sym2) -> bool {
|
std::sort(RANGE(macros), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
@@ -508,8 +527,9 @@ void out_WriteState(std::string name, std::vector<StateFeature> const &features)
|
|||||||
(void)setmode(STDOUT_FILENO, O_BINARY);
|
(void)setmode(STDOUT_FILENO, O_BINARY);
|
||||||
file = stdout;
|
file = stdout;
|
||||||
}
|
}
|
||||||
if (!file)
|
if (!file) {
|
||||||
err("Failed to open state file '%s'", name.c_str());
|
err("Failed to open state file '%s'", name.c_str());
|
||||||
|
}
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
static char const *dumpHeadings[NB_STATE_FEATURES] = {
|
static char const *dumpHeadings[NB_STATE_FEATURES] = {
|
||||||
@@ -530,7 +550,8 @@ void out_WriteState(std::string name, std::vector<StateFeature> const &features)
|
|||||||
fputs("; File generated by rgbasm\n", file);
|
fputs("; File generated by rgbasm\n", file);
|
||||||
for (StateFeature feature : features) {
|
for (StateFeature feature : features) {
|
||||||
fprintf(file, "\n; %s\n", dumpHeadings[feature]);
|
fprintf(file, "\n; %s\n", dumpHeadings[feature]);
|
||||||
if (!dumpFuncs[feature](file))
|
if (!dumpFuncs[feature](file)) {
|
||||||
fprintf(file, "; No values\n");
|
fprintf(file, "; No values\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,9 @@ int32_t Expression::getConstVal() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Symbol const *Expression::symbolOf() const {
|
Symbol const *Expression::symbolOf() const {
|
||||||
if (!isSymbol)
|
if (!isSymbol) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
return sym_FindScopedSymbol(reinterpret_cast<char const *>(&rpn[1]));
|
return sym_FindScopedSymbol(reinterpret_cast<char const *>(&rpn[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +56,9 @@ bool Expression::isDiffConstant(Symbol const *sym) const {
|
|||||||
// Check if both expressions only refer to a single symbol
|
// Check if both expressions only refer to a single symbol
|
||||||
Symbol const *sym1 = symbolOf();
|
Symbol const *sym1 = symbolOf();
|
||||||
|
|
||||||
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL)
|
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Section const *sect1 = sym1->getSection();
|
Section const *sect1 = sym1->getSection();
|
||||||
Section const *sect2 = sym->getSection();
|
Section const *sect2 = sym->getSection();
|
||||||
@@ -208,8 +210,9 @@ static bool tryConstNonzero(Expression const &lhs, Expression const &rhs) {
|
|||||||
|
|
||||||
static bool tryConstLogNot(Expression const &expr) {
|
static bool tryConstLogNot(Expression const &expr) {
|
||||||
Symbol const *sym = expr.symbolOf();
|
Symbol const *sym = expr.symbolOf();
|
||||||
if (!sym || !sym->getSection() || !sym->isDefined())
|
if (!sym || !sym->getSection() || !sym->isDefined()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
assume(sym->isNumeric());
|
assume(sym->isNumeric());
|
||||||
|
|
||||||
@@ -230,15 +233,17 @@ static bool tryConstLogNot(Expression const &expr) {
|
|||||||
// @return The constant `LOW(expr)` result if it can be computed, or -1 otherwise.
|
// @return The constant `LOW(expr)` result if it can be computed, or -1 otherwise.
|
||||||
static int32_t tryConstLow(Expression const &expr) {
|
static int32_t tryConstLow(Expression const &expr) {
|
||||||
Symbol const *sym = expr.symbolOf();
|
Symbol const *sym = expr.symbolOf();
|
||||||
if (!sym || !sym->getSection() || !sym->isDefined())
|
if (!sym || !sym->getSection() || !sym->isDefined()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
assume(sym->isNumeric());
|
assume(sym->isNumeric());
|
||||||
|
|
||||||
// The low byte must not cover any unknown bits
|
// The low byte must not cover any unknown bits
|
||||||
Section const § = *sym->getSection();
|
Section const § = *sym->getSection();
|
||||||
if (sect.align < 8)
|
if (sect.align < 8) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// `sym->getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
// `sym->getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
||||||
// because the section is floating (otherwise we wouldn't be here)
|
// because the section is floating (otherwise we wouldn't be here)
|
||||||
@@ -258,15 +263,17 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
|||||||
bool lhsIsSymbol = lhsSymbol && lhsSymbol->getSection();
|
bool lhsIsSymbol = lhsSymbol && lhsSymbol->getSection();
|
||||||
bool rhsIsSymbol = rhsSymbol && rhsSymbol->getSection();
|
bool rhsIsSymbol = rhsSymbol && rhsSymbol->getSection();
|
||||||
|
|
||||||
if (!lhsIsSymbol && !rhsIsSymbol)
|
if (!lhsIsSymbol && !rhsIsSymbol) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// If the lhs isn't a symbol, try again the other way around
|
// If the lhs isn't a symbol, try again the other way around
|
||||||
Symbol const &sym = lhsIsSymbol ? *lhsSymbol : *rhsSymbol;
|
Symbol const &sym = lhsIsSymbol ? *lhsSymbol : *rhsSymbol;
|
||||||
Expression const &expr = lhsIsSymbol ? rhs : lhs; // Opposite side of `sym`
|
Expression const &expr = lhsIsSymbol ? rhs : lhs; // Opposite side of `sym`
|
||||||
|
|
||||||
if (!sym.isDefined() || !expr.isKnown())
|
if (!sym.isDefined() || !expr.isKnown()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
assume(sym.isNumeric());
|
assume(sym.isNumeric());
|
||||||
|
|
||||||
@@ -275,8 +282,9 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
|||||||
|
|
||||||
// The mask must not cover any unknown bits
|
// The mask must not cover any unknown bits
|
||||||
Section const § = *sym.getSection();
|
Section const § = *sym.getSection();
|
||||||
if (int32_t unknownBits = (1 << 16) - (1 << sect.align); (unknownBits & mask) != 0)
|
if (int32_t unknownBits = (1 << 16) - (1 << sect.align); (unknownBits & mask) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// `sym.getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
// `sym.getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
||||||
// because the section is floating (otherwise we wouldn't be here)
|
// because the section is floating (otherwise we wouldn't be here)
|
||||||
@@ -412,38 +420,45 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
data = lval & rval;
|
data = lval & rval;
|
||||||
break;
|
break;
|
||||||
case RPN_SHL:
|
case RPN_SHL:
|
||||||
if (rval < 0)
|
if (rval < 0) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32 "\n", rval
|
WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32 "\n", rval
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (rval >= 32)
|
if (rval >= 32) {
|
||||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", rval);
|
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", rval);
|
||||||
|
}
|
||||||
|
|
||||||
data = op_shift_left(lval, rval);
|
data = op_shift_left(lval, rval);
|
||||||
break;
|
break;
|
||||||
case RPN_SHR:
|
case RPN_SHR:
|
||||||
if (lval < 0)
|
if (lval < 0) {
|
||||||
warning(WARNING_SHIFT, "Shifting right negative value %" PRId32 "\n", lval);
|
warning(WARNING_SHIFT, "Shifting right negative value %" PRId32 "\n", lval);
|
||||||
|
}
|
||||||
|
|
||||||
if (rval < 0)
|
if (rval < 0) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
|
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (rval >= 32)
|
if (rval >= 32) {
|
||||||
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
|
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
|
||||||
|
}
|
||||||
|
|
||||||
data = op_shift_right(lval, rval);
|
data = op_shift_right(lval, rval);
|
||||||
break;
|
break;
|
||||||
case RPN_USHR:
|
case RPN_USHR:
|
||||||
if (rval < 0)
|
if (rval < 0) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
|
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (rval >= 32)
|
if (rval >= 32) {
|
||||||
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
|
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval);
|
||||||
|
}
|
||||||
|
|
||||||
data = op_shift_right_unsigned(lval, rval);
|
data = op_shift_right_unsigned(lval, rval);
|
||||||
break;
|
break;
|
||||||
@@ -451,8 +466,9 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
data = static_cast<int32_t>(ulval * urval);
|
data = static_cast<int32_t>(ulval * urval);
|
||||||
break;
|
break;
|
||||||
case RPN_DIV:
|
case RPN_DIV:
|
||||||
if (rval == 0)
|
if (rval == 0) {
|
||||||
fatalerror("Division by zero\n");
|
fatalerror("Division by zero\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (lval == INT32_MIN && rval == -1) {
|
if (lval == INT32_MIN && rval == -1) {
|
||||||
warning(
|
warning(
|
||||||
@@ -467,17 +483,20 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_MOD:
|
case RPN_MOD:
|
||||||
if (rval == 0)
|
if (rval == 0) {
|
||||||
fatalerror("Modulo by zero\n");
|
fatalerror("Modulo by zero\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (lval == INT32_MIN && rval == -1)
|
if (lval == INT32_MIN && rval == -1) {
|
||||||
data = 0;
|
data = 0;
|
||||||
else
|
} else {
|
||||||
data = op_modulo(lval, rval);
|
data = op_modulo(lval, rval);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_EXP:
|
case RPN_EXP:
|
||||||
if (rval < 0)
|
if (rval < 0) {
|
||||||
fatalerror("Exponentiation by negative power\n");
|
fatalerror("Exponentiation by negative power\n");
|
||||||
|
}
|
||||||
|
|
||||||
data = op_exponent(lval, rval);
|
data = op_exponent(lval, rval);
|
||||||
break;
|
break;
|
||||||
@@ -554,9 +573,10 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
// Copy the right RPN and append the operator
|
// Copy the right RPN and append the operator
|
||||||
uint32_t rightRpnSize = src2.rpn.size();
|
uint32_t rightRpnSize = src2.rpn.size();
|
||||||
uint8_t *ptr = reserveSpace(rightRpnSize + 1, src2.rpnPatchSize + 1);
|
uint8_t *ptr = reserveSpace(rightRpnSize + 1, src2.rpnPatchSize + 1);
|
||||||
if (rightRpnSize > 0)
|
if (rightRpnSize > 0) {
|
||||||
// If `rightRpnSize == 0`, then `memcpy(ptr, nullptr, rightRpnSize)` would be UB
|
// If `rightRpnSize == 0`, then `memcpy(ptr, nullptr, rightRpnSize)` would be UB
|
||||||
memcpy(ptr, src2.rpn.data(), rightRpnSize);
|
memcpy(ptr, src2.rpn.data(), rightRpnSize);
|
||||||
|
}
|
||||||
ptr[rightRpnSize] = op;
|
ptr[rightRpnSize] = op;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -589,8 +609,9 @@ void Expression::makeCheckRST() {
|
|||||||
|
|
||||||
// Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
// Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
||||||
void Expression::checkNBit(uint8_t n) const {
|
void Expression::checkNBit(uint8_t n) const {
|
||||||
if (isKnown())
|
if (isKnown()) {
|
||||||
::checkNBit(value(), n, "Expression");
|
::checkNBit(value(), n, "Expression");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkNBit(int32_t v, uint8_t n, char const *name) {
|
bool checkNBit(int32_t v, uint8_t n, char const *name) {
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ int32_t loadOffset; // Offset into the LOAD section's parent (see sect_GetOutput
|
|||||||
// A quick check to see if we have an initialized section
|
// A quick check to see if we have an initialized section
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static bool requireSection() {
|
static bool requireSection() {
|
||||||
if (currentSection)
|
if (currentSection) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
error("Cannot output data outside of a SECTION\n");
|
error("Cannot output data outside of a SECTION\n");
|
||||||
return false;
|
return false;
|
||||||
@@ -62,11 +63,13 @@ static bool requireSection() {
|
|||||||
// this much initialized data
|
// this much initialized data
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static bool requireCodeSection() {
|
static bool requireCodeSection() {
|
||||||
if (!requireSection())
|
if (!requireSection()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (sect_HasData(currentSection->type))
|
if (sect_HasData(currentSection->type)) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
error(
|
error(
|
||||||
"Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
|
"Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
|
||||||
@@ -77,7 +80,7 @@ static bool requireCodeSection() {
|
|||||||
|
|
||||||
void sect_CheckSizes() {
|
void sect_CheckSizes() {
|
||||||
for (Section const § : sectionList) {
|
for (Section const § : sectionList) {
|
||||||
if (uint32_t maxSize = sectionTypeInfo[sect.type].size; sect.size > maxSize)
|
if (uint32_t maxSize = sectionTypeInfo[sect.type].size; sect.size > maxSize) {
|
||||||
error(
|
error(
|
||||||
"Section '%s' grew too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32
|
"Section '%s' grew too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32
|
||||||
").\n",
|
").\n",
|
||||||
@@ -85,6 +88,7 @@ void sect_CheckSizes() {
|
|||||||
maxSize,
|
maxSize,
|
||||||
sect.size
|
sect.size
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,33 +112,36 @@ static unsigned int mergeSectUnion(
|
|||||||
|
|
||||||
// Unionized sections only need "compatible" constraints, and they end up with the strictest
|
// Unionized sections only need "compatible" constraints, and they end up with the strictest
|
||||||
// combination of both.
|
// combination of both.
|
||||||
if (sect_HasData(type))
|
if (sect_HasData(type)) {
|
||||||
sectError("Cannot declare ROM sections as UNION\n");
|
sectError("Cannot declare ROM sections as UNION\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (org != UINT32_MAX) {
|
if (org != UINT32_MAX) {
|
||||||
// If both are fixed, they must be the same
|
// If both are fixed, they must be the same
|
||||||
if (sect.org != UINT32_MAX && sect.org != org)
|
if (sect.org != UINT32_MAX && sect.org != org) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at different address $%04" PRIx32 "\n", sect.org
|
"Section already declared as fixed at different address $%04" PRIx32 "\n", sect.org
|
||||||
);
|
);
|
||||||
else if (sect.align != 0 && (mask(sect.align) & (org - sect.alignOfs)))
|
} else if (sect.align != 0 && (mask(sect.align) & (org - sect.alignOfs))) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
||||||
1U << sect.align,
|
1U << sect.align,
|
||||||
sect.alignOfs
|
sect.alignOfs
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
// Otherwise, just override
|
// Otherwise, just override
|
||||||
sect.org = org;
|
sect.org = org;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (alignment != 0) {
|
} else if (alignment != 0) {
|
||||||
// Make sure any fixed address given is compatible
|
// Make sure any fixed address given is compatible
|
||||||
if (sect.org != UINT32_MAX) {
|
if (sect.org != UINT32_MAX) {
|
||||||
if ((sect.org - alignOffset) & mask(alignment))
|
if ((sect.org - alignOffset) & mask(alignment)) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||||
sect.org
|
sect.org
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Check if alignment offsets are compatible
|
// Check if alignment offsets are compatible
|
||||||
} else if ((alignOffset & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
} else if ((alignOffset & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
||||||
sectError(
|
sectError(
|
||||||
@@ -165,34 +172,37 @@ static unsigned int
|
|||||||
uint16_t curOrg = org - sect.size;
|
uint16_t curOrg = org - sect.size;
|
||||||
|
|
||||||
// If both are fixed, they must be the same
|
// If both are fixed, they must be the same
|
||||||
if (sect.org != UINT32_MAX && sect.org != curOrg)
|
if (sect.org != UINT32_MAX && sect.org != curOrg) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||||
sect.org
|
sect.org
|
||||||
);
|
);
|
||||||
else if (sect.align != 0 && (mask(sect.align) & (curOrg - sect.alignOfs)))
|
} else if (sect.align != 0 && (mask(sect.align) & (curOrg - sect.alignOfs))) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
||||||
1U << sect.align,
|
1U << sect.align,
|
||||||
sect.alignOfs
|
sect.alignOfs
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
// Otherwise, just override
|
// Otherwise, just override
|
||||||
sect.org = curOrg;
|
sect.org = curOrg;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (alignment != 0) {
|
} else if (alignment != 0) {
|
||||||
int32_t curOfs = (alignOffset - sect.size) % (1U << alignment);
|
int32_t curOfs = (alignOffset - sect.size) % (1U << alignment);
|
||||||
|
|
||||||
if (curOfs < 0)
|
if (curOfs < 0) {
|
||||||
curOfs += 1U << alignment;
|
curOfs += 1U << alignment;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure any fixed address given is compatible
|
// Make sure any fixed address given is compatible
|
||||||
if (sect.org != UINT32_MAX) {
|
if (sect.org != UINT32_MAX) {
|
||||||
if ((sect.org - curOfs) & mask(alignment))
|
if ((sect.org - curOfs) & mask(alignment)) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||||
sect.org
|
sect.org
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Check if alignment offsets are compatible
|
// Check if alignment offsets are compatible
|
||||||
} else if ((curOfs & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
} else if ((curOfs & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
||||||
sectError(
|
sectError(
|
||||||
@@ -222,10 +232,11 @@ static void mergeSections(
|
|||||||
) {
|
) {
|
||||||
unsigned int nbSectErrors = 0;
|
unsigned int nbSectErrors = 0;
|
||||||
|
|
||||||
if (type != sect.type)
|
if (type != sect.type) {
|
||||||
sectError(
|
sectError(
|
||||||
"Section already exists but with type %s\n", sectionTypeInfo[sect.type].name.c_str()
|
"Section already exists but with type %s\n", sectionTypeInfo[sect.type].name.c_str()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (sect.modifier != mod) {
|
if (sect.modifier != mod) {
|
||||||
sectError("Section already declared as %s section\n", sectionModNames[sect.modifier]);
|
sectError("Section already declared as %s section\n", sectionModNames[sect.modifier]);
|
||||||
@@ -240,11 +251,13 @@ static void mergeSections(
|
|||||||
// Common checks
|
// Common checks
|
||||||
|
|
||||||
// If the section's bank is unspecified, override it
|
// If the section's bank is unspecified, override it
|
||||||
if (sect.bank == UINT32_MAX)
|
if (sect.bank == UINT32_MAX) {
|
||||||
sect.bank = bank;
|
sect.bank = bank;
|
||||||
|
}
|
||||||
// If both specify a bank, it must be the same one
|
// If both specify a bank, it must be the same one
|
||||||
else if (bank != UINT32_MAX && sect.bank != bank)
|
else if (bank != UINT32_MAX && sect.bank != bank) {
|
||||||
sectError("Section already declared with different bank %" PRIu32 "\n", sect.bank);
|
sectError("Section already declared with different bank %" PRIu32 "\n", sect.bank);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SECTION_NORMAL:
|
case SECTION_NORMAL:
|
||||||
@@ -255,13 +268,14 @@ static void mergeSections(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbSectErrors)
|
if (nbSectErrors) {
|
||||||
fatalerror(
|
fatalerror(
|
||||||
"Cannot create section \"%s\" (%u error%s)\n",
|
"Cannot create section \"%s\" (%u error%s)\n",
|
||||||
sect.name.c_str(),
|
sect.name.c_str(),
|
||||||
nbSectErrors,
|
nbSectErrors,
|
||||||
nbSectErrors == 1 ? "" : "s"
|
nbSectErrors == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef sectError
|
#undef sectError
|
||||||
@@ -294,8 +308,9 @@ static Section *createSection(
|
|||||||
out_RegisterNode(sect.src);
|
out_RegisterNode(sect.src);
|
||||||
|
|
||||||
// It is only needed to allocate memory for ROM sections.
|
// It is only needed to allocate memory for ROM sections.
|
||||||
if (sect_HasData(type))
|
if (sect_HasData(type)) {
|
||||||
sect.data.resize(sectionTypeInfo[type].size);
|
sect.data.resize(sectionTypeInfo[type].size);
|
||||||
|
}
|
||||||
|
|
||||||
return §
|
return §
|
||||||
}
|
}
|
||||||
@@ -316,9 +331,10 @@ static Section *getSection(
|
|||||||
|
|
||||||
if (bank != UINT32_MAX) {
|
if (bank != UINT32_MAX) {
|
||||||
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM
|
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM
|
||||||
&& type != SECTTYPE_WRAMX)
|
&& type != SECTTYPE_WRAMX) {
|
||||||
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
|
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
|
||||||
else if (bank < sectionTypeInfo[type].firstBank || bank > sectionTypeInfo[type].lastBank)
|
} else if (bank < sectionTypeInfo[type].firstBank
|
||||||
|
|| bank > sectionTypeInfo[type].lastBank) {
|
||||||
error(
|
error(
|
||||||
"%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04" PRIx32 ")\n",
|
"%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04" PRIx32 ")\n",
|
||||||
sectionTypeInfo[type].name.c_str(),
|
sectionTypeInfo[type].name.c_str(),
|
||||||
@@ -326,6 +342,7 @@ static Section *getSection(
|
|||||||
sectionTypeInfo[type].firstBank,
|
sectionTypeInfo[type].firstBank,
|
||||||
sectionTypeInfo[type].lastBank
|
sectionTypeInfo[type].lastBank
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else if (nbbanks(type) == 1) {
|
} else if (nbbanks(type) == 1) {
|
||||||
// If the section type only has a single bank, implicitly force it
|
// If the section type only has a single bank, implicitly force it
|
||||||
bank = sectionTypeInfo[type].firstBank;
|
bank = sectionTypeInfo[type].firstBank;
|
||||||
@@ -341,7 +358,7 @@ static Section *getSection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (org != UINT32_MAX) {
|
if (org != UINT32_MAX) {
|
||||||
if (org < sectionTypeInfo[type].startAddr || org > endaddr(type))
|
if (org < sectionTypeInfo[type].startAddr || org > endaddr(type)) {
|
||||||
error(
|
error(
|
||||||
"Section \"%s\"'s fixed address $%04" PRIx32 " is outside of range [$%04" PRIx16
|
"Section \"%s\"'s fixed address $%04" PRIx32 " is outside of range [$%04" PRIx16
|
||||||
"; $%04" PRIx16 "]\n",
|
"; $%04" PRIx16 "]\n",
|
||||||
@@ -350,6 +367,7 @@ static Section *getSection(
|
|||||||
sectionTypeInfo[type].startAddr,
|
sectionTypeInfo[type].startAddr,
|
||||||
endaddr(type)
|
endaddr(type)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alignment != 0) {
|
if (alignment != 0) {
|
||||||
@@ -361,8 +379,9 @@ static Section *getSection(
|
|||||||
uint32_t mask = mask(alignment);
|
uint32_t mask = mask(alignment);
|
||||||
|
|
||||||
if (org != UINT32_MAX) {
|
if (org != UINT32_MAX) {
|
||||||
if ((org - alignOffset) & mask)
|
if ((org - alignOffset) & mask) {
|
||||||
error("Section \"%s\"'s fixed address doesn't match its alignment\n", name.c_str());
|
error("Section \"%s\"'s fixed address doesn't match its alignment\n", name.c_str());
|
||||||
|
}
|
||||||
alignment = 0; // Ignore it if it's satisfied
|
alignment = 0; // Ignore it if it's satisfied
|
||||||
} else if (sectionTypeInfo[type].startAddr & mask) {
|
} else if (sectionTypeInfo[type].startAddr & mask) {
|
||||||
error(
|
error(
|
||||||
@@ -395,25 +414,29 @@ static Section *getSection(
|
|||||||
|
|
||||||
// Set the current section
|
// Set the current section
|
||||||
static void changeSection() {
|
static void changeSection() {
|
||||||
if (!currentUnionStack.empty())
|
if (!currentUnionStack.empty()) {
|
||||||
fatalerror("Cannot change the section within a UNION\n");
|
fatalerror("Cannot change the section within a UNION\n");
|
||||||
|
}
|
||||||
|
|
||||||
sym_ResetCurrentLabelScopes();
|
sym_ResetCurrentLabelScopes();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Section::isSizeKnown() const {
|
bool Section::isSizeKnown() const {
|
||||||
// SECTION UNION and SECTION FRAGMENT can still grow
|
// SECTION UNION and SECTION FRAGMENT can still grow
|
||||||
if (modifier != SECTION_NORMAL)
|
if (modifier != SECTION_NORMAL) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// The current section (or current load section if within one) is still growing
|
// The current section (or current load section if within one) is still growing
|
||||||
if (this == currentSection || this == currentLoadSection)
|
if (this == currentSection || this == currentLoadSection) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Any section on the stack is still growing
|
// Any section on the stack is still growing
|
||||||
for (SectionStackEntry &entry : sectionStack) {
|
for (SectionStackEntry &entry : sectionStack) {
|
||||||
if (entry.section && entry.section->name == name)
|
if (entry.section && entry.section->name == name) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -428,12 +451,14 @@ void sect_NewSection(
|
|||||||
SectionModifier mod
|
SectionModifier mod
|
||||||
) {
|
) {
|
||||||
for (SectionStackEntry &entry : sectionStack) {
|
for (SectionStackEntry &entry : sectionStack) {
|
||||||
if (entry.section && entry.section->name == name)
|
if (entry.section && entry.section->name == name) {
|
||||||
fatalerror("Section '%s' is already on the stack\n", name.c_str());
|
fatalerror("Section '%s' is already on the stack\n", name.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentLoadSection)
|
if (currentLoadSection) {
|
||||||
sect_EndLoadSection("SECTION");
|
sect_EndLoadSection("SECTION");
|
||||||
|
}
|
||||||
|
|
||||||
Section *sect = getSection(name, type, org, attrs, mod);
|
Section *sect = getSection(name, type, org, attrs, mod);
|
||||||
|
|
||||||
@@ -456,16 +481,18 @@ void sect_SetLoadSection(
|
|||||||
// Therefore, any interactions are NOT TESTED, so lift either of those restrictions at
|
// Therefore, any interactions are NOT TESTED, so lift either of those restrictions at
|
||||||
// your own peril! ^^
|
// your own peril! ^^
|
||||||
|
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (sect_HasData(type)) {
|
if (sect_HasData(type)) {
|
||||||
error("`LOAD` blocks cannot create a ROM section\n");
|
error("`LOAD` blocks cannot create a ROM section\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentLoadSection)
|
if (currentLoadSection) {
|
||||||
sect_EndLoadSection("LOAD");
|
sect_EndLoadSection("LOAD");
|
||||||
|
}
|
||||||
|
|
||||||
Section *sect = getSection(name, type, org, attrs, mod);
|
Section *sect = getSection(name, type, org, attrs, mod);
|
||||||
|
|
||||||
@@ -477,10 +504,11 @@ void sect_SetLoadSection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sect_EndLoadSection(char const *cause) {
|
void sect_EndLoadSection(char const *cause) {
|
||||||
if (cause)
|
if (cause) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by `%s`\n", cause
|
WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by `%s`\n", cause
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentLoadSection) {
|
if (!currentLoadSection) {
|
||||||
error("Found `ENDL` outside of a `LOAD` block\n");
|
error("Found `ENDL` outside of a `LOAD` block\n");
|
||||||
@@ -495,8 +523,9 @@ void sect_EndLoadSection(char const *cause) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sect_CheckLoadClosed() {
|
void sect_CheckLoadClosed() {
|
||||||
if (currentLoadSection)
|
if (currentLoadSection) {
|
||||||
warning(WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by EOF\n");
|
warning(WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by EOF\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section *sect_GetSymbolSection() {
|
Section *sect_GetSymbolSection() {
|
||||||
@@ -515,16 +544,18 @@ uint32_t sect_GetOutputOffset() {
|
|||||||
// Returns how many bytes need outputting for the specified alignment and offset to succeed
|
// Returns how many bytes need outputting for the specified alignment and offset to succeed
|
||||||
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset) {
|
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset) {
|
||||||
Section *sect = sect_GetSymbolSection();
|
Section *sect = sect_GetSymbolSection();
|
||||||
if (!sect)
|
if (!sect) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool isFixed = sect->org != UINT32_MAX;
|
bool isFixed = sect->org != UINT32_MAX;
|
||||||
|
|
||||||
// If the section is not aligned, no bytes are needed
|
// If the section is not aligned, no bytes are needed
|
||||||
// (fixed sections count as being maximally aligned for this purpose)
|
// (fixed sections count as being maximally aligned for this purpose)
|
||||||
uint8_t curAlignment = isFixed ? 16 : sect->align;
|
uint8_t curAlignment = isFixed ? 16 : sect->align;
|
||||||
if (curAlignment == 0)
|
if (curAlignment == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// We need `(pcValue + curOffset + return value) % (1 << alignment) == offset`
|
// We need `(pcValue + curOffset + return value) % (1 << alignment) == offset`
|
||||||
uint16_t pcValue = isFixed ? sect->org : sect->alignOfs;
|
uint16_t pcValue = isFixed ? sect->org : sect->alignOfs;
|
||||||
@@ -533,18 +564,20 @@ uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sect_AlignPC(uint8_t alignment, uint16_t offset) {
|
void sect_AlignPC(uint8_t alignment, uint16_t offset) {
|
||||||
if (!requireSection())
|
if (!requireSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Section *sect = sect_GetSymbolSection();
|
Section *sect = sect_GetSymbolSection();
|
||||||
uint32_t alignSize = 1 << alignment; // Size of an aligned "block"
|
uint32_t alignSize = 1 << alignment; // Size of an aligned "block"
|
||||||
|
|
||||||
if (sect->org != UINT32_MAX) {
|
if (sect->org != UINT32_MAX) {
|
||||||
if ((sect->org + curOffset - offset) % alignSize)
|
if ((sect->org + curOffset - offset) % alignSize) {
|
||||||
error(
|
error(
|
||||||
"Section's fixed address fails required alignment (PC = $%04" PRIx32 ")\n",
|
"Section's fixed address fails required alignment (PC = $%04" PRIx32 ")\n",
|
||||||
sect->org + curOffset
|
sect->org + curOffset
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else if (sect->align != 0
|
} else if (sect->align != 0
|
||||||
&& (((sect->alignOfs + curOffset) % (1u << sect->align)) - offset) % alignSize) {
|
&& (((sect->alignOfs + curOffset) % (1u << sect->align)) - offset) % alignSize) {
|
||||||
error(
|
error(
|
||||||
@@ -568,18 +601,22 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void growSection(uint32_t growth) {
|
static void growSection(uint32_t growth) {
|
||||||
if (growth > 0 && curOffset > UINT32_MAX - growth)
|
if (growth > 0 && curOffset > UINT32_MAX - growth) {
|
||||||
fatalerror("Section size would overflow internal counter\n");
|
fatalerror("Section size would overflow internal counter\n");
|
||||||
|
}
|
||||||
curOffset += growth;
|
curOffset += growth;
|
||||||
if (uint32_t outOffset = sect_GetOutputOffset(); outOffset > currentSection->size)
|
if (uint32_t outOffset = sect_GetOutputOffset(); outOffset > currentSection->size) {
|
||||||
currentSection->size = outOffset;
|
currentSection->size = outOffset;
|
||||||
if (currentLoadSection && curOffset > currentLoadSection->size)
|
}
|
||||||
|
if (currentLoadSection && curOffset > currentLoadSection->size) {
|
||||||
currentLoadSection->size = curOffset;
|
currentLoadSection->size = curOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeByte(uint8_t byte) {
|
static void writeByte(uint8_t byte) {
|
||||||
if (uint32_t index = sect_GetOutputOffset(); index < currentSection->data.size())
|
if (uint32_t index = sect_GetOutputOffset(); index < currentSection->data.size()) {
|
||||||
currentSection->data[index] = byte;
|
currentSection->data[index] = byte;
|
||||||
|
}
|
||||||
growSection(1);
|
growSection(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,8 +658,9 @@ static void endUnionMember() {
|
|||||||
UnionStackEntry &member = currentUnionStack.top();
|
UnionStackEntry &member = currentUnionStack.top();
|
||||||
uint32_t memberSize = curOffset - member.start;
|
uint32_t memberSize = curOffset - member.start;
|
||||||
|
|
||||||
if (memberSize > member.size)
|
if (memberSize > member.size) {
|
||||||
member.size = memberSize;
|
member.size = memberSize;
|
||||||
|
}
|
||||||
curOffset = member.start;
|
curOffset = member.start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,64 +683,75 @@ void sect_EndUnion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sect_CheckUnionClosed() {
|
void sect_CheckUnionClosed() {
|
||||||
if (!currentUnionStack.empty())
|
if (!currentUnionStack.empty()) {
|
||||||
error("Unterminated UNION construct\n");
|
error("Unterminated UNION construct\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a constant byte
|
// Output a constant byte
|
||||||
void sect_ConstByte(uint8_t byte) {
|
void sect_ConstByte(uint8_t byte) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
writeByte(byte);
|
writeByte(byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a string's character units as bytes
|
// Output a string's character units as bytes
|
||||||
void sect_ByteString(std::vector<int32_t> const &string) {
|
void sect_ByteString(std::vector<int32_t> const &string) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int32_t unit : string) {
|
|
||||||
if (!checkNBit(unit, 8, "All character units"))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32_t unit : string)
|
for (int32_t unit : string) {
|
||||||
|
if (!checkNBit(unit, 8, "All character units")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t unit : string) {
|
||||||
writeByte(static_cast<uint8_t>(unit));
|
writeByte(static_cast<uint8_t>(unit));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a string's character units as words
|
// Output a string's character units as words
|
||||||
void sect_WordString(std::vector<int32_t> const &string) {
|
void sect_WordString(std::vector<int32_t> const &string) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int32_t unit : string) {
|
|
||||||
if (!checkNBit(unit, 16, "All character units"))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32_t unit : string)
|
for (int32_t unit : string) {
|
||||||
|
if (!checkNBit(unit, 16, "All character units")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t unit : string) {
|
||||||
writeWord(static_cast<uint16_t>(unit));
|
writeWord(static_cast<uint16_t>(unit));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a string's character units as longs
|
// Output a string's character units as longs
|
||||||
void sect_LongString(std::vector<int32_t> const &string) {
|
void sect_LongString(std::vector<int32_t> const &string) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int32_t unit : string)
|
for (int32_t unit : string) {
|
||||||
writeLong(static_cast<uint32_t>(unit));
|
writeLong(static_cast<uint32_t>(unit));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip this many bytes
|
// Skip this many bytes
|
||||||
void sect_Skip(uint32_t skip, bool ds) {
|
void sect_Skip(uint32_t skip, bool ds) {
|
||||||
if (!requireSection())
|
if (!requireSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!sect_HasData(currentSection->type)) {
|
if (!sect_HasData(currentSection->type)) {
|
||||||
growSection(skip);
|
growSection(skip);
|
||||||
} else {
|
} else {
|
||||||
if (!ds)
|
if (!ds) {
|
||||||
warning(
|
warning(
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||||
"%s directive without data in ROM\n",
|
"%s directive without data in ROM\n",
|
||||||
@@ -710,16 +759,19 @@ void sect_Skip(uint32_t skip, bool ds) {
|
|||||||
: (skip == 2) ? "DW"
|
: (skip == 2) ? "DW"
|
||||||
: "DB"
|
: "DB"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// We know we're in a code SECTION
|
// We know we're in a code SECTION
|
||||||
while (skip--)
|
while (skip--) {
|
||||||
writeByte(fillByte);
|
writeByte(fillByte);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a byte that can be relocatable or constant
|
// Output a byte that can be relocatable or constant
|
||||||
void sect_RelByte(Expression &expr, uint32_t pcShift) {
|
void sect_RelByte(Expression &expr, uint32_t pcShift) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!expr.isKnown()) {
|
if (!expr.isKnown()) {
|
||||||
createPatch(PATCHTYPE_BYTE, expr, pcShift);
|
createPatch(PATCHTYPE_BYTE, expr, pcShift);
|
||||||
@@ -731,8 +783,9 @@ void sect_RelByte(Expression &expr, uint32_t pcShift) {
|
|||||||
|
|
||||||
// Output several bytes that can be relocatable or constant
|
// Output several bytes that can be relocatable or constant
|
||||||
void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs) {
|
void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < n; i++) {
|
for (uint32_t i = 0; i < n; i++) {
|
||||||
Expression &expr = exprs[i % exprs.size()];
|
Expression &expr = exprs[i % exprs.size()];
|
||||||
@@ -748,8 +801,9 @@ void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs) {
|
|||||||
|
|
||||||
// Output a word that can be relocatable or constant
|
// Output a word that can be relocatable or constant
|
||||||
void sect_RelWord(Expression &expr, uint32_t pcShift) {
|
void sect_RelWord(Expression &expr, uint32_t pcShift) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!expr.isKnown()) {
|
if (!expr.isKnown()) {
|
||||||
createPatch(PATCHTYPE_WORD, expr, pcShift);
|
createPatch(PATCHTYPE_WORD, expr, pcShift);
|
||||||
@@ -761,8 +815,9 @@ void sect_RelWord(Expression &expr, uint32_t pcShift) {
|
|||||||
|
|
||||||
// Output a long that can be relocatable or constant
|
// Output a long that can be relocatable or constant
|
||||||
void sect_RelLong(Expression &expr, uint32_t pcShift) {
|
void sect_RelLong(Expression &expr, uint32_t pcShift) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!expr.isKnown()) {
|
if (!expr.isKnown()) {
|
||||||
createPatch(PATCHTYPE_LONG, expr, pcShift);
|
createPatch(PATCHTYPE_LONG, expr, pcShift);
|
||||||
@@ -774,8 +829,9 @@ void sect_RelLong(Expression &expr, uint32_t pcShift) {
|
|||||||
|
|
||||||
// Output a PC-relative byte that can be relocatable or constant
|
// Output a PC-relative byte that can be relocatable or constant
|
||||||
void sect_PCRelByte(Expression &expr, uint32_t pcShift) {
|
void sect_PCRelByte(Expression &expr, uint32_t pcShift) {
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Symbol const *pc = sym_GetPC(); !expr.isDiffConstant(pc)) {
|
if (Symbol const *pc = sym_GetPC(); !expr.isDiffConstant(pc)) {
|
||||||
createPatch(PATCHTYPE_JR, expr, pcShift);
|
createPatch(PATCHTYPE_JR, expr, pcShift);
|
||||||
@@ -786,10 +842,11 @@ void sect_PCRelByte(Expression &expr, uint32_t pcShift) {
|
|||||||
int16_t offset;
|
int16_t offset;
|
||||||
|
|
||||||
// Offset is relative to the byte *after* the operand
|
// Offset is relative to the byte *after* the operand
|
||||||
if (sym == pc)
|
if (sym == pc) {
|
||||||
offset = -2; // PC as operand to `jr` is lower than reference PC by 2
|
offset = -2; // PC as operand to `jr` is lower than reference PC by 2
|
||||||
else
|
} else {
|
||||||
offset = sym->getValue() - (pc->getValue() + 1);
|
offset = sym->getValue() - (pc->getValue() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (offset < -128 || offset > 127) {
|
if (offset < -128 || offset > 127) {
|
||||||
error(
|
error(
|
||||||
@@ -810,16 +867,19 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
|
|||||||
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
|
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
|
||||||
startPos = 0;
|
startPos = 0;
|
||||||
}
|
}
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *file = nullptr;
|
FILE *file = nullptr;
|
||||||
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath)
|
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||||
file = fopen(fullPath->c_str(), "rb");
|
file = fopen(fullPath->c_str(), "rb");
|
||||||
|
}
|
||||||
if (!file) {
|
if (!file) {
|
||||||
if (generatedMissingIncludes) {
|
if (generatedMissingIncludes) {
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", name.c_str(), strerror(errno));
|
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", name.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
failedOnMissingInclude = true;
|
failedOnMissingInclude = true;
|
||||||
} else {
|
} else {
|
||||||
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
||||||
@@ -836,10 +896,11 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
|
|||||||
// The file is seekable; skip to the specified start position
|
// The file is seekable; skip to the specified start position
|
||||||
fseek(file, startPos, SEEK_SET);
|
fseek(file, startPos, SEEK_SET);
|
||||||
} else {
|
} else {
|
||||||
if (errno != ESPIPE)
|
if (errno != ESPIPE) {
|
||||||
error(
|
error(
|
||||||
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
|
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// The file isn't seekable, so we'll just skip bytes one at a time
|
// The file isn't seekable, so we'll just skip bytes one at a time
|
||||||
while (startPos--) {
|
while (startPos--) {
|
||||||
if (fgetc(file) == EOF) {
|
if (fgetc(file) == EOF) {
|
||||||
@@ -851,11 +912,13 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int byte; (byte = fgetc(file)) != EOF;)
|
for (int byte; (byte = fgetc(file)) != EOF;) {
|
||||||
writeByte(byte);
|
writeByte(byte);
|
||||||
|
}
|
||||||
|
|
||||||
if (ferror(file))
|
if (ferror(file)) {
|
||||||
error("Error reading INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
error("Error reading INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output a slice of a binary file
|
// Output a slice of a binary file
|
||||||
@@ -868,18 +931,22 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
|
|||||||
error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length);
|
error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length);
|
||||||
length = 0;
|
length = 0;
|
||||||
}
|
}
|
||||||
if (!requireCodeSection())
|
if (!requireCodeSection()) {
|
||||||
return;
|
return;
|
||||||
if (length == 0) // Don't even bother with 0-byte slices
|
}
|
||||||
|
if (length == 0) { // Don't even bother with 0-byte slices
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *file = nullptr;
|
FILE *file = nullptr;
|
||||||
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath)
|
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||||
file = fopen(fullPath->c_str(), "rb");
|
file = fopen(fullPath->c_str(), "rb");
|
||||||
|
}
|
||||||
if (!file) {
|
if (!file) {
|
||||||
if (generatedMissingIncludes) {
|
if (generatedMissingIncludes) {
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", name.c_str(), strerror(errno));
|
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", name.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
failedOnMissingInclude = true;
|
failedOnMissingInclude = true;
|
||||||
} else {
|
} else {
|
||||||
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
|
||||||
@@ -906,10 +973,11 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
|
|||||||
// The file is seekable; skip to the specified start position
|
// The file is seekable; skip to the specified start position
|
||||||
fseek(file, startPos, SEEK_SET);
|
fseek(file, startPos, SEEK_SET);
|
||||||
} else {
|
} else {
|
||||||
if (errno != ESPIPE)
|
if (errno != ESPIPE) {
|
||||||
error(
|
error(
|
||||||
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
|
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// The file isn't seekable, so we'll just skip bytes one at a time
|
// The file isn't seekable, so we'll just skip bytes one at a time
|
||||||
while (startPos--) {
|
while (startPos--) {
|
||||||
if (fgetc(file) == EOF) {
|
if (fgetc(file) == EOF) {
|
||||||
@@ -955,11 +1023,13 @@ void sect_PushSection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sect_PopSection() {
|
void sect_PopSection() {
|
||||||
if (sectionStack.empty())
|
if (sectionStack.empty()) {
|
||||||
fatalerror("No entries in the section stack\n");
|
fatalerror("No entries in the section stack\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (currentLoadSection)
|
if (currentLoadSection) {
|
||||||
sect_EndLoadSection("POPS");
|
sect_EndLoadSection("POPS");
|
||||||
|
}
|
||||||
|
|
||||||
SectionStackEntry entry = sectionStack.front();
|
SectionStackEntry entry = sectionStack.front();
|
||||||
sectionStack.pop_front();
|
sectionStack.pop_front();
|
||||||
@@ -980,14 +1050,17 @@ void sect_CheckStack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sect_EndSection() {
|
void sect_EndSection() {
|
||||||
if (!currentSection)
|
if (!currentSection) {
|
||||||
fatalerror("Cannot end the section outside of a SECTION\n");
|
fatalerror("Cannot end the section outside of a SECTION\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentUnionStack.empty())
|
if (!currentUnionStack.empty()) {
|
||||||
fatalerror("Cannot end the section within a UNION\n");
|
fatalerror("Cannot end the section within a UNION\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (currentLoadSection)
|
if (currentLoadSection) {
|
||||||
sect_EndLoadSection("ENDSECTION");
|
sect_EndLoadSection("ENDSECTION");
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the section scope
|
// Reset the section scope
|
||||||
currentSection = nullptr;
|
currentSection = nullptr;
|
||||||
|
|||||||
@@ -40,8 +40,9 @@ bool sym_IsPC(Symbol const *sym) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sym_ForEach(void (*callback)(Symbol &)) {
|
void sym_ForEach(void (*callback)(Symbol &)) {
|
||||||
for (auto &it : symbols)
|
for (auto &it : symbols) {
|
||||||
callback(it.second);
|
callback(it.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t NARGCallback() {
|
static int32_t NARGCallback() {
|
||||||
@@ -101,8 +102,9 @@ std::shared_ptr<std::string> Symbol::getEqus() const {
|
|||||||
std::holds_alternative<std::shared_ptr<std::string>>(data)
|
std::holds_alternative<std::shared_ptr<std::string>>(data)
|
||||||
|| std::holds_alternative<std::shared_ptr<std::string> (*)()>(data)
|
|| std::holds_alternative<std::shared_ptr<std::string> (*)()>(data)
|
||||||
);
|
);
|
||||||
if (auto *callback = std::get_if<std::shared_ptr<std::string> (*)()>(&data); callback)
|
if (auto *callback = std::get_if<std::shared_ptr<std::string> (*)()>(&data); callback) {
|
||||||
return (*callback)();
|
return (*callback)();
|
||||||
|
}
|
||||||
return std::get<std::shared_ptr<std::string>>(data);
|
return std::get<std::shared_ptr<std::string>>(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +125,9 @@ static void updateSymbolFilename(Symbol &sym) {
|
|||||||
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
||||||
|
|
||||||
// If the old node was registered, ensure the new one is too
|
// If the old node was registered, ensure the new one is too
|
||||||
if (oldSrc && oldSrc->ID != UINT32_MAX)
|
if (oldSrc && oldSrc->ID != UINT32_MAX) {
|
||||||
out_RegisterNode(sym.src);
|
out_RegisterNode(sym.src);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alreadyDefinedError(Symbol const &sym, char const *asType) {
|
static void alreadyDefinedError(Symbol const &sym, char const *asType) {
|
||||||
@@ -133,8 +136,9 @@ static void alreadyDefinedError(Symbol const &sym, char const *asType) {
|
|||||||
error("'%s' is reserved for a built-in symbol\n", sym.name.c_str());
|
error("'%s' is reserved for a built-in symbol\n", sym.name.c_str());
|
||||||
} else {
|
} else {
|
||||||
error("'%s' already defined", sym.name.c_str());
|
error("'%s' already defined", sym.name.c_str());
|
||||||
if (asType)
|
if (asType) {
|
||||||
fprintf(stderr, " as %s", asType);
|
fprintf(stderr, " as %s", asType);
|
||||||
|
}
|
||||||
fputs(" at ", stderr);
|
fputs(" at ", stderr);
|
||||||
dumpFilename(sym);
|
dumpFilename(sym);
|
||||||
}
|
}
|
||||||
@@ -184,28 +188,34 @@ static bool isAutoScoped(std::string const &symName) {
|
|||||||
size_t dotPos = symName.find('.');
|
size_t dotPos = symName.find('.');
|
||||||
|
|
||||||
// If there are no dots, it's not a local label
|
// If there are no dots, it's not a local label
|
||||||
if (dotPos == std::string::npos)
|
if (dotPos == std::string::npos) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
||||||
if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos)
|
if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for nothing after the dot
|
// Check for nothing after the dot
|
||||||
if (dotPos == symName.length() - 1)
|
if (dotPos == symName.length() - 1) {
|
||||||
fatalerror("'%s' is a nonsensical reference to an empty local label\n", symName.c_str());
|
fatalerror("'%s' is a nonsensical reference to an empty local label\n", symName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// Check for more than one dot
|
// Check for more than one dot
|
||||||
if (symName.find('.', dotPos + 1) != std::string::npos)
|
if (symName.find('.', dotPos + 1) != std::string::npos) {
|
||||||
fatalerror("'%s' is a nonsensical reference to a nested local label\n", symName.c_str());
|
fatalerror("'%s' is a nonsensical reference to a nested local label\n", symName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// Check for already-qualified local label
|
// Check for already-qualified local label
|
||||||
if (dotPos > 0)
|
if (dotPos > 0) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for unqualifiable local label
|
// Check for unqualifiable local label
|
||||||
if (!globalScope)
|
if (!globalScope) {
|
||||||
fatalerror("Unqualified local label '%s' in main scope\n", symName.c_str());
|
fatalerror("Unqualified local label '%s' in main scope\n", symName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -252,24 +262,28 @@ void sym_Purge(std::string const &symName) {
|
|||||||
Symbol *sym = sym_FindScopedValidSymbol(symName);
|
Symbol *sym = sym_FindScopedValidSymbol(symName);
|
||||||
|
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
if (sym_IsPurgedScoped(symName))
|
if (sym_IsPurgedScoped(symName)) {
|
||||||
error("'%s' was already purged\n", symName.c_str());
|
error("'%s' was already purged\n", symName.c_str());
|
||||||
else
|
} else {
|
||||||
error("'%s' not defined\n", symName.c_str());
|
error("'%s' not defined\n", symName.c_str());
|
||||||
|
}
|
||||||
} else if (sym->isBuiltin) {
|
} else if (sym->isBuiltin) {
|
||||||
error("Built-in symbol '%s' cannot be purged\n", symName.c_str());
|
error("Built-in symbol '%s' cannot be purged\n", symName.c_str());
|
||||||
} else if (sym->ID != UINT32_MAX) {
|
} else if (sym->ID != UINT32_MAX) {
|
||||||
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName.c_str());
|
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName.c_str());
|
||||||
} else {
|
} else {
|
||||||
if (sym->isExported)
|
if (sym->isExported) {
|
||||||
warning(WARNING_PURGE_1, "Purging an exported symbol \"%s\"\n", symName.c_str());
|
warning(WARNING_PURGE_1, "Purging an exported symbol \"%s\"\n", symName.c_str());
|
||||||
else if (sym->isLabel())
|
} else if (sym->isLabel()) {
|
||||||
warning(WARNING_PURGE_2, "Purging a label \"%s\"\n", symName.c_str());
|
warning(WARNING_PURGE_2, "Purging a label \"%s\"\n", symName.c_str());
|
||||||
|
}
|
||||||
// Do not keep a reference to the label after purging it
|
// Do not keep a reference to the label after purging it
|
||||||
if (sym == globalScope)
|
if (sym == globalScope) {
|
||||||
globalScope = nullptr;
|
globalScope = nullptr;
|
||||||
if (sym == localScope)
|
}
|
||||||
|
if (sym == localScope) {
|
||||||
localScope = nullptr;
|
localScope = nullptr;
|
||||||
|
}
|
||||||
purgedSymbols.emplace(sym->name);
|
purgedSymbols.emplace(sym->name);
|
||||||
symbols.erase(sym->name);
|
symbols.erase(sym->name);
|
||||||
}
|
}
|
||||||
@@ -295,14 +309,16 @@ void sym_SetRSValue(int32_t value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Symbol::getConstantValue() const {
|
uint32_t Symbol::getConstantValue() const {
|
||||||
if (isConstant())
|
if (isConstant()) {
|
||||||
return getValue();
|
return getValue();
|
||||||
|
}
|
||||||
|
|
||||||
if (sym_IsPC(this)) {
|
if (sym_IsPC(this)) {
|
||||||
if (!getSection())
|
if (!getSection()) {
|
||||||
error("PC has no value outside of a section\n");
|
error("PC has no value outside of a section\n");
|
||||||
else
|
} else {
|
||||||
error("PC does not have a constant value; the current section is not fixed\n");
|
error("PC does not have a constant value; the current section is not fixed\n");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error("\"%s\" does not have a constant value\n", name.c_str());
|
error("\"%s\" does not have a constant value\n", name.c_str());
|
||||||
}
|
}
|
||||||
@@ -310,13 +326,15 @@ uint32_t Symbol::getConstantValue() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t sym_GetConstantValue(std::string const &symName) {
|
uint32_t sym_GetConstantValue(std::string const &symName) {
|
||||||
if (Symbol const *sym = sym_FindScopedSymbol(symName); sym)
|
if (Symbol const *sym = sym_FindScopedSymbol(symName); sym) {
|
||||||
return sym->getConstantValue();
|
return sym->getConstantValue();
|
||||||
|
}
|
||||||
|
|
||||||
if (sym_IsPurgedScoped(symName))
|
if (sym_IsPurgedScoped(symName)) {
|
||||||
error("'%s' not defined; it was purged\n", symName.c_str());
|
error("'%s' not defined; it was purged\n", symName.c_str());
|
||||||
else
|
} else {
|
||||||
error("'%s' not defined\n", symName.c_str());
|
error("'%s' not defined\n", symName.c_str());
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,8 +379,9 @@ static Symbol *createNonrelocSymbol(std::string const &symName, bool numeric) {
|
|||||||
Symbol *sym_AddEqu(std::string const &symName, int32_t value) {
|
Symbol *sym_AddEqu(std::string const &symName, int32_t value) {
|
||||||
Symbol *sym = createNonrelocSymbol(symName, true);
|
Symbol *sym = createNonrelocSymbol(symName, true);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
sym->type = SYM_EQU;
|
sym->type = SYM_EQU;
|
||||||
sym->data = value;
|
sym->data = value;
|
||||||
@@ -373,8 +392,9 @@ Symbol *sym_AddEqu(std::string const &symName, int32_t value) {
|
|||||||
Symbol *sym_RedefEqu(std::string const &symName, int32_t value) {
|
Symbol *sym_RedefEqu(std::string const &symName, int32_t value) {
|
||||||
Symbol *sym = sym_FindExactSymbol(symName);
|
Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return sym_AddEqu(symName, value);
|
return sym_AddEqu(symName, value);
|
||||||
|
}
|
||||||
|
|
||||||
if (sym->isDefined() && sym->type != SYM_EQU) {
|
if (sym->isDefined() && sym->type != SYM_EQU) {
|
||||||
alreadyDefinedError(*sym, "non-EQU");
|
alreadyDefinedError(*sym, "non-EQU");
|
||||||
@@ -394,8 +414,9 @@ Symbol *sym_RedefEqu(std::string const &symName, int32_t value) {
|
|||||||
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> str) {
|
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> str) {
|
||||||
Symbol *sym = createNonrelocSymbol(symName, false);
|
Symbol *sym = createNonrelocSymbol(symName, false);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
sym->type = SYM_EQUS;
|
sym->type = SYM_EQUS;
|
||||||
sym->data = str;
|
sym->data = str;
|
||||||
@@ -405,8 +426,9 @@ Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> s
|
|||||||
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> str) {
|
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> str) {
|
||||||
Symbol *sym = sym_FindExactSymbol(symName);
|
Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return sym_AddString(symName, str);
|
return sym_AddString(symName, str);
|
||||||
|
}
|
||||||
|
|
||||||
if (sym->type != SYM_EQUS) {
|
if (sym->type != SYM_EQUS) {
|
||||||
if (sym->isDefined()) {
|
if (sym->isDefined()) {
|
||||||
@@ -462,12 +484,14 @@ static Symbol *addLabel(std::string const &symName) {
|
|||||||
sym->type = SYM_LABEL;
|
sym->type = SYM_LABEL;
|
||||||
sym->data = static_cast<int32_t>(sect_GetSymbolOffset());
|
sym->data = static_cast<int32_t>(sect_GetSymbolOffset());
|
||||||
// Don't export anonymous labels
|
// Don't export anonymous labels
|
||||||
if (exportAll && !symName.starts_with('!'))
|
if (exportAll && !symName.starts_with('!')) {
|
||||||
sym->isExported = true;
|
sym->isExported = true;
|
||||||
|
}
|
||||||
sym->section = sect_GetSymbolSection();
|
sym->section = sect_GetSymbolSection();
|
||||||
|
|
||||||
if (sym && !sym->section)
|
if (sym && !sym->section) {
|
||||||
error("Label \"%s\" created outside of a SECTION\n", symName.c_str());
|
error("Label \"%s\" created outside of a SECTION\n", symName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
@@ -478,8 +502,9 @@ Symbol *sym_AddLocalLabel(std::string const &symName) {
|
|||||||
|
|
||||||
Symbol *sym = addLabel(isAutoScoped(symName) ? globalScope->name + symName : symName);
|
Symbol *sym = addLabel(isAutoScoped(symName) ? globalScope->name + symName : symName);
|
||||||
|
|
||||||
if (sym)
|
if (sym) {
|
||||||
localScope = sym;
|
localScope = sym;
|
||||||
|
}
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
@@ -516,7 +541,7 @@ std::string sym_MakeAnonLabelName(uint32_t ofs, bool neg) {
|
|||||||
uint32_t id = 0;
|
uint32_t id = 0;
|
||||||
|
|
||||||
if (neg) {
|
if (neg) {
|
||||||
if (ofs > anonLabelID)
|
if (ofs > anonLabelID) {
|
||||||
error(
|
error(
|
||||||
"Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
|
"Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
|
||||||
" ha%s been created so far\n",
|
" ha%s been created so far\n",
|
||||||
@@ -524,19 +549,21 @@ std::string sym_MakeAnonLabelName(uint32_t ofs, bool neg) {
|
|||||||
anonLabelID,
|
anonLabelID,
|
||||||
anonLabelID == 1 ? "s" : "ve"
|
anonLabelID == 1 ? "s" : "ve"
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
id = anonLabelID - ofs;
|
id = anonLabelID - ofs;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ofs--; // We're referencing symbols that haven't been created yet...
|
ofs--; // We're referencing symbols that haven't been created yet...
|
||||||
if (ofs > UINT32_MAX - anonLabelID)
|
if (ofs > UINT32_MAX - anonLabelID) {
|
||||||
error(
|
error(
|
||||||
"Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
|
"Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
|
||||||
" may still be created\n",
|
" may still be created\n",
|
||||||
ofs + 1,
|
ofs + 1,
|
||||||
UINT32_MAX - anonLabelID
|
UINT32_MAX - anonLabelID
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
id = anonLabelID + ofs;
|
id = anonLabelID + ofs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string anon("!");
|
std::string anon("!");
|
||||||
@@ -553,16 +580,18 @@ void sym_Export(std::string const &symName) {
|
|||||||
Symbol *sym = sym_FindScopedSymbol(symName);
|
Symbol *sym = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
// If the symbol doesn't exist, create a ref that can be purged
|
// If the symbol doesn't exist, create a ref that can be purged
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
sym = sym_Ref(symName);
|
sym = sym_Ref(symName);
|
||||||
|
}
|
||||||
sym->isExported = true;
|
sym->isExported = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol *sym_AddMacro(std::string const &symName, int32_t defLineNo, ContentSpan const &span) {
|
Symbol *sym_AddMacro(std::string const &symName, int32_t defLineNo, ContentSpan const &span) {
|
||||||
Symbol *sym = createNonrelocSymbol(symName, false);
|
Symbol *sym = createNonrelocSymbol(symName, false);
|
||||||
|
|
||||||
if (!sym)
|
if (!sym) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
sym->type = SYM_MACRO;
|
sym->type = SYM_MACRO;
|
||||||
sym->data = span;
|
sym->data = span;
|
||||||
|
|||||||
@@ -85,8 +85,9 @@ enum WarningBehavior { DISABLED, ENABLED, ERROR };
|
|||||||
|
|
||||||
static WarningBehavior getWarningBehavior(WarningID id) {
|
static WarningBehavior getWarningBehavior(WarningID id) {
|
||||||
// Check if warnings are globally disabled
|
// Check if warnings are globally disabled
|
||||||
if (!warnings)
|
if (!warnings) {
|
||||||
return WarningBehavior::DISABLED;
|
return WarningBehavior::DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the state of this warning flag
|
// Get the state of this warning flag
|
||||||
WarningState const &flagState = warningStates.flagStates[id];
|
WarningState const &flagState = warningStates.flagStates[id];
|
||||||
@@ -100,34 +101,43 @@ static WarningBehavior getWarningBehavior(WarningID id) {
|
|||||||
warningIsError ? WarningBehavior::ERROR : WarningBehavior::ENABLED;
|
warningIsError ? WarningBehavior::ERROR : WarningBehavior::ENABLED;
|
||||||
|
|
||||||
// First, check the state of the specific warning flag
|
// First, check the state of the specific warning flag
|
||||||
if (flagState.state == WARNING_DISABLED) // -Wno-<flag>
|
if (flagState.state == WARNING_DISABLED) { // -Wno-<flag>
|
||||||
return WarningBehavior::DISABLED;
|
return WarningBehavior::DISABLED;
|
||||||
if (flagState.error == WARNING_ENABLED) // -Werror=<flag>
|
}
|
||||||
|
if (flagState.error == WARNING_ENABLED) { // -Werror=<flag>
|
||||||
return WarningBehavior::ERROR;
|
return WarningBehavior::ERROR;
|
||||||
if (flagState.state == WARNING_ENABLED) // -W<flag>
|
}
|
||||||
|
if (flagState.state == WARNING_ENABLED) { // -W<flag>
|
||||||
return enabledBehavior;
|
return enabledBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
// If no flag is specified, check the state of the "meta" flags that affect this warning flag
|
// If no flag is specified, check the state of the "meta" flags that affect this warning flag
|
||||||
if (metaState.state == WARNING_DISABLED) // -Wno-<meta>
|
if (metaState.state == WARNING_DISABLED) { // -Wno-<meta>
|
||||||
return WarningBehavior::DISABLED;
|
return WarningBehavior::DISABLED;
|
||||||
if (metaState.error == WARNING_ENABLED) // -Werror=<meta>
|
}
|
||||||
|
if (metaState.error == WARNING_ENABLED) { // -Werror=<meta>
|
||||||
return WarningBehavior::ERROR;
|
return WarningBehavior::ERROR;
|
||||||
if (metaState.state == WARNING_ENABLED) // -W<meta>
|
}
|
||||||
|
if (metaState.state == WARNING_ENABLED) { // -W<meta>
|
||||||
return enabledBehavior;
|
return enabledBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
// If no meta flag is specified, check the default state of this warning flag
|
// If no meta flag is specified, check the default state of this warning flag
|
||||||
if (warningFlags[id].level == LEVEL_DEFAULT) // enabled by default
|
if (warningFlags[id].level == LEVEL_DEFAULT) { // enabled by default
|
||||||
return enabledBehavior;
|
return enabledBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
// No flag enables this warning, explicitly or implicitly
|
// No flag enables this warning, explicitly or implicitly
|
||||||
return WarningBehavior::DISABLED;
|
return WarningBehavior::DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WarningState::update(WarningState other) {
|
void WarningState::update(WarningState other) {
|
||||||
if (other.state != WARNING_DEFAULT)
|
if (other.state != WARNING_DEFAULT) {
|
||||||
state = other.state;
|
state = other.state;
|
||||||
if (other.error != WARNING_DEFAULT)
|
}
|
||||||
|
if (other.error != WARNING_DEFAULT) {
|
||||||
error = other.error;
|
error = other.error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void processWarningFlag(char const *flag) {
|
void processWarningFlag(char const *flag) {
|
||||||
@@ -184,12 +194,14 @@ void processWarningFlag(char const *flag) {
|
|||||||
// The `if`'s condition above ensures that this will run at least once
|
// The `if`'s condition above ensures that this will run at least once
|
||||||
do {
|
do {
|
||||||
// If we don't have a digit, bail
|
// If we don't have a digit, bail
|
||||||
if (*ptr < '0' || *ptr > '9')
|
if (*ptr < '0' || *ptr > '9') {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
// Avoid overflowing!
|
// Avoid overflowing!
|
||||||
if (param > UINT8_MAX - (*ptr - '0')) {
|
if (param > UINT8_MAX - (*ptr - '0')) {
|
||||||
if (!warned)
|
if (!warned) {
|
||||||
warnx("Invalid warning flag \"%s\": capping parameter at 255", flag);
|
warnx("Invalid warning flag \"%s\": capping parameter at 255", flag);
|
||||||
|
}
|
||||||
warned = true; // Only warn once, cap always
|
warned = true; // Only warn once, cap always
|
||||||
param = 255;
|
param = 255;
|
||||||
continue;
|
continue;
|
||||||
@@ -203,8 +215,9 @@ void processWarningFlag(char const *flag) {
|
|||||||
if (*ptr == '\0') {
|
if (*ptr == '\0') {
|
||||||
rootFlag.resize(equals);
|
rootFlag.resize(equals);
|
||||||
// `-W<flag>=0` is equivalent to `-Wno-<flag>`
|
// `-W<flag>=0` is equivalent to `-Wno-<flag>`
|
||||||
if (param == 0)
|
if (param == 0) {
|
||||||
state.state = WARNING_DISABLED;
|
state.state = WARNING_DISABLED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,8 +231,9 @@ void processWarningFlag(char const *flag) {
|
|||||||
assume(paramWarning.defaultLevel <= maxParam);
|
assume(paramWarning.defaultLevel <= maxParam);
|
||||||
|
|
||||||
if (rootFlag == warningFlags[baseID].name) { // Match!
|
if (rootFlag == warningFlags[baseID].name) { // Match!
|
||||||
if (rootFlag == "numeric-string")
|
if (rootFlag == "numeric-string") {
|
||||||
warning(WARNING_OBSOLETE, "Warning flag \"numeric-string\" is deprecated\n");
|
warning(WARNING_OBSOLETE, "Warning flag \"numeric-string\" is deprecated\n");
|
||||||
|
}
|
||||||
|
|
||||||
// If making the warning an error but param is 0, set to the maximum
|
// If making the warning an error but param is 0, set to the maximum
|
||||||
// This accommodates `-Werror=<flag>`, but also `-Werror=<flag>=0`, which is
|
// This accommodates `-Werror=<flag>`, but also `-Werror=<flag>=0`, which is
|
||||||
@@ -229,7 +243,7 @@ void processWarningFlag(char const *flag) {
|
|||||||
if (param == 0) {
|
if (param == 0) {
|
||||||
param = paramWarning.defaultLevel;
|
param = paramWarning.defaultLevel;
|
||||||
} else if (param > maxParam) {
|
} else if (param > maxParam) {
|
||||||
if (param != 255) // Don't warn if already capped
|
if (param != 255) { // Don't warn if already capped
|
||||||
warnx(
|
warnx(
|
||||||
"Invalid parameter %" PRIu8
|
"Invalid parameter %" PRIu8
|
||||||
" for warning flag \"%s\"; capping at maximum %" PRIu8,
|
" for warning flag \"%s\"; capping at maximum %" PRIu8,
|
||||||
@@ -237,16 +251,18 @@ void processWarningFlag(char const *flag) {
|
|||||||
rootFlag.c_str(),
|
rootFlag.c_str(),
|
||||||
maxParam
|
maxParam
|
||||||
);
|
);
|
||||||
|
}
|
||||||
param = maxParam;
|
param = maxParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the first <param> to enabled/error, and disable the rest
|
// Set the first <param> to enabled/error, and disable the rest
|
||||||
for (uint8_t ofs = 0; ofs < maxParam; ofs++) {
|
for (uint8_t ofs = 0; ofs < maxParam; ofs++) {
|
||||||
WarningState &warning = warningStates.flagStates[baseID + ofs];
|
WarningState &warning = warningStates.flagStates[baseID + ofs];
|
||||||
if (ofs < param)
|
if (ofs < param) {
|
||||||
warning.update(state);
|
warning.update(state);
|
||||||
else
|
} else {
|
||||||
warning.state = WARNING_DISABLED;
|
warning.state = WARNING_DISABLED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -259,8 +275,9 @@ void processWarningFlag(char const *flag) {
|
|||||||
if (rootFlag == metaWarning.name) {
|
if (rootFlag == metaWarning.name) {
|
||||||
// Set each of the warning flags that meets this level
|
// Set each of the warning flags that meets this level
|
||||||
for (WarningID id : EnumSeq(NB_WARNINGS)) {
|
for (WarningID id : EnumSeq(NB_WARNINGS)) {
|
||||||
if (metaWarning.level >= warningFlags[id].level)
|
if (metaWarning.level >= warningFlags[id].level) {
|
||||||
warningStates.metaStates[id].update(state);
|
warningStates.metaStates[id].update(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -299,13 +316,14 @@ void error(char const *fmt, ...) {
|
|||||||
|
|
||||||
// This intentionally makes 0 act as "unlimited" (or at least "limited to sizeof(unsigned)")
|
// This intentionally makes 0 act as "unlimited" (or at least "limited to sizeof(unsigned)")
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
if (nbErrors == maxErrors)
|
if (nbErrors == maxErrors) {
|
||||||
errx(
|
errx(
|
||||||
"The maximum of %u error%s was reached (configure with \"-X/--max-errors\"); assembly "
|
"The maximum of %u error%s was reached (configure with \"-X/--max-errors\"); assembly "
|
||||||
"aborted!",
|
"aborted!",
|
||||||
maxErrors,
|
maxErrors,
|
||||||
maxErrors == 1 ? "" : "s"
|
maxErrors == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
|
|||||||
71
src/extern/getopt.cpp
vendored
71
src/extern/getopt.cpp
vendored
@@ -19,8 +19,9 @@ static int musl_optpos;
|
|||||||
static void musl_getopt_msg(char const *a, char const *b, char const *c, size_t l) {
|
static void musl_getopt_msg(char const *a, char const *b, char const *c, size_t l) {
|
||||||
FILE *f = stderr;
|
FILE *f = stderr;
|
||||||
|
|
||||||
if (fputs(a, f) >= 0 && fwrite(b, strlen(b), 1, f) && fwrite(c, 1, l, f) == l)
|
if (fputs(a, f) >= 0 && fwrite(b, strlen(b), 1, f) && fwrite(c, 1, l, f) == l) {
|
||||||
putc('\n', f);
|
putc('\n', f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getopt(int argc, char *argv[], char const *optstring) {
|
static int getopt(int argc, char *argv[], char const *optstring) {
|
||||||
@@ -35,8 +36,9 @@ static int getopt(int argc, char *argv[], char const *optstring) {
|
|||||||
musl_optind = 1;
|
musl_optind = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (musl_optind >= argc || !argv[musl_optind])
|
if (musl_optind >= argc || !argv[musl_optind]) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (argv[musl_optind][0] != '-') {
|
if (argv[musl_optind][0] != '-') {
|
||||||
if (optstring[0] == '-') {
|
if (optstring[0] == '-') {
|
||||||
@@ -46,14 +48,17 @@ static int getopt(int argc, char *argv[], char const *optstring) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!argv[musl_optind][1])
|
if (!argv[musl_optind][1]) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (argv[musl_optind][1] == '-' && !argv[musl_optind][2])
|
if (argv[musl_optind][1] == '-' && !argv[musl_optind][2]) {
|
||||||
return musl_optind++, -1;
|
return musl_optind++, -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!musl_optpos)
|
if (!musl_optpos) {
|
||||||
musl_optpos++;
|
musl_optpos++;
|
||||||
|
}
|
||||||
k = mbtowc(&c, argv[musl_optind] + musl_optpos, MB_LEN_MAX);
|
k = mbtowc(&c, argv[musl_optind] + musl_optpos, MB_LEN_MAX);
|
||||||
if (k < 0) {
|
if (k < 0) {
|
||||||
k = 1;
|
k = 1;
|
||||||
@@ -67,23 +72,26 @@ static int getopt(int argc, char *argv[], char const *optstring) {
|
|||||||
musl_optpos = 0;
|
musl_optpos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optstring[0] == '-' || optstring[0] == '+')
|
if (optstring[0] == '-' || optstring[0] == '+') {
|
||||||
optstring++;
|
optstring++;
|
||||||
|
}
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
d = 0;
|
d = 0;
|
||||||
do {
|
do {
|
||||||
l = mbtowc(&d, optstring + i, MB_LEN_MAX);
|
l = mbtowc(&d, optstring + i, MB_LEN_MAX);
|
||||||
if (l > 0)
|
if (l > 0) {
|
||||||
i += l;
|
i += l;
|
||||||
else
|
} else {
|
||||||
i++;
|
i++;
|
||||||
|
}
|
||||||
} while (l && d != c);
|
} while (l && d != c);
|
||||||
|
|
||||||
if (d != c || c == ':') {
|
if (d != c || c == ':') {
|
||||||
musl_optopt = c;
|
musl_optopt = c;
|
||||||
if (optstring[0] != ':' && musl_opterr)
|
if (optstring[0] != ':' && musl_opterr) {
|
||||||
musl_getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
|
musl_getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
|
||||||
|
}
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
if (optstring[i] == ':') {
|
if (optstring[i] == ':') {
|
||||||
@@ -94,10 +102,12 @@ static int getopt(int argc, char *argv[], char const *optstring) {
|
|||||||
}
|
}
|
||||||
if (musl_optind > argc) {
|
if (musl_optind > argc) {
|
||||||
musl_optopt = c;
|
musl_optopt = c;
|
||||||
if (optstring[0] == ':')
|
if (optstring[0] == ':') {
|
||||||
return ':';
|
return ':';
|
||||||
if (musl_opterr)
|
}
|
||||||
|
if (musl_opterr) {
|
||||||
musl_getopt_msg(argv[0], ": option requires an argument: ", optchar, k);
|
musl_getopt_msg(argv[0], ": option requires an argument: ", optchar, k);
|
||||||
|
}
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,8 +118,9 @@ static void permute(char **argv, int dest, int src) {
|
|||||||
char *tmp = argv[src];
|
char *tmp = argv[src];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = src; i > dest; i--)
|
for (i = src; i > dest; i--) {
|
||||||
argv[i] = argv[i - 1];
|
argv[i] = argv[i - 1];
|
||||||
|
}
|
||||||
argv[dest] = tmp;
|
argv[dest] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,17 +139,20 @@ static int musl_getopt_long(
|
|||||||
musl_optind = 1;
|
musl_optind = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (musl_optind >= argc || !argv[musl_optind])
|
if (musl_optind >= argc || !argv[musl_optind]) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
skipped = musl_optind;
|
skipped = musl_optind;
|
||||||
if (optstring[0] != '+' && optstring[0] != '-') {
|
if (optstring[0] != '+' && optstring[0] != '-') {
|
||||||
int i;
|
int i;
|
||||||
for (i = musl_optind;; i++) {
|
for (i = musl_optind;; i++) {
|
||||||
if (i >= argc || !argv[i])
|
if (i >= argc || !argv[i]) {
|
||||||
return -1;
|
return -1;
|
||||||
if (argv[i][0] == '-' && argv[i][1])
|
}
|
||||||
|
if (argv[i][0] == '-' && argv[i][1]) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
musl_optind = i;
|
musl_optind = i;
|
||||||
}
|
}
|
||||||
@@ -147,8 +161,9 @@ static int musl_getopt_long(
|
|||||||
if (resumed > skipped) {
|
if (resumed > skipped) {
|
||||||
int i, cnt = musl_optind - resumed;
|
int i, cnt = musl_optind - resumed;
|
||||||
|
|
||||||
for (i = 0; i < cnt; i++)
|
for (i = 0; i < cnt; i++) {
|
||||||
permute(argv, skipped, musl_optind - 1);
|
permute(argv, skipped, musl_optind - 1);
|
||||||
|
}
|
||||||
musl_optind = skipped + cnt;
|
musl_optind = skipped + cnt;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -169,14 +184,16 @@ static int musl_getopt_long_core(
|
|||||||
char const *name = longopts[i].name;
|
char const *name = longopts[i].name;
|
||||||
|
|
||||||
opt = start;
|
opt = start;
|
||||||
if (*opt == '-')
|
if (*opt == '-') {
|
||||||
opt++;
|
opt++;
|
||||||
|
}
|
||||||
while (*opt && *opt != '=' && *opt == *name) {
|
while (*opt && *opt != '=' && *opt == *name) {
|
||||||
name++;
|
name++;
|
||||||
opt++;
|
opt++;
|
||||||
}
|
}
|
||||||
if (*opt && *opt != '=')
|
if (*opt && *opt != '=') {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
arg = opt;
|
arg = opt;
|
||||||
match = i;
|
match = i;
|
||||||
if (!*name) {
|
if (!*name) {
|
||||||
@@ -191,8 +208,9 @@ static int musl_getopt_long_core(
|
|||||||
for (i = 0; optstring[i]; i++) {
|
for (i = 0; optstring[i]; i++) {
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
|
||||||
while (j < l && start[j] == optstring[i + j])
|
while (j < l && start[j] == optstring[i + j]) {
|
||||||
j++;
|
j++;
|
||||||
|
}
|
||||||
if (j == l) {
|
if (j == l) {
|
||||||
cnt++;
|
cnt++;
|
||||||
break;
|
break;
|
||||||
@@ -206,8 +224,9 @@ static int musl_getopt_long_core(
|
|||||||
if (*opt == '=') {
|
if (*opt == '=') {
|
||||||
if (!longopts[i].has_arg) {
|
if (!longopts[i].has_arg) {
|
||||||
musl_optopt = longopts[i].val;
|
musl_optopt = longopts[i].val;
|
||||||
if (colon || !musl_opterr)
|
if (colon || !musl_opterr) {
|
||||||
return '?';
|
return '?';
|
||||||
|
}
|
||||||
musl_getopt_msg(
|
musl_getopt_msg(
|
||||||
argv[0],
|
argv[0],
|
||||||
": option does not take an argument: ",
|
": option does not take an argument: ",
|
||||||
@@ -221,10 +240,12 @@ static int musl_getopt_long_core(
|
|||||||
musl_optarg = argv[musl_optind];
|
musl_optarg = argv[musl_optind];
|
||||||
if (!musl_optarg) {
|
if (!musl_optarg) {
|
||||||
musl_optopt = longopts[i].val;
|
musl_optopt = longopts[i].val;
|
||||||
if (colon)
|
if (colon) {
|
||||||
return ':';
|
return ':';
|
||||||
if (!musl_opterr)
|
}
|
||||||
|
if (!musl_opterr) {
|
||||||
return '?';
|
return '?';
|
||||||
|
}
|
||||||
musl_getopt_msg(
|
musl_getopt_msg(
|
||||||
argv[0],
|
argv[0],
|
||||||
": option requires an argument: ",
|
": option requires an argument: ",
|
||||||
@@ -235,8 +256,9 @@ static int musl_getopt_long_core(
|
|||||||
}
|
}
|
||||||
musl_optind++;
|
musl_optind++;
|
||||||
}
|
}
|
||||||
if (idx)
|
if (idx) {
|
||||||
*idx = i;
|
*idx = i;
|
||||||
|
}
|
||||||
if (longopts[i].flag) {
|
if (longopts[i].flag) {
|
||||||
*longopts[i].flag = longopts[i].val;
|
*longopts[i].flag = longopts[i].val;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -245,13 +267,14 @@ static int musl_getopt_long_core(
|
|||||||
}
|
}
|
||||||
if (argv[musl_optind][1] == '-') {
|
if (argv[musl_optind][1] == '-') {
|
||||||
musl_optopt = 0;
|
musl_optopt = 0;
|
||||||
if (!colon && musl_opterr)
|
if (!colon && musl_opterr) {
|
||||||
musl_getopt_msg(
|
musl_getopt_msg(
|
||||||
argv[0],
|
argv[0],
|
||||||
cnt ? ": option is ambiguous: " : ": unrecognized option: ",
|
cnt ? ": option is ambiguous: " : ": unrecognized option: ",
|
||||||
argv[musl_optind] + 2,
|
argv[musl_optind] + 2,
|
||||||
strlen(argv[musl_optind] + 2)
|
strlen(argv[musl_optind] + 2)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
musl_optind++;
|
musl_optind++;
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
|
|||||||
206
src/fix/main.cpp
206
src/fix/main.cpp
@@ -83,8 +83,9 @@ static void report(char const *fmt, ...) {
|
|||||||
vfprintf(stderr, fmt, ap);
|
vfprintf(stderr, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
if (nbErrors != UINT8_MAX)
|
if (nbErrors != UINT8_MAX) {
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MbcType {
|
enum MbcType {
|
||||||
@@ -189,16 +190,19 @@ static bool readMBCSlice(char const *&name, char const *expected) {
|
|||||||
while (*expected) {
|
while (*expected) {
|
||||||
char c = *name++;
|
char c = *name++;
|
||||||
|
|
||||||
if (c == '\0') // Name too short
|
if (c == '\0') { // Name too short
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (c >= 'a' && c <= 'z') // Perform the comparison case-insensitive
|
if (c >= 'a' && c <= 'z') { // Perform the comparison case-insensitive
|
||||||
c = c - 'a' + 'A';
|
c = c - 'a' + 'A';
|
||||||
else if (c == '_') // Treat underscores as spaces
|
} else if (c == '_') { // Treat underscores as spaces
|
||||||
c = ' ';
|
c = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
if (c != *expected++)
|
if (c != *expected++) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -221,10 +225,12 @@ static MbcType parseMBC(char const *name) {
|
|||||||
char *endptr;
|
char *endptr;
|
||||||
unsigned long mbc = strtoul(name, &endptr, base);
|
unsigned long mbc = strtoul(name, &endptr, base);
|
||||||
|
|
||||||
if (*endptr)
|
if (*endptr) {
|
||||||
return MBC_BAD;
|
return MBC_BAD;
|
||||||
if (mbc > 0xFF)
|
}
|
||||||
|
if (mbc > 0xFF) {
|
||||||
return MBC_BAD_RANGE;
|
return MBC_BAD_RANGE;
|
||||||
|
}
|
||||||
return static_cast<MbcType>(mbc);
|
return static_cast<MbcType>(mbc);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -233,8 +239,9 @@ static MbcType parseMBC(char const *name) {
|
|||||||
char const *ptr = name;
|
char const *ptr = name;
|
||||||
|
|
||||||
// Trim off leading whitespace
|
// Trim off leading whitespace
|
||||||
while (*ptr == ' ' || *ptr == '\t')
|
while (*ptr == ' ' || *ptr == '\t') {
|
||||||
ptr++;
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
#define tryReadSlice(expected) \
|
#define tryReadSlice(expected) \
|
||||||
do { \
|
do { \
|
||||||
@@ -247,8 +254,9 @@ static MbcType parseMBC(char const *name) {
|
|||||||
case 'r':
|
case 'r':
|
||||||
tryReadSlice("OM");
|
tryReadSlice("OM");
|
||||||
// Handle optional " ONLY"
|
// Handle optional " ONLY"
|
||||||
while (*ptr == ' ' || *ptr == '\t' || *ptr == '_')
|
while (*ptr == ' ' || *ptr == '\t' || *ptr == '_') {
|
||||||
ptr++;
|
ptr++;
|
||||||
|
}
|
||||||
if (*ptr == 'O' || *ptr == 'o') {
|
if (*ptr == 'O' || *ptr == 'o') {
|
||||||
ptr++;
|
ptr++;
|
||||||
tryReadSlice("NLY");
|
tryReadSlice("NLY");
|
||||||
@@ -323,8 +331,9 @@ static MbcType parseMBC(char const *name) {
|
|||||||
case 'P': {
|
case 'P': {
|
||||||
tryReadSlice("P1");
|
tryReadSlice("P1");
|
||||||
// Parse version
|
// Parse version
|
||||||
while (*ptr == ' ' || *ptr == '_')
|
while (*ptr == ' ' || *ptr == '_') {
|
||||||
ptr++;
|
ptr++;
|
||||||
|
}
|
||||||
// Major
|
// Major
|
||||||
char *endptr;
|
char *endptr;
|
||||||
unsigned long val = strtoul(ptr, &endptr, 10);
|
unsigned long val = strtoul(ptr, &endptr, 10);
|
||||||
@@ -392,18 +401,22 @@ static MbcType parseMBC(char const *name) {
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Trim off trailing whitespace
|
// Trim off trailing whitespace
|
||||||
while (*ptr == ' ' || *ptr == '\t' || *ptr == '_')
|
while (*ptr == ' ' || *ptr == '\t' || *ptr == '_') {
|
||||||
ptr++;
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
// If done, start processing "features"
|
// If done, start processing "features"
|
||||||
if (!*ptr)
|
if (!*ptr) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
// We expect a '+' at this point
|
// We expect a '+' at this point
|
||||||
if (*ptr++ != '+')
|
if (*ptr++ != '+') {
|
||||||
return MBC_BAD;
|
return MBC_BAD;
|
||||||
|
}
|
||||||
// Trim off leading whitespace
|
// Trim off leading whitespace
|
||||||
while (*ptr == ' ' || *ptr == '\t' || *ptr == '_')
|
while (*ptr == ' ' || *ptr == '\t' || *ptr == '_') {
|
||||||
ptr++;
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
switch (*ptr++) {
|
switch (*ptr++) {
|
||||||
case 'B': // BATTERY
|
case 'B': // BATTERY
|
||||||
@@ -428,8 +441,9 @@ static MbcType parseMBC(char const *name) {
|
|||||||
break;
|
break;
|
||||||
case 'A':
|
case 'A':
|
||||||
case 'a':
|
case 'a':
|
||||||
if (*ptr != 'M' && *ptr != 'm')
|
if (*ptr != 'M' && *ptr != 'm') {
|
||||||
return MBC_BAD;
|
return MBC_BAD;
|
||||||
|
}
|
||||||
ptr++;
|
ptr++;
|
||||||
features |= RAM;
|
features |= RAM;
|
||||||
break;
|
break;
|
||||||
@@ -458,8 +472,9 @@ static MbcType parseMBC(char const *name) {
|
|||||||
|
|
||||||
switch (mbc) {
|
switch (mbc) {
|
||||||
case ROM:
|
case ROM:
|
||||||
if (!features)
|
if (!features) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
mbc = ROM_RAM - 1;
|
mbc = ROM_RAM - 1;
|
||||||
static_assert(ROM_RAM + 1 == ROM_RAM_BATTERY, "Enum sanity check failed!");
|
static_assert(ROM_RAM + 1 == ROM_RAM_BATTERY, "Enum sanity check failed!");
|
||||||
static_assert(MBC1 + 1 == MBC1_RAM, "Enum sanity check failed!");
|
static_assert(MBC1 + 1 == MBC1_RAM, "Enum sanity check failed!");
|
||||||
@@ -469,26 +484,29 @@ static MbcType parseMBC(char const *name) {
|
|||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case MBC1:
|
case MBC1:
|
||||||
case MMM01:
|
case MMM01:
|
||||||
if (features == RAM)
|
if (features == RAM) {
|
||||||
mbc++;
|
mbc++;
|
||||||
else if (features == (RAM | BATTERY))
|
} else if (features == (RAM | BATTERY)) {
|
||||||
mbc += 2;
|
mbc += 2;
|
||||||
else if (features)
|
} else if (features) {
|
||||||
return MBC_WRONG_FEATURES;
|
return MBC_WRONG_FEATURES;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MBC2:
|
case MBC2:
|
||||||
if (features == BATTERY)
|
if (features == BATTERY) {
|
||||||
mbc = MBC2_BATTERY;
|
mbc = MBC2_BATTERY;
|
||||||
else if (features)
|
} else if (features) {
|
||||||
return MBC_WRONG_FEATURES;
|
return MBC_WRONG_FEATURES;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MBC3:
|
case MBC3:
|
||||||
// Handle timer, which also requires battery
|
// Handle timer, which also requires battery
|
||||||
if (features & TIMER) {
|
if (features & TIMER) {
|
||||||
if (!(features & BATTERY))
|
if (!(features & BATTERY)) {
|
||||||
fprintf(stderr, "warning: MBC3+TIMER implies BATTERY\n");
|
fprintf(stderr, "warning: MBC3+TIMER implies BATTERY\n");
|
||||||
|
}
|
||||||
features &= ~(TIMER | BATTERY); // Reset those bits
|
features &= ~(TIMER | BATTERY); // Reset those bits
|
||||||
mbc = MBC3_TIMER_BATTERY;
|
mbc = MBC3_TIMER_BATTERY;
|
||||||
// RAM is handled below
|
// RAM is handled below
|
||||||
@@ -498,12 +516,13 @@ static MbcType parseMBC(char const *name) {
|
|||||||
static_assert(
|
static_assert(
|
||||||
MBC3_TIMER_BATTERY + 1 == MBC3_TIMER_RAM_BATTERY, "Enum sanity check failed!"
|
MBC3_TIMER_BATTERY + 1 == MBC3_TIMER_RAM_BATTERY, "Enum sanity check failed!"
|
||||||
);
|
);
|
||||||
if (features == RAM)
|
if (features == RAM) {
|
||||||
mbc++;
|
mbc++;
|
||||||
else if (features == (RAM | BATTERY))
|
} else if (features == (RAM | BATTERY)) {
|
||||||
mbc += 2;
|
mbc += 2;
|
||||||
else if (features)
|
} else if (features) {
|
||||||
return MBC_WRONG_FEATURES;
|
return MBC_WRONG_FEATURES;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MBC5:
|
case MBC5:
|
||||||
@@ -515,12 +534,13 @@ static MbcType parseMBC(char const *name) {
|
|||||||
static_assert(MBC5 + 2 == MBC5_RAM_BATTERY, "Enum sanity check failed!");
|
static_assert(MBC5 + 2 == MBC5_RAM_BATTERY, "Enum sanity check failed!");
|
||||||
static_assert(MBC5_RUMBLE + 1 == MBC5_RUMBLE_RAM, "Enum sanity check failed!");
|
static_assert(MBC5_RUMBLE + 1 == MBC5_RUMBLE_RAM, "Enum sanity check failed!");
|
||||||
static_assert(MBC5_RUMBLE + 2 == MBC5_RUMBLE_RAM_BATTERY, "Enum sanity check failed!");
|
static_assert(MBC5_RUMBLE + 2 == MBC5_RUMBLE_RAM_BATTERY, "Enum sanity check failed!");
|
||||||
if (features == RAM)
|
if (features == RAM) {
|
||||||
mbc++;
|
mbc++;
|
||||||
else if (features == (RAM | BATTERY))
|
} else if (features == (RAM | BATTERY)) {
|
||||||
mbc += 2;
|
mbc += 2;
|
||||||
else if (features)
|
} else if (features) {
|
||||||
return MBC_WRONG_FEATURES;
|
return MBC_WRONG_FEATURES;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MBC6:
|
case MBC6:
|
||||||
@@ -528,45 +548,56 @@ static MbcType parseMBC(char const *name) {
|
|||||||
case BANDAI_TAMA5:
|
case BANDAI_TAMA5:
|
||||||
case HUC3:
|
case HUC3:
|
||||||
// No extra features accepted
|
// No extra features accepted
|
||||||
if (features)
|
if (features) {
|
||||||
return MBC_WRONG_FEATURES;
|
return MBC_WRONG_FEATURES;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MBC7_SENSOR_RUMBLE_RAM_BATTERY:
|
case MBC7_SENSOR_RUMBLE_RAM_BATTERY:
|
||||||
if (features != (SENSOR | RUMBLE | RAM | BATTERY))
|
if (features != (SENSOR | RUMBLE | RAM | BATTERY)) {
|
||||||
return MBC_WRONG_FEATURES;
|
return MBC_WRONG_FEATURES;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HUC1_RAM_BATTERY:
|
case HUC1_RAM_BATTERY:
|
||||||
if (features != (RAM | BATTERY)) // HuC1 expects RAM+BATTERY
|
if (features != (RAM | BATTERY)) { // HuC1 expects RAM+BATTERY
|
||||||
return MBC_WRONG_FEATURES;
|
return MBC_WRONG_FEATURES;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TPP1:
|
case TPP1:
|
||||||
if (features & RAM)
|
if (features & RAM) {
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr, "warning: TPP1 requests RAM implicitly if given a non-zero RAM size"
|
stderr, "warning: TPP1 requests RAM implicitly if given a non-zero RAM size"
|
||||||
);
|
);
|
||||||
if (features & BATTERY)
|
}
|
||||||
|
if (features & BATTERY) {
|
||||||
mbc |= 0x08;
|
mbc |= 0x08;
|
||||||
if (features & TIMER)
|
}
|
||||||
|
if (features & TIMER) {
|
||||||
mbc |= 0x04;
|
mbc |= 0x04;
|
||||||
if (features & MULTIRUMBLE)
|
}
|
||||||
|
if (features & MULTIRUMBLE) {
|
||||||
mbc |= 0x03; // Also set the rumble flag
|
mbc |= 0x03; // Also set the rumble flag
|
||||||
if (features & RUMBLE)
|
}
|
||||||
|
if (features & RUMBLE) {
|
||||||
mbc |= 0x01;
|
mbc |= 0x01;
|
||||||
if (features & SENSOR)
|
}
|
||||||
|
if (features & SENSOR) {
|
||||||
return MBC_WRONG_FEATURES;
|
return MBC_WRONG_FEATURES;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim off trailing whitespace
|
// Trim off trailing whitespace
|
||||||
while (*ptr == ' ' || *ptr == '\t')
|
while (*ptr == ' ' || *ptr == '\t') {
|
||||||
ptr++;
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
// If there is still something past the whitespace, error out
|
// If there is still something past the whitespace, error out
|
||||||
if (*ptr)
|
if (*ptr) {
|
||||||
return MBC_BAD;
|
return MBC_BAD;
|
||||||
|
}
|
||||||
|
|
||||||
return static_cast<MbcType>(mbc);
|
return static_cast<MbcType>(mbc);
|
||||||
}
|
}
|
||||||
@@ -780,11 +811,13 @@ static ssize_t readBytes(int fd, uint8_t *buf, size_t len) {
|
|||||||
while (len) {
|
while (len) {
|
||||||
ssize_t ret = read(fd, buf, len);
|
ssize_t ret = read(fd, buf, len);
|
||||||
|
|
||||||
if (ret == -1 && errno != EINTR) // Return errors, unless we only were interrupted
|
if (ret == -1 && errno != EINTR) { // Return errors, unless we only were interrupted
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
// EOF reached
|
// EOF reached
|
||||||
if (ret == 0)
|
if (ret == 0) {
|
||||||
return total;
|
return total;
|
||||||
|
}
|
||||||
// If anything was read, accumulate it, and continue
|
// If anything was read, accumulate it, and continue
|
||||||
if (ret != -1) {
|
if (ret != -1) {
|
||||||
total += ret;
|
total += ret;
|
||||||
@@ -805,8 +838,9 @@ static ssize_t writeBytes(int fd, uint8_t *buf, size_t len) {
|
|||||||
while (len) {
|
while (len) {
|
||||||
ssize_t ret = write(fd, buf, len);
|
ssize_t ret = write(fd, buf, len);
|
||||||
|
|
||||||
if (ret == -1 && errno != EINTR) // Return errors, unless we only were interrupted
|
if (ret == -1 && errno != EINTR) { // Return errors, unless we only were interrupted
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
// If anything was written, accumulate it, and continue
|
// If anything was written, accumulate it, and continue
|
||||||
if (ret != -1) {
|
if (ret != -1) {
|
||||||
total += ret;
|
total += ret;
|
||||||
@@ -825,8 +859,9 @@ static ssize_t writeBytes(int fd, uint8_t *buf, size_t len) {
|
|||||||
static void overwriteByte(uint8_t *rom0, uint16_t addr, uint8_t fixedByte, char const *areaName) {
|
static void overwriteByte(uint8_t *rom0, uint16_t addr, uint8_t fixedByte, char const *areaName) {
|
||||||
uint8_t origByte = rom0[addr];
|
uint8_t origByte = rom0[addr];
|
||||||
|
|
||||||
if (!overwriteRom && origByte != 0 && origByte != fixedByte)
|
if (!overwriteRom && origByte != 0 && origByte != fixedByte) {
|
||||||
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n", areaName);
|
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n", areaName);
|
||||||
|
}
|
||||||
|
|
||||||
rom0[addr] = fixedByte;
|
rom0[addr] = fixedByte;
|
||||||
}
|
}
|
||||||
@@ -859,10 +894,11 @@ static void overwriteBytes(
|
|||||||
// @param fileSize The file's size if known, 0 if not.
|
// @param fileSize The file's size if known, 0 if not.
|
||||||
static void processFile(int input, int output, char const *name, off_t fileSize) {
|
static void processFile(int input, int output, char const *name, off_t fileSize) {
|
||||||
// Both of these should be true for seekable files, and neither otherwise
|
// Both of these should be true for seekable files, and neither otherwise
|
||||||
if (input == output)
|
if (input == output) {
|
||||||
assume(fileSize != 0);
|
assume(fileSize != 0);
|
||||||
else
|
} else {
|
||||||
assume(fileSize == 0);
|
assume(fileSize == 0);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t rom0[BANK_SIZE];
|
uint8_t rom0[BANK_SIZE];
|
||||||
ssize_t rom0Len = readBytes(input, rom0, sizeof(rom0));
|
ssize_t rom0Len = readBytes(input, rom0, sizeof(rom0));
|
||||||
@@ -884,21 +920,25 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
}
|
}
|
||||||
// Accept partial reads if the file contains at least the header
|
// Accept partial reads if the file contains at least the header
|
||||||
|
|
||||||
if (fixSpec & (FIX_LOGO | TRASH_LOGO))
|
if (fixSpec & (FIX_LOGO | TRASH_LOGO)) {
|
||||||
overwriteBytes(rom0, 0x0104, logo, sizeof(logo), logoFilename ? "logo" : "Nintendo logo");
|
overwriteBytes(rom0, 0x0104, logo, sizeof(logo), logoFilename ? "logo" : "Nintendo logo");
|
||||||
|
}
|
||||||
|
|
||||||
if (title)
|
if (title) {
|
||||||
overwriteBytes(rom0, 0x134, reinterpret_cast<uint8_t const *>(title), titleLen, "title");
|
overwriteBytes(rom0, 0x134, reinterpret_cast<uint8_t const *>(title), titleLen, "title");
|
||||||
|
}
|
||||||
|
|
||||||
if (gameID)
|
if (gameID) {
|
||||||
overwriteBytes(
|
overwriteBytes(
|
||||||
rom0, 0x13F, reinterpret_cast<uint8_t const *>(gameID), gameIDLen, "manufacturer code"
|
rom0, 0x13F, reinterpret_cast<uint8_t const *>(gameID), gameIDLen, "manufacturer code"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (model != DMG)
|
if (model != DMG) {
|
||||||
overwriteByte(rom0, 0x143, model == BOTH ? 0x80 : 0xC0, "CGB flag");
|
overwriteByte(rom0, 0x143, model == BOTH ? 0x80 : 0xC0, "CGB flag");
|
||||||
|
}
|
||||||
|
|
||||||
if (newLicensee)
|
if (newLicensee) {
|
||||||
overwriteBytes(
|
overwriteBytes(
|
||||||
rom0,
|
rom0,
|
||||||
0x144,
|
0x144,
|
||||||
@@ -906,9 +946,11 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
newLicenseeLen,
|
newLicenseeLen,
|
||||||
"new licensee code"
|
"new licensee code"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (sgb)
|
if (sgb) {
|
||||||
overwriteByte(rom0, 0x146, 0x03, "SGB flag");
|
overwriteByte(rom0, 0x146, 0x03, "SGB flag");
|
||||||
|
}
|
||||||
|
|
||||||
// If a valid MBC was specified...
|
// If a valid MBC was specified...
|
||||||
if (cartridgeType < MBC_NONE) {
|
if (cartridgeType < MBC_NONE) {
|
||||||
@@ -931,31 +973,36 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
|
|
||||||
overwriteBytes(rom0, 0x150, tpp1Rev, sizeof(tpp1Rev), "TPP1 revision number");
|
overwriteBytes(rom0, 0x150, tpp1Rev, sizeof(tpp1Rev), "TPP1 revision number");
|
||||||
|
|
||||||
if (ramSize != UNSPECIFIED)
|
if (ramSize != UNSPECIFIED) {
|
||||||
overwriteByte(rom0, 0x152, ramSize, "RAM size");
|
overwriteByte(rom0, 0x152, ramSize, "RAM size");
|
||||||
|
}
|
||||||
|
|
||||||
overwriteByte(rom0, 0x153, cartridgeType & 0xFF, "TPP1 feature flags");
|
overwriteByte(rom0, 0x153, cartridgeType & 0xFF, "TPP1 feature flags");
|
||||||
} else {
|
} else {
|
||||||
// Regular mappers
|
// Regular mappers
|
||||||
|
|
||||||
if (ramSize != UNSPECIFIED)
|
if (ramSize != UNSPECIFIED) {
|
||||||
overwriteByte(rom0, 0x149, ramSize, "RAM size");
|
overwriteByte(rom0, 0x149, ramSize, "RAM size");
|
||||||
|
}
|
||||||
|
|
||||||
if (!japanese)
|
if (!japanese) {
|
||||||
overwriteByte(rom0, 0x14A, 0x01, "destination code");
|
overwriteByte(rom0, 0x14A, 0x01, "destination code");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldLicensee != UNSPECIFIED)
|
if (oldLicensee != UNSPECIFIED) {
|
||||||
overwriteByte(rom0, 0x14B, oldLicensee, "old licensee code");
|
overwriteByte(rom0, 0x14B, oldLicensee, "old licensee code");
|
||||||
else if (sgb && rom0[0x14B] != 0x33)
|
} else if (sgb && rom0[0x14B] != 0x33) {
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"warning: SGB compatibility enabled, but old licensee was 0x%02x, not 0x33\n",
|
"warning: SGB compatibility enabled, but old licensee was 0x%02x, not 0x33\n",
|
||||||
rom0[0x14B]
|
rom0[0x14B]
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (romVersion != UNSPECIFIED)
|
if (romVersion != UNSPECIFIED) {
|
||||||
overwriteByte(rom0, 0x14C, romVersion, "mask ROM version number");
|
overwriteByte(rom0, 0x14C, romVersion, "mask ROM version number");
|
||||||
|
}
|
||||||
|
|
||||||
// Remain to be handled the ROM size, and header checksum.
|
// Remain to be handled the ROM size, and header checksum.
|
||||||
// The latter depends on the former, and so will be handled after it.
|
// The latter depends on the former, and so will be handled after it.
|
||||||
@@ -1003,13 +1050,15 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
nbBanks++;
|
nbBanks++;
|
||||||
|
|
||||||
// Update global checksum, too
|
// Update global checksum, too
|
||||||
for (uint16_t i = 0; i < bankLen; i++)
|
for (uint16_t i = 0; i < bankLen; i++) {
|
||||||
globalSum += romx[totalRomxLen + i];
|
globalSum += romx[totalRomxLen + i];
|
||||||
|
}
|
||||||
totalRomxLen += bankLen;
|
totalRomxLen += bankLen;
|
||||||
}
|
}
|
||||||
// Stop when an incomplete bank has been read
|
// Stop when an incomplete bank has been read
|
||||||
if (bankLen != BANK_SIZE)
|
if (bankLen != BANK_SIZE) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,8 +1085,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
// Alter number of banks to reflect required value
|
// Alter number of banks to reflect required value
|
||||||
// x&(x-1) is zero iff x is a power of 2, or 0; we know for sure it's non-zero,
|
// x&(x-1) is zero iff x is a power of 2, or 0; we know for sure it's non-zero,
|
||||||
// so this is true (non-zero) when we don't have a power of 2
|
// so this is true (non-zero) when we don't have a power of 2
|
||||||
if (nbBanks & (nbBanks - 1))
|
if (nbBanks & (nbBanks - 1)) {
|
||||||
nbBanks = 1 << (CHAR_BIT * sizeof(nbBanks) - clz(nbBanks));
|
nbBanks = 1 << (CHAR_BIT * sizeof(nbBanks) - clz(nbBanks));
|
||||||
|
}
|
||||||
// Write final ROM size
|
// Write final ROM size
|
||||||
rom0[0x148] = ctz(nbBanks / 2);
|
rom0[0x148] = ctz(nbBanks / 2);
|
||||||
// Alter global checksum based on how many bytes will be added (not counting ROM0)
|
// Alter global checksum based on how many bytes will be added (not counting ROM0)
|
||||||
@@ -1048,8 +1098,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
if (fixSpec & (FIX_HEADER_SUM | TRASH_HEADER_SUM)) {
|
if (fixSpec & (FIX_HEADER_SUM | TRASH_HEADER_SUM)) {
|
||||||
uint8_t sum = 0;
|
uint8_t sum = 0;
|
||||||
|
|
||||||
for (uint16_t i = 0x134; i < 0x14D; i++)
|
for (uint16_t i = 0x134; i < 0x14D; i++) {
|
||||||
sum -= rom0[i] + 1;
|
sum -= rom0[i] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
overwriteByte(rom0, 0x14D, fixSpec & TRASH_HEADER_SUM ? ~sum : sum, "header checksum");
|
overwriteByte(rom0, 0x14D, fixSpec & TRASH_HEADER_SUM ? ~sum : sum, "header checksum");
|
||||||
}
|
}
|
||||||
@@ -1057,24 +1108,29 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
if (fixSpec & (FIX_GLOBAL_SUM | TRASH_GLOBAL_SUM)) {
|
if (fixSpec & (FIX_GLOBAL_SUM | TRASH_GLOBAL_SUM)) {
|
||||||
// Computation of the global checksum does not include the checksum bytes
|
// Computation of the global checksum does not include the checksum bytes
|
||||||
assume(rom0Len >= 0x14E);
|
assume(rom0Len >= 0x14E);
|
||||||
for (uint16_t i = 0; i < 0x14E; i++)
|
for (uint16_t i = 0; i < 0x14E; i++) {
|
||||||
globalSum += rom0[i];
|
globalSum += rom0[i];
|
||||||
for (uint16_t i = 0x150; i < rom0Len; i++)
|
}
|
||||||
|
for (uint16_t i = 0x150; i < rom0Len; i++) {
|
||||||
globalSum += rom0[i];
|
globalSum += rom0[i];
|
||||||
|
}
|
||||||
// Pipes have already read ROMX and updated globalSum, but not regular files
|
// Pipes have already read ROMX and updated globalSum, but not regular files
|
||||||
if (input == output) {
|
if (input == output) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ssize_t bankLen = readBytes(input, bank, sizeof(bank));
|
ssize_t bankLen = readBytes(input, bank, sizeof(bank));
|
||||||
|
|
||||||
for (uint16_t i = 0; i < bankLen; i++)
|
for (uint16_t i = 0; i < bankLen; i++) {
|
||||||
globalSum += bank[i];
|
globalSum += bank[i];
|
||||||
if (bankLen != sizeof(bank))
|
}
|
||||||
|
if (bankLen != sizeof(bank)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fixSpec & TRASH_GLOBAL_SUM)
|
if (fixSpec & TRASH_GLOBAL_SUM) {
|
||||||
globalSum = ~globalSum;
|
globalSum = ~globalSum;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t bytes[2] = {
|
uint8_t bytes[2] = {
|
||||||
static_cast<uint8_t>(globalSum >> 8), static_cast<uint8_t>(globalSum & 0xFF)
|
static_cast<uint8_t>(globalSum >> 8), static_cast<uint8_t>(globalSum & 0xFF)
|
||||||
@@ -1094,8 +1150,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
|||||||
}
|
}
|
||||||
// If modifying the file in-place, we only need to edit the header
|
// If modifying the file in-place, we only need to edit the header
|
||||||
// However, padding may have modified ROM0 (added padding), so don't in that case
|
// However, padding may have modified ROM0 (added padding), so don't in that case
|
||||||
if (padValue == UNSPECIFIED)
|
if (padValue == UNSPECIFIED) {
|
||||||
rom0Len = headerSize;
|
rom0Len = headerSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
writeLen = writeBytes(output, rom0, rom0Len);
|
writeLen = writeBytes(output, rom0, rom0Len);
|
||||||
|
|
||||||
@@ -1198,7 +1255,7 @@ static bool processFilename(char const *name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbErrors)
|
if (nbErrors) {
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"Fixing \"%s\" failed with %u error%s\n",
|
"Fixing \"%s\" failed with %u error%s\n",
|
||||||
@@ -1206,6 +1263,7 @@ static bool processFilename(char const *name) {
|
|||||||
nbErrors,
|
nbErrors,
|
||||||
nbErrors == 1 ? "" : "s"
|
nbErrors == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
return nbErrors;
|
return nbErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1386,21 +1444,23 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cartridgeType & 0xFF00) == TPP1 && !japanese)
|
if ((cartridgeType & 0xFF00) == TPP1 && !japanese) {
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"warning: TPP1 overwrites region flag for its identification code, ignoring `-j`\n"
|
"warning: TPP1 overwrites region flag for its identification code, ignoring `-j`\n"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Check that RAM size is correct for "standard" mappers
|
// Check that RAM size is correct for "standard" mappers
|
||||||
if (ramSize != UNSPECIFIED && (cartridgeType & 0xFF00) == 0) {
|
if (ramSize != UNSPECIFIED && (cartridgeType & 0xFF00) == 0) {
|
||||||
if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
|
if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
|
||||||
if (ramSize != 1)
|
if (ramSize != 1) {
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"warning: MBC \"%s\" should have 2 KiB of RAM (-r 1)\n",
|
"warning: MBC \"%s\" should have 2 KiB of RAM (-r 1)\n",
|
||||||
mbcName(cartridgeType)
|
mbcName(cartridgeType)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else if (hasRAM(cartridgeType)) {
|
} else if (hasRAM(cartridgeType)) {
|
||||||
if (!ramSize) {
|
if (!ramSize) {
|
||||||
fprintf(
|
fprintf(
|
||||||
@@ -1425,12 +1485,13 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sgb && oldLicensee != UNSPECIFIED && oldLicensee != 0x33)
|
if (sgb && oldLicensee != UNSPECIFIED && oldLicensee != 0x33) {
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"warning: SGB compatibility enabled, but old licensee is 0x%02x, not 0x33\n",
|
"warning: SGB compatibility enabled, but old licensee is 0x%02x, not 0x33\n",
|
||||||
oldLicensee
|
oldLicensee
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
argv += musl_optind;
|
argv += musl_optind;
|
||||||
bool failed = nbErrors;
|
bool failed = nbErrors;
|
||||||
@@ -1481,8 +1542,9 @@ int main(int argc, char *argv[]) {
|
|||||||
memcpy(logo, nintendoLogo, sizeof(nintendoLogo));
|
memcpy(logo, nintendoLogo, sizeof(nintendoLogo));
|
||||||
}
|
}
|
||||||
if (fixSpec & TRASH_LOGO) {
|
if (fixSpec & TRASH_LOGO) {
|
||||||
for (uint16_t i = 0; i < sizeof(logo); i++)
|
for (uint16_t i = 0; i < sizeof(logo); i++) {
|
||||||
logo[i] = 0xFF ^ logo[i];
|
logo[i] = 0xFF ^ logo[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*argv) {
|
if (!*argv) {
|
||||||
|
|||||||
@@ -72,15 +72,17 @@ void error(char const *fmt, ...) {
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
|
|
||||||
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max())
|
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max()) {
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void errorMessage(char const *msg) {
|
void errorMessage(char const *msg) {
|
||||||
fprintf(stderr, "error: %s\n", msg);
|
fprintf(stderr, "error: %s\n", msg);
|
||||||
|
|
||||||
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max())
|
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max()) {
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
@@ -93,8 +95,9 @@ void fatal(char const *fmt, ...) {
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
|
|
||||||
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max())
|
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max()) {
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
|
}
|
||||||
|
|
||||||
giveUp();
|
giveUp();
|
||||||
}
|
}
|
||||||
@@ -354,8 +357,9 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
localOptions.autoAttrmap = false;
|
localOptions.autoAttrmap = false;
|
||||||
if (!options.attrmap.empty())
|
if (!options.attrmap.empty()) {
|
||||||
warning("Overriding attrmap file %s", options.attrmap.c_str());
|
warning("Overriding attrmap file %s", options.attrmap.c_str());
|
||||||
|
}
|
||||||
options.attrmap = musl_optarg;
|
options.attrmap = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
@@ -425,8 +429,9 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
printUsage();
|
printUsage();
|
||||||
exit(0);
|
exit(0);
|
||||||
case 'i':
|
case 'i':
|
||||||
if (!options.inputTileset.empty())
|
if (!options.inputTileset.empty()) {
|
||||||
warning("Overriding input tileset file %s", options.inputTileset.c_str());
|
warning("Overriding input tileset file %s", options.inputTileset.c_str());
|
||||||
|
}
|
||||||
options.inputTileset = musl_optarg;
|
options.inputTileset = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
@@ -524,8 +529,9 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
localOptions.groupOutputs = true;
|
localOptions.groupOutputs = true;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
if (!options.output.empty())
|
if (!options.output.empty()) {
|
||||||
warning("Overriding tile data file %s", options.output.c_str());
|
warning("Overriding tile data file %s", options.output.c_str());
|
||||||
|
}
|
||||||
options.output = musl_optarg;
|
options.output = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
@@ -533,8 +539,9 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
localOptions.autoPalettes = false;
|
localOptions.autoPalettes = false;
|
||||||
if (!options.palettes.empty())
|
if (!options.palettes.empty()) {
|
||||||
warning("Overriding palettes file %s", options.palettes.c_str());
|
warning("Overriding palettes file %s", options.palettes.c_str());
|
||||||
|
}
|
||||||
options.palettes = musl_optarg;
|
options.palettes = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
@@ -542,8 +549,9 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
case 'q':
|
case 'q':
|
||||||
localOptions.autoPalmap = false;
|
localOptions.autoPalmap = false;
|
||||||
if (!options.palmap.empty())
|
if (!options.palmap.empty()) {
|
||||||
warning("Overriding palette map file %s", options.palmap.c_str());
|
warning("Overriding palette map file %s", options.palmap.c_str());
|
||||||
|
}
|
||||||
options.palmap = musl_optarg;
|
options.palmap = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
@@ -569,8 +577,9 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
localOptions.autoTilemap = false;
|
localOptions.autoTilemap = false;
|
||||||
if (!options.tilemap.empty())
|
if (!options.tilemap.empty()) {
|
||||||
warning("Overriding tilemap file %s", options.tilemap.c_str());
|
warning("Overriding tilemap file %s", options.tilemap.c_str());
|
||||||
|
}
|
||||||
options.tilemap = musl_optarg;
|
options.tilemap = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
@@ -768,19 +777,25 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fputs("Options:\n", stderr);
|
fputs("Options:\n", stderr);
|
||||||
if (options.columnMajor)
|
if (options.columnMajor) {
|
||||||
fputs("\tVisit image in column-major order\n", stderr);
|
fputs("\tVisit image in column-major order\n", stderr);
|
||||||
if (options.allowDedup)
|
}
|
||||||
|
if (options.allowDedup) {
|
||||||
fputs("\tAllow deduplicating tiles\n", stderr);
|
fputs("\tAllow deduplicating tiles\n", stderr);
|
||||||
if (options.allowMirroringX)
|
}
|
||||||
|
if (options.allowMirroringX) {
|
||||||
fputs("\tAllow deduplicating horizontally mirrored tiles\n", stderr);
|
fputs("\tAllow deduplicating horizontally mirrored tiles\n", stderr);
|
||||||
if (options.allowMirroringY)
|
}
|
||||||
|
if (options.allowMirroringY) {
|
||||||
fputs("\tAllow deduplicating vertically mirrored tiles\n", stderr);
|
fputs("\tAllow deduplicating vertically mirrored tiles\n", stderr);
|
||||||
if (options.useColorCurve)
|
}
|
||||||
|
if (options.useColorCurve) {
|
||||||
fputs("\tUse color curve\n", stderr);
|
fputs("\tUse color curve\n", stderr);
|
||||||
|
}
|
||||||
fprintf(stderr, "\tBit depth: %" PRIu8 "bpp\n", options.bitDepth);
|
fprintf(stderr, "\tBit depth: %" PRIu8 "bpp\n", options.bitDepth);
|
||||||
if (options.trim != 0)
|
if (options.trim != 0) {
|
||||||
fprintf(stderr, "\tTrim the last %" PRIu64 " tiles\n", options.trim);
|
fprintf(stderr, "\tTrim the last %" PRIu64 " tiles\n", options.trim);
|
||||||
|
}
|
||||||
fprintf(stderr, "\tMaximum %" PRIu16 " palettes\n", options.nbPalettes);
|
fprintf(stderr, "\tMaximum %" PRIu16 " palettes\n", options.nbPalettes);
|
||||||
fprintf(stderr, "\tPalettes contain %" PRIu8 " colors\n", options.nbColorsPerPal);
|
fprintf(stderr, "\tPalettes contain %" PRIu8 " colors\n", options.nbColorsPerPal);
|
||||||
fprintf(stderr, "\t%s palette spec\n", [] {
|
fprintf(stderr, "\t%s palette spec\n", [] {
|
||||||
|
|||||||
@@ -167,8 +167,9 @@ void reverse() {
|
|||||||
// This avoids redundancy with `-r 1` which results in a vertical column.
|
// This avoids redundancy with `-r 1` which results in a vertical column.
|
||||||
width = static_cast<size_t>(ceil(sqrt(mapSize)));
|
width = static_cast<size_t>(ceil(sqrt(mapSize)));
|
||||||
for (; width < mapSize; ++width) {
|
for (; width < mapSize; ++width) {
|
||||||
if (mapSize % width == 0)
|
if (mapSize % width == 0) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
options.verbosePrint(Options::VERB_INTERM, "Picked reversing width of %zu tiles\n", width);
|
options.verbosePrint(Options::VERB_INTERM, "Picked reversing width of %zu tiles\n", width);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,14 +74,17 @@ static void assignSection(Section §ion, MemoryLocation const &location) {
|
|||||||
static bool isLocationSuitable(
|
static bool isLocationSuitable(
|
||||||
Section const §ion, FreeSpace const &freeSpace, MemoryLocation const &location
|
Section const §ion, FreeSpace const &freeSpace, MemoryLocation const &location
|
||||||
) {
|
) {
|
||||||
if (section.isAddressFixed && section.org != location.address)
|
if (section.isAddressFixed && section.org != location.address) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (section.isAlignFixed && ((location.address - section.alignOfs) & section.alignMask))
|
if (section.isAlignFixed && ((location.address - section.alignOfs) & section.alignMask)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (location.address < freeSpace.address)
|
if (location.address < freeSpace.address) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return location.address + section.size <= freeSpace.address + freeSpace.size;
|
return location.address + section.size <= freeSpace.address + freeSpace.size;
|
||||||
}
|
}
|
||||||
@@ -102,16 +105,19 @@ static ssize_t getPlacement(Section const §ion, MemoryLocation &location) {
|
|||||||
if (section.isBankFixed) {
|
if (section.isBankFixed) {
|
||||||
location.bank = section.bank;
|
location.bank = section.bank;
|
||||||
} else if (scrambleROMX && section.type == SECTTYPE_ROMX) {
|
} else if (scrambleROMX && section.type == SECTTYPE_ROMX) {
|
||||||
if (curScrambleROM < 1)
|
if (curScrambleROM < 1) {
|
||||||
curScrambleROM = scrambleROMX;
|
curScrambleROM = scrambleROMX;
|
||||||
|
}
|
||||||
location.bank = curScrambleROM--;
|
location.bank = curScrambleROM--;
|
||||||
} else if (scrambleWRAMX && section.type == SECTTYPE_WRAMX) {
|
} else if (scrambleWRAMX && section.type == SECTTYPE_WRAMX) {
|
||||||
if (curScrambleWRAM < 1)
|
if (curScrambleWRAM < 1) {
|
||||||
curScrambleWRAM = scrambleWRAMX;
|
curScrambleWRAM = scrambleWRAMX;
|
||||||
|
}
|
||||||
location.bank = curScrambleWRAM--;
|
location.bank = curScrambleWRAM--;
|
||||||
} else if (scrambleSRAM && section.type == SECTTYPE_SRAM) {
|
} else if (scrambleSRAM && section.type == SECTTYPE_SRAM) {
|
||||||
if (curScrambleSRAM < 0)
|
if (curScrambleSRAM < 0) {
|
||||||
curScrambleSRAM = scrambleSRAM;
|
curScrambleSRAM = scrambleSRAM;
|
||||||
|
}
|
||||||
location.bank = curScrambleSRAM--;
|
location.bank = curScrambleSRAM--;
|
||||||
} else {
|
} else {
|
||||||
location.bank = typeInfo.firstBank;
|
location.bank = typeInfo.firstBank;
|
||||||
@@ -128,18 +134,20 @@ static ssize_t getPlacement(Section const §ion, MemoryLocation &location) {
|
|||||||
// Process locations in that bank
|
// Process locations in that bank
|
||||||
while (spaceIdx < bankMem.size()) {
|
while (spaceIdx < bankMem.size()) {
|
||||||
// If that location is OK, return it
|
// If that location is OK, return it
|
||||||
if (isLocationSuitable(section, bankMem[spaceIdx], location))
|
if (isLocationSuitable(section, bankMem[spaceIdx], location)) {
|
||||||
return spaceIdx;
|
return spaceIdx;
|
||||||
|
}
|
||||||
|
|
||||||
// Go to the next *possible* location
|
// Go to the next *possible* location
|
||||||
if (section.isAddressFixed) {
|
if (section.isAddressFixed) {
|
||||||
// If the address is fixed, there can be only
|
// If the address is fixed, there can be only
|
||||||
// one candidate block per bank; if we already
|
// one candidate block per bank; if we already
|
||||||
// reached it, give up.
|
// reached it, give up.
|
||||||
if (location.address < section.org)
|
if (location.address < section.org) {
|
||||||
location.address = section.org;
|
location.address = section.org;
|
||||||
else
|
} else {
|
||||||
break; // Try again in next bank
|
break; // Try again in next bank
|
||||||
|
}
|
||||||
} else if (section.isAlignFixed) {
|
} else if (section.isAlignFixed) {
|
||||||
// Move to next aligned location
|
// Move to next aligned location
|
||||||
// Move back to alignment boundary
|
// Move back to alignment boundary
|
||||||
@@ -151,15 +159,17 @@ static ssize_t getPlacement(Section const §ion, MemoryLocation &location) {
|
|||||||
} else {
|
} else {
|
||||||
// Any location is fine, so, next free block
|
// Any location is fine, so, next free block
|
||||||
spaceIdx++;
|
spaceIdx++;
|
||||||
if (spaceIdx < bankMem.size())
|
if (spaceIdx < bankMem.size()) {
|
||||||
location.address = bankMem[spaceIdx].address;
|
location.address = bankMem[spaceIdx].address;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If that location is past the current block's end,
|
// If that location is past the current block's end,
|
||||||
// go forwards until that is no longer the case.
|
// go forwards until that is no longer the case.
|
||||||
while (spaceIdx < bankMem.size()
|
while (spaceIdx < bankMem.size()
|
||||||
&& location.address >= bankMem[spaceIdx].address + bankMem[spaceIdx].size)
|
&& location.address >= bankMem[spaceIdx].address + bankMem[spaceIdx].size) {
|
||||||
spaceIdx++;
|
spaceIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
// Try again with the new location/free space combo
|
// Try again with the new location/free space combo
|
||||||
}
|
}
|
||||||
@@ -171,27 +181,30 @@ static ssize_t getPlacement(Section const §ion, MemoryLocation &location) {
|
|||||||
if (section.isBankFixed) {
|
if (section.isBankFixed) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (scrambleROMX && section.type == SECTTYPE_ROMX && location.bank <= scrambleROMX) {
|
} else if (scrambleROMX && section.type == SECTTYPE_ROMX && location.bank <= scrambleROMX) {
|
||||||
if (location.bank > typeInfo.firstBank)
|
if (location.bank > typeInfo.firstBank) {
|
||||||
location.bank--;
|
location.bank--;
|
||||||
else if (scrambleROMX < typeInfo.lastBank)
|
} else if (scrambleROMX < typeInfo.lastBank) {
|
||||||
location.bank = scrambleROMX + 1;
|
location.bank = scrambleROMX + 1;
|
||||||
else
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
} else if (scrambleWRAMX && section.type == SECTTYPE_WRAMX
|
} else if (scrambleWRAMX && section.type == SECTTYPE_WRAMX
|
||||||
&& location.bank <= scrambleWRAMX) {
|
&& location.bank <= scrambleWRAMX) {
|
||||||
if (location.bank > typeInfo.firstBank)
|
if (location.bank > typeInfo.firstBank) {
|
||||||
location.bank--;
|
location.bank--;
|
||||||
else if (scrambleWRAMX < typeInfo.lastBank)
|
} else if (scrambleWRAMX < typeInfo.lastBank) {
|
||||||
location.bank = scrambleWRAMX + 1;
|
location.bank = scrambleWRAMX + 1;
|
||||||
else
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
} else if (scrambleSRAM && section.type == SECTTYPE_SRAM && location.bank <= scrambleSRAM) {
|
} else if (scrambleSRAM && section.type == SECTTYPE_SRAM && location.bank <= scrambleSRAM) {
|
||||||
if (location.bank > typeInfo.firstBank)
|
if (location.bank > typeInfo.firstBank) {
|
||||||
location.bank--;
|
location.bank--;
|
||||||
else if (scrambleSRAM < typeInfo.lastBank)
|
} else if (scrambleSRAM < typeInfo.lastBank) {
|
||||||
location.bank = scrambleSRAM + 1;
|
location.bank = scrambleSRAM + 1;
|
||||||
else
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
} else if (location.bank < typeInfo.lastBank) {
|
} else if (location.bank < typeInfo.lastBank) {
|
||||||
location.bank++;
|
location.bank++;
|
||||||
} else {
|
} else {
|
||||||
@@ -251,9 +264,10 @@ static void placeSection(Section §ion) {
|
|||||||
} else {
|
} else {
|
||||||
// The amount of free spaces doesn't change: resize!
|
// The amount of free spaces doesn't change: resize!
|
||||||
freeSpace.size -= section.size;
|
freeSpace.size -= section.size;
|
||||||
if (noLeftSpace)
|
if (noLeftSpace) {
|
||||||
// The free space is moved *and* resized
|
// The free space is moved *and* resized
|
||||||
freeSpace.address += section.size;
|
freeSpace.address += section.size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -262,11 +276,11 @@ static void placeSection(Section §ion) {
|
|||||||
char where[64];
|
char where[64];
|
||||||
|
|
||||||
if (section.isBankFixed && nbbanks(section.type) != 1) {
|
if (section.isBankFixed && nbbanks(section.type) != 1) {
|
||||||
if (section.isAddressFixed)
|
if (section.isAddressFixed) {
|
||||||
snprintf(
|
snprintf(
|
||||||
where, sizeof(where), "at $%02" PRIx32 ":%04" PRIx16, section.bank, section.org
|
where, sizeof(where), "at $%02" PRIx32 ":%04" PRIx16, section.bank, section.org
|
||||||
);
|
);
|
||||||
else if (section.isAlignFixed)
|
} else if (section.isAlignFixed) {
|
||||||
snprintf(
|
snprintf(
|
||||||
where,
|
where,
|
||||||
sizeof(where),
|
sizeof(where),
|
||||||
@@ -274,12 +288,13 @@ static void placeSection(Section §ion) {
|
|||||||
section.bank,
|
section.bank,
|
||||||
static_cast<uint16_t>(~section.alignMask)
|
static_cast<uint16_t>(~section.alignMask)
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
snprintf(where, sizeof(where), "in bank $%02" PRIx32, section.bank);
|
snprintf(where, sizeof(where), "in bank $%02" PRIx32, section.bank);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (section.isAddressFixed)
|
if (section.isAddressFixed) {
|
||||||
snprintf(where, sizeof(where), "at address $%04" PRIx16, section.org);
|
snprintf(where, sizeof(where), "at address $%04" PRIx16, section.org);
|
||||||
else if (section.isAlignFixed)
|
} else if (section.isAlignFixed) {
|
||||||
snprintf(
|
snprintf(
|
||||||
where,
|
where,
|
||||||
sizeof(where),
|
sizeof(where),
|
||||||
@@ -287,20 +302,22 @@ static void placeSection(Section §ion) {
|
|||||||
static_cast<uint16_t>(~section.alignMask),
|
static_cast<uint16_t>(~section.alignMask),
|
||||||
section.alignOfs
|
section.alignOfs
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
strcpy(where, "anywhere");
|
strcpy(where, "anywhere");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a section failed to go to several places, nothing we can report
|
// If a section failed to go to several places, nothing we can report
|
||||||
if (!section.isBankFixed || !section.isAddressFixed)
|
if (!section.isBankFixed || !section.isAddressFixed) {
|
||||||
errx(
|
errx(
|
||||||
"Unable to place \"%s\" (%s section) %s",
|
"Unable to place \"%s\" (%s section) %s",
|
||||||
section.name.c_str(),
|
section.name.c_str(),
|
||||||
sectionTypeInfo[section.type].name.c_str(),
|
sectionTypeInfo[section.type].name.c_str(),
|
||||||
where
|
where
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// If the section just can't fit the bank, report that
|
// If the section just can't fit the bank, report that
|
||||||
else if (section.org + section.size > endaddr(section.type) + 1)
|
else if (section.org + section.size > endaddr(section.type) + 1) {
|
||||||
errx(
|
errx(
|
||||||
"Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > "
|
"Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > "
|
||||||
"$%04x)",
|
"$%04x)",
|
||||||
@@ -310,8 +327,9 @@ static void placeSection(Section §ion) {
|
|||||||
section.org + section.size,
|
section.org + section.size,
|
||||||
endaddr(section.type) + 1
|
endaddr(section.type) + 1
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Otherwise there is overlap with another section
|
// Otherwise there is overlap with another section
|
||||||
else
|
else {
|
||||||
errx(
|
errx(
|
||||||
"Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
|
"Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
|
||||||
section.name.c_str(),
|
section.name.c_str(),
|
||||||
@@ -319,6 +337,7 @@ static void placeSection(Section §ion) {
|
|||||||
where,
|
where,
|
||||||
out_OverlappingSection(section)->name.c_str()
|
out_OverlappingSection(section)->name.c_str()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off: vertically align values
|
// clang-format off: vertically align values
|
||||||
@@ -334,20 +353,24 @@ static std::deque<Section *> unassignedSections[1 << 3];
|
|||||||
static void categorizeSection(Section §ion) {
|
static void categorizeSection(Section §ion) {
|
||||||
uint8_t constraints = 0;
|
uint8_t constraints = 0;
|
||||||
|
|
||||||
if (section.isBankFixed)
|
if (section.isBankFixed) {
|
||||||
constraints |= BANK_CONSTRAINED;
|
constraints |= BANK_CONSTRAINED;
|
||||||
if (section.isAddressFixed)
|
}
|
||||||
|
if (section.isAddressFixed) {
|
||||||
constraints |= ORG_CONSTRAINED;
|
constraints |= ORG_CONSTRAINED;
|
||||||
|
}
|
||||||
// Can't have both!
|
// Can't have both!
|
||||||
else if (section.isAlignFixed)
|
else if (section.isAlignFixed) {
|
||||||
constraints |= ALIGN_CONSTRAINED;
|
constraints |= ALIGN_CONSTRAINED;
|
||||||
|
}
|
||||||
|
|
||||||
std::deque<Section *> §ions = unassignedSections[constraints];
|
std::deque<Section *> §ions = unassignedSections[constraints];
|
||||||
auto pos = sections.begin();
|
auto pos = sections.begin();
|
||||||
|
|
||||||
// Insert section while keeping the list sorted by decreasing size
|
// Insert section while keeping the list sorted by decreasing size
|
||||||
while (pos != sections.end() && (*pos)->size > section.size)
|
while (pos != sections.end() && (*pos)->size > section.size) {
|
||||||
pos++;
|
pos++;
|
||||||
|
}
|
||||||
sections.insert(pos, §ion);
|
sections.insert(pos, §ion);
|
||||||
|
|
||||||
nbSectionsToAssign++;
|
nbSectionsToAssign++;
|
||||||
@@ -368,12 +391,14 @@ void assign_AssignSections() {
|
|||||||
|
|
||||||
// Specially process fully-constrained sections because of overlaying
|
// Specially process fully-constrained sections because of overlaying
|
||||||
verbosePrint("Assigning bank+org-constrained...\n");
|
verbosePrint("Assigning bank+org-constrained...\n");
|
||||||
for (Section *section : unassignedSections[BANK_CONSTRAINED | ORG_CONSTRAINED])
|
for (Section *section : unassignedSections[BANK_CONSTRAINED | ORG_CONSTRAINED]) {
|
||||||
placeSection(*section);
|
placeSection(*section);
|
||||||
|
}
|
||||||
|
|
||||||
// If all sections were fully constrained, we have nothing left to do
|
// If all sections were fully constrained, we have nothing left to do
|
||||||
if (!nbSectionsToAssign)
|
if (!nbSectionsToAssign) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Overlaying requires only fully-constrained sections
|
// Overlaying requires only fully-constrained sections
|
||||||
verbosePrint("Assigning other sections...\n");
|
verbosePrint("Assigning other sections...\n");
|
||||||
@@ -385,14 +410,16 @@ void assign_AssignSections() {
|
|||||||
for (Section *section : unassignedSections[constraints]) {
|
for (Section *section : unassignedSections[constraints]) {
|
||||||
fprintf(stderr, "%c \"%s\"", nbSections == 0 ? ';' : ',', section->name.c_str());
|
fprintf(stderr, "%c \"%s\"", nbSections == 0 ? ';' : ',', section->name.c_str());
|
||||||
nbSections++;
|
nbSections++;
|
||||||
if (nbSections == 10)
|
if (nbSections == 10) {
|
||||||
goto max_out; // Can't `break` out of a nested loop
|
goto max_out; // Can't `break` out of a nested loop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
max_out:
|
max_out:
|
||||||
if (nbSectionsToAssign != nbSections)
|
if (nbSectionsToAssign != nbSections) {
|
||||||
fprintf(stderr, " and %" PRIu64 " more", nbSectionsToAssign - nbSections);
|
fprintf(stderr, " and %" PRIu64 " more", nbSectionsToAssign - nbSections);
|
||||||
|
}
|
||||||
fprintf(stderr, " %sn't\n", nbSectionsToAssign == 1 ? "is" : "are");
|
fprintf(stderr, " %sn't\n", nbSectionsToAssign == 1 ? "is" : "are");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -400,11 +427,13 @@ max_out:
|
|||||||
// Assign all remaining sections by decreasing constraint order
|
// Assign all remaining sections by decreasing constraint order
|
||||||
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0;
|
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0;
|
||||||
constraints--) {
|
constraints--) {
|
||||||
for (Section *section : unassignedSections[constraints])
|
for (Section *section : unassignedSections[constraints]) {
|
||||||
placeSection(*section);
|
placeSection(*section);
|
||||||
|
}
|
||||||
|
|
||||||
if (!nbSectionsToAssign)
|
if (!nbSectionsToAssign) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unreachable_();
|
unreachable_();
|
||||||
|
|||||||
@@ -52,8 +52,9 @@ std::string const &FileStackNode::dump(uint32_t curLineNo) const {
|
|||||||
std::string const &lastName = parent->dump(lineNo);
|
std::string const &lastName = parent->dump(lineNo);
|
||||||
fputs(" -> ", stderr);
|
fputs(" -> ", stderr);
|
||||||
fputs(lastName.c_str(), stderr);
|
fputs(lastName.c_str(), stderr);
|
||||||
for (uint32_t iter : iters())
|
for (uint32_t iter : iters()) {
|
||||||
fprintf(stderr, "::REPT~%" PRIu32, iter);
|
fprintf(stderr, "::REPT~%" PRIu32, iter);
|
||||||
|
}
|
||||||
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
|
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
|
||||||
return lastName;
|
return lastName;
|
||||||
} else {
|
} else {
|
||||||
@@ -96,8 +97,9 @@ void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
|
|||||||
printDiag(fmt, args, "error", where, lineNo);
|
printDiag(fmt, args, "error", where, lineNo);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
if (nbErrors != UINT32_MAX)
|
if (nbErrors != UINT32_MAX) {
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void argErr(char flag, char const *fmt, ...) {
|
void argErr(char flag, char const *fmt, ...) {
|
||||||
@@ -109,8 +111,9 @@ void argErr(char flag, char const *fmt, ...) {
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
|
|
||||||
if (nbErrors != UINT32_MAX)
|
if (nbErrors != UINT32_MAX) {
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
@@ -121,8 +124,9 @@ void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
|
|||||||
printDiag(fmt, args, "FATAL", where, lineNo);
|
printDiag(fmt, args, "FATAL", where, lineNo);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
if (nbErrors != UINT32_MAX)
|
if (nbErrors != UINT32_MAX) {
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
|
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
|
||||||
@@ -216,10 +220,12 @@ static void parseScrambleSpec(char const *spec) {
|
|||||||
if (regionNameLen == 0) {
|
if (regionNameLen == 0) {
|
||||||
argErr('S', "Missing region name");
|
argErr('S', "Missing region name");
|
||||||
|
|
||||||
if (*spec == '\0')
|
if (*spec == '\0') {
|
||||||
break;
|
break;
|
||||||
if (*spec == '=') // Skip the limit, too
|
}
|
||||||
|
if (*spec == '=') { // Skip the limit, too
|
||||||
spec = strchr(&spec[1], ','); // Skip to next comma, if any
|
spec = strchr(&spec[1], ','); // Skip to next comma, if any
|
||||||
|
}
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,8 +248,9 @@ static void parseScrambleSpec(char const *spec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region == SCRAMBLE_UNK)
|
if (region == SCRAMBLE_UNK) {
|
||||||
argErr('S', "Unknown region \"%.*s\"", regionNameFmtLen, regionName);
|
argErr('S', "Unknown region \"%.*s\"", regionNameFmtLen, regionName);
|
||||||
|
}
|
||||||
|
|
||||||
if (*spec == '=') {
|
if (*spec == '=') {
|
||||||
spec++; // `strtoul` will skip the whitespace on its own
|
spec++; // `strtoul` will skip the whitespace on its own
|
||||||
@@ -301,10 +308,12 @@ static void parseScrambleSpec(char const *spec) {
|
|||||||
next: // Can't `continue` a `for` loop with this nontrivial iteration logic
|
next: // Can't `continue` a `for` loop with this nontrivial iteration logic
|
||||||
if (spec) {
|
if (spec) {
|
||||||
assume(*spec == ',' || *spec == '\0');
|
assume(*spec == ',' || *spec == '\0');
|
||||||
if (*spec == ',')
|
if (*spec == ',') {
|
||||||
spec += 1 + strspn(&spec[1], " \t");
|
spec += 1 + strspn(&spec[1], " \t");
|
||||||
if (*spec == '\0')
|
}
|
||||||
|
if (*spec == '\0') {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,31 +338,36 @@ int main(int argc, char *argv[]) {
|
|||||||
printUsage();
|
printUsage();
|
||||||
exit(0);
|
exit(0);
|
||||||
case 'l':
|
case 'l':
|
||||||
if (linkerScriptName)
|
if (linkerScriptName) {
|
||||||
warnx("Overriding linker script %s", linkerScriptName);
|
warnx("Overriding linker script %s", linkerScriptName);
|
||||||
|
}
|
||||||
linkerScriptName = musl_optarg;
|
linkerScriptName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
noSymInMap = true;
|
noSymInMap = true;
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
if (mapFileName)
|
if (mapFileName) {
|
||||||
warnx("Overriding map file %s", mapFileName);
|
warnx("Overriding map file %s", mapFileName);
|
||||||
|
}
|
||||||
mapFileName = musl_optarg;
|
mapFileName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
if (symFileName)
|
if (symFileName) {
|
||||||
warnx("Overriding sym file %s", symFileName);
|
warnx("Overriding sym file %s", symFileName);
|
||||||
|
}
|
||||||
symFileName = musl_optarg;
|
symFileName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'O':
|
case 'O':
|
||||||
if (overlayFileName)
|
if (overlayFileName) {
|
||||||
warnx("Overriding overlay file %s", overlayFileName);
|
warnx("Overriding overlay file %s", overlayFileName);
|
||||||
|
}
|
||||||
overlayFileName = musl_optarg;
|
overlayFileName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
if (outputFileName)
|
if (outputFileName) {
|
||||||
warnx("Overriding output file %s", outputFileName);
|
warnx("Overriding output file %s", outputFileName);
|
||||||
|
}
|
||||||
outputFileName = musl_optarg;
|
outputFileName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'p': {
|
case 'p': {
|
||||||
@@ -410,18 +424,22 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Patch the size array depending on command-line options
|
// Patch the size array depending on command-line options
|
||||||
if (!is32kMode)
|
if (!is32kMode) {
|
||||||
sectionTypeInfo[SECTTYPE_ROM0].size = 0x4000;
|
sectionTypeInfo[SECTTYPE_ROM0].size = 0x4000;
|
||||||
if (!isWRAM0Mode)
|
}
|
||||||
|
if (!isWRAM0Mode) {
|
||||||
sectionTypeInfo[SECTTYPE_WRAM0].size = 0x1000;
|
sectionTypeInfo[SECTTYPE_WRAM0].size = 0x1000;
|
||||||
|
}
|
||||||
|
|
||||||
// Patch the bank ranges array depending on command-line options
|
// Patch the bank ranges array depending on command-line options
|
||||||
if (isDmgMode)
|
if (isDmgMode) {
|
||||||
sectionTypeInfo[SECTTYPE_VRAM].lastBank = 0;
|
sectionTypeInfo[SECTTYPE_VRAM].lastBank = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Read all object files first,
|
// Read all object files first,
|
||||||
for (obj_Setup(argc - curArgIndex); curArgIndex < argc; curArgIndex++)
|
for (obj_Setup(argc - curArgIndex); curArgIndex < argc; curArgIndex++) {
|
||||||
obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1);
|
obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
// apply the linker script's modifications,
|
// apply the linker script's modifications,
|
||||||
if (linkerScriptName) {
|
if (linkerScriptName) {
|
||||||
@@ -430,20 +448,23 @@ int main(int argc, char *argv[]) {
|
|||||||
script_ProcessScript(linkerScriptName);
|
script_ProcessScript(linkerScriptName);
|
||||||
|
|
||||||
// If the linker script produced any errors, some sections may be in an invalid state
|
// If the linker script produced any errors, some sections may be in an invalid state
|
||||||
if (nbErrors != 0)
|
if (nbErrors != 0) {
|
||||||
reportErrors();
|
reportErrors();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// then process them,
|
// then process them,
|
||||||
sect_DoSanityChecks();
|
sect_DoSanityChecks();
|
||||||
if (nbErrors != 0)
|
if (nbErrors != 0) {
|
||||||
reportErrors();
|
reportErrors();
|
||||||
|
}
|
||||||
assign_AssignSections();
|
assign_AssignSections();
|
||||||
patch_CheckAssertions();
|
patch_CheckAssertions();
|
||||||
|
|
||||||
// and finally output the result.
|
// and finally output the result.
|
||||||
patch_ApplyPatches();
|
patch_ApplyPatches();
|
||||||
if (nbErrors != 0)
|
if (nbErrors != 0) {
|
||||||
reportErrors();
|
reportErrors();
|
||||||
|
}
|
||||||
out_WriteFiles();
|
out_WriteFiles();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ static int64_t readLong(FILE *file) {
|
|||||||
for (uint8_t shift = 0; shift < sizeof(value) * CHAR_BIT; shift += 8) {
|
for (uint8_t shift = 0; shift < sizeof(value) * CHAR_BIT; shift += 8) {
|
||||||
int byte = getc(file);
|
int byte = getc(file);
|
||||||
|
|
||||||
if (byte == EOF)
|
if (byte == EOF) {
|
||||||
return INT64_MAX;
|
return INT64_MAX;
|
||||||
|
}
|
||||||
// This must be casted to `unsigned`, not `uint8_t`. Rationale:
|
// This must be casted to `unsigned`, not `uint8_t`. Rationale:
|
||||||
// the type of the shift is the type of `byte` after undergoing
|
// the type of the shift is the type of `byte` after undergoing
|
||||||
// integer promotion, which would be `int` if this was casted to
|
// integer promotion, which would be `int` if this was casted to
|
||||||
@@ -141,7 +142,7 @@ static void readFileStackNode(
|
|||||||
case NODE_REPT:
|
case NODE_REPT:
|
||||||
tryReadLong(depth, file, "%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
|
tryReadLong(depth, file, "%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
|
||||||
node.data = std::vector<uint32_t>(depth);
|
node.data = std::vector<uint32_t>(depth);
|
||||||
for (uint32_t k = 0; k < depth; k++)
|
for (uint32_t k = 0; k < depth; k++) {
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
node.iters()[k],
|
node.iters()[k],
|
||||||
file,
|
file,
|
||||||
@@ -150,7 +151,8 @@ static void readFileStackNode(
|
|||||||
i,
|
i,
|
||||||
k
|
k
|
||||||
);
|
);
|
||||||
if (!node.parent)
|
}
|
||||||
|
if (!node.parent) {
|
||||||
fatal(
|
fatal(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
@@ -158,6 +160,7 @@ static void readFileStackNode(
|
|||||||
fileName,
|
fileName,
|
||||||
i
|
i
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +299,7 @@ static void readPatch(
|
|||||||
patch.rpnExpression.resize(rpnSize);
|
patch.rpnExpression.resize(rpnSize);
|
||||||
size_t nbElementsRead = fread(patch.rpnExpression.data(), 1, rpnSize, file);
|
size_t nbElementsRead = fread(patch.rpnExpression.data(), 1, rpnSize, file);
|
||||||
|
|
||||||
if (nbElementsRead != rpnSize)
|
if (nbElementsRead != rpnSize) {
|
||||||
errx(
|
errx(
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
||||||
fileName,
|
fileName,
|
||||||
@@ -304,6 +307,7 @@ static void readPatch(
|
|||||||
i,
|
i,
|
||||||
feof(file) ? "Unexpected end of file" : strerror(errno)
|
feof(file) ? "Unexpected end of file" : strerror(errno)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets a patch's pcSection from its pcSectionID.
|
// Sets a patch's pcSection from its pcSectionID.
|
||||||
@@ -338,8 +342,9 @@ static void readSection(
|
|||||||
section.name.c_str()
|
section.name.c_str()
|
||||||
);
|
);
|
||||||
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s", fileName, section.name.c_str());
|
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s", fileName, section.name.c_str());
|
||||||
if (tmp < 0 || tmp > UINT16_MAX)
|
if (tmp < 0 || tmp > UINT16_MAX) {
|
||||||
errx("\"%s\"'s section size ($%" PRIx32 ") is invalid", section.name.c_str(), tmp);
|
errx("\"%s\"'s section size ($%" PRIx32 ") is invalid", section.name.c_str(), tmp);
|
||||||
|
}
|
||||||
section.size = tmp;
|
section.size = tmp;
|
||||||
section.offset = 0;
|
section.offset = 0;
|
||||||
tryGetc(
|
tryGetc(
|
||||||
@@ -350,12 +355,13 @@ static void readSection(
|
|||||||
} else {
|
} else {
|
||||||
section.type = SectionType(type);
|
section.type = SectionType(type);
|
||||||
}
|
}
|
||||||
if (byte >> 7)
|
if (byte >> 7) {
|
||||||
section.modifier = SECTION_UNION;
|
section.modifier = SECTION_UNION;
|
||||||
else if (byte >> 6)
|
} else if (byte >> 6) {
|
||||||
section.modifier = SECTION_FRAGMENT;
|
section.modifier = SECTION_FRAGMENT;
|
||||||
else
|
} else {
|
||||||
section.modifier = SECTION_NORMAL;
|
section.modifier = SECTION_NORMAL;
|
||||||
|
}
|
||||||
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s org: %s", fileName, section.name.c_str());
|
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s org: %s", fileName, section.name.c_str());
|
||||||
section.isAddressFixed = tmp >= 0;
|
section.isAddressFixed = tmp >= 0;
|
||||||
if (tmp > UINT16_MAX) {
|
if (tmp > UINT16_MAX) {
|
||||||
@@ -374,8 +380,9 @@ static void readSection(
|
|||||||
fileName,
|
fileName,
|
||||||
section.name.c_str()
|
section.name.c_str()
|
||||||
);
|
);
|
||||||
if (byte > 16)
|
if (byte > 16) {
|
||||||
byte = 16;
|
byte = 16;
|
||||||
|
}
|
||||||
section.isAlignFixed = byte != 0;
|
section.isAlignFixed = byte != 0;
|
||||||
section.alignMask = (1 << byte) - 1;
|
section.alignMask = (1 << byte) - 1;
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
@@ -397,13 +404,14 @@ static void readSection(
|
|||||||
if (section.size) {
|
if (section.size) {
|
||||||
section.data.resize(section.size);
|
section.data.resize(section.size);
|
||||||
if (size_t nbRead = fread(section.data.data(), 1, section.size, file);
|
if (size_t nbRead = fread(section.data.data(), 1, section.size, file);
|
||||||
nbRead != section.size)
|
nbRead != section.size) {
|
||||||
errx(
|
errx(
|
||||||
"%s: Cannot read \"%s\"'s data: %s",
|
"%s: Cannot read \"%s\"'s data: %s",
|
||||||
fileName,
|
fileName,
|
||||||
section.name.c_str(),
|
section.name.c_str(),
|
||||||
feof(file) ? "Unexpected end of file" : strerror(errno)
|
feof(file) ? "Unexpected end of file" : strerror(errno)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t nbPatches;
|
uint32_t nbPatches;
|
||||||
@@ -417,8 +425,9 @@ static void readSection(
|
|||||||
);
|
);
|
||||||
|
|
||||||
section.patches.resize(nbPatches);
|
section.patches.resize(nbPatches);
|
||||||
for (uint32_t i = 0; i < nbPatches; i++)
|
for (uint32_t i = 0; i < nbPatches; i++) {
|
||||||
readPatch(file, section.patches[i], fileName, section.name, i, fileNodes);
|
readPatch(file, section.patches[i], fileName, section.name, i, fileNodes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,10 +442,11 @@ static void linkSymToSect(Symbol &symbol, Section §ion) {
|
|||||||
uint32_t c = (a + b) / 2;
|
uint32_t c = (a + b) / 2;
|
||||||
int32_t otherOffset = section.symbols[c]->label().offset;
|
int32_t otherOffset = section.symbols[c]->label().offset;
|
||||||
|
|
||||||
if (otherOffset > symbolOffset)
|
if (otherOffset > symbolOffset) {
|
||||||
b = c;
|
b = c;
|
||||||
else
|
} else {
|
||||||
a = c + 1;
|
a = c + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section.symbols.insert(section.symbols.begin() + a, &symbol);
|
section.symbols.insert(section.symbols.begin() + a, &symbol);
|
||||||
@@ -469,8 +479,9 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
|||||||
(void)setmode(STDIN_FILENO, O_BINARY);
|
(void)setmode(STDIN_FILENO, O_BINARY);
|
||||||
file = stdin;
|
file = stdin;
|
||||||
}
|
}
|
||||||
if (!file)
|
if (!file) {
|
||||||
err("Failed to open file \"%s\"", fileName);
|
err("Failed to open file \"%s\"", fileName);
|
||||||
|
}
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
// First, check if the object is a RGBDS object or a SDCC one. If the first byte is 'R',
|
// First, check if the object is a RGBDS object or a SDCC one. If the first byte is 'R',
|
||||||
@@ -506,15 +517,16 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
|||||||
int matchedElems;
|
int matchedElems;
|
||||||
|
|
||||||
if (fscanf(file, RGBDS_OBJECT_VERSION_STRING "%n", &matchedElems) == 1
|
if (fscanf(file, RGBDS_OBJECT_VERSION_STRING "%n", &matchedElems) == 1
|
||||||
&& matchedElems != literal_strlen(RGBDS_OBJECT_VERSION_STRING))
|
&& matchedElems != literal_strlen(RGBDS_OBJECT_VERSION_STRING)) {
|
||||||
errx("%s: Not a RGBDS object file", fileName);
|
errx("%s: Not a RGBDS object file", fileName);
|
||||||
|
}
|
||||||
|
|
||||||
verbosePrint("Reading object file %s\n", fileName);
|
verbosePrint("Reading object file %s\n", fileName);
|
||||||
|
|
||||||
uint32_t revNum;
|
uint32_t revNum;
|
||||||
|
|
||||||
tryReadLong(revNum, file, "%s: Cannot read revision number: %s", fileName);
|
tryReadLong(revNum, file, "%s: Cannot read revision number: %s", fileName);
|
||||||
if (revNum != RGBDS_OBJECT_REV)
|
if (revNum != RGBDS_OBJECT_REV) {
|
||||||
errx(
|
errx(
|
||||||
"%s: Unsupported object file for rgblink %s; try rebuilding \"%s\"%s"
|
"%s: Unsupported object file for rgblink %s; try rebuilding \"%s\"%s"
|
||||||
" (expected revision %d, got %d)",
|
" (expected revision %d, got %d)",
|
||||||
@@ -525,6 +537,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
|||||||
RGBDS_OBJECT_REV,
|
RGBDS_OBJECT_REV,
|
||||||
revNum
|
revNum
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t nbNodes;
|
uint32_t nbNodes;
|
||||||
uint32_t nbSymbols;
|
uint32_t nbSymbols;
|
||||||
@@ -538,8 +551,9 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
|||||||
tryReadLong(nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
|
tryReadLong(nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
|
||||||
nodes[fileID].resize(nbNodes);
|
nodes[fileID].resize(nbNodes);
|
||||||
verbosePrint("Reading %u nodes...\n", nbNodes);
|
verbosePrint("Reading %u nodes...\n", nbNodes);
|
||||||
for (uint32_t i = nbNodes; i--;)
|
for (uint32_t i = nbNodes; i--;) {
|
||||||
readFileStackNode(file, nodes[fileID], i, fileName);
|
readFileStackNode(file, nodes[fileID], i, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
// This file's symbols, kept to link sections to them
|
// This file's symbols, kept to link sections to them
|
||||||
std::vector<Symbol> &fileSymbols = symbolLists.emplace_front(nbSymbols);
|
std::vector<Symbol> &fileSymbols = symbolLists.emplace_front(nbSymbols);
|
||||||
@@ -553,8 +567,9 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
|||||||
readSymbol(file, symbol, fileName, nodes[fileID]);
|
readSymbol(file, symbol, fileName, nodes[fileID]);
|
||||||
|
|
||||||
sym_AddSymbol(symbol);
|
sym_AddSymbol(symbol);
|
||||||
if (symbol.data.holds<Label>())
|
if (symbol.data.holds<Label>()) {
|
||||||
nbSymPerSect[symbol.data.get<Label>().sectionID]++;
|
nbSymPerSect[symbol.data.get<Label>().sectionID]++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This file's sections, stored in a table to link symbols to them
|
// This file's sections, stored in a table to link symbols to them
|
||||||
@@ -585,8 +600,9 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
|||||||
// Give patches' PC section pointers to their sections
|
// Give patches' PC section pointers to their sections
|
||||||
for (uint32_t i = 0; i < nbSections; i++) {
|
for (uint32_t i = 0; i < nbSections; i++) {
|
||||||
if (sect_HasData(fileSections[i]->type)) {
|
if (sect_HasData(fileSections[i]->type)) {
|
||||||
for (Patch &patch : fileSections[i]->patches)
|
for (Patch &patch : fileSections[i]->patches) {
|
||||||
linkPatchToPCSect(patch, fileSections);
|
linkPatchToPCSect(patch, fileSections);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,8 +617,9 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calling `sect_AddSection` invalidates the contents of `fileSections`!
|
// Calling `sect_AddSection` invalidates the contents of `fileSections`!
|
||||||
for (uint32_t i = 0; i < nbSections; i++)
|
for (uint32_t i = 0; i < nbSections; i++) {
|
||||||
sect_AddSection(std::move(fileSections[i]));
|
sect_AddSection(std::move(fileSections[i]));
|
||||||
|
}
|
||||||
|
|
||||||
// Fix symbols' section pointers to component sections
|
// Fix symbols' section pointers to component sections
|
||||||
// This has to run **after** all the `sect_AddSection()` calls,
|
// This has to run **after** all the `sect_AddSection()` calls,
|
||||||
|
|||||||
@@ -66,24 +66,27 @@ void out_AddSection(Section const §ion) {
|
|||||||
uint32_t targetBank = section.bank - sectionTypeInfo[section.type].firstBank;
|
uint32_t targetBank = section.bank - sectionTypeInfo[section.type].firstBank;
|
||||||
uint32_t minNbBanks = targetBank + 1;
|
uint32_t minNbBanks = targetBank + 1;
|
||||||
|
|
||||||
if (minNbBanks > maxNbBanks[section.type])
|
if (minNbBanks > maxNbBanks[section.type]) {
|
||||||
errx(
|
errx(
|
||||||
"Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
|
"Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
|
||||||
section.name.c_str(),
|
section.name.c_str(),
|
||||||
section.bank,
|
section.bank,
|
||||||
maxNbBanks[section.type] - 1
|
maxNbBanks[section.type] - 1
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = sections[section.type].size(); i < minNbBanks; i++)
|
for (uint32_t i = sections[section.type].size(); i < minNbBanks; i++) {
|
||||||
sections[section.type].emplace_back();
|
sections[section.type].emplace_back();
|
||||||
|
}
|
||||||
|
|
||||||
std::deque<Section const *> &bankSections =
|
std::deque<Section const *> &bankSections =
|
||||||
section.size ? sections[section.type][targetBank].sections
|
section.size ? sections[section.type][targetBank].sections
|
||||||
: sections[section.type][targetBank].zeroLenSections;
|
: sections[section.type][targetBank].zeroLenSections;
|
||||||
auto pos = bankSections.begin();
|
auto pos = bankSections.begin();
|
||||||
|
|
||||||
while (pos != bankSections.end() && (*pos)->org < section.org)
|
while (pos != bankSections.end() && (*pos)->org < section.org) {
|
||||||
pos++;
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
bankSections.insert(pos, §ion);
|
bankSections.insert(pos, §ion);
|
||||||
}
|
}
|
||||||
@@ -92,8 +95,9 @@ Section const *out_OverlappingSection(Section const §ion) {
|
|||||||
uint32_t bank = section.bank - sectionTypeInfo[section.type].firstBank;
|
uint32_t bank = section.bank - sectionTypeInfo[section.type].firstBank;
|
||||||
|
|
||||||
for (Section const *ptr : sections[section.type][bank].sections) {
|
for (Section const *ptr : sections[section.type][bank].sections) {
|
||||||
if (ptr->org < section.org + section.size && section.org < ptr->org + ptr->size)
|
if (ptr->org < section.org + section.size && section.org < ptr->org + ptr->size) {
|
||||||
return ptr;
|
return ptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -101,8 +105,9 @@ Section const *out_OverlappingSection(Section const §ion) {
|
|||||||
// Performs sanity checks on the overlay file.
|
// Performs sanity checks on the overlay file.
|
||||||
// @return The number of ROM banks in the overlay file
|
// @return The number of ROM banks in the overlay file
|
||||||
static uint32_t checkOverlaySize() {
|
static uint32_t checkOverlaySize() {
|
||||||
if (!overlayFile)
|
if (!overlayFile) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (fseek(overlayFile, 0, SEEK_END) != 0) {
|
if (fseek(overlayFile, 0, SEEK_END) != 0) {
|
||||||
warnx("Overlay file is not seekable, cannot check if properly formed");
|
warnx("Overlay file is not seekable, cannot check if properly formed");
|
||||||
@@ -114,12 +119,13 @@ static uint32_t checkOverlaySize() {
|
|||||||
// Reset back to beginning
|
// Reset back to beginning
|
||||||
fseek(overlayFile, 0, SEEK_SET);
|
fseek(overlayFile, 0, SEEK_SET);
|
||||||
|
|
||||||
if (overlaySize % BANK_SIZE)
|
if (overlaySize % BANK_SIZE) {
|
||||||
warnx("Overlay file does not have a size multiple of 0x4000");
|
warnx("Overlay file does not have a size multiple of 0x4000");
|
||||||
else if (is32kMode && overlaySize != 0x8000)
|
} else if (is32kMode && overlaySize != 0x8000) {
|
||||||
warnx("Overlay is not exactly 0x8000 bytes large");
|
warnx("Overlay is not exactly 0x8000 bytes large");
|
||||||
else if (overlaySize < 0x8000)
|
} else if (overlaySize < 0x8000) {
|
||||||
warnx("Overlay is less than 0x8000 bytes large");
|
warnx("Overlay is less than 0x8000 bytes large");
|
||||||
|
}
|
||||||
|
|
||||||
return (overlaySize + BANK_SIZE - 1) / BANK_SIZE;
|
return (overlaySize + BANK_SIZE - 1) / BANK_SIZE;
|
||||||
}
|
}
|
||||||
@@ -137,8 +143,9 @@ static void coverOverlayBanks(uint32_t nbOverlayBanks) {
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
if (nbUncoveredBanks > sections[SECTTYPE_ROMX].size()) {
|
if (nbUncoveredBanks > sections[SECTTYPE_ROMX].size()) {
|
||||||
for (uint32_t i = sections[SECTTYPE_ROMX].size(); i < nbUncoveredBanks; i++)
|
for (uint32_t i = sections[SECTTYPE_ROMX].size(); i < nbUncoveredBanks; i++) {
|
||||||
sections[SECTTYPE_ROMX].emplace_back();
|
sections[SECTTYPE_ROMX].emplace_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,8 +186,9 @@ static void
|
|||||||
fwrite(section->data.data(), 1, section->size, outputFile);
|
fwrite(section->data.data(), 1, section->size, outputFile);
|
||||||
if (overlayFile) {
|
if (overlayFile) {
|
||||||
// Skip bytes even with pipes
|
// Skip bytes even with pipes
|
||||||
for (uint16_t i = 0; i < section->size; i++)
|
for (uint16_t i = 0; i < section->size; i++) {
|
||||||
getc(overlayFile);
|
getc(overlayFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
offset += section->size;
|
offset += section->size;
|
||||||
}
|
}
|
||||||
@@ -204,12 +212,14 @@ static void writeROM() {
|
|||||||
(void)setmode(STDOUT_FILENO, O_BINARY);
|
(void)setmode(STDOUT_FILENO, O_BINARY);
|
||||||
outputFile = stdout;
|
outputFile = stdout;
|
||||||
}
|
}
|
||||||
if (!outputFile)
|
if (!outputFile) {
|
||||||
err("Failed to open output file \"%s\"", outputFileName);
|
err("Failed to open output file \"%s\"", outputFileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Defer closeOutputFile{[&] {
|
Defer closeOutputFile{[&] {
|
||||||
if (outputFile)
|
if (outputFile) {
|
||||||
fclose(outputFile);
|
fclose(outputFile);
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
if (overlayFileName) {
|
if (overlayFileName) {
|
||||||
@@ -220,18 +230,21 @@ static void writeROM() {
|
|||||||
(void)setmode(STDIN_FILENO, O_BINARY);
|
(void)setmode(STDIN_FILENO, O_BINARY);
|
||||||
overlayFile = stdin;
|
overlayFile = stdin;
|
||||||
}
|
}
|
||||||
if (!overlayFile)
|
if (!overlayFile) {
|
||||||
err("Failed to open overlay file \"%s\"", overlayFileName);
|
err("Failed to open overlay file \"%s\"", overlayFileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Defer closeOverlayFile{[&] {
|
Defer closeOverlayFile{[&] {
|
||||||
if (overlayFile)
|
if (overlayFile) {
|
||||||
fclose(overlayFile);
|
fclose(overlayFile);
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
uint32_t nbOverlayBanks = checkOverlaySize();
|
uint32_t nbOverlayBanks = checkOverlaySize();
|
||||||
|
|
||||||
if (nbOverlayBanks > 0)
|
if (nbOverlayBanks > 0) {
|
||||||
coverOverlayBanks(nbOverlayBanks);
|
coverOverlayBanks(nbOverlayBanks);
|
||||||
|
}
|
||||||
|
|
||||||
if (outputFile) {
|
if (outputFile) {
|
||||||
writeBank(
|
writeBank(
|
||||||
@@ -240,12 +253,13 @@ static void writeROM() {
|
|||||||
sectionTypeInfo[SECTTYPE_ROM0].size
|
sectionTypeInfo[SECTTYPE_ROM0].size
|
||||||
);
|
);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < sections[SECTTYPE_ROMX].size(); i++)
|
for (uint32_t i = 0; i < sections[SECTTYPE_ROMX].size(); i++) {
|
||||||
writeBank(
|
writeBank(
|
||||||
§ions[SECTTYPE_ROMX][i].sections,
|
§ions[SECTTYPE_ROMX][i].sections,
|
||||||
sectionTypeInfo[SECTTYPE_ROMX].startAddr,
|
sectionTypeInfo[SECTTYPE_ROMX].startAddr,
|
||||||
sectionTypeInfo[SECTTYPE_ROMX].size
|
sectionTypeInfo[SECTTYPE_ROMX].size
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,8 +296,9 @@ static void printSymName(std::string const &name, FILE *file) {
|
|||||||
codepoint = 0xFFFD;
|
codepoint = 0xFFFD;
|
||||||
// Skip continuation bytes
|
// Skip continuation bytes
|
||||||
// A NUL byte does not qualify, so we're good
|
// A NUL byte does not qualify, so we're good
|
||||||
while ((*ptr & 0xC0) == 0x80)
|
while ((*ptr & 0xC0) == 0x80) {
|
||||||
++ptr;
|
++ptr;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++ptr;
|
++ptr;
|
||||||
@@ -297,8 +312,9 @@ static void printSymName(std::string const &name, FILE *file) {
|
|||||||
// Comparator function for `std::stable_sort` to sort symbols
|
// Comparator function for `std::stable_sort` to sort symbols
|
||||||
// Symbols are ordered by address, then by parentage
|
// Symbols are ordered by address, then by parentage
|
||||||
static bool compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2) {
|
static bool compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2) {
|
||||||
if (sym1.addr != sym2.addr)
|
if (sym1.addr != sym2.addr) {
|
||||||
return sym1.addr < sym2.addr;
|
return sym1.addr < sym2.addr;
|
||||||
|
}
|
||||||
|
|
||||||
std::string const &sym1_name = sym1.sym->name;
|
std::string const &sym1_name = sym1.sym->name;
|
||||||
std::string const &sym2_name = sym2.sym->name;
|
std::string const &sym2_name = sym2.sym->name;
|
||||||
@@ -310,10 +326,12 @@ static bool compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2) {
|
|||||||
size_t sym2_len = sym2_name.length();
|
size_t sym2_len = sym2_name.length();
|
||||||
|
|
||||||
// Sort parent labels before their child local labels
|
// Sort parent labels before their child local labels
|
||||||
if (sym2_name.starts_with(sym1_name) && sym2_name[sym1_len] == '.')
|
if (sym2_name.starts_with(sym1_name) && sym2_name[sym1_len] == '.') {
|
||||||
return true;
|
return true;
|
||||||
if (sym1_name.starts_with(sym2_name) && sym1_name[sym2_len] == '.')
|
}
|
||||||
|
if (sym1_name.starts_with(sym2_name) && sym1_name[sym2_len] == '.') {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
// Sort local labels before unrelated global labels
|
// Sort local labels before unrelated global labels
|
||||||
return sym1_local;
|
return sym1_local;
|
||||||
}
|
}
|
||||||
@@ -342,8 +360,9 @@ static void writeSymBank(SortedSections const &bankSections, SectionType type, u
|
|||||||
|
|
||||||
forEachSortedSection(sect, { nbSymbols += sect->symbols.size(); });
|
forEachSortedSection(sect, { nbSymbols += sect->symbols.size(); });
|
||||||
|
|
||||||
if (!nbSymbols)
|
if (!nbSymbols) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<SortedSymbol> symList;
|
std::vector<SortedSymbol> symList;
|
||||||
|
|
||||||
@@ -352,11 +371,12 @@ static void writeSymBank(SortedSections const &bankSections, SectionType type, u
|
|||||||
forEachSortedSection(sect, {
|
forEachSortedSection(sect, {
|
||||||
for (Symbol const *sym : sect->symbols) {
|
for (Symbol const *sym : sect->symbols) {
|
||||||
// Don't output symbols that begin with an illegal character
|
// Don't output symbols that begin with an illegal character
|
||||||
if (!sym->name.empty() && canStartSymName(sym->name[0]))
|
if (!sym->name.empty() && canStartSymName(sym->name[0])) {
|
||||||
symList.push_back({
|
symList.push_back({
|
||||||
.sym = sym,
|
.sym = sym,
|
||||||
.addr = static_cast<uint16_t>(sym->label().offset + sect->org),
|
.addr = static_cast<uint16_t>(sym->label().offset + sect->org),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -443,8 +463,9 @@ static void writeMapBank(SortedSections const §List, SectionType type, uint3
|
|||||||
prevEndAddr = sect->org + sect->size;
|
prevEndAddr = sect->org + sect->size;
|
||||||
|
|
||||||
fprintf(mapFile, "\tSECTION: $%04" PRIx16, sect->org);
|
fprintf(mapFile, "\tSECTION: $%04" PRIx16, sect->org);
|
||||||
if (sect->size != 0)
|
if (sect->size != 0) {
|
||||||
fprintf(mapFile, "-$%04x", prevEndAddr - 1);
|
fprintf(mapFile, "-$%04x", prevEndAddr - 1);
|
||||||
|
}
|
||||||
fprintf(mapFile, " ($%04" PRIx16 " byte%s) [\"", sect->size, sect->size == 1 ? "" : "s");
|
fprintf(mapFile, " ($%04" PRIx16 " byte%s) [\"", sect->size, sect->size == 1 ? "" : "s");
|
||||||
printSectionName(sect->name, mapFile);
|
printSectionName(sect->name, mapFile);
|
||||||
fputs("\"]\n", mapFile);
|
fputs("\"]\n", mapFile);
|
||||||
@@ -461,10 +482,11 @@ static void writeMapBank(SortedSections const §List, SectionType type, uint3
|
|||||||
|
|
||||||
if (sect->nextu) {
|
if (sect->nextu) {
|
||||||
// Announce the following "piece"
|
// Announce the following "piece"
|
||||||
if (sect->nextu->modifier == SECTION_UNION)
|
if (sect->nextu->modifier == SECTION_UNION) {
|
||||||
fprintf(mapFile, "\t ; Next union\n");
|
fprintf(mapFile, "\t ; Next union\n");
|
||||||
else if (sect->nextu->modifier == SECTION_FRAGMENT)
|
} else if (sect->nextu->modifier == SECTION_FRAGMENT) {
|
||||||
fprintf(mapFile, "\t ; Next fragment\n");
|
fprintf(mapFile, "\t ; Next fragment\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -494,12 +516,14 @@ static void writeMapSummary() {
|
|||||||
uint32_t nbBanks = sections[type].size();
|
uint32_t nbBanks = sections[type].size();
|
||||||
|
|
||||||
// Do not output used space for VRAM or OAM
|
// Do not output used space for VRAM or OAM
|
||||||
if (type == SECTTYPE_VRAM || type == SECTTYPE_OAM)
|
if (type == SECTTYPE_VRAM || type == SECTTYPE_OAM) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Do not output unused section types
|
// Do not output unused section types
|
||||||
if (nbBanks == 0)
|
if (nbBanks == 0) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t usedTotal = 0;
|
uint32_t usedTotal = 0;
|
||||||
|
|
||||||
@@ -532,16 +556,18 @@ static void writeMapSummary() {
|
|||||||
usedTotal == 1 ? "" : "s",
|
usedTotal == 1 ? "" : "s",
|
||||||
nbBanks * sectionTypeInfo[type].size - usedTotal
|
nbBanks * sectionTypeInfo[type].size - usedTotal
|
||||||
);
|
);
|
||||||
if (sectionTypeInfo[type].firstBank != sectionTypeInfo[type].lastBank || nbBanks > 1)
|
if (sectionTypeInfo[type].firstBank != sectionTypeInfo[type].lastBank || nbBanks > 1) {
|
||||||
fprintf(mapFile, " in %u bank%s", nbBanks, nbBanks == 1 ? "" : "s");
|
fprintf(mapFile, " in %u bank%s", nbBanks, nbBanks == 1 ? "" : "s");
|
||||||
|
}
|
||||||
putc('\n', mapFile);
|
putc('\n', mapFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes the sym file, if applicable.
|
// Writes the sym file, if applicable.
|
||||||
static void writeSym() {
|
static void writeSym() {
|
||||||
if (!symFileName)
|
if (!symFileName) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(symFileName, "-")) {
|
if (strcmp(symFileName, "-")) {
|
||||||
symFile = fopen(symFileName, "w");
|
symFile = fopen(symFileName, "w");
|
||||||
@@ -550,8 +576,9 @@ static void writeSym() {
|
|||||||
(void)setmode(STDOUT_FILENO, O_TEXT); // May have been set to O_BINARY previously
|
(void)setmode(STDOUT_FILENO, O_TEXT); // May have been set to O_BINARY previously
|
||||||
symFile = stdout;
|
symFile = stdout;
|
||||||
}
|
}
|
||||||
if (!symFile)
|
if (!symFile) {
|
||||||
err("Failed to open sym file \"%s\"", symFileName);
|
err("Failed to open sym file \"%s\"", symFileName);
|
||||||
|
}
|
||||||
Defer closeSymFile{[&] { fclose(symFile); }};
|
Defer closeSymFile{[&] { fclose(symFile); }};
|
||||||
|
|
||||||
fputs("; File generated by rgblink\n", symFile);
|
fputs("; File generated by rgblink\n", symFile);
|
||||||
@@ -559,8 +586,9 @@ static void writeSym() {
|
|||||||
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
|
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
|
||||||
SectionType type = typeMap[i];
|
SectionType type = typeMap[i];
|
||||||
|
|
||||||
for (uint32_t bank = 0; bank < sections[type].size(); bank++)
|
for (uint32_t bank = 0; bank < sections[type].size(); bank++) {
|
||||||
writeSymBank(sections[type][bank], type, bank);
|
writeSymBank(sections[type][bank], type, bank);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output the exported numeric constants
|
// Output the exported numeric constants
|
||||||
@@ -568,8 +596,9 @@ static void writeSym() {
|
|||||||
constants.clear();
|
constants.clear();
|
||||||
sym_ForEach([](Symbol &sym) {
|
sym_ForEach([](Symbol &sym) {
|
||||||
// Symbols are already limited to the exported ones
|
// Symbols are already limited to the exported ones
|
||||||
if (sym.data.holds<int32_t>())
|
if (sym.data.holds<int32_t>()) {
|
||||||
constants.push_back(&sym);
|
constants.push_back(&sym);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Numeric constants are ordered by value, then by name
|
// Numeric constants are ordered by value, then by name
|
||||||
std::sort(RANGE(constants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
std::sort(RANGE(constants), [](Symbol *sym1, Symbol *sym2) -> bool {
|
||||||
@@ -587,8 +616,9 @@ static void writeSym() {
|
|||||||
|
|
||||||
// Writes the map file, if applicable.
|
// Writes the map file, if applicable.
|
||||||
static void writeMap() {
|
static void writeMap() {
|
||||||
if (!mapFileName)
|
if (!mapFileName) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(mapFileName, "-")) {
|
if (strcmp(mapFileName, "-")) {
|
||||||
mapFile = fopen(mapFileName, "w");
|
mapFile = fopen(mapFileName, "w");
|
||||||
@@ -597,8 +627,9 @@ static void writeMap() {
|
|||||||
(void)setmode(STDOUT_FILENO, O_TEXT); // May have been set to O_BINARY previously
|
(void)setmode(STDOUT_FILENO, O_TEXT); // May have been set to O_BINARY previously
|
||||||
mapFile = stdout;
|
mapFile = stdout;
|
||||||
}
|
}
|
||||||
if (!mapFile)
|
if (!mapFile) {
|
||||||
err("Failed to open map file \"%s\"", mapFileName);
|
err("Failed to open map file \"%s\"", mapFileName);
|
||||||
|
}
|
||||||
Defer closeMapFile{[&] { fclose(mapFile); }};
|
Defer closeMapFile{[&] { fclose(mapFile); }};
|
||||||
|
|
||||||
writeMapSummary();
|
writeMapSummary();
|
||||||
@@ -606,8 +637,9 @@ static void writeMap() {
|
|||||||
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
|
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
|
||||||
SectionType type = typeMap[i];
|
SectionType type = typeMap[i];
|
||||||
|
|
||||||
for (uint32_t bank = 0; bank < sections[type].size(); bank++)
|
for (uint32_t bank = 0; bank < sections[type].size(); bank++) {
|
||||||
writeMapBank(sections[type][bank], type, bank);
|
writeMapBank(sections[type][bank], type, bank);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ static void pushRPN(int32_t value, bool comesFromError) {
|
|||||||
static bool isError = false;
|
static bool isError = false;
|
||||||
|
|
||||||
static int32_t popRPN(Patch const &patch) {
|
static int32_t popRPN(Patch const &patch) {
|
||||||
if (rpnStack.empty())
|
if (rpnStack.empty()) {
|
||||||
fatal(patch.src, patch.lineNo, "Internal error, RPN stack empty");
|
fatal(patch.src, patch.lineNo, "Internal error, RPN stack empty");
|
||||||
|
}
|
||||||
|
|
||||||
RPNStackEntry entry = rpnStack.front();
|
RPNStackEntry entry = rpnStack.front();
|
||||||
|
|
||||||
@@ -46,8 +47,9 @@ static int32_t popRPN(Patch const &patch) {
|
|||||||
// RPN operators
|
// RPN operators
|
||||||
|
|
||||||
static uint32_t getRPNByte(uint8_t const *&expression, int32_t &size, Patch const &patch) {
|
static uint32_t getRPNByte(uint8_t const *&expression, int32_t &size, Patch const &patch) {
|
||||||
if (!size--)
|
if (!size--) {
|
||||||
fatal(patch.src, patch.lineNo, "Internal error, RPN expression overread");
|
fatal(patch.src, patch.lineNo, "Internal error, RPN expression overread");
|
||||||
|
}
|
||||||
|
|
||||||
return *expression++;
|
return *expression++;
|
||||||
}
|
}
|
||||||
@@ -57,8 +59,9 @@ static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t i
|
|||||||
Symbol const &symbol = symbolList[index];
|
Symbol const &symbol = symbolList[index];
|
||||||
|
|
||||||
// If the symbol is defined elsewhere...
|
// If the symbol is defined elsewhere...
|
||||||
if (symbol.type == SYMTYPE_IMPORT)
|
if (symbol.type == SYMTYPE_IMPORT) {
|
||||||
return sym_GetSymbol(symbol.name);
|
return sym_GetSymbol(symbol.name);
|
||||||
|
}
|
||||||
|
|
||||||
return &symbol;
|
return &symbol;
|
||||||
}
|
}
|
||||||
@@ -218,8 +221,9 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
|
|
||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
value = 0;
|
value = 0;
|
||||||
for (uint8_t shift = 0; shift < 32; shift += 8)
|
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
||||||
value |= getRPNByte(expression, size, patch) << shift;
|
value |= getRPNByte(expression, size, patch) << shift;
|
||||||
|
}
|
||||||
|
|
||||||
if (Symbol const *symbol = getSymbol(fileSymbols, value); !symbol) {
|
if (Symbol const *symbol = getSymbol(fileSymbols, value); !symbol) {
|
||||||
error(
|
error(
|
||||||
@@ -377,14 +381,16 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
|
|
||||||
case RPN_CONST:
|
case RPN_CONST:
|
||||||
value = 0;
|
value = 0;
|
||||||
for (uint8_t shift = 0; shift < 32; shift += 8)
|
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
||||||
value |= getRPNByte(expression, size, patch) << shift;
|
value |= getRPNByte(expression, size, patch) << shift;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_SYM:
|
case RPN_SYM:
|
||||||
value = 0;
|
value = 0;
|
||||||
for (uint8_t shift = 0; shift < 32; shift += 8)
|
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
||||||
value |= getRPNByte(expression, size, patch) << shift;
|
value |= getRPNByte(expression, size, patch) << shift;
|
||||||
|
}
|
||||||
|
|
||||||
if (value == -1) { // PC
|
if (value == -1) { // PC
|
||||||
if (!patch.pcSection) {
|
if (!patch.pcSection) {
|
||||||
@@ -417,8 +423,9 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
pushRPN(value, isError);
|
pushRPN(value, isError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rpnStack.size() > 1)
|
if (rpnStack.size() > 1) {
|
||||||
error(patch.src, patch.lineNo, "RPN stack has %zu entries on exit, not 1", rpnStack.size());
|
error(patch.src, patch.lineNo, "RPN stack has %zu entries on exit, not 1", rpnStack.size());
|
||||||
|
}
|
||||||
|
|
||||||
isError = false;
|
isError = false;
|
||||||
return popRPN(patch);
|
return popRPN(patch);
|
||||||
@@ -505,7 +512,7 @@ static void applyFilePatches(Section §ion, Section &dataSection) {
|
|||||||
uint16_t address = patch.pcSection->org + patch.pcOffset + 2;
|
uint16_t address = patch.pcSection->org + patch.pcOffset + 2;
|
||||||
int16_t jumpOffset = value - address;
|
int16_t jumpOffset = value - address;
|
||||||
|
|
||||||
if (!isError && (jumpOffset < -128 || jumpOffset > 127))
|
if (!isError && (jumpOffset < -128 || jumpOffset > 127)) {
|
||||||
error(
|
error(
|
||||||
patch.src,
|
patch.src,
|
||||||
patch.lineNo,
|
patch.lineNo,
|
||||||
@@ -513,10 +520,11 @@ static void applyFilePatches(Section §ion, Section &dataSection) {
|
|||||||
"; use jp instead\n",
|
"; use jp instead\n",
|
||||||
jumpOffset
|
jumpOffset
|
||||||
);
|
);
|
||||||
|
}
|
||||||
dataSection.data[offset] = jumpOffset & 0xFF;
|
dataSection.data[offset] = jumpOffset & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
// Patch a certain number of bytes
|
// Patch a certain number of bytes
|
||||||
if (!isError && (value < type.min || value > type.max))
|
if (!isError && (value < type.min || value > type.max)) {
|
||||||
error(
|
error(
|
||||||
patch.src,
|
patch.src,
|
||||||
patch.lineNo,
|
patch.lineNo,
|
||||||
@@ -525,6 +533,7 @@ static void applyFilePatches(Section §ion, Section &dataSection) {
|
|||||||
value < 0 ? " (maybe negative?)" : "",
|
value < 0 ? " (maybe negative?)" : "",
|
||||||
type.size * 8U
|
type.size * 8U
|
||||||
);
|
);
|
||||||
|
}
|
||||||
for (uint8_t i = 0; i < type.size; i++) {
|
for (uint8_t i = 0; i < type.size; i++) {
|
||||||
dataSection.data[offset + i] = value & 0xFF;
|
dataSection.data[offset + i] = value & 0xFF;
|
||||||
value >>= 8;
|
value >>= 8;
|
||||||
@@ -536,11 +545,13 @@ static void applyFilePatches(Section §ion, Section &dataSection) {
|
|||||||
// Applies all of a section's patches, iterating over "components" of unionized sections
|
// Applies all of a section's patches, iterating over "components" of unionized sections
|
||||||
// @param section The section to patch
|
// @param section The section to patch
|
||||||
static void applyPatches(Section §ion) {
|
static void applyPatches(Section §ion) {
|
||||||
if (!sect_HasData(section.type))
|
if (!sect_HasData(section.type)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (Section *component = §ion; component; component = component->nextu.get())
|
for (Section *component = §ion; component; component = component->nextu.get()) {
|
||||||
applyFilePatches(*component, section);
|
applyFilePatches(*component, section);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void patch_ApplyPatches() {
|
void patch_ApplyPatches() {
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ enum NumberType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void consumeLF(FileStackNode const &where, uint32_t lineNo, FILE *file) {
|
static void consumeLF(FileStackNode const &where, uint32_t lineNo, FILE *file) {
|
||||||
if (getc(file) != '\n')
|
if (getc(file) != '\n') {
|
||||||
fatal(&where, lineNo, "Bad line ending (CR without LF)");
|
fatal(&where, lineNo, "Bad line ending (CR without LF)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static char const *delim = " \f\n\r\t\v"; // Whitespace according to the C and POSIX locales
|
static char const *delim = " \f\n\r\t\v"; // Whitespace according to the C and POSIX locales
|
||||||
@@ -49,8 +50,9 @@ retry:
|
|||||||
} while (firstChar != EOF && firstChar != '\r' && firstChar != '\n');
|
} while (firstChar != EOF && firstChar != '\r' && firstChar != '\n');
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case '\r':
|
case '\r':
|
||||||
if (firstChar == '\r' && getc(file) != '\n')
|
if (firstChar == '\r' && getc(file) != '\n') {
|
||||||
consumeLF(where, lineNo, file);
|
consumeLF(where, lineNo, file);
|
||||||
|
}
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case '\n':
|
case '\n':
|
||||||
goto retry;
|
goto retry;
|
||||||
@@ -90,14 +92,16 @@ static uint32_t readNumber(char const *str, char const *&endptr, NumberType base
|
|||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
parseNumber(FileStackNode const &where, uint32_t lineNo, char const *str, NumberType base) {
|
parseNumber(FileStackNode const &where, uint32_t lineNo, char const *str, NumberType base) {
|
||||||
if (str[0] == '\0')
|
if (str[0] == '\0') {
|
||||||
fatal(&where, lineNo, "Expected number, got empty string");
|
fatal(&where, lineNo, "Expected number, got empty string");
|
||||||
|
}
|
||||||
|
|
||||||
char const *endptr;
|
char const *endptr;
|
||||||
uint32_t res = readNumber(str, endptr, base);
|
uint32_t res = readNumber(str, endptr, base);
|
||||||
|
|
||||||
if (*endptr != '\0')
|
if (*endptr != '\0') {
|
||||||
fatal(&where, lineNo, "Expected number, got \"%s\"", str);
|
fatal(&where, lineNo, "Expected number, got \"%s\"", str);
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +109,9 @@ static uint8_t
|
|||||||
parseByte(FileStackNode const &where, uint32_t lineNo, char const *str, NumberType base) {
|
parseByte(FileStackNode const &where, uint32_t lineNo, char const *str, NumberType base) {
|
||||||
uint32_t num = parseNumber(where, lineNo, str, base);
|
uint32_t num = parseNumber(where, lineNo, str, base);
|
||||||
|
|
||||||
if (num > UINT8_MAX)
|
if (num > UINT8_MAX) {
|
||||||
fatal(&where, lineNo, "\"%s\" is not a byte", str);
|
fatal(&where, lineNo, "\"%s\" is not a byte", str);
|
||||||
|
}
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,17 +208,20 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
|
|
||||||
static constexpr uint8_t ADDR_SIZE = 3;
|
static constexpr uint8_t ADDR_SIZE = 3;
|
||||||
|
|
||||||
if (line[1] != '0' + ADDR_SIZE)
|
if (line[1] != '0' + ADDR_SIZE) {
|
||||||
fatal(&where, lineNo, "Unknown or unsupported address size '%c'", line[1]);
|
fatal(&where, lineNo, "Unknown or unsupported address size '%c'", line[1]);
|
||||||
|
}
|
||||||
|
|
||||||
if (line[2] != '\0')
|
if (line[2] != '\0') {
|
||||||
warning(&where, lineNo, "Ignoring unknown characters (\"%s\") in first line", &line[2]);
|
warning(&where, lineNo, "Ignoring unknown characters (\"%s\") in first line", &line[2]);
|
||||||
|
}
|
||||||
|
|
||||||
// Header line
|
// Header line
|
||||||
|
|
||||||
lineType = nextLine(line, lineNo, where, file);
|
lineType = nextLine(line, lineNo, where, file);
|
||||||
if (lineType != 'H')
|
if (lineType != 'H') {
|
||||||
fatal(&where, lineNo, "Expected header line, got '%c' line", lineType);
|
fatal(&where, lineNo, "Expected header line, got '%c' line", lineType);
|
||||||
|
}
|
||||||
// Expected format: "A areas S global symbols"
|
// Expected format: "A areas S global symbols"
|
||||||
|
|
||||||
getToken(line.data(), "Empty 'H' line");
|
getToken(line.data(), "Empty 'H' line");
|
||||||
@@ -242,8 +250,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
lineType = nextLine(line, lineNo, where, file);
|
lineType = nextLine(line, lineNo, where, file);
|
||||||
if (lineType == EOF)
|
if (lineType == EOF) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
switch (lineType) {
|
switch (lineType) {
|
||||||
case 'M': // Module name
|
case 'M': // Module name
|
||||||
case 'O': // Assembler flags
|
case 'O': // Assembler flags
|
||||||
@@ -251,10 +260,11 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'A': {
|
case 'A': {
|
||||||
if (fileSections.size() == expectedNbAreas)
|
if (fileSections.size() == expectedNbAreas) {
|
||||||
warning(
|
warning(
|
||||||
&where, lineNo, "Got more 'A' lines than the expected %" PRIu32, expectedNbAreas
|
&where, lineNo, "Got more 'A' lines than the expected %" PRIu32, expectedNbAreas
|
||||||
);
|
);
|
||||||
|
}
|
||||||
std::unique_ptr<Section> curSection = std::make_unique<Section>();
|
std::unique_ptr<Section> curSection = std::make_unique<Section>();
|
||||||
|
|
||||||
curSection->src = &where;
|
curSection->src = &where;
|
||||||
@@ -264,8 +274,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
assume(strlen(token) != 0); // This should be impossible, tokens are non-empty
|
assume(strlen(token) != 0); // This should be impossible, tokens are non-empty
|
||||||
// The following is required for fragment offsets to be reliably predicted
|
// The following is required for fragment offsets to be reliably predicted
|
||||||
for (FileSection &entry : fileSections) {
|
for (FileSection &entry : fileSections) {
|
||||||
if (!strcmp(token, entry.section->name.c_str()))
|
if (!strcmp(token, entry.section->name.c_str())) {
|
||||||
fatal(&where, lineNo, "Area \"%s\" already defined earlier", token);
|
fatal(&where, lineNo, "Area \"%s\" already defined earlier", token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
char const *sectName = token; // We'll deal with the section's name depending on type
|
char const *sectName = token; // We'll deal with the section's name depending on type
|
||||||
|
|
||||||
@@ -275,21 +286,23 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
|
|
||||||
uint32_t tmp = parseNumber(where, lineNo, token, numberType);
|
uint32_t tmp = parseNumber(where, lineNo, token, numberType);
|
||||||
|
|
||||||
if (tmp > UINT16_MAX)
|
if (tmp > UINT16_MAX) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
"Area \"%s\" is larger than the GB address space!?",
|
"Area \"%s\" is larger than the GB address space!?",
|
||||||
curSection->name.c_str()
|
curSection->name.c_str()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
curSection->size = tmp;
|
curSection->size = tmp;
|
||||||
|
|
||||||
expectToken("flags", 'A');
|
expectToken("flags", 'A');
|
||||||
|
|
||||||
getToken(nullptr, "'A' line is too short");
|
getToken(nullptr, "'A' line is too short");
|
||||||
tmp = parseNumber(where, lineNo, token, numberType);
|
tmp = parseNumber(where, lineNo, token, numberType);
|
||||||
if (tmp & (1 << AREA_PAGING))
|
if (tmp & (1 << AREA_PAGING)) {
|
||||||
fatal(&where, lineNo, "Internal error: paging is not supported");
|
fatal(&where, lineNo, "Internal error: paging is not supported");
|
||||||
|
}
|
||||||
curSection->isAddressFixed = tmp & (1 << AREA_ISABS);
|
curSection->isAddressFixed = tmp & (1 << AREA_ISABS);
|
||||||
curSection->isBankFixed = curSection->isAddressFixed;
|
curSection->isBankFixed = curSection->isAddressFixed;
|
||||||
curSection->modifier = curSection->isAddressFixed || (tmp & (1 << AREA_TYPE))
|
curSection->modifier = curSection->isAddressFixed || (tmp & (1 << AREA_TYPE))
|
||||||
@@ -430,11 +443,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
// It's fine to keep modifying the symbol after `AddSymbol`, only
|
// It's fine to keep modifying the symbol after `AddSymbol`, only
|
||||||
// the name must not be modified
|
// the name must not be modified
|
||||||
}
|
}
|
||||||
if (strncasecmp(&token[1], "ef", 2) != 0)
|
if (strncasecmp(&token[1], "ef", 2) != 0) {
|
||||||
fatal(&where, lineNo, "'S' line is neither \"Def\" nor \"Ref\"");
|
fatal(&where, lineNo, "'S' line is neither \"Def\" nor \"Ref\"");
|
||||||
|
}
|
||||||
|
|
||||||
if (!fileSections.empty())
|
if (!fileSections.empty()) {
|
||||||
fileSections.back().section->symbols.push_back(&symbol);
|
fileSections.back().section->symbols.push_back(&symbol);
|
||||||
|
}
|
||||||
|
|
||||||
expectEol("'S' line is too long");
|
expectEol("'S' line is too long");
|
||||||
break;
|
break;
|
||||||
@@ -442,15 +457,18 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
|
|
||||||
case 'T':
|
case 'T':
|
||||||
// Now, time to parse the data!
|
// Now, time to parse the data!
|
||||||
if (!data.empty())
|
if (!data.empty()) {
|
||||||
warning(&where, lineNo, "Previous 'T' line had no 'R' line (ignored)");
|
warning(&where, lineNo, "Previous 'T' line had no 'R' line (ignored)");
|
||||||
|
}
|
||||||
|
|
||||||
data.clear();
|
data.clear();
|
||||||
for (token = strtok(line.data(), delim); token; token = strtok(nullptr, delim))
|
for (token = strtok(line.data(), delim); token; token = strtok(nullptr, delim)) {
|
||||||
data.push_back(parseByte(where, lineNo, token, numberType));
|
data.push_back(parseByte(where, lineNo, token, numberType));
|
||||||
|
}
|
||||||
|
|
||||||
if (data.size() < ADDR_SIZE)
|
if (data.size() < ADDR_SIZE) {
|
||||||
fatal(&where, lineNo, "'T' line is too short");
|
fatal(&where, lineNo, "'T' line is too short");
|
||||||
|
}
|
||||||
// Importantly, now we know that there is "pending data" in `data`
|
// Importantly, now we know that there is "pending data" in `data`
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -470,7 +488,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
areaIdx = parseByte(where, lineNo, token, numberType);
|
areaIdx = parseByte(where, lineNo, token, numberType);
|
||||||
getToken(nullptr, "'R' line is too short");
|
getToken(nullptr, "'R' line is too short");
|
||||||
areaIdx |= static_cast<uint16_t>(parseByte(where, lineNo, token, numberType)) << 8;
|
areaIdx |= static_cast<uint16_t>(parseByte(where, lineNo, token, numberType)) << 8;
|
||||||
if (areaIdx >= fileSections.size())
|
if (areaIdx >= fileSections.size()) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -478,6 +496,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
areaIdx,
|
areaIdx,
|
||||||
fileSections.size()
|
fileSections.size()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
assume(!fileSections.empty()); // There should be at least one, from the above check
|
assume(!fileSections.empty()); // There should be at least one, from the above check
|
||||||
Section *section = fileSections[areaIdx].section.get();
|
Section *section = fileSections[areaIdx].section.get();
|
||||||
uint16_t *writeIndex = &fileSections[areaIdx].writeIndex;
|
uint16_t *writeIndex = &fileSections[areaIdx].writeIndex;
|
||||||
@@ -485,7 +504,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
uint16_t addr = data[0] | data[1] << 8;
|
uint16_t addr = data[0] | data[1] << 8;
|
||||||
|
|
||||||
if (section->isAddressFixed) {
|
if (section->isAddressFixed) {
|
||||||
if (addr < section->org)
|
if (addr < section->org) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -495,12 +514,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
section->name.c_str(),
|
section->name.c_str(),
|
||||||
section->org
|
section->org
|
||||||
);
|
);
|
||||||
|
}
|
||||||
addr -= section->org;
|
addr -= section->org;
|
||||||
}
|
}
|
||||||
// Lines are emitted that violate this check but contain no "payload";
|
// Lines are emitted that violate this check but contain no "payload";
|
||||||
// ignore those. "Empty" lines shouldn't trigger allocation, either.
|
// ignore those. "Empty" lines shouldn't trigger allocation, either.
|
||||||
if (data.size() != ADDR_SIZE) {
|
if (data.size() != ADDR_SIZE) {
|
||||||
if (addr != *writeIndex)
|
if (addr != *writeIndex) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -509,6 +529,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
addr,
|
addr,
|
||||||
*writeIndex
|
*writeIndex
|
||||||
);
|
);
|
||||||
|
}
|
||||||
if (section->data.empty()) {
|
if (section->data.empty()) {
|
||||||
assume(section->size != 0);
|
assume(section->size != 0);
|
||||||
section->data.resize(section->size);
|
section->data.resize(section->size);
|
||||||
@@ -541,7 +562,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
getToken(nullptr, "Incomplete relocation");
|
getToken(nullptr, "Incomplete relocation");
|
||||||
uint8_t offset = parseByte(where, lineNo, token, numberType);
|
uint8_t offset = parseByte(where, lineNo, token, numberType);
|
||||||
|
|
||||||
if (offset < ADDR_SIZE)
|
if (offset < ADDR_SIZE) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -549,7 +570,8 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
offset,
|
offset,
|
||||||
ADDR_SIZE
|
ADDR_SIZE
|
||||||
);
|
);
|
||||||
if (offset >= data.size())
|
}
|
||||||
|
if (offset >= data.size()) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -557,6 +579,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
offset,
|
offset,
|
||||||
data.size()
|
data.size()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getToken(nullptr, "Incomplete relocation");
|
getToken(nullptr, "Incomplete relocation");
|
||||||
uint16_t idx = parseByte(where, lineNo, token, numberType);
|
uint16_t idx = parseByte(where, lineNo, token, numberType);
|
||||||
@@ -565,10 +588,12 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
idx |= static_cast<uint16_t>(parseByte(where, lineNo, token, numberType));
|
idx |= static_cast<uint16_t>(parseByte(where, lineNo, token, numberType));
|
||||||
|
|
||||||
// Loudly fail on unknown flags
|
// Loudly fail on unknown flags
|
||||||
if (flags & (1 << RELOC_ZPAGE | 1 << RELOC_NPAGE))
|
if (flags & (1 << RELOC_ZPAGE | 1 << RELOC_NPAGE)) {
|
||||||
fatal(&where, lineNo, "Paging flags are not supported");
|
fatal(&where, lineNo, "Paging flags are not supported");
|
||||||
if (flags & ~RELOC_ALL_FLAGS)
|
}
|
||||||
|
if (flags & ~RELOC_ALL_FLAGS) {
|
||||||
warning(&where, lineNo, "Unknown reloc flags 0x%x", flags & ~RELOC_ALL_FLAGS);
|
warning(&where, lineNo, "Unknown reloc flags 0x%x", flags & ~RELOC_ALL_FLAGS);
|
||||||
|
}
|
||||||
|
|
||||||
// Turn this into a Patch
|
// Turn this into a Patch
|
||||||
Patch &patch = section->patches.emplace_back();
|
Patch &patch = section->patches.emplace_back();
|
||||||
@@ -578,7 +603,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
patch.offset = offset - writtenOfs + *writeIndex;
|
patch.offset = offset - writtenOfs + *writeIndex;
|
||||||
if (section->patches.size() > 1) {
|
if (section->patches.size() > 1) {
|
||||||
uint32_t prevOffset = section->patches[section->patches.size() - 2].offset;
|
uint32_t prevOffset = section->patches[section->patches.size() - 2].offset;
|
||||||
if (prevOffset >= patch.offset)
|
if (prevOffset >= patch.offset) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -587,6 +612,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
prevOffset,
|
prevOffset,
|
||||||
patch.offset
|
patch.offset
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
patch.pcSection = section; // No need to fill `pcSectionID`, then
|
patch.pcSection = section; // No need to fill `pcSectionID`, then
|
||||||
patch.pcOffset = patch.offset - 1; // For `jr`s
|
patch.pcOffset = patch.offset - 1; // For `jr`s
|
||||||
@@ -596,7 +622,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
uint32_t baseValue = 0;
|
uint32_t baseValue = 0;
|
||||||
|
|
||||||
assume(offset < data.size());
|
assume(offset < data.size());
|
||||||
if (data.size() - offset < nbBaseBytes)
|
if (data.size() - offset < nbBaseBytes) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -604,13 +630,15 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
nbBaseBytes,
|
nbBaseBytes,
|
||||||
data.size() - offset
|
data.size() - offset
|
||||||
);
|
);
|
||||||
for (uint8_t i = 0; i < nbBaseBytes; ++i)
|
}
|
||||||
|
for (uint8_t i = 0; i < nbBaseBytes; ++i) {
|
||||||
baseValue = baseValue | data[offset + i] << (8 * i);
|
baseValue = baseValue | data[offset + i] << (8 * i);
|
||||||
|
}
|
||||||
|
|
||||||
// Bit 4 specifies signedness, but I don't think that matters?
|
// Bit 4 specifies signedness, but I don't think that matters?
|
||||||
// Generate a RPN expression from the info and flags
|
// Generate a RPN expression from the info and flags
|
||||||
if (flags & 1 << RELOC_ISSYM) {
|
if (flags & 1 << RELOC_ISSYM) {
|
||||||
if (idx >= fileSymbols.size())
|
if (idx >= fileSymbols.size()) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -618,6 +646,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
idx,
|
idx,
|
||||||
fileSymbols.size()
|
fileSymbols.size()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
Symbol const &sym = fileSymbols[idx];
|
Symbol const &sym = fileSymbols[idx];
|
||||||
|
|
||||||
// SDCC has a bunch of "magic symbols" that start with a
|
// SDCC has a bunch of "magic symbols" that start with a
|
||||||
@@ -627,10 +656,11 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
// Look for the symbol being referenced, and use its index instead
|
// Look for the symbol being referenced, and use its index instead
|
||||||
for (idx = 0; idx < fileSymbols.size(); ++idx) {
|
for (idx = 0; idx < fileSymbols.size(); ++idx) {
|
||||||
if (sym.name.ends_with(fileSymbols[idx].name)
|
if (sym.name.ends_with(fileSymbols[idx].name)
|
||||||
&& 1 + sym.name.length() == fileSymbols[idx].name.length())
|
&& 1 + sym.name.length() == fileSymbols[idx].name.length()) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (idx == fileSymbols.size())
|
if (idx == fileSymbols.size()) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -638,6 +668,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
sym.name.c_str(),
|
sym.name.c_str(),
|
||||||
&sym.name.c_str()[1]
|
&sym.name.c_str()[1]
|
||||||
);
|
);
|
||||||
|
}
|
||||||
patch.rpnExpression.resize(5);
|
patch.rpnExpression.resize(5);
|
||||||
patch.rpnExpression[0] = RPN_BANK_SYM;
|
patch.rpnExpression[0] = RPN_BANK_SYM;
|
||||||
patch.rpnExpression[1] = idx;
|
patch.rpnExpression[1] = idx;
|
||||||
@@ -669,7 +700,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
patch.rpnExpression[4] = idx >> 24;
|
patch.rpnExpression[4] = idx >> 24;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (idx >= fileSections.size())
|
if (idx >= fileSections.size()) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -677,6 +708,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
idx,
|
idx,
|
||||||
fileSections.size()
|
fileSections.size()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// It gets funky. If the area is absolute, *actually*, we
|
// It gets funky. If the area is absolute, *actually*, we
|
||||||
// must not add its base address, as the assembler will
|
// must not add its base address, as the assembler will
|
||||||
// already have added it in `baseValue`.
|
// already have added it in `baseValue`.
|
||||||
@@ -684,8 +716,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
// address from `baseValue`, undoing what the assembler did;
|
// address from `baseValue`, undoing what the assembler did;
|
||||||
// this allows the relocation to still be correct, even if
|
// this allows the relocation to still be correct, even if
|
||||||
// the section gets moved for any reason.
|
// the section gets moved for any reason.
|
||||||
if (fileSections[idx].section->isAddressFixed)
|
if (fileSections[idx].section->isAddressFixed) {
|
||||||
baseValue -= fileSections[idx].section->org;
|
baseValue -= fileSections[idx].section->org;
|
||||||
|
}
|
||||||
std::string const &name = fileSections[idx].section->name;
|
std::string const &name = fileSections[idx].section->name;
|
||||||
Section const *other = sect_GetSection(name);
|
Section const *other = sect_GetSection(name);
|
||||||
|
|
||||||
@@ -697,8 +730,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
// so this fragment will be appended to the existing section
|
// so this fragment will be appended to the existing section
|
||||||
// *if any*, and thus its offset will be the section's
|
// *if any*, and thus its offset will be the section's
|
||||||
// current size.
|
// current size.
|
||||||
if (other)
|
if (other) {
|
||||||
baseValue += other->size;
|
baseValue += other->size;
|
||||||
|
}
|
||||||
patch.rpnExpression.resize(1 + name.length() + 1);
|
patch.rpnExpression.resize(1 + name.length() + 1);
|
||||||
patch.rpnExpression[0] = RPN_STARTOF_SECT;
|
patch.rpnExpression[0] = RPN_STARTOF_SECT;
|
||||||
// The cast is fine, it's just different signedness
|
// The cast is fine, it's just different signedness
|
||||||
@@ -720,7 +754,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
// Despite the flag's name, as soon as it is set, 3 bytes
|
// Despite the flag's name, as soon as it is set, 3 bytes
|
||||||
// are present, so we must skip two of them
|
// are present, so we must skip two of them
|
||||||
if (flags & 1 << RELOC_EXPR16) {
|
if (flags & 1 << RELOC_EXPR16) {
|
||||||
if (*writeIndex + (offset - writtenOfs) > section->size)
|
if (*writeIndex + (offset - writtenOfs) > section->size) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -729,6 +763,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
*writeIndex + (offset - writtenOfs),
|
*writeIndex + (offset - writtenOfs),
|
||||||
section->size
|
section->size
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Copy all bytes up to those (plus the byte that we'll overwrite)
|
// Copy all bytes up to those (plus the byte that we'll overwrite)
|
||||||
memcpy(
|
memcpy(
|
||||||
§ion->data[*writeIndex], &data[writtenOfs], offset - writtenOfs + 1
|
§ion->data[*writeIndex], &data[writtenOfs], offset - writtenOfs + 1
|
||||||
@@ -785,7 +820,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
// If there is some data left to append, do so
|
// If there is some data left to append, do so
|
||||||
if (writtenOfs != data.size()) {
|
if (writtenOfs != data.size()) {
|
||||||
assume(data.size() > writtenOfs);
|
assume(data.size() > writtenOfs);
|
||||||
if (*writeIndex + (data.size() - writtenOfs) > section->size)
|
if (*writeIndex + (data.size() - writtenOfs) > section->size) {
|
||||||
fatal(
|
fatal(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -794,6 +829,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
*writeIndex + (data.size() - writtenOfs),
|
*writeIndex + (data.size() - writtenOfs),
|
||||||
section->size
|
section->size
|
||||||
);
|
);
|
||||||
|
}
|
||||||
memcpy(§ion->data[*writeIndex], &data[writtenOfs], data.size() - writtenOfs);
|
memcpy(§ion->data[*writeIndex], &data[writtenOfs], data.size() - writtenOfs);
|
||||||
*writeIndex += data.size() - writtenOfs;
|
*writeIndex += data.size() - writtenOfs;
|
||||||
}
|
}
|
||||||
@@ -813,9 +849,10 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
#undef expectToken
|
#undef expectToken
|
||||||
#undef getToken
|
#undef getToken
|
||||||
|
|
||||||
if (!data.empty())
|
if (!data.empty()) {
|
||||||
warning(&where, lineNo, "Last 'T' line had no 'R' line (ignored)");
|
warning(&where, lineNo, "Last 'T' line had no 'R' line (ignored)");
|
||||||
if (fileSections.size() < expectedNbAreas)
|
}
|
||||||
|
if (fileSections.size() < expectedNbAreas) {
|
||||||
warning(
|
warning(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -823,7 +860,8 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
expectedNbAreas,
|
expectedNbAreas,
|
||||||
fileSections.size()
|
fileSections.size()
|
||||||
);
|
);
|
||||||
if (fileSymbols.size() < expectedNbSymbols)
|
}
|
||||||
|
if (fileSymbols.size() < expectedNbSymbols) {
|
||||||
warning(
|
warning(
|
||||||
&where,
|
&where,
|
||||||
lineNo,
|
lineNo,
|
||||||
@@ -831,6 +869,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
|||||||
expectedNbSymbols,
|
expectedNbSymbols,
|
||||||
fileSymbols.size()
|
fileSymbols.size()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
nbSectionsToAssign += fileSections.size();
|
nbSectionsToAssign += fileSections.size();
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ std::vector<std::unique_ptr<Section>> sectionList;
|
|||||||
std::unordered_map<std::string, size_t> sectionMap; // Indexes into `sectionList`
|
std::unordered_map<std::string, size_t> sectionMap; // Indexes into `sectionList`
|
||||||
|
|
||||||
void sect_ForEach(void (*callback)(Section &)) {
|
void sect_ForEach(void (*callback)(Section &)) {
|
||||||
for (auto &ptr : sectionList)
|
for (auto &ptr : sectionList) {
|
||||||
callback(*ptr);
|
callback(*ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkAgainstFixedAddress(Section const &target, Section const &other, uint16_t org) {
|
static void checkAgainstFixedAddress(Section const &target, Section const &other, uint16_t org) {
|
||||||
@@ -117,8 +118,9 @@ static void checkFragmentCompat(Section &target, Section &other) {
|
|||||||
target.org = org;
|
target.org = org;
|
||||||
} else if (other.isAlignFixed) {
|
} else if (other.isAlignFixed) {
|
||||||
int32_t ofs = (other.alignOfs - target.size) % (other.alignMask + 1);
|
int32_t ofs = (other.alignOfs - target.size) % (other.alignMask + 1);
|
||||||
if (ofs < 0)
|
if (ofs < 0) {
|
||||||
ofs += other.alignMask + 1;
|
ofs += other.alignMask + 1;
|
||||||
|
}
|
||||||
if (checkAgainstFixedAlign(target, other, ofs)) {
|
if (checkAgainstFixedAlign(target, other, ofs)) {
|
||||||
target.isAlignFixed = true;
|
target.isAlignFixed = true;
|
||||||
target.alignMask = other.alignMask;
|
target.alignMask = other.alignMask;
|
||||||
@@ -183,8 +185,9 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
|
|||||||
switch (other->modifier) {
|
switch (other->modifier) {
|
||||||
case SECTION_UNION:
|
case SECTION_UNION:
|
||||||
checkSectUnionCompat(target, *other);
|
checkSectUnionCompat(target, *other);
|
||||||
if (other->size > target.size)
|
if (other->size > target.size) {
|
||||||
target.size = other->size;
|
target.size = other->size;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SECTION_FRAGMENT:
|
case SECTION_FRAGMENT:
|
||||||
@@ -196,8 +199,9 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
|
|||||||
if (!other->data.empty()) {
|
if (!other->data.empty()) {
|
||||||
target.data.insert(target.data.end(), RANGE(other->data));
|
target.data.insert(target.data.end(), RANGE(other->data));
|
||||||
// Adjust patches' PC offsets
|
// Adjust patches' PC offsets
|
||||||
for (Patch &patch : other->patches)
|
for (Patch &patch : other->patches) {
|
||||||
patch.pcOffset += other->offset;
|
patch.pcOffset += other->offset;
|
||||||
|
}
|
||||||
} else if (!target.data.empty()) {
|
} else if (!target.data.empty()) {
|
||||||
assume(other->size == 0);
|
assume(other->size == 0);
|
||||||
}
|
}
|
||||||
@@ -250,37 +254,41 @@ static void doSanityChecks(Section §ion) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is32kMode && section.type == SECTTYPE_ROMX) {
|
if (is32kMode && section.type == SECTTYPE_ROMX) {
|
||||||
if (section.isBankFixed && section.bank != 1)
|
if (section.isBankFixed && section.bank != 1) {
|
||||||
error(
|
error(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
"%s: ROMX sections must be in bank 1 (if any) with option -t",
|
"%s: ROMX sections must be in bank 1 (if any) with option -t",
|
||||||
section.name.c_str()
|
section.name.c_str()
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
section.type = SECTTYPE_ROM0;
|
section.type = SECTTYPE_ROM0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isWRAM0Mode && section.type == SECTTYPE_WRAMX) {
|
if (isWRAM0Mode && section.type == SECTTYPE_WRAMX) {
|
||||||
if (section.isBankFixed && section.bank != 1)
|
if (section.isBankFixed && section.bank != 1) {
|
||||||
error(
|
error(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
"%s: WRAMX sections must be in bank 1 with options -w or -d",
|
"%s: WRAMX sections must be in bank 1 with options -w or -d",
|
||||||
section.name.c_str()
|
section.name.c_str()
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
section.type = SECTTYPE_WRAM0;
|
section.type = SECTTYPE_WRAM0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isDmgMode && section.type == SECTTYPE_VRAM && section.bank == 1)
|
if (isDmgMode && section.type == SECTTYPE_VRAM && section.bank == 1) {
|
||||||
error(nullptr, 0, "%s: VRAM bank 1 can't be used with option -d", section.name.c_str());
|
error(nullptr, 0, "%s: VRAM bank 1 can't be used with option -d", section.name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// Check if alignment is reasonable, this is important to avoid UB
|
// Check if alignment is reasonable, this is important to avoid UB
|
||||||
// An alignment of zero is equivalent to no alignment, basically
|
// An alignment of zero is equivalent to no alignment, basically
|
||||||
if (section.isAlignFixed && section.alignMask == 0)
|
if (section.isAlignFixed && section.alignMask == 0) {
|
||||||
section.isAlignFixed = false;
|
section.isAlignFixed = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Too large an alignment may not be satisfiable
|
// Too large an alignment may not be satisfiable
|
||||||
if (section.isAlignFixed && (section.alignMask & sectionTypeInfo[section.type].startAddr))
|
if (section.isAlignFixed && (section.alignMask & sectionTypeInfo[section.type].startAddr)) {
|
||||||
error(
|
error(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
@@ -289,11 +297,12 @@ static void doSanityChecks(Section §ion) {
|
|||||||
sectionTypeInfo[section.type].name.c_str(),
|
sectionTypeInfo[section.type].name.c_str(),
|
||||||
section.alignMask + 1
|
section.alignMask + 1
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t minbank = sectionTypeInfo[section.type].firstBank,
|
uint32_t minbank = sectionTypeInfo[section.type].firstBank,
|
||||||
maxbank = sectionTypeInfo[section.type].lastBank;
|
maxbank = sectionTypeInfo[section.type].lastBank;
|
||||||
|
|
||||||
if (section.isBankFixed && section.bank < minbank && section.bank > maxbank)
|
if (section.isBankFixed && section.bank < minbank && section.bank > maxbank) {
|
||||||
error(
|
error(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
@@ -306,9 +315,10 @@ static void doSanityChecks(Section §ion) {
|
|||||||
minbank,
|
minbank,
|
||||||
maxbank
|
maxbank
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if section has a chance to be placed
|
// Check if section has a chance to be placed
|
||||||
if (section.size > sectionTypeInfo[section.type].size)
|
if (section.size > sectionTypeInfo[section.type].size) {
|
||||||
error(
|
error(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
@@ -317,6 +327,7 @@ static void doSanityChecks(Section §ion) {
|
|||||||
section.size,
|
section.size,
|
||||||
sectionTypeInfo[section.type].size
|
sectionTypeInfo[section.type].size
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Translate loose constraints to strong ones when they're equivalent
|
// Translate loose constraints to strong ones when they're equivalent
|
||||||
|
|
||||||
@@ -328,19 +339,20 @@ static void doSanityChecks(Section §ion) {
|
|||||||
if (section.isAddressFixed) {
|
if (section.isAddressFixed) {
|
||||||
// It doesn't make sense to have both org and alignment set
|
// It doesn't make sense to have both org and alignment set
|
||||||
if (section.isAlignFixed) {
|
if (section.isAlignFixed) {
|
||||||
if ((section.org & section.alignMask) != section.alignOfs)
|
if ((section.org & section.alignMask) != section.alignOfs) {
|
||||||
error(
|
error(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
"Section \"%s\"'s fixed address doesn't match its alignment",
|
"Section \"%s\"'s fixed address doesn't match its alignment",
|
||||||
section.name.c_str()
|
section.name.c_str()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
section.isAlignFixed = false;
|
section.isAlignFixed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the target address is valid
|
// Ensure the target address is valid
|
||||||
if (section.org < sectionTypeInfo[section.type].startAddr
|
if (section.org < sectionTypeInfo[section.type].startAddr
|
||||||
|| section.org > endaddr(section.type))
|
|| section.org > endaddr(section.type)) {
|
||||||
error(
|
error(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
@@ -351,8 +363,9 @@ static void doSanityChecks(Section §ion) {
|
|||||||
sectionTypeInfo[section.type].startAddr,
|
sectionTypeInfo[section.type].startAddr,
|
||||||
endaddr(section.type)
|
endaddr(section.type)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (section.org + section.size > endaddr(section.type) + 1)
|
if (section.org + section.size > endaddr(section.type) + 1) {
|
||||||
error(
|
error(
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
@@ -361,6 +374,7 @@ static void doSanityChecks(Section §ion) {
|
|||||||
section.org + section.size,
|
section.org + section.size,
|
||||||
endaddr(section.type) + 1
|
endaddr(section.type) + 1
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,16 @@ std::unordered_map<std::string, Symbol *> symbols;
|
|||||||
std::unordered_map<std::string, std::vector<Symbol *>> localSymbols;
|
std::unordered_map<std::string, std::vector<Symbol *>> localSymbols;
|
||||||
|
|
||||||
void sym_ForEach(void (*callback)(Symbol &)) {
|
void sym_ForEach(void (*callback)(Symbol &)) {
|
||||||
for (auto &it : symbols)
|
for (auto &it : symbols) {
|
||||||
callback(*it.second);
|
callback(*it.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sym_AddSymbol(Symbol &symbol) {
|
void sym_AddSymbol(Symbol &symbol) {
|
||||||
if (symbol.type != SYMTYPE_EXPORT) {
|
if (symbol.type != SYMTYPE_EXPORT) {
|
||||||
if (symbol.type != SYMTYPE_IMPORT)
|
if (symbol.type != SYMTYPE_IMPORT) {
|
||||||
localSymbols[symbol.name].push_back(&symbol);
|
localSymbols[symbol.name].push_back(&symbol);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,17 +37,19 @@ void sym_AddSymbol(Symbol &symbol) {
|
|||||||
// Check if the symbol already exists with a different value
|
// Check if the symbol already exists with a different value
|
||||||
if (other && !(symValue && otherValue && *symValue == *otherValue)) {
|
if (other && !(symValue && otherValue && *symValue == *otherValue)) {
|
||||||
fprintf(stderr, "error: \"%s\" is defined as ", symbol.name.c_str());
|
fprintf(stderr, "error: \"%s\" is defined as ", symbol.name.c_str());
|
||||||
if (symValue)
|
if (symValue) {
|
||||||
fprintf(stderr, "%" PRId32, *symValue);
|
fprintf(stderr, "%" PRId32, *symValue);
|
||||||
else
|
} else {
|
||||||
fputs("a label", stderr);
|
fputs("a label", stderr);
|
||||||
|
}
|
||||||
fputs(" at ", stderr);
|
fputs(" at ", stderr);
|
||||||
symbol.src->dump(symbol.lineNo);
|
symbol.src->dump(symbol.lineNo);
|
||||||
fputs(", but as ", stderr);
|
fputs(", but as ", stderr);
|
||||||
if (otherValue)
|
if (otherValue) {
|
||||||
fprintf(stderr, "%" PRId32, *otherValue);
|
fprintf(stderr, "%" PRId32, *otherValue);
|
||||||
else
|
} else {
|
||||||
fputs("another label", stderr);
|
fputs("another label", stderr);
|
||||||
|
}
|
||||||
fputs(" at ", stderr);
|
fputs(" at ", stderr);
|
||||||
other->src->dump(other->lineNo);
|
other->src->dump(other->lineNo);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ int32_t op_exponent(int32_t base, uint32_t power) {
|
|||||||
int32_t result = 1;
|
int32_t result = 1;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (power % 2)
|
if (power % 2) {
|
||||||
result *= base;
|
result *= base;
|
||||||
|
}
|
||||||
power /= 2;
|
power /= 2;
|
||||||
if (!power)
|
if (!power) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
base *= base;
|
base *= base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,14 +39,18 @@ int32_t op_exponent(int32_t base, uint32_t power) {
|
|||||||
|
|
||||||
int32_t op_shift_left(int32_t value, int32_t amount) {
|
int32_t op_shift_left(int32_t value, int32_t amount) {
|
||||||
// Get the easy cases out of the way
|
// Get the easy cases out of the way
|
||||||
if (amount == 0)
|
if (amount == 0) {
|
||||||
return value;
|
return value;
|
||||||
if (value == 0 || amount >= 32)
|
}
|
||||||
|
if (value == 0 || amount >= 32) {
|
||||||
return 0;
|
return 0;
|
||||||
if (amount < -31)
|
}
|
||||||
|
if (amount < -31) {
|
||||||
return (value < 0) ? -1 : 0;
|
return (value < 0) ? -1 : 0;
|
||||||
if (amount < 0)
|
}
|
||||||
|
if (amount < 0) {
|
||||||
return op_shift_right(value, -amount);
|
return op_shift_right(value, -amount);
|
||||||
|
}
|
||||||
|
|
||||||
// Use unsigned to force a bitwise shift
|
// Use unsigned to force a bitwise shift
|
||||||
// Casting back is OK because the types implement two's complement behavior
|
// Casting back is OK because the types implement two's complement behavior
|
||||||
@@ -53,17 +59,22 @@ int32_t op_shift_left(int32_t value, int32_t amount) {
|
|||||||
|
|
||||||
int32_t op_shift_right(int32_t value, int32_t amount) {
|
int32_t op_shift_right(int32_t value, int32_t amount) {
|
||||||
// Repeat the easy cases here to avoid INT_MIN funny business
|
// Repeat the easy cases here to avoid INT_MIN funny business
|
||||||
if (amount == 0)
|
if (amount == 0) {
|
||||||
return value;
|
return value;
|
||||||
if (value == 0 || amount < -31)
|
}
|
||||||
|
if (value == 0 || amount < -31) {
|
||||||
return 0;
|
return 0;
|
||||||
if (amount > 31)
|
}
|
||||||
|
if (amount > 31) {
|
||||||
return (value < 0) ? -1 : 0;
|
return (value < 0) ? -1 : 0;
|
||||||
if (amount < 0)
|
}
|
||||||
|
if (amount < 0) {
|
||||||
return op_shift_left(value, -amount);
|
return op_shift_left(value, -amount);
|
||||||
|
}
|
||||||
|
|
||||||
if (value > 0)
|
if (value > 0) {
|
||||||
return static_cast<uint32_t>(value) >> amount;
|
return static_cast<uint32_t>(value) >> amount;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate an OR mask for sign extension
|
// Calculate an OR mask for sign extension
|
||||||
// 1->0x80000000, 2->0xC0000000, ..., 31->0xFFFFFFFE
|
// 1->0x80000000, 2->0xC0000000, ..., 31->0xFFFFFFFE
|
||||||
@@ -76,12 +87,15 @@ int32_t op_shift_right(int32_t value, int32_t amount) {
|
|||||||
|
|
||||||
int32_t op_shift_right_unsigned(int32_t value, int32_t amount) {
|
int32_t op_shift_right_unsigned(int32_t value, int32_t amount) {
|
||||||
// Repeat the easy cases here to avoid INT_MIN funny business
|
// Repeat the easy cases here to avoid INT_MIN funny business
|
||||||
if (amount == 0)
|
if (amount == 0) {
|
||||||
return value;
|
return value;
|
||||||
if (value == 0 || amount < -31 || amount > 31)
|
}
|
||||||
|
if (value == 0 || amount < -31 || amount > 31) {
|
||||||
return 0;
|
return 0;
|
||||||
if (amount < 0)
|
}
|
||||||
|
if (amount < 0) {
|
||||||
return op_shift_left(value, -amount);
|
return op_shift_left(value, -amount);
|
||||||
|
}
|
||||||
|
|
||||||
return static_cast<uint32_t>(value) >> amount;
|
return static_cast<uint32_t>(value) >> amount;
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/util.cpp
12
src/util.cpp
@@ -15,8 +15,9 @@ char const *printChar(int c) {
|
|||||||
// "0xFF" + '\0': 5 bytes
|
// "0xFF" + '\0': 5 bytes
|
||||||
static char buf[5];
|
static char buf[5];
|
||||||
|
|
||||||
if (c == EOF)
|
if (c == EOF) {
|
||||||
return "EOF";
|
return "EOF";
|
||||||
|
}
|
||||||
|
|
||||||
if (isprint(c)) {
|
if (isprint(c)) {
|
||||||
buf[0] = '\'';
|
buf[0] = '\'';
|
||||||
@@ -58,14 +59,17 @@ size_t readUTF8Char(std::vector<int32_t> *dest, char const *src) {
|
|||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (decode(&state, &codepoint, src[i]) == 1)
|
if (decode(&state, &codepoint, src[i]) == 1) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (dest)
|
if (dest) {
|
||||||
dest->push_back(src[i]);
|
dest->push_back(src[i]);
|
||||||
|
}
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
if (state == 0)
|
if (state == 0) {
|
||||||
return i;
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -341,8 +341,9 @@ static char *execProg(char const *name, char * const *argv) {
|
|||||||
|
|
||||||
std::vector<char> cmdLine;
|
std::vector<char> cmdLine;
|
||||||
for (size_t i = 0; argv[i]; ++i) {
|
for (size_t i = 0; argv[i]; ++i) {
|
||||||
if (i > 0)
|
if (i > 0) {
|
||||||
cmdLine.push_back(' ');
|
cmdLine.push_back(' ');
|
||||||
|
}
|
||||||
cmdLine.insert(cmdLine.end(), argv[i], argv[i] + strlen(argv[i]));
|
cmdLine.insert(cmdLine.end(), argv[i], argv[i] + strlen(argv[i]));
|
||||||
}
|
}
|
||||||
cmdLine.push_back('\0');
|
cmdLine.push_back('\0');
|
||||||
|
|||||||
Reference in New Issue
Block a user