Refactor to reduce nesting depth some more (#1740)

This commit is contained in:
Rangi
2025-07-09 22:46:40 -04:00
committed by GitHub
parent bf6875f160
commit 34cf959c9d
8 changed files with 348 additions and 356 deletions

View File

@@ -1735,6 +1735,23 @@ static bool isGarbageCharacter(int c) {
&& (c == '\0' || !strchr("; \t~[](),+-*/|^=!<>:&%`\"\r\n\\", c)); && (c == '\0' || !strchr("; \t~[](),+-*/|^=!<>:&%`\"\r\n\\", c));
} }
static void reportGarbageCharacters(int c) {
// '#' can be garbage if it doesn't start a raw string or identifier
assume(isGarbageCharacter(c) || c == '#');
if (isGarbageCharacter(peek())) {
// At least two characters are garbage; group them into one error report
std::string garbage = printChar(c);
while (isGarbageCharacter(peek())) {
c = nextChar();
garbage += ", ";
garbage += printChar(c);
}
error("Unknown characters %s", garbage.c_str());
} else {
error("Unknown character %s", printChar(c));
}
}
static Token yylex_NORMAL() { static Token yylex_NORMAL() {
if (int nextToken = lexerState->nextToken; nextToken) { if (int nextToken = lexerState->nextToken; nextToken) {
lexerState->nextToken = 0; lexerState->nextToken = 0;
@@ -2083,19 +2100,7 @@ static Token yylex_NORMAL() {
// Do not report weird characters when capturing, it'll be done later // Do not report weird characters when capturing, it'll be done later
if (!lexerState->capturing) { if (!lexerState->capturing) {
assume(isGarbageCharacter(c) || c == '#'); reportGarbageCharacters(c);
if (isGarbageCharacter(peek())) {
// At least two characters are garbage; group them into one error report
std::string garbage = printChar(c);
while (isGarbageCharacter(peek())) {
c = nextChar();
garbage += ", ";
garbage += printChar(c);
}
error("Unknown characters %s", garbage.c_str());
} else {
error("Unknown character %s", printChar(c));
}
} }
} }
lexerState->atLineStart = false; lexerState->atLineStart = false;

View File

@@ -124,6 +124,51 @@ static void fatalWithUsage(char const *fmt, ...) {
exit(1); exit(1);
} }
// Parse a comma-separated string of '-s/--state' features
static std::vector<StateFeature> parseStateFeatures(char *str) {
std::vector<StateFeature> features;
for (char *feature = str; feature;) {
// Split "<feature>,<rest>" so `feature` is "<feature>" and `next` is "<rest>"
char *next = strchr(feature, ',');
if (next) {
*next++ = '\0';
}
// Trim whitespace from the beginning of `feature`...
feature += strspn(feature, " \t");
// ...and from the end
if (char *end = strpbrk(feature, " \t"); end) {
*end = '\0';
}
// A feature must be specified
if (*feature == '\0') {
fatal("Empty feature for option 's'");
}
// Parse the `feature` and update the `features` list
if (!strcasecmp(feature, "all")) {
if (!features.empty()) {
warnx("Redundant feature before \"%s\" for option 's'", feature);
}
features.assign({STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO});
} else {
StateFeature value = !strcasecmp(feature, "equ") ? STATE_EQU
: !strcasecmp(feature, "var") ? STATE_VAR
: !strcasecmp(feature, "equs") ? STATE_EQUS
: !strcasecmp(feature, "char") ? STATE_CHAR
: !strcasecmp(feature, "macro") ? STATE_MACRO
: NB_STATE_FEATURES;
if (value == NB_STATE_FEATURES) {
fatal("Invalid feature for option 's': \"%s\"", feature);
} else if (std::find(RANGE(features), value) != features.end()) {
warnx("Ignoring duplicate feature for option 's': \"%s\"", feature);
} else {
features.push_back(value);
}
}
feature = next;
}
return features;
}
int main(int argc, char *argv[]) { 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
@@ -278,46 +323,7 @@ int main(int argc, char *argv[]) {
} }
*name++ = '\0'; *name++ = '\0';
std::vector<StateFeature> features; std::vector<StateFeature> features = parseStateFeatures(musl_optarg);
for (char *feature = musl_optarg; feature;) {
// Split "<feature>,<rest>" so `feature` is "<feature>" and `next` is "<rest>"
char *next = strchr(feature, ',');
if (next) {
*next++ = '\0';
}
// Trim whitespace from the beginning of `feature`...
feature += strspn(feature, " \t");
// ...and from the end
if (char *end = strpbrk(feature, " \t"); end) {
*end = '\0';
}
// A feature must be specified
if (*feature == '\0') {
fatal("Empty feature for option 's'");
}
// Parse the `feature` and update the `features` list
if (!strcasecmp(feature, "all")) {
if (!features.empty()) {
warnx("Redundant feature before \"%s\" for option 's'", feature);
}
features.assign({STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO});
} else {
StateFeature value = !strcasecmp(feature, "equ") ? STATE_EQU
: !strcasecmp(feature, "var") ? STATE_VAR
: !strcasecmp(feature, "equs") ? STATE_EQUS
: !strcasecmp(feature, "char") ? STATE_CHAR
: !strcasecmp(feature, "macro") ? STATE_MACRO
: NB_STATE_FEATURES;
if (value == NB_STATE_FEATURES) {
fatal("Invalid feature for option 's': \"%s\"", feature);
} else if (std::find(RANGE(features), value) != features.end()) {
warnx("Ignoring duplicate feature for option 's': \"%s\"", feature);
} else {
features.push_back(value);
}
}
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);

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// This implementation was taken from musl and modified for RGBDS // This implementation was taken from musl and modified for RGBDS.
#include "extern/getopt.hpp" #include "extern/getopt.hpp"

View File

@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// UTF-8 decoder: http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ // This implementation was taken from
// http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
// and modified for RGBDS.
#include "extern/utf8decoder.hpp" #include "extern/utf8decoder.hpp"
@@ -33,10 +35,8 @@ static uint8_t const utf8d[] = {
}; };
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte) { uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte) {
uint32_t type = utf8d[byte]; uint8_t type = utf8d[byte];
*codep = *state != 0 ? (byte & 0b111111) | (*codep << 6) : byte & (0xFF >> type);
*codep = (*state != 0) ? (byte & 0x3FU) | (*codep << 6) : (0xFF >> type) & (byte); *state = utf8d[0x100 + *state * 0x10 + type];
*state = utf8d[256 + *state * 16 + type];
return *state; return *state;
} }

View File

@@ -461,10 +461,7 @@ static MbcType parseMBC(char const *name) {
break; break;
case 'A': case 'A':
case 'a': case 'a':
if (*ptr != 'M' && *ptr != 'm') { tryReadSlice("M");
return MBC_BAD;
}
ptr++;
features |= RAM; features |= RAM;
break; break;
default: default:

View File

@@ -594,126 +594,7 @@ static char *parseArgv(int argc, char *argv[]) {
return nullptr; // Done processing this argv return nullptr; // Done processing this argv
} }
int main(int argc, char *argv[]) { static void verboseOutputConfig() {
struct AtFileStackEntry {
int parentInd; // Saved offset into parent argv
std::vector<char *> argv; // This context's arg pointer vec
AtFileStackEntry(int parentInd_, std::vector<char *> argv_)
: parentInd(parentInd_), argv(argv_) {}
};
std::vector<AtFileStackEntry> atFileStack;
int curArgc = argc;
char **curArgv = argv;
std::vector<std::vector<char>> argPools;
for (;;) {
char *atFileName = parseArgv(curArgc, curArgv);
if (atFileName) {
// We need to allocate a new arg pool for each at-file, so as not to invalidate pointers
// previous at-files may have generated to their own arg pools.
// But for the same reason, the arg pool must also outlive the at-file's stack entry!
auto &argPool = argPools.emplace_back();
// Copy `argv[0]` for error reporting, and because option parsing skips it
AtFileStackEntry &stackEntry =
atFileStack.emplace_back(musl_optind, std::vector{atFileName});
// It would be nice to compute the char pointers on the fly, but reallocs don't allow
// that; so we must compute the offsets after the pool is fixed
auto offsets = readAtFile(&musl_optarg[1], argPool);
stackEntry.argv.reserve(offsets.size() + 2); // Avoid a bunch of reallocs
for (size_t ofs : offsets) {
stackEntry.argv.push_back(&argPool.data()[ofs]);
}
stackEntry.argv.push_back(nullptr); // Don't forget the arg vector terminator!
curArgc = stackEntry.argv.size() - 1;
curArgv = stackEntry.argv.data();
musl_optind = 1; // Don't use 0 because we're not scanning a different argv per se
continue; // Begin scanning that arg vector
}
if (musl_optind != curArgc) {
// This happens if `--` is passed, process the remaining arg(s) as positional
assume(musl_optind < curArgc);
for (int i = musl_optind; i < curArgc; ++i) {
registerInput(argv[i]);
}
}
// Pop off the top stack entry, or end parsing if none
if (atFileStack.empty()) {
break;
}
// OK to restore `optind` directly, because `optpos` must be 0 right now.
// (Providing 0 would be a "proper" reset, but we want to resume parsing)
musl_optind = atFileStack.back().parentInd;
atFileStack.pop_back();
if (atFileStack.empty()) {
curArgc = argc;
curArgv = argv;
} else {
auto &vec = atFileStack.back().argv;
curArgc = vec.size();
curArgv = vec.data();
}
}
if (options.nbColorsPerPal == 0) {
options.nbColorsPerPal = 1u << options.bitDepth;
} else if (options.nbColorsPerPal > 1u << options.bitDepth) {
error(
"%" PRIu8 "bpp palettes can only contain %u colors, not %" PRIu8,
options.bitDepth,
1u << options.bitDepth,
options.nbColorsPerPal
);
}
auto autoOutPath = [](bool autoOptEnabled, std::string &path, char const *extension) {
if (autoOptEnabled) {
auto &image = localOptions.groupOutputs ? options.output : options.input;
if (image.empty()) {
fatalWithUsage(
"No %s specified",
localOptions.groupOutputs ? "output tile data file" : "input image"
);
}
// Manual implementation of std::filesystem::path.replace_extension().
constexpr std::string_view chars =
// Both must start with a dot!
#if defined(_MSC_VER) || defined(__MINGW32__)
"./\\"sv;
#else
"./"sv;
#endif
size_t len = image.npos;
size_t i = image.find_last_of(chars);
if (i != image.npos && image[i] == '.') {
// We found the last dot, but check if it's part of a stem
// (There must be a non-path separator character before it)
if (i != 0 && chars.find(image[i - 1], 1) == chars.npos) {
// We can replace the extension
len = i;
}
}
path.assign(image, 0, len);
path.append(extension);
}
};
autoOutPath(localOptions.autoAttrmap, options.attrmap, ".attrmap");
autoOutPath(localOptions.autoTilemap, options.tilemap, ".tilemap");
autoOutPath(localOptions.autoPalettes, options.palettes, ".pal");
autoOutPath(localOptions.autoPalmap, options.palmap, ".palmap");
// Execute deferred external pal spec parsing, now that all other params are known
if (localOptions.externalPalSpec) {
parseExternalPalSpec(localOptions.externalPalSpec);
}
// LCOV_EXCL_START
if (options.verbosity >= Options::VERB_CFG) {
fprintf(stderr, "rgbgfx %s\n", get_package_version_string()); fprintf(stderr, "rgbgfx %s\n", get_package_version_string());
if (options.verbosity >= Options::VERB_VVVVVV) { if (options.verbosity >= Options::VERB_VVVVVV) {
@@ -848,6 +729,129 @@ int main(int argc, char *argv[]) {
printPath("Output attrmap", options.attrmap); printPath("Output attrmap", options.attrmap);
printPath("Output palettes", options.palettes); printPath("Output palettes", options.palettes);
fputs("Ready.\n", stderr); fputs("Ready.\n", stderr);
}
int main(int argc, char *argv[]) {
struct AtFileStackEntry {
int parentInd; // Saved offset into parent argv
std::vector<char *> argv; // This context's arg pointer vec
AtFileStackEntry(int parentInd_, std::vector<char *> argv_)
: parentInd(parentInd_), argv(argv_) {}
};
std::vector<AtFileStackEntry> atFileStack;
int curArgc = argc;
char **curArgv = argv;
std::vector<std::vector<char>> argPools;
for (;;) {
char *atFileName = parseArgv(curArgc, curArgv);
if (atFileName) {
// We need to allocate a new arg pool for each at-file, so as not to invalidate pointers
// previous at-files may have generated to their own arg pools.
// But for the same reason, the arg pool must also outlive the at-file's stack entry!
auto &argPool = argPools.emplace_back();
// Copy `argv[0]` for error reporting, and because option parsing skips it
AtFileStackEntry &stackEntry =
atFileStack.emplace_back(musl_optind, std::vector{atFileName});
// It would be nice to compute the char pointers on the fly, but reallocs don't allow
// that; so we must compute the offsets after the pool is fixed
auto offsets = readAtFile(&musl_optarg[1], argPool);
stackEntry.argv.reserve(offsets.size() + 2); // Avoid a bunch of reallocs
for (size_t ofs : offsets) {
stackEntry.argv.push_back(&argPool.data()[ofs]);
}
stackEntry.argv.push_back(nullptr); // Don't forget the arg vector terminator!
curArgc = stackEntry.argv.size() - 1;
curArgv = stackEntry.argv.data();
musl_optind = 1; // Don't use 0 because we're not scanning a different argv per se
continue; // Begin scanning that arg vector
}
if (musl_optind != curArgc) {
// This happens if `--` is passed, process the remaining arg(s) as positional
assume(musl_optind < curArgc);
for (int i = musl_optind; i < curArgc; ++i) {
registerInput(argv[i]);
}
}
// Pop off the top stack entry, or end parsing if none
if (atFileStack.empty()) {
break;
}
// OK to restore `optind` directly, because `optpos` must be 0 right now.
// (Providing 0 would be a "proper" reset, but we want to resume parsing)
musl_optind = atFileStack.back().parentInd;
atFileStack.pop_back();
if (atFileStack.empty()) {
curArgc = argc;
curArgv = argv;
} else {
auto &vec = atFileStack.back().argv;
curArgc = vec.size();
curArgv = vec.data();
}
}
if (options.nbColorsPerPal == 0) {
options.nbColorsPerPal = 1u << options.bitDepth;
} else if (options.nbColorsPerPal > 1u << options.bitDepth) {
error(
"%" PRIu8 "bpp palettes can only contain %u colors, not %" PRIu8,
options.bitDepth,
1u << options.bitDepth,
options.nbColorsPerPal
);
}
auto autoOutPath = [](bool autoOptEnabled, std::string &path, char const *extension) {
if (autoOptEnabled) {
auto &image = localOptions.groupOutputs ? options.output : options.input;
if (image.empty()) {
fatalWithUsage(
"No %s specified",
localOptions.groupOutputs ? "output tile data file" : "input image"
);
}
// Manual implementation of std::filesystem::path.replace_extension().
constexpr std::string_view chars =
// Both must start with a dot!
#if defined(_MSC_VER) || defined(__MINGW32__)
"./\\"sv;
#else
"./"sv;
#endif
size_t len = image.npos;
size_t i = image.find_last_of(chars);
if (i != image.npos && image[i] == '.') {
// We found the last dot, but check if it's part of a stem
// (There must be a non-path separator character before it)
if (i != 0 && chars.find(image[i - 1], 1) == chars.npos) {
// We can replace the extension
len = i;
}
}
path.assign(image, 0, len);
path.append(extension);
}
};
autoOutPath(localOptions.autoAttrmap, options.attrmap, ".attrmap");
autoOutPath(localOptions.autoTilemap, options.tilemap, ".tilemap");
autoOutPath(localOptions.autoPalettes, options.palettes, ".pal");
autoOutPath(localOptions.autoPalmap, options.palmap, ".palmap");
// Execute deferred external pal spec parsing, now that all other params are known
if (localOptions.externalPalSpec) {
parseExternalPalSpec(localOptions.externalPalSpec);
}
// LCOV_EXCL_START
if (options.verbosity >= Options::VERB_CFG) {
verboseOutputConfig();
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP

View File

@@ -122,6 +122,7 @@ static ssize_t getPlacement(Section const &section, MemoryLocation &location) {
if (spaceIdx < bankMem.size()) { if (spaceIdx < bankMem.size()) {
location.address = bankMem[spaceIdx].address; location.address = bankMem[spaceIdx].address;
}
// Process locations in that bank // Process locations in that bank
while (spaceIdx < bankMem.size()) { while (spaceIdx < bankMem.size()) {
@@ -165,7 +166,6 @@ static ssize_t getPlacement(Section const &section, MemoryLocation &location) {
// Try again with the new location/free space combo // Try again with the new location/free space combo
} }
}
// Try again in the next bank, if one is available. // Try again in the next bank, if one is available.
// Try scrambled banks in descending order until no bank in the scrambled range is // Try scrambled banks in descending order until no bank in the scrambled range is
@@ -208,22 +208,22 @@ static ssize_t getPlacement(Section const &section, MemoryLocation &location) {
// Places a section in a suitable location, or error out if it fails to. // Places a section in a suitable location, or error out if it fails to.
// Due to the implemented algorithm, this should be called with sections of decreasing size! // Due to the implemented algorithm, this should be called with sections of decreasing size!
static void placeSection(Section &section) { static void placeSection(Section &section) {
MemoryLocation location;
// Specially handle 0-byte SECTIONs, as they can't overlap anything // Specially handle 0-byte SECTIONs, as they can't overlap anything
if (section.size == 0) { if (section.size == 0) {
// Unless the SECTION's address was fixed, the starting address // Unless the SECTION's address was fixed, the starting address
// is fine for any alignment, as checked in sect_DoSanityChecks. // is fine for any alignment, as checked in sect_DoSanityChecks.
location.address = MemoryLocation location = {
section.isAddressFixed ? section.org : sectionTypeInfo[section.type].startAddr; .address =
location.bank = section.isAddressFixed ? section.org : sectionTypeInfo[section.type].startAddr,
section.isBankFixed ? section.bank : sectionTypeInfo[section.type].firstBank; .bank = section.isBankFixed ? section.bank : sectionTypeInfo[section.type].firstBank,
};
assignSection(section, location); assignSection(section, location);
return; return;
} }
// Place section using first-fit decreasing algorithm // Place section using first-fit decreasing algorithm
// https://en.wikipedia.org/wiki/Bin_packing_problem#First-fit_algorithm // https://en.wikipedia.org/wiki/Bin_packing_problem#First-fit_algorithm
MemoryLocation location;
if (ssize_t spaceIdx = getPlacement(section, location); spaceIdx != -1) { if (ssize_t spaceIdx = getPlacement(section, location); spaceIdx != -1) {
std::deque<FreeSpace> &bankMem = std::deque<FreeSpace> &bankMem =
memory[section.type][location.bank - sectionTypeInfo[section.type].firstBank]; memory[section.type][location.bank - sectionTypeInfo[section.type].firstBank];

View File

@@ -168,7 +168,6 @@ static uint8_t getNextFillByte() {
return padValue; return padValue;
} }
// Write a ROM bank's sections, ordered by increasing address, to the output file.
static void static void
writeBank(std::deque<Section const *> *bankSections, uint16_t baseOffset, uint16_t size) { writeBank(std::deque<Section const *> *bankSections, uint16_t baseOffset, uint16_t size) {
uint16_t offset = 0; uint16_t offset = 0;
@@ -202,7 +201,6 @@ static void
} }
} }
// Writes a ROM file to the output.
static void writeROM() { static void writeROM() {
if (outputFileName) { if (outputFileName) {
if (strcmp(outputFileName, "-")) { if (strcmp(outputFileName, "-")) {
@@ -263,9 +261,7 @@ static void writeROM() {
} }
} }
// Prints a symbol's name to a file, assuming that the first character is legal. static void writeSymName(std::string const &name, FILE *file) {
// Illegal characters are UTF-8-decoded (errors are replaced by U+FFFD) and emitted as '\u'/'\U'.
static void printSymName(std::string const &name, FILE *file) {
for (char const *ptr = name.c_str(); *ptr != '\0';) { for (char const *ptr = name.c_str(); *ptr != '\0';) {
char c = *ptr; char c = *ptr;
@@ -274,7 +270,7 @@ static void printSymName(std::string const &name, FILE *file) {
putc(c, file); putc(c, file);
++ptr; ++ptr;
} else { } else {
// Output illegal characters using Unicode escapes // Output illegal characters using Unicode escapes ('\u' or '\U')
// Decode the UTF-8 codepoint; or at least attempt to // Decode the UTF-8 codepoint; or at least attempt to
uint32_t state = 0, codepoint; uint32_t state = 0, codepoint;
@@ -323,27 +319,26 @@ static bool compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2) {
return sym1_name < sym2_name; return sym1_name < sym2_name;
} }
// Write a bank's contents to the sym file template<typename F>
static void writeSymBank(SortedSections const &bankSections, SectionType type, uint32_t bank) { static void forEachSortedSection(SortedSections const &bankSections, F callback) {
#define forEachSortedSection(sect, ...) \ for (Section const *sect : bankSections.zeroLenSections) {
do { \ for (; sect; sect = sect->nextu.get()) {
for (auto it = bankSections.zeroLenSections.begin(); \ callback(*sect);
it != bankSections.zeroLenSections.end(); \ }
it++) { \ }
for (Section const *sect = *it; sect; sect = sect->nextu.get()) { \ for (Section const *sect : bankSections.sections) {
__VA_ARGS__ \ for (; sect; sect = sect->nextu.get()) {
} \ callback(*sect);
} \ }
for (auto it = bankSections.sections.begin(); it != bankSections.sections.end(); it++) { \ }
for (Section const *sect = *it; sect; sect = sect->nextu.get()) { \ }
__VA_ARGS__ \
} \
} \
} while (0)
static void writeSymBank(SortedSections const &bankSections, SectionType type, uint32_t bank) {
uint32_t nbSymbols = 0; uint32_t nbSymbols = 0;
forEachSortedSection(sect, { nbSymbols += sect->symbols.size(); }); forEachSortedSection(bankSections, [&](Section const &sect) {
nbSymbols += sect.symbols.size();
});
if (!nbSymbols) { if (!nbSymbols) {
return; return;
@@ -353,11 +348,13 @@ static void writeSymBank(SortedSections const &bankSections, SectionType type, u
symList.reserve(nbSymbols); symList.reserve(nbSymbols);
forEachSortedSection(sect, { forEachSortedSection(bankSections, [&](Section const &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() && startsIdentifier(sym->name[0])) { if (sym->name.empty() || !startsIdentifier(sym->name[0])) {
uint16_t addr = static_cast<uint16_t>(sym->label().offset + sect->org); continue;
}
uint16_t addr = static_cast<uint16_t>(sym->label().offset + sect.org);
uint16_t parentAddr = addr; uint16_t parentAddr = addr;
if (auto pos = sym->name.find('.'); pos != std::string::npos) { if (auto pos = sym->name.find('.'); pos != std::string::npos) {
std::string parentName = sym->name.substr(0, pos); std::string parentName = sym->name.substr(0, pos);
@@ -371,18 +368,15 @@ static void writeSymBank(SortedSections const &bankSections, SectionType type, u
} }
symList.push_back({.sym = sym, .addr = addr, .parentAddr = parentAddr}); symList.push_back({.sym = sym, .addr = addr, .parentAddr = parentAddr});
} }
}
}); });
#undef forEachSortedSection
std::stable_sort(RANGE(symList), compareSymbols); std::stable_sort(RANGE(symList), compareSymbols);
uint32_t symBank = bank + sectionTypeInfo[type].firstBank; uint32_t symBank = bank + sectionTypeInfo[type].firstBank;
for (SortedSymbol &sym : symList) { for (SortedSymbol &sym : symList) {
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " ", symBank, sym.addr); fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " ", symBank, sym.addr);
printSymName(sym.sym->name, symFile); writeSymName(sym.sym->name, symFile);
putc('\n', symFile); putc('\n', symFile);
} }
} }
@@ -402,8 +396,7 @@ static void writeEmptySpace(uint16_t begin, uint16_t end) {
} }
} }
// Prints a section's name to a file. static void writeSectionName(std::string const &name, FILE *file) {
static void printSectionName(std::string const &name, FILE *file) {
for (char c : name) { for (char c : name) {
// Escape characters that need escaping // Escape characters that need escaping
switch (c) { switch (c) {
@@ -427,7 +420,47 @@ static void printSectionName(std::string const &name, FILE *file) {
} }
} }
// Write a bank's contents to the map file template<typename F>
uint16_t forEachSection(SortedSections const &sectList, F callback) {
uint16_t used = 0;
auto section = sectList.sections.begin();
auto zeroLenSection = sectList.zeroLenSections.begin();
while (section != sectList.sections.end() || zeroLenSection != sectList.zeroLenSections.end()) {
// Pick the lowest section by address out of the two
auto &pickedSection = section == sectList.sections.end() ? zeroLenSection
: zeroLenSection == sectList.zeroLenSections.end() ? section
: (*section)->org < (*zeroLenSection)->org ? section
: zeroLenSection;
used += (*pickedSection)->size;
callback(**pickedSection);
pickedSection++;
}
return used;
}
static void writeMapSymbols(Section const *sect) {
for (uint16_t org = sect->org; sect; sect = sect->nextu.get()) {
for (Symbol *sym : sect->symbols) {
// Don't output symbols that begin with an illegal character
if (sym->name.empty() || !startsIdentifier(sym->name[0])) {
continue;
}
// Space matches "\tSECTION: $xxxx ..."
fprintf(mapFile, "\t $%04" PRIx32 " = ", sym->label().offset + org);
writeSymName(sym->name, mapFile);
putc('\n', mapFile);
}
// Announce the following "piece"
if (SectionModifier mod = sect->nextu ? sect->nextu->modifier : SECTION_NORMAL;
mod == SECTION_UNION) {
fputs("\t ; Next union\n", mapFile);
} else if (mod == SECTION_FRAGMENT) {
fputs("\t ; Next fragment\n", mapFile);
}
}
}
static void writeMapBank(SortedSections const &sectList, SectionType type, uint32_t bank) { static void writeMapBank(SortedSections const &sectList, SectionType type, uint32_t bank) {
fprintf( fprintf(
mapFile, mapFile,
@@ -436,60 +469,27 @@ static void writeMapBank(SortedSections const &sectList, SectionType type, uint3
bank + sectionTypeInfo[type].firstBank bank + sectionTypeInfo[type].firstBank
); );
uint16_t used = 0;
auto section = sectList.sections.begin();
auto zeroLenSection = sectList.zeroLenSections.begin();
uint16_t prevEndAddr = sectionTypeInfo[type].startAddr; uint16_t prevEndAddr = sectionTypeInfo[type].startAddr;
uint16_t used = forEachSection(sectList, [&](Section const &sect) {
assume(sect.offset == 0);
while (section != sectList.sections.end() || zeroLenSection != sectList.zeroLenSections.end()) { writeEmptySpace(prevEndAddr, sect.org);
// Pick the lowest section by address out of the two
auto &pickedSection = section == sectList.sections.end() ? zeroLenSection
: zeroLenSection == sectList.zeroLenSections.end() ? section
: (*section)->org < (*zeroLenSection)->org ? section
: zeroLenSection;
Section const *sect = *pickedSection;
used += sect->size; prevEndAddr = sect.org + sect.size;
assume(sect->offset == 0);
writeEmptySpace(prevEndAddr, sect->org); fprintf(mapFile, "\tSECTION: $%04" PRIx16, sect.org);
if (sect.size != 0) {
prevEndAddr = sect->org + sect->size;
fprintf(mapFile, "\tSECTION: $%04" PRIx16, sect->org);
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); writeSectionName(sect.name, mapFile);
fputs("\"]\n", mapFile); fputs("\"]\n", mapFile);
if (!noSymInMap) { if (!noSymInMap) {
// Also print symbols in the following "pieces" // Also print symbols in the following "pieces"
for (uint16_t org = sect->org; sect; sect = sect->nextu.get()) { writeMapSymbols(&sect);
for (Symbol *sym : sect->symbols) {
// Don't output symbols that begin with an illegal character
if (!sym->name.empty() && startsIdentifier(sym->name[0])) {
// Space matches "\tSECTION: $xxxx ..."
fprintf(mapFile, "\t $%04" PRIx32 " = ", sym->label().offset + org);
printSymName(sym->name, mapFile);
putc('\n', mapFile);
}
}
if (sect->nextu) {
// Announce the following "piece"
if (sect->nextu->modifier == SECTION_UNION) {
fputs("\t ; Next union\n", mapFile);
} else if (sect->nextu->modifier == SECTION_FRAGMENT) {
fputs("\t ; Next fragment\n", mapFile);
}
}
}
}
pickedSection++;
} }
});
if (used == 0) { if (used == 0) {
fputs("\tEMPTY\n", mapFile); fputs("\tEMPTY\n", mapFile);
@@ -504,7 +504,6 @@ static void writeMapBank(SortedSections const &sectList, SectionType type, uint3
} }
} }
// Write the total used and free space by section type to the map file
static void writeMapSummary() { static void writeMapSummary() {
fputs("SUMMARY:\n", mapFile); fputs("SUMMARY:\n", mapFile);
@@ -525,24 +524,7 @@ static void writeMapSummary() {
uint32_t usedTotal = 0; uint32_t usedTotal = 0;
for (uint32_t bank = 0; bank < nbBanks; bank++) { for (uint32_t bank = 0; bank < nbBanks; bank++) {
uint16_t used = 0; usedTotal += forEachSection(sections[type][bank], [](Section const &) {});
auto &sectList = sections[type][bank];
auto section = sectList.sections.begin();
auto zeroLenSection = sectList.zeroLenSections.begin();
while (section != sectList.sections.end()
|| zeroLenSection != sectList.zeroLenSections.end()) {
// Pick the lowest section by address out of the two
auto &pickedSection = section == sectList.sections.end() ? zeroLenSection
: zeroLenSection == sectList.zeroLenSections.end() ? section
: (*section)->org < (*zeroLenSection)->org ? section
: zeroLenSection;
used += (*pickedSection)->size;
pickedSection++;
}
usedTotal += used;
} }
fprintf( fprintf(
@@ -560,7 +542,6 @@ static void writeMapSummary() {
} }
} }
// Writes the sym file, if applicable.
static void writeSym() { static void writeSym() {
if (!symFileName) { if (!symFileName) {
return; return;
@@ -606,12 +587,11 @@ static void writeSym() {
int32_t val = std::get<int32_t>(sym->data); int32_t val = std::get<int32_t>(sym->data);
int width = val < 0x100 ? 2 : val < 0x10000 ? 4 : 8; int width = val < 0x100 ? 2 : val < 0x10000 ? 4 : 8;
fprintf(symFile, "%0*" PRIx32 " ", width, val); fprintf(symFile, "%0*" PRIx32 " ", width, val);
printSymName(sym->name, symFile); writeSymName(sym->name, symFile);
putc('\n', symFile); putc('\n', symFile);
} }
} }
// Writes the map file, if applicable.
static void writeMap() { static void writeMap() {
if (!mapFileName) { if (!mapFileName) {
return; return;