diff --git a/include/link/script.h b/include/link/script.h index 4e897cda..85b05144 100644 --- a/include/link/script.h +++ b/include/link/script.h @@ -17,8 +17,16 @@ #ifndef RGBDS_LINK_SCRIPT_H #define RGBDS_LINK_SCRIPT_H +#include "extern/stdnoreturn.h" + +noreturn void script_fatalerror(const char *fmt, ...); + void script_Parse(const char *path); +void script_IncludeFile(const char *path); +int script_IncludeDepthGet(void); +void script_IncludePop(void); + void script_InitSections(void); void script_SetCurrentSectionType(const char *type, unsigned int bank); void script_SetAddress(unsigned int addr); diff --git a/src/link/assign.c b/src/link/assign.c index e05cbadb..a2629c89 100644 --- a/src/link/assign.c +++ b/src/link/assign.c @@ -445,6 +445,7 @@ AssignSections(void) */ if (tzLinkerscriptName) { + script_InitSections(); script_Parse(tzLinkerscriptName); } diff --git a/src/link/lexer.l b/src/link/lexer.l index 1d36a293..afbfb4a3 100644 --- a/src/link/lexer.l +++ b/src/link/lexer.l @@ -16,22 +16,38 @@ %option noinput %option nounput +%option yylineno %{ +#include #include #include "extern/err.h" +#include "link/mylink.h" +#include "link/script.h" #include "parser.h" + +/* File include stack. */ + +#define MAX_INCLUDE_DEPTH 8 + +static int include_stack_ptr = 0; + +static YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; +static char include_path[MAX_INCLUDE_DEPTH][_MAX_PATH + 1]; +static int include_line[MAX_INCLUDE_DEPTH]; + +static char linkerscript_path[_MAX_PATH + 1]; /* Base file */ %} %% \"([^\\\"]|\\.)*\" { if (strlen(yytext) > sizeof(yylval.s) - 1) - errx(1, "String is too long: \"%s\"\n.", yytext); + script_fatalerror("String is too long: %s\n.", yytext); if (strlen(yytext) < 3) /* 2 quotes + 1 character */ - errx(1, "String \"%s\" is invalid\n.", yytext); + script_fatalerror("String %s is invalid\n.", yytext); yytext++; /* ignore first quote */ strcpy(yylval.s, yytext); @@ -62,13 +78,99 @@ (?i:ALIGN) { return COMMAND_ALIGN; } (?i:ORG) { return COMMAND_ORG; } +(?i:INCLUDE) { return COMMAND_INCLUDE; } + "\n" { return NEWLINE; } ;.* { /* Ignore comments. A dot doesn't match newline. */ } [[:space:]] { /* Ignore whitespace. */ } -. { errx(1, "Invalid character [%s]\n.", yytext); } +. { script_fatalerror("Invalid character [%s]\n.", yytext); } %% +extern FILE *yyin; + +void script_Parse(const char * path) +{ + yyin = fopen(path, "r"); + + if (!yyin) + errx(1, "Error opening file! \"%s\"\n", path); + + strncpy(linkerscript_path, path, sizeof(linkerscript_path)); + linkerscript_path[sizeof(linkerscript_path) - 1] = '\0'; + + do { + yyparse(); + } while (!feof(yyin)); + + fclose(yyin); + +} + +void script_IncludeFile(const char * path) +{ + if (include_stack_ptr == (MAX_INCLUDE_DEPTH-1)) + script_fatalerror("Includes nested too deeply."); + + include_line[include_stack_ptr] = yylineno; + include_stack[include_stack_ptr] = YY_CURRENT_BUFFER; + + include_stack_ptr++; + + yyin = fopen(path, "r" ); + + if (!yyin) + script_fatalerror("Couldn't open file \"%s\"", path); + + strncpy(include_path[include_stack_ptr], path, sizeof(include_path[0])); + include_path[include_stack_ptr][sizeof(include_path[0])-1] = '\0'; + + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + yylineno = 0; +} + +int script_IncludeDepthGet(void) +{ + return include_stack_ptr; +} + +void script_IncludePop(void) +{ + fclose(yyin); + + include_stack_ptr--; + + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(include_stack[include_stack_ptr]); + yylineno = include_line[include_stack_ptr]; +} + +void script_PrintFileStack(void) +{ + int i = include_stack_ptr; + + include_line[i] = yylineno; + + while (i > 0) { + fprintf(stderr, "%s(%d) -> ", include_path[i], include_line[i]); + i--; + } + fprintf(stderr, "%s(%d)", linkerscript_path, include_line[i]); +} + +noreturn void script_fatalerror(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + fprintf(stderr, "error: "); + script_PrintFileStack(); + fprintf(stderr, ":\n\t"); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(1); +} + diff --git a/src/link/parser.y b/src/link/parser.y index 6fc4e54a..ce89deb5 100644 --- a/src/link/parser.y +++ b/src/link/parser.y @@ -23,7 +23,7 @@ int yylex(); void yyerror(char *); -static int nline = 1; +extern int yylineno; %} %union { int i; char s[512]; } @@ -37,6 +37,8 @@ static int nline = 1; %token COMMAND_ALIGN %token COMMAND_ORG +%token COMMAND_INCLUDE + %token NEWLINE %start lines @@ -49,8 +51,8 @@ lines: ; line: - /* empty */ { nline++; } - | statement { nline++; } + /* empty */ + | statement ; statement: @@ -59,11 +61,11 @@ statement: script_SetCurrentSectionType($1, 0); } | SECTION_NONBANKED INTEGER { - errx(1, "%d:Trying to assign a bank to a non-banked section.\n", nline); + script_fatalerror("Trying to assign a bank to a non-banked section.\n"); } | SECTION_BANKED { - errx(1, "%d:Banked section without assigned bank.\n", nline); + script_fatalerror("Banked section without assigned bank.\n"); } | SECTION_BANKED INTEGER { script_SetCurrentSectionType($1, $2); @@ -74,13 +76,13 @@ statement: script_SetAlignment($2); } | COMMAND_ALIGN { - errx(1, "%d:ALIGN keyword needs an argument.\n", nline); + script_fatalerror("ALIGN keyword needs an argument.\n"); } | COMMAND_ORG INTEGER { script_SetAddress($2); } | COMMAND_ORG { - errx(1, "%d:ORG keyword needs an argument.\n", nline); + script_fatalerror("ORG keyword needs an argument.\n"); } /* Section name */ @@ -88,6 +90,11 @@ statement: script_OutputSection($1); } + /* Include file */ + | COMMAND_INCLUDE STRING { + script_IncludeFile($2); + } + /* End */ ; @@ -96,33 +103,18 @@ statement: extern int yylex(); extern int yyparse(); -extern FILE *yyin; - -int yywrap (void) +int yywrap(void) { - return 1; + if (script_IncludeDepthGet() == 0) + return 1; + + script_IncludePop(); + + return 0; } void yyerror(char *s) { - errx(1, "%d:Linkerscript parse error: \"%s\"\n", nline, s); -} - -void script_Parse(const char *path) -{ - script_InitSections(); - - FILE *f = fopen(path, "r"); - - if (!f) - errx(1, "Error opening file! \"%s\"\n", path); - - yyin = f; - - do { - yyparse(); - } while (!feof(yyin)); - - fclose(f); + script_fatalerror("Linkerscript parse error: \"%s\"\n", s); } diff --git a/src/link/rgblink.5 b/src/link/rgblink.5 index 7a6c431f..1f88330f 100644 --- a/src/link/rgblink.5 +++ b/src/link/rgblink.5 @@ -12,7 +12,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd March 27, 2017 +.Dd April 8, 2017 .Dt RGBLINK 5 .Os RGBDS Manual .Sh NAME @@ -34,35 +34,42 @@ Any line can contain a comment starting with that ends at the end of the line: .Pp ROMX $F ; This is a comment - "Functions to read array" - ALIGN 8 - "Array aligned to 256 bytes" + "Functions to read array" + ALIGN 8 + "Array aligned to 256 bytes" WRAMX 2 - "Some variables" + "Some variables" .Pp Numbers can be in decimal or hexadecimal format (the prefix is .Ql $ ) . It is an error if any bank or command is found before setting a bank. .Pp -The possible bank types are: ROM0, ROMX, VRAM, WRAM0, WRAMX, OAM and HRAM. -Types ROMX, VRAM, WRAMX and SRAM are banked, which means that it is needed to -specify a bank after the type. +Files can be included by using the +.Ar INCLUDE No keyword followed by a string with the path of the file that has +to be included. +.Pp +The possible bank types are: +.Sy ROM0 , ROMX , VRAM , WRAM0 , WRAMX , OAM No and Sy HRAM . +Types +.Sy ROMX , VRAM , WRAMX No and Sy SRAM No are banked, which means that it is +needed to specify a bank after the type. .Pp When a new bank statement is found, sections found after it will be placed right from the beginning of that bank. If the linkerscript switches to a different bank and then it comes back to the previous one it will continue from the last address that was used. .Pp -The only two commands are ORG and ALIGN: +The only two commands are +.Ar ORG No and Ar ALIGN : .Bl -bullet .It -ORG sets the address in which new sections will be placed. +.Ar ORG No sets the address in which new sections will be placed. It can not be lower than the current address. .It -ALIGN will increase the address until it is aligned to the specified boundary -(it tries to set to 0 the number of bits specified after the command: ALIGN 8 -will align to $100). +.Ar ALIGN No will increase the address until it is aligned to the specified +boundary (it tries to set to 0 the number of bits specified after the command: +.Ar ALIGN No 8 No will align to No $100 ) . .El .Pp Note: The bank, alignment, address and type of sections can be specified both