Skip to content

Commit 4fac8d0

Browse files
committed
Implement [[ inline fragments ]]
Fixes gbdev#500
1 parent d5cddb2 commit 4fac8d0

19 files changed

+247
-9
lines changed

include/asm/section.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ void sect_NewSection(char const *name, enum SectionType type, uint32_t org,
4343
void sect_SetLoadSection(char const *name, enum SectionType type, uint32_t org,
4444
struct SectionSpec const *attributes, enum SectionModifier mod);
4545
void sect_EndLoadSection(void);
46+
void sect_PushInlineFragmentSection(void);
4647

4748
struct Section *sect_GetSymbolSection(void);
4849
uint32_t sect_GetSymbolOffset(void);

man/rgbasm.5

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,70 @@ first, followed by the one from
921921
and the one from
922922
.Ql bar.o
923923
last.
924+
.Ss Inline Fragments
925+
Inline fragments are useful for short blocks of code or data that are only referenced once.
926+
They are section fragments created by surrounding instructions or directives with
927+
.Ql [[
928+
double brackets
929+
.Ql ]] ,
930+
without a separate
931+
.Ic SECTION FRAGMENT
932+
declaration.
933+
.Pp
934+
The content of an inline fragment becomes a
935+
.Ic SECTION FRAGMENT ,
936+
sharing the same name and bank as its parent ROM section, but without any other constraints.
937+
The parent section also becomes a
938+
.Ic FRAGMENT
939+
if it was not one already, so that it can be merged with its inline fragments.
940+
RGBLINK merges the fragments in no particular order.
941+
.Pp
942+
An inline fragment can take the place of any 16-bit integer constant
943+
.Ql n16
944+
from the
945+
.Xr gbz80 7
946+
documentation, as well as a
947+
.Ic DW
948+
item.
949+
The inline fragment then evaluates to its starting address.
950+
For example, you can
951+
.Ic CALL
952+
or
953+
.Ic JP
954+
to an inline fragment.
955+
.Pp
956+
This code using named labels:
957+
.Bd -literal -offset indent
958+
FortyTwo:
959+
call Sub1
960+
jp Sub2
961+
Sub1:
962+
ld a, [Twenty]
963+
ret
964+
Sub2:
965+
inc a
966+
add a
967+
ret
968+
Twenty: db 20
969+
dw FortyTwo
970+
.Ed
971+
.Pp
972+
is equivalent to this code using inline fragments:
973+
.Bd -literal -offset indent
974+
dw [[
975+
call [[
976+
ld a, [ [[db 20]] ]
977+
ret
978+
]]
979+
jp [[
980+
inc a
981+
add a
982+
ret
983+
]]
984+
]]
985+
.Ed
986+
.Pp
987+
The difference is that the example using inline fragments does not declare a particular order for its pieces.
924988
.Sh SYMBOLS
925989
RGBDS supports several types of symbols:
926990
.Bl -hang

src/asm/lexer.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ struct LexerState {
336336
uint32_t lineNo;
337337
uint32_t colNo;
338338
int lastToken;
339+
int nextToken;
339340

340341
struct IfStack *ifStack;
341342

@@ -359,6 +360,7 @@ static void initState(struct LexerState *state)
359360
state->mode = LEXER_NORMAL;
360361
state->atLineStart = true; // yylex() will init colNo due to this
361362
state->lastToken = T_EOF;
363+
state->nextToken = 0;
362364

363365
state->ifStack = NULL;
364366

@@ -1290,6 +1292,7 @@ static uint32_t readGfxConstant(void)
12901292
static bool startsIdentifier(int c)
12911293
{
12921294
// Anonymous labels internally start with '!'
1295+
// Section fragment labels internally start with '$'
12931296
return (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || c == '.' || c == '_';
12941297
}
12951298

@@ -1767,6 +1770,13 @@ static int yylex_SKIP_TO_ENDC(void); // forward declaration for yylex_NORMAL
17671770

17681771
static int yylex_NORMAL(void)
17691772
{
1773+
if (lexerState->nextToken) {
1774+
int token = lexerState->nextToken;
1775+
1776+
lexerState->nextToken = 0;
1777+
return token;
1778+
}
1779+
17701780
for (;;) {
17711781
int c = nextChar();
17721782

@@ -1790,10 +1800,6 @@ static int yylex_NORMAL(void)
17901800
yylval.symName[1] = '\0';
17911801
return T_ID;
17921802

1793-
case '[':
1794-
return T_LBRACK;
1795-
case ']':
1796-
return T_RBRACK;
17971803
case '(':
17981804
return T_LPAREN;
17991805
case ')':
@@ -1803,6 +1809,24 @@ static int yylex_NORMAL(void)
18031809

18041810
// Handle ambiguous 1- or 2-char tokens
18051811

1812+
case '[': // Either [ or [[
1813+
if (peek() == '[') {
1814+
shiftChar();
1815+
return T_2LBRACK;
1816+
}
1817+
return T_LBRACK;
1818+
1819+
case ']': // Either ] or ]]
1820+
if (peek() == ']') {
1821+
shiftChar();
1822+
// [[ Inline fragments ]] inject a T_EOL token to
1823+
// end their contents even without a newline.
1824+
// Retroactively lex the ]] after it.
1825+
lexerState->nextToken = T_2RBRACK;
1826+
return T_EOL;
1827+
}
1828+
return T_RBRACK;
1829+
18061830
case '+': // Either += or ADD
18071831
if (peek() == '=') {
18081832
shiftChar();

src/asm/parser.y

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "platform.hpp" // strncasecmp, strdup
3232

3333
static struct CaptureBody captureBody; // Captures a REPT/FOR or MACRO
34+
static uint32_t inlineFragmentID = 0; // Incrementing unique ID for inline fragment labels
3435

3536
static void upperstring(char *dest, char const *src)
3637
{
@@ -545,6 +546,7 @@ enum {
545546
%token T_COMMA ","
546547
%token T_COLON ":" T_DOUBLE_COLON "::"
547548
%token T_LBRACK "[" T_RBRACK "]"
549+
%token T_2LBRACK "[[" T_2RBRACK "]]"
548550
%token T_LPAREN "(" T_RPAREN ")"
549551
%token T_NEWLINE "newline"
550552

@@ -611,6 +613,7 @@ enum {
611613
%type <symName> redef_id
612614
%type <symName> scoped_id
613615
%type <symName> scoped_anon_id
616+
%type <symName> inline_fragment
614617
%token T_POP_EQU "EQU"
615618
%token T_POP_EQUAL "="
616619
%token T_POP_EQUS "EQUS"
@@ -709,6 +712,7 @@ enum {
709712
%type <expr> op_mem_ind
710713
%type <assertType> assert_type
711714

715+
%token T_EOL "end of line"
712716
%token T_EOB "end of buffer"
713717
%token T_EOF 0 "end of file"
714718
%start asmfile
@@ -722,7 +726,7 @@ lines : %empty
722726
| lines opt_diff_mark line
723727
;
724728

725-
endofline : T_NEWLINE | T_EOB
729+
endofline : T_NEWLINE | T_EOL | T_EOB
726730
;
727731

728732
opt_diff_mark : %empty // OK
@@ -1459,14 +1463,25 @@ reloc_16bit : relocexpr {
14591463
rpn_CheckNBit(&$1, 16);
14601464
$$ = $1;
14611465
}
1466+
| inline_fragment { rpn_Symbol(&$$, $1); }
14621467
;
14631468

14641469
reloc_16bit_no_str : relocexpr_no_str {
14651470
rpn_CheckNBit(&$1, 16);
14661471
$$ = $1;
14671472
}
1473+
| inline_fragment { rpn_Symbol(&$$, $1); }
14681474
;
14691475

1476+
inline_fragment : T_2LBRACK {
1477+
sect_PushInlineFragmentSection();
1478+
sprintf($<symName>$, "$%" PRIu32, inlineFragmentID++);
1479+
sym_AddLabel($<symName>$);
1480+
} asmfile T_2RBRACK {
1481+
sect_PopSection();
1482+
strcpy($$, $<symName>2);
1483+
}
1484+
;
14701485

14711486
relocexpr : relocexpr_no_str
14721487
| string {

src/asm/section.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,40 @@ void sect_EndLoadSection(void)
455455
sym_SetCurrentSymbolScope(currentLoadScope);
456456
}
457457

458+
void sect_PushInlineFragmentSection(void)
459+
{
460+
if (!checkcodesection())
461+
return;
462+
463+
if (currentLoadSection)
464+
fatalerror("`LOAD` blocks cannot contain inline fragments\n");
465+
466+
struct Section *sect = currentSection;
467+
468+
// SECTION UNION (RAM-only) is incompatible with SECTION FRAGMENT (ROM-only)
469+
if (sect->modifier == SECTION_UNION)
470+
fatalerror("`SECTION UNION` cannot contain inline fragments\n");
471+
472+
// A section containing an inline fragment has to become a fragment too
473+
sect->modifier = SECTION_FRAGMENT;
474+
475+
sect_PushSection();
476+
477+
// `SECTION "...", ROM0, BANK[0]` is not allowed
478+
uint32_t bank = sect->bank == 0 ? (uint32_t)-1 : sect->bank;
479+
480+
struct Section *newSect = createSection(sect->name, sect->type, -1, bank, 0, 0,
481+
SECTION_FRAGMENT);
482+
483+
// Add the new section fragment to the list (after the section containing it)
484+
newSect->next = sect->next;
485+
sect->next = newSect;
486+
487+
changeSection();
488+
curOffset = newSect->size;
489+
currentSection = newSect;
490+
}
491+
458492
struct Section *sect_GetSymbolSection(void)
459493
{
460494
return currentLoadSection ? currentLoadSection : currentSection;
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
error: code-after-endm-endr-endc.asm(6):
2-
syntax error, unexpected PRINTLN, expecting newline or end of buffer
2+
syntax error, unexpected PRINTLN, expecting newline or end of line or end of buffer
33
error: code-after-endm-endr-endc.asm(7):
44
Macro "mac" not defined
55
error: code-after-endm-endr-endc.asm(12):
6-
syntax error, unexpected PRINTLN, expecting newline or end of buffer
6+
syntax error, unexpected PRINTLN, expecting newline or end of line or end of buffer
77
error: code-after-endm-endr-endc.asm(17):
88
syntax error, unexpected PRINTLN, expecting newline
99
error: code-after-endm-endr-endc.asm(19):
10-
syntax error, unexpected PRINTLN, expecting newline or end of buffer
10+
syntax error, unexpected PRINTLN, expecting newline or end of line or end of buffer
1111
error: code-after-endm-endr-endc.asm(23):
1212
syntax error, unexpected PRINTLN, expecting newline
1313
error: code-after-endm-endr-endc.asm(25):
14-
syntax error, unexpected PRINTLN, expecting newline or end of buffer
14+
syntax error, unexpected PRINTLN, expecting newline or end of line or end of buffer
1515
error: Assembly aborted (7 errors)!

test/asm/inline-fragment-in-load.asm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
SECTION "OAMDMACode", ROM0
2+
OAMDMACode:
3+
LOAD "hOAMDMA", HRAM
4+
hOAMDMA::
5+
ldh [$ff46], a
6+
ld a, 40
7+
jp [[
8+
: dec a
9+
jr nz, :-
10+
ret
11+
]]
12+
.end
13+
ENDL
14+
OAMDMACodeEnd:

test/asm/inline-fragment-in-load.err

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FATAL: inline-fragment-in-load.asm(7):
2+
`LOAD` blocks cannot contain inline fragments

test/asm/inline-fragment-in-load.out

Whitespace-only changes.

test/asm/inline-fragment-in-ram.asm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
SECTION "RAM", WRAM0
2+
3+
wFoo:: db
4+
wBar:: ds 3
5+
println "ok"
6+
wQux:: dw [[
7+
ds 4
8+
println "inline"
9+
]]

test/asm/inline-fragment-in-ram.err

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
error: inline-fragment-in-ram.asm(6):
2+
Section 'RAM' cannot contain code or data (not ROM0 or ROMX)
3+
FATAL: inline-fragment-in-ram.asm(9):
4+
No entries in the section stack

test/asm/inline-fragment-in-ram.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ok
2+
inline

test/asm/inline-fragment-in-union.asm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
SECTION UNION "U", ROM0
2+
db $11
3+
dw [[ db $22 ]]
4+
SECTION UNION "U", ROM0
5+
db $33

test/asm/inline-fragment-in-union.err

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FATAL: inline-fragment-in-union.asm(3):
2+
`SECTION UNION` cannot contain inline fragments

test/asm/inline-fragment-in-union.out

Whitespace-only changes.

test/asm/inline-fragments.asm

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
SECTION "1", ROM0[0]
2+
3+
DEF VERSION EQU $11
4+
GetVersion::
5+
ld a, [ [[db VERSION]] ]
6+
ret
7+
8+
SECTION "2", ROM0, ALIGN[4]
9+
10+
MACRO text
11+
db \1, 0
12+
ENDM
13+
14+
MACRO text_pointer
15+
dw [[
16+
text \1
17+
]]
18+
ENDM
19+
20+
GetText::
21+
ld hl, [[
22+
dw [[ db "Alpha", 0 ]]
23+
dw [[
24+
text "Beta"
25+
]]
26+
text_pointer "Gamma"
27+
dw 0
28+
]]
29+
ld c, a
30+
ld b, 0
31+
add hl, bc
32+
add hl, bc
33+
ld a, [hli]
34+
ld h, [hl]
35+
ld l, a
36+
ret
37+
38+
SECTION "C", ROM0
39+
40+
Foo::
41+
call [[ jp [[ jp [[ ret ]] ]] ]]
42+
call [[
43+
Label::
44+
call GetVersion
45+
DEF MYTEXT EQU 3
46+
ld a, MYTEXT
47+
call GetText
48+
ld b, h
49+
ld c, l
50+
ret
51+
]]
52+
jp [[
53+
Bar:
54+
inc hl
55+
.loop
56+
nop
57+
: dec l
58+
jr nz, :-
59+
dec h
60+
jr nz, .loop
61+
ret
62+
]]

test/asm/inline-fragments.err

Whitespace-only changes.

test/asm/inline-fragments.out

Whitespace-only changes.

test/asm/inline-fragments.out.bin

89 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)