shithub: rgbds

Download patch

ref: e123b6dec79f25a765ae0d0f960db10e14b4644d
parent: cb52ae0f26462f3069f0fec0643dd98fd0929154
author: ISSOtm <[email protected]>
date: Fri Mar 20 19:44:02 EDT 2020

Implement unionized sections in RGBLINK

--- a/include/link/section.h
+++ b/include/link/section.h
@@ -37,6 +37,7 @@
 	char *name;
 	uint16_t size;
 	enum SectionType type;
+	bool isUnion;
 	bool isAddressFixed;
 	uint16_t org;
 	bool isBankFixed;
@@ -50,6 +51,7 @@
 	struct Symbol **fileSymbols;
 	uint32_t nbSymbols;
 	struct Symbol const **symbols;
+	struct Section *nextu; /* The next "component" of this unionized sect */
 };
 
 /*
--- a/include/linkdefs.h
+++ b/include/linkdefs.h
@@ -14,7 +14,7 @@
 
 #define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu"
 #define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)9
-#define RGBDS_OBJECT_REV 2
+#define RGBDS_OBJECT_REV 3
 
 enum AssertionType {
 	ASSERT_WARN,
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -189,7 +189,7 @@
 
 	fputlong(pSect->size, f);
 
-	fputc(pSect->nType, f);
+	fputc(pSect->nType | pSect->isUnion << 7, f);
 
 	fputlong(pSect->nOrg, f);
 	fputlong(pSect->nBank, f);
--- a/src/link/object.c
+++ b/src/link/object.c
@@ -245,6 +245,7 @@
 			char const *fileName)
 {
 	int32_t tmp;
+	uint8_t type;
 
 	tryReadstr(section->name, file, "%s: Cannot read section name: %s",
 		   fileName);
@@ -254,8 +255,10 @@
 		errx(1, "\"%s\"'s section size (%d) is invalid", section->name,
 		     tmp);
 	section->size = tmp;
-	tryGetc(section->type, file, "%s: Cannot read \"%s\"'s type: %s",
+	tryGetc(type, file, "%s: Cannot read \"%s\"'s type: %s",
 		fileName, section->name);
+	section->type = type & 0x7F;
+	section->isUnion = type >> 7;
 	tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s",
 		    fileName, section->name);
 	section->isAddressFixed = tmp >= 0;
@@ -358,6 +361,14 @@
 		   fileName);
 }
 
+static inline struct Section *getMainSection(struct Section *section)
+{
+	if (section->isUnion)
+		section = sect_GetSection(section->name);
+
+	return section;
+}
+
 /**
  * Reads an object file of any supported format
  * @param fileName The filename to report for errors
@@ -448,6 +459,7 @@
 
 		if (!section)
 			err(1, "%s: Couldn't create new section", fileName);
+		section->nextu = NULL;
 		readSection(file, section, fileName);
 		section->fileSymbols = fileSymbols;
 
@@ -472,9 +484,10 @@
 		if (sectionID == -1) {
 			fileSymbols[i]->section = NULL;
 		} else {
-			fileSymbols[i]->section = fileSections[sectionID];
 			/* Give the section a pointer to the symbol as well */
 			linkSymToSect(fileSymbols[i], fileSections[sectionID]);
+			fileSymbols[i]->section =
+					getMainSection(fileSections[sectionID]);
 		}
 	}
 
--- a/src/link/patch.c
+++ b/src/link/patch.c
@@ -117,6 +117,24 @@
 	return *(*expression)++;
 }
 
+static struct Symbol const *getSymbol(struct Symbol ** const symbolList,
+				      uint32_t index, char const *fileName)
+{
+	struct Symbol const *symbol = symbolList[index];
+
+	/* If the symbol is defined elsewhere... */
+	if (symbol->type == SYMTYPE_IMPORT) {
+		struct Symbol const *symbolDefinition =
+						sym_GetSymbol(symbol->name);
+		if (!symbolDefinition)
+			errx(1, "%s: Unknown symbol \"%s\"", fileName,
+			     symbol->name);
+		symbol = symbolDefinition;
+	}
+
+	return symbol;
+}
+
 /**
  * Compute a patch's value from its RPN string.
  * @param patch The patch to compute the value of
@@ -238,19 +256,8 @@
 				value |= getRPNByte(&expression, &size,
 						    patch->fileName) << shift;
 
-			symbol = section->fileSymbols[value];
-
-			/* If the symbol is defined elsewhere... */
-			if (symbol->type == SYMTYPE_IMPORT) {
-				struct Symbol const *symbolDefinition =
-						sym_GetSymbol(symbol->name);
-				if (!symbolDefinition)
-					errx(1, "%s: Unknown symbol \"%s\"",
-					     patch->fileName, symbol->name);
-				symbol = symbolDefinition;
-			}
-
-			value = symbol->section->bank;
+			value = getSymbol(section->fileSymbols, value,
+					  patch->fileName)->section->bank;
 			break;
 
 		case RPN_BANK_SECT:
@@ -305,18 +312,9 @@
 				value |= getRPNByte(&expression, &size,
 						    patch->fileName) << shift;
 
-			symbol = section->fileSymbols[value];
+			symbol = getSymbol(section->fileSymbols, value,
+					   patch->fileName);
 
-			/* If the symbol is defined elsewhere... */
-			if (symbol->type == SYMTYPE_IMPORT) {
-				struct Symbol const *symbolDefinition =
-						sym_GetSymbol(symbol->name);
-				if (!symbolDefinition)
-					errx(1, "%s: Unknown symbol \"%s\"",
-					     patch->fileName, symbol->name);
-				symbol = symbolDefinition;
-			}
-
 			if (!strcmp(symbol->name, "@")) {
 				value = section->org + patch->offset;
 			} else {
@@ -387,10 +385,8 @@
  * @param section The section to patch
  * @param arg Ignored callback arg
  */
-static void applyPatches(struct Section *section, void *arg)
+static void applyFilePatches(struct Section *section)
 {
-	(void)arg;
-
 	if (!sect_HasData(section->type))
 		return;
 
@@ -433,6 +429,22 @@
 			}
 		}
 	}
+}
+
+/**
+ * Applies all of a section's patches, iterating over "components" of
+ * unionized sections
+ * @param section The section to patch
+ * @param arg Ignored callback arg
+ */
+static void applyPatches(struct Section *section, void *arg)
+{
+	(void)arg;
+
+	do {
+		applyFilePatches(section);
+		section = section->nextu;
+	} while (section);
 }
 
 void patch_ApplyPatches(void)
--- a/src/link/section.c
+++ b/src/link/section.c
@@ -36,17 +36,76 @@
 	hash_ForEach(sections, forEach, &callbackArg);
 }
 
+static void mergeSections(struct Section *target, struct Section *other)
+{
+	if (other->isAddressFixed) {
+		if (target->isAddressFixed) {
+			if (target->org != other->org)
+				errx(1, "Section \"%s\" is defined with conflicting addresses $%x and $%x",
+				     other->name, target->org, other->org);
+		} else if (target->isAlignFixed) {
+			if (other->org & target->alignMask)
+				errx(1, "Section \"%s\" is defined with conflicting %u-byte alignment and address $%x",
+				     other->name, target->alignMask + 1,
+				     other->org);
+		}
+		target->isAddressFixed = true;
+		target->org = other->org;
+	} else if (other->isAlignFixed) {
+		if (target->isAddressFixed) {
+			if (target->org & other->alignMask)
+				errx(1, "Section \"%s\" is defined with conflicting address $%x and %u-byte alignment",
+				     other->name, target->org,
+				     other->alignMask + 1);
+		} else if (!target->isAlignFixed
+			|| other->alignMask > target->alignMask) {
+			target->isAlignFixed = true;
+			target->alignMask = other->alignMask;
+		}
+	}
+
+	if (other->isBankFixed) {
+		if (!target->isBankFixed) {
+			target->isBankFixed = true;
+			target->bank = other->bank;
+		} else if (target->bank != other->bank) {
+			errx(1, "Section \"%s\" is defined with conflicting banks %u and %u",
+			     other->name, target->bank, other->bank);
+		}
+	}
+
+	if (other->size > target->size)
+		target->size = other->size;
+
+	target->nextu = other;
+}
+
 void sect_AddSection(struct Section *section)
 {
 	/* Check if the section already exists */
-	if (hash_GetElement(sections, section->name))
-		errx(1, "Section name \"%s\" is already in use", section->name);
+	struct Section *other = hash_GetElement(sections, section->name);
 
-	/* If not, add it */
-	bool collided = hash_AddElement(sections, section->name, section);
+	if (other) {
+		if (other->isUnion && section->isUnion) {
+			mergeSections(other, section);
+		} else if (section->isUnion || other->isUnion) {
+			errx(1, "Section \"%s\" defined as both unionized and not",
+			     section->name);
+		} else {
+			errx(1, "Section name \"%s\" is already in use",
+			     section->name);
+		}
+	} else if (section->isUnion && sect_HasData(section->type)) {
+		errx(1, "Section \"%s\" is of type %s, which cannot be unionized",
+		     section->name, typeNames[section->type]);
+	} else {
+		/* If not, add it */
+		bool collided = hash_AddElement(sections, section->name,
+						section);
 
-	if (beVerbose && collided)
-		warnx("Section hashmap collision occurred!");
+		if (beVerbose && collided)
+			warnx("Section hashmap collision occurred!");
+	}
 }
 
 struct Section *sect_GetSection(char const *name)
--- /dev/null
+++ b/test/link/section-union/a.asm
@@ -1,0 +1,21 @@
+SECTION UNION "a", WRAM0
+	ds 10
+a1:
+
+SECTION UNION "b", WRAMX,ALIGN[4]
+banked::
+	ds 10
+b1:
+
+SECTION UNION "c", HRAM[$FFC0]
+	ds 5
+c1::
+
+
+SECTION "output 1", ROM0
+	dw a1,a2 ; $C00A, $C02A
+	dw b1,b2 ; $DABA, $DAB2
+	dw c1,c2 ; $FFC5, $FFC5
+
+SECTION "output 3", ROM0
+	db BANK(banked)
--- /dev/null
+++ b/test/link/section-union/b.asm
@@ -1,0 +1,18 @@
+SECTION UNION "a", WRAM0
+a1: ; This is here to check that the two `a1` don't conflict
+	ds 42
+a2::
+
+SECTION UNION "b", WRAMX[$DAB0]
+	ds 2
+b2::
+
+SECTION UNION "c", HRAM[$FFC0]
+b1: ; Same but in different sections now
+	ds 5
+c2::
+
+SECTION "output 2", ROM0
+	dw a1,a2
+	dw b1,b2
+	dw c1,c2
binary files /dev/null b/test/link/section-union/ref.out.bin differ
--- /dev/null
+++ b/test/link/section-union/script.link
@@ -1,0 +1,9 @@
+ROM0
+	"output 1"
+	"output 2"
+	"output 3"
+WRAM0
+	"a" ; $C000
+WRAMX 1
+	ORG $DAB0
+	"b"
--- a/test/link/test.sh
+++ b/test/link/test.sh
@@ -71,12 +71,20 @@
 	fi
 done
 
-# This test does its own thing
+# These tests do their own thing
+
 $RGBASM -o $otemp high-low/a.asm
 $RGBLINK -o $gbtemp $otemp
 $RGBASM -o $otemp high-low/b.asm
 $RGBLINK -o $gbtemp2 $otemp
-i="high-low.asm" tryDiff $gbtemp $gbtemp2
+i="high-low.asm" tryCmp $gbtemp $gbtemp2
+rc=$(($? || $rc))
+
+$RGBASM -o $otemp section-union/a.asm
+$RGBASM -o $gbtemp2 section-union/b.asm
+$RGBLINK -o $gbtemp -l section-union/script.link $otemp $gbtemp2
+dd if=$gbtemp count=1 bs=$(printf %s $(wc -c < section-union/ref.out.bin)) > $otemp 2>/dev/null
+i="section-union.asm" tryCmp section-union/ref.out.bin $otemp
 rc=$(($? || $rc))
 
 rm -f $otemp $gbtemp $gbtemp2 $outtemp