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:
6
LICENSE
6
LICENSE
@@ -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.
|
||||||
|
|||||||
20
Makefile
20
Makefile
@@ -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
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -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
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 <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
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); }
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
@@ -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
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
|
.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
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