ref: 8f2a894b886cb9020c7b837170d924c03cdd31d9
parent: 0e405437574b9c37f23c1ded0efc0d9b3d690931
author: ISSOtm <eldredhabert0@gmail.com>
date: Sat Dec 12 06:52:06 EST 2020
Add anonymous labels Fix #497
--- a/include/asm/symbol.h
+++ b/include/asm/symbol.h
@@ -115,6 +115,8 @@
void sym_SetExportAll(bool set);
struct Symbol *sym_AddLocalLabel(char const *symName);
struct Symbol *sym_AddLabel(char const *symName);
+struct Symbol *sym_AddAnonLabel(void);
+void sym_WriteAnonLabelName(char name[static MAXSYMLEN + 1], uint32_t ofs, bool neg);
void sym_Export(char const *symName);
struct Symbol *sym_AddEqu(char const *symName, int32_t value);
struct Symbol *sym_AddSet(char const *symName, int32_t value);
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -1045,6 +1045,21 @@
}
}
+/* Function to read an anonymous label ref */
+
+static void readAnonLabelRef(char c)
+{
+ uint32_t n = 0;
+
+ // We come here having already peeked at one char, so no need to do it again
+ do {
+ shiftChars(1);
+ n++;
+ } while (peek(0) == c);
+
+ sym_WriteAnonLabelName(yylval.tzSym, n, c == '-');
+}
+
/* Functions to lex numbers of various radixes */
static void readNumber(int radix, int32_t baseValue)
@@ -1568,8 +1583,6 @@
yylval.tzSym[1] = '\0';
return T_ID;
- /* Handle accepted single chars */
-
case '[':
return T_LBRACK;
case ']':
@@ -1580,8 +1593,6 @@
return T_RPAREN;
case ',':
return T_COMMA;
- case ':':
- return T_COLON;
/* Handle ambiguous 1- or 2-char tokens */
char secondChar;
@@ -1638,6 +1649,16 @@
return T_OP_LOGICNE;
}
return T_OP_LOGICNOT;
+
+ /* Handle colon, which may begin an anonymous label ref */
+
+ case ':':
+ c = peek(0);
+ if (c != '+' && c != '-')
+ return T_COLON;
+
+ readAnonLabelRef(c);
+ return T_ANON;
/* Handle numbers */
--- a/src/asm/parser.y
+++ b/src/asm/parser.y
@@ -262,7 +262,9 @@
%token <tzSym> T_LABEL
%token <tzSym> T_ID
%token <tzSym> T_LOCAL_ID
+%token <tzSym> T_ANON
%type <tzSym> scoped_id
+%type <tzSym> scoped_anon_id
%token T_POP_EQU
%token T_POP_SET
%token T_POP_EQUAL
@@ -423,9 +425,13 @@
}
;
-scoped_id : T_ID | T_LOCAL_ID ;
+scoped_id : T_ID | T_LOCAL_ID;
+scoped_anon_id : scoped_id | T_ANON;
label : /* empty */
+ | T_COLON {
+ sym_AddAnonLabel();
+ }
| T_LOCAL_ID {
sym_AddLocalLabel($1);
}
@@ -914,7 +920,7 @@
}
;
-relocexpr_no_str : scoped_id { rpn_Symbol(&$$, $1); }
+relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
| T_NUMBER { rpn_Number(&$$, $1); }
| T_OP_LOGICNOT relocexpr %prec NEG {
rpn_LOGNOT(&$$, &$2);
@@ -979,7 +985,7 @@
| T_OP_HIGH T_LPAREN relocexpr T_RPAREN { rpn_HIGH(&$$, &$3); }
| T_OP_LOW T_LPAREN relocexpr T_RPAREN { rpn_LOW(&$$, &$3); }
| T_OP_ISCONST T_LPAREN relocexpr T_RPAREN{ rpn_ISCONST(&$$, &$3); }
- | T_OP_BANK T_LPAREN scoped_id T_RPAREN {
+ | T_OP_BANK T_LPAREN scoped_anon_id T_RPAREN {
/* '@' is also a T_ID, it is handled here. */
rpn_BankSymbol(&$$, $3);
}
@@ -986,7 +992,7 @@
| T_OP_BANK T_LPAREN string T_RPAREN { rpn_BankSection(&$$, $3); }
| T_OP_DEF {
lexer_ToggleStringExpansion(false);
- } T_LPAREN scoped_id T_RPAREN {
+ } T_LPAREN scoped_anon_id T_RPAREN {
struct Symbol const *sym = sym_FindScopedSymbol($4);
rpn_Number(&$$, !!sym);
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -800,6 +800,33 @@
.Pp
Local labels may have whitespace before their declaration as the only exception to the rule.
.Pp
+.Sy Anonymous labels
+are useful for short blocks of code.
+They are defined like normal labels, but without a name before the colon.
+Defining one does not change the label scope (unlike global labels).
+Referencing one is done using a colon
+.Ql \&:
+followed by pluses
+.Ql +
+or minuses
+.Ql - .
+.Ic :+
+references the next one after the expression,
+.Ic :++
+the one after it, and so on.
+The logic is similar for -, just backwards.
+.Bd -literal -offset indent
+ ld hl, :++
+: ld a, [hli] ; Jumps to here
+ ldh [c], a
+ dec c
+ jr nz, :-
+ ret
+
+: ; This address referenced by "ld hl"
+ dw $7FFF, $1061, $03E0, $58A5
+.Ed
+.Pp
A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants.
However, if the section in which the label is declared has a fixed base address, its value is known at assembly time.
.Pp
--- a/src/asm/symbol.c
+++ b/src/asm/symbol.c
@@ -515,11 +515,60 @@
return sym;
}
+static uint32_t anonLabelID;
+
/*
+ * Add an anonymous label
+ */
+struct Symbol *sym_AddAnonLabel(void)
+{
+ if (anonLabelID == UINT32_MAX) {
+ error("Only %" PRIu32 " anonymous labels can be created!");
+ return NULL;
+ }
+ char name[MAXSYMLEN + 1];
+
+ sym_WriteAnonLabelName(name, 0, true); // The direction is important!!
+ anonLabelID++;
+ return addLabel(name);
+}
+
+/*
+ * Write an anonymous label's name to a buffer
+ */
+void sym_WriteAnonLabelName(char buf[static MAXSYMLEN + 1], uint32_t ofs, bool neg)
+{
+ uint32_t id = 0;
+
+ if (neg) {
+ if (ofs > anonLabelID)
+ error("Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
+ " ha%s been created so far\n",
+ ofs, anonLabelID, anonLabelID == 1 ? "s" : "ve");
+ else
+ id = anonLabelID - ofs;
+ } else {
+ ofs--; // We're referencing symbols that haven't been created yet...
+ if (ofs > UINT32_MAX - anonLabelID)
+ error("Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
+ " may still be created\n", ofs + 1, UINT32_MAX - anonLabelID);
+ else
+ id = anonLabelID + ofs;
+ }
+
+ sprintf(buf, "!%u", id);
+}
+
+/*
* Export a symbol
*/
void sym_Export(char const *symName)
{
+ if (symName[0] == '!') {
+ error("Anonymous labels cannot be exported\n");
+ return;
+ }
+
struct Symbol *sym = sym_FindScopedSymbol(symName);
/* If the symbol doesn't exist, create a ref that can be purged */
@@ -671,6 +720,7 @@
#undef addString
labelScope = NULL;
+ anonLabelID = 0;
math_DefinePI();
}
--- /dev/null
+++ b/test/asm/anon-label-bad.asm
@@ -1,0 +1,18 @@
+
+: ; Outside of section
+
+SECTION "Anonymous label errors test", ROM0
+
+ db :-- ; Reference goes too far back
+
+; Uncomment this if you're a badass with a *lot* of RAM
+; REPT 2147483647
+; :
+; ENDR
+; REPT 2147483647
+; :
+; ENDR
+; db :+ ; OK
+; db :++ ; Reference goes too far
+
+:: ; Syntax error, can't export this
--- /dev/null
+++ b/test/asm/anon-label-bad.err
@@ -1,0 +1,7 @@
+ERROR: anon-label-bad.asm(2):
+ Label "!0" created outside of a SECTION
+ERROR: anon-label-bad.asm(6):
+ Reference to anonymous label 2 before, when only 1 has been created so far
+ERROR: anon-label-bad.asm(18):
+ syntax error
+error: Assembly aborted (3 errors)!
--- /dev/null
+++ b/test/asm/anon-label.asm
@@ -1,0 +1,12 @@
+
+SECTION "Anonymous label test", ROM0[0]
+
+ ld hl, :++
+: ld a, [hli]
+ ldh [c], a
+ dec c
+ jr nz, :-
+ ret
+
+:
+ dw $7FFF, $1061, $03E0, $58A5
binary files /dev/null b/test/asm/anon-label.out.bin differ