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/asm/main.c b/src/asm/main.c index 8423c029..ef7155a3 100644 --- a/src/asm/main.c +++ b/src/asm/main.c @@ -20,8 +20,6 @@ int cldefines_index; int cldefines_size; char **cldefines; -char *progname; - clock_t nStartClock, nEndClock; SLONG nLineNo; ULONG nTotalLines, nPass, nPC, nIFDepth, nErrors; @@ -301,8 +299,6 @@ main(int argc, char *argv[]) if (argc == 1) usage(); - progname = "rgbasm"; - /* yydebug=1; */ DefaultOptions.gbgfx[0] = '0'; diff --git a/src/extern/err.c b/src/extern/err.c index bfeced2b..77c653e0 100644 --- a/src/extern/err.c +++ b/src/extern/err.c @@ -26,11 +26,9 @@ #include #include "extern/err.h" -extern char *progname; - void rgbds_vwarn(const char *fmt, va_list ap) { - fprintf (stderr, "%s: warning", progname); + fprintf (stderr, "warning"); if (fmt) { fputs (": ", stderr); vfprintf(stderr, fmt, ap); @@ -41,7 +39,7 @@ void rgbds_vwarn(const char *fmt, va_list ap) void rgbds_vwarnx(const char *fmt, va_list ap) { - fprintf (stderr, "%s: warning", progname); + fprintf (stderr, "warning"); if (fmt) { fputs (": ", stderr); vfprintf(stderr, fmt, ap); @@ -51,7 +49,7 @@ void rgbds_vwarnx(const char *fmt, va_list ap) noreturn void rgbds_verr(int status, const char *fmt, va_list ap) { - fprintf (stderr, "%s: error", progname); + fprintf (stderr, "error"); if (fmt) { fputs (": ", stderr); vfprintf(stderr, fmt, ap); @@ -62,7 +60,7 @@ noreturn void rgbds_verr(int status, const char *fmt, va_list ap) noreturn void rgbds_verrx(int status, const char *fmt, va_list ap) { - fprintf (stderr, "%s: error", progname); + fprintf (stderr, "error"); if (fmt) { fputs (": ", stderr); vfprintf(stderr, fmt, ap); diff --git a/src/fix/main.c b/src/fix/main.c index 26d33877..f634ce57 100644 --- a/src/fix/main.c +++ b/src/fix/main.c @@ -23,8 +23,6 @@ #include "extern/err.h" -char *progname; - static void usage(void) { @@ -71,8 +69,6 @@ main(int argc, char *argv[]) int version; /* mask ROM version number */ int padvalue; /* to pad the rom with if it changes size */ - progname = "rgbfix"; - while ((ch = getopt(argc, argv, "Cci:jk:l:m:n:p:sr:t:v")) != -1) { switch (ch) { case 'C': diff --git a/src/gfx/main.c b/src/gfx/main.c index fac25d5d..cc8bca2a 100644 --- a/src/gfx/main.c +++ b/src/gfx/main.c @@ -19,8 +19,6 @@ #include #include "gfx/main.h" -char *progname; - static void usage(void) { @@ -41,8 +39,6 @@ main(int argc, char *argv[]) char *ext; const char *errmsg = "Warning: The PNG's %s setting is not the same as the setting defined on the command line."; - progname = "rgbgfx"; - if (argc == 1) { usage(); } 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/main.c b/src/link/main.c index d602f77f..c24d0f6b 100644 --- a/src/link/main.c +++ b/src/link/main.c @@ -24,8 +24,6 @@ SLONG options = 0; SLONG fillchar = 0; char *smartlinkstartsymbol; -char *progname; - /* * Print the usagescreen * @@ -54,8 +52,6 @@ main(int argc, char *argv[]) if (argc == 1) usage(); - progname = "rgblink"; - while ((ch = getopt(argc, argv, "l:m:n:o:O:p:s:tw")) != -1) { switch (ch) { case 'l': 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 diff --git a/test/asm/undefined-dot.out b/test/asm/undefined-dot.out index e1e4ffb6..c1966e4b 100644 --- a/test/asm/undefined-dot.out +++ b/test/asm/undefined-dot.out @@ -1,3 +1,3 @@ ERROR: undefined-dot.asm(3): '.' not defined -rgbasm: error: Assembly aborted in pass 2 (1 errors)! +error: Assembly aborted in pass 2 (1 errors)! diff --git a/test/link/romx-tiny-no-t.out b/test/link/romx-tiny-no-t.out index 700b9a74..dabf2904 100644 --- a/test/link/romx-tiny-no-t.out +++ b/test/link/romx-tiny-no-t.out @@ -1 +1 @@ -rgblink: error: Unable to place 'r0b' (ROM0 section) anywhere +error: Unable to place 'r0b' (ROM0 section) anywhere diff --git a/test/link/romx-tiny-t.out b/test/link/romx-tiny-t.out index e8fa3e82..ef714e24 100644 --- a/test/link/romx-tiny-t.out +++ b/test/link/romx-tiny-t.out @@ -1 +1 @@ -rgblink: error: ROMX sections can't be used with option -t. +error: ROMX sections can't be used with option -t. diff --git a/test/link/vram-fixed-dmg-mode-w.out b/test/link/vram-fixed-dmg-mode-w.out index 277c6954..1ed998ce 100644 --- a/test/link/vram-fixed-dmg-mode-w.out +++ b/test/link/vram-fixed-dmg-mode-w.out @@ -1 +1 @@ -rgblink: error: VRAM bank 1 can't be used with option -w. +error: VRAM bank 1 can't be used with option -w. diff --git a/test/link/vram-floating-dmg-mode-w.out b/test/link/vram-floating-dmg-mode-w.out index 31ad1d17..4367a910 100644 --- a/test/link/vram-floating-dmg-mode-w.out +++ b/test/link/vram-floating-dmg-mode-w.out @@ -1 +1 @@ -rgblink: error: Unable to place 'v1' (VRAM section) in any bank +error: Unable to place 'v1' (VRAM section) in any bank diff --git a/test/link/wramx-dmg-mode-no-w.out b/test/link/wramx-dmg-mode-no-w.out index 8f5a6dd3..dab00306 100644 --- a/test/link/wramx-dmg-mode-no-w.out +++ b/test/link/wramx-dmg-mode-no-w.out @@ -1 +1 @@ -rgblink: error: Unable to place 'w0b' (WRAM0 section) anywhere +error: Unable to place 'w0b' (WRAM0 section) anywhere diff --git a/test/link/wramx-dmg-mode-w.out b/test/link/wramx-dmg-mode-w.out index 4317b2d4..8ca11a50 100644 --- a/test/link/wramx-dmg-mode-w.out +++ b/test/link/wramx-dmg-mode-w.out @@ -1 +1 @@ -rgblink: error: WRAMX sections can't be used with option -w. +error: WRAMX sections can't be used with option -w.