Merge pull request #434 from ISSOtm/rgblink_rewrite

Rewrite RGBLINK entirely
This commit is contained in:
Eldred Habert
2019-11-03 16:15:37 +01:00
committed by GitHub
41 changed files with 2753 additions and 2623 deletions

View File

@@ -32,10 +32,11 @@ VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
WARNFLAGS := -Wall WARNFLAGS := -Wall
# Overridable CFLAGS # Overridable CFLAGS
CFLAGS := -g CFLAGS := -g -O0
# Non-overridable CFLAGS # Non-overridable CFLAGS
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=c99 -D_POSIX_C_SOURCE=200809L \ REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=c11 -D_POSIX_C_SOURCE=200809L \
-Iinclude -DBUILD_VERSION_STRING=\"${VERSION_STRING}\" -D_DEFAULT_SOURCE -Iinclude \
-DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
# Overridable LDFLAGS # Overridable LDFLAGS
LDFLAGS := LDFLAGS :=
# Non-overridable LDFLAGS # Non-overridable LDFLAGS
@@ -73,21 +74,17 @@ src/asm/globlex.o src/asm/lexer.o src/asm/constexpr.o: src/asm/asmy.h
rgblink_obj := \ rgblink_obj := \
src/link/assign.o \ src/link/assign.o \
src/link/lexer.o \
src/link/library.o \
src/link/main.o \ src/link/main.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/script.o \
src/link/section.o \
src/link/symbol.o \ src/link/symbol.o \
src/extern/err.o \ src/extern/err.o \
src/hashmap.o \
src/version.o src/version.o
src/link/lexer.o: src/link/parser.h
rgbfix_obj := \ rgbfix_obj := \
src/fix/main.o \ src/fix/main.o \
src/extern/err.o \ src/extern/err.o \

View File

@@ -9,7 +9,8 @@
#ifndef RGBDS_COMMON_H #ifndef RGBDS_COMMON_H
#define RGBDS_COMMON_H #define RGBDS_COMMON_H
#define RGBDS_OBJECT_VERSION_STRING "RGB6" #define RGBDS_OBJECT_VERSION_STRING "RGB%1hhu"
#define RGBDS_OBJECT_VERSION_NUMBER (uint8_t)6
enum eBankCount { enum eBankCount {
BANK_COUNT_ROM0 = 1, BANK_COUNT_ROM0 = 1,

68
include/hashmap.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
/* Generic hashmap implementation (C++ templates are calling...) */
#ifndef RGBDS_LINK_HASHMAP_H
#define RGBDS_LINK_HASHMAP_H
#include <assert.h>
#define HASH_NB_BITS 32
#define HALF_HASH_NB_BITS 16
_Static_assert(HALF_HASH_NB_BITS * 2 == HASH_NB_BITS, "");
#define HASHMAP_NB_BUCKETS (1 << HALF_HASH_NB_BITS)
/* HashMapEntry is internal, please do not attempt to use it */
typedef struct HashMapEntry *HashMap[HASHMAP_NB_BUCKETS];
/**
* Adds an element to a hashmap.
* @warning Adding a new element with an already-present key will not cause an
* error, this must be handled externally.
* @warning Inserting a NULL will make `hash_GetElement`'s return ambiguous!
* @param map The HashMap to add the element to
* @param key The key with which the element will be stored and retrieved
* @param element The element to add
* @return True if a collision occurred (for statistics)
*/
bool hash_AddElement(HashMap map, char const *key, void *element);
/**
* Removes an element from a hashmap.
* @param map The HashMap to remove the element from
* @param key The key to search the element with
* @return True if the element was found and removed
*/
bool hash_RemoveElement(HashMap map, char const *key);
/**
* Finds an element in a hashmap.
* @param map The map to consider the elements of
* @param key The key to search an element for
* @return A pointer to the element, or NULL if not found. (NULL can be returned
* if such an element was added, but that sounds pretty silly.)
*/
void *hash_GetElement(HashMap const map, char const *key);
/**
* Executes a function on each element in a hashmap.
* @param map The map to consider the elements of
* @param func The function to run. The first argument will be the element,
* the second will be `arg`.
* @param arg An argument to be passed to all function calls
*/
void hash_ForEach(HashMap const map, void (*func)(void *, void *), void *arg);
/**
* Cleanly empties a hashmap from its contents.
* This does not `free` the data structure itself!
* @param map The map to empty
*/
void hash_EmptyMap(HashMap map);
#endif /* RGBDS_LINK_HASHMAP_H */

View File

@@ -13,10 +13,12 @@
/* GCC or compatible */ /* GCC or compatible */
#define noreturn_ __attribute__ ((noreturn)) #define noreturn_ __attribute__ ((noreturn))
#define unused_ __attribute__ ((unused)) #define unused_ __attribute__ ((unused))
#define trap_ __builtin_trap()
#else #else
/* Unsupported, but no need to throw a fit */ /* Unsupported, but no need to throw a fit */
#define noreturn_ #define noreturn_
#define unused_ #define unused_
#define trap_
#endif #endif
#endif /* HELPERS_H */ #endif /* HELPERS_H */

View File

@@ -1,54 +1,29 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* Assigning all sections a place */
#ifndef RGBDS_LINK_ASSIGN_H #ifndef RGBDS_LINK_ASSIGN_H
#define RGBDS_LINK_ASSIGN_H #define RGBDS_LINK_ASSIGN_H
#include <stdint.h> #include <stdint.h>
#include "common.h" #include "common.h"
#include "mylink.h"
/* Bank numbers as seen by the linker */ extern uint64_t nbSectionsToAssign;
enum eBankDefine {
BANK_INDEX_ROM0 = 0,
BANK_INDEX_ROMX = BANK_INDEX_ROM0 + BANK_COUNT_ROM0,
BANK_INDEX_WRAM0 = BANK_INDEX_ROMX + BANK_COUNT_ROMX,
BANK_INDEX_WRAMX = BANK_INDEX_WRAM0 + BANK_COUNT_WRAM0,
BANK_INDEX_VRAM = BANK_INDEX_WRAMX + BANK_COUNT_WRAMX,
BANK_INDEX_OAM = BANK_INDEX_VRAM + BANK_COUNT_VRAM,
BANK_INDEX_HRAM = BANK_INDEX_OAM + BANK_COUNT_OAM,
BANK_INDEX_SRAM = BANK_INDEX_HRAM + BANK_COUNT_HRAM,
BANK_INDEX_MAX = BANK_INDEX_SRAM + BANK_COUNT_SRAM
};
extern int32_t MaxBankUsed; /**
extern int32_t MaxAvail[BANK_INDEX_MAX]; * Assigns all sections a slice of the address space
*/
void assign_AssignSections(void);
int32_t area_Avail(int32_t bank); /**
void AssignSections(void); * `free`s all assignment memory that was allocated.
void CreateSymbolTable(void); */
struct sSection *GetSectionByName(const char *name); void assign_Cleanup(void);
int32_t IsSectionNameInUse(const char *name);
void SetLinkerscriptName(char *tzLinkerscriptFile);
int32_t IsSectionSameTypeBankAndAttrs(const char *name,
enum eSectionType type, int32_t bank,
int32_t org, int32_t align);
uint32_t AssignSectionAddressAndBankByName(const char *name, uint32_t address,
int32_t bank);
int BankIndexIsROM0(int32_t bank);
int BankIndexIsROMX(int32_t bank);
int BankIndexIsWRAM0(int32_t bank);
int BankIndexIsWRAMX(int32_t bank);
int BankIndexIsVRAM(int32_t bank);
int BankIndexIsOAM(int32_t bank);
int BankIndexIsHRAM(int32_t bank);
int BankIndexIsSRAM(int32_t bank);
#endif /* RGBDS_LINK_ASSIGN_H */ #endif /* RGBDS_LINK_ASSIGN_H */

View File

@@ -1,14 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_LINK_LIBRARY_H
#define RGBDS_LINK_LIBRARY_H
void AddNeededModules(void);
#endif /* RGBDS_LINK_LIBRARY_H */

View File

@@ -1,17 +1,49 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* Declarations that all modules use, as well as `main` and related */
#ifndef RGBDS_LINK_MAIN_H #ifndef RGBDS_LINK_MAIN_H
#define RGBDS_LINK_MAIN_H #define RGBDS_LINK_MAIN_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
extern int32_t fillchar; /* Variables related to CLI options */
extern char *smartlinkstartsymbol; extern bool isDmgMode;
extern char const *linkerScriptName;
extern char const *mapFileName;
extern char const *symFileName;
extern char const *overlayFileName;
extern char const *outputFileName;
extern uint8_t padValue;
extern bool is32kMode;
extern bool beVerbose;
extern bool isWRA0Mode;
/* Helper macro for printing verbose-mode messages */
#define verbosePrint(...) do { \
if (beVerbose) \
fprintf(stderr, __VA_ARGS__); \
} while (0)
/**
* Opens a file if specified, and aborts on error.
* @param fileName The name of the file to open; if NULL, no file will be opened
* @param mode The mode to open the file with
* @return A pointer to a valid FILE structure, or NULL if fileName was NULL
*/
FILE *openFile(char const *fileName, char const *mode);
#define closeFile(file) do { \
FILE *tmp = file; \
if (tmp) \
fclose(tmp); \
} while (0)
#endif /* RGBDS_LINK_MAIN_H */ #endif /* RGBDS_LINK_MAIN_H */

View File

@@ -1,21 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_LINK_MAPFILE_H
#define RGBDS_LINK_MAPFILE_H
#include <stdint.h>
void SetMapfileName(char *name);
void SetSymfileName(char *name);
void CloseMapfile(void);
void MapfileWriteSection(const struct sSection *pSect);
void MapfileInitBank(int32_t bank);
void MapfileCloseBank(int32_t slack);
#endif /* RGBDS_LINK_MAPFILE_H */

View File

@@ -1,68 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_LINK_LINK_H
#define RGBDS_LINK_LINK_H
#include <stdint.h>
#include "linkdefs.h"
extern int32_t options;
#define OPT_TINY 0x01
#define OPT_SMART_C_LINK 0x02
#define OPT_OVERLAY 0x04
#define OPT_CONTWRAM 0x08
#define OPT_DMG_MODE 0x10
struct sSection {
int32_t nBank;
int32_t nOrg;
int32_t nAlign;
uint8_t oAssigned;
char *pzName;
int32_t nByteSize;
enum eSectionType Type;
uint8_t *pData;
int32_t nNumberOfSymbols;
struct sSymbol **tSymbols;
struct sPatch *pPatches;
struct sSection *pNext;
};
struct sSymbol {
char *pzName;
enum eSymbolType Type;
/* The following 3 items only valid when Type!=SYM_IMPORT */
int32_t nSectionID; /* Internal to object.c */
struct sSection *pSection;
int32_t nOffset;
char *pzObjFileName; /* Object file where the symbol is located. */
char *pzFileName; /* Source file where the symbol was defined. */
uint32_t nFileLine; /* Line where the symbol was defined. */
};
struct sPatch {
char *pzFilename;
int32_t nLineNo;
int32_t nOffset;
enum ePatchType Type;
int32_t nRPNSize;
uint8_t *pRPN;
struct sPatch *pNext;
uint8_t oRelocPatch;
};
extern struct sSection *pSections;
extern struct sSection *pLibSections;
#endif /* RGBDS_LINK_LINK_H */

View File

@@ -1,14 +1,30 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* Declarations related to processing of object (.o) files */
#ifndef RGBDS_LINK_OBJECT_H #ifndef RGBDS_LINK_OBJECT_H
#define RGBDS_LINK_OBJECT_H #define RGBDS_LINK_OBJECT_H
void obj_Readfile(char *tzObjectfile); /**
* Read an object (.o) file, and add its info to the data structures.
* @param fileName A path to the object file to be read
*/
void obj_ReadFile(char const *fileName);
/**
* Perform validation on the object files' contents
*/
void obj_DoSanityChecks(void);
/**
* `free`s all object memory that was allocated.
*/
void obj_Cleanup(void);
#endif /* RGBDS_LINK_OBJECT_H */ #endif /* RGBDS_LINK_OBJECT_H */

View File

@@ -1,16 +1,28 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* Outputting the result of linking */
#ifndef RGBDS_LINK_OUTPUT_H #ifndef RGBDS_LINK_OUTPUT_H
#define RGBDS_LINK_OUTPUT_H #define RGBDS_LINK_OUTPUT_H
void out_Setname(char *tzOutputfile); #include <stdint.h>
void out_SetOverlayname(char *tzOverlayfile);
void Output(void); #include "link/section.h"
/**
* Registers a section for output.
* @param section The section to add
*/
void out_AddSection(struct Section const *section);
/**
* Writes all output (bin, sym, map) files.
*/
void out_WriteFiles(void);
#endif /* RGBDS_LINK_OUTPUT_H */ #endif /* RGBDS_LINK_OUTPUT_H */

View File

@@ -1,17 +1,18 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* Applying patches to SECTIONs */
#ifndef RGBDS_LINK_PATCH_H #ifndef RGBDS_LINK_PATCH_H
#define RGBDS_LINK_PATCH_H #define RGBDS_LINK_PATCH_H
#include <stdint.h> /**
* Applies all SECTIONs' patches to them
void Patch(void); */
extern int32_t nPC; void patch_ApplyPatches(void);
#endif /* RGBDS_LINK_PATCH_H */ #endif /* RGBDS_LINK_PATCH_H */

View File

@@ -1,30 +1,36 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* Parsing a linker script */
#ifndef RGBDS_LINK_SCRIPT_H #ifndef RGBDS_LINK_SCRIPT_H
#define RGBDS_LINK_SCRIPT_H #define RGBDS_LINK_SCRIPT_H
#include <stdint.h> #include <stdint.h>
#include "helpers.h" extern FILE *linkerScript;
noreturn_ void script_fatalerror(const char *fmt, ...); struct SectionPlacement {
struct Section *section;
uint16_t org;
uint32_t bank;
};
void script_Parse(const char *path); extern uint64_t script_lineNo;
void script_IncludeFile(const char *path); /**
int32_t script_IncludeDepthGet(void); * Parses the linker script to return the next section constraint
void script_IncludePop(void); * @return A pointer to a struct, or NULL on EOF. The pointer shouldn't be freed
*/
struct SectionPlacement *script_NextSection(void);
void script_InitSections(void); /**
void script_SetCurrentSectionType(const char *type, uint32_t bank); * `free`s all assignment memory that was allocated.
void script_SetAddress(uint32_t addr); */
void script_SetAlignment(uint32_t alignment); void script_Cleanup(void);
void script_OutputSection(const char *section_name);
#endif /* RGBDS_LINK_SCRIPT_H */ #endif /* RGBDS_LINK_SCRIPT_H */

112
include/link/section.h Normal file
View File

@@ -0,0 +1,112 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
/* Declarations manipulating symbols */
#ifndef RGBDS_LINK_SECTION_H
#define RGBDS_LINK_SECTION_H
/* GUIDELINE: external code MUST NOT BE AWARE of the data structure used!! */
#include <stdint.h>
#include <stdbool.h>
#include "link/main.h"
#include "linkdefs.h"
struct AttachedSymbol {
struct Symbol *symbol;
struct AttachedSymbol *next;
};
struct Patch {
char *fileName;
int32_t lineNo;
int32_t offset;
enum PatchType type;
int32_t rpnSize;
uint8_t *rpnExpression;
};
struct Section {
/* Info contained in the object files */
char *name;
uint16_t size;
enum SectionType type;
bool isAddressFixed;
uint16_t org;
bool isBankFixed;
uint32_t bank;
bool isAlignFixed;
uint16_t alignMask;
uint8_t *data; /* Array of size `size`*/
uint32_t nbPatches;
struct Patch *patches;
/* Extra info computed during linking */
struct Symbol **fileSymbols;
uint32_t nbSymbols;
struct Symbol const **symbols;
};
extern uint16_t startaddr[];
extern uint16_t maxsize[];
extern uint32_t bankranges[][2];
extern char const * const typeNames[SECTTYPE_INVALID];
/**
* Computes a memory region's end address (last byte), eg. 0x7FFF
* @return The address of the last byte in that memory region
*/
static inline uint16_t endaddr(enum SectionType type)
{
return startaddr[type] + maxsize[type] - 1;
}
/**
* Computes a memory region's number of banks
* @return The number of banks, 1 for regions without banking
*/
static inline uint32_t nbbanks(enum SectionType type)
{
return bankranges[type][1] - bankranges[type][0] + 1;
}
/*
* Execute a callback for each section currently registered.
* This is to avoid exposing the data structure in which sections are stored.
* @param callback The function to call for each structure;
* the first argument will be a pointer to the structure,
* the second argument will be the pointer `arg`.
* @param arg A pointer which will be passed to all calls to `callback`.
*/
void sect_ForEach(void (*callback)(struct Section *, void *), void *arg);
/**
* Registers a section to be processed.
* @param section The section to register.
*/
void sect_AddSection(struct Section *section);
/**
* Finds a section by its name.
* @param name The name of the section to look for
* @return A pointer to the section, or NULL if it wasn't found
*/
struct Section *sect_GetSection(char const *name);
/**
* `free`s all section memory that was allocated.
*/
void sect_CleanupSections(void);
/**
* Checks if all sections meet reasonable criteria, such as max size
*/
void sect_DoSanityChecks(void);
#endif /* RGBDS_LINK_SECTION_H */

View File

@@ -1,21 +1,59 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* Declarations manipulating symbols */
#ifndef RGBDS_LINK_SYMBOL_H #ifndef RGBDS_LINK_SYMBOL_H
#define RGBDS_LINK_SYMBOL_H #define RGBDS_LINK_SYMBOL_H
/* GUIDELINE: external code MUST NOT BE AWARE of the data structure used!! */
#include <stdint.h> #include <stdint.h>
void sym_Init(void); #include "linkdefs.h"
void sym_CreateSymbol(char *tzName, int32_t nValue, int32_t nBank,
char *tzObjFileName, char *tzFileName, struct Symbol {
uint32_t nFileLine); /* Info contained in the object files */
int32_t sym_GetValue(struct sPatch *pPatch, char *tzName); char *name;
int32_t sym_GetBank(struct sPatch *pPatch, char *tzName); enum SymbolType type;
char const *objFileName;
char *fileName;
int32_t lineNo;
int32_t sectionID;
union {
int32_t offset;
int32_t value;
};
/* Extra info computed during linking */
struct Section *section;
};
/*
* Execute a callback for each symbol currently registered.
* This is done to avoid exposing the data structure in which symbol are stored.
* @param callback The function to call for each symbol;
* the first argument will be a pointer to the symbol,
* the second argument will be the pointer `arg`.
* @param arg A pointer which will be passed to all calls to `callback`.
*/
void sym_ForEach(void (*callback)(struct Symbol *, void *), void *arg);
void sym_AddSymbol(struct Symbol *symbol);
/**
* Finds a symbol in all the defined symbols.
* @param name The name of the symbol to look for
* @return A pointer to the symbol, or NULL if not found.
*/
struct Symbol *sym_GetSymbol(char const *name);
/**
* `free`s all symbol memory that was allocated.
*/
void sym_CleanupSymbols(void);
#endif /* RGBDS_LINK_SYMBOL_H */ #endif /* RGBDS_LINK_SYMBOL_H */

View File

@@ -9,7 +9,7 @@
#ifndef RGBDS_LINKDEFS_H #ifndef RGBDS_LINKDEFS_H
#define RGBDS_LINKDEFS_H #define RGBDS_LINKDEFS_H
enum eRpnData { enum RPNCommand {
RPN_ADD = 0x00, RPN_ADD = 0x00,
RPN_SUB = 0x01, RPN_SUB = 0x01,
RPN_MUL = 0x02, RPN_MUL = 0x02,
@@ -46,28 +46,43 @@ enum eRpnData {
RPN_SYM = 0x81 RPN_SYM = 0x81
}; };
enum eSectionType { enum SectionType {
SECT_WRAM0 = 0x00, SECTTYPE_WRAM0,
SECT_VRAM = 0x01, SECTTYPE_VRAM,
SECT_ROMX = 0x02, SECTTYPE_ROMX,
SECT_ROM0 = 0x03, SECTTYPE_ROM0,
SECT_HRAM = 0x04, SECTTYPE_HRAM,
SECT_WRAMX = 0x05, SECTTYPE_WRAMX,
SECT_SRAM = 0x06, SECTTYPE_SRAM,
SECT_OAM = 0x07 SECTTYPE_OAM,
SECTTYPE_INVALID
}; };
enum eSymbolType { enum SymbolType {
SYM_LOCAL = 0x00, SYMTYPE_LOCAL,
SYM_IMPORT = 0x01, SYMTYPE_IMPORT,
SYM_EXPORT = 0x02 SYMTYPE_EXPORT
}; };
enum ePatchType { enum PatchType {
PATCH_BYTE = 0x00, PATCHTYPE_BYTE,
PATCH_WORD_L = 0x01, PATCHTYPE_WORD,
PATCH_LONG_L = 0x02, PATCHTYPE_LONG,
PATCH_BYTE_JR = 0x03 PATCHTYPE_JR,
PATCHTYPE_INVALID
}; };
/**
* Tells whether a section has data in its object file definition,
* depending on type.
* @param type The section's type
* @return `true` if the section's definition includes data
*/
static inline bool sect_HasData(enum SectionType type)
{
return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX;
}
#endif /* RGBDS_LINKDEFS_H */ #endif /* RGBDS_LINKDEFS_H */

View File

@@ -45,22 +45,22 @@ static void bankrangecheck(char *name, uint32_t secttype, int32_t org,
char *stype = NULL; char *stype = NULL;
switch (secttype) { switch (secttype) {
case SECT_ROMX: case SECTTYPE_ROMX:
stype = "ROMX"; stype = "ROMX";
minbank = BANK_MIN_ROMX; minbank = BANK_MIN_ROMX;
maxbank = BANK_MAX_ROMX; maxbank = BANK_MAX_ROMX;
break; break;
case SECT_SRAM: case SECTTYPE_SRAM:
stype = "SRAM"; stype = "SRAM";
minbank = BANK_MIN_SRAM; minbank = BANK_MIN_SRAM;
maxbank = BANK_MAX_SRAM; maxbank = BANK_MAX_SRAM;
break; break;
case SECT_WRAMX: case SECTTYPE_WRAMX:
stype = "WRAMX"; stype = "WRAMX";
minbank = BANK_MIN_WRAMX; minbank = BANK_MIN_WRAMX;
maxbank = BANK_MAX_WRAMX; maxbank = BANK_MAX_WRAMX;
break; break;
case SECT_VRAM: case SECTTYPE_VRAM:
stype = "VRAM"; stype = "VRAM";
minbank = BANK_MIN_VRAM; minbank = BANK_MIN_VRAM;
maxbank = BANK_MAX_VRAM; maxbank = BANK_MAX_VRAM;
@@ -1523,33 +1523,33 @@ section : T_POP_SECTION string comma sectiontype
} }
; ;
sectiontype : T_SECT_WRAM0 { $$ = SECT_WRAM0; } sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; }
| T_SECT_VRAM { $$ = SECT_VRAM; } | T_SECT_VRAM { $$ = SECTTYPE_VRAM; }
| T_SECT_ROMX { $$ = SECT_ROMX; } | T_SECT_ROMX { $$ = SECTTYPE_ROMX; }
| T_SECT_ROM0 { $$ = SECT_ROM0; } | T_SECT_ROM0 { $$ = SECTTYPE_ROM0; }
| T_SECT_HRAM { $$ = SECT_HRAM; } | T_SECT_HRAM { $$ = SECTTYPE_HRAM; }
| T_SECT_WRAMX { $$ = SECT_WRAMX; } | T_SECT_WRAMX { $$ = SECTTYPE_WRAMX; }
| T_SECT_SRAM { $$ = SECT_SRAM; } | T_SECT_SRAM { $$ = SECTTYPE_SRAM; }
| T_SECT_OAM { $$ = SECT_OAM; } | T_SECT_OAM { $$ = SECTTYPE_OAM; }
| T_SECT_HOME | T_SECT_HOME
{ {
warning("HOME section name is deprecated, use ROM0 instead."); warning("HOME section name is deprecated, use ROM0 instead.");
$$ = SECT_ROM0; $$ = SECTTYPE_ROM0;
} }
| T_SECT_DATA | T_SECT_DATA
{ {
warning("DATA section name is deprecated, use ROMX instead."); warning("DATA section name is deprecated, use ROMX instead.");
$$ = SECT_ROMX; $$ = SECTTYPE_ROMX;
} }
| T_SECT_CODE | T_SECT_CODE
{ {
warning("CODE section name is deprecated, use ROMX instead."); warning("CODE section name is deprecated, use ROMX instead.");
$$ = SECT_ROMX; $$ = SECTTYPE_ROMX;
} }
| T_SECT_BSS | T_SECT_BSS
{ {
warning("BSS section name is deprecated, use WRAM0 instead."); warning("BSS section name is deprecated, use WRAM0 instead.");
$$ = SECT_WRAM0; $$ = SECTTYPE_WRAM0;
} }
; ;

View File

@@ -96,21 +96,21 @@ void out_PopSection(void)
static uint32_t getmaxsectionsize(uint32_t secttype, char *sectname) static uint32_t getmaxsectionsize(uint32_t secttype, char *sectname)
{ {
switch (secttype) { switch (secttype) {
case SECT_ROM0: case SECTTYPE_ROM0:
return 0x8000; /* If ROMX sections not used */ return 0x8000; /* If ROMX sections not used */
case SECT_ROMX: case SECTTYPE_ROMX:
return 0x4000; return 0x4000;
case SECT_VRAM: case SECTTYPE_VRAM:
return 0x2000; return 0x2000;
case SECT_SRAM: case SECTTYPE_SRAM:
return 0x2000; return 0x2000;
case SECT_WRAM0: case SECTTYPE_WRAM0:
return 0x2000; /* If WRAMX sections not used */ return 0x2000; /* If WRAMX sections not used */
case SECT_WRAMX: case SECTTYPE_WRAMX:
return 0x1000; return 0x1000;
case SECT_OAM: case SECTTYPE_OAM:
return 0xA0; return 0xA0;
case SECT_HRAM: case SECTTYPE_HRAM:
return 0x7F; return 0x7F;
default: default:
break; break;
@@ -241,7 +241,7 @@ static void writesection(struct Section *pSect, FILE *f)
fputlong(pSect->nBank, f); fputlong(pSect->nBank, f);
fputlong(pSect->nAlign, f); fputlong(pSect->nAlign, f);
if ((pSect->nType == SECT_ROM0) || (pSect->nType == SECT_ROMX)) { if (sect_HasData(pSect->nType)) {
struct Patch *pPatch; struct Patch *pPatch;
fwrite(pSect->tData, 1, pSect->nPC, f); fwrite(pSect->tData, 1, pSect->nPC, f);
@@ -265,23 +265,23 @@ static void writesymbol(struct sSymbol *pSym, FILE *f)
int32_t sectid; int32_t sectid;
if (!(pSym->nType & SYMF_DEFINED)) { if (!(pSym->nType & SYMF_DEFINED)) {
type = SYM_IMPORT; type = SYMTYPE_IMPORT;
} else if (pSym->nType & SYMF_EXPORT) { } else if (pSym->nType & SYMF_EXPORT) {
type = SYM_EXPORT; type = SYMTYPE_EXPORT;
} else { } else {
type = SYM_LOCAL; type = SYMTYPE_LOCAL;
} }
switch (type) { switch (type) {
case SYM_LOCAL: case SYMTYPE_LOCAL:
offset = pSym->nValue; offset = pSym->nValue;
sectid = getsectid(pSym->pSection); sectid = getsectid(pSym->pSection);
break; break;
case SYM_IMPORT: case SYMTYPE_IMPORT:
offset = 0; offset = 0;
sectid = -1; sectid = -1;
break; break;
case SYM_EXPORT: case SYMTYPE_EXPORT:
offset = pSym->nValue; offset = pSym->nValue;
if (pSym->nType & SYMF_CONST) if (pSym->nType & SYMF_CONST)
sectid = -1; sectid = -1;
@@ -293,7 +293,7 @@ static void writesymbol(struct sSymbol *pSym, FILE *f)
fputstring(pSym->tzName, f); fputstring(pSym->tzName, f);
fputc(type, f); fputc(type, f);
if (type != SYM_IMPORT) { if (type != SYMTYPE_IMPORT) {
fputstring(pSym->tzFileName, f); fputstring(pSym->tzFileName, f);
fputlong(pSym->nFileLine, f); fputlong(pSym->nFileLine, f);
@@ -496,8 +496,7 @@ static void checksection(void)
static void checkcodesection(void) static void checkcodesection(void)
{ {
checksection(); checksection();
if (pCurrentSection->nType != SECT_ROM0 && if (!sect_HasData(pCurrentSection->nType)) {
pCurrentSection->nType != SECT_ROMX) {
fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)", fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)",
pCurrentSection->pzName); pCurrentSection->pzName);
} else if (nUnionDepth > 0) { } else if (nUnionDepth > 0) {
@@ -570,8 +569,7 @@ void out_WriteObject(void)
if (f == NULL) if (f == NULL)
fatalerror("Couldn't write file '%s'\n", tzObjectname); fatalerror("Couldn't write file '%s'\n", tzObjectname);
fwrite(RGBDS_OBJECT_VERSION_STRING, 1, fprintf(f, RGBDS_OBJECT_VERSION_STRING, RGBDS_OBJECT_VERSION_NUMBER);
strlen(RGBDS_OBJECT_VERSION_STRING), f);
fputlong(countsymbols(), f); fputlong(countsymbols(), f);
fputlong(countsections(), f); fputlong(countsections(), f);
@@ -647,7 +645,7 @@ struct Section *out_FindSection(char *pzName, uint32_t secttype, int32_t org,
pSect->charmap = NULL; pSect->charmap = NULL;
/* It is only needed to allocate memory for ROM sections. */ /* It is only needed to allocate memory for ROM sections. */
if (secttype == SECT_ROM0 || secttype == SECT_ROMX) { if (sect_HasData(secttype)) {
uint32_t sectsize; uint32_t sectsize;
sectsize = getmaxsectionsize(secttype, pzName); sectsize = getmaxsectionsize(secttype, pzName);
@@ -743,8 +741,7 @@ void out_Skip(int32_t skip)
{ {
checksection(); checksection();
checksectionoverflow(skip); checksectionoverflow(skip);
if (!((pCurrentSection->nType == SECT_ROM0) if (!sect_HasData(pCurrentSection->nType)) {
|| (pCurrentSection->nType == SECT_ROMX))) {
pCurrentSection->nPC += skip; pCurrentSection->nPC += skip;
nPC += skip; nPC += skip;
pPCSymbol->nValue += skip; pPCSymbol->nValue += skip;
@@ -779,7 +776,7 @@ void out_RelByte(struct Expression *expr)
checksectionoverflow(1); checksectionoverflow(1);
if (rpn_isReloc(expr)) { if (rpn_isReloc(expr)) {
pCurrentSection->tData[nPC] = 0; pCurrentSection->tData[nPC] = 0;
createpatch(PATCH_BYTE, expr); createpatch(PATCHTYPE_BYTE, expr);
pCurrentSection->nPC += 1; pCurrentSection->nPC += 1;
nPC += 1; nPC += 1;
pPCSymbol->nValue += 1; pPCSymbol->nValue += 1;
@@ -815,7 +812,7 @@ void out_RelWord(struct Expression *expr)
if (rpn_isReloc(expr)) { if (rpn_isReloc(expr)) {
pCurrentSection->tData[nPC] = 0; pCurrentSection->tData[nPC] = 0;
pCurrentSection->tData[nPC + 1] = 0; pCurrentSection->tData[nPC + 1] = 0;
createpatch(PATCH_WORD_L, expr); createpatch(PATCHTYPE_WORD, expr);
pCurrentSection->nPC += 2; pCurrentSection->nPC += 2;
nPC += 2; nPC += 2;
pPCSymbol->nValue += 2; pPCSymbol->nValue += 2;
@@ -854,7 +851,7 @@ void out_RelLong(struct Expression *expr)
pCurrentSection->tData[nPC + 1] = 0; pCurrentSection->tData[nPC + 1] = 0;
pCurrentSection->tData[nPC + 2] = 0; pCurrentSection->tData[nPC + 2] = 0;
pCurrentSection->tData[nPC + 3] = 0; pCurrentSection->tData[nPC + 3] = 0;
createpatch(PATCH_LONG_L, expr); createpatch(PATCHTYPE_LONG, expr);
pCurrentSection->nPC += 4; pCurrentSection->nPC += 4;
nPC += 4; nPC += 4;
pPCSymbol->nValue += 4; pPCSymbol->nValue += 4;
@@ -875,7 +872,7 @@ void out_PCRelByte(struct Expression *expr)
/* Always let the linker calculate the offset. */ /* Always let the linker calculate the offset. */
pCurrentSection->tData[nPC] = 0; pCurrentSection->tData[nPC] = 0;
createpatch(PATCH_BYTE_JR, expr); createpatch(PATCHTYPE_JR, expr);
pCurrentSection->nPC += 1; pCurrentSection->nPC += 1;
nPC += 1; nPC += 1;
pPCSymbol->nValue += 1; pPCSymbol->nValue += 1;

13
src/extern/err.c vendored
View File

@@ -14,13 +14,12 @@
void rgbds_vwarn(const char *fmt, va_list ap) void rgbds_vwarn(const char *fmt, va_list ap)
{ {
fprintf(stderr, "warning"); fprintf(stderr, "warning: ");
if (fmt) { if (fmt) {
fputs(": ", stderr);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
fputs(": ", stderr);
} }
putc('\n', stderr); perror(NULL);
perror(0);
} }
void rgbds_vwarnx(const char *fmt, va_list ap) void rgbds_vwarnx(const char *fmt, va_list ap)
@@ -35,12 +34,12 @@ void rgbds_vwarnx(const char *fmt, va_list ap)
noreturn_ void rgbds_verr(int status, const char *fmt, va_list ap) noreturn_ void rgbds_verr(int status, const char *fmt, va_list ap)
{ {
fprintf(stderr, "error"); fprintf(stderr, "error: ");
if (fmt) { if (fmt) {
fputs(": ", stderr);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
fputs(": ", stderr);
} }
putc('\n', stderr); perror(NULL);
exit(status); exit(status);
} }

118
src/hashmap.c Normal file
View File

@@ -0,0 +1,118 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "hashmap.h"
#include "extern/err.h"
/*
* The lower half of the hash is used to index the "master" table,
* the upper half is used to help resolve collisions more quickly
*/
#define UINT_BITS_(NB_BITS) uint##NB_BITS##_t
#define UINT_BITS(NB_BITS) UINT_BITS_(NB_BITS)
typedef UINT_BITS(HASH_NB_BITS) HashType;
typedef UINT_BITS(HALF_HASH_NB_BITS) HalfHashType;
struct HashMapEntry {
HalfHashType hash;
char const *key;
void *content;
struct HashMapEntry *next;
};
#define FNV_OFFSET_BASIS 0x811c9dc5
#define FNV_PRIME 16777619
/* FNV-1a hash */
static HashType hash(char const *str)
{
HashType hash = FNV_OFFSET_BASIS;
while (*str) {
hash ^= (uint8_t)*str++;
hash *= FNV_PRIME;
}
return hash;
}
bool hash_AddElement(HashMap map, char const *key, void *element)
{
HashType hashedKey = hash(key);
HalfHashType index = hashedKey;
struct HashMapEntry *newEntry = malloc(sizeof(*newEntry));
if (!newEntry)
err(1, "%s: Failed to allocate new entry", __func__);
newEntry->hash = hashedKey >> HALF_HASH_NB_BITS;
newEntry->key = key;
newEntry->content = element;
newEntry->next = map[index];
map[index] = newEntry;
return newEntry->next != NULL;
}
bool hash_DeleteElement(HashMap map, char const *key)
{
HashType hashedKey = hash(key);
struct HashMapEntry **ptr = &map[(HalfHashType)hashedKey];
while (*ptr) {
if (hashedKey >> HALF_HASH_NB_BITS == (*ptr)->hash
&& !strcmp((*ptr)->key, key)) {
struct HashMapEntry *next = (*ptr)->next;
free(*ptr);
*ptr = next;
return true;
}
}
return false;
}
void *hash_GetElement(HashMap const map, char const *key)
{
HashType hashedKey = hash(key);
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
while (ptr) {
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
&& !strcmp(ptr->key, key)) {
return ptr->content;
}
ptr = ptr->next;
}
return NULL;
}
void hash_ForEach(HashMap const map, void (*func)(void *, void *), void *arg)
{
for (size_t i = 0; i < HASHMAP_NB_BUCKETS; i++) {
struct HashMapEntry *ptr = map[i];
while (ptr) {
func(ptr->content, arg);
ptr = ptr->next;
}
}
}
void hash_EmptyMap(HashMap map)
{
for (size_t i = 0; i < HASHMAP_NB_BUCKETS; i++) {
struct HashMapEntry *ptr = map[i];
while (ptr) {
struct HashMapEntry *next = ptr->next;
free(ptr);
ptr = next;
}
map[i] = NULL;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,194 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
%option noinput
%option yylineno
%{
#include <stdarg.h>
#include <stdint.h>
#include <unistd.h>
#include "extern/err.h"
#include "link/mylink.h"
#include "link/script.h"
#include "parser.h"
#include "types.h"
extern int yyparse(void);
/* File include stack. */
#define MAX_INCLUDE_DEPTH 8
static int32_t include_stack_ptr;
static YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
static char include_path[MAX_INCLUDE_DEPTH][_MAX_PATH + 1];
static int32_t include_line[MAX_INCLUDE_DEPTH];
static char linkerscript_path[_MAX_PATH + 1]; /* Base file */
%}
%%
\"([^\\\"]|\\.)*\" {
if (strlen(yytext) > sizeof(yylval.s) - 1) {
script_fatalerror("String is too long: %s\n.",
yytext);
}
if (strlen(yytext) < 3) { /* 2 quotes + 1 character */
script_fatalerror("String %s is invalid\n.",
yytext);
}
/* Ignore first quote */
yytext++;
strcpy(yylval.s, yytext);
/* Remove end quote */
yylval.s[strlen(yylval.s)-1] = '\0';
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; }
(?i:INCLUDE) { return COMMAND_INCLUDE; }
"\n" { return NEWLINE; }
;.* { /* Ignore comments. A dot doesn't match newline. */ }
[[:space:]] { /* Ignore whitespace. */ }
. { script_fatalerror("Invalid character [%s]\n.", yytext); }
%%
extern FILE *yyin;
void script_Parse(const char * path)
{
yyin = fopen(path, "r");
if (!yyin)
errx(1, "Error opening file! \"%s\"\n", path);
strncpy(linkerscript_path, path, sizeof(linkerscript_path));
linkerscript_path[sizeof(linkerscript_path) - 1] = '\0';
do {
yyparse();
} while (!feof(yyin));
fclose(yyin);
}
void script_IncludeFile(const char * path)
{
if (include_stack_ptr == (MAX_INCLUDE_DEPTH - 1))
script_fatalerror("Includes nested too deeply.");
include_line[include_stack_ptr] = yylineno;
include_stack[include_stack_ptr] = YY_CURRENT_BUFFER;
include_stack_ptr++;
yyin = fopen(path, "r");
if (!yyin)
script_fatalerror("Couldn't open file \"%s\"", path);
strncpy(include_path[include_stack_ptr], path, sizeof(include_path[0]));
include_path[include_stack_ptr][sizeof(include_path[0]) - 1] = '\0';
yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
yylineno = 1;
/*
* The INCLUDE keyword is handled before reaching a newline token, it's
* handled right after parsing the string with the file name that has to
* be included. It isn't actually needed to include a newline after the
* path, the last line of the linkerscript doesn't need to have a
* newline character but it can have a command.
*
* This means that, when opening a new file, we must tell the parser
* that what it is going to start at a new line or it will think that
* the first line of the included script is the continuation of the
* INCLUDE line of the parent script. If this is not done, the first
* line of an included linkerscript can only be a comment (or empty).
*/
unput('\n');
}
int32_t script_IncludeDepthGet(void)
{
return include_stack_ptr;
}
void script_IncludePop(void)
{
fclose(yyin);
include_stack_ptr--;
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_switch_to_buffer(include_stack[include_stack_ptr]);
yylineno = include_line[include_stack_ptr];
}
void script_PrintFileStack(void)
{
int32_t i = include_stack_ptr;
include_line[i] = yylineno;
while (i > 0) {
fprintf(stderr, "%s(%d) -> ", include_path[i], include_line[i]);
i--;
}
fprintf(stderr, "%s(%d)", linkerscript_path, include_line[i]);
}
noreturn_ void script_fatalerror(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "error: ");
script_PrintFileStack();
fprintf(stderr, ":\n ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
exit(1);
}

View File

@@ -1,122 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extern/err.h"
#include "link/mylink.h"
#include "link/main.h"
static uint8_t symboldefined(char *name)
{
const struct sSection *pSect;
pSect = pSections;
while (pSect) {
int32_t i;
for (i = 0; i < pSect->nNumberOfSymbols; i += 1) {
const struct sSymbol *tSymbol = pSect->tSymbols[i];
if ((tSymbol->Type == SYM_EXPORT)
|| ((tSymbol->Type == SYM_LOCAL)
&& (pSect == tSymbol->pSection))) {
if (strcmp(tSymbol->pzName, name) == 0)
return 1;
}
}
pSect = pSect->pNext;
}
return 0;
}
static uint8_t addmodulecontaining(char *name)
{
struct sSection **ppLSect = &pLibSections;
while (*ppLSect) {
int32_t i;
for (i = 0; i < (*ppLSect)->nNumberOfSymbols; i += 1) {
const struct sSymbol *tSymbol = (*ppLSect)->tSymbols[i];
if ((tSymbol->Type == SYM_EXPORT)
|| ((tSymbol->Type == SYM_LOCAL)
&& ((*ppLSect) == tSymbol->pSection))) {
if (strcmp(tSymbol->pzName, name) == 0) {
struct sSection **ppSect = &pSections;
while (*ppSect)
ppSect = &((*ppSect)->pNext);
*ppSect = *ppLSect;
*ppLSect = (*ppLSect)->pNext;
(*ppSect)->pNext = NULL;
return 1;
}
}
}
ppLSect = &((*ppLSect)->pNext);
}
return 0;
}
void AddNeededModules(void)
{
struct sSection *pSect;
if ((options & OPT_SMART_C_LINK) == 0) {
struct sSection **ppLSect;
ppLSect = &pLibSections;
while (*ppLSect) {
struct sSection **ppSect = &pSections;
while (*ppSect)
ppSect = &((*ppSect)->pNext);
*ppSect = *ppLSect;
*ppLSect = (*ppLSect)->pNext;
(*ppSect)->pNext = NULL;
/* ppLSect=&((*ppLSect)->pNext); */
}
return;
}
if (options & OPT_SMART_C_LINK) {
if (!addmodulecontaining(smartlinkstartsymbol)) {
errx(1, "Can't find start symbol '%s'",
smartlinkstartsymbol);
} else {
printf("Smart linking with symbol '%s'\n",
smartlinkstartsymbol);
}
}
pSect = pSections;
while (pSect) {
int32_t i;
for (i = 0; i < pSect->nNumberOfSymbols; i += 1) {
if ((pSect->tSymbols[i]->Type == SYM_IMPORT)
|| (pSect->tSymbols[i]->Type == SYM_LOCAL)) {
if (!symboldefined(pSect->tSymbols[i]->pzName))
addmodulecontaining(pSect->tSymbols[i]->pzName);
}
}
pSect = pSect->pNext;
}
}

View File

@@ -1,141 +1,162 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <stdint.h> #include <sys/types.h>
#include <stdio.h> #include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdio.h>
#include "extern/err.h" #include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "link/object.h" #include "link/object.h"
#include "link/output.h" #include "link/symbol.h"
#include "link/section.h"
#include "link/assign.h" #include "link/assign.h"
#include "link/patch.h" #include "link/patch.h"
#include "link/mylink.h" #include "link/output.h"
#include "link/mapfile.h"
#include "link/main.h"
#include "link/library.h"
#include "extern/err.h"
#include "version.h" #include "version.h"
enum eBlockType { bool isDmgMode; /* -d */
BLOCK_COMMENT, char const *linkerScriptName; /* -l */
BLOCK_OBJECTS, char const *mapFileName; /* -m */
BLOCK_LIBRARIES, char const *symFileName; /* -n */
BLOCK_OUTPUT char const *overlayFileName; /* -O */
}; char const *outputFileName; /* -o */
uint8_t padValue; /* -p */
bool is32kMode; /* -t */
bool beVerbose; /* -v */
bool isWRA0Mode; /* -w */
int32_t options; FILE *openFile(char const *fileName, char const *mode)
int32_t fillchar;
char *smartlinkstartsymbol;
/*
* Print the usagescreen
*/
static void print_usage(void)
{ {
printf( if (!fileName)
"usage: rgblink [-dtVw] [-l linkerscript] [-m mapfile] [-n symfile] [-O overlay]\n" return NULL;
" [-o outfile] [-p pad_value] [-s symbol] file [...]\n");
exit(1); FILE *file = fopen(fileName, mode);
if (!file)
err(1, "Could not open file \"%s\"", fileName);
return file;
} }
/* /**
* The main routine * Prints the program's usage to stdout.
*/ */
static void printUsage(void)
{
puts("usage: rgblink [-dtVvw] [-l linkerscript] [-m mapfile] [-n symfile] [-O overlay]\n"
" [-o outfile] [-p pad_value] [-s symbol] file [...]");
}
/**
* Cleans up what has been done
* Mostly here to please tools such as `valgrind` so actual errors can be seen
*/
static void cleanup(void)
{
obj_Cleanup();
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ch; char optionChar;
char *ep; char *endptr; /* For error checking with `strtol` */
unsigned long value; /* For storing `strtoul`'s return value */
if (argc == 1) /* Parse options */
print_usage(); while ((optionChar = getopt(argc, argv, "dl:m:n:O:o:p:s:tVvw")) != -1) {
switch (optionChar) {
while ((ch = getopt(argc, argv, "dl:m:n:O:o:p:s:tVw")) != -1) { case 'd':
switch (ch) { isDmgMode = true;
isWRA0Mode = true;
break;
case 'l': case 'l':
SetLinkerscriptName(optarg); linkerScriptName = optarg;
break; break;
case 'm': case 'm':
SetMapfileName(optarg); mapFileName = optarg;
break; break;
case 'n': case 'n':
SetSymfileName(optarg); symFileName = optarg;
break;
case 'o':
out_Setname(optarg);
break; break;
case 'O': case 'O':
out_SetOverlayname(optarg); overlayFileName = optarg;
options |= OPT_OVERLAY; break;
case 'o':
outputFileName = optarg;
break; break;
case 'p': case 'p':
fillchar = strtoul(optarg, &ep, 0); value = strtoul(optarg, &endptr, 0);
if (optarg[0] == '\0' || *ep != '\0') if (optarg[0] == '\0' || *endptr != '\0')
errx(1, "Invalid argument for option 'p'"); errx(1, "Invalid argument for option 'p'");
if (fillchar < 0 || fillchar > 0xFF) if (value > 0xFF)
errx(1, "Argument for option 'p' must be between 0 and 0xFF"); errx(1, "Argument for 'p' must be a byte (between 0 and 0xFF)");
padValue = value;
break; break;
case 's': case 's':
options |= OPT_SMART_C_LINK; /* FIXME: nobody knows what this does, figure it out */
smartlinkstartsymbol = optarg; (void)optarg;
warnx("Nobody has any idea what `-s` does");
break; break;
case 't': case 't':
options |= OPT_TINY; is32kMode = true;
break;
case 'd':
/*
* Set to set WRAM as a single continuous block as on
* DMG. All WRAM sections must be WRAM0 as bankable WRAM
* sections do not exist in this mode. A WRAMX section
* will raise an error. VRAM bank 1 can't be used if
* this option is enabled either.
*
* This option implies OPT_CONTWRAM.
*/
options |= OPT_DMG_MODE;
/* FALLTHROUGH */
case 'w':
/*
* Set to set WRAM as a single continuous block as on
* DMG. All WRAM sections must be WRAM0 as bankable WRAM
* sections do not exist in this mode. A WRAMX section
* will raise an error.
*/
options |= OPT_CONTWRAM;
break; break;
case 'V': case 'V':
printf("rgblink %s\n", get_package_version_string()); printf("rgblink %s\n", get_package_version_string());
exit(0); exit(0);
case 'v':
beVerbose = true;
break;
case 'w':
isWRA0Mode = true;
break;
default: default:
print_usage(); printUsage();
/* NOTREACHED */ exit(1);
} }
} }
argc -= optind;
argv += optind;
if (argc == 0) int curArgIndex = optind;
print_usage();
for (int32_t i = 0; i < argc; ++i) /* If no input files were specified, the user must have screwed up */
obj_Readfile(argv[i]); if (curArgIndex == argc) {
fprintf(stderr, "No input files");
printUsage();
exit(1);
}
AddNeededModules(); /* Patch the size array depending on command-line options */
AssignSections(); if (is32kMode)
CreateSymbolTable(); maxsize[SECTTYPE_ROM0] = 0x8000;
Patch(); if (isWRA0Mode)
Output(); maxsize[SECTTYPE_WRAM0] = 0x2000;
CloseMapfile();
return 0; /* Patch the bank ranges array depending on command-line options */
if (isDmgMode)
bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM;
/* Read all object files first, */
while (curArgIndex < argc)
obj_ReadFile(argv[curArgIndex++]);
/* then process them, */
obj_DoSanityChecks();
assign_AssignSections();
assign_Cleanup();
/* and finally output the result. */
patch_ApplyPatches();
out_WriteFiles();
/* Do cleanup before quitting, though. */
cleanup();
} }

View File

@@ -1,151 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extern/err.h"
#include "link/assign.h"
#include "link/main.h"
#include "link/mylink.h"
static int32_t currentbank;
static int32_t sfbank;
static FILE *mf;
static FILE *sf;
void SetMapfileName(char *name)
{
mf = fopen(name, "w");
if (mf == NULL)
err(1, "Cannot open mapfile '%s'", name);
}
void SetSymfileName(char *name)
{
sf = fopen(name, "w");
if (sf == NULL)
err(1, "Cannot open symfile '%s'", name);
fprintf(sf, "; File generated by rgblink\n\n");
}
void CloseMapfile(void)
{
if (mf) {
fclose(mf);
mf = NULL;
}
if (sf) {
fclose(sf);
sf = NULL;
}
}
void MapfileInitBank(int32_t bank)
{
if ((bank < 0) || (bank >= BANK_INDEX_MAX))
errx(1, "%s: Unknown bank %d\n", __func__, bank);
if (mf) {
currentbank = bank;
if (BankIndexIsROM0(bank)) {
fprintf(mf, "ROM Bank #0 (HOME):\n");
} else if (BankIndexIsROMX(bank)) {
fprintf(mf, "ROM Bank #%d:\n",
bank - BANK_INDEX_ROMX + 1);
} else if (BankIndexIsWRAM0(bank)) {
fprintf(mf, "WRAM Bank #0:\n");
} else if (BankIndexIsWRAMX(bank)) {
fprintf(mf, "WRAM Bank #%d:\n",
bank - BANK_INDEX_WRAMX + 1);
} else if (BankIndexIsVRAM(bank)) {
fprintf(mf, "VRAM Bank #%d:\n", bank - BANK_INDEX_VRAM);
} else if (BankIndexIsOAM(bank)) {
fprintf(mf, "OAM:\n");
} else if (BankIndexIsHRAM(bank)) {
fprintf(mf, "HRAM:\n");
} else if (BankIndexIsSRAM(bank)) {
fprintf(mf, "SRAM Bank #%d:\n", bank - BANK_INDEX_SRAM);
}
}
if (sf) {
if (BankIndexIsROM0(bank))
sfbank = 0;
else if (BankIndexIsROMX(bank))
sfbank = bank - BANK_INDEX_ROMX + 1;
else if (BankIndexIsWRAM0(bank))
sfbank = 0;
else if (BankIndexIsWRAMX(bank))
sfbank = bank - BANK_INDEX_WRAMX + 1;
else if (BankIndexIsVRAM(bank))
sfbank = bank - BANK_INDEX_VRAM;
else if (BankIndexIsOAM(bank))
sfbank = 0;
else if (BankIndexIsHRAM(bank))
sfbank = 0;
else if (BankIndexIsSRAM(bank))
sfbank = bank - BANK_INDEX_SRAM;
else
sfbank = 0;
}
}
void MapfileWriteSection(const struct sSection *pSect)
{
int32_t i;
if (mf) {
if (pSect->nByteSize > 0) {
fprintf(mf, " SECTION: $%04X-$%04X ($%04X bytes) [\"%s\"]\n",
pSect->nOrg, pSect->nOrg + pSect->nByteSize - 1,
pSect->nByteSize, pSect->pzName);
} else {
fprintf(mf, " SECTION: $%04X ($0 bytes) [\"%s\"]\n",
pSect->nOrg, pSect->pzName);
}
}
for (i = 0; i < pSect->nNumberOfSymbols; i += 1) {
const struct sSymbol *pSym = pSect->tSymbols[i];
/* Don't print '@' */
if (strcmp(pSym->pzName, "@") == 0)
continue;
if ((pSym->pSection == pSect) && (pSym->Type != SYM_IMPORT)) {
if (mf) {
fprintf(mf, " $%04X = %s\n",
pSym->nOffset + pSect->nOrg,
pSym->pzName);
}
if (sf) {
fprintf(sf, "%02X:%04X %s\n", sfbank,
pSym->nOffset + pSect->nOrg,
pSym->pzName);
}
}
}
}
void MapfileCloseBank(int32_t slack)
{
if (!mf)
return;
if (slack == MaxAvail[currentbank])
fprintf(mf, " EMPTY\n\n");
else
fprintf(mf, " SLACK: $%04X bytes\n\n", slack);
}

View File

@@ -1,381 +1,500 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
/*
* Here we have the routines that read an objectfile
*/
#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include "common.h" #include "link/object.h"
#include "link/main.h"
#include "link/symbol.h"
#include "link/section.h"
#include "link/assign.h"
#include "extern/err.h" #include "extern/err.h"
#include "linkdefs.h"
#include "common.h"
#include "link/assign.h" static struct SymbolList {
#include "link/mylink.h" size_t nbSymbols;
#include "link/main.h" struct Symbol **symbolList;
struct SymbolList *next;
} *symbolLists;
struct sSymbol **tSymbols; /***** Helper functions for reading object files *****/
struct sSection *pSections;
struct sSection *pLibSections;
uint8_t oReadLib;
/* /*
* Read 32-bit values with the correct endianness * Internal, DO NOT USE.
* For helper wrapper macros defined below, such as `tryReadlong`
*/ */
static int32_t readlong(FILE *f) #define tryRead(func, type, errval, var, file, ...) \
do { \
FILE *tmpFile = file; \
type tmpVal = func(tmpFile); \
if (tmpVal == (errval)) { \
errx(1, __VA_ARGS__, feof(tmpFile) \
? "Unexpected end of file" \
: strerror(errno)); \
} \
var = tmpVal; \
} while (0)
/**
* Reads an unsigned long (32-bit) value from a file.
* @param file The file to read from. This will read 4 bytes from the file.
* @return The value read, cast to a int64_t, or -1 on failure.
*/
static int64_t readlong(FILE *file)
{ {
uint32_t r; uint32_t value = 0;
r = ((uint32_t)(uint8_t)fgetc(f)); /* Read the little-endian value byte by byte */
r |= ((uint32_t)(uint8_t)fgetc(f)) << 8; for (uint8_t shift = 0; shift < sizeof(value) * CHAR_BIT; shift += 8) {
r |= ((uint32_t)(uint8_t)fgetc(f)) << 16; int byte = getc(file);
r |= ((uint32_t)(uint8_t)fgetc(f)) << 24;
return (int32_t)r; if (byte == EOF)
return INT64_MAX;
value |= (uint8_t)byte << shift;
}
return value;
} }
/* /**
* Read a NULL terminated string from a file * Helper macro for reading longs from a file, and errors out if it fails to.
* Not as a function to avoid overhead in the general case.
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
* @param var The variable to stash the number into
* @param file The file to read from. Its position will be advanced
* @param ... A format string and related arguments; note that an extra string
* argument is provided, the reason for failure
*/ */
int32_t readasciiz(char **dest, FILE *f) #define tryReadlong(var, file, ...) \
{ tryRead(readlong, int64_t, INT64_MAX, var, file, __VA_ARGS__)
size_t r = 0;
size_t bufferLength = 16; /* There is no `readbyte`, just use `fgetc` or `getc`. */
char *start = malloc(bufferLength);
char *s = start;
if (!s) /**
err(1, "%s: Couldn't allocate memory", __func__); * Helper macro for reading bytes from a file, and errors out if it fails to.
* Differs from `tryGetc` in that the backing function is fgetc(1).
while (((*s++) = fgetc(f)) != 0) { * Not as a function to avoid overhead in the general case.
r += 1; * TODO: maybe mark the condition as `unlikely`; how to do that portably?
* @param var The variable to stash the number into
if (r >= bufferLength) { * @param file The file to read from. Its position will be advanced
bufferLength *= 2; * @param ... A format string and related arguments; note that an extra string
start = realloc(start, bufferLength); * argument is provided, the reason for failure
if (!start) {
err(1, "%s: Couldn't allocate memory",
__func__);
}
s = start + r;
}
}
*dest = start;
return (r + 1);
}
/*
* Allocate a new section and link it into the list
*/ */
struct sSection *AllocSection(void) #define tryFgetc(var, file, ...) \
{ tryRead(fgetc, int, EOF, var, file, __VA_ARGS__)
struct sSection **ppSections;
if (oReadLib == 1) /**
ppSections = &pLibSections; * Helper macro for reading bytes from a file, and errors out if it fails to.
else * Differs from `tryGetc` in that the backing function is fgetc(1).
ppSections = &pSections; * Not as a function to avoid overhead in the general case.
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
while (*ppSections) * @param var The variable to stash the number into
ppSections = &((*ppSections)->pNext); * @param file The file to read from. Its position will be advanced
* @param ... A format string and related arguments; note that an extra string
*ppSections = malloc(sizeof **ppSections); * argument is provided, the reason for failure
if (!*ppSections)
err(1, "%s: Couldn't allocate memory", __func__);
(*ppSections)->tSymbols = tSymbols;
(*ppSections)->pNext = NULL;
(*ppSections)->pPatches = NULL;
(*ppSections)->oAssigned = 0;
return *ppSections;
}
/*
* Read a symbol from a file
*/ */
struct sSymbol *obj_ReadSymbol(FILE *f, char *tzObjectfile) #define tryGetc(var, file, ...) \
{ tryRead(getc, int, EOF, var, file, __VA_ARGS__)
struct sSymbol *pSym;
pSym = malloc(sizeof(*pSym)); /**
if (!pSym) * Reads a '\0'-terminated string from a file.
err(1, "%s: Couldn't allocate memory", __func__); * @param file The file to read from. The file position will be advanced.
* @return The string read, or NULL on failure.
readasciiz(&pSym->pzName, f); * If a non-NULL pointer is returned, make sure to `free` it when done!
pSym->Type = (enum eSymbolType)fgetc(f);
pSym->pzObjFileName = tzObjectfile;
if (pSym->Type != SYM_IMPORT) {
readasciiz(&pSym->pzFileName, f);
pSym->nFileLine = readlong(f);
pSym->nSectionID = readlong(f);
pSym->nOffset = readlong(f);
}
return pSym;
}
/*
* RGB object reader routines
*/ */
struct sSection *obj_ReadRGBSection(FILE *f) static char *readstr(FILE *file)
{ {
struct sSection *pSection; /* Default buffer size, have it close to the average string length */
char *pzName; size_t capacity = 32 / 2;
size_t index = -1;
/* Force the first iteration to allocate */
char *str = NULL;
readasciiz(&pzName, f); do {
if (IsSectionNameInUse(pzName)) /* Prepare going to next char */
errx(1, "Section name \"%s\" is already in use.", pzName); index++;
pSection = AllocSection(); /* If the buffer isn't suitable to write the next char... */
pSection->pzName = pzName; if (index >= capacity || !str) {
capacity *= 2;
pSection->nByteSize = readlong(f); str = realloc(str, capacity);
pSection->Type = (enum eSectionType)fgetc(f); /* End now in case of error */
pSection->nOrg = readlong(f); if (!str)
pSection->nBank = readlong(f); return NULL;
pSection->nAlign = readlong(f);
if ((options & OPT_TINY) && (pSection->Type == SECT_ROMX))
errx(1, "ROMX sections can't be used with option -t.");
if ((options & OPT_CONTWRAM) && (pSection->Type == SECT_WRAMX))
errx(1, "WRAMX sections can't be used with options -w or -d.");
if (options & OPT_DMG_MODE) {
/* WRAMX sections are checked for OPT_CONTWRAM */
if (pSection->Type == SECT_VRAM && pSection->nBank == 1)
errx(1, "VRAM bank 1 can't be used with option -d.");
}
uint32_t maxsize = 0;
/* Verify that the section isn't too big */
switch (pSection->Type) {
case SECT_ROM0:
maxsize = (options & OPT_TINY) ? 0x8000 : 0x4000;
break;
case SECT_ROMX:
maxsize = 0x4000;
break;
case SECT_VRAM:
case SECT_SRAM:
maxsize = 0x2000;
break;
case SECT_WRAM0:
maxsize = (options & OPT_CONTWRAM) ? 0x2000 : 0x1000;
break;
case SECT_WRAMX:
maxsize = 0x1000;
break;
case SECT_OAM:
maxsize = 0xA0;
break;
case SECT_HRAM:
maxsize = 0x7F;
break;
default:
errx(1, "Section \"%s\" has an invalid section type.", pzName);
break;
}
if (pSection->nByteSize > maxsize) {
errx(1, "Section \"%s\" is bigger than the max size for that type: 0x%X > 0x%X",
pzName, pSection->nByteSize, maxsize);
}
/*
* If the section doesn't contain data, it is ready
*/
if ((pSection->Type != SECT_ROMX) && (pSection->Type != SECT_ROM0))
return pSection;
/* If there is no data to read, exit */
if (pSection->nByteSize == 0) {
/* Skip number of patches */
readlong(f);
pSection->pData = NULL;
return pSection;
}
pSection->pData = malloc(pSection->nByteSize);
if (!pSection->pData)
err(1, "%s: Couldn't allocate memory", __func__);
int32_t nNumberOfPatches;
struct sPatch **ppPatch, *pPatch;
if (fread(pSection->pData, sizeof(uint8_t), pSection->nByteSize, f)
!= pSection->nByteSize) {
err(1, "%s: Read error", __func__);
}
nNumberOfPatches = readlong(f);
ppPatch = &pSection->pPatches;
/*
* And patches...
*/
while (nNumberOfPatches--) {
pPatch = malloc(sizeof(*pPatch));
if (!pPatch)
err(1, "%s: Couldn't allocate memory", __func__);
*ppPatch = pPatch;
readasciiz(&pPatch->pzFilename, f);
pPatch->nLineNo = readlong(f);
pPatch->nOffset = readlong(f);
pPatch->Type = (enum ePatchType)fgetc(f);
pPatch->nRPNSize = readlong(f);
if (pPatch->nRPNSize > 0) {
pPatch->pRPN = malloc(pPatch->nRPNSize);
if (!pPatch->pRPN) {
err(1, "%s: Couldn't allocate memory",
__func__);
}
if (fread(pPatch->pRPN, sizeof(uint8_t),
pPatch->nRPNSize, f) != pPatch->nRPNSize) {
errx(1, "%s: Read error", __func__);
}
} else {
pPatch->pRPN = NULL;
} }
pPatch->pNext = NULL; /* Read char */
ppPatch = &(pPatch->pNext); int byte = getc(file);
}
return pSection; if (byte == EOF)
return NULL;
str[index] = byte;
} while (str[index]);
return str;
} }
void obj_ReadRGB(FILE *pObjfile, char *tzObjectfile) /**
* Helper macro for reading bytes from a file, and errors out if it fails to.
* Not as a function to avoid overhead in the general case.
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
* @param var The variable to stash the string into
* @param file The file to read from. Its position will be advanced
* @param ... A format string and related arguments; note that an extra string
* argument is provided, the reason for failure
*/
#define tryReadstr(var, file, ...) \
tryRead(readstr, char*, NULL, var, file, __VA_ARGS__)
/***** Functions to parse object files *****/
/**
* Reads a RGB6 symbol from a file.
* @param file The file to read from
* @param symbol The struct to fill
* @param fileName The filename to report in errors
*/
static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
{ {
struct sSection *pFirstSection; tryReadstr(symbol->name, file, "%s: Cannot read symbol name: %s",
int32_t nNumberOfSymbols, nNumberOfSections, i; fileName);
tryGetc(symbol->type, file, "%s: Cannot read \"%s\"'s type: %s",
nNumberOfSymbols = readlong(pObjfile); fileName, symbol->name);
nNumberOfSections = readlong(pObjfile); /* If the symbol is defined in this file, read its definition */
if (symbol->type != SYMTYPE_IMPORT) {
/* First comes the symbols */ symbol->objFileName = fileName;
tryReadstr(symbol->fileName, file,
if (nNumberOfSymbols) { "%s: Cannot read \"%s\"'s file name: %s",
tSymbols = malloc(nNumberOfSymbols * sizeof(*tSymbols)); fileName, symbol->name);
if (!tSymbols) tryReadlong(symbol->lineNo, file,
err(1, "%s: Couldn't allocate memory", __func__); "%s: Cannot read \"%s\"'s line number: %s",
fileName, symbol->name);
for (i = 0; i < nNumberOfSymbols; i += 1) tryReadlong(symbol->sectionID, file,
tSymbols[i] = obj_ReadSymbol(pObjfile, tzObjectfile); "%s: Cannot read \"%s\"'s section ID: %s",
fileName, symbol->name);
tryReadlong(symbol->offset, file,
"%s: Cannot read \"%s\"'s value: %s",
fileName, symbol->name);
} else { } else {
tSymbols = NULL; symbol->sectionID = -1;
}
}
/**
* Reads a RGB6 patch from a file.
* @param file The file to read from
* @param patch The struct to fill
* @param fileName The filename to report in errors
* @param i The number of the patch to report in errors
*/
static void readPatch(FILE *file, struct Patch *patch,
char const *fileName, char const *sectName, uint32_t i)
{
tryReadstr(patch->fileName, file,
"%s: Unable to read \"%s\"'s patch #%u's name: %s",
fileName, sectName, i);
tryReadlong(patch->lineNo, file,
"%s: Unable to read \"%s\"'s patch #%u's line number: %s",
fileName, sectName, i);
tryReadlong(patch->offset, file,
"%s: Unable to read \"%s\"'s patch #%u's offset: %s",
fileName, sectName, i);
tryGetc(patch->type, file,
"%s: Unable to read \"%s\"'s patch #%u's type: %s",
fileName, sectName, i);
tryReadlong(patch->rpnSize, file,
"%s: Unable to read \"%s\"'s patch #%u's RPN size: %s",
fileName, sectName, i);
uint8_t *rpnExpression =
malloc(sizeof(*rpnExpression) * patch->rpnSize);
size_t nbElementsRead = fread(rpnExpression, sizeof(*rpnExpression),
patch->rpnSize, file);
if (nbElementsRead != patch->rpnSize)
errx(1, "%s: Cannot read \"%s\"'s patch #%u's RPN expression: %s",
fileName, sectName, i);
patch->rpnExpression = rpnExpression;
}
/**
* Reads a RGB6 section from a file.
* @param file The file to read from
* @param section The struct to fill
* @param fileName The filename to report in errors
*/
static void readSection(FILE *file, struct Section *section,
char const *fileName)
{
int32_t tmp;
tryReadstr(section->name, file, "%s: Cannot read section name: %s",
fileName);
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s",
fileName, section->name);
if (tmp < 0 || tmp > UINT16_MAX)
errx(1, "\"%s\"'s section size (%d) is invalid", section->name,
tmp);
section->size = tmp;
tryGetc(section->type, file, "%s: Cannot read \"%s\"'s type: %s",
fileName, section->name);
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s org: %s",
fileName, section->name);
section->isAddressFixed = tmp >= 0;
if (tmp > UINT16_MAX)
errx(1, "\"%s\" is too large (%d)", tmp);
section->org = tmp;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s bank: %s",
fileName, section->name);
section->isBankFixed = tmp >= 0;
section->bank = tmp;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment: %s",
fileName, section->name);
section->isAlignFixed = tmp != 1;
section->alignMask = tmp - 1;
if (sect_HasData(section->type)) {
/* Ensure we never allocate 0 bytes */
uint8_t *data = malloc(sizeof(*data) * section->size + 1);
if (!data)
err(1, "%s: Unable to read \"%s\"'s data", fileName,
section->name);
if (section->size) {
size_t nbElementsRead = fread(data, sizeof(*data),
section->size, file);
if (nbElementsRead != section->size)
errx(1, "%s: Cannot read \"%s\"'s data: %s",
fileName, section->name,
feof(file) ? "Unexpected end of file"
: strerror(errno));
}
section->data = data;
tryReadlong(section->nbPatches, file,
"%s: Cannot read \"%s\"'s number of patches: %s",
fileName, section->name);
struct Patch *patches =
malloc(sizeof(*patches) * section->nbPatches + 1);
if (!patches)
err(1, "%s: Unable to read \"%s\"'s patches", fileName,
section->name);
for (uint32_t i = 0; i < section->nbPatches; i++)
readPatch(file, &patches[i], fileName, section->name,
i);
section->patches = patches;
}
}
/**
* Links a symbol to a section, keeping the section's symbol list sorted.
* @param symbol The symbol to link
* @param section The section to link
*/
static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
{
uint32_t a = 0, b = section->nbSymbols;
while (a != b) {
uint32_t c = (a + b) / 2;
if (section->symbols[c]->offset > symbol->offset)
b = c;
else
a = c + 1;
} }
/* Next we have the sections */ struct Symbol const *tmp = symbol;
pFirstSection = NULL; for (uint32_t i = a; i <= section->nbSymbols; i++) {
while (nNumberOfSections--) { symbol = tmp;
struct sSection *pNewSection; tmp = section->symbols[i];
section->symbols[i] = symbol;
pNewSection = obj_ReadRGBSection(pObjfile);
pNewSection->nNumberOfSymbols = nNumberOfSymbols;
if (pFirstSection == NULL)
pFirstSection = pNewSection;
} }
/* section->nbSymbols++;
* Fill in the pSection entry in the symbolstructure. }
* This REALLY needs some cleaning up... but, hey, it works
*/
for (i = 0; i < nNumberOfSymbols; i += 1) { /**
struct sSection *pConvSect = pFirstSection; * Reads a RGB6 object file.
* @param file The file to read from
* @param fileName The filename to report in errors
*/
static void readRGB6File(FILE *file, char const *fileName)
{
uint32_t nbSymbols;
uint32_t nbSections;
if ((tSymbols[i]->Type != SYM_IMPORT) && tryReadlong(nbSymbols, file, "%s: Cannot read number of symbols: %s",
(tSymbols[i]->nSectionID != -1)) { fileName);
int32_t j = 0; tryReadlong(nbSections, file, "%s: Cannot read number of sections: %s",
fileName);
while (j != tSymbols[i]->nSectionID) { nbSectionsToAssign += nbSections;
j += 1;
pConvSect = pConvSect->pNext; /* This file's symbols, kept to link sections to them */
} struct Symbol **fileSymbols =
tSymbols[i]->pSection = pConvSect; malloc(sizeof(*fileSymbols) * nbSymbols + 1);
if (!fileSymbols)
err(1, "Failed to get memory for %s's symbols", fileName);
struct SymbolList *symbolList = malloc(sizeof(*symbolList));
if (!symbolList)
err(1, "Failed to register %s's symbol list", fileName);
symbolList->symbolList = fileSymbols;
symbolList->nbSymbols = nbSymbols;
symbolList->next = symbolLists;
symbolLists = symbolList;
uint32_t nbSymPerSect[nbSections];
memset(nbSymPerSect, 0, sizeof(nbSymPerSect));
verbosePrint("Reading %u symbols...\n", nbSymbols);
for (uint32_t i = 0; i < nbSymbols; i++) {
/* Read symbol */
struct Symbol *symbol = malloc(sizeof(*symbol));
if (!symbol)
err(1, "%s: Couldn't create new symbol", fileName);
readSymbol(file, symbol, fileName);
fileSymbols[i] = symbol;
if (symbol->type == SYMTYPE_EXPORT)
sym_AddSymbol(symbol);
if (symbol->sectionID != -1)
nbSymPerSect[symbol->sectionID]++;
}
/* This file's sections, stored in a table to link symbols to them */
struct Section *fileSections[nbSections ? nbSections : 1];
verbosePrint("Reading %u sections...\n", nbSections);
for (uint32_t i = 0; i < nbSections; i++) {
/* Read section */
struct Section *section = malloc(sizeof(*section));
if (!section)
err(1, "%s: Couldn't create new section", fileName);
readSection(file, section, fileName);
section->fileSymbols = fileSymbols;
sect_AddSection(section);
fileSections[i] = section;
if (nbSymPerSect[i]) {
section->symbols = malloc(sizeof(*section->symbols)
* nbSymPerSect[i]);
if (!section->symbols)
err(1, "%s: Couldn't link to symbols");
} else { } else {
tSymbols[i]->pSection = NULL; section->symbols = NULL;
}
section->nbSymbols = 0;
}
/* Give symbols pointers to their sections */
for (uint32_t i = 0; i < nbSymbols; i++) {
int32_t sectionID = fileSymbols[i]->sectionID;
if (sectionID == -1) {
fileSymbols[i]->section = NULL;
} else {
fileSymbols[i]->section = fileSections[sectionID];
/* Give the section a pointer to the symbol as well */
linkSymToSect(fileSymbols[i], fileSections[sectionID]);
} }
} }
} }
/* /**
* The main objectfileloadroutine (phew) * Reads an object file of any supported format
* @param fileName The filename to report for errors
*/ */
void obj_ReadOpenFile(FILE *pObjfile, char *tzObjectfile) void obj_ReadFile(char const *fileName)
{ {
char tzHeader[strlen(RGBDS_OBJECT_VERSION_STRING) + 1]; FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
if (fread(tzHeader, sizeof(char), strlen(RGBDS_OBJECT_VERSION_STRING), if (!file) {
pObjfile) != strlen(RGBDS_OBJECT_VERSION_STRING)) { err(1, "Could not open file %s", fileName);
errx(1, "%s: Read error", tzObjectfile); return;
} }
tzHeader[strlen(RGBDS_OBJECT_VERSION_STRING)] = 0; /* Begin by reading the magic bytes and version number */
uint8_t versionNumber;
int matchedElems = fscanf(file, RGBDS_OBJECT_VERSION_STRING,
&versionNumber);
if (strncmp(tzHeader, RGBDS_OBJECT_VERSION_STRING, if (matchedElems != 1)
strlen(RGBDS_OBJECT_VERSION_STRING)) == 0) { errx(1, "\"%s\" is not a RGBDS object file", fileName);
obj_ReadRGB(pObjfile, tzObjectfile); /* TODO: support other versions? */
} else { if (versionNumber != 6)
int32_t i; errx(1, "\"%s\" is an incompatible version %hhu object file",
fileName, versionNumber);
for (i = 0; i < strlen(RGBDS_OBJECT_VERSION_STRING); i++) verbosePrint("Reading object file %s, version %hhu\n",
if (!isprint(tzHeader[i])) fileName, versionNumber);
tzHeader[i] = '?';
errx(1, "%s: Invalid file or object file version [%s]", readRGB6File(file, fileName);
tzObjectfile, tzHeader);
fclose(file);
}
void obj_DoSanityChecks(void)
{
sect_DoSanityChecks();
}
static void freeSection(struct Section *section, void *arg)
{
(void)arg;
free(section->name);
if (sect_HasData(section->type)) {
free(section->data);
for (int32_t i = 0; i < section->nbPatches; i++) {
struct Patch *patch = &section->patches[i];
free(patch->fileName);
free(patch->rpnExpression);
}
free(section->patches);
}
free(section->symbols);
free(section);
}
static void freeSymbol(struct Symbol *symbol)
{
free(symbol->name);
if (symbol->type != SYMTYPE_IMPORT)
free(symbol->fileName);
free(symbol);
}
void obj_Cleanup(void)
{
sym_CleanupSymbols();
sect_ForEach(freeSection, NULL);
sect_CleanupSections();
struct SymbolList *list = symbolLists;
while (list) {
for (size_t i = 0; i < list->nbSymbols; i++)
freeSymbol(list->symbolList[i]);
free(list->symbolList);
struct SymbolList *next = list->next;
free(list);
list = next;
} }
} }
void obj_Readfile(char *tzObjectfile)
{
FILE *pObjfile;
if (options & OPT_SMART_C_LINK)
oReadLib = 1;
else
oReadLib = 0;
pObjfile = fopen(tzObjectfile, "rb");
if (pObjfile == NULL)
err(1, "Unable to open object '%s'", tzObjectfile);
obj_ReadOpenFile(pObjfile, tzObjectfile);
fclose(pObjfile);
oReadLib = 0;
}
int32_t file_Length(FILE *f)
{
uint32_t r, p;
p = ftell(f);
fseek(f, 0, SEEK_END);
r = ftell(f);
fseek(f, p, SEEK_SET);
return r;
}

View File

@@ -1,174 +1,394 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <stdint.h>
#include "link/output.h"
#include "link/main.h"
#include "link/section.h"
#include "link/symbol.h"
#include "extern/err.h" #include "extern/err.h"
#include "link/mylink.h" FILE *outputFile;
#include "link/mapfile.h" FILE *overlayFile;
#include "link/main.h" FILE *symFile;
#include "link/assign.h" FILE *mapFile;
char *tzOutname; struct SortedSection {
char *tzOverlayname; struct Section const *section;
struct SortedSection *next;
};
int32_t MaxOverlayBank; static struct {
uint32_t nbBanks;
struct SortedSections {
struct SortedSection *sections;
struct SortedSection *zeroLenSections;
} *banks;
} sections[SECTTYPE_INVALID];
void writehome(FILE *f, FILE *f_overlay) void out_AddSection(struct Section const *section)
{ {
const struct sSection *pSect; static uint32_t maxNbBanks[] = {
uint8_t *mem; [SECTTYPE_ROM0] = 1,
[SECTTYPE_ROMX] = UINT32_MAX,
[SECTTYPE_VRAM] = 2,
[SECTTYPE_SRAM] = UINT32_MAX,
[SECTTYPE_WRAM0] = 1,
[SECTTYPE_WRAMX] = 7,
[SECTTYPE_OAM] = 1,
[SECTTYPE_HRAM] = 1
};
mem = malloc(MaxAvail[BANK_INDEX_ROM0]); uint32_t targetBank = section->bank - bankranges[section->type][0];
if (!mem) uint32_t minNbBanks = targetBank + 1;
if (minNbBanks > maxNbBanks[section->type])
errx(1, "Section \"%s\" has invalid bank range (%u > %u)",
section->name, section->bank,
maxNbBanks[section->type] - 1);
if (minNbBanks > sections[section->type].nbBanks) {
sections[section->type].banks =
realloc(sections[section->type].banks,
sizeof(*sections[0].banks) * minNbBanks);
for (uint32_t i = sections[section->type].nbBanks;
i < minNbBanks; i++) {
sections[section->type].banks[i].sections = NULL;
sections[section->type].banks[i].zeroLenSections = NULL;
}
sections[section->type].nbBanks = minNbBanks;
}
if (!sections[section->type].banks)
err(1, "Failed to realloc banks");
struct SortedSection *newSection = malloc(sizeof(*newSection));
struct SortedSection **ptr = section->size
? &sections[section->type].banks[targetBank].sections
: &sections[section->type].banks[targetBank].zeroLenSections;
if (!newSection)
err(1, "Failed to add new section \"%s\"", section->name);
newSection->section = section;
while (*ptr && (*ptr)->section->org < section->org)
ptr = &(*ptr)->next;
newSection->next = *ptr;
*ptr = newSection;
}
/**
* Performs sanity checks on the overlay file.
*/
static void checkOverlay(void)
{
if (!overlayFile)
return; return;
if (f_overlay != NULL) { if (fseek(overlayFile, 0, SEEK_END) != 0) {
fseek(f_overlay, 0L, SEEK_SET); warnx("Overlay file is not seekable, cannot check if properly formed");
if (fread(mem, 1, MaxAvail[BANK_INDEX_ROM0], f_overlay) != return;
MaxAvail[BANK_INDEX_ROM0]) {
warnx("Failed to read data from overlay file.");
}
} else {
memset(mem, fillchar, MaxAvail[BANK_INDEX_ROM0]);
}
MapfileInitBank(0);
pSect = pSections;
while (pSect) {
if (pSect->Type == SECT_ROM0) {
memcpy(mem + pSect->nOrg, pSect->pData,
pSect->nByteSize);
MapfileWriteSection(pSect);
}
pSect = pSect->pNext;
} }
MapfileCloseBank(area_Avail(0)); long overlaySize = ftell(overlayFile);
fwrite(mem, 1, MaxAvail[BANK_INDEX_ROM0], f); if (overlaySize % 0x4000)
free(mem); errx(1, "Overlay file must have a size multiple of 0x4000");
/* Reset back to beginning */
fseek(overlayFile, 0, SEEK_SET);
uint32_t nbOverlayBanks = overlaySize / 0x4000 - 1;
if (nbOverlayBanks < 1)
errx(1, "Overlay must be at least 0x8000 bytes large");
if (nbOverlayBanks > sections[SECTTYPE_ROMX].nbBanks) {
sections[SECTTYPE_ROMX].banks =
realloc(sections[SECTTYPE_ROMX].banks,
sizeof(*sections[SECTTYPE_ROMX].banks) *
nbOverlayBanks);
if (!sections[SECTTYPE_ROMX].banks)
err(1, "Failed to realloc banks for overlay");
sections[SECTTYPE_ROMX].nbBanks = nbOverlayBanks;
}
} }
void writebank(FILE *f, FILE *f_overlay, int32_t bank) /**
* Write a ROM bank's sections to the output file.
* @param bankSections The bank's sections, ordered by increasing address
* @param baseOffset The address of the bank's first byte in GB address space
* @param size The size of the bank
*/
static void writeBank(struct SortedSection *bankSections, uint16_t baseOffset,
uint16_t size)
{ {
const struct sSection *pSect; uint16_t offset = 0;
uint8_t *mem;
mem = malloc(MaxAvail[bank]); while (bankSections) {
if (!mem) struct Section const *section = bankSections->section;
/* Output padding up to the next SECTION */
while (offset + baseOffset < section->org) {
putc_unlocked(overlayFile ? getc_unlocked(overlayFile)
: padValue,
outputFile);
offset++;
}
/* Output the section itself */
fwrite(section->data, sizeof(*section->data), section->size,
outputFile);
if (overlayFile) {
/* Skip bytes even with pipes */
for (uint16_t i = 0; i < section->size; i++)
getc_unlocked(overlayFile);
}
offset += section->size;
bankSections = bankSections->next;
}
while (offset < size) {
putc_unlocked(overlayFile ? getc_unlocked(overlayFile)
: padValue,
outputFile);
offset++;
}
}
/**
* Writes a ROM file to the output.
*/
static void writeROM(void)
{
outputFile = openFile(outputFileName, "wb");
overlayFile = openFile(overlayFileName, "rb");
checkOverlay();
if (outputFile) {
flockfile(outputFile);
if (overlayFile)
flockfile(overlayFile);
if (sections[SECTTYPE_ROM0].nbBanks > 0)
writeBank(sections[SECTTYPE_ROM0].banks[0].sections,
0x0000, 0x4000);
for (uint32_t i = 0 ; i < sections[SECTTYPE_ROMX].nbBanks; i++)
writeBank(sections[SECTTYPE_ROMX].banks[i].sections,
0x4000, 0x4000);
if (overlayFile)
funlockfile(overlayFile);
funlockfile(outputFile);
}
closeFile(outputFile);
closeFile(overlayFile);
}
/**
* Get the lowest section by address out of the two
* @param s1 One choice
* @param s2 The other
* @return The lowest section of the two, or the non-NULL one if applicable
*/
static struct SortedSection const **nextSection(struct SortedSection const **s1,
struct SortedSection const **s2)
{
if (!*s1)
return s2;
if (!*s2)
return s1;
return (*s1)->section->org < (*s2)->section->org ? s1 : s2;
}
/**
* Write a bank's contents to the sym file
* @param bankSections The bank's sections
*/
static void writeSymBank(struct SortedSections const *bankSections)
{
if (!symFile)
return; return;
if (f_overlay != NULL && bank <= MaxOverlayBank) { struct {
fseek(f_overlay, bank * 0x4000, SEEK_SET); struct SortedSection const *sections;
if (fread(mem, 1, MaxAvail[bank], f_overlay) != MaxAvail[bank]) #define sect sections->section /* Fake member as a shortcut */
warnx("Failed to read data from overlay file."); uint32_t i;
} else { struct Symbol const *sym;
memset(mem, fillchar, MaxAvail[bank]); uint16_t addr;
} } sectList = { .sections = bankSections->sections, .i = 0 },
MapfileInitBank(bank); zlSectList = { .sections = bankSections->zeroLenSections, .i = 0 },
*minSectList;
pSect = pSections; for (;;) {
while (pSect) { while (sectList.sections
if (pSect->Type == SECT_ROMX && pSect->nBank == bank) { && sectList.i == sectList.sect->nbSymbols) {
memcpy(mem + pSect->nOrg - 0x4000, pSect->pData, sectList.sections = sectList.sections->next;
pSect->nByteSize); sectList.i = 0;
MapfileWriteSection(pSect);
} }
pSect = pSect->pNext; while (zlSectList.sections
} && zlSectList.i == zlSectList.sect->nbSymbols) {
zlSectList.sections = zlSectList.sections->next;
MapfileCloseBank(area_Avail(bank)); zlSectList.i = 0;
fwrite(mem, 1, MaxAvail[bank], f);
free(mem);
}
void out_Setname(char *tzOutputfile)
{
tzOutname = tzOutputfile;
}
void out_SetOverlayname(char *tzOverlayfile)
{
tzOverlayname = tzOverlayfile;
}
void Output(void)
{
int32_t i;
FILE *f;
FILE *f_overlay = NULL;
/*
* Load overlay
*/
if (tzOverlayname) {
f_overlay = fopen(tzOverlayname, "rb");
if (!f_overlay) {
errx(1, "Failed to open overlay file %s\n",
tzOverlayname);
} }
fseek(f_overlay, 0, SEEK_END); if (!sectList.sections && !zlSectList.sections) {
break;
} else if (sectList.sections && zlSectList.sections) {
sectList.sym = sectList.sect->symbols[sectList.i];
zlSectList.sym = zlSectList.sect->symbols[zlSectList.i];
sectList.addr =
sectList.sym->offset + sectList.sect->org;
zlSectList.addr =
zlSectList.sym->offset + zlSectList.sect->org;
if (ftell(f_overlay) % 0x4000 != 0) minSectList = sectList.addr < zlSectList.addr
errx(1, "Overlay file must be aligned to 0x4000 bytes."); ? &sectList
: &zlSectList;
} else if (sectList.sections) {
sectList.sym = sectList.sect->symbols[sectList.i];
sectList.addr =
sectList.sym->offset + sectList.sect->org;
MaxOverlayBank = (ftell(f_overlay) / 0x4000) - 1; minSectList = &sectList;
} else {
zlSectList.sym = zlSectList.sect->symbols[zlSectList.i];
zlSectList.addr =
zlSectList.sym->offset + zlSectList.sect->org;
if (MaxOverlayBank < 1) minSectList = &zlSectList;
errx(1, "Overlay file must be at least 0x8000 bytes.");
if (MaxOverlayBank > MaxBankUsed)
MaxBankUsed = MaxOverlayBank;
}
/*
* Write ROM.
*/
f = fopen(tzOutname, "wb");
if (f != NULL) {
writehome(f, f_overlay);
for (i = 1; i <= MaxBankUsed; i += 1)
writebank(f, f_overlay, i);
fclose(f);
}
/*
* Close overlay
*/
if (tzOverlayname)
fclose(f_overlay);
/*
* Add regular sections to map and sym files.
*/
for (i = BANK_INDEX_WRAM0; i < BANK_INDEX_MAX; i++) {
const struct sSection *pSect;
MapfileInitBank(i);
pSect = pSections;
while (pSect) {
if (pSect->nBank == i)
MapfileWriteSection(pSect);
pSect = pSect->pNext;
} }
MapfileCloseBank(area_Avail(i)); fprintf(symFile, "%02x:%04x %s\n",
minSectList->sect->bank, minSectList->addr,
minSectList->sym->name);
minSectList->i++;
}
#undef sect
}
/**
* Write a bank's contents to the map file
* @param bankSections The bank's sections
*/
static void writeMapBank(struct SortedSections const *sectList,
enum SectionType type, uint32_t bank)
{
if (!mapFile)
return;
struct SortedSection const *section = sectList->sections;
struct SortedSection const *zeroLenSection = sectList->zeroLenSections;
fprintf(mapFile, "%s bank #%u:\n", typeNames[type],
bank + bankranges[type][0]);
uint16_t slack = maxsize[type];
while (section || zeroLenSection) {
struct SortedSection const **pickedSection =
nextSection(&section, &zeroLenSection);
struct Section const *sect = (*pickedSection)->section;
slack -= sect->size;
fprintf(mapFile, " SECTION: $%04x-$%04x ($%04x byte%s) [\"%s\"]\n",
sect->org, sect->org + sect->size - 1, sect->size,
sect->size == 1 ? "" : "s", sect->name);
for (size_t i = 0; i < sect->nbSymbols; i++)
fprintf(mapFile, " $%04x = %s\n",
sect->symbols[i]->offset + sect->org,
sect->symbols[i]->name);
*pickedSection = (*pickedSection)->next;
}
if (slack == maxsize[type])
fputs(" EMPTY\n\n", mapFile);
else
fprintf(mapFile, " SLACK: $%04x byte%s\n\n", slack,
slack == 1 ? "" : "s");
}
/**
* Writes the sym and/or map files, if applicable.
*/
static void writeSymAndMap(void)
{
if (!symFileName && !mapFileName)
return;
enum SectionType typeMap[SECTTYPE_INVALID] = {
SECTTYPE_ROM0,
SECTTYPE_ROMX,
SECTTYPE_VRAM,
SECTTYPE_SRAM,
SECTTYPE_WRAM0,
SECTTYPE_WRAMX,
SECTTYPE_OAM,
SECTTYPE_HRAM
};
symFile = openFile(symFileName, "w");
mapFile = openFile(mapFileName, "w");
if (symFileName) {
fputs("; File generated by rgblink\n", symFile);
}
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
enum SectionType type = typeMap[i];
if (sections[type].nbBanks > 0) {
for (uint32_t bank = 0; bank < sections[type].nbBanks;
bank++) {
writeSymBank(&sections[type].banks[bank]);
writeMapBank(&sections[type].banks[bank],
type, bank);
}
}
}
closeFile(symFile);
closeFile(mapFile);
}
static void cleanupSections(struct SortedSection *section)
{
while (section) {
struct SortedSection *next = section->next;
free(section);
section = next;
} }
} }
static void cleanup(void)
{
for (enum SectionType type = 0; type < SECTTYPE_INVALID; type++) {
if (sections[type].nbBanks > 0) {
for (uint32_t i = 0; i < sections[type].nbBanks; i++) {
struct SortedSections *bank =
&sections[type].banks[i];
cleanupSections(bank->sections);
cleanupSections(bank->zeroLenSections);
}
free(sections[type].banks);
}
}
}
void out_WriteFiles(void)
{
writeROM();
writeSymAndMap();
cleanup();
}

View File

@@ -1,127 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
%{
#include <stdint.h>
#include <stdio.h>
#include "extern/err.h"
#include "link/script.h"
int yylex(void);
void yyerror(char *);
extern int yylineno;
%}
%union {
int32_t 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 COMMAND_INCLUDE
%token NEWLINE
%start lines
%%
lines:
/* empty */
| lines line NEWLINE
;
line:
/* empty */
| statement
;
statement:
/* Statements to set the current section */
SECTION_NONBANKED
{
script_SetCurrentSectionType($1, 0);
}
| SECTION_NONBANKED INTEGER
{
script_fatalerror("Trying to assign a bank to a non-banked section.\n");
}
| SECTION_BANKED
{
script_fatalerror("Banked section without assigned bank.\n");
}
| SECTION_BANKED INTEGER
{
script_SetCurrentSectionType($1, $2);
}
/* Commands to adjust the address inside the current section */
| COMMAND_ALIGN INTEGER
{
script_SetAlignment($2);
}
| COMMAND_ALIGN
{
script_fatalerror("ALIGN keyword needs an argument.\n");
}
| COMMAND_ORG INTEGER
{
script_SetAddress($2);
}
| COMMAND_ORG
{
script_fatalerror("ORG keyword needs an argument.\n");
}
/* Section name */
| STRING
{
script_OutputSection($1);
}
/* Include file */
| COMMAND_INCLUDE STRING
{
script_IncludeFile($2);
}
/* End */
;
%%
extern int yylex(void);
extern int yyparse(void);
int yywrap(void)
{
if (script_IncludeDepthGet() == 0)
return 1;
script_IncludePop();
return 0;
}
void yyerror(char *s)
{
script_fatalerror("Linkerscript parse error: \"%s\"\n", s);
}

View File

@@ -1,344 +1,343 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <limits.h>
#include <string.h> #include <string.h>
#include "link/patch.h"
#include "link/section.h"
#include "link/symbol.h"
#include "linkdefs.h"
#include "extern/err.h" #include "extern/err.h"
#include "link/assign.h" /* This is an "empty"-type stack */
#include "link/main.h" struct RPNStack {
#include "link/mylink.h" int32_t *buf;
#include "link/symbol.h" size_t size;
size_t capacity;
} stack;
#define RPN_STACK_SIZE 256 static inline void initRPNStack(void)
static struct sSection *pCurrentSection;
static int32_t rpnstack[RPN_STACK_SIZE];
static int32_t rpnp;
int32_t nPC;
static void rpnpush(int32_t i)
{ {
if (rpnp >= RPN_STACK_SIZE) stack.capacity = 64;
errx(1, "RPN stack overflow"); stack.buf = malloc(sizeof(*stack.buf) * stack.capacity);
if (!stack.buf)
rpnstack[rpnp] = i; err(1, "Failed to init RPN stack");
rpnp++;
} }
static int32_t rpnpop(void) static inline void clearRPNStack(void)
{ {
rpnp--; stack.size = 0;
return rpnstack[rpnp];
} }
static int32_t getsymvalue(struct sPatch *pPatch, int32_t symid) static void pushRPN(int32_t value)
{ {
const struct sSymbol *tSymbol = pCurrentSection->tSymbols[symid]; if (stack.size >= stack.capacity) {
stack.capacity *= 2;
switch (tSymbol->Type) { stack.buf =
case SYM_IMPORT: realloc(stack.buf, sizeof(*stack.buf) * stack.capacity);
return sym_GetValue(pPatch, tSymbol->pzName); if (!stack.buf)
err(1, "Failed to resize RPN stack");
case SYM_EXPORT:
case SYM_LOCAL:
if (strcmp(tSymbol->pzName, "@") == 0)
return nPC;
return tSymbol->nOffset + tSymbol->pSection->nOrg;
default:
break;
} }
errx(1, "%s: Unknown symbol type", __func__); stack.buf[stack.size] = value;
stack.size++;
} }
static int32_t getrealbankfrominternalbank(int32_t n) static int32_t popRPN(void)
{ {
if (BankIndexIsWRAM0(n) || BankIndexIsROM0(n) || if (stack.size == 0)
BankIndexIsOAM(n) || BankIndexIsHRAM(n)) { errx(1, "Internal error, RPN stack empty");
return 0;
} else if (BankIndexIsROMX(n)) {
return n - BANK_INDEX_ROMX + 1;
} else if (BankIndexIsWRAMX(n)) {
return n - BANK_INDEX_WRAMX + 1;
} else if (BankIndexIsVRAM(n)) {
return n - BANK_INDEX_VRAM;
} else if (BankIndexIsSRAM(n)) {
return n - BANK_INDEX_SRAM;
}
return n; stack.size--;
return stack.buf[stack.size];
} }
static int32_t getsymbank(struct sPatch *pPatch, int32_t symid) static inline void freeRPNStack(void)
{ {
int32_t nBank; free(stack.buf);
const struct sSymbol *tSymbol = pCurrentSection->tSymbols[symid];
switch (tSymbol->Type) {
case SYM_IMPORT:
nBank = sym_GetBank(pPatch, tSymbol->pzName);
break;
case SYM_EXPORT:
case SYM_LOCAL:
nBank = tSymbol->pSection->nBank;
break;
default:
errx(1, "%s: Unknown symbol type", __func__);
}
return getrealbankfrominternalbank(nBank);
} }
int32_t calcrpn(struct sPatch *pPatch) /* RPN operators */
static uint8_t getRPNByte(uint8_t const **expression, int32_t *size,
char const *fileName, int32_t lineNo)
{ {
int32_t t, size; if (!(*size)--)
uint8_t *rpn; errx(1, "%s(%d): RPN expression overread", fileName, lineNo);
uint8_t rpn_cmd; return *(*expression)++;
int32_t nBank; }
rpnp = 0; /**
* Compute a patch's value from its RPN string.
* @param patch The patch to compute the value of
* @param section The section the patch is contained in
* @return The patch's value
*/
static int32_t computeRPNExpr(struct Patch const *patch,
struct Section const *section)
{
uint8_t const *expression = patch->rpnExpression;
int32_t size = patch->rpnSize;
size = pPatch->nRPNSize; clearRPNStack();
rpn = pPatch->pRPN;
pPatch->oRelocPatch = 0;
while (size > 0) { while (size > 0) {
size -= 1; enum RPNCommand command = getRPNByte(&expression, &size,
rpn_cmd = *rpn++; patch->fileName,
patch->lineNo);
int32_t value;
/*
* Friendly reminder:
* Be VERY careful with two `popRPN` in the same expression.
* C does not guarantee order of evaluation of operands!!
* So, if there are two `popRPN` in the same expression, make
* sure the operation is commutative.
*/
switch (command) {
struct Symbol const *symbol;
char const *name;
struct Section const *sect;
switch (rpn_cmd) {
case RPN_ADD: case RPN_ADD:
rpnpush(rpnpop() + rpnpop()); value = popRPN() + popRPN();
break; break;
case RPN_SUB: case RPN_SUB:
t = rpnpop(); value = popRPN();
rpnpush(rpnpop() - t); value = popRPN() - value;
break; break;
case RPN_MUL: case RPN_MUL:
rpnpush(rpnpop() * rpnpop()); value = popRPN() * popRPN();
break; break;
case RPN_DIV: case RPN_DIV:
t = rpnpop(); value = popRPN();
rpnpush(rpnpop() / t); value = popRPN() / value;
break; break;
case RPN_MOD: case RPN_MOD:
t = rpnpop(); value = popRPN();
rpnpush(rpnpop() % t); value = popRPN() % value;
break; break;
case RPN_UNSUB: case RPN_UNSUB:
rpnpush(-rpnpop()); value = -popRPN();
break; break;
case RPN_OR: case RPN_OR:
rpnpush(rpnpop() | rpnpop()); value = popRPN() | popRPN();
break; break;
case RPN_AND: case RPN_AND:
rpnpush(rpnpop() & rpnpop()); value = popRPN() & popRPN();
break; break;
case RPN_XOR: case RPN_XOR:
rpnpush(rpnpop() ^ rpnpop()); value = popRPN() ^ popRPN();
break; break;
case RPN_UNNOT: case RPN_UNNOT:
rpnpush(~rpnpop()); value = ~popRPN();
break; break;
case RPN_LOGAND: case RPN_LOGAND:
rpnpush(rpnpop() && rpnpop()); value = popRPN();
value = popRPN() && value;
break; break;
case RPN_LOGOR: case RPN_LOGOR:
rpnpush(rpnpop() || rpnpop()); value = popRPN();
value = popRPN() || value;
break; break;
case RPN_LOGUNNOT: case RPN_LOGUNNOT:
rpnpush(!rpnpop()); value = !popRPN();
break; break;
case RPN_LOGEQ: case RPN_LOGEQ:
rpnpush(rpnpop() == rpnpop()); value = popRPN() == popRPN();
break; break;
case RPN_LOGNE: case RPN_LOGNE:
rpnpush(rpnpop() != rpnpop()); value = popRPN() != popRPN();
break; break;
case RPN_LOGGT: case RPN_LOGGT:
t = rpnpop(); value = popRPN();
rpnpush(rpnpop() > t); value = popRPN() > value;
break; break;
case RPN_LOGLT: case RPN_LOGLT:
t = rpnpop(); value = popRPN();
rpnpush(rpnpop() < t); value = popRPN() < value;
break; break;
case RPN_LOGGE: case RPN_LOGGE:
t = rpnpop(); value = popRPN();
rpnpush(rpnpop() >= t); value = popRPN() >= value;
break; break;
case RPN_LOGLE: case RPN_LOGLE:
t = rpnpop(); value = popRPN();
rpnpush(rpnpop() <= t); value = popRPN() <= value;
break; break;
/* FIXME: sanitize shifts */
case RPN_SHL: case RPN_SHL:
t = rpnpop(); value = popRPN();
rpnpush(rpnpop() << t); value = popRPN() << value;
break; break;
case RPN_SHR: case RPN_SHR:
t = rpnpop(); value = popRPN();
rpnpush(rpnpop() >> t); value = popRPN() >> value;
break;
case RPN_HRAM:
t = rpnpop();
rpnpush(t & 0xFF);
if (t < 0 || (t > 0xFF && t < 0xFF00) || t > 0xFFFF) {
errx(1,
"%s(%ld) : Value must be in the HRAM area",
pPatch->pzFilename, pPatch->nLineNo);
}
break;
case RPN_CONST:
/* constant */
t = (*rpn++);
t |= (*rpn++) << 8;
t |= (*rpn++) << 16;
t |= (*rpn++) << 24;
rpnpush(t);
size -= 4;
break;
case RPN_SYM:
/* symbol */
t = (*rpn++);
t |= (*rpn++) << 8;
t |= (*rpn++) << 16;
t |= (*rpn++) << 24;
rpnpush(getsymvalue(pPatch, t));
pPatch->oRelocPatch |= (getsymbank(pPatch, t) != -1);
size -= 4;
break; break;
case RPN_BANK_SYM: case RPN_BANK_SYM:
/* symbol */ value = 0;
t = (*rpn++); for (uint8_t shift = 0; shift < 32; shift += 8)
t |= (*rpn++) << 8; value |= getRPNByte(&expression, &size,
t |= (*rpn++) << 16; patch->fileName,
t |= (*rpn++) << 24; patch->lineNo) << shift;
rpnpush(getsymbank(pPatch, t));
size -= 4; symbol = section->fileSymbols[value];
/* If the symbol is defined elsewhere... */
if (symbol->type == SYMTYPE_IMPORT) {
struct Symbol const *symbolDefinition =
sym_GetSymbol(symbol->name);
if (!symbolDefinition)
errx(1, "%s(%d): Unknown symbol \"%s\"",
patch->fileName, patch->lineNo,
symbol->name);
symbol = symbolDefinition;
}
value = symbol->section->bank;
break; break;
case RPN_BANK_SECT: case RPN_BANK_SECT:
{ name = (char const *)expression;
char *name = (char *)rpn; while (getRPNByte(&expression, &size, patch->fileName,
patch->lineNo))
;
struct sSection *pSection = GetSectionByName(name); sect = sect_GetSection(name);
if (pSection == NULL) { if (!sect)
errx(1, errx(1, "%s(%d): Requested BANK() of section \"%s\", which was not found",
"%s(%ld) : Requested BANK() of section \"%s\", which was not found.\n", patch->fileName, patch->lineNo, name);
pPatch->pzFilename, pPatch->nLineNo,
name);
}
nBank = pSection->nBank; value = sect->bank;
rpnpush(getrealbankfrominternalbank(nBank));
int len = strlen(name);
size -= len + 1;
rpn += len + 1;
break; break;
}
case RPN_BANK_SELF: case RPN_BANK_SELF:
nBank = pCurrentSection->nBank; value = section->bank;
rpnpush(getrealbankfrominternalbank(nBank));
break; break;
default:
errx(1, "%s: Invalid command %d\n", __func__, case RPN_HRAM:
rpn_cmd); value = popRPN();
if (value < 0
|| (value > 0xFF && value < 0xFF00)
|| value > 0xFFFF)
errx(1, "%s(%d): Value %d is not in HRAM range",
patch->fileName, patch->lineNo, value);
value &= 0xFF;
break; break;
}
}
return rpnpop();
}
void Patch(void) case RPN_CONST:
{ value = 0;
struct sSection *pSect; for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size,
patch->fileName,
patch->lineNo) << shift;
break;
pSect = pSections; case RPN_SYM:
while (pSect) { value = 0;
struct sPatch *pPatch; for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size,
patch->fileName,
patch->lineNo) << shift;
pCurrentSection = pSect; symbol = section->fileSymbols[value];
pPatch = pSect->pPatches;
while (pPatch) {
int32_t t;
int32_t nPatchOrg;
nPC = pSect->nOrg + pPatch->nOffset; /* If the symbol is defined elsewhere... */
t = calcrpn(pPatch); if (symbol->type == SYMTYPE_IMPORT) {
switch (pPatch->Type) { struct Symbol const *symbolDefinition =
case PATCH_BYTE: sym_GetSymbol(symbol->name);
if (t >= -128 && t <= 255) { if (!symbolDefinition)
t &= 0xFF; errx(1, "%s(%d): Unknown symbol \"%s\"",
pSect->pData[pPatch->nOffset] = patch->fileName, patch->lineNo,
(uint8_t)t; symbol->name);
} else { symbol = symbolDefinition;
errx(1,
"%s(%ld) : Value must be 8-bit",
pPatch->pzFilename,
pPatch->nLineNo);
}
break;
case PATCH_WORD_L:
if (t >= -32768 && t <= 65535) {
t &= 0xFFFF;
pSect->pData[pPatch->nOffset] =
t & 0xFF;
pSect->pData[pPatch->nOffset + 1] =
(t >> 8) & 0xFF;
} else {
errx(1,
"%s(%ld) : Value must be 16-bit",
pPatch->pzFilename,
pPatch->nLineNo);
}
break;
case PATCH_LONG_L:
pSect->pData[pPatch->nOffset + 0] = t & 0xFF;
pSect->pData[pPatch->nOffset + 1] =
(t >> 8) & 0xFF;
pSect->pData[pPatch->nOffset + 2] =
(t >> 16) & 0xFF;
pSect->pData[pPatch->nOffset + 3] =
(t >> 24) & 0xFF;
break;
case PATCH_BYTE_JR:
/* Calculate absolute address of the patch */
nPatchOrg = pSect->nOrg + pPatch->nOffset;
/* t contains the destination of the jump */
t = (int16_t)((t & 0xFFFF) - (nPatchOrg + 1));
if (t >= -128 && t <= 127) {
t &= 0xFF;
pSect->pData[pPatch->nOffset] =
(uint8_t)t;
} else {
errx(1,
"%s(%ld) : Value must be 8-bit",
pPatch->pzFilename,
pPatch->nLineNo);
}
break;
default:
errx(1, "%s: Internal error.", __func__);
} }
pPatch = pPatch->pNext; if (!strcmp(symbol->name, "@")) {
value = section->org + patch->offset;
} else {
value = symbol->value;
/* Symbols attached to sections have offsets */
if (symbol->section)
value += symbol->section->org;
}
break;
} }
pSect = pSect->pNext; pushRPN(value);
}
if (stack.size > 1)
warnx("%s(%d): RPN stack has %lu entries on exit, not 1",
patch->fileName, patch->lineNo, stack.size);
return popRPN();
}
/**
* Applies all of a section's patches
* @param section The section to patch
* @param arg Ignored callback arg
*/
static void applyPatches(struct Section *section, void *arg)
{
(void)arg;
if (!sect_HasData(section->type))
return;
verbosePrint("Patching section \"%s\"...\n", section->name);
for (uint32_t patchID = 0; patchID < section->nbPatches; patchID++) {
struct Patch *patch = &section->patches[patchID];
int32_t value = computeRPNExpr(patch, section);
if (patch->type == PATCHTYPE_JR) {
/* `jr` is quite unlike the others... */
uint16_t address = section->org + patch->offset;
/* Target is relative to the byte *after* the operand */
int32_t offset = value - (address + 1);
if (offset < -128 || offset > 127)
errx(1, "%s(%d): jr target out of reach (%d)",
patch->fileName, patch->lineNo, offset);
section->data[patch->offset] = offset & 0xFF;
} else {
/* Patch a certain number of bytes */
struct {
uint8_t size;
int32_t min;
int32_t max;
} const types[] = {
[PATCHTYPE_BYTE] = {1, -128, 255},
[PATCHTYPE_WORD] = {2, -32768, 65536},
[PATCHTYPE_LONG] = {4, INT32_MIN, INT32_MAX}
};
if (value < types[patch->type].min
|| value > types[patch->type].max)
errx(1, "%s(%d): Value %#x%s is not %u-bit",
patch->fileName, patch->lineNo, value,
value < 0 ? " (maybe negative?)" : "",
types[patch->type].size * 8);
for (uint8_t i = 0; i < types[patch->type].size; i++) {
section->data[patch->offset + i] = value & 0xFF;
value >>= 8;
}
}
} }
} }
void patch_ApplyPatches(void)
{
initRPNStack();
sect_ForEach(applyPatches, NULL);
freeRPNStack();
}

View File

@@ -1,237 +1,516 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include "link/main.h"
#include "link/script.h"
#include "link/section.h"
#include "extern/err.h" #include "extern/err.h"
#include "link/assign.h" FILE *linkerScript;
#include "link/mylink.h"
static uint32_t lineNo;
static struct { static struct {
uint32_t address; /* current address to write sections to */ FILE *file;
uint32_t top_address; /* not inclusive */ uint32_t lineNo;
enum eSectionType type; char const *name;
} bank[BANK_INDEX_MAX]; } *fileStack;
static int32_t current_bank = -1; /* Bank as seen by the bank array */ static uint32_t fileStackSize;
static int32_t current_real_bank = -1; /* bank as seen by the GB */ static uint32_t fileStackIndex;
/* Current section attributes */ static void pushFile(char const *newFileName)
static int32_t fix_org = -1;
static int32_t fix_align = 1;
void script_InitSections(void)
{ {
int32_t i; if (fileStackIndex == UINT32_MAX)
errx(1, "%s(%u): INCLUDE recursion limit reached",
linkerScriptName, lineNo);
for (i = 0; i < BANK_INDEX_MAX; i++) { if (fileStackIndex == fileStackSize) {
if (BankIndexIsROM0(i)) { if (!fileStackSize) /* Init file stack */
/* ROM0 bank */ fileStackSize = 4;
bank[i].address = 0x0000; fileStackSize *= 2;
if (options & OPT_TINY) fileStack = realloc(fileStack,
bank[i].top_address = 0x8000; sizeof(*fileStack) * fileStackSize);
else if (!fileStack)
bank[i].top_address = 0x4000; err(1, "%s(%u): Internal INCLUDE error",
bank[i].type = SECT_ROM0; linkerScriptName, lineNo);
} else if (BankIndexIsROMX(i)) { }
/* Swappable ROM bank */
bank[i].address = 0x4000; fileStack[fileStackIndex].file = linkerScript;
bank[i].top_address = 0x8000; fileStack[fileStackIndex].lineNo = lineNo;
bank[i].type = SECT_ROMX; fileStack[fileStackIndex].name = linkerScriptName;
} else if (BankIndexIsWRAM0(i)) { fileStackIndex++;
/* WRAM */
bank[i].address = 0xC000; linkerScript = fopen(newFileName, "r");
if (options & OPT_CONTWRAM) if (!linkerScript)
bank[i].top_address = 0xE000; err(1, "%s(%u): Could not open \"%s\"",
else linkerScriptName, lineNo, newFileName);
bank[i].top_address = 0xD000; lineNo = 1;
bank[i].type = SECT_WRAM0; linkerScriptName = newFileName;
} else if (BankIndexIsSRAM(i)) { }
/* Swappable SRAM bank */
bank[i].address = 0xA000; static bool popFile(void)
bank[i].top_address = 0xC000; {
bank[i].type = SECT_SRAM; if (!fileStackIndex)
} else if (BankIndexIsWRAMX(i)) { return false;
/* Swappable WRAM bank */
bank[i].address = 0xD000; fileStackIndex--;
bank[i].top_address = 0xE000; linkerScript = fileStack[fileStackIndex].file;
bank[i].type = SECT_WRAMX; lineNo = fileStack[fileStackIndex].lineNo;
} else if (BankIndexIsVRAM(i)) { linkerScriptName = fileStack[fileStackIndex].name;
/* Swappable VRAM bank */
bank[i].address = 0x8000; return true;
bank[i].type = SECT_VRAM; }
if (options & OPT_DMG_MODE && i != BANK_INDEX_VRAM) {
/* In DMG the only available bank is bank 0. */ static inline bool isWhiteSpace(int c)
bank[i].top_address = 0x8000; {
} else { return c == ' ' || c == '\t';
bank[i].top_address = 0xA000; }
static inline bool isNewline(int c)
{
return c == '\r' || c == '\n';
}
/**
* Try parsing a number, in base 16 if it begins with a dollar,
* in base 10 otherwise
* @param str The number to parse
* @param number A pointer where the number will be written to
* @return True if parsing was successful, false otherwise
*/
static bool tryParseNumber(char const *str, uint32_t *number)
{
static char const digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F'
};
uint8_t base = 10;
if (*str == '$') {
str++;
base = 16;
}
/* An empty string is not a number */
if (!*str)
return false;
*number = 0;
do {
char chr = toupper(*str++);
uint8_t digit = 0;
while (digit < base) {
if (chr == digits[digit])
break;
digit++;
}
if (digit == base)
return false;
*number = *number * base + digit;
} while (*str);
return true;
}
enum LinkerScriptTokenType {
TOKEN_NEWLINE,
TOKEN_COMMAND,
TOKEN_BANK,
TOKEN_INCLUDE,
TOKEN_NUMBER,
TOKEN_STRING,
TOKEN_EOF,
TOKEN_INVALID
};
char const *tokenTypes[] = {
[TOKEN_NEWLINE] = "newline",
[TOKEN_COMMAND] = "command",
[TOKEN_BANK] = "bank command",
[TOKEN_NUMBER] = "number",
[TOKEN_STRING] = "string",
[TOKEN_EOF] = "end of file"
};
enum LinkerScriptCommand {
COMMAND_ORG,
COMMAND_ALIGN,
COMMAND_INVALID
};
struct LinkerScriptToken {
enum LinkerScriptTokenType type;
union LinkerScriptTokenAttr {
enum LinkerScriptCommand command;
enum SectionType secttype;
uint32_t number;
char *string;
} attr;
};
static char const * const commands[] = {
[COMMAND_ORG] = "ORG",
[COMMAND_ALIGN] = "ALIGN"
};
static int readChar(FILE *file)
{
int curchar = getc_unlocked(file);
if (curchar == EOF && ferror(file))
err(1, "%s(%u): Unexpected error in %s", linkerScriptName,
lineNo, __func__);
return curchar;
}
static struct LinkerScriptToken const *nextToken(void)
{
static struct LinkerScriptToken token;
int curchar;
/* If the token has a string, make sure to avoid leaking it */
if (token.type == TOKEN_STRING)
free(token.attr.string);
/* Skip initial whitespace... */
do
curchar = readChar(linkerScript);
while (isWhiteSpace(curchar));
/* If this is a comment, skip to the end of the line */
if (curchar == ';') {
do
curchar = readChar(linkerScript);
while (!isNewline(curchar) && curchar != EOF);
}
if (curchar == EOF) {
token.type = TOKEN_EOF;
} else if (isNewline(curchar)) {
/* If we have a newline char, this is a newline token */
token.type = TOKEN_NEWLINE;
/* FIXME: This works with CRLF newlines, but not CR-only */
if (curchar == '\r')
readChar(linkerScript); /* Read and discard LF */
} else if (curchar == '"') {
/* If we have a string start, this is a string */
token.type = TOKEN_STRING;
token.attr.string = NULL; /* Force initial alloc */
size_t size = 0;
size_t capacity = 16; /* Half of the default capacity */
do {
curchar = readChar(linkerScript);
if (curchar == EOF || isNewline(curchar))
errx(1, "%s(%u): Unterminated string",
linkerScriptName, lineNo);
else if (curchar == '"')
/* Quotes force a string termination */
curchar = '\0';
if (size >= capacity || token.attr.string == NULL) {
capacity *= 2;
token.attr.string = realloc(token.attr.string,
capacity);
if (!token.attr.string)
err(1, "%s: Failed to allocate memory for string",
__func__);
}
token.attr.string[size++] = curchar;
} while (curchar);
} else {
/* This is either a number, command or bank, that is: a word */
char *str = NULL;
size_t size = 0;
size_t capacity = 8; /* Half of the default capacity */
for (;;) {
if (size >= capacity || str == NULL) {
capacity *= 2;
str = realloc(str, capacity);
if (!str)
err(1, "%s: Failed to allocate memory for token",
__func__);
}
str[size] = toupper(curchar);
size++;
if (!curchar)
break;
curchar = readChar(linkerScript);
/* Whitespace, a newline or a comment end the token */
if (isWhiteSpace(curchar) || isNewline(curchar)
|| curchar == ';') {
ungetc(curchar, linkerScript);
curchar = '\0';
} }
} else if (BankIndexIsOAM(i)) {
/* OAM */
bank[i].address = 0xFE00;
bank[i].top_address = 0xFEA0;
bank[i].type = SECT_OAM;
} else if (BankIndexIsHRAM(i)) {
/* HRAM */
bank[i].address = 0xFF80;
bank[i].top_address = 0xFFFF;
bank[i].type = SECT_HRAM;
} else {
errx(1, "%s: Unknown bank type %d", __func__, i);
} }
token.type = TOKEN_INVALID;
/* Try to match a command */
for (enum LinkerScriptCommand i = 0; i < COMMAND_INVALID; i++) {
if (!strcmp(commands[i], str)) {
token.type = TOKEN_COMMAND;
token.attr.command = i;
break;
}
}
if (token.type == TOKEN_INVALID) {
/* Try to match a bank specifier */
for (enum SectionType type = 0; type < SECTTYPE_INVALID;
type++) {
if (!strcmp(typeNames[type], str)) {
token.type = TOKEN_BANK;
token.attr.secttype = type;
break;
}
}
}
if (token.type == TOKEN_INVALID) {
/* Try to match an include token */
if (!strcmp("INCLUDE", str))
token.type = TOKEN_INCLUDE;
}
if (token.type == TOKEN_INVALID) {
/* None of the strings matched, do we have a number? */
if (tryParseNumber(str, &token.attr.number))
token.type = TOKEN_NUMBER;
else
errx(1, "%s(%u): Unknown token \"%s\"",
linkerScriptName, lineNo, str);
}
free(str);
} }
return &token;
} }
void script_SetCurrentSectionType(const char *type, uint32_t bank_num) static void processCommand(enum LinkerScriptCommand command, uint16_t arg,
uint16_t *pc)
{ {
if (strcmp(type, "ROM0") == 0) { switch (command) {
if (bank_num != 0) case COMMAND_INVALID:
errx(1, "Trying to assign a bank number to ROM0.\n"); trap_;
current_bank = BANK_INDEX_ROM0;
current_real_bank = 0;
return;
} else if (strcmp(type, "ROMX") == 0) {
if (bank_num == 0)
errx(1, "ROMX index can't be 0.\n");
if (bank_num > BANK_COUNT_ROMX) {
errx(1, "ROMX index too big (%d > %d).\n", bank_num,
BANK_COUNT_ROMX);
}
current_bank = BANK_INDEX_ROMX + bank_num - 1;
current_real_bank = bank_num;
return;
} else if (strcmp(type, "VRAM") == 0) {
if (bank_num >= BANK_COUNT_VRAM) {
errx(1, "VRAM index too big (%d >= %d).\n", bank_num,
BANK_COUNT_VRAM);
}
current_bank = BANK_INDEX_VRAM + bank_num;
current_real_bank = bank_num;
return;
} else if (strcmp(type, "WRAM0") == 0) {
if (bank_num != 0)
errx(1, "Trying to assign a bank number to WRAM0.\n");
current_bank = BANK_INDEX_WRAM0; case COMMAND_ORG:
current_real_bank = 0; *pc = arg;
return; break;
} else if (strcmp(type, "WRAMX") == 0) {
if (bank_num == 0) case COMMAND_ALIGN:
errx(1, "WRAMX index can't be 0.\n"); if (arg >= 16)
if (bank_num > BANK_COUNT_WRAMX) { arg = 0;
errx(1, "WRAMX index too big (%d > %d).\n", bank_num, else
BANK_COUNT_WRAMX); arg = (*pc + (1 << arg) - 1) & ~((1 << arg) - 1);
}
current_bank = BANK_INDEX_WRAMX + bank_num - 1;
current_real_bank = bank_num;
return;
} else if (strcmp(type, "SRAM") == 0) {
if (bank_num >= BANK_COUNT_SRAM) {
errx(1, "SRAM index too big (%d >= %d).\n", bank_num,
BANK_COUNT_SRAM);
}
current_bank = BANK_INDEX_SRAM + bank_num;
current_real_bank = bank_num;
return;
} else if (strcmp(type, "OAM") == 0) {
if (bank_num != 0) {
errx(1, "%s: Trying to assign a bank number to OAM.\n",
__func__);
}
current_bank = BANK_INDEX_OAM;
current_real_bank = 0;
return;
} else if (strcmp(type, "HRAM") == 0) {
if (bank_num != 0) {
errx(1, "%s: Trying to assign a bank number to HRAM.\n",
__func__);
}
current_bank = BANK_INDEX_HRAM;
current_real_bank = 0;
return;
} }
errx(1, "%s: Unknown section type \"%s\".\n", __func__, type); if (arg < *pc)
errx(1, "%s(%u): `%s` cannot be used to go backwards",
linkerScriptName, lineNo, commands[command]);
*pc = arg;
} }
void script_SetAddress(uint32_t addr) enum LinkerScriptParserState {
PARSER_FIRSTTIME,
PARSER_LINESTART,
PARSER_INCLUDE, /* After an INCLUDE token */
PARSER_LINEEND
};
/* Part of internal state, but has data that needs to be freed */
static uint16_t *curaddr[SECTTYPE_INVALID];
/* Put as global to ensure it's initialized only once */
static enum LinkerScriptParserState parserState = PARSER_FIRSTTIME;
struct SectionPlacement *script_NextSection(void)
{ {
if (current_bank == -1) static struct SectionPlacement section;
errx(1, "Trying to set an address without assigned bank\n"); static enum SectionType type;
static uint32_t bank;
static uint32_t bankID;
/* Make sure that we don't go back. */ if (parserState == PARSER_FIRSTTIME) {
if (bank[current_bank].address > addr) { lineNo = 1;
errx(1, "Trying to go to a previous address (0x%04X to 0x%04X)\n",
bank[current_bank].address, addr); /* Init PC for all banks */
for (enum SectionType i = 0; i < SECTTYPE_INVALID; i++) {
curaddr[i] = malloc(sizeof(*curaddr[i]) * nbbanks(i));
for (uint32_t b = 0; b < nbbanks(i); b++)
curaddr[i][b] = startaddr[i];
}
type = SECTTYPE_INVALID;
parserState = PARSER_LINESTART;
} }
bank[current_bank].address = addr; for (;;) {
struct LinkerScriptToken const *token = nextToken();
enum LinkerScriptTokenType tokType;
union LinkerScriptTokenAttr attr;
bool hasArg;
uint32_t arg;
/* Make sure we don't overflow */ if (type != SECTTYPE_INVALID) {
if (bank[current_bank].address >= bank[current_bank].top_address) { if (curaddr[type][bankID] > endaddr(type) + 1)
errx(1, "Bank overflowed (0x%04X >= 0x%04X)\n", errx(1, "%s(%u): Sections would extend past the end of %s ($%04hx > $%04hx)",
bank[current_bank].address, linkerScriptName, lineNo, typeNames[type],
bank[current_bank].top_address); curaddr[type][bankID], endaddr(type));
if (curaddr[type][bankID] < startaddr[type])
errx(1, "%s(%u): PC underflowed ($%04hx < $%04hx)",
linkerScriptName, lineNo,
curaddr[type][bankID], startaddr[type]);
}
switch (parserState) {
case PARSER_FIRSTTIME:
trap_;
case PARSER_LINESTART:
switch (token->type) {
case TOKEN_INVALID:
trap_;
case TOKEN_EOF:
if (!popFile())
return NULL;
parserState = PARSER_LINEEND;
break;
case TOKEN_NUMBER:
errx(1, "%s(%u): stray number \"%u\"",
linkerScriptName, lineNo,
token->attr.number);
case TOKEN_NEWLINE:
lineNo++;
break;
/* A stray string is a section name */
case TOKEN_STRING:
parserState = PARSER_LINEEND;
if (type == SECTTYPE_INVALID)
errx(1, "%s(%u): Didn't specify a location before the section",
linkerScriptName, lineNo);
section.section =
sect_GetSection(token->attr.string);
section.org = curaddr[type][bankID];
section.bank = bank;
curaddr[type][bankID] += section.section->size;
return &section;
case TOKEN_COMMAND:
case TOKEN_BANK:
tokType = token->type;
attr = token->attr;
token = nextToken();
hasArg = token->type == TOKEN_NUMBER;
/*
* Leaving `arg` uninitialized when `!hasArg`
* causes GCC to warn about its use as an
* argument to `processCommand`. This cannot
* happen because `hasArg` has to be true, but
* silence the warning anyways.
* I dislike doing this because it could swallow
* actual errors, but I don't have a choice.
*/
arg = hasArg ? token->attr.number : 0;
if (tokType == TOKEN_COMMAND) {
if (type == SECTTYPE_INVALID)
errx(1, "%s(%u): Didn't specify a location before the command",
linkerScriptName, lineNo);
if (!hasArg)
errx(1, "%s(%u): Command specified without an argument",
linkerScriptName, lineNo);
processCommand(attr.command, arg,
&curaddr[type][bankID]);
} else { /* TOKEN_BANK */
type = attr.secttype;
/*
* If there's only one bank,
* specifying the number is optional.
*/
if (!hasArg && nbbanks(type) != 1)
errx(1, "%s(%u): Didn't specify a bank number",
linkerScriptName, lineNo);
else if (!hasArg)
arg = bankranges[type][0];
else if (arg < bankranges[type][0])
errx(1, "%s(%u): specified bank number is too low (%u < %u)",
linkerScriptName, lineNo,
arg, bankranges[type][0]);
else if (arg > bankranges[type][1])
errx(1, "%s(%u): specified bank number is too high (%u > %u)",
linkerScriptName, lineNo,
arg, bankranges[type][1]);
bank = arg;
bankID = arg - bankranges[type][0];
}
/* If we read a token we shouldn't have... */
if (token->type != TOKEN_NUMBER)
goto lineend;
break;
case TOKEN_INCLUDE:
parserState = PARSER_INCLUDE;
break;
}
break;
case PARSER_INCLUDE:
if (token->type != TOKEN_STRING)
errx(1, "%s(%u): Expected a file name after INCLUDE",
linkerScriptName, lineNo);
/* Switch to that file */
pushFile(token->attr.string);
parserState = PARSER_LINESTART;
break;
case PARSER_LINEEND:
lineend:
lineNo++;
parserState = PARSER_LINESTART;
if (token->type == TOKEN_EOF) {
if (!popFile())
return NULL;
parserState = PARSER_LINEEND;
} else if (token->type != TOKEN_NEWLINE)
errx(1, "%s(%u): Unexpected %s at the end of the line",
linkerScriptName, lineNo,
tokenTypes[token->type]);
break;
}
} }
fix_org = addr;
} }
void script_SetAlignment(uint32_t alignment) void script_Cleanup(void)
{ {
if (current_bank == -1) for (enum SectionType type = 0; type < SECTTYPE_INVALID; type++)
errx(1, "Trying to set an alignment without assigned bank\n"); free(curaddr[type]);
if (alignment > 15)
errx(1, "Trying to set an alignment too big: %d\n", alignment);
uint32_t size = 1 << alignment;
uint32_t 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);
}
fix_align = size;
}
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 (!IsSectionSameTypeBankAndAttrs(section_name,
bank[current_bank].type,
current_real_bank,
fix_org,
fix_align)) {
errx(1, "Different attributes for \"%s\" in source and linkerscript\n",
section_name);
}
/* Move section to its place. */
bank[current_bank].address +=
AssignSectionAddressAndBankByName(section_name,
bank[current_bank].address,
current_real_bank);
fix_org = -1;
fix_align = 1;
} }

168
src/link/section.c Normal file
View File

@@ -0,0 +1,168 @@
#include <stdbool.h>
#include "link/main.h"
#include "link/section.h"
#include "extern/err.h"
#include "hashmap.h"
#include "common.h"
uint16_t startaddr[] = {
[SECTTYPE_ROM0] = 0x0000,
[SECTTYPE_ROMX] = 0x4000,
[SECTTYPE_VRAM] = 0x8000,
[SECTTYPE_SRAM] = 0xA000,
[SECTTYPE_WRAM0] = 0xC000,
[SECTTYPE_WRAMX] = 0xD000,
[SECTTYPE_OAM] = 0xFE00,
[SECTTYPE_HRAM] = 0xFF80
};
uint16_t maxsize[] = {
[SECTTYPE_ROM0] = 0x4000,
[SECTTYPE_ROMX] = 0x4000,
[SECTTYPE_VRAM] = 0x2000,
[SECTTYPE_SRAM] = 0x2000,
[SECTTYPE_WRAM0] = 0x1000,
[SECTTYPE_WRAMX] = 0x1000,
[SECTTYPE_OAM] = 0x00A0,
[SECTTYPE_HRAM] = 0x007F
};
uint32_t bankranges[][2] = {
[SECTTYPE_ROM0] = {BANK_MIN_ROM0, BANK_MAX_ROM0},
[SECTTYPE_ROMX] = {BANK_MIN_ROMX, BANK_MAX_ROMX},
[SECTTYPE_VRAM] = {BANK_MIN_VRAM, BANK_MAX_VRAM},
[SECTTYPE_SRAM] = {BANK_MIN_SRAM, BANK_MAX_SRAM},
[SECTTYPE_WRAM0] = {BANK_MIN_WRAM0, BANK_MAX_WRAM0},
[SECTTYPE_WRAMX] = {BANK_MIN_WRAMX, BANK_MAX_WRAMX},
[SECTTYPE_OAM] = {BANK_MIN_OAM, BANK_MAX_OAM},
[SECTTYPE_HRAM] = {BANK_MIN_HRAM, BANK_MAX_HRAM}
};
char const * const typeNames[] = {
[SECTTYPE_ROM0] = "ROM0",
[SECTTYPE_ROMX] = "ROMX",
[SECTTYPE_VRAM] = "VRAM",
[SECTTYPE_SRAM] = "SRAM",
[SECTTYPE_WRAM0] = "WRAM0",
[SECTTYPE_WRAMX] = "WRAMX",
[SECTTYPE_OAM] = "OAM",
[SECTTYPE_HRAM] = "HRAM"
};
HashMap sections;
struct ForEachArg {
void (*callback)(struct Section *section, void *arg);
void *arg;
};
static void forEach(void *section, void *arg)
{
struct ForEachArg *callbackArg = (struct ForEachArg *)arg;
callbackArg->callback((struct Section *)section, callbackArg->arg);
}
void sect_ForEach(void (*callback)(struct Section *, void *), void *arg)
{
struct ForEachArg callbackArg = { .callback = callback, .arg = arg};
hash_ForEach(sections, forEach, &callbackArg);
}
void sect_AddSection(struct Section *section)
{
/* Check if the section already exists */
if (hash_GetElement(sections, section->name))
errx(1, "Section name \"%s\" is already in use", section->name);
/* If not, add it */
bool collided = hash_AddElement(sections, section->name, section);
if (beVerbose && collided)
warnx("Section hashmap collision occurred!");
}
struct Section *sect_GetSection(char const *name)
{
return (struct Section *)hash_GetElement(sections, name);
}
void sect_CleanupSections(void)
{
hash_EmptyMap(sections);
}
static void doSanityChecks(struct Section *section, void *ptr)
{
(void)ptr;
/* Sanity check the section's type */
if (section->type < 0 || section->type >= SECTTYPE_INVALID)
errx(1, "Section \"%s\" has an invalid type.", section->name);
if (is32kMode && section->type == SECTTYPE_ROMX)
errx(1, "%s: ROMX sections cannot be used with option -t.",
section->name);
if (isWRA0Mode && section->type == SECTTYPE_WRAMX)
errx(1, "%s: WRAMX sections cannot be used with options -w or -d.",
section->name);
if (isDmgMode && section->type == SECTTYPE_VRAM && section->bank == 1)
errx(1, "%s: VRAM bank 1 can't be used with option -d.",
section->name);
/*
* Check if alignment is reasonable, this is important to avoid UB
* An alignment of zero is equivalent to no alignment, basically
*/
if (section->isAlignFixed && section->alignMask == 1)
section->isAlignFixed = false;
uint32_t minbank = bankranges[section->type][0],
maxbank = bankranges[section->type][1];
if (section->isBankFixed && section->bank < minbank
&& section->bank > maxbank)
errx(1, minbank == maxbank
? "Cannot place section \"%s\" in bank %d, it must be %d"
: "Cannot place section \"%s\" in bank %d, it must be between %d and %d",
section->name, section->bank, minbank, maxbank);
/* Check if section has a chance to be placed */
if (section->size > maxsize[section->type])
errx(1, "Section \"%s\" is bigger than the max size for that type: %#X > %#X",
section->size, maxsize[section->type]);
/* Translate loose constraints to strong ones when they're equivalent */
if (minbank == maxbank) {
section->bank = minbank;
section->isBankFixed = true;
}
if (section->isAlignFixed) {
enum SectionType type = section->type;
/* It doesn't make sense to have both org and alignment set */
if (section->isAddressFixed) {
if (section->org & section->alignMask)
errx(1, "Section \"%s\"'s fixed address doesn't match its alignment",
section->name);
section->isAlignFixed = false;
} else if ((endaddr(type) & section->alignMask)
== startaddr[type]) {
section->org = startaddr[type];
section->isAlignFixed = false;
section->isAddressFixed = true;
}
}
}
void sect_DoSanityChecks(void)
{
sect_ForEach(doSanityChecks, NULL);
}

View File

@@ -1,137 +1,56 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <stdint.h> #include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extern/err.h"
#include "link/symbol.h"
#include "link/main.h" #include "link/main.h"
#include "link/patch.h" #include "extern/err.h"
#include "link/mylink.h" #include "hashmap.h"
#include "types.h" HashMap symbols;
#define HASHSIZE 73 struct ForEachArg {
void (*callback)(struct Symbol *symbol, void *arg);
struct ISymbol { void *arg;
char *pzName;
int32_t nValue;
int32_t nBank; /* -1 = constant */
/* Object file where the symbol was defined. */
char tzObjFileName[_MAX_PATH + 1];
/* Source file where the symbol was defined. */
char tzFileName[_MAX_PATH + 1];
/* Line where the symbol was defined. */
uint32_t nFileLine;
struct ISymbol *pNext;
}; };
struct ISymbol *tHash[HASHSIZE]; static void forEach(void *symbol, void *arg)
int32_t calchash(char *s)
{ {
int32_t r = 0; struct ForEachArg *callbackArg = (struct ForEachArg *)arg;
while (*s) callbackArg->callback((struct Symbol *)symbol, callbackArg->arg);
r += *s++;
return r % HASHSIZE;
} }
void sym_Init(void) void sym_ForEach(void (*callback)(struct Symbol *, void *), void *arg)
{ {
int32_t i; struct ForEachArg callbackArg = { .callback = callback, .arg = arg};
for (i = 0; i < HASHSIZE; i += 1) hash_ForEach(symbols, forEach, &callbackArg);
tHash[i] = NULL;
} }
int32_t sym_GetValue(struct sPatch *pPatch, char *tzName) void sym_AddSymbol(struct Symbol *symbol)
{ {
if (strcmp(tzName, "@") == 0) /* Check if the symbol already exists */
return nPC; struct Symbol *other = hash_GetElement(symbols, symbol->name);
struct ISymbol **ppSym; if (other)
errx(1, "\"%s\" both in %s from %s(%d) and in %s from %s(%d)",
symbol->name,
symbol->objFileName, symbol->fileName, symbol->lineNo,
other->objFileName, other->fileName, other->lineNo);
ppSym = &(tHash[calchash(tzName)]); /* If not, add it */
while (*ppSym) { bool collided = hash_AddElement(symbols, symbol->name, symbol);
if (strcmp(tzName, (*ppSym)->pzName))
ppSym = &((*ppSym)->pNext);
else
return ((*ppSym)->nValue);
}
errx(1, if (beVerbose && collided)
"%s(%ld) : Unknown symbol '%s'", warnx("Symbol hashmap collision occurred!");
pPatch->pzFilename, pPatch->nLineNo,
tzName);
} }
int32_t sym_GetBank(struct sPatch *pPatch, char *tzName) struct Symbol *sym_GetSymbol(char const *name)
{ {
struct ISymbol **ppSym; return (struct Symbol *)hash_GetElement(symbols, name);
ppSym = &(tHash[calchash(tzName)]);
while (*ppSym) {
if (strcmp(tzName, (*ppSym)->pzName))
ppSym = &((*ppSym)->pNext);
else
return ((*ppSym)->nBank);
}
errx(1,
"%s(%ld) : Unknown symbol '%s'",
pPatch->pzFilename, pPatch->nLineNo,
tzName);
} }
void sym_CreateSymbol(char *tzName, int32_t nValue, int32_t nBank, void sym_CleanupSymbols(void)
char *tzObjFileName, char *tzFileName, uint32_t nFileLine)
{ {
if (strcmp(tzName, "@") == 0) hash_EmptyMap(symbols);
return;
struct ISymbol **ppSym;
ppSym = &(tHash[calchash(tzName)]);
while (*ppSym) {
if (strcmp(tzName, (*ppSym)->pzName)) {
ppSym = &((*ppSym)->pNext);
} else {
if (nBank == -1)
return;
errx(1, "'%s' in both %s : %s(%d) and %s : %s(%d)",
tzName, tzObjFileName, tzFileName, nFileLine,
(*ppSym)->tzObjFileName,
(*ppSym)->tzFileName, (*ppSym)->nFileLine);
}
}
*ppSym = malloc(sizeof **ppSym);
if (*ppSym != NULL) {
(*ppSym)->pzName = malloc(strlen(tzName) + 1);
if ((*ppSym)->pzName != NULL) {
strcpy((*ppSym)->pzName, tzName);
(*ppSym)->nValue = nValue;
(*ppSym)->nBank = nBank;
(*ppSym)->pNext = NULL;
strncpy((*ppSym)->tzObjFileName, tzObjFileName,
sizeof((*ppSym)->tzObjFileName));
strncpy((*ppSym)->tzFileName, tzFileName,
sizeof((*ppSym)->tzFileName));
(*ppSym)->nFileLine = nFileLine;
}
}
} }

View File

@@ -91,8 +91,8 @@ REPT NumberOfSections
; decide (floating bank). This field is only valid for ROMX, ; decide (floating bank). This field is only valid for ROMX,
; VRAM, WRAMX and SRAM sections. ; VRAM, WRAMX and SRAM sections.
LONG Align ; Alignment of this section (expressed as number of low bits LONG Align ; Alignment of this section, expressed as 1 << align. 1 if
; to leave as 0). -1 if not defined. ; not specified.
IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data. IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data.

View File

@@ -1 +1 @@
error: Unable to place 'r0b' (ROM0 section) anywhere error: Unable to place "r0a" (ROM0 section) anywhere

View File

@@ -1 +1 @@
error: ROMX sections can't be used with option -t. error: rx: ROMX sections cannot be used with option -t.

View File

@@ -1,5 +1,5 @@
ROM0 ROM0
org $10 org $18
"sec" "sec"
org $20 org $20
"secfix" "secfix"

View File

@@ -1,2 +1 @@
error: Different attributes for "sec" in source and linkerscript error: Linker script contradicts "sec"'s alignment

View File

@@ -1 +1 @@
error: VRAM bank 1 can't be used with option -d. error: v1: VRAM bank 1 can't be used with option -d.

View File

@@ -1 +1 @@
error: Unable to place 'v1' (VRAM section) in any bank error: Unable to place "v1" (VRAM section) anywhere

View File

@@ -1 +1 @@
error: WRAMX sections can't be used with options -w or -d. error: wx: WRAMX sections cannot be used with options -w or -d.

View File

@@ -1 +1 @@
error: Unable to place 'w0b' (WRAM0 section) anywhere error: Unable to place "w0b" (WRAM0 section) anywhere