shithub: rgbds

Download patch

ref: b0ec8468e65c1fdc17102fcfd29747ef29535f4f
parent: e82ad21704bf4df1806195d30d8fa2bf65f0d812
author: ISSOtm <[email protected]>
date: Tue Apr 7 20:40:41 EDT 2020

Allow specifying offset in addition to alignment

--- a/include/asm/section.h
+++ b/include/asm/section.h
@@ -22,7 +22,8 @@
 	uint32_t size;
 	uint32_t nOrg;
 	uint32_t nBank;
-	uint32_t nAlign;
+	uint8_t nAlign;
+	uint16_t alignOfs;
 	struct Section *pNext;
 	struct Patch *pPatches;
 	uint8_t *tData;
@@ -30,7 +31,8 @@
 
 struct SectionSpec {
 	uint32_t bank;
-	uint32_t alignment;
+	uint8_t alignment;
+	uint16_t alignOfs;
 };
 
 struct Section *out_FindSectionByName(const char *pzName);
--- a/include/link/section.h
+++ b/include/link/section.h
@@ -50,6 +50,7 @@
 	uint32_t bank;
 	bool isAlignFixed;
 	uint16_t alignMask;
+	uint16_t alignOfs;
 	uint8_t *data; /* Array of size `size`*/
 	uint32_t nbPatches;
 	struct Patch *patches;
--- a/src/asm/asmy.y
+++ b/src/asm/asmy.y
@@ -1425,6 +1425,7 @@
 
 sectunion	: /* empty */	{ $$ = false; }
 		| T_POP_UNION	{ $$ = true; }
+;
 
 sectiontype	: T_SECT_WRAM0	{ $$ = SECTTYPE_WRAM0; }
 		| T_SECT_VRAM	{ $$ = SECTTYPE_VRAM; }
@@ -1449,14 +1450,28 @@
 
 sectattrs	: /* empty */ {
 			$$.alignment = 0;
+			$$.alignOfs = 0;
 			$$.bank = -1;
 		}
 		| sectattrs ',' T_OP_ALIGN '[' uconst ']' {
-			if ($5 < 0 || $5 > 16)
-				yyerror("Alignment must be between 0 and 16 bits, not %u",
+			if ($5 > 16)
+				yyerror("Alignment must be between 0 and 16, not %u",
 					$5);
 			else
 				$$.alignment = $5;
+		}
+		| sectattrs ',' T_OP_ALIGN '[' uconst ',' uconst ']' {
+			if ($5 > 16) {
+				yyerror("Alignment must be between 0 and 16, not %u",
+					$5);
+			} else {
+				$$.alignment = $5;
+				if ($7 >= 1 << $$.alignment)
+					yyerror("Alignment offset must not be greater than alignment (%u < %u)",
+						$7, 1 << $$.alignment);
+				else
+					$$.alignOfs = $7;
+			}
 		}
 		| sectattrs ',' T_OP_BANK '[' uconst ']' {
 			/* We cannot check the validity of this now */
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -181,6 +181,7 @@
 	fputlong(pSect->nOrg, f);
 	fputlong(pSect->nBank, f);
 	fputc(pSect->nAlign, f);
+	fputlong(pSect->alignOfs, f);
 
 	if (sect_HasData(pSect->nType)) {
 		fwrite(pSect->tData, 1, pSect->size, f);
--- a/src/asm/section.c
+++ b/src/asm/section.c
@@ -85,9 +85,14 @@
  * Find a section by name and type. If it doesn't exist, create it
  */
 static struct Section *getSection(char const *pzName, enum SectionType type,
-				  uint32_t org, uint32_t bank,
-				  uint8_t alignment, bool isUnion)
+				  uint32_t org, struct SectionSpec const *attrs,
+				  bool isUnion)
 {
+#define mask(align) ((1 << (align)) - 1)
+	uint32_t bank = attrs->bank;
+	uint8_t alignment = attrs->alignment;
+	uint16_t alignOffset = attrs->alignOfs;
+
 	if (bank != -1) {
 		if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
 		 && type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
@@ -99,12 +104,18 @@
 				bankranges[type][0], bankranges[type][1]);
 	}
 
+	if (alignOffset >= 1 << alignment) {
+		yyerror("Alignment offset must not be greater than alignment (%u < %u)",
+			alignOffset, 1 << alignment);
+		alignOffset = 0;
+	}
+
 	if (alignment != 0) {
-		/* It doesn't make sense to have both set */
-		uint32_t mask = (1 << alignment) - 1;
+		/* It doesn't make sense to have both alignment and org set */
+		uint32_t mask = mask(alignment);
 
 		if (org != -1) {
-			if (org & mask)
+			if ((org - alignOffset) & mask)
 				yyerror("Section \"%s\"'s fixed address doesn't match its alignment",
 					pzName);
 			alignment = 0; /* Ignore it if it's satisfied */
@@ -153,14 +164,16 @@
 			if (sect_HasData(type))
 				fail("Cannot declare ROM sections as UNION");
 			if (org != -1) {
-				/* If neither is fixed, they must be the same */
+				/* If both are fixed, they must be the same */
 				if (pSect->nOrg != -1 && pSect->nOrg != org)
 					fail("Section \"%s\" already declared as fixed at different address $%x",
 					     pSect->pzName, pSect->nOrg);
 				else if (pSect->nAlign != 0
-				      && (((1 << pSect->nAlign) - 1) & org))
-					fail("Section \"%s\" already declared as aligned to %u bytes",
-					     pSect->pzName, 1 << pSect->nAlign);
+				      && (mask(pSect->nAlign)
+						& (org - pSect->alignOfs)))
+					fail("Section \"%s\" already declared as aligned to %u bytes (offset %u)",
+					     pSect->pzName, 1 << pSect->nAlign,
+					     pSect->alignOfs);
 				else
 					/* Otherwise, just override */
 					pSect->nOrg = org;
@@ -167,12 +180,18 @@
 			} else if (alignment != 0) {
 				/* Make sure any fixed address is compatible */
 				if (pSect->nOrg != -1) {
-					uint32_t mask = alignment - 1;
-
-					if (pSect->nOrg & mask)
+					if ((pSect->nOrg - alignOffset)
+							& mask(alignment))
 						fail("Section \"%s\" already declared as fixed at incompatible address $%x",
 						     pSect->pzName,
 						     pSect->nOrg);
+				/* Check if alignment offsets are compatible */
+				} else if ((alignOffset & mask(pSect->nAlign))
+					!= (pSect->alignOfs
+							& mask(alignment))) {
+					fail("Section \"%s\" already declared with incompatible %u-byte alignment (offset %u)",
+					     pSect->pzName, pSect->nAlign,
+					     pSect->alignOfs);
 				} else if (alignment > pSect->nAlign) {
 					/*
 					 * If the section is not fixed,
@@ -179,6 +198,7 @@
 					 * its alignment is the largest of both
 					 */
 					pSect->nAlign = alignment;
+					pSect->alignOfs = alignOffset;
 				}
 			}
 			/* If the section's bank is unspecified, override it */
@@ -239,6 +259,7 @@
 	pSect->nOrg = org;
 	pSect->nBank = bank;
 	pSect->nAlign = alignment;
+	pSect->alignOfs = alignOffset;
 	pSect->pNext = pSectionList;
 	pSect->pPatches = NULL;
 
@@ -261,6 +282,7 @@
 	pSectionList = pSect;
 
 	return pSect;
+#undef mask
 }
 
 /*
@@ -280,13 +302,12 @@
  * Set the current section by name and type
  */
 void out_NewSection(char const *pzName, uint32_t type, uint32_t org,
-		    struct SectionSpec const *attributes, bool isUnion)
+		    struct SectionSpec const *attribs, bool isUnion)
 {
 	if (currentLoadSection)
 		fatalerror("Cannot change the section within a `LOAD` block");
 
-	struct Section *pSect = getSection(pzName, type, org, attributes->bank,
-					   attributes->alignment, isUnion);
+	struct Section *pSect = getSection(pzName, type, org, attribs, isUnion);
 
 	setSection(pSect);
 	curOffset = isUnion ? 0 : pSect->size;
@@ -297,7 +318,7 @@
  * Set the current section by name and type
  */
 void out_SetLoadSection(char const *name, uint32_t type, uint32_t org,
-			struct SectionSpec const *attributes)
+			struct SectionSpec const *attribs)
 {
 	checkcodesection();
 
@@ -304,8 +325,7 @@
 	if (currentLoadSection)
 		fatalerror("`LOAD` blocks cannot be nested");
 
-	struct Section *pSect = getSection(name, type, org, attributes->bank,
-					   attributes->alignment, false);
+	struct Section *pSect = getSection(name, type, org, attribs, false);
 
 	loadOffset = curOffset;
 	curOffset = 0; /* curOffset -= loadOffset; */
--- a/src/link/assign.c
+++ b/src/link/assign.c
@@ -132,7 +132,8 @@
 	if (section->isAddressFixed && section->org != location->address)
 		return false;
 
-	if (section->isAlignFixed && location->address & section->alignMask)
+	if (section->isAlignFixed
+	 && ((location->address - section->alignOfs) & section->alignMask))
 		return false;
 
 	if (location->address < freeSpace->address)
@@ -183,8 +184,13 @@
 					space = NULL;
 			} 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;
-				location->address += section->alignMask + 1;
+				/* Go to next align boundary and add offset */
+				location->address += section->alignMask + 1
+							+ section->alignOfs;
 			} else {
 				/* Any location is fine, so, next free block */
 				space = space->next;
@@ -309,8 +315,8 @@
 		if (section->isAddressFixed)
 			snprintf(where, 64, "at address $%04x", section->org);
 		else if (section->isAlignFixed)
-			snprintf(where, 64, "with align mask %x",
-				 ~section->alignMask);
+			snprintf(where, 64, "with align mask %x and offset %u",
+				 ~section->alignMask, section->alignOfs);
 		else
 			strcpy(where, "anywhere");
 	}
--- a/src/link/object.c
+++ b/src/link/object.c
@@ -255,7 +255,7 @@
 			char const *fileName, struct Section *fileSections[])
 {
 	int32_t tmp;
-	uint8_t type;
+	uint8_t byte;
 
 	tryReadstr(section->name, file, "%s: Cannot read section name: %s",
 		   fileName);
@@ -265,24 +265,34 @@
 		errx(1, "\"%s\"'s section size (%d) is invalid", section->name,
 		     tmp);
 	section->size = tmp;
-	tryGetc(type, file, "%s: Cannot read \"%s\"'s type: %s",
+	tryGetc(byte, file, "%s: Cannot read \"%s\"'s type: %s",
 		fileName, section->name);
-	section->type = type & 0x7F;
-	section->isUnion = type >> 7;
+	section->type = byte & 0x7F;
+	section->isUnion = byte >> 7;
 	tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s",
 		    fileName, section->name);
 	section->isAddressFixed = tmp >= 0;
-	if (tmp > UINT16_MAX)
-		errx(1, "\"%s\"'s org' is too large (%d)", section->name, tmp);
+	if (tmp > UINT16_MAX) {
+		error("\"%s\"'s org is too large (%d)", section->name, tmp);
+		tmp = UINT16_MAX;
+	}
 	section->org = tmp;
 	tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s bank: %s",
 		    fileName, section->name);
 	section->isBankFixed = tmp >= 0;
 	section->bank = tmp;
-	tryGetc(tmp, file, "%s: Cannot read \"%s\"'s alignment: %s",
+	tryGetc(byte, file, "%s: Cannot read \"%s\"'s alignment: %s",
 		fileName, section->name);
-	section->isAlignFixed = tmp != 0;
-	section->alignMask = (1 << tmp) - 1;
+	section->isAlignFixed = byte != 0;
+	section->alignMask = (1 << byte) - 1;
+	tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
+		    fileName, section->name);
+	if (tmp > UINT16_MAX) {
+		error("\"%s\"'s alignment offset is too large (%d)",
+		      section->name, tmp);
+		tmp = UINT16_MAX;
+	}
+	section->alignOfs = tmp;
 
 	if (sect_HasData(section->type)) {
 		/* Ensure we never allocate 0 bytes */
--- a/src/link/section.c
+++ b/src/link/section.c
@@ -48,21 +48,28 @@
 				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",
+			if ((other->org - target->alignOfs) & target->alignMask)
+				errx(1, "Section \"%s\" is defined with conflicting %u-byte alignment (offset %u) and address $%x",
 				     other->name, target->alignMask + 1,
-				     other->org);
+				     target->alignOfs, 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",
+			if ((target->org - other->alignOfs) & other->alignMask)
+				errx(1, "Section \"%s\" is defined with conflicting address $%x and %u-byte alignment (offset %u)",
 				     other->name, target->org,
-				     other->alignMask + 1);
+				     other->alignMask + 1, other->alignOfs);
+		} else if (target->isAlignFixed
+			&& (other->alignMask & target->alignOfs)
+				 != (target->alignMask & other->alignOfs)) {
+				errx(1, "Section \"%s\" is defined with conflicting %u-byte alignment (offset %u) and %u-byte alignment (offset %u)",
+				     other->name, target->alignMask + 1,
+				     target->alignOfs, other->alignMask + 1,
+				     other->alignOfs);
 		} else if (!target->isAlignFixed
-			|| other->alignMask > target->alignMask) {
+			|| (other->alignMask > target->alignMask)) {
 			target->isAlignFixed = true;
 			target->alignMask = other->alignMask;
 		}
--- a/src/rgbds.5
+++ b/src/rgbds.5
@@ -94,6 +94,9 @@
 
     BYTE    Align ; Alignment of this section, as N bits. 0 when not specified.
 
+    LONG    Ofs   ; Offset relative to the alignment specified above.
+                  ; Must be below 1 << Align.
+
     IF      (Type == ROMX) || (Type == ROM0) ; Sections that can contain data.
 
         BYTE    Data[Size]      ; Raw data of the section.
--- a/test/link/section-union/align-conflict.out
+++ b/test/link/section-union/align-conflict.out
@@ -1,6 +1,6 @@
-error: Section "conflicting alignment" is defined with conflicting 4-byte alignment and address $cafe
+error: Section "conflicting alignment" is defined with conflicting 4-byte alignment (offset 0) and address $cafe
 ---
 ERROR: -(18):
-    Section "conflicting alignment" already declared as aligned to 4 bytes
+    Section "conflicting alignment" already declared as aligned to 4 bytes (offset 0)
 ERROR: -(18):
     Cannot create section "conflicting alignment" (1 errors)
--- /dev/null
+++ b/test/link/section-union/align-ofs-conflict.asm
@@ -1,0 +1,10 @@
+IF !DEF(SECOND)
+ATTRS equs ",ALIGN[3,7]"
+ELSE
+ATTRS equs ",ALIGN[4,14]"
+ENDC
+
+SECTION UNION "conflicting alignment", WRAM0 ATTRS
+	db
+
+	PURGE ATTRS
--- /dev/null
+++ b/test/link/section-union/align-ofs-conflict.out
@@ -1,0 +1,6 @@
+error: Section "conflicting alignment" is defined with conflicting 8-byte alignment (offset 7) and 16-byte alignment (offset 14)
+---
+ERROR: -(18):
+    Section "conflicting alignment" already declared with incompatible 3-byte alignment (offset 7)
+ERROR: -(18):
+    Cannot create section "conflicting alignment" (1 errors)
--- /dev/null
+++ b/test/link/section-union/different-ofs.asm
@@ -1,0 +1,10 @@
+IF !DEF(SECOND)
+ATTRS equs ",ALIGN[3,7]"
+ELSE
+ATTRS equs ",ALIGN[3,6]"
+ENDC
+
+SECTION UNION "conflicting alignment", WRAM0 ATTRS
+	db
+
+	PURGE ATTRS
--- /dev/null
+++ b/test/link/section-union/different-ofs.out
@@ -1,0 +1,6 @@
+error: Section "conflicting alignment" is defined with conflicting 8-byte alignment (offset 7) and 8-byte alignment (offset 6)
+---
+ERROR: -(18):
+    Section "conflicting alignment" already declared with incompatible 3-byte alignment (offset 7)
+ERROR: -(18):
+    Cannot create section "conflicting alignment" (1 errors)
--- a/test/link/section-union/good/a.asm
+++ b/test/link/section-union/good/a.asm
@@ -11,7 +11,12 @@
 	ds 5
 c1::
 
+SECTION UNION "d", SRAM,ALIGN[8,$BA]
+d1::
 
+SECTION UNION "e", SRAM[$BABE]
+
+
 SECTION "output 1", ROM0
 	dw a1,a2 ; $C00A, $C02A
 	dw b1,b2 ; $DABA, $DAB2
@@ -19,3 +24,4 @@
 
 SECTION "output 3", ROM0
 	db BANK(banked)
+	dw d1,d2 ; $ABBA, $BBBA
--- a/test/link/section-union/good/b.asm
+++ b/test/link/section-union/good/b.asm
@@ -12,6 +12,13 @@
 	ds 5
 c2::
 
+SECTION UNION "d", SRAM,ALIGN[12,$BBA]
+	ds $1000
+d2::
+
+SECTION UNION "e", SRAM,ALIGN[8,$BE]
+
+
 SECTION "output 2", ROM0
 	dw a1,a2
 	dw b1,b2