shithub: rgbds

Download patch

ref: 6ffa751090c7c8db55fbf3cb0f7a28e90f558a56
parent: c3641321d7a71e3c4759b3e22209454a363c85bb
parent: 995265c5498cbabe86dd95026ffea03df42e02d7
author: Eldred Habert <[email protected]>
date: Thu Aug 29 16:12:32 EDT 2019

Merge pull request #390 from ISSOtm/print_types

Add "print types" to bracketed symbols

--- a/include/asm/asm.h
+++ b/include/asm/asm.h
@@ -39,6 +39,7 @@
 extern struct sSymbol *pPCSymbol;
 extern bool oDontExpandStrings;
 
-size_t symvaluetostring(char *dest, size_t maxLength, char *sym);
+size_t symvaluetostring(char *dest, size_t maxLength, char *sym,
+			const char *mode);
 
 #endif /* RGBDS_ASM_ASM_H */
--- a/src/asm/asmy.y
+++ b/src/asm/asmy.y
@@ -1,7 +1,7 @@
 /*
  * This file is part of RGBDS.
  *
- * Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
+ * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
  *
  * SPDX-License-Identifier: MIT
  */
@@ -75,7 +75,8 @@
 	out_NewAbsSection(name, secttype, org, bank);
 }
 
-size_t symvaluetostring(char *dest, size_t maxLength, char *sym)
+size_t symvaluetostring(char *dest, size_t maxLength, char *sym,
+			const char *mode)
 {
 	size_t length;
 
@@ -83,6 +84,9 @@
 		char *src = sym_GetStringValue(sym);
 		size_t i;
 
+		if (mode)
+			yyerror("Print types are only allowed for numbers");
+
 		for (i = 0; src[i] != 0; i++) {
 			if (i >= maxLength)
 				fatalerror("Symbol value too long to fit buffer");
@@ -94,8 +98,25 @@
 
 	} else {
 		uint32_t value = sym_GetConstantValue(sym);
-		int32_t fullLength = snprintf(dest, maxLength + 1, "$%X",
-					      value);
+		int32_t fullLength;
+
+		/* Special cheat for binary */
+		if (mode && !mode[0]) {
+			char binary[33]; /* 32 bits + 1 terminator */
+			char *write_ptr = binary + 32;
+			fullLength = 0;
+			binary[32] = 0;
+			do {
+				*(--write_ptr) = (value & 1) + '0';
+				value >>= 1;
+				fullLength++;
+			} while(value);
+			strncpy(dest, write_ptr, maxLength + 1);
+		} else {
+			fullLength = snprintf(dest, maxLength + 1,
+							  mode ? : "$%X",
+						      value);
+		}
 
 		if (fullLength < 0) {
 			fatalerror("snprintf encoding error");
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of RGBDS.
  *
- * Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
+ * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
  *
  * SPDX-License-Identifier: MIT
  */
@@ -599,6 +599,7 @@
 	char ch;
 	size_t i = 0;
 	size_t length, maxLength;
+	const char *mode = NULL;
 
 	for (ch = *pLexBuffer;
 	     ch != '}' && ch != '"' && ch != '\n';
@@ -612,16 +613,42 @@
 				i += length;
 			else
 				fatalerror("Illegal character escape '%c'", ch);
+		} else if (ch == ':' && !mode) { /* Only grab 1st colon */
+			/* Use a whitelist of modes, which does prevent the
+			 * use of some features such as precision,
+			 * but also avoids a security flaw
+			 */
+			const char *acceptedModes = "bxXd";
+			/* Binary isn't natively supported,
+			 * so it's handled differently
+			 */
+			static const char * const formatSpecifiers[] = {
+				"", "%x", "%X", "%d"
+			};
+			/* Prevent reading out of bounds! */
+			const char *designatedMode;
+
+			if (i != 1)
+				fatalerror("Print types are exactly 1 character long");
+
+			designatedMode = strchr(acceptedModes, sym[i - 1]);
+			if (!designatedMode)
+				fatalerror("Illegal print type '%c'",
+					   sym[i - 1]);
+			mode = formatSpecifiers[designatedMode - acceptedModes];
+			/* Begin writing the symbol again */
+			i = 0;
 		} else {
 			yylex_SymbolWriteChar(sym, i++, ch);
 		}
 	}
 
+	/* Properly terminate the string */
 	yylex_SymbolWriteChar(sym, i, 0);
 
 	/* It's assumed we're writing to a T_STRING */
 	maxLength = MAXSTRLEN - index;
-	length = symvaluetostring(&dest[index], maxLength, sym);
+	length = symvaluetostring(&dest[index], maxLength, sym, mode);
 
 	if (*pLexBuffer == '}')
 		pLexBuffer++;
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -1079,7 +1079,20 @@
 This will examine the type of the symbol and insert its value accordingly.
 If symbol is a string symbol, the symbols value is simply copied.
 If it's a numeric symbol, the value is converted to hexadecimal notation and
-inserted as a string.
+inserted as a string with a dollar prepended.
+.Pp
+It's possible to change the way numeric symbols are converted by specifying
+a print type like so:
+.Sy {d:symbol}
+Valid print types are:
+.Bl -column -offset indent
+.It Sy Print type Ta Sy Format Ta Sy Example
+.It Li d Ta Decimal Ta 42
+.It Li x Ta Lowercase hexadecimal Ta 2a
+.It Li X Ta Uppercase hexadecimal Ta 2A
+.It Li b Ta Binary Ta 101010
+.Pp
+Note that print types should only be used with numeric values, not strings.
 .Pp
 HINT: The
 .Sy {symbol}
--- /dev/null
+++ b/test/asm/bracketed-symbols.asm
@@ -1,0 +1,20 @@
+X = 42
+PRINTT "{X}\n"
+PRINTT "{x:X}\n"
+PRINTT "{X:X}\n"
+PRINTT "{d:X}\n"
+PRINTT "{b:X}\n"
+
+Y equ 1337
+PRINTT "{b:Y}\n"
+
+rsreset
+R rb 0
+PRINTT "{d:R}\n"
+
+S equs "You can't format me!"
+PRINTT "{X:S}\n"
+
+SECTION "Test", ROM0
+Label:
+PRINTT "{x:Label}\n"
--- /dev/null
+++ b/test/asm/bracketed-symbols.out
@@ -1,0 +1,12 @@
+ERROR: bracketed-symbols.asm(16):
+    Print types are only allowed for numbers
+ERROR: bracketed-symbols.asm(20):
+    Expression must have a constant value
+$2A
+2a
+2A
+42
+101010
+10100111001
+0
+You can't format me!
--- /dev/null
+++ b/test/asm/bracketed-symbols.out.pipe
@@ -1,0 +1,12 @@
+ERROR: -(16):
+    Print types are only allowed for numbers
+ERROR: -(20):
+    Expression must have a constant value
+$2A
+2a
+2A
+42
+101010
+10100111001
+0
+You can't format me!