Reduce deep nesting some more, including larger refactors to assign.cpp

This commit is contained in:
Rangi42
2025-07-22 13:03:21 -04:00
parent eea532ded1
commit 2ce4cdbff6
8 changed files with 330 additions and 311 deletions

View File

@@ -31,28 +31,28 @@ struct CharmapNode {
struct Charmap { struct Charmap {
std::string name; std::string name;
std::vector<CharmapNode> nodes; // first node is reserved for the root node std::vector<CharmapNode> nodes; // first node is reserved for the root node
};
// Traverse the trie depth-first to derive the character mappings in definition order // Traverse the trie depth-first to derive the character mappings in definition order
template<typename F> template<typename F>
bool forEachChar(F callback) const { bool forEachChar(Charmap const &charmap, F callback) {
// clang-format off: nested initializers // clang-format off: nested initializers
for (std::stack<std::pair<size_t, std::string>> prefixes({{0, ""}}); !prefixes.empty();) { for (std::stack<std::pair<size_t, std::string>> prefixes({{0, ""}}); !prefixes.empty();) {
// clang-format on // clang-format on
auto [nodeIdx, mapping] = std::move(prefixes.top()); auto [nodeIdx, mapping] = std::move(prefixes.top());
prefixes.pop(); prefixes.pop();
CharmapNode const &node = nodes[nodeIdx]; CharmapNode const &node = charmap.nodes[nodeIdx];
if (node.isTerminal() && !callback(nodeIdx, mapping)) { if (node.isTerminal() && !callback(nodeIdx, mapping)) {
return false; return false;
} }
for (unsigned c = 0; c < std::size(node.next); c++) { for (unsigned c = 0; c < std::size(node.next); 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)});
}
} }
} }
return true;
} }
}; return true;
}
static std::deque<Charmap> charmapList; static std::deque<Charmap> charmapList;
static std::unordered_map<std::string, size_t> charmapMap; // Indexes into `charmapList` static std::unordered_map<std::string, size_t> charmapMap; // Indexes into `charmapList`
@@ -66,7 +66,7 @@ bool charmap_ForEach(
) { ) {
for (Charmap const &charmap : charmapList) { for (Charmap const &charmap : charmapList) {
std::map<size_t, std::string> mappings; std::map<size_t, std::string> mappings;
charmap.forEachChar([&mappings](size_t nodeIdx, std::string const &mapping) { forEachChar(charmap, [&mappings](size_t nodeIdx, std::string const &mapping) {
mappings[nodeIdx] = mapping; mappings[nodeIdx] = mapping;
return true; return true;
}); });
@@ -305,7 +305,7 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique) { std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique) {
Charmap const &charmap = *currentCharmap; Charmap const &charmap = *currentCharmap;
std::string revMapping; std::string revMapping;
unique = charmap.forEachChar([&](size_t nodeIdx, std::string const &mapping) { unique = forEachChar(charmap, [&](size_t nodeIdx, std::string const &mapping) {
if (charmap.nodes[nodeIdx].value == value) { if (charmap.nodes[nodeIdx].value == value) {
if (revMapping.empty()) { if (revMapping.empty()) {
revMapping = mapping; revMapping = mapping;

View File

@@ -144,12 +144,19 @@ static void registerUnregisteredSymbol(Symbol &sym) {
} }
static void writeRpn(std::vector<uint8_t> &rpnexpr, std::vector<uint8_t> const &rpn) { static void writeRpn(std::vector<uint8_t> &rpnexpr, std::vector<uint8_t> const &rpn) {
std::string symName;
size_t rpnptr = 0; size_t rpnptr = 0;
for (size_t offset = 0; offset < rpn.size();) { for (size_t offset = 0; offset < rpn.size();) {
uint8_t rpndata = rpn[offset++]; uint8_t rpndata = rpn[offset++];
auto getSymName = [&](){
std::string symName;
for (uint8_t c; (c = rpn[offset++]) != 0;) {
symName += c;
}
return symName;
};
switch (rpndata) { switch (rpndata) {
Symbol *sym; Symbol *sym;
uint32_t value; uint32_t value;
@@ -164,17 +171,8 @@ static void writeRpn(std::vector<uint8_t> &rpnexpr, std::vector<uint8_t> const &
break; break;
case RPN_SYM: case RPN_SYM:
symName.clear();
for (;;) {
uint8_t c = rpn[offset++];
if (c == 0) {
break;
}
symName += c;
}
// The symbol name is always written expanded // The symbol name is always written expanded
sym = sym_FindExactSymbol(symName); sym = sym_FindExactSymbol(getSymName());
if (sym->isConstant()) { if (sym->isConstant()) {
rpnexpr[rpnptr++] = RPN_CONST; rpnexpr[rpnptr++] = RPN_CONST;
value = sym->getConstantValue(); value = sym->getConstantValue();
@@ -191,17 +189,8 @@ static void writeRpn(std::vector<uint8_t> &rpnexpr, std::vector<uint8_t> const &
break; break;
case RPN_BANK_SYM: case RPN_BANK_SYM:
symName.clear();
for (;;) {
uint8_t c = rpn[offset++];
if (c == 0) {
break;
}
symName += c;
}
// The symbol name is always written expanded // The symbol name is always written expanded
sym = sym_FindExactSymbol(symName); sym = sym_FindExactSymbol(getSymName());
registerUnregisteredSymbol(*sym); // Ensure that `sym->ID` is set registerUnregisteredSymbol(*sym); // Ensure that `sym->ID` is set
value = sym->ID; value = sym->ID;

View File

@@ -364,51 +364,50 @@ public:
std::vector<uint32_t> indeterminates; std::vector<uint32_t> indeterminates;
// Assign a color to the given position, and register it in the image palette as well // Assign a color to the given position, and register it in the image palette as well
auto assignColor = auto assignColor = [&](png_uint_32 x, png_uint_32 y, Rgba &&color) {
[this, &conflicts, &indeterminates](png_uint_32 x, png_uint_32 y, Rgba &&color) { if (!color.isTransparent() && !color.isOpaque()) {
if (!color.isTransparent() && !color.isOpaque()) { uint32_t css = color.toCSS();
uint32_t css = color.toCSS(); if (std::find(RANGE(indeterminates), css) == indeterminates.end()) {
if (std::find(RANGE(indeterminates), css) == indeterminates.end()) { error(
error( "Color #%08x is neither transparent (alpha < %u) nor opaque (alpha >= "
"Color #%08x is neither transparent (alpha < %u) nor opaque (alpha >= " "%u) [first seen at x: %" PRIu32 ", y: %" PRIu32 "]",
"%u) [first seen at x: %" PRIu32 ", y: %" PRIu32 "]", css,
css, Rgba::transparency_threshold,
Rgba::transparency_threshold, Rgba::opacity_threshold,
Rgba::opacity_threshold, x,
x, y
y );
); indeterminates.push_back(css);
indeterminates.push_back(css); }
} } else if (Rgba const *other = colors.registerColor(color); other) {
} else if (Rgba const *other = colors.registerColor(color); other) { std::tuple conflicting{color.toCSS(), other->toCSS()};
std::tuple conflicting{color.toCSS(), other->toCSS()}; // Do not report combinations twice
// Do not report combinations twice if (std::find(RANGE(conflicts), conflicting) == conflicts.end()) {
if (std::find(RANGE(conflicts), conflicting) == conflicts.end()) { warnx(
warnx( "Fusing colors #%08x and #%08x into Game Boy color $%04x [first seen "
"Fusing colors #%08x and #%08x into Game Boy color $%04x [first seen " "at x: %" PRIu32 ", y: %" PRIu32 "]",
"at x: %" PRIu32 ", y: %" PRIu32 "]", std::get<0>(conflicting),
std::get<0>(conflicting), std::get<1>(conflicting),
std::get<1>(conflicting), color.cgbColor(),
color.cgbColor(), x,
x, y
y );
); // Do not report this combination again
// Do not report this combination again conflicts.emplace_back(conflicting);
conflicts.emplace_back(conflicting); }
} }
}
pixel(x, y) = color; pixel(x, y) = color;
}; };
if (interlaceType == PNG_INTERLACE_NONE) { if (interlaceType == PNG_INTERLACE_NONE) {
for (png_uint_32 y = 0; y < height; ++y) { for (png_uint_32 y = 0; y < height; ++y) {
png_read_row(png, row.data(), nullptr); png_bytep ptr = row.data();
png_read_row(png, ptr, nullptr);
for (png_uint_32 x = 0; x < width; ++x) { for (png_uint_32 x = 0; x < width; ++x) {
assignColor( assignColor(x, y, Rgba(ptr[0], ptr[1], ptr[2], ptr[3]));
x, y, Rgba(row[x * 4], row[x * 4 + 1], row[x * 4 + 2], row[x * 4 + 3]) ptr += 4;
);
} }
} }
} else { } else {

View File

@@ -211,28 +211,28 @@ void reverse() {
palettes.clear(); palettes.clear();
std::array<uint8_t, sizeof(uint16_t) * 4> buf; // 4 colors std::array<uint8_t, sizeof(uint16_t) * 4> buf; // 4 colors
size_t nbRead; for (;;) {
do { if (size_t nbRead = file->sgetn(reinterpret_cast<char *>(buf.data()), buf.size());
nbRead = file->sgetn(reinterpret_cast<char *>(buf.data()), buf.size()); nbRead == 0) {
if (nbRead == buf.size()) { break;
// Expand the colors } else if (nbRead != buf.size()) {
auto &palette = palettes.emplace_back();
std::generate(
palette.begin(),
palette.begin() + options.nbColorsPerPal,
[&buf, i = 0]() mutable {
i += 2;
return Rgba::fromCGBColor(buf[i - 2] + (buf[i - 1] << 8));
}
);
} else if (nbRead != 0) {
fatal( fatal(
"Palette data size (%zu) is not a multiple of %zu bytes!\n", "Palette data size (%zu) is not a multiple of %zu bytes!\n",
palettes.size() * buf.size() + nbRead, palettes.size() * buf.size() + nbRead,
buf.size() buf.size()
); );
} }
} while (nbRead != 0); // Expand the colors
auto &palette = palettes.emplace_back();
std::generate(
palette.begin(),
palette.begin() + options.nbColorsPerPal,
[&buf, i = 0]() mutable {
i += 2;
return Rgba::fromCGBColor(buf[i - 2] + (buf[i - 1] << 8));
}
);
}
if (palettes.size() > options.nbPalettes) { if (palettes.size() > options.nbPalettes) {
warnx( warnx(

View File

@@ -34,8 +34,6 @@ struct FreeSpace {
// Table of free space for each bank // Table of free space for each bank
static std::vector<std::deque<FreeSpace>> memory[SECTTYPE_INVALID]; static std::vector<std::deque<FreeSpace>> memory[SECTTYPE_INVALID];
static uint64_t nbSectionsToAssign;
// Init the free space-modelling structs // Init the free space-modelling structs
static void initFreeSpace() { static void initFreeSpace() {
for (SectionType type : EnumSeq(SECTTYPE_INVALID)) { for (SectionType type : EnumSeq(SECTTYPE_INVALID)) {
@@ -58,8 +56,6 @@ static void assignSection(Section &section, MemoryLocation const &location) {
next->bank = location.bank; next->bank = location.bank;
} }
--nbSectionsToAssign;
out_AddSection(section); out_AddSection(section);
} }
@@ -84,15 +80,13 @@ static bool isLocationSuitable(
return location.address + section.size <= freeSpace.address + freeSpace.size; return location.address + section.size <= freeSpace.address + freeSpace.size;
} }
// Returns a suitable free space index into `memory[section->type]` at which to place the given static MemoryLocation getStartLocation(Section const &section) {
// section, or -1 if none was found.
static ssize_t getPlacement(Section const &section, MemoryLocation &location) {
SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type];
static uint16_t curScrambleROM = 0; static uint16_t curScrambleROM = 0;
static uint8_t curScrambleWRAM = 0; static uint8_t curScrambleWRAM = 0;
static int8_t curScrambleSRAM = 0; static int8_t curScrambleSRAM = 0;
MemoryLocation location;
// Determine which bank we should start searching in // Determine which bank we should start searching in
if (section.isBankFixed) { if (section.isBankFixed) {
location.bank = section.bank; location.bank = section.bank;
@@ -112,96 +106,150 @@ static ssize_t getPlacement(Section const &section, MemoryLocation &location) {
} }
location.bank = curScrambleSRAM--; location.bank = curScrambleSRAM--;
} else { } else {
location.bank = typeInfo.firstBank; location.bank = sectionTypeInfo[section.type].firstBank;
} }
for (;;) { return location;
// Switch to the beginning of the next bank }
std::deque<FreeSpace> &bankMem = memory[section.type][location.bank - typeInfo.firstBank];
size_t spaceIdx = 0;
if (spaceIdx < bankMem.size()) { // Returns a suitable free space index into `memory[section->type]` at which to place the given
// section, or -1 if none was found.
static ssize_t getPlacement(Section const &section, MemoryLocation &location) {
SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type];
// Switch to the beginning of the next bank
std::deque<FreeSpace> &bankMem = memory[section.type][location.bank - typeInfo.firstBank];
size_t spaceIdx = 0;
if (spaceIdx < bankMem.size()) {
location.address = bankMem[spaceIdx].address;
}
// Process locations in that bank
while (spaceIdx < bankMem.size()) {
// If that location is OK, return it
if (isLocationSuitable(section, bankMem[spaceIdx], location)) {
return spaceIdx;
}
// Go to the next *possible* location
if (section.isAddressFixed) {
// If the address is fixed, there can be only one candidate block per bank;
// if we already reached it, give up.
if (location.address < section.org) {
location.address = section.org;
} else {
break; // Try again in next bank
}
} else if (section.isAlignFixed) {
// Move to next aligned location
// Move back to alignment boundary
location.address -= section.alignOfs;
// Ensure we're there (e.g. on first check)
location.address &= ~section.alignMask;
// Go to next align boundary and add offset
location.address += section.alignMask + 1 + section.alignOfs;
} else if (++spaceIdx < bankMem.size()) {
// Any location is fine, so, next free block
location.address = bankMem[spaceIdx].address; location.address = bankMem[spaceIdx].address;
} }
// Process locations in that bank // If that location is past the current block's end,
while (spaceIdx < bankMem.size()) { // go forwards until that is no longer the case.
// If that location is OK, return it while (spaceIdx < bankMem.size()
if (isLocationSuitable(section, bankMem[spaceIdx], location)) { && location.address >= bankMem[spaceIdx].address + bankMem[spaceIdx].size) {
return spaceIdx; ++spaceIdx;
}
// Go to the next *possible* location
if (section.isAddressFixed) {
// If the address is fixed, there can be only
// one candidate block per bank; if we already
// reached it, give up.
if (location.address < section.org) {
location.address = section.org;
} else {
break; // Try again in next bank
}
} else if (section.isAlignFixed) {
// Move to next aligned location
// Move back to alignment boundary
location.address -= section.alignOfs;
// Ensure we're there (e.g. on first check)
location.address &= ~section.alignMask;
// Go to next align boundary and add offset
location.address += section.alignMask + 1 + section.alignOfs;
} else if (++spaceIdx < bankMem.size()) {
// Any location is fine, so, next free block
location.address = bankMem[spaceIdx].address;
}
// If that location is past the current block's end,
// go forwards until that is no longer the case.
while (spaceIdx < bankMem.size()
&& location.address >= bankMem[spaceIdx].address + bankMem[spaceIdx].size) {
++spaceIdx;
}
// Try again with the new location/free space combo
} }
// Try again in the next bank, if one is available. // Try again with the new location/free space combo
// Try scrambled banks in descending order until no bank in the scrambled range is }
// available. Otherwise, try in ascending order.
if (section.isBankFixed) { // Try again in the next bank, if one is available.
return -1; // Try scrambled banks in descending order until no bank in the scrambled range is
} else if (options.scrambleROMX && section.type == SECTTYPE_ROMX // available. Otherwise, try in ascending order.
&& location.bank <= options.scrambleROMX) { if (section.isBankFixed) {
if (location.bank > typeInfo.firstBank) { return -1;
--location.bank; } else if (options.scrambleROMX && section.type == SECTTYPE_ROMX
} else if (options.scrambleROMX < typeInfo.lastBank) { && location.bank <= options.scrambleROMX) {
location.bank = options.scrambleROMX + 1; if (location.bank > typeInfo.firstBank) {
} else { --location.bank;
return -1; } else if (options.scrambleROMX < typeInfo.lastBank) {
} location.bank = options.scrambleROMX + 1;
} else if (options.scrambleWRAMX && section.type == SECTTYPE_WRAMX
&& location.bank <= options.scrambleWRAMX) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleWRAMX < typeInfo.lastBank) {
location.bank = options.scrambleWRAMX + 1;
} else {
return -1;
}
} else if (options.scrambleSRAM && section.type == SECTTYPE_SRAM
&& location.bank <= options.scrambleSRAM) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleSRAM < typeInfo.lastBank) {
location.bank = options.scrambleSRAM + 1;
} else {
return -1;
}
} else if (location.bank < typeInfo.lastBank) {
++location.bank;
} else { } else {
return -1; return -1;
} }
} else if (options.scrambleWRAMX && section.type == SECTTYPE_WRAMX
&& location.bank <= options.scrambleWRAMX) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleWRAMX < typeInfo.lastBank) {
location.bank = options.scrambleWRAMX + 1;
} else {
return -1;
}
} else if (options.scrambleSRAM && section.type == SECTTYPE_SRAM
&& location.bank <= options.scrambleSRAM) {
if (location.bank > typeInfo.firstBank) {
--location.bank;
} else if (options.scrambleSRAM < typeInfo.lastBank) {
location.bank = options.scrambleSRAM + 1;
} else {
return -1;
}
} else if (location.bank < typeInfo.lastBank) {
++location.bank;
} else {
return -1;
} }
return getPlacement(section, location); // Tail recursion
}
static std::string getSectionDescription(Section const &section) {
std::string where;
char bank[8], addr[8], mask[8], offset[8];
if (section.isBankFixed && nbbanks(section.type) != 1) {
snprintf(bank, sizeof(bank), "%02" PRIx32, section.bank);
}
if (section.isAddressFixed) {
snprintf(addr, sizeof(addr), "%04" PRIx16, section.org);
}
if (section.isAlignFixed) {
snprintf(mask, sizeof(mask), "%" PRIx16, static_cast<uint16_t>(~section.alignMask));
snprintf(offset, sizeof(offset), "%" PRIx16, section.alignOfs);
}
if (section.isBankFixed && nbbanks(section.type) != 1) {
if (section.isAddressFixed) {
where = "at $";
where += bank;
where += ":";
where += addr;
} else if (section.isAlignFixed) {
where = "in bank $";
where += bank;
where += " with align mask $";
where += mask;
} else {
where = "in bank $";
where += bank;
}
} else {
if (section.isAddressFixed) {
where = "at address $";
where += addr;
} else if (section.isAlignFixed) {
where = "with align mask $";
where += mask;
where += " and offset $";
where += offset;
} else {
where = "anywhere";
}
}
return where;
} }
// 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.
@@ -222,7 +270,7 @@ static void placeSection(Section &section) {
// 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; MemoryLocation location = getStartLocation(section);
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];
@@ -258,80 +306,53 @@ static void placeSection(Section &section) {
return; return;
} }
// Please adjust depending on longest message below
char where[64];
if (section.isBankFixed && nbbanks(section.type) != 1) {
if (section.isAddressFixed) {
snprintf(
where, sizeof(where), "at $%02" PRIx32 ":%04" PRIx16, section.bank, section.org
);
} else if (section.isAlignFixed) {
snprintf(
where,
sizeof(where),
"in bank $%02" PRIx32 " with align mask $%" PRIx16,
section.bank,
static_cast<uint16_t>(~section.alignMask)
);
} else {
snprintf(where, sizeof(where), "in bank $%02" PRIx32, section.bank);
}
} else {
if (section.isAddressFixed) {
snprintf(where, sizeof(where), "at address $%04" PRIx16, section.org);
} else if (section.isAlignFixed) {
snprintf(
where,
sizeof(where),
"with align mask $%" PRIx16 " and offset $%" PRIx16,
static_cast<uint16_t>(~section.alignMask),
section.alignOfs
);
} else {
strcpy(where, "anywhere");
}
}
// If a section failed to go to several places, nothing we can report
if (!section.isBankFixed || !section.isAddressFixed) { if (!section.isBankFixed || !section.isAddressFixed) {
// If a section failed to go to several places, nothing we can report
fatal( fatal(
"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 getSectionDescription(section).c_str()
); );
} } else if (section.org + section.size > endaddr(section.type) + 1) {
// 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) {
fatal( fatal(
"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)",
section.name.c_str(), section.name.c_str(),
sectionTypeInfo[section.type].name.c_str(), sectionTypeInfo[section.type].name.c_str(),
where, getSectionDescription(section).c_str(),
section.org + section.size, section.org + section.size,
endaddr(section.type) + 1 endaddr(section.type) + 1
); );
} } else {
// Otherwise there is overlap with another section // Otherwise there is overlap with another section
else {
fatal( fatal(
"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(),
sectionTypeInfo[section.type].name.c_str(), sectionTypeInfo[section.type].name.c_str(),
where, getSectionDescription(section).c_str(),
out_OverlappingSection(section)->name.c_str() out_OverlappingSection(section)->name.c_str()
); );
} }
} }
static std::deque<Section *> unassignedSections[1 << 3];
// 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;
static constexpr uint8_t ORG_CONSTRAINED = 1 << 1; static constexpr uint8_t ORG_CONSTRAINED = 1 << 1;
static constexpr uint8_t ALIGN_CONSTRAINED = 1 << 0; static constexpr uint8_t ALIGN_CONSTRAINED = 1 << 0;
// clang-format on // clang-format on
static std::deque<Section *> unassignedSections[1 << 3]; static char const * const constraintNames[] = {
"un",
"align-",
"org-",
nullptr, // align+org (impossible)
"bank-",
"bank+align-",
"bank+org-",
nullptr, // bank+align+org (impossible)
};
// Categorize a section depending on how constrained it is. // Categorize a section depending on how constrained it is.
// This is so the most-constrained sections are placed first. // This is so the most-constrained sections are placed first.
@@ -341,11 +362,10 @@ static void categorizeSection(Section &section) {
if (section.isBankFixed) { if (section.isBankFixed) {
constraints |= BANK_CONSTRAINED; constraints |= BANK_CONSTRAINED;
} }
// Can't have both!
if (section.isAddressFixed) { if (section.isAddressFixed) {
constraints |= ORG_CONSTRAINED; constraints |= ORG_CONSTRAINED;
} } else if (section.isAlignFixed) {
// Can't have both!
else if (section.isAlignFixed) {
constraints |= ALIGN_CONSTRAINED; constraints |= ALIGN_CONSTRAINED;
} }
@@ -357,68 +377,77 @@ static void categorizeSection(Section &section) {
++pos; ++pos;
} }
sections.insert(pos, &section); sections.insert(pos, &section);
}
++nbSectionsToAssign; static std::vector<Section *> checkOverlayCompat() {
std::vector<Section *> unfixedSections;
if (!options.overlayFileName) {
return unfixedSections;
}
for (uint8_t constraints = std::size(unassignedSections); constraints--;) {
if ((constraints & (BANK_CONSTRAINED | ORG_CONSTRAINED))
|| unassignedSections[constraints].empty()) {
continue;
}
for (Section *section : unassignedSections[constraints]) {
unfixedSections.push_back(section);
if (unfixedSections.size() == 10) {
return unfixedSections;
}
}
}
return unfixedSections;
} }
void assign_AssignSections() { void assign_AssignSections() {
verbosePrint("Beginning assignment...\n"); verbosePrint("Beginning assignment...\n");
// Initialize assignment // Initialize assignment
initFreeSpace(); initFreeSpace();
// Generate linked lists of sections to assign // Generate linked lists of sections to assign
nbSectionsToAssign = 0; static uint64_t nbSectionsToAssign = 0; // `static` so `sect_ForEach` callback can see it
sect_ForEach(categorizeSection); sect_ForEach([](Section &section) {
categorizeSection(section);
// Place sections, starting with the most constrained ++nbSectionsToAssign;
});
// Specially process fully-constrained sections because of overlaying
verbosePrint("Assigning bank+org-constrained...\n");
for (Section *section : unassignedSections[BANK_CONSTRAINED | ORG_CONSTRAINED]) {
placeSection(*section);
}
// If all sections were fully constrained, we have nothing left to do
if (!nbSectionsToAssign) {
return;
}
// Overlaying requires only fully-constrained sections // Overlaying requires only fully-constrained sections
verbosePrint("Assigning other sections...\n"); if (std::vector<Section *> unfixedSections = checkOverlayCompat(); !unfixedSections.empty()) {
if (options.overlayFileName) { size_t nbUnfixedSections = unfixedSections.size();
fputs("FATAL: All sections must be fixed when using an overlay file", stderr); fputs("FATAL: All sections must be fixed when using an overlay file", stderr);
uint8_t nbSections = 0; for (size_t i = 0; i < nbUnfixedSections; ++i) {
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0; fprintf(stderr, "%c \"%s\"", i == 0 ? ';' : ',', unfixedSections[i]->name.c_str());
constraints--) {
for (Section *section : unassignedSections[constraints]) {
fprintf(stderr, "%c \"%s\"", nbSections == 0 ? ';' : ',', section->name.c_str());
if (++nbSections == 10) {
goto max_out; // Can't `break` out of a nested loop
}
}
} }
if (nbSectionsToAssign != nbUnfixedSections) {
max_out: fprintf(stderr, " and %" PRIu64 " more", nbSectionsToAssign - nbUnfixedSections);
if (nbSectionsToAssign != nbSections) {
fprintf(stderr, " and %" PRIu64 " more", nbSectionsToAssign - nbSections);
} }
fprintf(stderr, " %sn't\n", nbSectionsToAssign == 1 ? "is" : "are"); fprintf(stderr, " %s not\n", nbSectionsToAssign == 1 ? "is" : "are");
exit(1); exit(1);
} }
// Assign all remaining sections by decreasing constraint order // Assign sections in decreasing constraint order
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0; for (uint8_t constraints = std::size(unassignedSections); constraints--;) {
constraints--) { if (char const *constraintName = constraintNames[constraints]; constraintName) {
for (Section *section : unassignedSections[constraints]) { verbosePrint("Assigning %sconstrained sections...\n", constraintName);
placeSection(*section); } else {
assume(unassignedSections[constraints].empty());
} }
if (!nbSectionsToAssign) { for (Section *section : unassignedSections[constraints]) {
return; placeSection(*section);
// If all sections were fully constrained, we have nothing left to do
if (!--nbSectionsToAssign) {
return;
}
} }
} }
unreachable_(); // LCOV_EXCL_LINE assume(nbSectionsToAssign == 0);
} }

View File

@@ -77,7 +77,7 @@ static int64_t readLong(FILE *file) {
do { \ do { \
FILE *tmpFile = file; \ FILE *tmpFile = file; \
std::string &tmpVal = var; \ std::string &tmpVal = var; \
for (int tmpByte = getc(tmpFile); tmpByte != '\0'; tmpByte = getc(tmpFile)) { \ for (int tmpByte; (tmpByte = getc(tmpFile)) != '\0';) { \
if (tmpByte == EOF) { \ if (tmpByte == EOF) { \
fatal(__VA_ARGS__, feof(tmpFile) ? "Unexpected end of file" : strerror(errno)); \ fatal(__VA_ARGS__, feof(tmpFile) ? "Unexpected end of file" : strerror(errno)); \
} else { \ } else { \
@@ -582,15 +582,16 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
for (uint32_t i = 0; i < nbSymbols; i++) { for (uint32_t i = 0; i < nbSymbols; i++) {
if (std::holds_alternative<Label>(fileSymbols[i].data)) { if (std::holds_alternative<Label>(fileSymbols[i].data)) {
Label &label = std::get<Label>(fileSymbols[i].data); Label &label = std::get<Label>(fileSymbols[i].data);
if (Section *section = label.section; section->modifier != SECTION_NORMAL) { Section *section = label.section;
if (section->modifier == SECTION_FRAGMENT) { if (section->modifier != SECTION_NORMAL) {
// Add the fragment's offset to the symbol's
// (`section->offset` is computed by `sect_AddSection`)
label.offset += section->offset;
}
// Associate the symbol with the main section, not the "component" one // Associate the symbol with the main section, not the "component" one
label.section = sect_GetSection(section->name); label.section = sect_GetSection(section->name);
} }
if (section->modifier == SECTION_FRAGMENT) {
// Add the fragment's offset to the symbol's
// (`section->offset` is computed by `sect_AddSection`)
label.offset += section->offset;
}
} }
} }
} }

View File

@@ -183,13 +183,15 @@ static void
// Output the section itself // Output the section itself
fwrite(section->data.data(), 1, section->size, outputFile); fwrite(section->data.data(), 1, section->size, outputFile);
if (overlayFile) {
// Skip bytes even with pipes
for (uint16_t i = 0; i < section->size; i++) {
getc(overlayFile);
}
}
offset += section->size; offset += section->size;
if (!overlayFile) {
continue;
}
// Skip bytes even with pipes
for (uint16_t i = 0; i < section->size; i++) {
getc(overlayFile);
}
} }
} }
@@ -277,17 +279,18 @@ static void writeSymName(std::string const &name, FILE *file) {
uint32_t state = UTF8_ACCEPT, codepoint; uint32_t state = UTF8_ACCEPT, codepoint;
do { do {
decode(&state, &codepoint, *ptr); decode(&state, &codepoint, *ptr);
if (state == UTF8_REJECT) { if (state != UTF8_REJECT) {
// This sequence was invalid; emit a U+FFFD, and recover ++ptr;
codepoint = 0xFFFD; continue;
// Skip continuation bytes
// A NUL byte does not qualify, so we're good
while ((*ptr & 0xC0) == 0x80) {
++ptr;
}
break;
} }
++ptr; // This sequence was invalid; emit a U+FFFD, and recover
codepoint = 0xFFFD;
// Skip continuation bytes
// A NUL byte does not qualify, so we're good
while ((*ptr & 0xC0) == 0x80) {
++ptr;
}
break;
} while (state != UTF8_ACCEPT); } while (state != UTF8_ACCEPT);
fprintf(file, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32, codepoint); fprintf(file, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32, codepoint);
} }
@@ -337,7 +340,7 @@ static void writeSymBank(SortedSections const &bankSections, SectionType type, u
symList.reserve(nbSymbols); symList.reserve(nbSymbols);
forEachSortedSection(bankSections, [&](Section const &sect) { forEachSortedSection(bankSections, [&symList](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])) {

View File

@@ -426,24 +426,22 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
} }
if (value == -1) { // PC if (value == -1) { // PC
if (!patch.pcSection) { if (patch.pcSection) {
value = patch.pcOffset + patch.pcSection->org;
} else {
errorAt(patch, "PC has no value outside of a section"); errorAt(patch, "PC has no value outside of a section");
value = 0; value = 0;
isError = true; isError = true;
} else {
value = patch.pcOffset + patch.pcSection->org;
} }
} else if (Symbol const *symbol = getSymbol(fileSymbols, value); !symbol) {
errorAt(patch, "Unknown symbol \"%s\"", fileSymbols[value].name.c_str());
sym_DumpLocalAliasedSymbols(fileSymbols[value].name);
isError = true;
} else if (std::holds_alternative<Label>(symbol->data)) {
Label const &label = std::get<Label>(symbol->data);
value = label.section->org + label.offset;
} else { } else {
if (Symbol const *symbol = getSymbol(fileSymbols, value); !symbol) { value = std::get<int32_t>(symbol->data);
errorAt(patch, "Unknown symbol \"%s\"", fileSymbols[value].name.c_str());
sym_DumpLocalAliasedSymbols(fileSymbols[value].name);
isError = true;
} else if (std::holds_alternative<Label>(symbol->data)) {
Label const &label = std::get<Label>(symbol->data);
value = label.section->org + label.offset;
} else {
value = std::get<int32_t>(symbol->data);
}
} }
break; break;
} }