mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Implement compact file stacks in object files
Gets rid of `open_memstream`, enabling Windows compatibility again Also fixes #491 as a nice bonus!
This commit is contained in:
@@ -46,7 +46,7 @@ if(MSVC)
|
||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||
else()
|
||||
if(DEVELOP)
|
||||
add_compile_options(-Werror -Wall -Wextra -pedantic
|
||||
add_compile_options(-Werror -Wall -Wextra -pedantic -Wno-type-limits
|
||||
-Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2
|
||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
|
||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5
|
||||
|
||||
2
Makefile
2
Makefile
@@ -186,7 +186,7 @@ checkpatch:
|
||||
# compilation and make the continous integration infrastructure return failure.
|
||||
|
||||
develop:
|
||||
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic \
|
||||
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
|
||||
-Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2 \
|
||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
|
||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \
|
||||
|
||||
@@ -21,26 +21,44 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct MacroArgs;
|
||||
struct FileStackNode {
|
||||
struct FileStackNode *parent; /* Pointer to parent node, for error reporting */
|
||||
/* Line at which the parent context was exited; meaningless for the root level */
|
||||
uint32_t lineNo;
|
||||
|
||||
struct sContext {
|
||||
struct LexerState *lexerState;
|
||||
struct Symbol const *pMacro;
|
||||
struct sContext *next;
|
||||
char tzFileName[_MAX_PATH + 1];
|
||||
struct MacroArgs *macroArgs;
|
||||
uint32_t uniqueID;
|
||||
int32_t nLine;
|
||||
uint32_t nStatus;
|
||||
char const *pREPTBlock;
|
||||
uint32_t nREPTBlockCount;
|
||||
uint32_t nREPTBlockSize;
|
||||
int32_t nREPTBodyFirstLine;
|
||||
int32_t nREPTBodyLastLine;
|
||||
struct FileStackNode *next; /* Next node in the output linked list */
|
||||
bool referenced; /* If referenced, don't free! */
|
||||
uint32_t ID; /* Set only if referenced: ID within the object file, -1 if not output yet */
|
||||
|
||||
enum {
|
||||
NODE_REPT,
|
||||
NODE_FILE,
|
||||
NODE_MACRO,
|
||||
} type;
|
||||
};
|
||||
|
||||
struct FileStackReptNode { /* NODE_REPT */
|
||||
struct FileStackNode node;
|
||||
uint32_t reptDepth;
|
||||
/* WARNING: if changing this type, change overflow check in `fstk_Init` */
|
||||
uint32_t iters[]; /* REPT iteration counts since last named node, in reverse depth order */
|
||||
};
|
||||
|
||||
struct FileStackNamedNode { /* NODE_FILE, NODE_MACRO */
|
||||
struct FileStackNode node;
|
||||
char name[]; /* File name for files, file::macro name for macros */
|
||||
};
|
||||
|
||||
extern size_t nMaxRecursionDepth;
|
||||
|
||||
struct MacroArgs;
|
||||
|
||||
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo);
|
||||
void fstk_DumpCurrent(void);
|
||||
struct FileStackNode *fstk_GetFileStack(void);
|
||||
/* The lifetime of the returned chars is until reaching the end of that file */
|
||||
char const *fstk_GetFileName(void);
|
||||
|
||||
void fstk_AddIncludePath(char const *s);
|
||||
/**
|
||||
* @param path The user-provided file name
|
||||
@@ -53,14 +71,9 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
|
||||
|
||||
bool yywrap(void);
|
||||
void fstk_RunInclude(char const *path);
|
||||
void fstk_RunMacro(char *macroName, struct MacroArgs *args);
|
||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
|
||||
|
||||
void fstk_Dump(void);
|
||||
char *fstk_DumpToStr(void);
|
||||
char const *fstk_GetFileName(void);
|
||||
uint32_t fstk_GetLine(void);
|
||||
|
||||
void fstk_Init(char *mainPath, size_t maxRecursionDepth);
|
||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
|
||||
|
||||
#endif /* RGBDS_ASM_FSTACK_H */
|
||||
|
||||
@@ -43,6 +43,9 @@ static inline void lexer_SetGfxDigits(char const *digits)
|
||||
gfxDigits = digits;
|
||||
}
|
||||
|
||||
/*
|
||||
* `path` is referenced, but not held onto..!
|
||||
*/
|
||||
struct LexerState *lexer_OpenFile(char const *path);
|
||||
struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo);
|
||||
void lexer_RestartRept(uint32_t lineNo);
|
||||
|
||||
@@ -18,6 +18,8 @@ struct Expression;
|
||||
extern char *tzObjectname;
|
||||
extern struct Section *pSectionList, *pCurrentSection;
|
||||
|
||||
void out_RegisterNode(struct FileStackNode *node);
|
||||
void out_ReplaceNode(struct FileStackNode *node);
|
||||
void out_SetFileName(char *s);
|
||||
void out_CreatePatch(uint32_t type, struct Expression const *expr,
|
||||
uint32_t ofs);
|
||||
|
||||
@@ -35,8 +35,8 @@ struct Symbol {
|
||||
bool isExported; /* Whether the symbol is to be exported */
|
||||
bool isBuiltin; /* Whether the symbol is a built-in */
|
||||
struct Section *section;
|
||||
char fileName[_MAX_PATH + 1]; /* File where the symbol was defined. */
|
||||
uint32_t fileLine; /* Line where the symbol was defined. */
|
||||
struct FileStackNode *src; /* Where the symbol was defined */
|
||||
uint32_t fileLine; /* Line where the symbol was defined */
|
||||
|
||||
bool hasCallback;
|
||||
union {
|
||||
|
||||
@@ -29,15 +29,45 @@ extern bool beVerbose;
|
||||
extern bool isWRA0Mode;
|
||||
extern bool disablePadding;
|
||||
|
||||
struct FileStackNode {
|
||||
struct FileStackNode *parent;
|
||||
/* Line at which the parent context was exited; meaningless for the root level */
|
||||
uint32_t lineNo;
|
||||
|
||||
enum {
|
||||
NODE_REPT,
|
||||
NODE_FILE,
|
||||
NODE_MACRO,
|
||||
} type;
|
||||
union {
|
||||
char *name; /* NODE_FILE, NODE_MACRO */
|
||||
struct { /* NODE_REPT */
|
||||
uint32_t reptDepth;
|
||||
uint32_t *iters;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/* Helper macro for printing verbose-mode messages */
|
||||
#define verbosePrint(...) do { \
|
||||
if (beVerbose) \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
void error(char const *fmt, ...);
|
||||
/**
|
||||
* Dump a file stack to stderr
|
||||
* @param node The leaf node to dump the context of
|
||||
*/
|
||||
char const *dumpFileStack(struct FileStackNode const *node);
|
||||
|
||||
noreturn_ void fatal(char const *fmt, ...);
|
||||
void warning(struct FileStackNode const *where, uint32_t lineNo,
|
||||
char const *fmt, ...) format_(printf, 3, 4);
|
||||
|
||||
void error(struct FileStackNode const *where, uint32_t lineNo,
|
||||
char const *fmt, ...) format_(printf, 3, 4);
|
||||
|
||||
noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo,
|
||||
char const *fmt, ...) format_(printf, 3, 4);
|
||||
|
||||
/**
|
||||
* Opens a file if specified, and aborts on error.
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
/**
|
||||
* Read an object (.o) file, and add its info to the data structures.
|
||||
* @param fileName A path to the object file to be read
|
||||
* @param i The ID of the file
|
||||
*/
|
||||
void obj_ReadFile(char const *fileName);
|
||||
void obj_ReadFile(char const *fileName, unsigned int i);
|
||||
|
||||
/**
|
||||
* Perform validation on the object files' contents
|
||||
@@ -27,6 +28,12 @@ void obj_DoSanityChecks(void);
|
||||
*/
|
||||
void obj_CheckAssertions(void);
|
||||
|
||||
/**
|
||||
* Sets up object file reading
|
||||
* @param nbFiles The number of object files that will be read
|
||||
*/
|
||||
void obj_Setup(unsigned int nbFiles);
|
||||
|
||||
/**
|
||||
* `free`s all object memory that was allocated.
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "linkdefs.h"
|
||||
|
||||
struct FileStackNode;
|
||||
struct Section;
|
||||
|
||||
struct AttachedSymbol {
|
||||
@@ -27,7 +28,8 @@ struct AttachedSymbol {
|
||||
};
|
||||
|
||||
struct Patch {
|
||||
char *fileName;
|
||||
struct FileStackNode const *src;
|
||||
uint32_t lineNo;
|
||||
int32_t offset;
|
||||
uint32_t pcSectionID;
|
||||
uint32_t pcOffset;
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
#include "linkdefs.h"
|
||||
|
||||
struct FileStackNode;
|
||||
|
||||
struct Symbol {
|
||||
/* Info contained in the object files */
|
||||
char *name;
|
||||
enum ExportLevel type;
|
||||
char const *objFileName;
|
||||
char *fileName;
|
||||
struct FileStackNode const *src;
|
||||
int32_t lineNo;
|
||||
int32_t sectionID;
|
||||
union {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
||||
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
||||
#define RGBDS_OBJECT_REV 5U
|
||||
#define RGBDS_OBJECT_REV 6U
|
||||
|
||||
enum AssertionType {
|
||||
ASSERT_WARN,
|
||||
|
||||
392
src/asm/fstack.c
392
src/asm/fstack.c
@@ -29,27 +29,83 @@
|
||||
|
||||
struct Context {
|
||||
struct Context *parent;
|
||||
struct Context *child;
|
||||
struct FileStackNode *fileInfo;
|
||||
struct LexerState *lexerState;
|
||||
uint32_t uniqueID;
|
||||
char const *fileName;
|
||||
char *fileNameBuf;
|
||||
uint32_t lineNo; /* Line number at which the context was EXITED */
|
||||
struct Symbol const *macro;
|
||||
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
||||
uint32_t nbReptIters; /* If zero, this isn't a REPT block */
|
||||
size_t reptDepth;
|
||||
uint32_t reptIters[];
|
||||
uint32_t nbReptIters;
|
||||
};
|
||||
|
||||
static struct Context *contextStack;
|
||||
static struct Context *topLevelContext;
|
||||
static size_t contextDepth = 0;
|
||||
#define DEFAULT_MAX_DEPTH 64
|
||||
size_t nMaxRecursionDepth;
|
||||
|
||||
static unsigned int nbIncPaths = 0;
|
||||
static char const *includePaths[MAXINCPATHS];
|
||||
|
||||
char const *dumpNodeAndParents(struct FileStackNode const *node)
|
||||
{
|
||||
char const *name;
|
||||
|
||||
if (node->type == NODE_REPT) {
|
||||
assert(node->parent); /* REPT nodes should always have a parent */
|
||||
struct FileStackReptNode const *reptInfo = (struct FileStackReptNode const *)node;
|
||||
|
||||
name = dumpNodeAndParents(node->parent);
|
||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
|
||||
for (uint32_t i = reptInfo->reptDepth; i--; )
|
||||
fprintf(stderr, "::REPT~%" PRIu32, reptInfo->iters[i]);
|
||||
} else {
|
||||
name = ((struct FileStackNamedNode const *)node)->name;
|
||||
if (node->parent) {
|
||||
dumpNodeAndParents(node->parent);
|
||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
|
||||
} else {
|
||||
fputs(name, stderr);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo)
|
||||
{
|
||||
dumpNodeAndParents(node);
|
||||
fprintf(stderr, "(%" PRIu32 ")", lineNo);
|
||||
}
|
||||
|
||||
void fstk_DumpCurrent(void)
|
||||
{
|
||||
if (!contextStack) {
|
||||
fputs("at top level", stderr);
|
||||
return;
|
||||
}
|
||||
fstk_Dump(contextStack->fileInfo, lexer_GetLineNo());
|
||||
}
|
||||
|
||||
struct FileStackNode *fstk_GetFileStack(void)
|
||||
{
|
||||
struct FileStackNode *node = contextStack->fileInfo;
|
||||
|
||||
/* Mark node and all of its parents as referenced if not already so they don't get freed */
|
||||
while (node && !node->referenced) {
|
||||
node->ID = -1;
|
||||
node->referenced = true;
|
||||
node = node->parent;
|
||||
}
|
||||
return contextStack->fileInfo;
|
||||
}
|
||||
|
||||
char const *fstk_GetFileName(void)
|
||||
{
|
||||
/* Iterating via the nodes themselves skips nested REPTs */
|
||||
struct FileStackNode const *node = contextStack->fileInfo;
|
||||
|
||||
while (node->type != NODE_FILE)
|
||||
node = node->parent;
|
||||
return ((struct FileStackNamedNode const *)node)->name;
|
||||
}
|
||||
|
||||
void fstk_AddIncludePath(char const *path)
|
||||
{
|
||||
if (path[0] == '\0')
|
||||
@@ -141,12 +197,28 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
||||
|
||||
bool yywrap(void)
|
||||
{
|
||||
if (contextStack->nbReptIters) { /* The context is a REPT block, which may loop */
|
||||
contextStack->reptIters[contextStack->reptDepth - 1]++;
|
||||
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
||||
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
||||
|
||||
/* If the node is referenced, we can't edit it; duplicate it */
|
||||
if (contextStack->fileInfo->referenced) {
|
||||
struct FileStackReptNode *copy = malloc(sizeof(*copy) + sizeof(copy->iters[0]) * fileInfo->reptDepth);
|
||||
|
||||
if (!copy)
|
||||
fatalerror("Failed to duplicate REPT file node: %s\n", strerror(errno));
|
||||
/* Copy all info but the referencing */
|
||||
*copy = *fileInfo;
|
||||
copy->node.next = NULL;
|
||||
copy->node.referenced = false;
|
||||
|
||||
fileInfo = copy;
|
||||
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
|
||||
}
|
||||
|
||||
fileInfo->iters[0]++;
|
||||
/* If this wasn't the last iteration, wrap instead of popping */
|
||||
if (contextStack->reptIters[contextStack->reptDepth - 1]
|
||||
<= contextStack->nbReptIters) {
|
||||
lexer_RestartRept(contextStack->parent->lineNo);
|
||||
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||
lexer_RestartRept(contextStack->fileInfo->lineNo);
|
||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||
return false;
|
||||
}
|
||||
@@ -155,44 +227,52 @@ bool yywrap(void)
|
||||
}
|
||||
dbgPrint("Popping context\n");
|
||||
|
||||
/* Free an `INCLUDE`'s path */
|
||||
if (contextStack->fileNameBuf)
|
||||
free(contextStack->fileNameBuf);
|
||||
struct Context *context = contextStack;
|
||||
|
||||
contextStack = contextStack->parent;
|
||||
contextDepth--;
|
||||
|
||||
lexer_DeleteState(contextStack->child->lexerState);
|
||||
lexer_DeleteState(context->lexerState);
|
||||
/* Restore args if a macro (not REPT) saved them */
|
||||
if (contextStack->child->nbReptIters == 0 && contextStack->child->macro) {
|
||||
if (context->fileInfo->type == NODE_MACRO) {
|
||||
dbgPrint("Restoring macro args %p\n", contextStack->macroArgs);
|
||||
macro_UseNewArgs(contextStack->macroArgs);
|
||||
}
|
||||
/* Free the file stack node */
|
||||
if (!context->fileInfo->referenced)
|
||||
free(context->fileInfo);
|
||||
/* Free the entry and make its parent the current entry */
|
||||
free(contextStack->child);
|
||||
free(context);
|
||||
|
||||
contextStack->child = NULL;
|
||||
lexer_SetState(contextStack->lexerState);
|
||||
macro_SetUniqueID(contextStack->uniqueID);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void newContext(uint32_t reptDepth)
|
||||
/*
|
||||
* Make sure not to switch the lexer state before calling this, so the saved line no is correct
|
||||
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
|
||||
*/
|
||||
static void newContext(struct FileStackNode *fileInfo)
|
||||
{
|
||||
if (++contextDepth >= nMaxRecursionDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
|
||||
contextStack->child = malloc(sizeof(*contextStack->child)
|
||||
+ reptDepth * sizeof(contextStack->reptIters[0]));
|
||||
if (!contextStack->child)
|
||||
struct Context *context = malloc(sizeof(*context));
|
||||
|
||||
if (!context)
|
||||
fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno));
|
||||
fileInfo->parent = contextStack->fileInfo;
|
||||
fileInfo->lineNo = 0; /* Init to a default value, see struct definition for info */
|
||||
fileInfo->referenced = false;
|
||||
fileInfo->lineNo = lexer_GetLineNo();
|
||||
context->fileInfo = fileInfo;
|
||||
/*
|
||||
* Link new entry to its parent so it's reachable later
|
||||
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
||||
*/
|
||||
context->parent = contextStack;
|
||||
contextStack = context;
|
||||
|
||||
contextStack->lineNo = lexer_GetLineNo();
|
||||
/* Link new entry to its parent so it's reachable later */
|
||||
contextStack->child->parent = contextStack;
|
||||
contextStack = contextStack->child;
|
||||
|
||||
contextStack->child = NULL;
|
||||
contextStack->reptDepth = reptDepth;
|
||||
}
|
||||
|
||||
void fstk_RunInclude(char const *path)
|
||||
@@ -212,21 +292,27 @@ void fstk_RunInclude(char const *path)
|
||||
}
|
||||
dbgPrint("Full path: \"%s\"\n", fullPath);
|
||||
|
||||
newContext(0);
|
||||
contextStack->lexerState = lexer_OpenFile(fullPath);
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for INCLUDE: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_FILE;
|
||||
strcpy(fileInfo->name, fullPath);
|
||||
free(fullPath);
|
||||
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
contextStack->lexerState = lexer_OpenFile(fileInfo->name);
|
||||
if (!contextStack->lexerState)
|
||||
fatalerror("Failed to set up lexer for file include\n");
|
||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||
/* We're back at top-level, so most things are reset */
|
||||
contextStack->uniqueID = 0;
|
||||
macro_SetUniqueID(0);
|
||||
contextStack->fileName = fullPath;
|
||||
contextStack->fileNameBuf = fullPath;
|
||||
contextStack->macro = NULL;
|
||||
contextStack->nbReptIters = 0;
|
||||
}
|
||||
|
||||
void fstk_RunMacro(char *macroName, struct MacroArgs *args)
|
||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
||||
{
|
||||
dbgPrint("Running macro \"%s\"\n", macroName);
|
||||
|
||||
@@ -242,7 +328,53 @@ void fstk_RunMacro(char *macroName, struct MacroArgs *args)
|
||||
}
|
||||
contextStack->macroArgs = macro_GetCurrentArgs();
|
||||
|
||||
newContext(0);
|
||||
/* Compute total length of this node's name: <base name>::<macro> */
|
||||
size_t reptNameLen = 0;
|
||||
struct FileStackNode const *node = macro->src;
|
||||
|
||||
if (node->type == NODE_REPT) {
|
||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||
|
||||
/* 4294967295 = 2^32 - 1, aka UINT32_MAX */
|
||||
reptNameLen += reptNode->reptDepth * strlen("::REPT~4294967295");
|
||||
/* Look for next named node */
|
||||
do {
|
||||
node = node->parent;
|
||||
} while (node->type == NODE_REPT);
|
||||
}
|
||||
struct FileStackNamedNode const *baseNode = (struct FileStackNamedNode const *)node;
|
||||
size_t baseLen = strlen(baseNode->name);
|
||||
size_t macroNameLen = strlen(macro->name);
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + baseLen
|
||||
+ reptNameLen + 2 + macroNameLen + 1);
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for \"%s\": %s\n", macro->name, strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_MACRO;
|
||||
/* Print the name... */
|
||||
char *dest = fileInfo->name;
|
||||
|
||||
memcpy(dest, baseNode->name, baseLen);
|
||||
dest += baseLen;
|
||||
if (node->type == NODE_REPT) {
|
||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||
|
||||
for (uint32_t i = reptNode->reptDepth; i--; ) {
|
||||
int nbChars = sprintf(dest, "::REPT~%" PRIu32, reptNode->iters[i]);
|
||||
|
||||
if (nbChars < 0)
|
||||
fatalerror("Failed to write macro invocation info: %s\n",
|
||||
strerror(errno));
|
||||
dest += nbChars;
|
||||
}
|
||||
}
|
||||
*dest++ = ':';
|
||||
*dest++ = ':';
|
||||
memcpy(dest, macro->name, macroNameLen + 1);
|
||||
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
/* Line minus 1 because buffer begins with a newline */
|
||||
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
|
||||
macro->fileLine - 1);
|
||||
@@ -250,143 +382,93 @@ void fstk_RunMacro(char *macroName, struct MacroArgs *args)
|
||||
fatalerror("Failed to set up lexer for macro invocation\n");
|
||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||
contextStack->fileName = macro->fileName;
|
||||
contextStack->fileNameBuf = NULL;
|
||||
contextStack->macro = macro;
|
||||
contextStack->nbReptIters = 0;
|
||||
macro_UseNewArgs(args);
|
||||
}
|
||||
|
||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size)
|
||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
||||
{
|
||||
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
uint32_t reptDepth = contextStack->reptDepth;
|
||||
|
||||
newContext(reptDepth + 1);
|
||||
contextStack->lexerState = lexer_OpenFileView(body, size, nReptLineNo);
|
||||
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
|
||||
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
|
||||
: 0;
|
||||
struct FileStackReptNode *fileInfo = malloc(sizeof(*fileInfo)
|
||||
+ (reptDepth + 1) * sizeof(fileInfo->iters[0]));
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
fileInfo->node.type = NODE_REPT;
|
||||
fileInfo->reptDepth = reptDepth + 1;
|
||||
fileInfo->iters[0] = 1;
|
||||
if (reptDepth)
|
||||
/* Copy all parent iter counts */
|
||||
memcpy(&fileInfo->iters[1],
|
||||
((struct FileStackReptNode *)contextStack->fileInfo)->iters,
|
||||
reptDepth * sizeof(fileInfo->iters[0]));
|
||||
|
||||
newContext((struct FileStackNode *)fileInfo);
|
||||
/* Correct our line number, which currently points to the `ENDR` line */
|
||||
contextStack->fileInfo->lineNo = reptLineNo;
|
||||
|
||||
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
|
||||
if (!contextStack->lexerState)
|
||||
fatalerror("Failed to set up lexer for macro invocation\n");
|
||||
fatalerror("Failed to set up lexer for rept block\n");
|
||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||
contextStack->fileName = contextStack->parent->fileName;
|
||||
contextStack->fileNameBuf = NULL;
|
||||
contextStack->macro = contextStack->parent->macro; /* Inherit */
|
||||
contextStack->nbReptIters = count;
|
||||
/* Copy all of parent's iters, and add ours */
|
||||
if (reptDepth)
|
||||
memcpy(contextStack->reptIters, contextStack->parent->reptIters,
|
||||
sizeof(contextStack->reptIters[0]) * reptDepth);
|
||||
contextStack->reptIters[reptDepth] = 1;
|
||||
|
||||
/* Correct our parent's line number, which currently points to the `ENDR` line */
|
||||
contextStack->parent->lineNo = nReptLineNo;
|
||||
}
|
||||
|
||||
static void printContext(FILE *stream, struct Context const *context)
|
||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
||||
{
|
||||
fprintf(stream, "%s", context->fileName);
|
||||
if (context->macro)
|
||||
fprintf(stream, "::%s", context->macro->name);
|
||||
for (size_t i = 0; i < context->reptDepth; i++)
|
||||
fprintf(stream, "::REPT~%" PRIu32, context->reptIters[i]);
|
||||
fprintf(stream, "(%" PRId32 ")", context->lineNo);
|
||||
}
|
||||
struct LexerState *state = lexer_OpenFile(mainPath);
|
||||
|
||||
static void dumpToStream(FILE *stream)
|
||||
{
|
||||
struct Context *context = topLevelContext;
|
||||
|
||||
while (context != contextStack) {
|
||||
printContext(stream, context);
|
||||
fprintf(stream, " -> ");
|
||||
context = context->child;
|
||||
}
|
||||
contextStack->lineNo = lexer_GetLineNo();
|
||||
printContext(stream, contextStack);
|
||||
}
|
||||
|
||||
void fstk_Dump(void)
|
||||
{
|
||||
dumpToStream(stderr);
|
||||
}
|
||||
|
||||
char *fstk_DumpToStr(void)
|
||||
{
|
||||
char *str;
|
||||
size_t size;
|
||||
/* `open_memstream` is specified to always include a '\0' at the end of the buffer! */
|
||||
FILE *stream = open_memstream(&str, &size);
|
||||
|
||||
if (!stream)
|
||||
fatalerror("Failed to dump file stack to string: %s\n", strerror(errno));
|
||||
dumpToStream(stream);
|
||||
fclose(stream);
|
||||
return str;
|
||||
}
|
||||
|
||||
char const *fstk_GetFileName(void)
|
||||
{
|
||||
/* FIXME: this is awful, but all callees copy the buffer anyways */
|
||||
static char fileName[_MAX_PATH + 1];
|
||||
size_t remainingChars = _MAX_PATH + 1;
|
||||
char *dest = fileName;
|
||||
char const *src = contextStack->fileName;
|
||||
|
||||
#define append(...) do { \
|
||||
int nbChars = snprintf(dest, remainingChars, __VA_ARGS__); \
|
||||
\
|
||||
if (nbChars >= remainingChars) \
|
||||
fatalerror("File stack entry too large\n"); \
|
||||
remainingChars -= nbChars; \
|
||||
dest += nbChars; \
|
||||
} while (0)
|
||||
|
||||
while (*src && --remainingChars) /* Leave room for terminator */
|
||||
*dest++ = *src++;
|
||||
if (remainingChars && contextStack->macro)
|
||||
append("::%s", contextStack->macro->name);
|
||||
for (size_t i = 0; i < contextStack->reptDepth; i++)
|
||||
append("::REPT~%" PRIu32, contextStack->reptIters[i]);
|
||||
|
||||
*dest = '\0';
|
||||
return fileName;
|
||||
}
|
||||
|
||||
uint32_t fstk_GetLine(void)
|
||||
{
|
||||
return lexer_GetLineNo();
|
||||
}
|
||||
|
||||
void fstk_Init(char *mainPath, size_t maxRecursionDepth)
|
||||
{
|
||||
topLevelContext = malloc(sizeof(*topLevelContext));
|
||||
if (!topLevelContext)
|
||||
fatalerror("Failed to allocate memory for initial context: %s\n", strerror(errno));
|
||||
topLevelContext->parent = NULL;
|
||||
topLevelContext->child = NULL;
|
||||
topLevelContext->lexerState = lexer_OpenFile(mainPath);
|
||||
if (!topLevelContext->lexerState)
|
||||
if (!state)
|
||||
fatalerror("Failed to open main file!\n");
|
||||
lexer_SetState(topLevelContext->lexerState);
|
||||
topLevelContext->uniqueID = 0;
|
||||
lexer_SetState(state);
|
||||
char const *fileName = lexer_GetFileName();
|
||||
size_t len = strlen(fileName);
|
||||
struct Context *context = malloc(sizeof(*contextStack));
|
||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + len + 1);
|
||||
|
||||
if (!context)
|
||||
fatalerror("Failed to allocate memory for main context: %s\n", strerror(errno));
|
||||
if (!fileInfo)
|
||||
fatalerror("Failed to allocate memory for main file info: %s\n", strerror(errno));
|
||||
|
||||
context->fileInfo = (struct FileStackNode *)fileInfo;
|
||||
/* lineNo and reptIter are unused on the top-level context */
|
||||
context->fileInfo->parent = NULL;
|
||||
context->fileInfo->referenced = false;
|
||||
context->fileInfo->type = NODE_FILE;
|
||||
memcpy(fileInfo->name, fileName, len + 1);
|
||||
|
||||
context->parent = NULL;
|
||||
context->lexerState = state;
|
||||
context->uniqueID = 0;
|
||||
macro_SetUniqueID(0);
|
||||
topLevelContext->fileName = lexer_GetFileName();
|
||||
topLevelContext->fileNameBuf = NULL;
|
||||
topLevelContext->macro = NULL;
|
||||
topLevelContext->nbReptIters = 0;
|
||||
topLevelContext->reptDepth = 0;
|
||||
context->nbReptIters = 0;
|
||||
|
||||
contextStack = topLevelContext;
|
||||
/* Now that it's set up properly, register the context */
|
||||
contextStack = context;
|
||||
|
||||
if (maxRecursionDepth
|
||||
> (SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0])) {
|
||||
error("Recursion depth may not be higher than %zu, defaulting to 64\n",
|
||||
(SIZE_MAX - sizeof(*contextStack)) / sizeof(contextStack->reptIters[0]));
|
||||
nMaxRecursionDepth = 64;
|
||||
/*
|
||||
* Check that max recursion depth won't allow overflowing node `malloc`s
|
||||
* This assumes that the rept node is larger
|
||||
*/
|
||||
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
|
||||
if (maxRecursionDepth > DEPTH_LIMIT) {
|
||||
error("Recursion depth may not be higher than %zu, defaulting to "
|
||||
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
|
||||
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
|
||||
} else {
|
||||
nMaxRecursionDepth = maxRecursionDepth;
|
||||
}
|
||||
/* Make sure that the default of 64 is OK, though */
|
||||
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
||||
#undef DEPTH_LIMIT
|
||||
}
|
||||
|
||||
131
src/asm/output.c
131
src/asm/output.c
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -33,7 +34,8 @@
|
||||
#include "platform.h" // strdup
|
||||
|
||||
struct Patch {
|
||||
char *tzFilename;
|
||||
struct FileStackNode const *src;
|
||||
uint32_t lineNo;
|
||||
uint32_t nOffset;
|
||||
struct Section *pcSection;
|
||||
uint32_t pcOffset;
|
||||
@@ -62,19 +64,17 @@ static uint32_t nbSymbols = 0; /* Length of the above list */
|
||||
|
||||
static struct Assertion *assertions = NULL;
|
||||
|
||||
static struct FileStackNode *fileStackNodes = NULL;
|
||||
|
||||
/*
|
||||
* Count the number of sections used in this object
|
||||
*/
|
||||
static uint32_t countsections(void)
|
||||
{
|
||||
struct Section *sect;
|
||||
uint32_t count = 0;
|
||||
|
||||
sect = pSectionList;
|
||||
while (sect) {
|
||||
for (struct Section const *sect = pSectionList; sect; sect = sect->next)
|
||||
count++;
|
||||
sect = sect->next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -129,16 +129,60 @@ static void fputstring(char const *s, FILE *f)
|
||||
fputc(0, f);
|
||||
}
|
||||
|
||||
static uint32_t getNbFileStackNodes(void)
|
||||
{
|
||||
return fileStackNodes ? fileStackNodes->ID + 1 : 0;
|
||||
}
|
||||
|
||||
void out_RegisterNode(struct FileStackNode *node)
|
||||
{
|
||||
/* If node is not already registered, register it (and parents), and give it a unique ID */
|
||||
while (node->ID == -1) {
|
||||
node->ID = getNbFileStackNodes();
|
||||
if (node->ID == -1)
|
||||
fatalerror("Reached too many file stack nodes; try splitting the file up\n");
|
||||
node->next = fileStackNodes;
|
||||
fileStackNodes = node;
|
||||
|
||||
/* Also register the node's parents */
|
||||
node = node->parent;
|
||||
if (!node)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void out_ReplaceNode(struct FileStackNode *node)
|
||||
{
|
||||
(void)node;
|
||||
#if 0
|
||||
This is code intended to replace a node, which is pretty useless until ref counting is added...
|
||||
|
||||
struct FileStackNode **ptr = &fileStackNodes;
|
||||
|
||||
/*
|
||||
* The linked list is supposed to have decrementing IDs, so iterate with less memory reads,
|
||||
* to hopefully hit the cache less. A debug check is added after, in case a change is made
|
||||
* that breaks this assumption.
|
||||
*/
|
||||
for (uint32_t i = fileStackNodes->ID; i != node->ID; i--)
|
||||
ptr = &(*ptr)->next;
|
||||
assert((*ptr)->ID == node->ID);
|
||||
|
||||
node->next = (*ptr)->next;
|
||||
assert(!node->next || node->next->ID == node->ID - 1); /* Catch inconsistencies early */
|
||||
/* TODO: unreference the node */
|
||||
*ptr = node;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a section's ID
|
||||
*/
|
||||
static uint32_t getsectid(struct Section const *sect)
|
||||
{
|
||||
struct Section const *sec;
|
||||
struct Section const *sec = pSectionList;
|
||||
uint32_t ID = 0;
|
||||
|
||||
sec = pSectionList;
|
||||
|
||||
while (sec) {
|
||||
if (sec == sect)
|
||||
return ID;
|
||||
@@ -159,7 +203,10 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
|
||||
*/
|
||||
static void writepatch(struct Patch const *patch, FILE *f)
|
||||
{
|
||||
fputstring(patch->tzFilename, f);
|
||||
assert(patch->src->ID != -1);
|
||||
|
||||
fputlong(patch->src->ID, f);
|
||||
fputlong(patch->lineNo, f);
|
||||
fputlong(patch->nOffset, f);
|
||||
fputlong(getSectIDIfAny(patch->pcSection), f);
|
||||
fputlong(patch->pcOffset, f);
|
||||
@@ -206,26 +253,35 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
|
||||
if (!sym_IsDefined(sym)) {
|
||||
fputc(SYMTYPE_IMPORT, f);
|
||||
} else {
|
||||
assert(sym->src->ID != -1);
|
||||
|
||||
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
|
||||
fputstring(sym->fileName, f);
|
||||
fputlong(sym->src->ID, f);
|
||||
fputlong(sym->fileLine, f);
|
||||
fputlong(getSectIDIfAny(sym_GetSection(sym)), f);
|
||||
fputlong(sym->value, f);
|
||||
}
|
||||
}
|
||||
|
||||
static void registerSymbol(struct Symbol *sym)
|
||||
{
|
||||
*objectSymbolsTail = sym;
|
||||
objectSymbolsTail = &sym->next;
|
||||
out_RegisterNode(sym->src);
|
||||
if (nbSymbols == -1)
|
||||
fatalerror("Registered too many symbols (%" PRIu32
|
||||
"); try splitting up your files\n", (uint32_t)-1);
|
||||
sym->ID = nbSymbols++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a symbol's ID within the object file
|
||||
* If the symbol does not have one, one is assigned by registering the symbol
|
||||
*/
|
||||
static uint32_t getSymbolID(struct Symbol *sym)
|
||||
{
|
||||
if (sym->ID == -1) {
|
||||
sym->ID = nbSymbols++;
|
||||
|
||||
*objectSymbolsTail = sym;
|
||||
objectSymbolsTail = &sym->next;
|
||||
}
|
||||
if (sym->ID == -1 && !sym_IsPC(sym))
|
||||
registerSymbol(sym);
|
||||
return sym->ID;
|
||||
}
|
||||
|
||||
@@ -303,22 +359,25 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||
|
||||
/*
|
||||
* Allocate a new patch structure and link it into the list
|
||||
* WARNING: all patches are assumed to eventually be written, so the file stack node is registered
|
||||
*/
|
||||
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
|
||||
uint32_t ofs)
|
||||
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
|
||||
{
|
||||
struct Patch *patch = malloc(sizeof(struct Patch));
|
||||
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
|
||||
struct FileStackNode *node = fstk_GetFileStack();
|
||||
|
||||
if (!patch)
|
||||
fatalerror("No memory for patch: %s\n", strerror(errno));
|
||||
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
|
||||
|
||||
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
|
||||
if (!patch->pRPN)
|
||||
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
|
||||
|
||||
patch->type = type;
|
||||
patch->tzFilename = fstk_DumpToStr();
|
||||
patch->src = node;
|
||||
out_RegisterNode(node);
|
||||
patch->lineNo = lexer_GetLineNo();
|
||||
patch->nOffset = ofs;
|
||||
patch->pcSection = sect_GetSymbolSection();
|
||||
patch->pcOffset = sect_GetSymbolOffset();
|
||||
@@ -382,13 +441,28 @@ static void writeassert(struct Assertion *assert, FILE *f)
|
||||
fputstring(assert->message, f);
|
||||
}
|
||||
|
||||
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
||||
{
|
||||
fputlong(node->parent ? node->parent->ID : -1, f);
|
||||
fputlong(node->lineNo, f);
|
||||
fputc(node->type, f);
|
||||
if (node->type != NODE_REPT) {
|
||||
fputstring(((struct FileStackNamedNode const *)node)->name, f);
|
||||
} else {
|
||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||
|
||||
fputlong(reptNode->reptDepth, f);
|
||||
/* Iters are stored by decreasing depth, so reverse the order for output */
|
||||
for (uint32_t i = reptNode->reptDepth; i--; )
|
||||
fputlong(reptNode->iters[i], f);
|
||||
}
|
||||
}
|
||||
|
||||
static void registerExportedSymbol(struct Symbol *symbol, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
if (sym_IsExported(symbol) && symbol->ID == -1) {
|
||||
*objectSymbolsTail = symbol;
|
||||
objectSymbolsTail = &symbol->next;
|
||||
nbSymbols++;
|
||||
registerSymbol(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,6 +485,15 @@ void out_WriteObject(void)
|
||||
fputlong(nbSymbols, f);
|
||||
fputlong(countsections(), f);
|
||||
|
||||
fputlong(getNbFileStackNodes(), f);
|
||||
for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) {
|
||||
writeFileStackNode(node, f);
|
||||
if (node->next && node->next->ID != node->ID - 1)
|
||||
fatalerror("Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
|
||||
". Please report this to the developers!\n",
|
||||
node->next->ID, node->ID);
|
||||
}
|
||||
|
||||
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
|
||||
writesymbol(sym, f);
|
||||
|
||||
|
||||
@@ -258,8 +258,8 @@ static int32_t shift(int32_t shiftee, int32_t amount)
|
||||
if (amount >= 0) {
|
||||
// Left shift
|
||||
if (amount >= 32) {
|
||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n",
|
||||
amount);
|
||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %"
|
||||
PRId32 "\n", amount);
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "asm/macro.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/mymath.h"
|
||||
#include "asm/output.h"
|
||||
#include "asm/section.h"
|
||||
#include "asm/symbol.h"
|
||||
#include "asm/util.h"
|
||||
@@ -121,7 +122,7 @@ static char const *Callback__FILE__(void)
|
||||
buf[j - 1] = '\\';
|
||||
buf[j] = fileName[i];
|
||||
}
|
||||
/* Write everything after the loop, to ensure everything has been allocated */
|
||||
/* Write everything after the loop, to ensure the buffer has been allocated */
|
||||
buf[0] = '"';
|
||||
buf[j++] = '"';
|
||||
buf[j] = '\0';
|
||||
@@ -150,15 +151,35 @@ int32_t sym_GetValue(struct Symbol const *sym)
|
||||
return sym->value;
|
||||
}
|
||||
|
||||
static void dumpFilename(struct Symbol const *sym)
|
||||
{
|
||||
if (!sym->src)
|
||||
fputs("<builtin>", stderr);
|
||||
else
|
||||
fstk_Dump(sym->src, sym->fileLine);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a symbol's definition filename and line
|
||||
*/
|
||||
static void setSymbolFilename(struct Symbol *sym)
|
||||
{
|
||||
sym->src = fstk_GetFileStack();
|
||||
sym->fileLine = lexer_GetLineNo();
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a symbol's definition filename and line
|
||||
*/
|
||||
static void updateSymbolFilename(struct Symbol *sym)
|
||||
{
|
||||
if (snprintf(sym->fileName, _MAX_PATH + 1, "%s",
|
||||
fstk_GetFileName()) > _MAX_PATH)
|
||||
fatalerror("%s: File name is too long: '%s'\n", __func__, fstk_GetFileName());
|
||||
sym->fileLine = fstk_GetLine();
|
||||
struct FileStackNode *oldSrc = sym->src;
|
||||
|
||||
setSymbolFilename(sym);
|
||||
/* If the old node was referenced, ensure the new one is */
|
||||
if (oldSrc->referenced && oldSrc->ID != -1)
|
||||
out_RegisterNode(sym->src);
|
||||
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -178,7 +199,7 @@ static struct Symbol *createsymbol(char const *s)
|
||||
symbol->isBuiltin = false;
|
||||
symbol->hasCallback = false;
|
||||
symbol->section = NULL;
|
||||
updateSymbolFilename(symbol);
|
||||
setSymbolFilename(symbol);
|
||||
symbol->ID = -1;
|
||||
symbol->next = NULL;
|
||||
|
||||
@@ -253,6 +274,7 @@ void sym_Purge(char const *symName)
|
||||
labelScope = NULL;
|
||||
|
||||
hash_RemoveElement(symbols, symbol->name);
|
||||
/* TODO: ideally, also unref the file stack nodes */
|
||||
free(symbol);
|
||||
}
|
||||
}
|
||||
@@ -338,9 +360,11 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
|
||||
|
||||
if (!symbol)
|
||||
symbol = createsymbol(symbolName);
|
||||
else if (sym_IsDefined(symbol))
|
||||
error("'%s' already defined at %s(%" PRIu32 ")\n", symbolName,
|
||||
symbol->fileName, symbol->fileLine);
|
||||
else if (sym_IsDefined(symbol)) {
|
||||
error("'%s' already defined at ", symbolName);
|
||||
dumpFilename(symbol);
|
||||
putc('\n', stderr);
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
@@ -395,15 +419,17 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
||||
{
|
||||
struct Symbol *sym = findsymbol(symName, NULL);
|
||||
|
||||
if (sym == NULL)
|
||||
if (sym == NULL) {
|
||||
sym = createsymbol(symName);
|
||||
else if (sym_IsDefined(sym) && sym->type != SYM_SET)
|
||||
error("'%s' already defined as %s at %s(%" PRIu32 ")\n",
|
||||
symName, sym->type == SYM_LABEL ? "label" : "constant",
|
||||
sym->fileName, sym->fileLine);
|
||||
else
|
||||
/* TODO: can the scope be incorrect when talking over refs? */
|
||||
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
|
||||
error("'%s' already defined as %s at ",
|
||||
symName, sym->type == SYM_LABEL ? "label" : "constant");
|
||||
dumpFilename(sym);
|
||||
putc('\n', stderr);
|
||||
} else {
|
||||
/* TODO: can the scope be incorrect when taking over refs? */
|
||||
updateSymbolFilename(sym);
|
||||
}
|
||||
|
||||
sym->type = SYM_SET;
|
||||
sym->value = value;
|
||||
@@ -424,9 +450,12 @@ static struct Symbol *addLabel(char const *name)
|
||||
if (!sym) {
|
||||
sym = createsymbol(name);
|
||||
} else if (sym_IsDefined(sym)) {
|
||||
error("'%s' already defined in %s(%" PRIu32 ")\n",
|
||||
name, sym->fileName, sym->fileLine);
|
||||
error("'%s' already defined at ", name);
|
||||
dumpFilename(sym);
|
||||
putc('\n', stderr);
|
||||
return NULL;
|
||||
} else {
|
||||
updateSymbolFilename(sym);
|
||||
}
|
||||
/* If the symbol already exists as a ref, just "take over" it */
|
||||
sym->type = SYM_LABEL;
|
||||
@@ -434,7 +463,6 @@ static struct Symbol *addLabel(char const *name)
|
||||
if (exportall)
|
||||
sym->isExported = true;
|
||||
sym->section = sect_GetSymbolSection();
|
||||
updateSymbolFilename(sym);
|
||||
|
||||
if (sym && !sym->section)
|
||||
error("Label \"%s\" created outside of a SECTION\n", name);
|
||||
@@ -517,7 +545,7 @@ struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body,
|
||||
sym->type = SYM_MACRO;
|
||||
sym->macroSize = size;
|
||||
sym->macro = body;
|
||||
updateSymbolFilename(sym);
|
||||
setSymbolFilename(sym); /* TODO: is this really necessary? */
|
||||
/*
|
||||
* The symbol is created at the line after the `endm`,
|
||||
* override this with the actual definition line
|
||||
@@ -577,10 +605,11 @@ static inline struct Symbol *createBuiltinSymbol(char const *name)
|
||||
|
||||
sym->isBuiltin = true;
|
||||
sym->hasCallback = true;
|
||||
strcpy(sym->fileName, "<builtin>");
|
||||
sym->src = NULL;
|
||||
sym->fileLine = 0;
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the symboltable
|
||||
*/
|
||||
|
||||
@@ -202,7 +202,7 @@ void printDiag(const char *fmt, va_list args, char const *type,
|
||||
char const *flagfmt, char const *flag)
|
||||
{
|
||||
fputs(type, stderr);
|
||||
fstk_Dump();
|
||||
fstk_DumpCurrent();
|
||||
fprintf(stderr, flagfmt, flag);
|
||||
vfprintf(stderr, fmt, args);
|
||||
lexer_DumpStringExpansions();
|
||||
|
||||
@@ -81,14 +81,14 @@ static void processLinkerScript(void)
|
||||
|
||||
/* Check if this doesn't conflict with what the code says */
|
||||
if (section->isBankFixed && placement->bank != section->bank)
|
||||
error("Linker script contradicts \"%s\"'s bank placement",
|
||||
error(NULL, 0, "Linker script contradicts \"%s\"'s bank placement",
|
||||
section->name);
|
||||
if (section->isAddressFixed && placement->org != section->org)
|
||||
error("Linker script contradicts \"%s\"'s address placement",
|
||||
error(NULL, 0, "Linker script contradicts \"%s\"'s address placement",
|
||||
section->name);
|
||||
if (section->isAlignFixed
|
||||
&& (placement->org & section->alignMask) != 0)
|
||||
error("Linker script contradicts \"%s\"'s alignment",
|
||||
error(NULL, 0, "Linker script contradicts \"%s\"'s alignment",
|
||||
section->name);
|
||||
|
||||
section->isAddressFixed = true;
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -39,11 +41,55 @@ bool disablePadding; /* -x */
|
||||
|
||||
static uint32_t nbErrors = 0;
|
||||
|
||||
void error(char const *fmt, ...)
|
||||
/***** Helper function to dump a file stack to stderr *****/
|
||||
|
||||
char const *dumpFileStack(struct FileStackNode const *node)
|
||||
{
|
||||
char const *lastName;
|
||||
|
||||
if (node->parent) {
|
||||
lastName = dumpFileStack(node->parent);
|
||||
/* REPT nodes use their parent's name */
|
||||
if (node->type != NODE_REPT)
|
||||
lastName = node->name;
|
||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, lastName);
|
||||
if (node->type == NODE_REPT) {
|
||||
for (uint32_t i = 0; i < node->reptDepth; i++)
|
||||
fprintf(stderr, "::REPT~%" PRIu32, node->iters[i]);
|
||||
}
|
||||
} else {
|
||||
assert(node->type != NODE_REPT);
|
||||
lastName = node->name;
|
||||
fputs(lastName, stderr);
|
||||
}
|
||||
|
||||
return lastName;
|
||||
}
|
||||
|
||||
void warning(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "error: ");
|
||||
fputs("warning: ", stderr);
|
||||
if (where) {
|
||||
dumpFileStack(where);
|
||||
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
putc('\n', stderr);
|
||||
}
|
||||
|
||||
void error(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fputs("error: ", stderr);
|
||||
if (where) {
|
||||
dumpFileStack(where);
|
||||
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
@@ -53,11 +99,15 @@ void error(char const *fmt, ...)
|
||||
nbErrors++;
|
||||
}
|
||||
|
||||
noreturn_ void fatal(char const *fmt, ...)
|
||||
noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "fatal: ");
|
||||
fputs("fatal: ", stderr);
|
||||
if (where) {
|
||||
dumpFileStack(where);
|
||||
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
@@ -177,11 +227,11 @@ int main(int argc, char *argv[])
|
||||
case 'p':
|
||||
value = strtoul(optarg, &endptr, 0);
|
||||
if (optarg[0] == '\0' || *endptr != '\0') {
|
||||
error("Invalid argument for option 'p'");
|
||||
error(NULL, 0, "Invalid argument for option 'p'");
|
||||
value = 0xFF;
|
||||
}
|
||||
if (value > 0xFF) {
|
||||
error("Argument for 'p' must be a byte (between 0 and 0xFF)");
|
||||
error(NULL, 0, "Argument for 'p' must be a byte (between 0 and 0xFF)");
|
||||
value = 0xFF;
|
||||
}
|
||||
padValue = value;
|
||||
@@ -189,7 +239,7 @@ int main(int argc, char *argv[])
|
||||
case 's':
|
||||
/* FIXME: nobody knows what this does, figure it out */
|
||||
(void)optarg;
|
||||
warnx("Nobody has any idea what `-s` does");
|
||||
warning(NULL, 0, "Nobody has any idea what `-s` does");
|
||||
break;
|
||||
case 't':
|
||||
is32kMode = true;
|
||||
@@ -234,8 +284,8 @@ int main(int argc, char *argv[])
|
||||
bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM;
|
||||
|
||||
/* Read all object files first, */
|
||||
while (curArgIndex < argc)
|
||||
obj_ReadFile(argv[curArgIndex++]);
|
||||
for (obj_Setup(argc - curArgIndex); curArgIndex < argc; curArgIndex++)
|
||||
obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1);
|
||||
|
||||
/* then process them, */
|
||||
obj_DoSanityChecks();
|
||||
|
||||
@@ -31,6 +31,11 @@ static struct SymbolList {
|
||||
struct SymbolList *next;
|
||||
} *symbolLists;
|
||||
|
||||
unsigned int nbObjFiles;
|
||||
static struct {
|
||||
struct FileStackNode *nodes;
|
||||
uint32_t nbNodes;
|
||||
} *nodes;
|
||||
static struct Assertion *assertions;
|
||||
|
||||
/***** Helper functions for reading object files *****/
|
||||
@@ -170,12 +175,56 @@ static char *readstr(FILE *file)
|
||||
/***** Functions to parse object files *****/
|
||||
|
||||
/**
|
||||
* Reads a RGB6 symbol from a file.
|
||||
* Reads a file stack node form a file.
|
||||
* @param file The file to read from
|
||||
* @param nodes The file's array of nodes
|
||||
* @param i The ID of the node in the array
|
||||
* @param fileName The filename to report in errors
|
||||
*/
|
||||
static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint32_t i,
|
||||
char const *fileName)
|
||||
{
|
||||
uint32_t parentID;
|
||||
|
||||
tryReadlong(parentID, file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
|
||||
fileNodes[i].parent = parentID == -1 ? NULL : &fileNodes[parentID];
|
||||
tryReadlong(fileNodes[i].lineNo, file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
|
||||
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
|
||||
fileName, i);
|
||||
switch (fileNodes[i].type) {
|
||||
case NODE_FILE:
|
||||
case NODE_MACRO:
|
||||
tryReadstr(fileNodes[i].name, file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, i);
|
||||
break;
|
||||
|
||||
case NODE_REPT:
|
||||
tryReadlong(fileNodes[i].reptDepth, file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
|
||||
fileNodes[i].iters = malloc(sizeof(*fileNodes[i].iters) * fileNodes[i].reptDepth);
|
||||
if (!fileNodes[i].iters)
|
||||
fatal(NULL, 0, "%s: Failed to alloc node #%" PRIu32 "'s iters: %s",
|
||||
fileName, i, strerror(errno));
|
||||
for (uint32_t k = 0; k < fileNodes[i].reptDepth; k++)
|
||||
tryReadlong(fileNodes[i].iters[k], file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
|
||||
fileName, i, k);
|
||||
if (!fileNodes[i].parent)
|
||||
fatal(NULL, 0, "%s is not a valid object file: root node (#%"
|
||||
PRIu32 ") may not be REPT", fileName, i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 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)
|
||||
static void readSymbol(FILE *file, struct Symbol *symbol,
|
||||
char const *fileName, struct FileStackNode fileNodes[])
|
||||
{
|
||||
tryReadstr(symbol->name, file, "%s: Cannot read symbol name: %s",
|
||||
fileName);
|
||||
@@ -184,9 +233,12 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
|
||||
/* If the symbol is defined in this file, read its definition */
|
||||
if (symbol->type != SYMTYPE_IMPORT) {
|
||||
symbol->objFileName = fileName;
|
||||
tryReadstr(symbol->fileName, file,
|
||||
"%s: Cannot read \"%s\"'s file name: %s",
|
||||
uint32_t nodeID;
|
||||
|
||||
tryReadlong(nodeID, file,
|
||||
"%s: Cannot read \"%s\"'s node ID: %s",
|
||||
fileName, symbol->name);
|
||||
symbol->src = &fileNodes[nodeID];
|
||||
tryReadlong(symbol->lineNo, file,
|
||||
"%s: Cannot read \"%s\"'s line number: %s",
|
||||
fileName, symbol->name);
|
||||
@@ -202,7 +254,7 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a RGB6 patch from a file.
|
||||
* Reads a 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
|
||||
@@ -210,10 +262,16 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
|
||||
*/
|
||||
static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
||||
char const *sectName, uint32_t i,
|
||||
struct Section *fileSections[])
|
||||
struct Section *fileSections[], struct FileStackNode fileNodes[])
|
||||
{
|
||||
tryReadstr(patch->fileName, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s name: %s",
|
||||
uint32_t nodeID;
|
||||
|
||||
tryReadlong(nodeID, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
|
||||
fileName, sectName, i);
|
||||
patch->src = &fileNodes[nodeID];
|
||||
tryReadlong(patch->lineNo, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
|
||||
fileName, sectName, i);
|
||||
tryReadlong(patch->offset, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
|
||||
@@ -221,8 +279,7 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
||||
tryReadlong(patch->pcSectionID, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
||||
fileName, sectName, i);
|
||||
patch->pcSection = patch->pcSectionID == -1
|
||||
? NULL
|
||||
patch->pcSection = patch->pcSectionID == -1 ? NULL
|
||||
: fileSections[patch->pcSectionID];
|
||||
tryReadlong(patch->pcOffset, file,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
||||
@@ -234,16 +291,17 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
|
||||
fileName, sectName, i);
|
||||
|
||||
uint8_t *rpnExpression =
|
||||
malloc(sizeof(*rpnExpression) * patch->rpnSize);
|
||||
size_t nbElementsRead = fread(rpnExpression, sizeof(*rpnExpression),
|
||||
patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
|
||||
if (!patch->rpnExpression)
|
||||
err(1, "%s: Failed to alloc \"%s\"'s patch #%" PRIu32 "'s RPN expression",
|
||||
fileName, sectName, i);
|
||||
size_t nbElementsRead = fread(patch->rpnExpression, sizeof(*patch->rpnExpression),
|
||||
patch->rpnSize, file);
|
||||
|
||||
if (nbElementsRead != patch->rpnSize)
|
||||
errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
||||
fileName, sectName, i,
|
||||
feof(file) ? "Unexpected end of file" : strerror(errno));
|
||||
patch->rpnExpression = rpnExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,8 +310,8 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
||||
* @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, struct Section *fileSections[])
|
||||
static void readSection(FILE *file, struct Section *section, char const *fileName,
|
||||
struct Section *fileSections[], struct FileStackNode fileNodes[])
|
||||
{
|
||||
int32_t tmp;
|
||||
uint8_t byte;
|
||||
@@ -280,7 +338,7 @@ static void readSection(FILE *file, struct Section *section,
|
||||
fileName, section->name);
|
||||
section->isAddressFixed = tmp >= 0;
|
||||
if (tmp > UINT16_MAX) {
|
||||
error("\"%s\"'s org is too large (%" PRId32 ")",
|
||||
error(NULL, 0, "\"%s\"'s org is too large (%" PRId32 ")",
|
||||
section->name, tmp);
|
||||
tmp = UINT16_MAX;
|
||||
}
|
||||
@@ -296,7 +354,7 @@ static void readSection(FILE *file, struct Section *section,
|
||||
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
|
||||
fileName, section->name);
|
||||
if (tmp > UINT16_MAX) {
|
||||
error("\"%s\"'s alignment offset is too large (%" PRId32 ")",
|
||||
error(NULL, 0, "\"%s\"'s alignment offset is too large (%" PRId32 ")",
|
||||
section->name, tmp);
|
||||
tmp = UINT16_MAX;
|
||||
}
|
||||
@@ -332,7 +390,7 @@ static void readSection(FILE *file, struct Section *section,
|
||||
section->name);
|
||||
for (uint32_t i = 0; i < section->nbPatches; i++) {
|
||||
readPatch(file, &patches[i], fileName, section->name,
|
||||
i, fileSections);
|
||||
i, fileSections, fileNodes);
|
||||
}
|
||||
section->patches = patches;
|
||||
}
|
||||
@@ -375,13 +433,13 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
|
||||
*/
|
||||
static void readAssertion(FILE *file, struct Assertion *assert,
|
||||
char const *fileName, uint32_t i,
|
||||
struct Section *fileSections[])
|
||||
struct Section *fileSections[], struct FileStackNode fileNodes[])
|
||||
{
|
||||
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
|
||||
|
||||
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
|
||||
|
||||
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections);
|
||||
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections, fileNodes);
|
||||
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
|
||||
fileName);
|
||||
}
|
||||
@@ -394,11 +452,7 @@ static inline struct Section *getMainSection(struct Section *section)
|
||||
return section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an object file of any supported format
|
||||
* @param fileName The filename to report for errors
|
||||
*/
|
||||
void obj_ReadFile(char const *fileName)
|
||||
void obj_ReadFile(char const *fileName, unsigned int fileID)
|
||||
{
|
||||
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
|
||||
|
||||
@@ -438,6 +492,14 @@ void obj_ReadFile(char const *fileName)
|
||||
|
||||
nbSectionsToAssign += nbSections;
|
||||
|
||||
tryReadlong(nodes[fileID].nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
|
||||
nodes[fileID].nodes = calloc(nodes[fileID].nbNodes, sizeof(nodes[fileID].nodes[0]));
|
||||
if (!nodes[fileID].nodes)
|
||||
err(1, "Failed to get memory for %s's nodes", fileName);
|
||||
verbosePrint("Reading %u nodes...\n", nodes[fileID].nbNodes);
|
||||
for (uint32_t i = 0; i < nodes[fileID].nbNodes; i++)
|
||||
readFileStackNode(file, nodes[fileID].nodes, i, fileName);
|
||||
|
||||
/* This file's symbols, kept to link sections to them */
|
||||
struct Symbol **fileSymbols =
|
||||
malloc(sizeof(*fileSymbols) * nbSymbols + 1);
|
||||
@@ -464,7 +526,7 @@ void obj_ReadFile(char const *fileName)
|
||||
|
||||
if (!symbol)
|
||||
err(1, "%s: Couldn't create new symbol", fileName);
|
||||
readSymbol(file, symbol, fileName);
|
||||
readSymbol(file, symbol, fileName, nodes[fileID].nodes);
|
||||
|
||||
fileSymbols[i] = symbol;
|
||||
if (symbol->type == SYMTYPE_EXPORT)
|
||||
@@ -485,7 +547,7 @@ void obj_ReadFile(char const *fileName)
|
||||
err(1, "%s: Couldn't create new section", fileName);
|
||||
|
||||
fileSections[i]->nextu = NULL;
|
||||
readSection(file, fileSections[i], fileName, fileSections);
|
||||
readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes);
|
||||
fileSections[i]->fileSymbols = fileSymbols;
|
||||
if (nbSymPerSect[i]) {
|
||||
fileSections[i]->symbols = malloc(nbSymPerSect[i]
|
||||
@@ -535,7 +597,7 @@ void obj_ReadFile(char const *fileName)
|
||||
|
||||
if (!assertion)
|
||||
err(1, "%s: Couldn't create new assertion", fileName);
|
||||
readAssertion(file, assertion, fileName, i, fileSections);
|
||||
readAssertion(file, assertion, fileName, i, fileSections, nodes[fileID].nodes);
|
||||
assertion->fileSymbols = fileSymbols;
|
||||
assertion->next = assertions;
|
||||
assertions = assertion;
|
||||
@@ -555,6 +617,15 @@ void obj_CheckAssertions(void)
|
||||
patch_CheckAssertions(assertions);
|
||||
}
|
||||
|
||||
void obj_Setup(unsigned int nbFiles)
|
||||
{
|
||||
nbObjFiles = nbFiles;
|
||||
|
||||
if (nbFiles > SIZE_MAX / sizeof(*nodes))
|
||||
fatal(NULL, 0, "Impossible to link more than %zu files!", SIZE_MAX / sizeof(*nodes));
|
||||
nodes = malloc(sizeof(*nodes) * nbFiles);
|
||||
}
|
||||
|
||||
static void freeSection(struct Section *section, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
@@ -562,12 +633,8 @@ static void freeSection(struct Section *section, 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 = §ion->patches[i];
|
||||
|
||||
free(patch->fileName);
|
||||
free(patch->rpnExpression);
|
||||
}
|
||||
for (int32_t i = 0; i < section->nbPatches; i++)
|
||||
free(section->patches[i].rpnExpression);
|
||||
free(section->patches);
|
||||
}
|
||||
free(section->symbols);
|
||||
@@ -577,13 +644,20 @@ static void freeSection(struct Section *section, void *arg)
|
||||
static void freeSymbol(struct Symbol *symbol)
|
||||
{
|
||||
free(symbol->name);
|
||||
if (symbol->type != SYMTYPE_IMPORT)
|
||||
free(symbol->fileName);
|
||||
free(symbol);
|
||||
}
|
||||
|
||||
void obj_Cleanup(void)
|
||||
{
|
||||
for (unsigned int i = 0; i < nbObjFiles; i++) {
|
||||
for (uint32_t j = 0; j < nodes[i].nbNodes; j++) {
|
||||
if (nodes[i].nodes[j].type == NODE_REPT)
|
||||
free(nodes[i].nodes[j].iters);
|
||||
}
|
||||
free(nodes[i].nodes);
|
||||
}
|
||||
free(nodes);
|
||||
|
||||
sym_CleanupSymbols();
|
||||
|
||||
sect_ForEach(freeSection, NULL);
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "link/object.h"
|
||||
#include "link/patch.h"
|
||||
#include "link/section.h"
|
||||
#include "link/symbol.h"
|
||||
@@ -104,10 +106,10 @@ static void pushRPN(int32_t value)
|
||||
stack.size++;
|
||||
}
|
||||
|
||||
static int32_t popRPN(char const *fileName)
|
||||
static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
|
||||
{
|
||||
if (stack.size == 0)
|
||||
errx(1, "%s: Internal error, RPN stack empty", fileName);
|
||||
fatal(node, lineNo, "Internal error, RPN stack empty");
|
||||
|
||||
stack.size--;
|
||||
return stack.buf[stack.size];
|
||||
@@ -121,16 +123,18 @@ static inline void freeRPNStack(void)
|
||||
/* RPN operators */
|
||||
|
||||
static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
|
||||
char const *fileName)
|
||||
struct FileStackNode const *node, uint32_t lineNo)
|
||||
{
|
||||
if (!(*size)--)
|
||||
errx(1, "%s: RPN expression overread", fileName);
|
||||
fatal(node, lineNo, "Internal error, RPN expression overread");
|
||||
|
||||
return *(*expression)++;
|
||||
}
|
||||
|
||||
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
|
||||
uint32_t index)
|
||||
{
|
||||
assert(index != -1); /* PC needs to be handled specially, not here */
|
||||
struct Symbol const *symbol = symbolList[index];
|
||||
|
||||
/* If the symbol is defined elsewhere... */
|
||||
@@ -150,7 +154,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
struct Symbol const * const *fileSymbols)
|
||||
{
|
||||
/* Small shortcut to avoid a lot of repetition */
|
||||
#define popRPN() popRPN(patch->fileName)
|
||||
#define popRPN() popRPN(patch->src, patch->lineNo)
|
||||
|
||||
uint8_t const *expression = patch->rpnExpression;
|
||||
int32_t size = patch->rpnSize;
|
||||
@@ -159,7 +163,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
|
||||
while (size > 0) {
|
||||
enum RPNCommand command = getRPNByte(&expression, &size,
|
||||
patch->fileName);
|
||||
patch->src, patch->lineNo);
|
||||
int32_t value;
|
||||
|
||||
/*
|
||||
@@ -187,7 +191,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
case RPN_DIV:
|
||||
value = popRPN();
|
||||
if (value == 0) {
|
||||
error("%s: Division by 0", patch->fileName);
|
||||
error(patch->src, patch->lineNo, "Division by 0");
|
||||
popRPN();
|
||||
value = INT32_MAX;
|
||||
} else {
|
||||
@@ -197,7 +201,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
case RPN_MOD:
|
||||
value = popRPN();
|
||||
if (value == 0) {
|
||||
error("%s: Modulo by 0", patch->fileName);
|
||||
error(patch->src, patch->lineNo, "Modulo by 0");
|
||||
popRPN();
|
||||
value = 0;
|
||||
} else {
|
||||
@@ -269,17 +273,17 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
value = 0;
|
||||
for (uint8_t shift = 0; shift < 32; shift += 8)
|
||||
value |= getRPNByte(&expression, &size,
|
||||
patch->fileName) << shift;
|
||||
patch->src, patch->lineNo) << shift;
|
||||
symbol = getSymbol(fileSymbols, value);
|
||||
|
||||
if (!symbol) {
|
||||
error("%s: Requested BANK() of symbol \"%s\", which was not found",
|
||||
patch->fileName,
|
||||
error(patch->src, patch->lineNo,
|
||||
"Requested BANK() of symbol \"%s\", which was not found",
|
||||
fileSymbols[value]->name);
|
||||
value = 1;
|
||||
} else if (!symbol->section) {
|
||||
error("%s: Requested BANK() of non-label symbol \"%s\"",
|
||||
patch->fileName,
|
||||
error(patch->src, patch->lineNo,
|
||||
"Requested BANK() of non-label symbol \"%s\"",
|
||||
fileSymbols[value]->name);
|
||||
value = 1;
|
||||
} else {
|
||||
@@ -289,14 +293,15 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
|
||||
case RPN_BANK_SECT:
|
||||
name = (char const *)expression;
|
||||
while (getRPNByte(&expression, &size, patch->fileName))
|
||||
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
||||
;
|
||||
|
||||
sect = sect_GetSection(name);
|
||||
|
||||
if (!sect) {
|
||||
error("%s: Requested BANK() of section \"%s\", which was not found",
|
||||
patch->fileName, name);
|
||||
error(patch->src, patch->lineNo,
|
||||
"Requested BANK() of section \"%s\", which was not found",
|
||||
name);
|
||||
value = 1;
|
||||
} else {
|
||||
value = sect->bank;
|
||||
@@ -305,7 +310,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
|
||||
case RPN_BANK_SELF:
|
||||
if (!patch->pcSection) {
|
||||
error("%s: PC has no bank outside a section");
|
||||
error(patch->src, patch->lineNo,
|
||||
"PC has no bank outside a section");
|
||||
value = 1;
|
||||
} else {
|
||||
value = patch->pcSection->bank;
|
||||
@@ -317,8 +323,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
if (value < 0
|
||||
|| (value > 0xFF && value < 0xFF00)
|
||||
|| value > 0xFFFF)
|
||||
error("%s: Value %" PRId32 " is not in HRAM range",
|
||||
patch->fileName, value);
|
||||
error(patch->src, patch->lineNo,
|
||||
"Value %" PRId32 " is not in HRAM range", value);
|
||||
value &= 0xFF;
|
||||
break;
|
||||
|
||||
@@ -328,8 +334,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
* They can be easily checked with a bitmask
|
||||
*/
|
||||
if (value & ~0x38)
|
||||
error("%s: Value %" PRId32 " is not a RST vector",
|
||||
patch->fileName, value);
|
||||
error(patch->src, patch->lineNo,
|
||||
"Value %" PRId32 " is not a RST vector", value);
|
||||
value |= 0xC7;
|
||||
break;
|
||||
|
||||
@@ -337,32 +343,35 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
value = 0;
|
||||
for (uint8_t shift = 0; shift < 32; shift += 8)
|
||||
value |= getRPNByte(&expression, &size,
|
||||
patch->fileName) << shift;
|
||||
patch->src, patch->lineNo) << shift;
|
||||
break;
|
||||
|
||||
case RPN_SYM:
|
||||
value = 0;
|
||||
for (uint8_t shift = 0; shift < 32; shift += 8)
|
||||
value |= getRPNByte(&expression, &size,
|
||||
patch->fileName) << shift;
|
||||
patch->src, patch->lineNo) << shift;
|
||||
|
||||
if (value == -1) { /* PC */
|
||||
if (!patch->pcSection) {
|
||||
error(patch->src, patch->lineNo,
|
||||
"PC has no value outside a section");
|
||||
value = 0;
|
||||
} else {
|
||||
value = patch->pcOffset + patch->pcSection->org;
|
||||
}
|
||||
} else {
|
||||
symbol = getSymbol(fileSymbols, value);
|
||||
|
||||
if (!symbol) {
|
||||
error("%s: Unknown symbol \"%s\"",
|
||||
patch->fileName,
|
||||
fileSymbols[value]->name);
|
||||
} else if (strcmp(symbol->name, "@")) {
|
||||
error(patch->src, patch->lineNo,
|
||||
"Unknown symbol \"%s\"", fileSymbols[value]->name);
|
||||
} else {
|
||||
value = symbol->value;
|
||||
/* Symbols attached to sections have offsets */
|
||||
if (symbol->section)
|
||||
value += symbol->section->org;
|
||||
} else if (!patch->pcSection) {
|
||||
error("%s: PC has no value outside a section",
|
||||
patch->fileName);
|
||||
value = 0;
|
||||
} else {
|
||||
value = patch->pcOffset + patch->pcSection->org;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -371,8 +380,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
||||
}
|
||||
|
||||
if (stack.size > 1)
|
||||
error("%s: RPN stack has %zu entries on exit, not 1",
|
||||
patch->fileName, stack.size);
|
||||
error(patch->src, patch->lineNo,
|
||||
"RPN stack has %zu entries on exit, not 1", stack.size);
|
||||
|
||||
return popRPN();
|
||||
|
||||
@@ -390,18 +399,18 @@ void patch_CheckAssertions(struct Assertion *assert)
|
||||
assert->fileSymbols)) {
|
||||
switch ((enum AssertionType)assert->patch.type) {
|
||||
case ASSERT_FATAL:
|
||||
fatal("%s: %s", assert->patch.fileName,
|
||||
fatal(assert->patch.src, assert->patch.lineNo, "%s",
|
||||
assert->message[0] ? assert->message
|
||||
: "assert failure");
|
||||
/* Not reached */
|
||||
break; /* Here so checkpatch doesn't complain */
|
||||
case ASSERT_ERROR:
|
||||
error("%s: %s", assert->patch.fileName,
|
||||
error(assert->patch.src, assert->patch.lineNo, "%s",
|
||||
assert->message[0] ? assert->message
|
||||
: "assert failure");
|
||||
break;
|
||||
case ASSERT_WARN:
|
||||
warnx("%s: %s", assert->patch.fileName,
|
||||
warning(assert->patch.src, assert->patch.lineNo, "%s",
|
||||
assert->message[0] ? assert->message
|
||||
: "assert failure");
|
||||
break;
|
||||
@@ -442,8 +451,9 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
|
||||
int16_t jumpOffset = value - address;
|
||||
|
||||
if (jumpOffset < -128 || jumpOffset > 127)
|
||||
error("%s: jr target out of reach (expected -129 < %" PRId16 " < 128)",
|
||||
patch->fileName, jumpOffset);
|
||||
error(patch->src, patch->lineNo,
|
||||
"jr target out of reach (expected -129 < %" PRId16 " < 128)",
|
||||
jumpOffset);
|
||||
dataSection->data[offset] = jumpOffset & 0xFF;
|
||||
} else {
|
||||
/* Patch a certain number of bytes */
|
||||
@@ -459,9 +469,9 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
|
||||
|
||||
if (value < types[patch->type].min
|
||||
|| value > types[patch->type].max)
|
||||
error("%s: Value %#" PRIx32 "%s is not %u-bit",
|
||||
patch->fileName, value,
|
||||
value < 0 ? " (maybe negative?)" : "",
|
||||
error(patch->src, patch->lineNo,
|
||||
"Value %#" PRIx32 "%s is not %u-bit",
|
||||
value, value < 0 ? " (maybe negative?)" : "",
|
||||
types[patch->type].size * 8U);
|
||||
for (uint8_t i = 0; i < types[patch->type].size; i++) {
|
||||
dataSection->data[offset + i] = value & 0xFF;
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "link/object.h"
|
||||
#include "link/symbol.h"
|
||||
#include "link/main.h"
|
||||
|
||||
#include "extern/err.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
@@ -40,11 +43,15 @@ void sym_AddSymbol(struct Symbol *symbol)
|
||||
/* Check if the symbol already exists */
|
||||
struct Symbol *other = hash_GetElement(symbols, symbol->name);
|
||||
|
||||
if (other)
|
||||
errx(1, "\"%s\" both in %s from %s(%" PRId32 ") and in %s from %s(%" PRId32 ")",
|
||||
symbol->name,
|
||||
symbol->objFileName, symbol->fileName, symbol->lineNo,
|
||||
other->objFileName, other->fileName, other->lineNo);
|
||||
if (other) {
|
||||
fprintf(stderr, "error: \"%s\" both in %s from ", symbol->name, symbol->objFileName);
|
||||
dumpFileStack(symbol->src);
|
||||
fprintf(stderr, "(%" PRIu32 ") and in %s from ",
|
||||
symbol->lineNo, other->objFileName);
|
||||
dumpFileStack(other->src);
|
||||
fprintf(stderr, "(%" PRIu32 ")\n", other->lineNo);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* If not, add it */
|
||||
bool collided = hash_AddElement(symbols, symbol->name, symbol);
|
||||
|
||||
55
src/rgbds.5
55
src/rgbds.5
@@ -16,7 +16,7 @@ This is the description of the object files used by
|
||||
.Xr rgbasm 1
|
||||
and
|
||||
.Xr rgblink 1 .
|
||||
.Em Please note that the specifications may change.
|
||||
.Em Please note that the specifications may change .
|
||||
This toolchain is in development and new features may require adding more information to the current format, or modifying some fields, which would break compatibility with older versions.
|
||||
.Pp
|
||||
.Sh FILE STRUCTURE
|
||||
@@ -34,9 +34,42 @@ is a 0‐terminated string of
|
||||
; Header
|
||||
|
||||
BYTE ID[4] ; "RGB9"
|
||||
LONG RevisionNumber ; The format's revision number this file uses
|
||||
LONG NumberOfSymbols ; The number of symbols used in this file
|
||||
LONG NumberOfSections ; The number of sections used in this file
|
||||
LONG RevisionNumber ; The format's revision number this file uses.
|
||||
LONG NumberOfSymbols ; The number of symbols used in this file.
|
||||
LONG NumberOfSections ; The number of sections used in this file.
|
||||
|
||||
; File info
|
||||
|
||||
LONG NumberOfNodes ; The number of nodes contained in this file.
|
||||
|
||||
REPT NumberOfNodes ; IMPORTANT NOTE: the nodes are actually written in
|
||||
; **reverse** order, meaningthe node with ID 0 is
|
||||
; the last one in the file!
|
||||
|
||||
LONG ParentID ; ID of the parent node, -1 means this is the root.
|
||||
|
||||
LONG ParentLineNo ; Line at which the parent context was exited.
|
||||
; Meaningless on the root node.
|
||||
|
||||
BYTE Type ; 0 = REPT node
|
||||
; 1 = File node
|
||||
; 2 = Macro node
|
||||
|
||||
IF Type != 0 ; If the node is not a REPT...
|
||||
|
||||
STRING Name ; The node's name: either a file name, or macro name
|
||||
; prefixed by its definition file name.
|
||||
|
||||
ELSE ; If the node is a REPT, it also contains the iter
|
||||
; counts of all the parent REPTs.
|
||||
|
||||
LONG Depth ; Size of the array below.
|
||||
|
||||
LONG Iter[Depth] ; The number of REPT iterations by increasing depth.
|
||||
|
||||
ENDC
|
||||
|
||||
ENDR
|
||||
|
||||
; Symbols
|
||||
|
||||
@@ -51,7 +84,7 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file.
|
||||
|
||||
IF (Type & 0x7F) != 1 ; If symbol is defined in this object file.
|
||||
|
||||
STRING FileName ; File where the symbol is defined.
|
||||
LONG SourceFile ; File where the symbol is defined.
|
||||
|
||||
LONG LineNum ; Line number in the file where the symbol is defined.
|
||||
|
||||
@@ -107,8 +140,10 @@ REPT NumberOfSections
|
||||
|
||||
REPT NumberOfPatches
|
||||
|
||||
STRING SourceFile ; Name of the source file (for printing error
|
||||
; messages).
|
||||
LONG SourceFile ; ID of the source file node (for printing
|
||||
; error messages).
|
||||
|
||||
LONG LineNo ; Line at which the patch was created.
|
||||
|
||||
LONG Offset ; Offset into the section where patch should
|
||||
; be applied (in bytes).
|
||||
@@ -145,7 +180,9 @@ LONG NumberOfAssertions
|
||||
|
||||
REPT NumberOfAssertions
|
||||
|
||||
STRING SourceFile ; Name of the source file (for printing the failure).
|
||||
LONG SourceFile ; ID of the source file node (for printing the failure).
|
||||
|
||||
LONG LineNo ; Line at which the assertion was created.
|
||||
|
||||
LONG Offset ; Offset into the section where the assertion is located.
|
||||
|
||||
@@ -209,7 +246,7 @@ with some bytes being special prefixes for integers and symbols.
|
||||
.It Li $50 Ta Li BANK(symbol) ,
|
||||
a
|
||||
.Ar LONG
|
||||
Symbol ID follows.
|
||||
Symbol ID follows, where -1 means PC
|
||||
.It Li $51 Ta Li BANK(section_name) ,
|
||||
a null-terminated string follows.
|
||||
.It Li $52 Ta Li Current BANK()
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
ERROR: label-redefinition.asm(7):
|
||||
'Sym' already defined in label-redefinition.asm::m(4)
|
||||
'Sym' already defined at label-redefinition.asm(6) -> label-redefinition.asm::m(4)
|
||||
error: Assembly aborted (1 errors)!
|
||||
|
||||
Reference in New Issue
Block a user