shithub: rgbds

Download patch

ref: 22d4a10cb61566c0b696fd6accbad2a31a87358c
parent: dfb99618f5ff518c5b93e1b5212b36b8b99e7f74
author: AntonioND <[email protected]>
date: Mon Mar 20 19:13:29 EDT 2017

Implement linkerscript parser

Signed-off-by: AntonioND <[email protected]>

--- a/LICENSE
+++ b/LICENSE
@@ -11,7 +11,7 @@
  This program is free software. It comes without any warranty, to
  the extent permitted by applicable law. You can redistribute it
  and/or modify it under the terms of the DO WHATEVER PUBLIC LICENSE
- 
+
  Software originally created by Justin Lloyd @ http://otakunozoku.com/
 
 
@@ -19,6 +19,10 @@
 under the ISC license; see the source file for the text of the license.
 
 rgbgfx was written by stag019, and is released under the ISC license.
+
+Some files of rgblink were written by Antonio Niño Díaz, and they are relased
+under the ISC license. The affected files have the appropriate license in the
+header of the file.
 
 The UTF-8 decoder in src/asm/charmap.c was written by Björn Höhrmann and is
 released under the MIT license. The remainder of charmap.c was written by
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,12 @@
 REALCFLAGS =	${CFLAGS} ${WARNFLAGS} ${PNGFLAGS} -Iinclude -g \
 		-std=c99 -D_POSIX_C_SOURCE=200809L
 
+LFLAGS		:= --nounistd
+
+YACC		:= yacc
+FLEX		:= flex
+RM		:= rm -rf
+
 # User-defined variables
 PREFIX =	/usr/local
 BINPREFIX =	${PREFIX}/bin
@@ -32,6 +38,7 @@
 
 rgblink_obj = \
 	src/link/assign.o \
+	src/link/lexer.o \
 	src/link/library.o \
 	src/link/main.o \
 	src/link/mapfile.o \
@@ -38,6 +45,8 @@
 	src/link/object.o \
 	src/link/output.o \
 	src/link/patch.o \
+	src/link/parser.o \
+	src/link/script.o \
 	src/link/symbol.o \
 	src/extern/err.o
 
@@ -60,6 +69,7 @@
 	$Qrm -rf rgbfix rgbfix.exe ${rgbfix_obj} rgbfix.html
 	$Qrm -rf rgbgfx rgbgfx.exe ${rgbgfx_obj} rgbgfx.html
 	$Qrm -rf src/asm/asmy.c src/asm/asmy.h
+	$Qrm -rf src/link/lexer.c src/link/parser.c src/link/parser.h
 
 install: all
 	$Qmkdir -p ${BINPREFIX}
@@ -89,11 +99,20 @@
 .y.c:
 	$Q${YACC} -d ${YFLAGS} -o $@ $<
 
+.l.o:
+	$Q${RM} $*.c
+	$Q${FLEX} ${LFLAGS} -o $*.c $<
+	$Q${CC} ${REALCFLAGS} -c -o $@ $*.c
+	$Q${RM} $*.c
+
 .c.o:
 	$Q${CC} ${REALCFLAGS} -c -o $@ $<
 
 src/asm/locallex.o src/asm/globlex.o src/asm/lexer.o: src/asm/asmy.h
 src/asm/asmy.h: src/asm/asmy.c
+
+src/link/lexer.o : src/link/parser.h
+src/link/parser.h : src/link/parser.c
 
 # Below is a target for the project maintainer to easily create win32 exes.
 # This is not for Windows users!
--- a/include/link/assign.h
+++ b/include/link/assign.h
@@ -1,6 +1,7 @@
 #ifndef RGBDS_LINK_ASSIGN_H
 #define RGBDS_LINK_ASSIGN_H
 
+#include "mylink.h"
 #include "types.h"
 
 enum eBankCount {
@@ -33,5 +34,11 @@
 extern void CreateSymbolTable(void);
 extern SLONG MaxBankUsed;
 extern SLONG MaxAvail[MAXBANKS];
+
+int
+IsSectionSameTypeBankAndFloating(const char *name, enum eSectionType type, int bank);
+
+unsigned int
+AssignSectionAddressByName(const char *name, unsigned int address);
 
 #endif
--- /dev/null
+++ b/include/link/script.h
@@ -1,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Antonio Nino Diaz <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef RGBDS_LINK_SCRIPT_H
+#define RGBDS_LINK_SCRIPT_H
+
+void script_Parse(const char *path);
+
+void script_InitSections(void);
+void script_SetCurrentSectionType(const char *type, unsigned int bank);
+void script_SetAddress(unsigned int addr);
+void script_SetAlignment(unsigned int alignment);
+void script_OutputSection(const char *section_name);
+
+#endif
+
--- /dev/null
+++ b/src/link/.gitignore
@@ -1,0 +1,2 @@
+parser.c
+parser.h
--- a/src/link/assign.c
+++ b/src/link/assign.c
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
+#include <string.h>
 
 #include "extern/err.h"
 #include "link/mylink.h"
@@ -214,6 +215,54 @@
 	return r;
 }
 
+int
+IsSectionSameTypeBankAndFloating(const char *name, enum eSectionType type, int bank)
+{
+	struct sSection *pSection;
+
+	pSection = pSections;
+	while (pSection) {
+		if (pSection->oAssigned == 0) {
+			if (strcmp(pSection->pzName, name) == 0) {
+				/* Section must be floating in source */
+				if (pSection->nOrg != -1 || pSection->nAlign != 1)
+					return 0;
+				/* It must have the same type in source and linkerscript */
+				if (pSection->Type != type)
+					return 0;
+				/* Bank number must be unassigned in source or equal */
+				if (pSection->nBank != -1 && pSection->nBank != bank)
+					return 0;
+				return 1;
+			}
+		}
+		pSection = pSection->pNext;
+	}
+
+	errx(1, "Section \"%s\" not found (or already used).\n", name);
+}
+
+unsigned int
+AssignSectionAddressByName(const char *name, unsigned int address)
+{
+	struct sSection *pSection;
+
+	pSection = pSections;
+	while (pSection) {
+		if (pSection->oAssigned == 0) {
+			if (strcmp(pSection->pzName, name) == 0) {
+				if (pSection->nOrg != -1 || pSection->nAlign != 1)
+					errx(1, "Section \"%s\" from linkerscript isn't floating.\n", name);
+				pSection->nOrg = address;
+				pSection->nAlign = -1;
+				return pSection->nByteSize;
+			}
+		}
+		pSection = pSection->pNext;
+	}
+
+	errx(1, "Section \"%s\" not found (or already used).\n", name);
+}
 
 bool
 VerifyAndSetBank(struct sSection *pSection)
--- /dev/null
+++ b/src/link/lexer.l
@@ -1,0 +1,75 @@
+/*
+ * Copyright (C) 2017 Antonio Nino Diaz <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%option noinput
+%option nounput
+%option noyywrap
+
+%{
+#include <unistd.h>
+
+#include "extern/err.h"
+
+#include "parser.h"
+%}
+
+%%
+
+\"([^\\\"]|\\.)*\"  {
+                        if (strlen(yytext) > sizeof(yylval.s) - 1)
+                            errx(1, "String is too long: \"%s\"\n.", yytext);
+                        if (strlen(yytext) < 3) /* 2 quotes + 1 character */
+                            errx(1, "String \"%s\" is invalid\n.", yytext);
+
+                        yytext++; /* ignore first quote */
+                        strcpy(yylval.s, yytext);
+                        yylval.s[strlen(yylval.s)-1] = '\0'; /* remove end quote */
+
+                        return STRING;
+                    }
+
+\$[a-fA-F0-9]+      {
+                        yytext++; /* Skip prefix */
+                        yylval.i = strtol(yytext, NULL, 16);
+                        return INTEGER;
+                    }
+[0-9]+              {
+                        yylval.i = strtol(yytext, NULL, 10);
+                        return INTEGER;
+                    }
+
+(?i:ROM0)           { strcpy(yylval.s, "ROM0");  return SECTION_NONBANKED; }
+(?i:ROMX)           { strcpy(yylval.s, "ROMX");  return SECTION_BANKED; }
+(?i:VRAM)           { strcpy(yylval.s, "VRAM");  return SECTION_BANKED; }
+(?i:WRAM0)          { strcpy(yylval.s, "WRAM0"); return SECTION_NONBANKED; }
+(?i:WRAMX)          { strcpy(yylval.s, "WRAMX"); return SECTION_BANKED; }
+(?i:SRAM)           { strcpy(yylval.s, "SRAM");  return SECTION_BANKED; }
+(?i:OAM)            { strcpy(yylval.s, "OAM");   return SECTION_NONBANKED; }
+(?i:HRAM)           { strcpy(yylval.s, "HRAM");  return SECTION_NONBANKED; }
+
+(?i:ALIGN)          { return COMMAND_ALIGN; }
+(?i:ORG)            { return COMMAND_ORG; }
+
+"\n"                { return NEWLINE; }
+
+;.*                 { /* Ignore comments. A dot doesn't match newline. */ }
+
+[[:space:]]         { /* Ignore whitespace. */ }
+
+.                   { errx(1, "Invalid character [%s]\n.", yytext); }
+
+%%
+
--- /dev/null
+++ b/src/link/parser.y
@@ -1,0 +1,123 @@
+/*
+ * Copyright (C) 2017 Antonio Nino Diaz <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <stdio.h>
+
+#include "extern/err.h"
+#include "link/script.h"
+
+int yylex();
+void yyerror(char *);
+
+static int nline = 1;
+%}
+
+%union { int i; char s[512]; }
+
+%token<i> INTEGER
+%token<s> STRING
+
+%token<s> SECTION_NONBANKED
+%token<s> SECTION_BANKED
+
+%token COMMAND_ALIGN
+%token COMMAND_ORG
+
+%token NEWLINE
+
+%start lines
+
+%%
+
+lines:
+      /* empty */
+    | lines line NEWLINE
+    ;
+
+line:
+      /* empty */       { nline++; }
+    | statement         { nline++; }
+    ;
+
+statement:
+    /* Statements to set the current section */
+      SECTION_NONBANKED {
+        script_SetCurrentSectionType($1, 0);
+    }
+    | SECTION_NONBANKED INTEGER {
+        errx(1, "%d:Trying to assign a bank to a non-banked section.\n", nline);
+    }
+
+    | SECTION_BANKED {
+        errx(1, "%d:Banked section without assigned bank.\n", nline);
+    }
+    | SECTION_BANKED INTEGER {
+        script_SetCurrentSectionType($1, $2);
+    }
+
+    /* Commands to adjust the address inside the current section */
+    | COMMAND_ALIGN INTEGER {
+        script_SetAlignment($2);
+    }
+    | COMMAND_ALIGN {
+        errx(1, "%d:ALIGN keyword needs an argument.\n", nline);
+    }
+    | COMMAND_ORG INTEGER {
+        script_SetAddress($2);
+    }
+    | COMMAND_ORG {
+        errx(1, "%d:ORG keyword needs an argument.\n", nline);
+    }
+
+    /* Section name */
+    | STRING {
+        script_OutputSection($1);
+    }
+
+    /* End */
+    ;
+
+%%
+
+extern int yylex();
+extern int yyparse();
+
+extern FILE *yyin;
+
+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);
+}
+
--- /dev/null
+++ b/src/link/script.c
@@ -1,0 +1,218 @@
+/*
+ * Copyright (C) 2017 Antonio Nino Diaz <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+#include "extern/err.h"
+#include "link/assign.h"
+#include "link/mylink.h"
+
+static struct {
+	unsigned int address; /* current address to write sections to */
+	unsigned int top_address; /* not inclusive */
+	enum eSectionType type;
+} bank[MAXBANKS];
+
+static int current_bank = -1; /* Bank as seen by the bank array */
+static int current_real_bank = -1; /* bank as seen by the GB */
+
+void script_InitSections(void)
+{
+	int i;
+	for (i = 0; i < MAXBANKS; i++) {
+		if (i == BANK_ROM0) {
+			/* ROM0 bank */
+			bank[i].address = 0x0000;
+			if (options & OPT_SMALL) {
+				bank[i].top_address = 0x8000;
+			} else {
+				bank[i].top_address = 0x4000;
+			}
+			bank[i].type = SECT_ROM0;
+		} else if (i >= BANK_ROMX && i < BANK_ROMX + BANK_COUNT_ROMX) {
+			/* Swappable ROM bank */
+			bank[i].address = 0x4000;
+			/*
+			 * Now, this shouldn't really be necessary... but for
+			 * good measure we'll do it anyway.
+			 */
+			if (options & OPT_SMALL) {
+				bank[i].top_address = 0x4000;
+			} else {
+				bank[i].top_address = 0x8000;
+			}
+			bank[i].type = SECT_ROMX;
+		} else if (i == BANK_WRAM0) {
+			/* WRAM */
+			bank[i].address = 0xC000;
+			if (options & OPT_CONTWRAM) {
+				bank[i].top_address = 0xE000;
+			} else {
+				bank[i].top_address = 0xD000;
+			}
+			bank[i].type = SECT_WRAM0;
+		} else if (i >= BANK_SRAM && i < BANK_SRAM + BANK_COUNT_SRAM) {
+			/* Swappable SRAM bank */
+			bank[i].address = 0xA000;
+			bank[i].top_address = 0xC000;
+			bank[i].type = SECT_SRAM;
+		} else if (i >= BANK_WRAMX && i < BANK_WRAMX + BANK_COUNT_WRAMX) {
+			/* Swappable WRAM bank */
+			bank[i].address = 0xD000;
+			bank[i].top_address = 0xE000;
+			bank[i].type = SECT_WRAMX;
+		} else if (i >= BANK_VRAM && i < BANK_VRAM + BANK_COUNT_VRAM) {
+			/* Swappable VRAM bank */
+			bank[i].address = 0x8000;
+			bank[i].top_address = 0xA000;
+			bank[i].type = SECT_VRAM;
+		} else if (i == BANK_OAM) {
+			/* OAM */
+			bank[i].address = 0xFE00;
+			bank[i].top_address = 0xFEA0;
+			bank[i].type = SECT_OAM;
+		} else if (i == BANK_HRAM) {
+			/* HRAM */
+			bank[i].address = 0xFF80;
+			bank[i].top_address = 0xFFFF;
+			bank[i].type = SECT_HRAM;
+		} else {
+			errx(1, "(INTERNAL) Unknown bank type!");
+		}
+	}
+}
+
+void script_SetCurrentSectionType(const char *type, unsigned int bank)
+{
+	if (strcmp(type, "ROM0") == 0) {
+		if (bank != 0)
+			errx(1, "(Internal) Trying to assign a bank number to ROM0.\n");
+		current_bank = BANK_ROM0;
+		current_real_bank = 0;
+		return;
+	} else if (strcmp(type, "ROMX") == 0) {
+		if (bank == 0)
+			errx(1, "ROMX index can't be 0.\n");
+		if (bank > BANK_COUNT_ROMX)
+			errx(1, "ROMX index too big (%d > %d).\n", bank, BANK_COUNT_ROMX);
+		current_bank = BANK_ROMX + bank - 1;
+		current_real_bank = bank;
+		return;
+	} else if (strcmp(type, "VRAM") == 0) {
+		if (bank >= BANK_COUNT_VRAM)
+			errx(1, "VRAM index too big (%d >= %d).\n", bank, BANK_COUNT_VRAM);
+		current_bank = BANK_VRAM + bank;
+		current_real_bank = bank;
+		return;
+	} else if (strcmp(type, "WRAM0") == 0) {
+		if (bank != 0)
+			errx(1, "(Internal) Trying to assign a bank number to WRAM0.\n");
+		current_bank = BANK_WRAM0;
+		current_real_bank = 0;
+		return;
+	} else if (strcmp(type, "WRAMX") == 0) {
+		if (bank == 0)
+			errx(1, "WRAMX index can't be 0.\n");
+		if (bank > BANK_COUNT_WRAMX)
+			errx(1, "WRAMX index too big (%d > %d).\n", bank, BANK_COUNT_WRAMX);
+		current_bank = BANK_WRAMX + bank - 1;
+		current_real_bank = bank - 1;
+		return;
+	} else if (strcmp(type, "SRAM") == 0) {
+		if (bank >= BANK_COUNT_SRAM)
+			errx(1, "SRAM index too big (%d >= %d).\n", bank, BANK_COUNT_SRAM);
+		current_bank = BANK_SRAM + bank;
+		current_real_bank = bank;
+		return;
+	} else if (strcmp(type, "OAM") == 0) {
+		if (bank != 0)
+			errx(1, "(Internal) Trying to assign a bank number to OAM.\n");
+		current_bank = BANK_OAM;
+		current_real_bank = 0;
+		return;
+	} else if (strcmp(type, "HRAM") == 0) {
+		if (bank != 0)
+			errx(1, "(Internal) Trying to assign a bank number to HRAM.\n");
+		current_bank = BANK_HRAM;
+		current_real_bank = 0;
+		return;
+	}
+
+	errx(1, "(Internal) Unknown section type \"%s\".\n", type);
+}
+
+void script_SetAddress(unsigned int addr)
+{
+	if (current_bank == -1) {
+		errx(1, "Trying to set an address without assigned bank\n");
+	}
+
+	/* Make sure that we don't go back. */
+	if (bank[current_bank].address > addr) {
+		errx(1, "Trying to go to a previous address (0x%04X to 0x%04X)\n",
+			bank[current_bank].address, addr);
+	}
+
+	bank[current_bank].address = addr;
+
+	/* Make sure we don't overflow */
+	if (bank[current_bank].address >= bank[current_bank].top_address) {
+		errx(1, "Bank overflowed (0x%04X >= 0x%04X)\n",
+			bank[current_bank].address, bank[current_bank].top_address);
+	}
+}
+
+void script_SetAlignment(unsigned int alignment)
+{
+	if (current_bank == -1) {
+		errx(1, "Trying to set an alignment without assigned bank\n");
+	}
+
+	if (alignment > 15) {
+		errx(1, "Trying to set an alignment too big: %d\n", alignment);
+	}
+
+	unsigned int size = 1 << alignment;
+	unsigned int mask = size - 1;
+
+	if (bank[current_bank].address & mask) {
+		bank[current_bank].address &= ~mask;
+		bank[current_bank].address += size;
+	}
+
+	/* Make sure we don't overflow */
+	if (bank[current_bank].address >= bank[current_bank].top_address) {
+		errx(1, "Bank overflowed (0x%04X >= 0x%04X)\n",
+			bank[current_bank].address, bank[current_bank].top_address);
+	}
+}
+
+void script_OutputSection(const char *section_name)
+{
+	if (current_bank == -1) {
+		errx(1, "Trying to place section \"%s\" without assigned bank\n", section_name);
+	}
+
+	if (!IsSectionSameTypeBankAndFloating(section_name, bank[current_bank].type,
+						current_real_bank)) {
+		errx(1, "Different attributes for \"%s\" in source and linkerscript\n",
+			section_name);
+	}
+
+	/* Move section to its place. */
+	bank[current_bank].address +=
+		AssignSectionAddressByName(section_name, bank[current_bank].address);
+}
+