shithub: rgbds

Download patch

ref: 350f40300cb2126ab1d701bb1cf21841cc930fce
parent: 7e3af4b3cd59ce6c3d059484477b80aec6deb4dc
parent: d3db5f0d76db501dfda123d1606ffbb6dfe5a6b7
author: Eldred Habert <[email protected]>
date: Sat Aug 31 00:44:58 EDT 2019

Merge pull request #403 from dbrotz/multiple-charmaps

Add support for multiple charmaps

--- a/.checkpatch.conf
+++ b/.checkpatch.conf
@@ -70,3 +70,6 @@
 
 # Don't complain when bools are used in structs
 --ignore BOOL_MEMBER
+
+# Don't complain about initializing statics (this is specific to the kernel)
+--ignore INITIALISED_STATIC
--- a/Makefile
+++ b/Makefile
@@ -57,6 +57,7 @@
 	src/asm/output.o \
 	src/asm/rpn.o \
 	src/asm/symbol.o \
+	src/asm/util.o \
 	src/extern/err.o \
 	src/extern/utf8decoder.o \
 	src/version.o
--- a/include/asm/charmap.h
+++ b/include/asm/charmap.h
@@ -25,13 +25,18 @@
 };
 
 struct Charmap {
+	char name[MAXSYMLEN + 1];
 	int32_t charCount; /* user-side count. */
 	int32_t nodeCount; /* node-side count. */
 	struct Charnode nodes[MAXCHARNODES]; /* first node is reserved for the root node in charmap. */
+	struct Charmap *next; /* next charmap in hash table bucket */
 };
 
-int32_t readUTF8Char(char *destination, char *source);
-
+void charmap_InitMain(void);
+struct Charmap *charmap_New(const char *name, const char *baseName);
+void charmap_Set(const char *name);
+void charmap_Push(void);
+void charmap_Pop(void);
 int32_t charmap_Add(char *input, uint8_t output);
 int32_t charmap_Convert(char **input);
 
--- a/include/asm/main.h
+++ b/include/asm/main.h
@@ -11,6 +11,7 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include <stdio.h>
 
 #include "helpers.h"
 
--- a/include/asm/symbol.h
+++ b/include/asm/symbol.h
@@ -51,7 +51,7 @@
 /* Symbol has a constant value, will not be changed during linking */
 #define SYMF_CONST	0x200
 
-uint32_t calchash(char *s);
+uint32_t sym_CalcHash(const char *s);
 void sym_SetExportAll(uint8_t set);
 void sym_AddLocalReloc(char *tzSym);
 void sym_AddReloc(char *tzSym);
--- /dev/null
+++ b/include/asm/util.h
@@ -1,0 +1,17 @@
+/*
+ * This file is part of RGBDS.
+ *
+ * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef RGBDS_UTIL_H
+#define RGBDS_UTIL_H
+
+#include <stdint.h>
+
+uint32_t calchash(const char *s);
+int32_t readUTF8Char(char *dest, char *src);
+
+#endif /* RGBDS_UTIL_H */
--- a/src/asm/asmy.y
+++ b/src/asm/asmy.y
@@ -26,6 +26,7 @@
 #include "asm/output.h"
 #include "asm/rpn.h"
 #include "asm/symbol.h"
+#include "asm/util.h"
 
 #include "extern/utf8decoder.h"
 
@@ -618,6 +619,10 @@
 %token	T_POP_UNION T_POP_NEXTU T_POP_ENDU
 %token	T_POP_INCBIN T_POP_REPT
 %token	T_POP_CHARMAP
+%token	T_POP_NEWCHARMAP
+%token	T_POP_SETCHARMAP
+%token	T_POP_PUSHC
+%token	T_POP_POPC
 %token	T_POP_SHIFT
 %token	T_POP_ENDR
 %token	T_POP_FAIL
@@ -771,6 +776,10 @@
 		| endu
 		| incbin
 		| charmap
+		| newcharmap
+		| setcharmap
+		| pushc
+		| popc
 		| rept
 		| shift
 		| fail
@@ -1032,6 +1041,28 @@
 				yyerror("Error parsing charmap.");
 			}
 		}
+;
+
+newcharmap	: T_POP_NEWCHARMAP T_ID
+		{
+			charmap_New($2, NULL);
+		}
+		| T_POP_NEWCHARMAP T_ID comma T_ID
+		{
+			charmap_New($2, $4);
+		}
+;
+
+setcharmap	: T_POP_SETCHARMAP T_ID
+		{
+			charmap_Set($2);
+		}
+;
+
+pushc		: T_POP_PUSHC	{ charmap_Push(); }
+;
+
+popc		: T_POP_POPC	{ charmap_Pop(); }
 ;
 
 printt		: T_POP_PRINTT string
--- a/src/asm/charmap.c
+++ b/src/asm/charmap.c
@@ -6,6 +6,7 @@
  * SPDX-License-Identifier: MIT
  */
 
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -15,30 +16,147 @@
 #include "asm/charmap.h"
 #include "asm/main.h"
 #include "asm/output.h"
+#include "asm/util.h"
 
-#include "extern/utf8decoder.h"
+#define CHARMAP_HASH_SIZE (1 << 9)
 
-struct Charmap globalCharmap = {0};
+struct CharmapStackEntry {
+	struct Charmap *charmap;
+	struct CharmapStackEntry *next;
+};
 
-int32_t readUTF8Char(char *dest, char *src)
+static struct Charmap *tHashedCharmaps[CHARMAP_HASH_SIZE];
+
+static struct Charmap *mainCharmap;
+static struct Charmap *currentCharmap;
+
+struct CharmapStackEntry *charmapStack;
+
+static void warnSectionCharmap(void)
 {
-	uint32_t state;
-	uint32_t codep;
-	int32_t i;
+	static bool warned = false;
 
-	for (i = 0, state = 0;; i++) {
-		if (decode(&state, &codep, (uint8_t)src[i]) == 1)
-			fatalerror("invalid UTF-8 character");
+	if (warned)
+		return;
 
-		dest[i] = src[i];
+	warning("Using 'charmap' within a section when the current charmap is 'main' is deprecated");
+	warned = true;
+}
 
-		if (state == 0) {
-			dest[++i] = '\0';
-			return i;
+static uint32_t charmap_CalcHash(const char *s)
+{
+	return calchash(s) % CHARMAP_HASH_SIZE;
+}
+
+static struct Charmap **charmap_Get(const char *name)
+{
+	struct Charmap **ppCharmap = &tHashedCharmaps[charmap_CalcHash(name)];
+
+	while (*ppCharmap != NULL && strcmp((*ppCharmap)->name, name))
+		ppCharmap = &(*ppCharmap)->next;
+
+	return ppCharmap;
+}
+
+static void CopyNode(struct Charmap *dest,
+		     const struct Charmap *src,
+		     int nodeIdx)
+{
+	dest->nodes[nodeIdx].code = src->nodes[nodeIdx].code;
+	dest->nodes[nodeIdx].isCode = src->nodes[nodeIdx].isCode;
+	for (int i = 0; i < 256; i++)
+		if (src->nodes[nodeIdx].next[i])
+			dest->nodes[nodeIdx].next[i] = dest->nodes +
+				(src->nodes[nodeIdx].next[i] - src->nodes);
+}
+
+struct Charmap *charmap_New(const char *name, const char *baseName)
+{
+	struct Charmap *pBase = NULL;
+
+	if (baseName != NULL) {
+		struct Charmap **ppBase = charmap_Get(baseName);
+
+		if (*ppBase == NULL) {
+			yyerror("Base charmap '%s' doesn't exist", baseName);
+			return NULL;
 		}
+
+		pBase = *ppBase;
 	}
+
+	struct Charmap **ppCharmap = charmap_Get(name);
+
+	if (*ppCharmap != NULL) {
+		yyerror("Charmap '%s' already exists", name);
+		return NULL;
+	}
+
+	*ppCharmap = calloc(1, sizeof(struct Charmap));
+
+	if (*ppCharmap == NULL)
+		fatalerror("Not enough memory for charmap");
+
+	struct Charmap *pCharmap = *ppCharmap;
+
+	snprintf(pCharmap->name, sizeof(pCharmap->name), "%s", name);
+
+	if (pBase != NULL) {
+		pCharmap->charCount = pBase->charCount;
+		pCharmap->nodeCount = pBase->nodeCount;
+
+		for (int i = 0; i < MAXCHARNODES; i++)
+			CopyNode(pCharmap, pBase, i);
+	}
+
+	currentCharmap = pCharmap;
+
+	return pCharmap;
 }
 
+void charmap_Set(const char *name)
+{
+	struct Charmap **ppCharmap = charmap_Get(name);
+
+	if (*ppCharmap == NULL) {
+		yyerror("Charmap '%s' doesn't exist", name);
+		return;
+	}
+
+	currentCharmap = *ppCharmap;
+}
+
+void charmap_Push(void)
+{
+	struct CharmapStackEntry *stackEntry;
+
+	stackEntry = malloc(sizeof(struct CharmapStackEntry));
+	if (stackEntry == NULL)
+		fatalerror("No memory for charmap stack");
+
+	stackEntry->charmap = currentCharmap;
+	stackEntry->next = charmapStack;
+
+	charmapStack = stackEntry;
+}
+
+void charmap_Pop(void)
+{
+	if (charmapStack == NULL)
+		fatalerror("No entries in the charmap stack");
+
+	struct CharmapStackEntry *top = charmapStack;
+
+	currentCharmap = top->charmap;
+	charmapStack = top->next;
+	free(top);
+}
+
+void charmap_InitMain(void)
+{
+	mainCharmap = charmap_New("main", NULL);
+}
+
 int32_t charmap_Add(char *input, uint8_t output)
 {
 	int32_t i;
@@ -47,7 +165,15 @@
 	struct Charmap 	*charmap;
 	struct Charnode	*curr_node, *temp_node;
 
-	if (pCurrentSection) {
+	/*
+	 * If the user tries to define a character mapping inside a section
+	 * and the current global charmap is the "main" one, then a local
+	 * section charmap will be created or modified instead of the global
+	 * one. In other words, the local section charmap can override the
+	 * main global one, but not the others.
+	 */
+	if (pCurrentSection && currentCharmap == mainCharmap) {
+		warnSectionCharmap();
 		if (pCurrentSection->charmap) {
 			charmap = pCurrentSection->charmap;
 		} else {
@@ -57,7 +183,7 @@
 			pCurrentSection->charmap = charmap;
 		}
 	} else {
-		charmap = &globalCharmap;
+		charmap = currentCharmap;
 	}
 
 	if (charmap->charCount >= MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
@@ -99,10 +225,18 @@
 	int32_t i, match, length;
 	uint8_t v, foundCode;
 
-	if (pCurrentSection && pCurrentSection->charmap)
+	/*
+	 * If there is a local section charmap and the current global charmap
+	 * is the "main" one, the local one is used. Otherwise, the global
+	 * one is used. In other words, the local section charmap can override
+	 * the main global one, but not the others.
+	 */
+	if (pCurrentSection &&
+	    pCurrentSection->charmap &&
+	    currentCharmap == mainCharmap)
 		charmap = pCurrentSection->charmap;
 	else
-		charmap = &globalCharmap;
+		charmap = currentCharmap;
 
 	output = malloc(strlen(*input));
 	if (output == NULL)
--- a/src/asm/globlex.c
+++ b/src/asm/globlex.c
@@ -479,6 +479,10 @@
 
 	{"incbin", T_POP_INCBIN},
 	{"charmap", T_POP_CHARMAP},
+	{"newcharmap", T_POP_NEWCHARMAP},
+	{"setcharmap", T_POP_SETCHARMAP},
+	{"pushc", T_POP_PUSHC},
+	{"popc", T_POP_POPC},
 
 	{"fail", T_POP_FAIL},
 	{"warn", T_POP_WARN},
--- a/src/asm/main.c
+++ b/src/asm/main.c
@@ -21,6 +21,7 @@
 #include "asm/lexer.h"
 #include "asm/output.h"
 #include "asm/main.h"
+#include "asm/charmap.h"
 
 #include "extern/err.h"
 
@@ -437,6 +438,7 @@
 	sym_SetExportAll(CurrentOptions.exportall);
 	fstk_Init(tzMainfile);
 	opt_ParseDefines();
+	charmap_InitMain();
 
 	yy_set_state(LEX_STATE_NORMAL);
 	opt_SetCurrentOptions(&DefaultOptions);
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -321,7 +321,7 @@
 	struct PatchSymbol *pPSym, **ppPSym;
 	uint32_t hash;
 
-	hash = calchash(pSym->tzName);
+	hash = sym_CalcHash(pSym->tzName);
 	ppPSym = &(tHashedPatchSymbols[hash]);
 
 	while ((*ppPSym) != NULL) {
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -1140,9 +1140,30 @@
 CHARMAP "A", 128
 .Ed
 .Pp
+It is possible to create multiple character maps and then switch between them
+as desired. This can be used to encode debug information in ASCII and use
+a different encoding for other purposes, for example. Initially, there is
+one character map called
+.Sy main
+and it is automatically selected as the current character map from the
+beginning. There is also a character map stack that can be used to save and
+restore which character map is currently active.
+.Bl -column "NEWCHARMAP name, basename"
+.It Sy Command Ta Sy Meaning
+.It Ic NEWCHARMAP Ar name Ta Creates a new, empty character map called
+.Ic name .
+.It Ic NEWCHARMAP Ar name , basename Ta Creates a new character map called
+. Ic name ,
+copied from character map
+.Ic basename .
+.It Ic SETCHARMAP Ar name Ta Switch to character map Ic name .
+.It Ic PUSHC Ta Push the current character map onto the stack.
+.It Ic POPC Ta Pop a character map off the stack and switch to it.
+.El
+.Pp
 .Sy Note:
 Character maps affect all strings in the file from the point in which they are
-defined.
+defined, until switching to a different character map.
 This means that any string that the code may want to print as debug information
 will also be affected by it.
 .Pp
--- a/src/asm/symbol.c
+++ b/src/asm/symbol.c
@@ -22,6 +22,7 @@
 #include "asm/main.h"
 #include "asm/mymath.h"
 #include "asm/output.h"
+#include "asm/util.h"
 
 #include "extern/err.h"
 
@@ -90,16 +91,11 @@
 }
 
 /*
- * Calculate the hash value for a string
+ * Calculate the hash value for a symbol name
  */
-uint32_t calchash(char *s)
+uint32_t sym_CalcHash(const char *s)
 {
-	uint32_t hash = 5381;
-
-	while (*s != 0)
-		hash = (hash * 33) ^ (*s++);
-
-	return hash % HASHSIZE;
+	return calchash(s) % HASHSIZE;
 }
 
 /*
@@ -123,7 +119,7 @@
 	struct sSymbol **ppsym;
 	uint32_t hash;
 
-	hash = calchash(s);
+	hash = sym_CalcHash(s);
 	ppsym = &(tHashedSymbols[hash]);
 
 	while ((*ppsym) != NULL)
@@ -187,7 +183,7 @@
 				   s);
 	}
 
-	hash = calchash(s);
+	hash = sym_CalcHash(s);
 	ppsym = &(tHashedSymbols[hash]);
 
 	while ((*ppsym) != NULL) {
--- /dev/null
+++ b/src/asm/util.c
@@ -1,0 +1,46 @@
+/*
+ * This file is part of RGBDS.
+ *
+ * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <stdint.h>
+
+#include "asm/main.h"
+#include "asm/util.h"
+
+#include "extern/utf8decoder.h"
+
+/*
+ * Calculate the hash value for a string
+ */
+uint32_t calchash(const char *s)
+{
+	uint32_t hash = 5381;
+
+	while (*s != 0)
+		hash = (hash * 33) ^ (*s++);
+
+	return hash;
+}
+
+int32_t readUTF8Char(char *dest, char *src)
+{
+	uint32_t state;
+	uint32_t codep;
+	int32_t i;
+
+	for (i = 0, state = 0;; i++) {
+		if (decode(&state, &codep, (uint8_t)src[i]) == 1)
+			fatalerror("invalid UTF-8 character");
+
+		dest[i] = src[i];
+
+		if (state == 0) {
+			dest[++i] = '\0';
+			return i;
+		}
+	}
+}
--- a/test/asm/equ-charmap.asm
+++ b/test/asm/equ-charmap.asm
@@ -1,4 +1,4 @@
-SECTION "sec", ROM0
 	charmap "A", 1
+SECTION "sec", ROM0
 _A_ EQU "A"
 	db _A_
--- /dev/null
+++ b/test/asm/multiple-charmaps.asm
@@ -1,0 +1,104 @@
+new_: MACRO
+	IF _NARG > 1
+	printt "newcharmap \1, \2\n"
+	newcharmap \1, \2
+	ELSE
+	printt "newcharmap \1\n"
+	newcharmap \1
+	ENDC
+ENDM
+
+set_: MACRO
+	printt "setcharmap \1\n"
+	setcharmap \1
+ENDM
+
+push_: MACRO
+	printt "pushc\n"
+	pushc
+ENDM
+
+pop_: MACRO
+	printt "popc\n"
+	popc
+ENDM
+
+print: MACRO
+x = \1
+printt "{x}\n"
+ENDM
+
+printt "main charmap\n"
+
+charmap "ab", $0
+
+	print "ab"
+
+	new_ map1
+
+	print "ab"
+
+	new_ map2, main
+
+	print "ab"
+
+	set_ map1
+
+	print "ab"
+
+	new_ map3
+
+charmap "ab", $1
+
+	print "ab"
+
+	new_ map4, map3
+
+charmap "ab", $1
+charmap "cd", $2
+
+	print "ab"
+	print "cd"
+
+	set_ map3
+
+	print "ab"
+	print "cd"
+
+	set_ main
+
+SECTION "sec0", ROM0
+
+	print "ab"
+
+printt "override main charmap\n"
+charmap "ef", $3
+
+	print "ab"
+	print "ef"
+
+	set_ map1
+
+	push_
+	set_ map2
+	push_
+
+	set_ map3
+
+	print "ab"
+	print "cd"
+	print "ef"
+
+	pop_
+
+	print "ab"
+
+	pop_
+
+	print "ab"
+
+	new_ map1
+
+	set_ map5
+
+	pop_
--- /dev/null
+++ b/test/asm/multiple-charmaps.out
@@ -1,0 +1,44 @@
+warning: multiple-charmaps.asm(75):
+    Using 'charmap' within a section when the current charmap is 'main' is deprecated
+ERROR: multiple-charmaps.asm(100) -> new_(5):
+    Charmap 'map1' already exists
+ERROR: multiple-charmaps.asm(102) -> set_(2):
+    Charmap 'map5' doesn't exist
+ERROR: multiple-charmaps.asm(104) -> pop_(2):
+    No entries in the charmap stack
+main charmap
+$0
+newcharmap map1
+$6162
+newcharmap map2, main
+$0
+setcharmap map1
+$6162
+newcharmap map3
+$1
+newcharmap map4, map3
+$1
+$2
+setcharmap map3
+$1
+$6364
+setcharmap main
+$0
+override main charmap
+$6162
+$3
+setcharmap map1
+pushc
+setcharmap map2
+pushc
+setcharmap map3
+$1
+$6364
+$6566
+popc
+$0
+popc
+$6162
+newcharmap map1
+setcharmap map5
+popc
--- /dev/null
+++ b/test/asm/multiple-charmaps.out.pipe
@@ -1,0 +1,44 @@
+warning: -(75):
+    Using 'charmap' within a section when the current charmap is 'main' is deprecated
+ERROR: -(100) -> new_(5):
+    Charmap 'map1' already exists
+ERROR: -(102) -> set_(2):
+    Charmap 'map5' doesn't exist
+ERROR: -(104) -> pop_(2):
+    No entries in the charmap stack
+main charmap
+$0
+newcharmap map1
+$6162
+newcharmap map2, main
+$0
+setcharmap map1
+$6162
+newcharmap map3
+$1
+newcharmap map4, map3
+$1
+$2
+setcharmap map3
+$1
+$6364
+setcharmap main
+$0
+override main charmap
+$6162
+$3
+setcharmap map1
+pushc
+setcharmap map2
+pushc
+setcharmap map3
+$1
+$6364
+$6566
+popc
+$0
+popc
+$6162
+newcharmap map1
+setcharmap map5
+popc