Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement [[ fragment literals ]] #1614

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/asm/lexer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ struct LexerState {
uint32_t lineNo;
uint32_t colNo;
int lastToken;
int nextToken;

std::deque<IfStackEntry> ifStack;

Expand Down
2 changes: 2 additions & 0 deletions include/asm/section.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,6 @@ void sect_PushSection();
void sect_PopSection();
void sect_CheckStack();

std::string sect_PushSectionFragmentLiteral();

#endif // RGBDS_ASM_SECTION_HPP
66 changes: 65 additions & 1 deletion man/rgbasm.5
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@ and
.Ic WRAMX
types are still considered different.
.It
Different constraints (alignment, bank, etc.) can be specified for each unionized section declaration, but they must all be compatible.
Different constraints (alignment, bank, etc.) can be specified for each section fragment declaration, but they must all be compatible.
For example, alignment must be compatible with any fixed address, all specified banks must be the same, etc.
.It
A section fragment may not be unionized; after all, that wouldn't make much sense.
Expand All @@ -1075,6 +1075,70 @@ first, followed by the one from
and the one from
.Ql bar.o
last.
.Ss Fragment literals
Fragment literals 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 a fragment literal 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 fragment literals.
RGBLINK merges the fragments in no particular order.
.Pp
A fragment literal 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 fragment literal then evaluates to its starting address.
For example, you can
.Ic CALL
or
.Ic JP
to a fragment literal.
.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 fragment literals:
.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 fragment literals does not declare a particular order for its pieces.
.Sh SYMBOLS
RGBDS supports several types of symbols:
.Bl -hang
Expand Down
28 changes: 24 additions & 4 deletions src/asm/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ void LexerState::clear(uint32_t lineNo_) {
mode = LEXER_NORMAL;
atLineStart = true; // yylex() will init colNo due to this
lastToken = T_(YYEOF);
nextToken = 0;

ifStack.clear();

Expand Down Expand Up @@ -1146,6 +1147,7 @@ static uint32_t readGfxConstant() {

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

Expand Down Expand Up @@ -1573,6 +1575,11 @@ static void appendStringLiteral(std::string &str, bool raw) {
static Token yylex_SKIP_TO_ENDC(); // forward declaration for yylex_NORMAL

static Token yylex_NORMAL() {
if (int nextToken = lexerState->nextToken; nextToken) {
lexerState->nextToken = 0;
return Token(nextToken);
}

for (;;) {
int c = nextChar();

Expand All @@ -1596,10 +1603,6 @@ static Token yylex_NORMAL() {
return Token(T_(ID), symName);
}

case '[':
return Token(T_(LBRACK));
case ']':
return Token(T_(RBRACK));
case '(':
return Token(T_(LPAREN));
case ')':
Expand All @@ -1609,6 +1612,23 @@ static Token yylex_NORMAL() {

// Handle ambiguous 1- or 2-char tokens

case '[': // Either [ or [[
if (peek() == '[') {
shiftChar();
return Token(T_(LBRACKS));
}
return Token(T_(LBRACK));

case ']': // Either ] or ]]
if (peek() == ']') {
shiftChar();
// `[[ Fragment literals ]]` inject an EOL token to end their contents
// even without a newline. Retroactively lex the `]]` after it.
lexerState->nextToken = T_(RBRACKS);
return Token(T_(EOL));
}
return Token(T_(RBRACK));

case '+': // Either += or ADD
if (peek() == '=') {
shiftChar();
Expand Down
8 changes: 6 additions & 2 deletions src/asm/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,12 @@ static uint32_t getSectIDIfAny(Section *sect) {
if (!sect)
return UINT32_MAX;

if (auto search = sectionMap.find(sect->name); search != sectionMap.end())
return static_cast<uint32_t>(search->second);
// Search in `sectionList` instead of `sectionMap`, since section fragments share the
// same name but have different IDs
if (auto search =
std::find_if(RANGE(sectionList), [&sect](Section const &s) { return &s == sect; });
search != sectionList.end())
return static_cast<uint32_t>(std::distance(sectionList.begin(), search));

fatalerror("Unknown section '%s'\n", sect->name.c_str());
}
Expand Down
26 changes: 25 additions & 1 deletion src/asm/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,13 @@
%token YYEOF 0 "end of file"
%token NEWLINE "end of line"
%token EOB "end of buffer"
%token EOL "end of fragment literal"

// General punctuation
%token COMMA ","
%token COLON ":" DOUBLE_COLON "::"
%token LBRACK "[" RBRACK "]"
%token LBRACKS "[[" RBRACKS "]]"
%token LPAREN "(" RPAREN ")"

// Arithmetic operators
Expand Down Expand Up @@ -362,6 +364,8 @@
%type <std::string> redef_equs
%type <std::string> scoped_id
%type <std::string> scoped_anon_id
%type <std::string> fragment_literal
%type <std::string> fragment_literal_name

// SM83 instruction parameters
%type <int32_t> reg_r
Expand Down Expand Up @@ -435,7 +439,7 @@ line:
| line_directive // Directives that manage newlines themselves
;

endofline: NEWLINE | EOB;
endofline: NEWLINE | EOB | EOL;

// For "logistical" reasons, these directives must manage newlines themselves.
// This is because we need to switch the lexer's mode *after* the newline has been read,
Expand Down Expand Up @@ -1310,13 +1314,33 @@ reloc_16bit:
$$ = std::move($1);
$$.checkNBit(16);
}
| fragment_literal {
$$.makeSymbol($1);
}
;

reloc_16bit_no_str:
relocexpr_no_str {
$$ = std::move($1);
$$.checkNBit(16);
}
| fragment_literal {
$$.makeSymbol($1);
}
;

fragment_literal:
LBRACKS fragment_literal_name asm_file RBRACKS {
sect_PopSection();
$$ = std::move($2);
}
;

fragment_literal_name:
%empty {
$$ = sect_PushSectionFragmentLiteral();
sym_AddLabel($$);
}
;

relocexpr:
Expand Down
61 changes: 61 additions & 0 deletions src/asm/section.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include "asm/symbol.hpp"
#include "asm/warning.hpp"

using namespace std::literals;

uint8_t fillByte;

struct UnionStackEntry {
Expand Down Expand Up @@ -298,6 +300,32 @@ static Section *createSection(
return &sect;
}

// Create a new section fragment literal, not yet in the list.
static Section *createSectionFragmentLiteral(Section const &parent) {
// Add the new section to the list, but do not update the map
Section &sect = sectionList.emplace_back();
assume(sectionMap.find(parent.name) != sectionMap.end());

sect.name = parent.name;
sect.type = parent.type;
sect.modifier = SECTION_FRAGMENT;
sect.src = fstk_GetFileStack();
sect.fileLine = lexer_GetLineNo();
sect.size = 0;
sect.org = UINT32_MAX;
sect.bank = parent.bank == 0 ? UINT32_MAX : parent.bank;
sect.align = 0;
sect.alignOfs = 0;

out_RegisterNode(sect.src);

// Section fragment literals must be ROM sections.
assume(sect_HasData(sect.type));
sect.data.resize(sectionTypeInfo[sect.type].size);

return &sect;
}

// Find a section by name and type. If it doesn't exist, create it.
static Section *getSection(
std::string const &name,
Expand Down Expand Up @@ -993,3 +1021,36 @@ void sect_EndSection() {
currentSection = nullptr;
sym_ResetCurrentLabelScopes();
}

std::string sect_PushSectionFragmentLiteral() {
static uint64_t nextFragmentLiteralID = 0;

// Like `requireCodeSection` but fatal
if (!currentSection)
fatalerror("Cannot output fragment literals outside of a SECTION\n");
if (!sect_HasData(currentSection->type))
fatalerror(
"Section '%s' cannot contain fragment literals (not ROM0 or ROMX)\n",
currentSection->name.c_str()
);

if (currentLoadSection)
fatalerror("`LOAD` blocks cannot contain fragment literals\n");
if (currentSection->modifier == SECTION_UNION)
fatalerror("`SECTION UNION` cannot contain fragment literals\n");

// A section containing a fragment literal has to become a fragment too
currentSection->modifier = SECTION_FRAGMENT;

Section *parent = currentSection;
sect_PushSection(); // Resets `currentSection`

Section *sect = createSectionFragmentLiteral(*parent);

changeSection();
curOffset = sect->size;
currentSection = sect;

// Return a symbol ID to use for the address of this section fragment
return "$"s + std::to_string(nextFragmentLiteralID++);
}
4 changes: 1 addition & 3 deletions src/asm/symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,9 +539,7 @@ std::string sym_MakeAnonLabelName(uint32_t ofs, bool neg) {
id = anonLabelID + ofs;
}

std::string anon("!");
anon += std::to_string(id);
return anon;
return "!"s + std::to_string(id);
}

void sym_Export(std::string const &symName) {
Expand Down
8 changes: 4 additions & 4 deletions test/asm/code-after-endm-endr-endc.err
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
error: code-after-endm-endr-endc.asm(6):
syntax error, unexpected PRINTLN, expecting end of line or end of buffer
syntax error, unexpected PRINTLN, expecting end of line or end of buffer or end of fragment literal
error: code-after-endm-endr-endc.asm(7):
Macro "mac" not defined
error: code-after-endm-endr-endc.asm(12):
syntax error, unexpected PRINTLN, expecting end of line or end of buffer
syntax error, unexpected PRINTLN, expecting end of line or end of buffer or end of fragment literal
error: code-after-endm-endr-endc.asm(17):
syntax error, unexpected PRINTLN, expecting end of line
error: code-after-endm-endr-endc.asm(19):
syntax error, unexpected PRINTLN, expecting end of line or end of buffer
syntax error, unexpected PRINTLN, expecting end of line or end of buffer or end of fragment literal
error: code-after-endm-endr-endc.asm(23):
syntax error, unexpected PRINTLN, expecting end of line
error: code-after-endm-endr-endc.asm(25):
syntax error, unexpected PRINTLN, expecting end of line or end of buffer
syntax error, unexpected PRINTLN, expecting end of line or end of buffer or end of fragment literal
error: Assembly aborted (7 errors)!
14 changes: 14 additions & 0 deletions test/asm/fragment-literal-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/fragment-literal-in-load.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FATAL: fragment-literal-in-load.asm(7):
`LOAD` blocks cannot contain fragment literals
9 changes: 9 additions & 0 deletions test/asm/fragment-literal-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/fragment-literal-in-ram.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FATAL: fragment-literal-in-ram.asm(6):
Section 'RAM' cannot contain fragment literals (not ROM0 or ROMX)
1 change: 1 addition & 0 deletions test/asm/fragment-literal-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/fragment-literal-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/fragment-literal-in-union.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FATAL: fragment-literal-in-union.asm(3):
`SECTION UNION` cannot contain fragment literals
Loading
Loading