Skip to content

Commit

Permalink
Implement [[ inline fragments ]]
Browse files Browse the repository at this point in the history
Fixes #500
  • Loading branch information
Rangi42 committed Feb 11, 2021
1 parent 88e1cc7 commit f5b93f4
Show file tree
Hide file tree
Showing 15 changed files with 252 additions and 38 deletions.
1 change: 1 addition & 0 deletions include/asm/section.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ void out_NewSection(char const *name, uint32_t secttype, uint32_t org,
void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes);
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 @@ -1251,6 +1251,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 @@ -1648,10 +1650,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 @@ -1661,6 +1659,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
16 changes: 16 additions & 0 deletions src/asm/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

static bool executeElseBlock; /* If this is set, ELIFs cannot be executed anymore */
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 @@ -404,6 +405,7 @@ enum {
} forArgs;
struct StrFmtArgList strfmtArgs;
bool hasEmpty; // Whether `db`, `dw`, `dl` argument lists contain any empty entries
char inlineFragmentName[12]; // space for "$4294967295" + '\0'
}

%type <sVal> relocexpr
Expand All @@ -430,12 +432,15 @@ enum {
%type <nConstValue> sectorg
%type <sectSpec> sectattrs

%type <inlineFragmentName> inline_fragment

%token <nConstValue> T_NUMBER "number"
%token <tzString> T_STRING "string"

%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 @@ -1206,6 +1211,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 @@ -1214,8 +1220,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($<inlineFragmentName>$, "$%" PRIu32, inlineFragmentID++);
sym_AddLabel($<inlineFragmentName>$);
} asmfile T_2RBRACK {
out_PopSection();
strcpy($$, $<inlineFragmentName>2);
}
;

relocexpr : relocexpr_no_str
| string {
Expand Down
60 changes: 60 additions & 0 deletions src/asm/rgbasm.5
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,66 @@ 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 section fragment, sharing the same constraints as its parent ROM section.
Its parent section also becomes a section fragment if it is not one already, and RGBLINK merges them together.
Inline fragments are appended after their parent section in no particular order.
.Pp
Any instance of a 16-bit integer constant
.Ql n16
in the
.Xr gbz80 7
documentation can define an inline fragment, and so can
.Ic DW
items.
The 16-bit value will then be the address at which the inline fragment is placed.
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
109 changes: 75 additions & 34 deletions src/asm/section.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,48 @@ 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->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)) {
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;
}

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 @@ -317,42 +358,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->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 @@ -416,6 +428,35 @@ void out_EndLoadSection(void)
loadOffset = 0;
}

void out_PushInlineFragmentSection(void)
{
checkcodesection();

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

struct Section *sect = pCurrentSection;

// 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
58 changes: 58 additions & 0 deletions test/asm/inline-fragments.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
SECTION "1", ROM0[0]

VERSION EQU $11
GetVersion::
ld a, [ [[db VERSION]] ]
ret

SECTION "2", ROM0, ALIGN[4]

text: MACRO
db \1, 0
ENDM

text_pointer: MACRO
dw [[ text \1 ]]
ENDM

GetText::
ld hl, [[
dw [[ db "Alpha", 0 ]]
dw [[ text "Beta" ]]
text_pointer "Gamma"
dw 0
]]
ld c, a
ld b, 0
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
ret

SECTION "C", ROM0

Foo::
call [[ jp [[ jp [[ ret ]] ]] ]]
call [[
Label::
call GetVersion
MYTEXT EQU 3
ld a, MYTEXT
call GetText
ld b, h
ld c, l
ret
]]
jp [[
Bar:
inc hl
.loop
halt
: dec l
jr nz, :-
dec h
jr nz, .loop
ret
]]
Empty file added test/asm/inline-fragments.err
Empty file.
Empty file added test/asm/inline-fragments.out
Empty file.
Binary file added test/asm/inline-fragments.out.bin
Binary file not shown.

0 comments on commit f5b93f4

Please sign in to comment.