Skip to content

Commit

Permalink
Implement [[ inline fragments ]]
Browse files Browse the repository at this point in the history
Fixes gbdev#500
  • Loading branch information
Rangi42 committed Feb 28, 2021
1 parent 1dafc1c commit 0d57f45
Show file tree
Hide file tree
Showing 18 changed files with 264 additions and 40 deletions.
1 change: 1 addition & 0 deletions include/asm/section.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes,
enum SectionModifier mod);
void out_EndLoadSection(void);
void out_PushInlineFragmentSection(void);

struct Section *sect_GetSymbolSection(void);
uint32_t sect_GetSymbolOffset(void);
Expand Down
18 changes: 14 additions & 4 deletions src/asm/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,8 @@ static void readGfxConstant(void)

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

Expand Down Expand Up @@ -1887,10 +1889,6 @@ static int yylex_NORMAL(void)
yylval.tzSym[1] = '\0';
return T_ID;

case '[':
return T_LBRACK;
case ']':
return T_RBRACK;
case '(':
return T_LPAREN;
case ')':
Expand All @@ -1900,6 +1898,18 @@ static int yylex_NORMAL(void)

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

case '[': /* Either [ or [[ */
if (peek(0) == '[') {
shiftChars(1);
return T_2LBRACK;
}
return T_LBRACK;
case ']': /* Either ] or ]] */
if (peek(0) == ']') {
shiftChars(1);
return T_2RBRACK;
}
return T_RBRACK;
case '*': /* Either MUL or EXP */
if (peek(0) == '*') {
shiftChars(1);
Expand Down
14 changes: 14 additions & 0 deletions src/asm/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "platform.h" // strncasecmp, strdup

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

static void upperstring(char *dest, char const *src)
{
Expand Down Expand Up @@ -464,6 +465,7 @@ enum {
%token T_COMMA ","
%token T_COLON ":"
%token T_LBRACK "[" T_RBRACK "]"
%token T_2LBRACK "[[" T_2RBRACK "]]"
%token T_LPAREN "(" T_RPAREN ")"
%token T_NEWLINE "newline"

Expand Down Expand Up @@ -520,6 +522,7 @@ enum {
%token <tzSym> T_ANON "anonymous label"
%type <tzSym> scoped_id
%type <tzSym> scoped_anon_id
%type <tzSym> inline_fragment
%token T_POP_EQU "EQU"
%token T_POP_SET "SET"
%token T_POP_EQUAL "="
Expand Down Expand Up @@ -1258,6 +1261,7 @@ reloc_16bit : relocexpr {
warning(WARNING_TRUNCATION, "Expression must be 16-bit\n");
$$ = $1;
}
| inline_fragment { rpn_Symbol(&$$, $1); }
;

reloc_16bit_no_str : relocexpr_no_str {
Expand All @@ -1266,8 +1270,18 @@ reloc_16bit_no_str : relocexpr_no_str {
warning(WARNING_TRUNCATION, "Expression must be 16-bit\n");
$$ = $1;
}
| inline_fragment { rpn_Symbol(&$$, $1); }
;

inline_fragment : T_2LBRACK {
out_PushInlineFragmentSection();
sprintf($<tzSym>$, "$%" PRIu32, inlineFragmentID++);
sym_AddLabel($<tzSym>$);
} asmfile T_2RBRACK {
out_PopSection();
strcpy($$, $<tzSym>2);
}
;

relocexpr : relocexpr_no_str
| string {
Expand Down
64 changes: 64 additions & 0 deletions src/asm/rgbasm.5
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,70 @@ first, followed by the one from
and the one from
.Ql bar.o
last.
.Ss Inline Fragments
Inline fragments are useful for short blocks of code or data that are only referenced once.
They are section fragments created by surrounding instructions or directives with
.Ql [[
double brackets
.Ql ]] ,
without a separate
.Ic SECTION FRAGMENT
declaration.
.Pp
The content of an inline fragment becomes a
.Ic SECTION FRAGMENT ,
sharing the same name and bank as its parent ROM section, but without any other constraints.
The parent section also becomes a
.Ic FRAGMENT
if it was not one already, so that it can be merged with its inline fragments.
RGBLINK merges the fragments in no particular order.
.Pp
An inline fragment can take the place of any 16-bit integer constant
.Ql n16
from the
.Xr gbz80 7
documentation, as well as a
.Ic DW
item.
The inline fragment then evaluates to its starting address.
For example, you can
.Ic CALL
or
.Ic JP
to an inline fragment.
.Pp
This code using named labels:
.Bd -literal -offset indent
FortyTwo:
call Sub1
jp Sub2
Sub1:
ld a, [Twenty]
ret
Sub2:
inc a
add a
ret
Twenty: db 20
dw FortyTwo
.Ed
.Pp
is equivalent to this code using inline fragments:
.Bd -literal -offset indent
dw [[
call [[
ld a, [ [[db 20]] ]
ret
]]
jp [[
inc a
add a
ret
]]
]]
.Ed
.Pp
The difference is that the example using inline fragments does not declare a particular order for its pieces.
.Sh SYMBOLS
RGBDS supports several types of symbols:
.Bl -hang
Expand Down
114 changes: 78 additions & 36 deletions src/asm/section.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,47 @@ static void mergeSections(struct Section *sect, enum SectionType type, uint32_t
#undef fail

/*
* Find a section by name and type. If it doesn't exist, create it
* Create a new section, not yet in the list.
*/
static struct Section *createSection(char const *name, enum SectionType type,
uint32_t org, uint32_t bank, uint8_t alignment,
uint16_t alignOffset, enum SectionModifier mod)
{
struct Section *sect = malloc(sizeof(*sect));

if (sect == NULL)
fatalerror("Not enough memory for section: %s\n", strerror(errno));

sect->name = strdup(name);
if (sect->name == NULL)
fatalerror("Not enough memory for section name: %s\n", strerror(errno));

sect->type = type;
sect->modifier = mod;
sect->src = fstk_GetFileStack();
sect->fileLine = lexer_GetLineNo();
sect->size = 0;
sect->org = org;
sect->bank = bank;
sect->align = alignment;
sect->alignOfs = alignOffset;
sect->next = NULL;
sect->patches = NULL;

/* It is only needed to allocate memory for ROM sections. */
if (sect_HasData(type)) {
sect->data = malloc(maxsize[type]);
if (sect->data == NULL)
fatalerror("Not enough memory for section: %s\n", strerror(errno));
} else {
sect->data = NULL;
}

return sect;
}

/*
* Find a section by name and type. If it doesn't exist, create it.
*/
static struct Section *getSection(char const *name, enum SectionType type, uint32_t org,
struct SectionSpec const *attrs, enum SectionModifier mod)
Expand Down Expand Up @@ -313,44 +353,13 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3

if (sect) {
mergeSections(sect, type, org, bank, alignment, alignOffset, mod);
return sect;
}

sect = malloc(sizeof(*sect));
if (sect == NULL)
fatalerror("Not enough memory for section: %s\n", strerror(errno));

sect->name = strdup(name);
if (sect->name == NULL)
fatalerror("Not enough memory for section name: %s\n", strerror(errno));

sect->type = type;
sect->modifier = mod;
sect->src = fstk_GetFileStack();
sect->fileLine = lexer_GetLineNo();
sect->size = 0;
sect->org = org;
sect->bank = bank;
sect->align = alignment;
sect->alignOfs = alignOffset;
sect->patches = NULL;

/* It is only needed to allocate memory for ROM sections. */
if (sect_HasData(type)) {
uint32_t sectsize;

sectsize = maxsize[type];
sect->data = malloc(sectsize);
if (sect->data == NULL)
fatalerror("Not enough memory for section: %s\n", strerror(errno));
} else {
sect->data = NULL;
sect = createSection(name, type, org, bank, alignment, alignOffset, mod);
// Add the new section to the list (order doesn't matter)
sect->next = pSectionList;
pSectionList = sect;
}

// Add the new section to the list (order doesn't matter)
sect->next = pSectionList;
pSectionList = sect;

return sect;
}

Expand Down Expand Up @@ -420,6 +429,39 @@ void out_EndLoadSection(void)
currentLoadSection = NULL;
}

void out_PushInlineFragmentSection(void)
{
checkcodesection();

if (currentLoadSection)
fatalerror("`LOAD` blocks cannot contain inline fragments\n");

struct Section *sect = pCurrentSection;

// SECTION UNION (RAM-only) is incompatible with SECTION FRAGMENT (ROM-only)
if (sect->modifier == SECTION_UNION)
fatalerror("`SECTION UNION` cannot contain inline fragments\n");

// A section containing an inline fragment has to become a fragment too
sect->modifier = SECTION_FRAGMENT;

out_PushSection();

// `SECTION "...", ROM0, BANK[0]` is not allowed
uint32_t bank = sect->bank == 0 ? -1 : sect->bank;

struct Section *newSect = createSection(sect->name, sect->type, -1, bank, 0, 0,
SECTION_FRAGMENT);

// Add the new section fragment to the list (after the section containing it)
newSect->next = sect->next;
sect->next = newSect;

changeSection();
curOffset = newSect->size;
pCurrentSection = newSect;
}

struct Section *sect_GetSymbolSection(void)
{
return currentLoadSection ? currentLoadSection : pCurrentSection;
Expand Down
14 changes: 14 additions & 0 deletions test/asm/inline-fragment-in-load.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
SECTION "OAMDMACode", ROM0
OAMDMACode:
LOAD "hOAMDMA", HRAM
hOAMDMA::
ldh [$ff46], a
ld a, 40
jp [[
: dec a
jr nz, :-
ret
]]
.end
ENDL
OAMDMACodeEnd:
2 changes: 2 additions & 0 deletions test/asm/inline-fragment-in-load.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FATAL: inline-fragment-in-load.asm(7):
`LOAD` blocks cannot contain inline fragments
Empty file.
9 changes: 9 additions & 0 deletions test/asm/inline-fragment-in-ram.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SECTION "RAM", WRAM0

wFoo:: db
wBar:: ds 3
println "ok"
wQux:: dw [[
ds 4
println "inline"
]]
2 changes: 2 additions & 0 deletions test/asm/inline-fragment-in-ram.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FATAL: inline-fragment-in-ram.asm(6):
Section 'RAM' cannot contain code or data (not ROM0 or ROMX)
1 change: 1 addition & 0 deletions test/asm/inline-fragment-in-ram.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ok
5 changes: 5 additions & 0 deletions test/asm/inline-fragment-in-union.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SECTION UNION "U", ROM0
db $11
dw [[ db $22 ]]
SECTION UNION "U", ROM0
db $33
2 changes: 2 additions & 0 deletions test/asm/inline-fragment-in-union.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FATAL: inline-fragment-in-union.asm(3):
`SECTION UNION` cannot contain inline fragments
Empty file.
Loading

0 comments on commit 0d57f45

Please sign in to comment.