diff --git a/include/asm/section.hpp b/include/asm/section.hpp index 544a2110..d7f86fdc 100644 --- a/include/asm/section.hpp +++ b/include/asm/section.hpp @@ -42,6 +42,8 @@ struct Section { std::deque patches; std::vector data; + SectionTypeInfo const &typeInfo() const { return sectionTypeInfo[type]; } + uint32_t getID() const; // ID of the section in the object file (`UINT32_MAX` if none) bool isSizeKnown() const; }; diff --git a/include/link/section.hpp b/include/link/section.hpp index 003cc19d..b808a25e 100644 --- a/include/link/section.hpp +++ b/include/link/section.hpp @@ -51,6 +51,8 @@ struct Section { std::vector symbols; std::unique_ptr
nextPiece; // The next fragment or union "piece" of this section + SectionTypeInfo const &typeInfo() const { return sectionTypeInfo[type]; } + private: // Template class for both const and non-const iterators over the "pieces" of this section template SectionT> diff --git a/include/linkdefs.hpp b/include/linkdefs.hpp index 67a32de1..28e365b9 100644 --- a/include/linkdefs.hpp +++ b/include/linkdefs.hpp @@ -82,6 +82,13 @@ static constexpr uint8_t SECTTYPE_TYPE_MASK = 0b111; static constexpr uint8_t SECTTYPE_UNION_BIT = 7; static constexpr uint8_t SECTTYPE_FRAGMENT_BIT = 6; +// Tells whether a section has data in its object file definition, +// depending on type. +static inline bool sectTypeHasData(SectionType type) { + assume(type != SECTTYPE_INVALID); + return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX; +} + enum FileStackNodeType { NODE_REPT, NODE_FILE, @@ -90,31 +97,24 @@ enum FileStackNodeType { static constexpr uint8_t FSTACKNODE_QUIET_BIT = 7; -// Nont-`const` members may be patched in RGBLINK depending on CLI flags -extern struct SectionTypeInfo { +// Non-`const` members may be patched in RGBLINK depending on CLI flags +struct SectionTypeInfo { std::string const name; uint16_t const startAddr; uint16_t size; uint32_t const firstBank; uint32_t lastBank; -} sectionTypeInfo[SECTTYPE_INVALID]; -// Tells whether a section has data in its object file definition, -// depending on type. -static inline bool sectTypeHasData(SectionType type) { - assume(type != SECTTYPE_INVALID); - return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX; -} + // Returns a memory region's end address (last byte), e.g. 0x7FFF + uint16_t endAddr() const { return startAddr + size - 1; } -// Returns a memory region's end address (last byte), e.g. 0x7FFF -static inline uint16_t sectTypeEndAddr(SectionType type) { - return sectionTypeInfo[type].startAddr + sectionTypeInfo[type].size - 1; -} + // Returns a memory region's number of banks, or 1 for regions without banking + uint32_t nbBanks() const { return lastBank - firstBank + 1; } -// Returns a memory region's number of banks, or 1 for regions without banking -static inline uint32_t sectTypeBanks(SectionType type) { - return sectionTypeInfo[type].lastBank - sectionTypeInfo[type].firstBank + 1; -} + bool isBanked() const { return nbBanks() != 1; } +}; + +extern SectionTypeInfo sectionTypeInfo[SECTTYPE_INVALID]; enum SectionModifier { SECTION_NORMAL, SECTION_UNION, SECTION_FRAGMENT }; diff --git a/src/asm/section.cpp b/src/asm/section.cpp index 5157a6ec..8585a29a 100644 --- a/src/asm/section.cpp +++ b/src/asm/section.cpp @@ -100,7 +100,7 @@ void sect_ForEach(void (*callback)(Section &)) { void sect_CheckSizes() { for (Section const § : sections) { - if (uint32_t maxSize = sectionTypeInfo[sect.type].size; sect.size > maxSize) { + if (uint32_t maxSize = sect.typeInfo().size; sect.size > maxSize) { error( "Section \"%s\" grew too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32 ")", @@ -309,7 +309,7 @@ static void mergeSections( sectError( "Section \"%s\" already exists but with type `%s`", sect.name.c_str(), - sectionTypeInfo[sect.type].name.c_str() + sect.typeInfo().name.c_str() ); } @@ -377,8 +377,8 @@ static Section *createSection( out_RegisterNode(sect.src); // It is only needed to allocate memory for ROM sections. - if (sectTypeHasData(type)) { - sect.data.resize(sectionTypeInfo[type].size); + if (sectTypeHasData(sect.type)) { + sect.data.resize(sect.typeInfo().size); } return § @@ -403,7 +403,7 @@ static Section *createSectionFragmentLiteral(Section const &parent) { // Section fragment literals must be ROM sections. assume(sectTypeHasData(sect.type)); - sect.data.resize(sectionTypeInfo[sect.type].size); + sect.data.resize(sect.typeInfo().size); return § } @@ -424,24 +424,24 @@ static Section *getSection( uint32_t alignMask = alignSize - 1; // First, validate parameters, and normalize them if applicable + SectionTypeInfo const &typeInfo = sectionTypeInfo[type]; if (bank != UINT32_MAX) { if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX) { error("`BANK` only allowed for `ROMX`, `WRAMX`, `SRAM`, or `VRAM` sections"); - } else if (bank < sectionTypeInfo[type].firstBank - || bank > sectionTypeInfo[type].lastBank) { + } else if (bank < typeInfo.firstBank || bank > typeInfo.lastBank) { error( "%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04" PRIx32 ")", - sectionTypeInfo[type].name.c_str(), + typeInfo.name.c_str(), bank, - sectionTypeInfo[type].firstBank, - sectionTypeInfo[type].lastBank + typeInfo.firstBank, + typeInfo.lastBank ); } - } else if (sectTypeBanks(type) == 1) { + } else if (!typeInfo.isBanked()) { // If the section type only has a single bank, implicitly force it - bank = sectionTypeInfo[type].firstBank; + bank = typeInfo.firstBank; } // This should be redundant, as the parser guarantees that `AlignmentSpec` will be valid. @@ -457,14 +457,14 @@ static Section *getSection( } if (org != UINT32_MAX) { - if (org < sectionTypeInfo[type].startAddr || org > sectTypeEndAddr(type)) { + if (org < typeInfo.startAddr || org > typeInfo.endAddr()) { error( "Section \"%s\"'s fixed address $%04" PRIx32 " is outside of range [$%04" PRIx16 "; $%04" PRIx16 "]", name.c_str(), org, - sectionTypeInfo[type].startAddr, - sectTypeEndAddr(type) + typeInfo.startAddr, + typeInfo.endAddr() ); } } @@ -476,11 +476,11 @@ static Section *getSection( error("Section \"%s\"'s fixed address does not match its alignment", name.c_str()); } alignment = 0; // Ignore it if it's satisfied - } else if (sectionTypeInfo[type].startAddr & alignMask) { + } else if (typeInfo.startAddr & alignMask) { error( "Section \"%s\"'s alignment cannot be attained in %s", name.c_str(), - sectionTypeInfo[type].name.c_str() + typeInfo.name.c_str() ); alignment = 0; // Ignore it if it's unattainable org = 0; diff --git a/src/link/assign.cpp b/src/link/assign.cpp index b40c43da..ee50545e 100644 --- a/src/link/assign.cpp +++ b/src/link/assign.cpp @@ -79,7 +79,7 @@ static MemoryLocation getStartLocation(Section const §ion) { if (section.isBankFixed) { location.bank = section.bank; } else { - location.bank = sectionTypeInfo[section.type].firstBank; + location.bank = section.typeInfo().firstBank; // Scramble the bank if applicable if (options.scrambleROMX && section.type == SECTTYPE_ROMX) { @@ -106,14 +106,14 @@ static MemoryLocation getStartLocation(Section const §ion) { // Returns a suitable free space index into `memory[section->type]` at which to place the given // section, or `std::nullopt` if none was found. static std::optional getPlacement(Section const §ion, MemoryLocation &location) { - SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type]; + SectionTypeInfo const &typeInfo = section.typeInfo(); for (;;) { if (location.bank < typeInfo.firstBank || location.bank >= memory[section.type].size() + typeInfo.firstBank) { fatal( "Invalid bank for %s section \"%s\": %" PRIu32, - sectionTypeInfo[section.type].name.c_str(), + typeInfo.name.c_str(), section.name.c_str(), location.bank ); @@ -217,9 +217,8 @@ static std::optional getPlacement(Section const §ion, MemoryLocation } static std::string getSectionDescription(Section const §ion) { - std::string description = - "\"" + section.name + "\" (" + sectionTypeInfo[section.type].name + " section) "; - if (section.isBankFixed && sectTypeBanks(section.type) != 1) { + std::string description = "\"" + section.name + "\" (" + section.typeInfo().name + " section) "; + if (section.isBankFixed && section.typeInfo().isBanked()) { char bank[8]; snprintf(bank, sizeof(bank), "%02" PRIx32, section.bank); if (section.isAddressFixed) { @@ -253,14 +252,15 @@ static std::string getSectionDescription(Section const §ion) { // 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! static void placeSection(Section §ion) { + SectionTypeInfo const &typeInfo = section.typeInfo(); + // Specially handle 0-byte SECTIONs, as they can't overlap anything if (section.size == 0) { // Unless the SECTION's address was fixed, the starting address // is fine for any alignment, as checked in sect_DoSanityChecks. MemoryLocation location = { - .address = - section.isAddressFixed ? section.org : sectionTypeInfo[section.type].startAddr, - .bank = section.isBankFixed ? section.bank : sectionTypeInfo[section.type].firstBank, + .address = section.isAddressFixed ? section.org : typeInfo.startAddr, + .bank = section.isBankFixed ? section.bank : typeInfo.firstBank, }; assignSection(section, location); return; @@ -270,8 +270,7 @@ static void placeSection(Section §ion) { // https://en.wikipedia.org/wiki/Bin_packing_problem#First-fit_algorithm MemoryLocation location = getStartLocation(section); if (std::optional spaceIdx = getPlacement(section, location); spaceIdx) { - std::deque &bankMem = - memory[section.type][location.bank - sectionTypeInfo[section.type].firstBank]; + std::deque &bankMem = memory[section.type][location.bank - typeInfo.firstBank]; FreeSpace &freeSpace = bankMem[*spaceIdx]; assignSection(section, location); @@ -308,13 +307,14 @@ static void placeSection(Section §ion) { if (!section.isBankFixed || !section.isAddressFixed) { // If a section failed to go to several places, nothing we can report fatal("Unable to place %s", getSectionDescription(section).c_str()); - } else if (section.org + section.size > sectTypeEndAddr(section.type) + 1) { + } else if (uint16_t onePastEnd = typeInfo.endAddr() + 1; + section.org + section.size > onePastEnd) { // If the section just can't fit the bank, report that fatal( "Unable to place %s: section runs past end of region ($%04x > $%04x)", getSectionDescription(section).c_str(), section.org + section.size, - sectTypeEndAddr(section.type) + 1 + onePastEnd ); } else { // Otherwise there is overlap with another section @@ -430,11 +430,12 @@ void assign_AssignSections() { // Initialize the free space-modelling structs for (SectionType type : EnumSeq(SECTTYPE_INVALID)) { - memory[type].resize(sectTypeBanks(type)); + SectionTypeInfo const &typeInfo = sectionTypeInfo[type]; + memory[type].resize(typeInfo.nbBanks()); for (std::deque &bankMem : memory[type]) { bankMem.push_back({ - .address = sectionTypeInfo[type].startAddr, - .size = sectionTypeInfo[type].size, + .address = typeInfo.startAddr, + .size = typeInfo.size, }); } } diff --git a/src/link/layout.cpp b/src/link/layout.cpp index 5cee068c..5d4db1b5 100644 --- a/src/link/layout.cpp +++ b/src/link/layout.cpp @@ -31,7 +31,7 @@ static void setActiveTypeAndIdx(SectionType type, uint32_t idx) { } void layout_SetFloatingSectionType(SectionType type) { - if (sectTypeBanks(type) == 1) { + if (!sectionTypeInfo[type].isBanked()) { // There is only a single bank anyway, so just set the index to 0. setActiveTypeAndIdx(type, 0); } else { @@ -46,8 +46,8 @@ void layout_SetFloatingSectionType(SectionType type) { } void layout_SetSectionType(SectionType type) { - if (sectTypeBanks(type) != 1) { - scriptError("A bank number must be specified for %s", sectionTypeInfo[type].name.c_str()); + if (SectionTypeInfo const &typeInfo = sectionTypeInfo[type]; typeInfo.isBanked()) { + scriptError("A bank number must be specified for %s", typeInfo.name.c_str()); // Keep going with a default value for the bank index. } @@ -88,22 +88,22 @@ void layout_SetAddr(uint32_t addr) { return; } - uint16_t &pc = curAddr[activeType][activeBankIdx]; SectionTypeInfo const &typeInfo = sectionTypeInfo[activeType]; - if (addr < pc) { + if (uint16_t &pc = curAddr[activeType][activeBankIdx]; addr < pc) { scriptError("Cannot decrease the current address (from $%04x to $%04x)", pc, addr); - } else if (addr > sectTypeEndAddr(activeType)) { // Allow "one past the end" sections. + } else if (addr > typeInfo.endAddr()) { // Allow "one past the end" sections. scriptError( "Cannot set the current address to $%04" PRIx32 ": %s ends at $%04" PRIx16, addr, typeInfo.name.c_str(), - sectTypeEndAddr(activeType) + typeInfo.endAddr() ); - pc = sectTypeEndAddr(activeType); + pc = typeInfo.endAddr(); } else { pc = addr; } + isPcFloating = false; } @@ -182,7 +182,7 @@ void layout_AlignTo(uint32_t alignment, uint32_t alignOfs) { ", past $%04" PRIx16, pc, static_cast(pc + length), - static_cast(sectTypeEndAddr(activeType) + 1) + static_cast(typeInfo.endAddr() + 1) ); return; } @@ -212,7 +212,7 @@ void layout_Pad(uint32_t length) { "Cannot increase the current address by %u bytes: only %u bytes to $%04" PRIx16, length, typeInfo.size - offset, - static_cast(sectTypeEndAddr(activeType) + 1) + static_cast(typeInfo.endAddr() + 1) ); } else { pc += length; diff --git a/src/link/output.cpp b/src/link/output.cpp index 66b07133..a8098be9 100644 --- a/src/link/output.cpp +++ b/src/link/output.cpp @@ -71,7 +71,7 @@ void out_AddSection(Section const §ion) { 1, // SECTTYPE_OAM }; - uint32_t targetBank = section.bank - sectionTypeInfo[section.type].firstBank; + uint32_t targetBank = section.bank - section.typeInfo().firstBank; if (targetBank >= maxNbBanks[section.type]) { fatal( "Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")", @@ -96,7 +96,7 @@ void out_AddSection(Section const §ion) { } Section const *out_OverlappingSection(Section const §ion) { - uint32_t bank = section.bank - sectionTypeInfo[section.type].firstBank; + uint32_t bank = section.bank - section.typeInfo().firstBank; for (Section const *ptr : sections[section.type][bank].sections) { if (ptr->org < section.org + section.size && section.org < ptr->org + ptr->size) { @@ -491,12 +491,10 @@ static void writeMapBank(SortedSections const §List, SectionType type, uint3 if (used == 0) { fputs("\tEMPTY\n", mapFile); } else { - uint16_t bankEndAddr = sectionTypeInfo[type].startAddr + sectionTypeInfo[type].size; - + uint16_t bankEndAddr = sectionTypeInfo[type].endAddr() + 1; writeEmptySpace(prevEndAddr, bankEndAddr); uint16_t slack = sectionTypeInfo[type].size - used; - fprintf(mapFile, "\tTOTAL EMPTY: $%04" PRIx16 " byte%s\n", slack, slack == 1 ? "" : "s"); } } @@ -513,13 +511,12 @@ static void writeMapSummary() { continue; } - // Do not output unused section types + // Skip types which haven't been used at all. if (nbBanks == 0) { continue; } uint32_t usedTotal = 0; - for (uint32_t bank = 0; bank < nbBanks; ++bank) { usedTotal += forEachSection(sections[type][bank], [](Section const &) {}); } @@ -532,7 +529,7 @@ static void writeMapSummary() { usedTotal == 1 ? "" : "s", static_cast(nbBanks) * sectionTypeInfo[type].size - usedTotal ); - if (sectionTypeInfo[type].firstBank != sectionTypeInfo[type].lastBank) { + if (sectionTypeInfo[type].isBanked()) { fprintf(mapFile, " in %u bank%s", nbBanks, nbBanks == 1 ? "" : "s"); } putc('\n', mapFile); diff --git a/src/link/section.cpp b/src/link/section.cpp index 4be65ec8..a516556b 100644 --- a/src/link/section.cpp +++ b/src/link/section.cpp @@ -254,18 +254,20 @@ static void doSanityChecks(Section §ion) { section.isAlignFixed = false; } + // The section's type is determined now, so we can get its type info + SectionTypeInfo const &typeInfo = section.typeInfo(); + // Too large an alignment may not be satisfiable - if (section.isAlignFixed && (section.alignMask & sectionTypeInfo[section.type].startAddr)) { + if (section.isAlignFixed && (section.alignMask & typeInfo.startAddr)) { error( "Section \"%s\" has type `%s`, which cannot be aligned to $%04x bytes", section.name.c_str(), - sectionTypeInfo[section.type].name.c_str(), + typeInfo.name.c_str(), section.alignMask + 1 ); } - uint32_t minbank = sectionTypeInfo[section.type].firstBank, - maxbank = sectionTypeInfo[section.type].lastBank; + uint32_t minbank = typeInfo.firstBank, maxbank = typeInfo.lastBank; if (!bankModeError && section.isBankFixed && (section.bank < minbank || section.bank > maxbank)) { @@ -282,12 +284,12 @@ static void doSanityChecks(Section §ion) { } // Check if section has a chance to be placed - if (section.size > sectionTypeInfo[section.type].size) { + if (section.size > typeInfo.size) { error( "Section \"%s\" is bigger than the max size for that type: $%" PRIx16 " > $%" PRIx16, section.name.c_str(), section.size, - sectionTypeInfo[section.type].size + typeInfo.size ); } @@ -311,24 +313,23 @@ static void doSanityChecks(Section §ion) { } // Ensure the target address is valid - if (section.org < sectionTypeInfo[section.type].startAddr - || section.org > sectTypeEndAddr(section.type)) { + if (section.org < typeInfo.startAddr || section.org > typeInfo.endAddr()) { error( "Section \"%s\"'s fixed address $%04" PRIx16 " is outside of range [$%04" PRIx16 "; $%04" PRIx16 "]", section.name.c_str(), section.org, - sectionTypeInfo[section.type].startAddr, - sectTypeEndAddr(section.type) + typeInfo.startAddr, + typeInfo.endAddr() ); } - if (section.org + section.size > sectTypeEndAddr(section.type) + 1) { + if (section.org + section.size > typeInfo.endAddr() + 1) { error( "Section \"%s\"'s end address $%04x is greater than last address $%04x", section.name.c_str(), section.org + section.size, - sectTypeEndAddr(section.type) + 1 + typeInfo.endAddr() + 1 ); } }