Always use braces with InsertBraces: true in .clang-format

This commit is contained in:
Rangi42
2025-01-27 19:31:35 -05:00
committed by Rangi
parent 25c9f8f383
commit cae31005f8
29 changed files with 1350 additions and 717 deletions

View File

@@ -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

View File

@@ -52,17 +52,20 @@ 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,11 +73,12 @@ 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()) {
error("Charmap '%s' already exists\n", name.c_str()); error("Charmap '%s' already exists\n", name.c_str());
@@ -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,11 +101,12 @@ 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() {
charmapStack.push(currentCharmap); charmapStack.push(currentCharmap);
@@ -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,9 +169,10 @@ 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;

View File

@@ -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);
} }

View File

@@ -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,9 +118,10 @@ 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) {
std::string escaped; std::string escaped;
@@ -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,24 +201,29 @@ 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;
} }
} }
}
char prefixChar = !useExact ? 0 char prefixChar = !useExact ? 0
: useType == 'X' ? '$' : useType == 'X' ? '$'
@@ -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,28 +298,34 @@ 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);
} }
} }

View File

@@ -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,29 +104,34 @@ 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());
} }
} }
}
static bool isValidFilePath(std::string const &path) { static bool isValidFilePath(std::string const &path) {
struct stat statBuf; struct stat statBuf;
@@ -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,9 +185,10 @@ 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()++;
// If this wasn't the last iteration, wrap instead of popping // If this wasn't the last iteration, wrap instead of popping
@@ -199,9 +208,10 @@ 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) {
checkRecursionDepth(); checkRecursionDepth();
@@ -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 &macroName, std::shared_ptr<MacroArgs> macroArgs) { void fstk_RunMacro(std::string const &macroName, 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 &macroName, 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);
} }
}

View File

@@ -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,25 +526,29 @@ 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) {
// This buffer overflow made me lose WEEKS of my life. Never again. // This buffer overflow made me lose WEEKS of my life. Never again.
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,20 +567,23 @@ 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) {
return c == '@' || c == '#' || c == '<' || (c >= '1' && c <= '9'); return c == '@' || c == '#' || c == '<' || (c >= '1' && c <= '9');
@@ -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,22 +722,26 @@ 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
return EOF; return EOF;
@@ -736,23 +755,27 @@ 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
return EOF; return EOF;
@@ -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,15 +865,17 @@ 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() {
lexerState->disableMacroArgs = true; lexerState->disableMacroArgs = true;
@@ -870,15 +897,17 @@ 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());
} }
} }
}
// Functions to discard non-tokenized characters // Functions to discard non-tokenized characters
@@ -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,10 +951,11 @@ 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;
} }
} }
}
static void discardLineContinuation() { static void discardLineContinuation() {
for (;;) { for (;;) {
@@ -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,9 +1209,10 @@ 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
if (!raw) { if (!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,9 +2153,10 @@ 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)) {
shiftChar(); shiftChar();
@@ -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

View File

@@ -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);
} }

View File

@@ -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;
} }

View File

@@ -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:

View File

@@ -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,10 +111,11 @@ static void writeSection(Section const &sect, 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);
} }
} }
}
static void writeSymbol(Symbol const &sym, FILE *file) { static void writeSymbol(Symbol const &sym, FILE *file) {
putString(sym.name, file); putString(sym.name, 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,7 +343,7 @@ 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",
@@ -343,26 +351,32 @@ void out_WriteObject() {
node.ID node.ID
); );
} }
}
for (Symbol const *sym : objectSymbols) for (Symbol const *sym : objectSymbols) {
writeSymbol(*sym, file); writeSymbol(*sym, file);
}
for (Section const &sect : sectionList) for (Section const &sect : 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) {
for (char c : escape) { for (char c : escape) {
@@ -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");
} }
} }
}

View File

@@ -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 &sect = *sym->getSection(); Section const &sect = *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 &sect = *sym.getSection(); Section const &sect = *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,9 +609,10 @@ 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) {
assume(n != 0); // That doesn't make sense assume(n != 0); // That doesn't make sense

View File

@@ -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 &sect : sectionList) { for (Section const &sect : 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",
@@ -87,6 +90,7 @@ void sect_CheckSizes() {
); );
} }
} }
}
Section *sect_FindSectionByName(std::string const &name) { Section *sect_FindSectionByName(std::string const &name) {
auto search = sectionMap.find(name); auto search = sectionMap.find(name);
@@ -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,7 +268,7 @@ 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(),
@@ -263,6 +276,7 @@ static void mergeSections(
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 &sect; return &sect;
} }
@@ -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",
@@ -351,6 +368,7 @@ static Section *getSection(
endaddr(type) endaddr(type)
); );
} }
}
if (alignment != 0) { if (alignment != 0) {
if (alignment > 16) { if (alignment > 16) {
@@ -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,26 +414,30 @@ 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,9 +523,10 @@ 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() {
return currentLoadSection ? currentLoadSection : currentSection; return currentLoadSection ? currentLoadSection : currentSection;
@@ -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,12 +912,14 @@ 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
void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t length) { void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t length) {
@@ -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;

View File

@@ -40,9 +40,10 @@ 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() {
if (MacroArgs const *macroArgs = fstk_GetCurrentMacroArgs(); macroArgs) { if (MacroArgs const *macroArgs = fstk_GetCurrentMacroArgs(); macroArgs) {
@@ -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,9 +125,10 @@ 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) {
if (sym.isBuiltin && !sym_FindScopedValidSymbol(sym.name)) { if (sym.isBuiltin && !sym_FindScopedValidSymbol(sym.name)) {
@@ -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,20 +549,22 @@ 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("!");
anon += std::to_string(id); anon += std::to_string(id);
@@ -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;

View File

@@ -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,35 +101,44 @@ 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) {
std::string rootFlag = flag; std::string rootFlag = 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,11 +215,12 @@ 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;
} }
} }
} }
}
// Try to match the flag against a parametric warning // Try to match the flag against a parametric warning
// If there was an equals sign, it will have set `param`; if not, `param` will be 0, which // If there was an equals sign, it will have set `param`; if not, `param` will be 0, which
@@ -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,17 +251,19 @@ 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,9 +275,10 @@ 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,7 +316,7 @@ 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!",
@@ -307,6 +324,7 @@ void error(char const *fmt, ...) {
maxErrors == 1 ? "" : "s" maxErrors == 1 ? "" : "s"
); );
} }
}
[[noreturn]] [[noreturn]]
void fatalerror(char const *fmt, ...) { void fatalerror(char const *fmt, ...) {

71
src/extern/getopt.cpp vendored
View File

@@ -19,9 +19,10 @@ 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) {
int i; int i;
@@ -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,18 +139,21 @@ 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;
} }
resumed = musl_optind; resumed = musl_optind;
@@ -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 '?';
} }

View File

@@ -83,9 +83,10 @@ 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 {
ROM = 0x00, ROM = 0x00,
@@ -189,17 +190,20 @@ 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)
overwriteByte(rom0, 0x14A, 0x01, "destination code");
} }
if (oldLicensee != UNSPECIFIED) if (!japanese) {
overwriteByte(rom0, 0x14A, 0x01, "destination code");
}
}
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,15 +1050,17 @@ 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;
} }
} }
}
// Handle setting the ROM size if padding was requested // Handle setting the ROM size if padding was requested
// Pad to the next valid power of 2. This is because padding is required by flashers, which // Pad to the next valid power of 2. This is because padding is required by flashers, which
@@ -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,9 +1150,10 @@ 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);
if (writeLen == -1) { if (writeLen == -1) {
@@ -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,9 +1542,10 @@ 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) {
fputs( fputs(

View File

@@ -72,16 +72,18 @@ 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]]
void fatal(char const *fmt, ...) { void fatal(char const *fmt, ...) {
@@ -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", [] {

View File

@@ -167,9 +167,10 @@ 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);
} }
if (mapSize % width != 0) { if (mapSize % width != 0) {

View File

@@ -74,14 +74,17 @@ static void assignSection(Section &section, MemoryLocation const &location) {
static bool isLocationSuitable( static bool isLocationSuitable(
Section const &section, FreeSpace const &freeSpace, MemoryLocation const &location Section const &section, 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 &section, 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 &section, 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 &section, 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 &section, 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,10 +264,11 @@ static void placeSection(Section &section) {
} 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 &section) {
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 &section) {
section.bank, section.bank,
static_cast<uint16_t>(~section.alignMask) static_cast<uint16_t>(~section.alignMask)
); );
else
snprintf(where, sizeof(where), "in bank $%02" PRIx32, section.bank);
} else { } else {
if (section.isAddressFixed) snprintf(where, sizeof(where), "in bank $%02" PRIx32, section.bank);
}
} else {
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 &section) {
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 &section) {
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(),
@@ -320,6 +338,7 @@ static void placeSection(Section &section) {
out_OverlappingSection(section)->name.c_str() out_OverlappingSection(section)->name.c_str()
); );
} }
}
// clang-format off: vertically align values // clang-format off: vertically align values
static constexpr uint8_t BANK_CONSTRAINED = 1 << 2; static constexpr uint8_t BANK_CONSTRAINED = 1 << 2;
@@ -334,20 +353,24 @@ static std::deque<Section *> unassignedSections[1 << 3];
static void categorizeSection(Section &section) { static void categorizeSection(Section &section) {
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 *> &sections = unassignedSections[constraints]; std::deque<Section *> &sections = 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, &section); sections.insert(pos, &section);
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,12 +427,14 @@ 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_();
} }

View File

@@ -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,9 +97,10 @@ 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, ...) {
va_list args; va_list args;
@@ -109,9 +111,10 @@ 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]]
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) { void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
@@ -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,13 +308,15 @@ 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;
} }
} }
} }
}
[[noreturn]] [[noreturn]]
void reportErrors() { void reportErrors() {
@@ -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();
} }

View File

@@ -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,
@@ -160,6 +162,7 @@ static void readFileStackNode(
); );
} }
} }
}
// Reads a symbol from a file. // Reads a symbol from a file.
// @param file The file to read from // @param file The file to read from
@@ -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,
@@ -305,6 +308,7 @@ static void readPatch(
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.
// @param patch The patch to fix // @param patch The patch to fix
@@ -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,7 +404,7 @@ 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,
@@ -405,6 +412,7 @@ static void readSection(
feof(file) ? "Unexpected end of file" : strerror(errno) feof(file) ? "Unexpected end of file" : strerror(errno)
); );
} }
}
uint32_t nbPatches; uint32_t nbPatches;
@@ -417,10 +425,11 @@ 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);
} }
} }
}
// Links a symbol to a section, keeping the section's symbol list sorted. // Links a symbol to a section, keeping the section's symbol list sorted.
// @param symbol The symbol to link // @param symbol The symbol to link
@@ -433,11 +442,12 @@ static void linkSymToSect(Symbol &symbol, Section &section) {
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,9 +567,10 @@ 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
std::vector<std::unique_ptr<Section>> fileSections(nbSections); std::vector<std::unique_ptr<Section>> fileSections(nbSections);
@@ -585,10 +600,11 @@ 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);
} }
} }
}
// Give symbols' section pointers to their sections // Give symbols' section pointers to their sections
for (uint32_t i = 0; i < nbSymbols; i++) { for (uint32_t i = 0; i < nbSymbols; i++) {
@@ -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,

View File

@@ -66,24 +66,27 @@ void out_AddSection(Section const &section) {
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, &section); bankSections.insert(pos, &section);
} }
@@ -92,17 +95,19 @@ Section const *out_OverlappingSection(Section const &section) {
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;
} }
// 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,10 +143,11 @@ 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();
} }
} }
}
static uint8_t getNextFillByte() { static uint8_t getNextFillByte() {
if (overlayFile) { if (overlayFile) {
@@ -179,9 +186,10 @@ 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,7 +253,7 @@ 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(
&sections[SECTTYPE_ROMX][i].sections, &sections[SECTTYPE_ROMX][i].sections,
sectionTypeInfo[SECTTYPE_ROMX].startAddr, sectionTypeInfo[SECTTYPE_ROMX].startAddr,
@@ -248,6 +261,7 @@ static void writeROM() {
); );
} }
} }
}
// Checks whether this character is legal as the first character of a symbol's name in a sym file // Checks whether this character is legal as the first character of a symbol's name in a sym file
static bool canStartSymName(char c) { static bool canStartSymName(char c) {
@@ -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,12 +371,13 @@ 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),
}); });
} }
}
}); });
#undef forEachSortedSection #undef forEachSortedSection
@@ -443,8 +463,9 @@ static void writeMapBank(SortedSections const &sectList, 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,13 +482,14 @@ static void writeMapBank(SortedSections const &sectList, 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");
} }
} }
} }
}
pickedSection++; pickedSection++;
} }
@@ -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,17 +586,19 @@ 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
static std::vector<Symbol *> constants; // `static` so `sym_ForEach` callback can see it static std::vector<Symbol *> constants; // `static` so `sym_ForEach` callback can see it
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,10 +637,11 @@ 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);
} }
} }
}
void out_WriteFiles() { void out_WriteFiles() {
writeROM(); writeROM();

View File

@@ -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 &section, 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 &section, 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 &section, 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,12 +545,14 @@ static void applyFilePatches(Section &section, 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 &section) { static void applyPatches(Section &section) {
if (!sect_HasData(section.type)) if (!sect_HasData(section.type)) {
return; return;
}
for (Section *component = &section; component; component = component->nextu.get()) for (Section *component = &section; component; component = component->nextu.get()) {
applyFilePatches(*component, section); applyFilePatches(*component, section);
} }
}
void patch_ApplyPatches() { void patch_ApplyPatches() {
sect_ForEach(applyPatches); sect_ForEach(applyPatches);

View File

@@ -25,9 +25,10 @@ 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,9 +274,10 @@ 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
expectToken("size", 'A'); expectToken("size", 'A');
@@ -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,
@@ -588,6 +613,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
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(
&section->data[*writeIndex], &data[writtenOfs], offset - writtenOfs + 1 &section->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(&section->data[*writeIndex], &data[writtenOfs], data.size() - writtenOfs); memcpy(&section->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();

View File

@@ -14,9 +14,10 @@ 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) {
if (target.isAddressFixed) { if (target.isAddressFixed) {
@@ -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 &section) {
} }
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 &section) {
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 &section) {
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 &section) {
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 &section) {
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 &section) {
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,
@@ -363,6 +376,7 @@ static void doSanityChecks(Section &section) {
); );
} }
} }
}
void sect_DoSanityChecks() { void sect_DoSanityChecks() {
sect_ForEach(doSanityChecks); sect_ForEach(doSanityChecks);

View File

@@ -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);

View File

@@ -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;
} }

View File

@@ -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;
} }
} }
}

View File

@@ -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');