mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Merge pull request #140 from AntonioND/an/linkerscript
Implement linkerscript
This commit is contained in:
4
LICENSE
4
LICENSE
@@ -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.
|
||||
|
||||
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
|
||||
stag019, and is released under the ISC license.
|
||||
|
||||
20
Makefile
20
Makefile
@@ -4,6 +4,12 @@ PNGFLAGS != ${PKG_CONFIG} --cflags libpng
|
||||
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,12 +38,15 @@ rgbasm_obj = \
|
||||
|
||||
rgblink_obj = \
|
||||
src/link/assign.o \
|
||||
src/link/lexer.o \
|
||||
src/link/library.o \
|
||||
src/link/main.o \
|
||||
src/link/mapfile.o \
|
||||
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 @@ clean:
|
||||
$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 @@ install: all
|
||||
$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,12 +100,21 @@ rgbgfx: ${rgbgfx_obj}
|
||||
.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!
|
||||
# If you're building on Windows with Cygwin or Mingw, just follow the Unix
|
||||
|
||||
14
README.md
14
README.md
@@ -14,14 +14,18 @@ other UNIX tools.
|
||||
|
||||
## 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:
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef RGBDS_LINK_ASSIGN_H
|
||||
#define RGBDS_LINK_ASSIGN_H
|
||||
|
||||
#include "mylink.h"
|
||||
#include "types.h"
|
||||
|
||||
enum eBankCount {
|
||||
@@ -34,4 +35,13 @@ 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
|
||||
|
||||
29
include/link/script.h
Normal file
29
include/link/script.h
Normal 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
2
src/link/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
parser.c
|
||||
parser.h
|
||||
@@ -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,6 +216,54 @@ FindLargestSection(enum eSectionType type, bool bankFixed)
|
||||
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,6 +339,14 @@ AssignFloatingBankSections(enum eSectionType type)
|
||||
}
|
||||
}
|
||||
|
||||
char *tzLinkerscriptName = NULL;
|
||||
|
||||
void
|
||||
SetLinkerscriptName(char *tzLinkerscriptFile)
|
||||
{
|
||||
tzLinkerscriptName = tzLinkerscriptFile;
|
||||
}
|
||||
|
||||
void
|
||||
AssignSections(void)
|
||||
{
|
||||
@@ -366,8 +424,16 @@ AssignSections(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* 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...
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
75
src/link/lexer.l
Normal file
75
src/link/lexer.l
Normal 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); }
|
||||
|
||||
%%
|
||||
|
||||
@@ -35,8 +35,8 @@ 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);
|
||||
}
|
||||
|
||||
@@ -56,8 +56,11 @@ main(int argc, char *argv[])
|
||||
|
||||
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;
|
||||
|
||||
123
src/link/parser.y
Normal file
123
src/link/parser.y
Normal 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);
|
||||
}
|
||||
|
||||
@@ -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 @@ Write a tiny
|
||||
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 @@ to fix these so that the program will actually run in a Game Boy:
|
||||
.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.
|
||||
|
||||
68
src/link/rgblink.5
Normal file
68
src/link/rgblink.5
Normal 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
218
src/link/script.c
Normal 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user