shithub: rgbds

Download patch

ref: eed098ac8e255f09b3727fa13ee577b7e0efada9
parent: dfb99618f5ff518c5b93e1b5212b36b8b99e7f74
parent: 64dbcc09123d55fcb880eb50edec75290637d571
author: AntonioND <[email protected]>
date: Sat Apr 1 06:33:49 EDT 2017

Merge pull request #140 from AntonioND/an/linkerscript

Implement linkerscript

--- 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}
@@ -72,6 +82,7 @@
 	$Qinstall -m ${MANMODE} src/asm/rgbasm.1 ${MANPREFIX}/man1/rgbasm.1
 	$Qinstall -m ${MANMODE} src/fix/rgbfix.1 ${MANPREFIX}/man1/rgbfix.1
 	$Qinstall -m ${MANMODE} src/link/rgblink.1 ${MANPREFIX}/man1/rgblink.1
+	$Qinstall -m ${MANMODE} src/link/rgblink.5 ${MANPREFIX}/man1/rgblink.5
 	$Qinstall -m ${MANMODE} src/gfx/rgbgfx.1 ${MANPREFIX}/man1/rgbgfx.1
 
 rgbasm: ${rgbasm_obj}
@@ -89,11 +100,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/README.md
+++ b/README.md
@@ -14,14 +14,18 @@
 
 ## Installing RGBDS (UNIX)
 
-RGBDS requires libpng and pkg-config to be installed.
+RGBDS requires yacc, flex, libpng and pkg-config to be installed.
 
 On Mac OS X, install them with [Homebrew](http://brew.sh/). On other Unixes,
-use the built-in package manager.
+use the built-in package manager. For example, on Debian or Ubuntu:
 
-You can test if they're installed by running `pkg-config --cflags libpng`:
-if the output is a path, then you're good, and if it outputs an error then
-you need to install them via a package manager.
+```sh
+sudo apt-get install byacc flex pkg-config libpng-dev
+```
+
+You can test if libpng and pkg-config are installed by running
+`pkg-config --cflags libpng`: if the output is a path, then you're good, and if
+it outputs an error then you need to install them via a package manager.
 
 To build the programs on a UNIX or UNIX-like system, just run in your terminal:
 
--- 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,14 @@
 extern void CreateSymbolTable(void);
 extern SLONG MaxBankUsed;
 extern SLONG MaxAvail[MAXBANKS];
+
+void
+SetLinkerscriptName(char *tzLinkerscriptFile);
+
+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,12 +1,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
+#include <string.h>
 
 #include "extern/err.h"
+#include "link/assign.h"
 #include "link/mylink.h"
 #include "link/main.h"
+#include "link/script.h"
 #include "link/symbol.h"
-#include "link/assign.h"
 
 struct sFreeArea {
 	SLONG nOrg;
@@ -214,7 +216,55 @@
 	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)
 {
@@ -289,7 +339,15 @@
 	}	
 }
 
+char *tzLinkerscriptName = NULL;
+
 void
+SetLinkerscriptName(char *tzLinkerscriptFile)
+{
+	tzLinkerscriptName = tzLinkerscriptFile;
+}
+
+void
 AssignSections(void)
 {
 	SLONG i;
@@ -366,8 +424,16 @@
 	}
 
 	/*
-	 * First, let's assign all the fixed sections...
-	 * And all because of that Jens Restemeier character ;)
+	 * First, let's parse the linkerscript.
+	 *
+	 */
+
+	if (tzLinkerscriptName) {
+		script_Parse(tzLinkerscriptName);
+	}
+
+	/*
+	 * Second, let's assign all the fixed sections...
 	 *
 	 */
 
--- /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); }
+
+%%
+
--- a/src/link/main.c
+++ b/src/link/main.c
@@ -31,12 +31,12 @@
  *
  */
 
-static void 
+static void
 usage(void)
 {
 	printf(
-"usage: rgblink [-tw] [-m mapfile] [-n symfile] [-O overlay] [-o outfile] \n"
-"               [-p pad_value] [-s symbol] file [...]\n");
+"usage: rgblink [-tw] [-l linkerscript] [-m mapfile] [-n symfile] [-O overlay]\n"
+"               [-o outfile] [-p pad_value] [-s symbol] file [...]\n");
 	exit(1);
 }
 
@@ -45,7 +45,7 @@
  *
  */
 
-int 
+int
 main(int argc, char *argv[])
 {
 	int ch;
@@ -56,8 +56,11 @@
 
 	progname = argv[0];
 
-	while ((ch = getopt(argc, argv, "m:n:o:O:p:s:tw")) != -1) {
+	while ((ch = getopt(argc, argv, "l:m:n:o:O:p:s:tw")) != -1) {
 		switch (ch) {
+		case 'l':
+			SetLinkerscriptName(optarg);
+			break;
 		case 'm':
 			SetMapfileName(optarg);
 			break;
--- /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);
+}
+
--- a/src/link/rgblink.1
+++ b/src/link/rgblink.1
@@ -1,4 +1,4 @@
-.Dd February 26, 2015
+.Dd March 27, 2017
 .Dt RGBLINK 1
 .Os RGBDS Manual
 .Sh NAME
@@ -14,6 +14,7 @@
 .Op Fl o Ar outfile
 .Op Fl p Ar pad_value
 .Op Fl s Ar symbol
+.Op Fl l Ar linkerscript
 .Ar
 .Sh DESCRIPTION
 The
@@ -54,6 +55,14 @@
 ROM file.
 This forces all ROMX sections to be of type ROM0, and increases the ROM0
 section size from 16KiB to 32KiB.
+.It Fl l Ar linkerscript
+Specify a linkerscript file that tells the linker how sections must be placed in
+the ROM.
+This file has priority over the attributes assigned in the source code, but they
+have to be consistent.
+See
+.Xr rgblink 5
+for more information about its format.
 .El
 .Sh EXAMPLES
 All you need for a basic ROM is an object file, which can be made into a ROM
@@ -70,9 +79,11 @@
 .D1 $ rgbfix -v bar.gb
 .Sh SEE ALSO
 .Xr rgbasm 1 ,
+.Xr rgblink 5 ,
 .Xr rgbfix 1 ,
 .Xr rgbds 7
 .Sh HISTORY
 .Nm
 was originally written by Carsten S\(/orensen as part of the ASMotor package,
-and was later packaged in RGBDS by Justin Lloyd.
+and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a
+number of contributors at https://github.com/rednex/rgbds.
--- /dev/null
+++ b/src/link/rgblink.5
@@ -1,0 +1,68 @@
+.Dd March 27, 2017
+.Dt RGBLINK 5
+.Os RGBDS Manual
+.Sh NAME
+.Nm rgblink
+.Nd linkerscript file format
+.Sh DESCRIPTION
+The linkerscript is an external file that allows the user to specify the
+order of sections without the need for doing so before assembling each object
+file.
+.Pp
+The placement of sections specified in the linkerscript is done before the
+sections whose placement is defined in the source code.
+.Pp
+A linkerscript consists on a series of banks followed by a list of sections
+and, optionally, commands.
+They can be lowercase or uppercase, it is ignored.
+Any line can contain a comment starting with
+.Ql \&;
+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"
+
+  WRAMX 2
+    "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.
+.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:
+.Bl -bullet
+.It
+ORG 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).
+.El
+.Pp
+Note: The bank, alignment, address and type of sections can be specified both
+in the source code and in the linkerscript.
+For a section to be able to be placed with the linkerscript the bank must be
+left unassigned in the source code or be the same as the one specified in the
+linkerscript. The address and alignment musn't be set.
+.Sh SEE ALSO
+.Xr rgbasm 1 ,
+.Xr rgblink 1 ,
+.Xr rgbfix 1 ,
+.Xr rgbds 7
+.Sh HISTORY
+.Nm
+was originally written by Carsten S\(/orensen as part of the ASMotor package,
+and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a
+number of contributors at https://github.com/rednex/rgbds.
--- /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);
+}
+