shithub: rgbds

Download patch

ref: 52797b6f68bd470d538f5b12dff1c716eea4db09
parent: 5108c5643cbcab4463c831c44c83a18298edf4cc
author: Rangi <[email protected]>
date: Sat Apr 17 14:36:26 EDT 2021

Implement SIZEOF("Section") and STARTOF("Section") (#766)

Updates the object file revision to 8

Fixes #765

--- a/include/asm/rpn.h
+++ b/include/asm/rpn.h
@@ -59,6 +59,8 @@
 void rpn_BankSymbol(struct Expression *expr, char const *tzSym);
 void rpn_BankSection(struct Expression *expr, char const *tzSectionName);
 void rpn_BankSelf(struct Expression *expr);
+void rpn_SizeOfSection(struct Expression *expr, char const *tzSectionName);
+void rpn_StartOfSection(struct Expression *expr, char const *tzSectionName);
 void rpn_Free(struct Expression *expr);
 void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src);
 void rpn_CheckRST(struct Expression *expr, const struct Expression *src);
--- a/include/linkdefs.h
+++ b/include/linkdefs.h
@@ -14,7 +14,7 @@
 
 #define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
 #define RGBDS_OBJECT_VERSION_NUMBER 9U
-#define RGBDS_OBJECT_REV 7U
+#define RGBDS_OBJECT_REV 8U
 
 enum AssertionType {
 	ASSERT_WARN,
@@ -23,42 +23,44 @@
 };
 
 enum RPNCommand {
-	RPN_ADD		= 0x00,
-	RPN_SUB		= 0x01,
-	RPN_MUL		= 0x02,
-	RPN_DIV		= 0x03,
-	RPN_MOD		= 0x04,
-	RPN_UNSUB	= 0x05,
-	RPN_EXP		= 0x06,
+	RPN_ADD			= 0x00,
+	RPN_SUB			= 0x01,
+	RPN_MUL			= 0x02,
+	RPN_DIV			= 0x03,
+	RPN_MOD			= 0x04,
+	RPN_UNSUB		= 0x05,
+	RPN_EXP			= 0x06,
 
-	RPN_OR		= 0x10,
-	RPN_AND		= 0x11,
-	RPN_XOR		= 0x12,
-	RPN_UNNOT	= 0x13,
+	RPN_OR			= 0x10,
+	RPN_AND			= 0x11,
+	RPN_XOR			= 0x12,
+	RPN_UNNOT		= 0x13,
 
-	RPN_LOGAND	= 0x21,
-	RPN_LOGOR	= 0x22,
-	RPN_LOGUNNOT	= 0x23,
+	RPN_LOGAND		= 0x21,
+	RPN_LOGOR		= 0x22,
+	RPN_LOGUNNOT		= 0x23,
 
-	RPN_LOGEQ	= 0x30,
-	RPN_LOGNE	= 0x31,
-	RPN_LOGGT	= 0x32,
-	RPN_LOGLT	= 0x33,
-	RPN_LOGGE	= 0x34,
-	RPN_LOGLE	= 0x35,
+	RPN_LOGEQ		= 0x30,
+	RPN_LOGNE		= 0x31,
+	RPN_LOGGT		= 0x32,
+	RPN_LOGLT		= 0x33,
+	RPN_LOGGE		= 0x34,
+	RPN_LOGLE		= 0x35,
 
-	RPN_SHL		= 0x40,
-	RPN_SHR		= 0x41,
+	RPN_SHL			= 0x40,
+	RPN_SHR			= 0x41,
 
-	RPN_BANK_SYM	= 0x50,
-	RPN_BANK_SECT	= 0x51,
-	RPN_BANK_SELF	= 0x52,
+	RPN_BANK_SYM		= 0x50,
+	RPN_BANK_SECT		= 0x51,
+	RPN_BANK_SELF		= 0x52,
+	RPN_SIZEOF_SECT		= 0x53,
+	RPN_STARTOF_SECT	= 0x54,
 
-	RPN_HRAM	= 0x60,
-	RPN_RST         = 0x61,
+	RPN_HRAM		= 0x60,
+	RPN_RST			= 0x61,
 
-	RPN_CONST	= 0x80,
-	RPN_SYM		= 0x81
+	RPN_CONST		= 0x80,
+	RPN_SYM			= 0x81
 };
 
 enum SectionType {
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -180,6 +180,9 @@
 	{"BANK", T_OP_BANK},
 	{"ALIGN", T_OP_ALIGN},
 
+	{"SIZEOF", T_OP_SIZEOF},
+	{"STARTOF", T_OP_STARTOF},
+
 	{"ROUND", T_OP_ROUND},
 	{"CEIL", T_OP_CEIL},
 	{"FLOOR", T_OP_FLOOR},
@@ -592,7 +595,7 @@
 	uint16_t children[0x60 - ' '];
 	struct KeywordMapping const *keyword;
 /* Since the keyword structure is invariant, the min number of nodes is known at compile time */
-} keywordDict[357] = {0}; /* Make sure to keep this correct when adding keywords! */
+} keywordDict[365] = {0}; /* Make sure to keep this correct when adding keywords! */
 
 /* Convert a char into its index into the dict */
 static uint8_t dictIndex(char c)
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -354,6 +354,22 @@
 			} while (b != 0);
 			break;
 
+		case RPN_SIZEOF_SECT:
+			writebyte(RPN_SIZEOF_SECT);
+			do {
+				b = popbyte();
+				writebyte(b);
+			} while (b != 0);
+			break;
+
+		case RPN_STARTOF_SECT:
+			writebyte(RPN_STARTOF_SECT);
+			do {
+				b = popbyte();
+				writebyte(b);
+			} while (b != 0);
+			break;
+
 		default:
 			writebyte(rpndata);
 			break;
--- a/src/asm/parser.y
+++ b/src/asm/parser.y
@@ -514,6 +514,7 @@
 %token	T_OP_DEF "DEF"
 %token	T_OP_BANK "BANK"
 %token	T_OP_ALIGN "ALIGN"
+%token	T_OP_SIZEOF "SIZEOF" T_OP_STARTOF "STARTOF"
 %token	T_OP_SIN "SIN" T_OP_COS "COS" T_OP_TAN "TAN"
 %token	T_OP_ASIN "ASIN" T_OP_ACOS "ACOS" T_OP_ATAN "ATAN" T_OP_ATAN2 "ATAN2"
 %token	T_OP_FDIV "FDIV"
@@ -1419,6 +1420,8 @@
 			rpn_BankSymbol(&$$, $3);
 		}
 		| T_OP_BANK T_LPAREN string T_RPAREN	{ rpn_BankSection(&$$, $3); }
+		| T_OP_SIZEOF T_LPAREN string T_RPAREN	{ rpn_SizeOfSection(&$$, $3); }
+		| T_OP_STARTOF T_LPAREN string T_RPAREN	{ rpn_StartOfSection(&$$, $3); }
 		| T_OP_DEF {
 			lexer_ToggleStringExpansion(false);
 		} T_LPAREN scoped_anon_id T_RPAREN {
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -461,6 +461,12 @@
 The result may be constant if
 .Nm
 is able to compute it.
+.It Fn SIZEOF arg Ta Returns the size of the section named
+.Ar arg .
+The result is not constant, since only RGBLINK can compute its value.
+.It Fn STARTOF arg Ta Returns the starting address of the section named
+.Ar arg .
+The result is not constant, since only RGBLINK can compute its value.
 .It Fn DEF symbol Ta Returns TRUE (1) if
 .Ar symbol
 has been defined, FALSE (0) otherwise.
--- a/src/asm/rpn.c
+++ b/src/asm/rpn.c
@@ -201,6 +201,34 @@
 	}
 }
 
+void rpn_SizeOfSection(struct Expression *expr, char const *tzSectionName)
+{
+	rpn_Init(expr);
+
+	makeUnknown(expr, "Section \"%s\"'s size is not known", tzSectionName);
+
+	size_t nameLen = strlen(tzSectionName) + 1; /* Room for NUL! */
+	uint8_t *ptr = reserveSpace(expr, nameLen + 1);
+
+	expr->nRPNPatchSize += nameLen + 1;
+	*ptr++ = RPN_SIZEOF_SECT;
+	memcpy(ptr, tzSectionName, nameLen);
+}
+
+void rpn_StartOfSection(struct Expression *expr, char const *tzSectionName)
+{
+	rpn_Init(expr);
+
+	makeUnknown(expr, "Section \"%s\"'s start is not known", tzSectionName);
+
+	size_t nameLen = strlen(tzSectionName) + 1; /* Room for NUL! */
+	uint8_t *ptr = reserveSpace(expr, nameLen + 1);
+
+	expr->nRPNPatchSize += nameLen + 1;
+	*ptr++ = RPN_STARTOF_SECT;
+	memcpy(ptr, tzSectionName, nameLen);
+}
+
 void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
 {
 	*expr = *src;
@@ -396,6 +424,8 @@
 		case RPN_BANK_SYM:
 		case RPN_BANK_SECT:
 		case RPN_BANK_SELF:
+		case RPN_SIZEOF_SECT:
+		case RPN_STARTOF_SECT:
 		case RPN_HRAM:
 		case RPN_RST:
 		case RPN_CONST:
--- a/src/link/patch.c
+++ b/src/link/patch.c
@@ -292,6 +292,11 @@
 			break;
 
 		case RPN_BANK_SECT:
+			/*
+			 * `expression` is not guaranteed to be '\0'-terminated. If it is not,
+			 * `getRPNByte` will have a fatal internal error.
+			 * In either case, `getRPNByte` will not free `expression`.
+			 */
 			name = (char const *)expression;
 			while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
 				;
@@ -317,6 +322,44 @@
 				value = 1;
 			} else {
 				value = patch->pcSection->bank;
+			}
+			break;
+
+		case RPN_SIZEOF_SECT:
+			/* This has assumptions commented in the `RPN_BANK_SECT` case above. */
+			name = (char const *)expression;
+			while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
+				;
+
+			sect = sect_GetSection(name);
+
+			if (!sect) {
+				error(patch->src, patch->lineNo,
+				      "Requested SIZEOF() of section \"%s\", which was not found",
+				      name);
+				isError = true;
+				value = 1;
+			} else {
+				value = sect->size;
+			}
+			break;
+
+		case RPN_STARTOF_SECT:
+			/* This has assumptions commented in the `RPN_BANK_SECT` case above. */
+			name = (char const *)expression;
+			while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
+				;
+
+			sect = sect_GetSection(name);
+
+			if (!sect) {
+				error(patch->src, patch->lineNo,
+				      "Requested STARTOF() of section \"%s\", which was not found",
+				      name);
+				isError = true;
+				value = 1;
+			} else {
+				value = sect->org;
 			}
 			break;
 
--- a/src/rgbds.5
+++ b/src/rgbds.5
@@ -248,6 +248,10 @@
 .It Li $51 Ta Li BANK(section_name) ,
 a null-terminated string follows.
 .It Li $52 Ta Li Current BANK()
+.It Li $53 Ta Li SIZEOF(section_name) ,
+a null-terminated string follows.
+.It Li $54 Ta Li STARTOF(section_name) ,
+a null-terminated string follows.
 .It Li $60 Ta Li HRAMCheck .
 Checks if the value is in HRAM, ANDs it with 0xFF.
 .It Li $61 Ta Li RSTCheck .
--- /dev/null
+++ b/test/asm/section-sizeof-startof.asm
@@ -1,0 +1,8 @@
+SECTION "sect", ROMX[$4567], BANK[$23]
+	ds 42
+
+W = BANK("sect")
+X = SIZEOF("sect")
+Y = STARTOF("sect")
+
+	println "{W} {X} {Y}"
--- /dev/null
+++ b/test/asm/section-sizeof-startof.err
@@ -1,0 +1,5 @@
+ERROR: section-sizeof-startof.asm(5):
+    Expected constant expression: Section "sect"'s size is not known
+ERROR: section-sizeof-startof.asm(6):
+    Expected constant expression: Section "sect"'s start is not known
+error: Assembly aborted (2 errors)!
--- /dev/null
+++ b/test/asm/section-sizeof-startof.out
@@ -1,0 +1,1 @@
+$23 $0 $0
--- /dev/null
+++ b/test/link/sizeof-startof.asm
@@ -1,0 +1,7 @@
+SECTION "meta", ROM0[0]
+	db BANK("sect")
+	dw STARTOF("sect")
+	dw SIZEOF("sect")
+
+SECTION "sect", ROMX[$4567], BANK[$23]
+	ds 42
--- /dev/null
+++ b/test/link/sizeof-startof.out.bin
@@ -1,0 +1,1 @@
+#gE*
\ No newline at end of file