mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Implement linkerscript parser
Signed-off-by: AntonioND <antonio_nd@outlook.com>
This commit is contained in:
2
src/link/.gitignore
vendored
Normal file
2
src/link/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
parser.c
|
||||
parser.h
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "extern/err.h"
|
||||
#include "link/mylink.h"
|
||||
@@ -214,6 +215,54 @@ 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)
|
||||
|
||||
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); }
|
||||
|
||||
%%
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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