Merge pull request #140 from AntonioND/an/linkerscript

Implement linkerscript
This commit is contained in:
AntonioND
2017-04-01 10:33:49 +01:00
committed by GitHub
13 changed files with 649 additions and 16 deletions

View File

@@ -11,7 +11,7 @@ released under the following license:
This program is free software. It comes without any warranty, to This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the DO WHATEVER PUBLIC LICENSE and/or modify it under the terms of the DO WHATEVER PUBLIC LICENSE
Software originally created by Justin Lloyd @ http://otakunozoku.com/ Software originally created by Justin Lloyd @ http://otakunozoku.com/
@@ -20,6 +20,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. 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 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 released under the MIT license. The remainder of charmap.c was written by
stag019, and is released under the ISC license. stag019, and is released under the ISC license.

View File

@@ -4,6 +4,12 @@ PNGFLAGS != ${PKG_CONFIG} --cflags libpng
REALCFLAGS = ${CFLAGS} ${WARNFLAGS} ${PNGFLAGS} -Iinclude -g \ REALCFLAGS = ${CFLAGS} ${WARNFLAGS} ${PNGFLAGS} -Iinclude -g \
-std=c99 -D_POSIX_C_SOURCE=200809L -std=c99 -D_POSIX_C_SOURCE=200809L
LFLAGS := --nounistd
YACC := yacc
FLEX := flex
RM := rm -rf
# User-defined variables # User-defined variables
PREFIX = /usr/local PREFIX = /usr/local
BINPREFIX = ${PREFIX}/bin BINPREFIX = ${PREFIX}/bin
@@ -32,12 +38,15 @@ rgbasm_obj = \
rgblink_obj = \ rgblink_obj = \
src/link/assign.o \ src/link/assign.o \
src/link/lexer.o \
src/link/library.o \ src/link/library.o \
src/link/main.o \ src/link/main.o \
src/link/mapfile.o \ src/link/mapfile.o \
src/link/object.o \ src/link/object.o \
src/link/output.o \ src/link/output.o \
src/link/patch.o \ src/link/patch.o \
src/link/parser.o \
src/link/script.o \
src/link/symbol.o \ src/link/symbol.o \
src/extern/err.o src/extern/err.o
@@ -60,6 +69,7 @@ clean:
$Qrm -rf rgbfix rgbfix.exe ${rgbfix_obj} rgbfix.html $Qrm -rf rgbfix rgbfix.exe ${rgbfix_obj} rgbfix.html
$Qrm -rf rgbgfx rgbgfx.exe ${rgbgfx_obj} rgbgfx.html $Qrm -rf rgbgfx rgbgfx.exe ${rgbgfx_obj} rgbgfx.html
$Qrm -rf src/asm/asmy.c src/asm/asmy.h $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 install: all
$Qmkdir -p ${BINPREFIX} $Qmkdir -p ${BINPREFIX}
@@ -72,6 +82,7 @@ install: all
$Qinstall -m ${MANMODE} src/asm/rgbasm.1 ${MANPREFIX}/man1/rgbasm.1 $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/fix/rgbfix.1 ${MANPREFIX}/man1/rgbfix.1
$Qinstall -m ${MANMODE} src/link/rgblink.1 ${MANPREFIX}/man1/rgblink.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 $Qinstall -m ${MANMODE} src/gfx/rgbgfx.1 ${MANPREFIX}/man1/rgbgfx.1
rgbasm: ${rgbasm_obj} rgbasm: ${rgbasm_obj}
@@ -89,12 +100,21 @@ rgbgfx: ${rgbgfx_obj}
.y.c: .y.c:
$Q${YACC} -d ${YFLAGS} -o $@ $< $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: .c.o:
$Q${CC} ${REALCFLAGS} -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/locallex.o src/asm/globlex.o src/asm/lexer.o: src/asm/asmy.h
src/asm/asmy.h: src/asm/asmy.c 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. # Below is a target for the project maintainer to easily create win32 exes.
# This is not for Windows users! # This is not for Windows users!
# If you're building on Windows with Cygwin or Mingw, just follow the Unix # If you're building on Windows with Cygwin or Mingw, just follow the Unix

View File

@@ -14,14 +14,18 @@ other UNIX tools.
## Installing RGBDS (UNIX) ## 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, 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`: ```sh
if the output is a path, then you're good, and if it outputs an error then sudo apt-get install byacc flex pkg-config libpng-dev
you need to install them via a package manager. ```
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: To build the programs on a UNIX or UNIX-like system, just run in your terminal:

View File

@@ -1,6 +1,7 @@
#ifndef RGBDS_LINK_ASSIGN_H #ifndef RGBDS_LINK_ASSIGN_H
#define RGBDS_LINK_ASSIGN_H #define RGBDS_LINK_ASSIGN_H
#include "mylink.h"
#include "types.h" #include "types.h"
enum eBankCount { enum eBankCount {
@@ -34,4 +35,13 @@ extern void CreateSymbolTable(void);
extern SLONG MaxBankUsed; extern SLONG MaxBankUsed;
extern SLONG MaxAvail[MAXBANKS]; 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 #endif

29
include/link/script.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2017 Antonio Nino Diaz <antonio_nd@outlook.com>
*
* 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

2
src/link/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
parser.c
parser.h

View File

@@ -1,12 +1,14 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include "extern/err.h" #include "extern/err.h"
#include "link/assign.h"
#include "link/mylink.h" #include "link/mylink.h"
#include "link/main.h" #include "link/main.h"
#include "link/script.h"
#include "link/symbol.h" #include "link/symbol.h"
#include "link/assign.h"
struct sFreeArea { struct sFreeArea {
SLONG nOrg; SLONG nOrg;
@@ -214,6 +216,54 @@ FindLargestSection(enum eSectionType type, bool bankFixed)
return r; 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 bool
VerifyAndSetBank(struct sSection *pSection) VerifyAndSetBank(struct sSection *pSection)
@@ -289,6 +339,14 @@ AssignFloatingBankSections(enum eSectionType type)
} }
} }
char *tzLinkerscriptName = NULL;
void
SetLinkerscriptName(char *tzLinkerscriptFile)
{
tzLinkerscriptName = tzLinkerscriptFile;
}
void void
AssignSections(void) AssignSections(void)
{ {
@@ -366,8 +424,16 @@ AssignSections(void)
} }
/* /*
* First, let's assign all the fixed sections... * First, let's parse the linkerscript.
* And all because of that Jens Restemeier character ;) *
*/
if (tzLinkerscriptName) {
script_Parse(tzLinkerscriptName);
}
/*
* Second, let's assign all the fixed sections...
* *
*/ */

75
src/link/lexer.l Normal file
View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2017 Antonio Nino Diaz <antonio_nd@outlook.com>
*
* 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); }
%%

View File

@@ -31,12 +31,12 @@ char *progname;
* *
*/ */
static void static void
usage(void) usage(void)
{ {
printf( printf(
"usage: rgblink [-tw] [-m mapfile] [-n symfile] [-O overlay] [-o outfile] \n" "usage: rgblink [-tw] [-l linkerscript] [-m mapfile] [-n symfile] [-O overlay]\n"
" [-p pad_value] [-s symbol] file [...]\n"); " [-o outfile] [-p pad_value] [-s symbol] file [...]\n");
exit(1); exit(1);
} }
@@ -45,7 +45,7 @@ usage(void)
* *
*/ */
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
int ch; int ch;
@@ -56,8 +56,11 @@ main(int argc, char *argv[])
progname = argv[0]; 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) { switch (ch) {
case 'l':
SetLinkerscriptName(optarg);
break;
case 'm': case 'm':
SetMapfileName(optarg); SetMapfileName(optarg);
break; break;

123
src/link/parser.y Normal file
View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2017 Antonio Nino Diaz <antonio_nd@outlook.com>
*
* 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);
}

View File

@@ -1,4 +1,4 @@
.Dd February 26, 2015 .Dd March 27, 2017
.Dt RGBLINK 1 .Dt RGBLINK 1
.Os RGBDS Manual .Os RGBDS Manual
.Sh NAME .Sh NAME
@@ -14,6 +14,7 @@
.Op Fl o Ar outfile .Op Fl o Ar outfile
.Op Fl p Ar pad_value .Op Fl p Ar pad_value
.Op Fl s Ar symbol .Op Fl s Ar symbol
.Op Fl l Ar linkerscript
.Ar .Ar
.Sh DESCRIPTION .Sh DESCRIPTION
The The
@@ -54,6 +55,14 @@ Write a tiny
ROM file. ROM file.
This forces all ROMX sections to be of type ROM0, and increases the ROM0 This forces all ROMX sections to be of type ROM0, and increases the ROM0
section size from 16KiB to 32KiB. 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 .El
.Sh EXAMPLES .Sh EXAMPLES
All you need for a basic ROM is an object file, which can be made into a ROM All you need for a basic ROM is an object file, which can be made into a ROM
@@ -70,9 +79,11 @@ to fix these so that the program will actually run in a Game Boy:
.D1 $ rgbfix -v bar.gb .D1 $ rgbfix -v bar.gb
.Sh SEE ALSO .Sh SEE ALSO
.Xr rgbasm 1 , .Xr rgbasm 1 ,
.Xr rgblink 5 ,
.Xr rgbfix 1 , .Xr rgbfix 1 ,
.Xr rgbds 7 .Xr rgbds 7
.Sh HISTORY .Sh HISTORY
.Nm .Nm
was originally written by Carsten S\(/orensen as part of the ASMotor package, 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.

68
src/link/rgblink.5 Normal file
View File

@@ -0,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.

218
src/link/script.c Normal file
View File

@@ -0,0 +1,218 @@
/*
* Copyright (C) 2017 Antonio Nino Diaz <antonio_nd@outlook.com>
*
* 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);
}