mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
@@ -115,6 +115,8 @@ int32_t sym_GetValue(struct Symbol const *sym);
|
||||
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);
|
||||
|
||||
@@ -1045,6 +1045,21 @@ static void readLineContinuation(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 @@ static int yylex_NORMAL(void)
|
||||
yylval.tzSym[1] = '\0';
|
||||
return T_ID;
|
||||
|
||||
/* Handle accepted single chars */
|
||||
|
||||
case '[':
|
||||
return T_LBRACK;
|
||||
case ']':
|
||||
@@ -1580,8 +1593,6 @@ static int yylex_NORMAL(void)
|
||||
return T_RPAREN;
|
||||
case ',':
|
||||
return T_COMMA;
|
||||
case ':':
|
||||
return T_COLON;
|
||||
|
||||
/* Handle ambiguous 1- or 2-char tokens */
|
||||
char secondChar;
|
||||
@@ -1639,6 +1650,16 @@ static int yylex_NORMAL(void)
|
||||
}
|
||||
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 */
|
||||
|
||||
case '$':
|
||||
|
||||
@@ -262,7 +262,9 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
|
||||
%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 @@ endc : T_POP_ENDC T_NEWLINE {
|
||||
}
|
||||
;
|
||||
|
||||
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 : relocexpr_no_str
|
||||
}
|
||||
;
|
||||
|
||||
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,14 +985,14 @@ relocexpr_no_str : scoped_id { rpn_Symbol(&$$, $1); }
|
||||
| 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);
|
||||
}
|
||||
| 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);
|
||||
|
||||
@@ -800,6 +800,33 @@ must be the actual current scope.
|
||||
.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
|
||||
|
||||
@@ -515,11 +515,60 @@ struct Symbol *sym_AddLabel(char const *name)
|
||||
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 @@ void sym_Init(void)
|
||||
#undef addString
|
||||
|
||||
labelScope = NULL;
|
||||
anonLabelID = 0;
|
||||
|
||||
math_DefinePI();
|
||||
}
|
||||
|
||||
18
test/asm/anon-label-bad.asm
Normal file
18
test/asm/anon-label-bad.asm
Normal file
@@ -0,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
|
||||
7
test/asm/anon-label-bad.err
Normal file
7
test/asm/anon-label-bad.err
Normal file
@@ -0,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)!
|
||||
0
test/asm/anon-label-bad.out
Normal file
0
test/asm/anon-label-bad.out
Normal file
12
test/asm/anon-label.asm
Normal file
12
test/asm/anon-label.asm
Normal file
@@ -0,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
|
||||
0
test/asm/anon-label.err
Normal file
0
test/asm/anon-label.err
Normal file
0
test/asm/anon-label.out
Normal file
0
test/asm/anon-label.out
Normal file
BIN
test/asm/anon-label.out.bin
Normal file
BIN
test/asm/anon-label.out.bin
Normal file
Binary file not shown.
Reference in New Issue
Block a user