Implement linkerscript parser

Signed-off-by: AntonioND <antonio_nd@outlook.com>
This commit is contained in:
AntonioND
2017-03-20 23:13:29 +00:00
parent dfb99618f5
commit 22d4a10cb6
9 changed files with 527 additions and 1 deletions

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

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

View File

@@ -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
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); }
%%

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);
}

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);
}