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);
+}
+