diff --git a/include/asm/section.h b/include/asm/section.h index d8c7855a44..dd8838ed41 100644 --- a/include/asm/section.h +++ b/include/asm/section.h @@ -56,6 +56,9 @@ void sect_NextUnionMember(void); void sect_EndUnion(void); void sect_CheckUnionClosed(void); +char *sect_StartInlineBlock(void); +void sect_EndInlineBlock(void); + void out_AbsByte(uint8_t b); void out_AbsByteGroup(uint8_t const *s, int32_t length); void out_AbsWordGroup(uint8_t const *s, int32_t length); diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 706363f619..7d44da2a86 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -1650,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 ')': @@ -1663,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); diff --git a/src/asm/parser.y b/src/asm/parser.y index 0ae82123c6..34e637c9cc 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -406,6 +406,11 @@ enum { int32_t step; } forArgs; struct StrFmtArgList strfmtArgs; + struct { + char *name; + int32_t nPCOffset; + uint32_t nListCountEmpty; + } inlineBlock; } %type relocexpr @@ -429,12 +434,15 @@ enum { %type sectorg %type sectattrs +%type inline_block + %token T_NUMBER "number" %token 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" @@ -1185,6 +1193,10 @@ reloc_16bit : relocexpr { warning(WARNING_TRUNCATION, "Expression must be 16-bit\n"); $$ = $1; } + | inline_block { + rpn_Symbol(&$$, $1.name); + free($1.name); + } ; reloc_16bit_no_str : relocexpr_no_str { @@ -1193,8 +1205,23 @@ reloc_16bit_no_str : relocexpr_no_str { warning(WARNING_TRUNCATION, "Expression must be 16-bit\n"); $$ = $1; } + | inline_block { + rpn_Symbol(&$$, $1.name); + free($1.name); + } ; +inline_block : T_2LBRACK T_NEWLINE { + $$.name = sect_StartInlineBlock(); + $$.nPCOffset = nPCOffset; + $$.nListCountEmpty = nListCountEmpty; + } lines T_2RBRACK { + sect_EndInlineBlock(); + $$.name = $3.name; + nPCOffset = $3.nPCOffset; + nListCountEmpty = $3.nListCountEmpty; + } +; relocexpr : relocexpr_no_str | string { diff --git a/src/asm/section.c b/src/asm/section.c index f96de32a59..ff87362e46 100644 --- a/src/asm/section.c +++ b/src/asm/section.c @@ -31,6 +31,8 @@ uint32_t curOffset; /* Offset into the current section (see sect_GetSymbolOffset static struct Section *currentLoadSection = NULL; uint32_t loadOffset; /* The offset of the LOAD section within its parent */ +static uint32_t inlineBlockID = 0; /* Incrementing unique ID for inline block labels */ + struct UnionStackEntry { uint32_t start; uint32_t size; @@ -492,6 +494,62 @@ void sect_CheckUnionClosed(void) error("Unterminated UNION construct!\n"); } +char *sect_StartInlineBlock(void) +{ + checkcodesection(); + + if (currentLoadSection) + fatalerror("`LOAD` blocks cannot contain inline blocks\n"); + + struct Section *sect = pCurrentSection; + + /* + * Inline blocks are section fragments, so the section containing them + * has to become a fragment too. + */ + sect->modifier = SECTION_FRAGMENT; + + struct SectionSpec attrs; + + // 'SECTION "...", ROM0, BANK[0]' is not allowed + attrs.bank = sect->bank == 0 ? -1 : sect->bank; + attrs.alignment = 0; + attrs.alignOfs = 0; + + /* + * Initialize the inline block's section with a unique name so out_NewSection + * won't just find the current existing section. + */ + char *name = malloc(24); // space for "inline_block$4294967295" + '\0' + + if (name == NULL) + fatalerror("Not enough memory for inline block name: %s\n", strerror(errno)); + + sprintf(name, "inline_block$%" PRIu32, inlineBlockID++); + + out_PushSection(); + out_NewSection(name, sect->type, -1, &attrs, SECTION_FRAGMENT); + + /* + * Rename the inline block's section fragment to match the current section + * so they'll be merged. + */ + free(pCurrentSection->name); + pCurrentSection->name = strdup(sect->name); + if (pCurrentSection->name == NULL) + fatalerror("Not enough memory for inline block name: %s\n", strerror(errno)); + + // Label the start of the inline block to get its address. + sym_AddLabel(name); + + return name; +} + +void sect_EndInlineBlock(void) +{ + out_PopSection(); +} + /* * Output an absolute byte */