Skip to content

Commit 3c75bc0

Browse files
committed
Implement [[ inline fragments ]]
Fixes gbdev#500
1 parent 75f1bcd commit 3c75bc0

19 files changed

+235
-9
lines changed

include/asm/section.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ void sect_NewSection(char const *name, uint32_t secttype, uint32_t org,
4848
void sect_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
4949
struct SectionSpec const *attributes, enum SectionModifier mod);
5050
void sect_EndLoadSection(void);
51+
void sect_PushInlineFragmentSection(void);
5152

5253
struct Section *sect_GetSymbolSection(void);
5354
uint32_t sect_GetSymbolOffset(void);

src/asm/lexer.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,8 @@ static uint32_t readGfxConstant(void)
13091309

13101310
static bool startsIdentifier(int c)
13111311
{
1312+
// Anonymous labels internally start with '!'
1313+
// Section fragment labels internally start with '$'
13121314
return (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || c == '.' || c == '_';
13131315
}
13141316

@@ -1832,10 +1834,6 @@ static int yylex_NORMAL(void)
18321834
yylval.symName[1] = '\0';
18331835
return T_ID;
18341836

1835-
case '[':
1836-
return T_LBRACK;
1837-
case ']':
1838-
return T_RBRACK;
18391837
case '(':
18401838
return T_LPAREN;
18411839
case ')':
@@ -1845,6 +1843,24 @@ static int yylex_NORMAL(void)
18451843

18461844
/* Handle ambiguous 1- or 2-char tokens */
18471845

1846+
case '[': /* Either [ or [[ */
1847+
if (peek() == '[') {
1848+
shiftChar();
1849+
return T_2LBRACK;
1850+
}
1851+
return T_LBRACK;
1852+
case ']': /* Either ] or ]] */
1853+
if (peek() == ']') {
1854+
shiftChar();
1855+
/*
1856+
* [[ Inline fragments ]] inject a T_EOL token to
1857+
* end their contents even without a newline.
1858+
* Retroactively lex the ]] after it.
1859+
*/
1860+
lexerState->nextToken = T_2RBRACK;
1861+
return T_EOL;
1862+
}
1863+
return T_RBRACK;
18481864
case '*': /* Either MUL or EXP */
18491865
if (peek() == '*') {
18501866
shiftChar();

src/asm/parser.y

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "platform.h" // strncasecmp, strdup
3838

3939
static struct CaptureBody captureBody; /* Captures a REPT/FOR or MACRO */
40+
static uint32_t inlineFragmentID = 0; /* Incrementing unique ID for inline fragment labels */
4041

4142
static void upperstring(char *dest, char const *src)
4243
{
@@ -507,6 +508,7 @@ enum {
507508
%token T_COMMA ","
508509
%token T_COLON ":"
509510
%token T_LBRACK "[" T_RBRACK "]"
511+
%token T_2LBRACK "[[" T_2RBRACK "]]"
510512
%token T_LPAREN "(" T_RPAREN ")"
511513
%token T_NEWLINE "newline"
512514

@@ -569,6 +571,7 @@ enum {
569571
%type <symName> redef_id
570572
%type <symName> scoped_id
571573
%type <symName> scoped_anon_id
574+
%type <symName> inline_fragment
572575
%token T_POP_EQU "EQU"
573576
%token T_POP_SET "SET"
574577
%token T_POP_EQUAL "="
@@ -660,6 +663,7 @@ enum {
660663
%type <expr> op_mem_ind
661664
%type <assertType> assert_type
662665

666+
%token T_EOL "end of line"
663667
%token T_EOB "end of buffer"
664668
%token T_EOF 0 "end of file"
665669
%start asmfile
@@ -673,7 +677,7 @@ lines : %empty
673677
| lines line
674678
;
675679

676-
endofline : T_NEWLINE | T_EOB
680+
endofline : T_NEWLINE | T_EOL | T_EOB
677681
;
678682

679683
plain_directive : label
@@ -1359,14 +1363,25 @@ reloc_16bit : relocexpr {
13591363
rpn_CheckNBit(&$1, 16);
13601364
$$ = $1;
13611365
}
1366+
| inline_fragment { rpn_Symbol(&$$, $1); }
13621367
;
13631368

13641369
reloc_16bit_no_str : relocexpr_no_str {
13651370
rpn_CheckNBit(&$1, 16);
13661371
$$ = $1;
13671372
}
1373+
| inline_fragment { rpn_Symbol(&$$, $1); }
13681374
;
13691375

1376+
inline_fragment : T_2LBRACK {
1377+
sect_PushInlineFragmentSection();
1378+
sprintf($<symName>$, "$%" PRIu32, inlineFragmentID++);
1379+
sym_AddLabel($<symName>$);
1380+
} asmfile T_2RBRACK {
1381+
sect_PopSection();
1382+
strcpy($$, $<symName>2);
1383+
}
1384+
;
13701385

13711386
relocexpr : relocexpr_no_str
13721387
| string {

src/asm/rgbasm.5

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,70 @@ first, followed by the one from
850850
and the one from
851851
.Ql bar.o
852852
last.
853+
.Ss Inline Fragments
854+
Inline fragments are useful for short blocks of code or data that are only referenced once.
855+
They are section fragments created by surrounding instructions or directives with
856+
.Ql [[
857+
double brackets
858+
.Ql ]] ,
859+
without a separate
860+
.Ic SECTION FRAGMENT
861+
declaration.
862+
.Pp
863+
The content of an inline fragment becomes a
864+
.Ic SECTION FRAGMENT ,
865+
sharing the same name and bank as its parent ROM section, but without any other constraints.
866+
The parent section also becomes a
867+
.Ic FRAGMENT
868+
if it was not one already, so that it can be merged with its inline fragments.
869+
RGBLINK merges the fragments in no particular order.
870+
.Pp
871+
An inline fragment can take the place of any 16-bit integer constant
872+
.Ql n16
873+
from the
874+
.Xr gbz80 7
875+
documentation, as well as a
876+
.Ic DW
877+
item.
878+
The inline fragment then evaluates to its starting address.
879+
For example, you can
880+
.Ic CALL
881+
or
882+
.Ic JP
883+
to an inline fragment.
884+
.Pp
885+
This code using named labels:
886+
.Bd -literal -offset indent
887+
FortyTwo:
888+
call Sub1
889+
jp Sub2
890+
Sub1:
891+
ld a, [Twenty]
892+
ret
893+
Sub2:
894+
inc a
895+
add a
896+
ret
897+
Twenty: db 20
898+
dw FortyTwo
899+
.Ed
900+
.Pp
901+
is equivalent to this code using inline fragments:
902+
.Bd -literal -offset indent
903+
dw [[
904+
call [[
905+
ld a, [ [[db 20]] ]
906+
ret
907+
]]
908+
jp [[
909+
inc a
910+
add a
911+
ret
912+
]]
913+
]]
914+
.Ed
915+
.Pp
916+
The difference is that the example using inline fragments does not declare a particular order for its pieces.
853917
.Sh SYMBOLS
854918
RGBDS supports several types of symbols:
855919
.Bl -hang

src/asm/section.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,40 @@ void sect_EndLoadSection(void)
466466
currentLoadSection = NULL;
467467
}
468468

469+
void sect_PushInlineFragmentSection(void)
470+
{
471+
if (!checkcodesection())
472+
return;
473+
474+
if (currentLoadSection)
475+
fatalerror("`LOAD` blocks cannot contain inline fragments\n");
476+
477+
struct Section *sect = currentSection;
478+
479+
// SECTION UNION (RAM-only) is incompatible with SECTION FRAGMENT (ROM-only)
480+
if (sect->modifier == SECTION_UNION)
481+
fatalerror("`SECTION UNION` cannot contain inline fragments\n");
482+
483+
// A section containing an inline fragment has to become a fragment too
484+
sect->modifier = SECTION_FRAGMENT;
485+
486+
sect_PushSection();
487+
488+
// `SECTION "...", ROM0, BANK[0]` is not allowed
489+
uint32_t bank = sect->bank == 0 ? -1 : sect->bank;
490+
491+
struct Section *newSect = createSection(sect->name, sect->type, -1, bank, 0, 0,
492+
SECTION_FRAGMENT);
493+
494+
// Add the new section fragment to the list (after the section containing it)
495+
newSect->next = sect->next;
496+
sect->next = newSect;
497+
498+
changeSection();
499+
curOffset = newSect->size;
500+
currentSection = newSect;
501+
}
502+
469503
struct Section *sect_GetSymbolSection(void)
470504
{
471505
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: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
SECTION "1", ROM0[0]
2+
3+
VERSION EQU $11
4+
GetVersion::
5+
ld a, [ [[db VERSION]] ]
6+
ret
7+
8+
SECTION "2", ROM0, ALIGN[4]
9+
10+
text: MACRO
11+
db \1, 0
12+
ENDM
13+
14+
text_pointer: MACRO
15+
dw [[ text \1 ]]
16+
ENDM
17+
18+
GetText::
19+
ld hl, [[
20+
dw [[ db "Alpha", 0 ]]
21+
dw [[ text "Beta" ]]
22+
text_pointer "Gamma"
23+
dw 0
24+
]]
25+
ld c, a
26+
ld b, 0
27+
add hl, bc
28+
add hl, bc
29+
ld a, [hli]
30+
ld h, [hl]
31+
ld l, a
32+
ret
33+
34+
SECTION "C", ROM0
35+
36+
Foo::
37+
call [[ jp [[ jp [[ ret ]] ]] ]]
38+
call [[
39+
Label::
40+
call GetVersion
41+
MYTEXT EQU 3
42+
ld a, MYTEXT
43+
call GetText
44+
ld b, h
45+
ld c, l
46+
ret
47+
]]
48+
jp [[
49+
Bar:
50+
inc hl
51+
.loop
52+
halt
53+
: dec l
54+
jr nz, :-
55+
dec h
56+
jr nz, .loop
57+
ret
58+
]]

test/asm/inline-fragments.err

Whitespace-only changes.

test/asm/inline-fragments.out

Whitespace-only changes.

test/asm/inline-fragments.out.bin

92 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)