Merge pull request #557 from ISSOtm/new-lexer-electric-boogaloo

New lexer 2 — Electric Boogaloo
This commit is contained in:
Eldred Habert
2020-10-04 16:45:47 +02:00
committed by GitHub
111 changed files with 3404 additions and 2966 deletions

View File

@@ -46,7 +46,7 @@ if(MSVC)
add_definitions(/D_CRT_SECURE_NO_WARNINGS) add_definitions(/D_CRT_SECURE_NO_WARNINGS)
else() else()
if(DEVELOP) 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 -Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused -Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 -Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5

View File

@@ -56,7 +56,6 @@ rgbasm_obj := \
src/asm/asmy.o \ src/asm/asmy.o \
src/asm/charmap.o \ src/asm/charmap.o \
src/asm/fstack.o \ src/asm/fstack.o \
src/asm/globlex.o \
src/asm/lexer.o \ src/asm/lexer.o \
src/asm/macro.o \ src/asm/macro.o \
src/asm/main.o \ src/asm/main.o \
@@ -73,7 +72,7 @@ rgbasm_obj := \
src/hashmap.o \ src/hashmap.o \
src/linkdefs.o src/linkdefs.o
src/asm/globlex.o src/asm/lexer.o src/asm/constexpr.o: src/asm/asmy.h src/asm/lexer.o src/asm/main.o: src/asm/asmy.h
rgblink_obj := \ rgblink_obj := \
src/link/assign.o \ src/link/assign.o \
@@ -187,7 +186,7 @@ checkpatch:
# compilation and make the continous integration infrastructure return failure. # compilation and make the continous integration infrastructure return failure.
develop: 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 \ -Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2 \
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \ -Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \ -Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \
@@ -199,7 +198,7 @@ develop:
-fsanitize=unreachable -fsanitize=vla-bound \ -fsanitize=unreachable -fsanitize=vla-bound \
-fsanitize=signed-integer-overflow -fsanitize=bounds \ -fsanitize=signed-integer-overflow -fsanitize=bounds \
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \ -fsanitize=object-size -fsanitize=bool -fsanitize=enum \
-fsanitize=alignment -fsanitize=null -DDEVELOP" CFLAGS="-g -O0" -fsanitize=alignment -fsanitize=null -DDEVELOP" CFLAGS="-ggdb3 -O0"
# Targets for the project maintainer to easily create Windows exes. # Targets for the project maintainer to easily create Windows exes.
# This is not for Windows users! # This is not for Windows users!

View File

@@ -24,15 +24,8 @@
#define MAXMACROARGS 99999 #define MAXMACROARGS 99999
#define MAXINCPATHS 128 #define MAXINCPATHS 128
extern int32_t nLineNo;
extern uint32_t nTotalLines; extern uint32_t nTotalLines;
extern uint32_t nIFDepth; extern uint32_t nIFDepth;
extern bool skipElif;
extern char tzCurrentFileName[_MAX_PATH + 1];
extern struct Section *pCurrentSection; extern struct Section *pCurrentSection;
extern bool oDontExpandStrings;
size_t symvaluetostring(char *dest, size_t maxLength, char *sym,
const char *mode);
#endif /* RGBDS_ASM_ASM_H */ #endif /* RGBDS_ASM_ASM_H */

View File

@@ -21,36 +21,59 @@
#include "types.h" #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 FileStackNode *next; /* Next node in the output linked list */
YY_BUFFER_STATE FlexHandle; bool referenced; /* If referenced, don't free! */
struct Symbol const *pMacro; uint32_t ID; /* Set only if referenced: ID within the object file, -1 if not output yet */
struct sContext *next;
char tzFileName[_MAX_PATH + 1]; enum {
struct MacroArgs *macroArgs; NODE_REPT,
uint32_t uniqueID; NODE_FILE,
int32_t nLine; NODE_MACRO,
uint32_t nStatus; } type;
FILE *pFile;
char *pREPTBlock;
uint32_t nREPTBlockCount;
uint32_t nREPTBlockSize;
int32_t nREPTBodyFirstLine;
int32_t nREPTBodyLastLine;
}; };
extern unsigned int nMaxRecursionDepth; 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 */
};
void fstk_RunInclude(char *tzFileName); struct FileStackNamedNode { /* NODE_FILE, NODE_MACRO */
void fstk_Init(char *s); struct FileStackNode node;
void fstk_Dump(void); char name[]; /* File name for files, file::macro name for macros */
void fstk_DumpToStr(char *buf, size_t len); };
void fstk_DumpStringExpansions(void);
void fstk_AddIncludePath(char *s); extern size_t nMaxRecursionDepth;
void fstk_RunMacro(char *s, struct MacroArgs *args);
void fstk_RunRept(uint32_t count, int32_t nReptLineNo); struct MacroArgs;
FILE *fstk_FindFile(char const *fname, char **incPathUsed);
int32_t fstk_GetLine(void); 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
* @param fullPath The address of a pointer, which will be made to point at the full path
* The pointer's value must be a valid argument to `realloc`, including NULL
* @param size Current size of the buffer, or 0 if the pointer is NULL
* @return True if the file was found, false if no path worked
*/
bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
bool yywrap(void);
void fstk_RunInclude(char const *path);
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_Init(char const *mainPath, size_t maxRecursionDepth);
#endif /* RGBDS_ASM_FSTACK_H */ #endif /* RGBDS_ASM_FSTACK_H */

View File

@@ -9,78 +9,65 @@
#ifndef RGBDS_ASM_LEXER_H #ifndef RGBDS_ASM_LEXER_H
#define RGBDS_ASM_LEXER_H #define RGBDS_ASM_LEXER_H
#include <stdint.h>
#include <stdio.h>
#define LEXHASHSIZE (1 << 11)
#define MAXSTRLEN 255 #define MAXSTRLEN 255
struct sLexInitString { struct LexerState;
char *tzName; extern struct LexerState *lexerState;
uint32_t nToken; extern struct LexerState *lexerStateEOL;
static inline struct LexerState *lexer_GetState(void)
{
return lexerState;
}
static inline void lexer_SetState(struct LexerState *state)
{
lexerState = state;
}
static inline void lexer_SetStateAtEOL(struct LexerState *state)
{
lexerStateEOL = state;
}
extern char const *binDigits;
extern char const *gfxDigits;
static inline void lexer_SetBinDigits(char const *digits)
{
binDigits = digits;
}
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);
void lexer_DeleteState(struct LexerState *state);
void lexer_Init(void);
enum LexerMode {
LEXER_NORMAL,
LEXER_RAW,
LEXER_SKIP_TO_ELIF,
LEXER_SKIP_TO_ENDC
}; };
struct sLexFloat { void lexer_SetMode(enum LexerMode mode);
uint32_t (*Callback)(char *s, uint32_t size); void lexer_ToggleStringExpansion(bool enable);
uint32_t nToken;
};
struct yy_buffer_state { char const *lexer_GetFileName(void);
/* Actual starting address */ uint32_t lexer_GetLineNo(void);
char *pBufferRealStart; uint32_t lexer_GetColNo(void);
/* Address where the data is initially written after a safety margin */ void lexer_DumpStringExpansions(void);
char *pBufferStart;
char *pBuffer;
size_t nBufferSize;
uint32_t oAtLineStart;
};
enum eLexerState {
LEX_STATE_NORMAL,
LEX_STATE_MACROARGS
};
struct sStringExpansionPos {
char *tzName;
char *pBuffer;
char *pBufferPos;
struct sStringExpansionPos *pParent;
};
#define INITIAL 0
#define macroarg 3
typedef struct yy_buffer_state *YY_BUFFER_STATE;
void setup_lexer(void);
void yy_set_state(enum eLexerState i);
YY_BUFFER_STATE yy_create_buffer(FILE *f);
YY_BUFFER_STATE yy_scan_bytes(char const *mem, uint32_t size);
void yy_delete_buffer(YY_BUFFER_STATE buf);
void yy_switch_to_buffer(YY_BUFFER_STATE buf);
uint32_t lex_FloatAlloc(const struct sLexFloat *tok);
void lex_FloatAddRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatDeleteRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatAddFirstRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatDeleteFirstRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatAddSecondRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatDeleteSecondRange(uint32_t id, uint16_t start, uint16_t end);
void lex_Init(void);
void lex_AddStrings(const struct sLexInitString *lex);
void lex_SetBuffer(char *buffer, uint32_t len);
void lex_BeginStringExpansion(const char *tzName);
int yywrap(void);
int yylex(void); int yylex(void);
void yyunput(char c); void lexer_CaptureRept(char **capture, size_t *size);
void yyunputstr(const char *s); void lexer_CaptureMacroBody(char **capture, size_t *size);
void yyskipbytes(uint32_t count);
void yyunputbytes(uint32_t count);
extern YY_BUFFER_STATE pCurrentBuffer;
extern struct sStringExpansionPos *pCurrentStringExpansion;
void upperstring(char *s);
void lowerstring(char *s);
#endif /* RGBDS_ASM_LEXER_H */ #endif /* RGBDS_ASM_LEXER_H */

View File

@@ -28,6 +28,7 @@ char const *macro_GetArg(uint32_t i);
uint32_t macro_GetUniqueID(void); uint32_t macro_GetUniqueID(void);
char const *macro_GetUniqueIDStr(void); char const *macro_GetUniqueIDStr(void);
void macro_SetUniqueID(uint32_t id); void macro_SetUniqueID(uint32_t id);
uint32_t macro_UseNewUniqueID(void);
void macro_ShiftCurrentArgs(void); void macro_ShiftCurrentArgs(void);
uint32_t macro_NbArgs(void); uint32_t macro_NbArgs(void);

View File

@@ -43,6 +43,10 @@ void opt_Push(void);
void opt_Pop(void); void opt_Pop(void);
void opt_Parse(char *s); void opt_Parse(char *s);
void upperstring(char *s);
void lowerstring(char *s);
/* TODO: are these really needed? */
#define YY_FATAL_ERROR fatalerror #define YY_FATAL_ERROR fatalerror
#ifdef YYLMAX #ifdef YYLMAX

View File

@@ -18,6 +18,8 @@ struct Expression;
extern char *tzObjectname; extern char *tzObjectname;
extern struct Section *pSectionList, *pCurrentSection; extern struct Section *pSectionList, *pCurrentSection;
void out_RegisterNode(struct FileStackNode *node);
void out_ReplaceNode(struct FileStackNode *node);
void out_SetFileName(char *s); void out_SetFileName(char *s);
void out_CreatePatch(uint32_t type, struct Expression const *expr, void out_CreatePatch(uint32_t type, struct Expression const *expr,
uint32_t ofs); uint32_t ofs);

View File

@@ -35,18 +35,21 @@ struct Symbol {
bool isExported; /* Whether the symbol is to be exported */ bool isExported; /* Whether the symbol is to be exported */
bool isBuiltin; /* Whether the symbol is a built-in */ bool isBuiltin; /* Whether the symbol is a built-in */
struct Section *section; struct Section *section;
char fileName[_MAX_PATH + 1]; /* File where the symbol was defined. */ struct FileStackNode *src; /* Where the symbol was defined */
uint32_t fileLine; /* Line where the symbol was defined. */ uint32_t fileLine; /* Line where the symbol was defined */
bool hasCallback;
union { union {
struct { /* If sym_IsNumeric */ /* If sym_IsNumeric */
int32_t value; int32_t value;
int32_t (*callback)(void); int32_t (*numCallback)(void);
}; /* For SYM_MACRO */
struct { /* For SYM_MACRO */ struct {
uint32_t macroSize; size_t macroSize;
char *macro; char *macro;
}; };
/* For SYM_EQUS, TODO: separate "base" fields from SYM_MACRO */
char const *(*strCallback)(void); /* For SYM_EQUS */
}; };
uint32_t ID; /* ID of the symbol in the object file (-1 if none) */ uint32_t ID; /* ID of the symbol in the object file (-1 if none) */
@@ -101,6 +104,8 @@ static inline bool sym_IsExported(struct Symbol const *sym)
*/ */
static inline char const *sym_GetStringValue(struct Symbol const *sym) static inline char const *sym_GetStringValue(struct Symbol const *sym)
{ {
if (sym->hasCallback)
return sym->strCallback();
return sym->macro; return sym->macro;
} }
@@ -114,9 +119,10 @@ void sym_Export(char const *symName);
struct Symbol *sym_AddEqu(char const *symName, int32_t value); struct Symbol *sym_AddEqu(char const *symName, int32_t value);
struct Symbol *sym_AddSet(char const *symName, int32_t value); struct Symbol *sym_AddSet(char const *symName, int32_t value);
uint32_t sym_GetPCValue(void); uint32_t sym_GetPCValue(void);
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
uint32_t sym_GetConstantValue(char const *s); uint32_t sym_GetConstantValue(char const *s);
struct Symbol *sym_FindSymbol(char const *symName); struct Symbol *sym_FindSymbol(char const *symName);
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo); struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
struct Symbol *sym_Ref(char const *symName); struct Symbol *sym_Ref(char const *symName);
struct Symbol *sym_AddString(char const *symName, char const *value); struct Symbol *sym_AddString(char const *symName, char const *value);
uint32_t sym_GetDefinedValue(char const *s); uint32_t sym_GetDefinedValue(char const *s);

View File

@@ -12,6 +12,7 @@
#include <stdint.h> #include <stdint.h>
uint32_t calchash(const char *s); uint32_t calchash(const char *s);
char const *print(int c);
size_t readUTF8Char(uint8_t *dest, char const *src); size_t readUTF8Char(uint8_t *dest, char const *src);
#endif /* RGBDS_UTIL_H */ #endif /* RGBDS_UTIL_H */

View File

@@ -29,15 +29,45 @@ extern bool beVerbose;
extern bool isWRA0Mode; extern bool isWRA0Mode;
extern bool disablePadding; 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 */ /* Helper macro for printing verbose-mode messages */
#define verbosePrint(...) do { \ #define verbosePrint(...) do { \
if (beVerbose) \ if (beVerbose) \
fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, __VA_ARGS__); \
} while (0) } 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. * Opens a file if specified, and aborts on error.

View File

@@ -14,8 +14,9 @@
/** /**
* Read an object (.o) file, and add its info to the data structures. * 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 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 * Perform validation on the object files' contents
@@ -27,6 +28,12 @@ void obj_DoSanityChecks(void);
*/ */
void obj_CheckAssertions(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. * `free`s all object memory that was allocated.
*/ */

View File

@@ -19,6 +19,7 @@
#include "linkdefs.h" #include "linkdefs.h"
struct FileStackNode;
struct Section; struct Section;
struct AttachedSymbol { struct AttachedSymbol {
@@ -27,7 +28,8 @@ struct AttachedSymbol {
}; };
struct Patch { struct Patch {
char *fileName; struct FileStackNode const *src;
uint32_t lineNo;
int32_t offset; int32_t offset;
uint32_t pcSectionID; uint32_t pcSectionID;
uint32_t pcOffset; uint32_t pcOffset;

View File

@@ -16,12 +16,14 @@
#include "linkdefs.h" #include "linkdefs.h"
struct FileStackNode;
struct Symbol { struct Symbol {
/* Info contained in the object files */ /* Info contained in the object files */
char *name; char *name;
enum ExportLevel type; enum ExportLevel type;
char const *objFileName; char const *objFileName;
char *fileName; struct FileStackNode const *src;
int32_t lineNo; int32_t lineNo;
int32_t sectionID; int32_t sectionID;
union { union {

View File

@@ -14,7 +14,7 @@
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u" #define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
#define RGBDS_OBJECT_VERSION_NUMBER 9U #define RGBDS_OBJECT_VERSION_NUMBER 9U
#define RGBDS_OBJECT_REV 5U #define RGBDS_OBJECT_REV 6U
enum AssertionType { enum AssertionType {
ASSERT_WARN, ASSERT_WARN,

View File

@@ -32,4 +32,11 @@
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif #endif
/* MSVC doesn't use POSIX types or defines for `read` */
#ifdef _MSC_VER
# define STDIN_FILENO 0
# define ssize_t int
# define SSIZE_MAX INT_MAX
#endif
#endif /* RGBDS_PLATFORM_H */ #endif /* RGBDS_PLATFORM_H */

View File

@@ -31,7 +31,6 @@ set(rgbasm_src
"${BISON_ASMy_OUTPUT_SOURCE}" "${BISON_ASMy_OUTPUT_SOURCE}"
"asm/charmap.c" "asm/charmap.c"
"asm/fstack.c" "asm/fstack.c"
"asm/globlex.c"
"asm/lexer.c" "asm/lexer.c"
"asm/macro.c" "asm/macro.c"
"asm/main.c" "asm/main.c"

View File

@@ -39,62 +39,7 @@ uint32_t nListCountEmpty;
char *tzNewMacro; char *tzNewMacro;
uint32_t ulNewMacroSize; uint32_t ulNewMacroSize;
int32_t nPCOffset; int32_t nPCOffset;
bool executeElseBlock; /* If this is set, ELIFs cannot be executed anymore */
size_t symvaluetostring(char *dest, size_t maxLength, char *symName,
const char *mode)
{
size_t length;
struct Symbol *sym = sym_FindSymbol(symName);
if (sym && sym->type == SYM_EQUS) {
char const *src = sym_GetStringValue(sym);
size_t i;
if (mode)
error("Print types are only allowed for numbers\n");
for (i = 0; src[i] != 0; i++) {
if (i >= maxLength)
fatalerror("Symbol value too long to fit buffer\n");
dest[i] = src[i];
}
length = i;
} else {
uint32_t value = sym_GetConstantValue(symName);
int32_t fullLength;
/* Special cheat for binary */
if (mode && !mode[0]) {
char binary[33]; /* 32 bits + 1 terminator */
char *write_ptr = binary + 32;
fullLength = 0;
binary[32] = 0;
do {
*(--write_ptr) = (value & 1) + '0';
value >>= 1;
fullLength++;
} while(value);
strncpy(dest, write_ptr, maxLength + 1);
} else {
fullLength = snprintf(dest, maxLength + 1,
mode ? mode : "$%" PRIX32,
value);
}
if (fullLength < 0) {
fatalerror("snprintf encoding error\n");
} else {
length = (size_t)fullLength;
if (length > maxLength)
fatalerror("Symbol value too long to fit buffer\n");
}
}
return length;
}
static uint32_t str2int2(uint8_t *s, int32_t length) static uint32_t str2int2(uint8_t *s, int32_t length)
{ {
@@ -111,278 +56,6 @@ static uint32_t str2int2(uint8_t *s, int32_t length)
return r; return r;
} }
static uint32_t isWhiteSpace(char s)
{
return (s == ' ') || (s == '\t') || (s == '\0') || (s == '\n');
}
static uint32_t isRept(char *s)
{
return (strncasecmp(s, "REPT", 4) == 0)
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
}
static uint32_t isEndr(char *s)
{
return (strncasecmp(s, "ENDR", 4) == 0)
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
}
static void copyrept(void)
{
int32_t level = 1, len, instring = 0;
char *src = pCurrentBuffer->pBuffer;
char *bufferEnd = pCurrentBuffer->pBufferStart
+ pCurrentBuffer->nBufferSize;
while (src < bufferEnd && level) {
if (instring == 0) {
if (isRept(src)) {
level++;
src += 4;
} else if (isEndr(src)) {
level--;
src += 4;
} else {
if (*src == '\"')
instring = 1;
src++;
}
} else {
if (*src == '\\') {
src += 2;
} else if (*src == '\"') {
src++;
instring = 0;
} else {
src++;
}
}
}
if (level != 0)
fatalerror("Unterminated REPT block\n");
len = src - pCurrentBuffer->pBuffer - 4;
src = pCurrentBuffer->pBuffer;
ulNewMacroSize = len;
tzNewMacro = malloc(ulNewMacroSize + 1);
if (tzNewMacro == NULL)
fatalerror("Not enough memory for REPT block.\n");
uint32_t i;
tzNewMacro[ulNewMacroSize] = 0;
for (i = 0; i < ulNewMacroSize; i++) {
tzNewMacro[i] = src[i];
if (src[i] == '\n')
nLineNo++;
}
yyskipbytes(ulNewMacroSize + 4);
}
static uint32_t isMacro(char *s)
{
return (strncasecmp(s, "MACRO", 4) == 0)
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[5]);
}
static uint32_t isEndm(char *s)
{
return (strncasecmp(s, "ENDM", 4) == 0)
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
}
static void copymacro(void)
{
int32_t level = 1, len, instring = 0;
char *src = pCurrentBuffer->pBuffer;
char *bufferEnd = pCurrentBuffer->pBufferStart
+ pCurrentBuffer->nBufferSize;
while (src < bufferEnd && level) {
if (instring == 0) {
if (isMacro(src)) {
level++;
src += 4;
} else if (isEndm(src)) {
level--;
src += 4;
} else {
if(*src == '\"')
instring = 1;
src++;
}
} else {
if (*src == '\\') {
src += 2;
} else if (*src == '\"') {
src++;
instring = 0;
} else {
src++;
}
}
}
if (level != 0)
fatalerror("Unterminated MACRO definition.\n");
len = src - pCurrentBuffer->pBuffer - 4;
src = pCurrentBuffer->pBuffer;
ulNewMacroSize = len;
tzNewMacro = (char *)malloc(ulNewMacroSize + 1);
if (tzNewMacro == NULL)
fatalerror("Not enough memory for MACRO definition.\n");
uint32_t i;
tzNewMacro[ulNewMacroSize] = 0;
for (i = 0; i < ulNewMacroSize; i++) {
tzNewMacro[i] = src[i];
if (src[i] == '\n')
nLineNo++;
}
yyskipbytes(ulNewMacroSize + 4);
}
static bool endsIf(char c)
{
return isWhiteSpace(c) || c == '(' || c == '{';
}
static uint32_t isIf(char *s)
{
return (strncasecmp(s, "IF", 2) == 0)
&& isWhiteSpace(s[-1]) && endsIf(s[2]);
}
static uint32_t isElif(char *s)
{
return (strncasecmp(s, "ELIF", 4) == 0)
&& isWhiteSpace(s[-1]) && endsIf(s[4]);
}
static uint32_t isElse(char *s)
{
return (strncasecmp(s, "ELSE", 4) == 0)
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]);
}
static uint32_t isEndc(char *s)
{
return (strncasecmp(s, "ENDC", 4) == 0)
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]);
}
static void if_skip_to_else(void)
{
int32_t level = 1;
bool inString = false;
char *src = pCurrentBuffer->pBuffer;
while (*src && level) {
if (*src == '\n')
nLineNo++;
if (!inString) {
if (isIf(src)) {
level++;
src += 2;
} else if (level == 1 && isElif(src)) {
level--;
skipElif = false;
} else if (level == 1 && isElse(src)) {
level--;
src += 4;
} else if (isEndc(src)) {
level--;
if (level != 0)
src += 4;
} else {
if (*src == '\"')
inString = true;
src++;
}
} else {
if (*src == '\"') {
inString = false;
} else if (*src == '\\') {
/* Escaped quotes don't end the string */
if (*++src != '\"')
src--;
}
src++;
}
}
if (level != 0)
fatalerror("Unterminated IF construct\n");
int32_t len = src - pCurrentBuffer->pBuffer;
yyskipbytes(len);
yyunput('\n');
nLineNo--;
}
static void if_skip_to_endc(void)
{
int32_t level = 1;
bool inString = false;
char *src = pCurrentBuffer->pBuffer;
while (*src && level) {
if (*src == '\n')
nLineNo++;
if (!inString) {
if (isIf(src)) {
level++;
src += 2;
} else if (isEndc(src)) {
level--;
if (level != 0)
src += 4;
} else {
if (*src == '\"')
inString = true;
src++;
}
} else {
if (*src == '\"') {
inString = false;
} else if (*src == '\\') {
/* Escaped quotes don't end the string */
if (*++src != '\"')
src--;
}
src++;
}
}
if (level != 0)
fatalerror("Unterminated IF construct\n");
int32_t len = src - pCurrentBuffer->pBuffer;
yyskipbytes(len);
yyunput('\n');
nLineNo--;
}
static size_t strlenUTF8(const char *s) static size_t strlenUTF8(const char *s)
{ {
size_t len = 0; size_t len = 0;
@@ -659,17 +332,70 @@ lines : /* empty */
| lines { | lines {
nListCountEmpty = 0; nListCountEmpty = 0;
nPCOffset = 0; nPCOffset = 0;
} line '\n' { } line {
nLineNo++;
nTotalLines++; nTotalLines++;
} }
; ;
line : label line : label '\n'
| label cpu_command | label cpu_command '\n'
| label macro | label macro '\n'
| label simple_pseudoop | label simple_pseudoop '\n'
| pseudoop | pseudoop '\n'
| conditional /* May not necessarily be followed by a newline, see below */
;
/*
* For "logistical" reasons, conditionals must manage newlines themselves.
* This is because we need to switch the lexer's mode *after* the newline has been read,
* and to avoid causing some grammar conflicts (token reducing is finicky).
* This is DEFINITELY one of the more FRAGILE parts of the codebase, handle with care.
*/
conditional : if
/* It's important that all of these require being at line start for `skipIfBlock` */
| elif
| else
| endc
;
if : T_POP_IF const '\n' {
nIFDepth++;
executeElseBlock = !$2;
if (executeElseBlock)
lexer_SetMode(LEXER_SKIP_TO_ELIF);
}
;
elif : T_POP_ELIF const '\n' {
if (nIFDepth <= 0)
fatalerror("Found ELIF outside an IF construct\n");
if (!executeElseBlock) {
lexer_SetMode(LEXER_SKIP_TO_ENDC);
} else {
executeElseBlock = !$2;
if (executeElseBlock)
lexer_SetMode(LEXER_SKIP_TO_ELIF);
}
}
;
else : T_POP_ELSE '\n' {
if (nIFDepth <= 0)
fatalerror("Found ELSE outside an IF construct\n");
if (!executeElseBlock)
lexer_SetMode(LEXER_SKIP_TO_ENDC);
}
;
endc : T_POP_ENDC '\n' {
if (nIFDepth <= 0)
fatalerror("Found ENDC outside an IF construct\n");
nIFDepth--;
executeElseBlock = false;
}
; ;
scoped_id : T_ID | T_LOCAL_ID ; scoped_id : T_ID | T_LOCAL_ID ;
@@ -699,9 +425,9 @@ label : /* empty */
; ;
macro : T_ID { macro : T_ID {
yy_set_state(LEX_STATE_MACROARGS); lexer_SetMode(LEXER_RAW);
} macroargs { } macroargs {
yy_set_state(LEX_STATE_NORMAL); lexer_SetMode(LEXER_NORMAL);
fstk_RunMacro($1, $3); fstk_RunMacro($1, $3);
} }
; ;
@@ -732,10 +458,6 @@ simple_pseudoop : include
| printt | printt
| printv | printv
| printi | printi
| if
| elif
| else
| endc
| export | export
| db | db
| dw | dw
@@ -786,9 +508,9 @@ align : T_OP_ALIGN uconst {
; ;
opt : T_POP_OPT { opt : T_POP_OPT {
yy_set_state(LEX_STATE_MACROARGS); lexer_SetMode(LEXER_RAW);
} opt_list { } opt_list {
yy_set_state(LEX_STATE_NORMAL); lexer_SetMode(LEXER_NORMAL);
} }
; ;
@@ -875,16 +597,20 @@ load : T_POP_LOAD string ',' sectiontype sectorg sectattrs {
; ;
rept : T_POP_REPT uconst { rept : T_POP_REPT uconst {
uint32_t nDefinitionLineNo = nLineNo; uint32_t nDefinitionLineNo = lexer_GetLineNo();
copyrept(); char *body;
fstk_RunRept($2, nDefinitionLineNo); size_t size;
lexer_CaptureRept(&body, &size);
fstk_RunRept($2, nDefinitionLineNo, body, size);
} }
; ;
macrodef : T_LABEL ':' T_POP_MACRO { macrodef : T_LABEL ':' T_POP_MACRO {
int32_t nDefinitionLineNo = nLineNo; int32_t nDefinitionLineNo = lexer_GetLineNo();
copymacro(); char *body;
sym_AddMacro($1, nDefinitionLineNo); size_t size;
lexer_CaptureMacroBody(&body, &size);
sym_AddMacro($1, nDefinitionLineNo, body, size);
} }
; ;
@@ -956,9 +682,9 @@ dl : T_POP_DL constlist_32bit_entry ',' constlist_32bit {
; ;
purge : T_POP_PURGE { purge : T_POP_PURGE {
oDontExpandStrings = true; lexer_ToggleStringExpansion(false);
} purge_list { } purge_list {
oDontExpandStrings = false; lexer_ToggleStringExpansion(true);
} }
; ;
@@ -1052,62 +778,6 @@ printi : T_POP_PRINTI const { printf("%" PRId32, $2); }
printf : T_POP_PRINTF const { math_Print($2); } printf : T_POP_PRINTF const { math_Print($2); }
; ;
if : T_POP_IF const {
nIFDepth++;
if (!$2)
if_skip_to_else();
}
;
elif : T_POP_ELIF const {
if (nIFDepth <= 0)
fatalerror("Found ELIF outside an IF construct\n");
if (skipElif) {
/*
* Executed when ELIF is reached at the end of
* an IF or ELIF block for which the condition
* was true.
*
* Continue parsing at ENDC keyword
*/
if_skip_to_endc();
} else {
/*
* Executed when ELIF is skipped to because the
* condition of the previous IF or ELIF block
* was false.
*/
skipElif = true;
if (!$2) {
/*
* Continue parsing after ELSE, or at
* ELIF or ENDC keyword.
*/
if_skip_to_else();
}
}
}
;
else : T_POP_ELSE {
if (nIFDepth <= 0)
fatalerror("Found ELSE outside an IF construct\n");
/* Continue parsing at ENDC keyword */
if_skip_to_endc();
}
;
endc : T_POP_ENDC {
if (nIFDepth <= 0)
fatalerror("Found ENDC outside an IF construct\n");
nIFDepth--;
}
;
const_3bit : const { const_3bit : const {
int32_t value = $1; int32_t value = $1;
@@ -1267,13 +937,13 @@ relocexpr_no_str : scoped_id { rpn_Symbol(&$$, $1); }
} }
| T_OP_BANK '(' string ')' { rpn_BankSection(&$$, $3); } | T_OP_BANK '(' string ')' { rpn_BankSection(&$$, $3); }
| T_OP_DEF { | T_OP_DEF {
oDontExpandStrings = true; lexer_ToggleStringExpansion(false);
} '(' scoped_id ')' { } '(' scoped_id ')' {
struct Symbol const *sym = sym_FindSymbol($4); struct Symbol const *sym = sym_FindSymbol($4);
rpn_Number(&$$, !!sym); rpn_Number(&$$, !!sym);
oDontExpandStrings = false; lexer_ToggleStringExpansion(true);
} }
| T_OP_ROUND '(' const ')' { | T_OP_ROUND '(' const ')' {
rpn_Number(&$$, math_Round($3)); rpn_Number(&$$, math_Round($3));

View File

@@ -6,554 +6,470 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* #include <sys/stat.h>
* FileStack routines #include <assert.h>
*/
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "asm/fstack.h" #include "asm/fstack.h"
#include "asm/lexer.h"
#include "asm/macro.h" #include "asm/macro.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/output.h" #include "asm/symbol.h"
#include "asm/warning.h" #include "asm/warning.h"
#include "platform.h" /* S_ISDIR (stat macro) */
#include "extern/err.h" #ifdef LEXER_DEBUG
#define dbgPrint(...) fprintf(stderr, "[lexer] " __VA_ARGS__)
#else
#define dbgPrint(...)
#endif
#include "platform.h" // S_ISDIR (stat macro) struct Context {
#include "types.h" struct Context *parent;
struct FileStackNode *fileInfo;
struct LexerState *lexerState;
uint32_t uniqueID;
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
uint32_t nbReptIters;
};
static struct sContext *pFileStack; static struct Context *contextStack;
static unsigned int nFileStackDepth; static size_t contextDepth = 0;
unsigned int nMaxRecursionDepth; #define DEFAULT_MAX_DEPTH 64
static struct Symbol const *pCurrentMacro; size_t nMaxRecursionDepth;
static YY_BUFFER_STATE CurrentFlexHandle;
static FILE *pCurrentFile;
static uint32_t nCurrentStatus;
char tzCurrentFileName[_MAX_PATH + 1];
static char IncludePaths[MAXINCPATHS][_MAX_PATH + 1];
static int32_t NextIncPath;
static uint32_t nMacroCount;
static char *pCurrentREPTBlock; static unsigned int nbIncPaths = 0;
static uint32_t nCurrentREPTBlockSize; static char const *includePaths[MAXINCPATHS];
static uint32_t nCurrentREPTBlockCount;
static int32_t nCurrentREPTBodyFirstLine;
static int32_t nCurrentREPTBodyLastLine;
uint32_t ulMacroReturnValue; char const *dumpNodeAndParents(struct FileStackNode const *node)
/*
* defines for nCurrentStatus
*/
#define STAT_isInclude 0 /* 'Normal' state as well */
#define STAT_isMacro 1
#define STAT_isMacroArg 2
#define STAT_isREPTBlock 3
/* Max context stack size */
/*
* Context push and pop
*/
static void pushcontext(void)
{ {
struct sContext **ppFileStack; char const *name;
if (++nFileStackDepth > nMaxRecursionDepth) if (node->type == NODE_REPT) {
fatalerror("Recursion limit (%u) exceeded\n", nMaxRecursionDepth); assert(node->parent); /* REPT nodes should always have a parent */
struct FileStackReptNode const *reptInfo = (struct FileStackReptNode const *)node;
ppFileStack = &pFileStack; name = dumpNodeAndParents(node->parent);
while (*ppFileStack) fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
ppFileStack = &((*ppFileStack)->next); for (uint32_t i = reptInfo->reptDepth; i--; )
fprintf(stderr, "::REPT~%" PRIu32, reptInfo->iters[i]);
*ppFileStack = malloc(sizeof(struct sContext)); } else {
name = ((struct FileStackNamedNode const *)node)->name;
if (*ppFileStack == NULL) if (node->parent) {
fatalerror("No memory for context\n"); dumpNodeAndParents(node->parent);
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
(*ppFileStack)->FlexHandle = CurrentFlexHandle; } else {
(*ppFileStack)->next = NULL; fputs(name, stderr);
strcpy((char *)(*ppFileStack)->tzFileName, (char *)tzCurrentFileName);
(*ppFileStack)->nLine = nLineNo;
switch ((*ppFileStack)->nStatus = nCurrentStatus) {
case STAT_isMacroArg:
case STAT_isMacro:
(*ppFileStack)->macroArgs = macro_GetCurrentArgs();
(*ppFileStack)->pMacro = pCurrentMacro;
break;
case STAT_isInclude:
(*ppFileStack)->pFile = pCurrentFile;
break;
case STAT_isREPTBlock:
(*ppFileStack)->macroArgs = macro_GetCurrentArgs();
(*ppFileStack)->pREPTBlock = pCurrentREPTBlock;
(*ppFileStack)->nREPTBlockSize = nCurrentREPTBlockSize;
(*ppFileStack)->nREPTBlockCount = nCurrentREPTBlockCount;
(*ppFileStack)->nREPTBodyFirstLine = nCurrentREPTBodyFirstLine;
(*ppFileStack)->nREPTBodyLastLine = nCurrentREPTBodyLastLine;
break;
default:
fatalerror("%s: Internal error.\n", __func__);
}
(*ppFileStack)->uniqueID = macro_GetUniqueID();
nLineNo = 0;
}
static int32_t popcontext(void)
{
struct sContext *pLastFile, **ppLastFile;
if (nCurrentStatus == STAT_isREPTBlock) {
if (--nCurrentREPTBlockCount) {
char *pREPTIterationWritePtr;
unsigned long nREPTIterationNo;
int nNbCharsWritten;
int nNbCharsLeft;
yy_delete_buffer(CurrentFlexHandle);
CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock,
nCurrentREPTBlockSize);
yy_switch_to_buffer(CurrentFlexHandle);
macro_SetUniqueID(nMacroCount++);
/* Increment REPT count in file path */
pREPTIterationWritePtr =
strrchr(tzCurrentFileName, '~') + 1;
nREPTIterationNo =
strtoul(pREPTIterationWritePtr, NULL, 10);
nNbCharsLeft = sizeof(tzCurrentFileName)
- (pREPTIterationWritePtr - tzCurrentFileName);
nNbCharsWritten = snprintf(pREPTIterationWritePtr,
nNbCharsLeft, "%lu",
nREPTIterationNo + 1);
if (nNbCharsWritten >= nNbCharsLeft) {
/*
* The string is probably corrupted somehow,
* revert the change to avoid a bad error
* output.
*/
sprintf(pREPTIterationWritePtr, "%lu",
nREPTIterationNo);
fatalerror("Cannot write REPT count to file path\n");
}
nLineNo = nCurrentREPTBodyFirstLine;
return 0;
} }
} }
return name;
pLastFile = pFileStack;
if (pLastFile == NULL)
return 1;
ppLastFile = &pFileStack;
while (pLastFile->next) {
ppLastFile = &(pLastFile->next);
pLastFile = *ppLastFile;
}
yy_delete_buffer(CurrentFlexHandle);
nLineNo = nCurrentStatus == STAT_isREPTBlock ? nCurrentREPTBodyLastLine
: pLastFile->nLine;
if (nCurrentStatus == STAT_isInclude)
fclose(pCurrentFile);
if (nCurrentStatus == STAT_isMacro
|| nCurrentStatus == STAT_isREPTBlock)
nLineNo++;
CurrentFlexHandle = pLastFile->FlexHandle;
strcpy((char *)tzCurrentFileName, (char *)pLastFile->tzFileName);
switch (pLastFile->nStatus) {
struct MacroArgs *args;
case STAT_isMacroArg:
case STAT_isMacro:
args = macro_GetCurrentArgs();
if (nCurrentStatus == STAT_isMacro) {
macro_FreeArgs(args);
free(args);
}
macro_UseNewArgs(pLastFile->macroArgs);
pCurrentMacro = pLastFile->pMacro;
break;
case STAT_isInclude:
pCurrentFile = pLastFile->pFile;
break;
case STAT_isREPTBlock:
args = macro_GetCurrentArgs();
if (nCurrentStatus == STAT_isMacro) {
macro_FreeArgs(args);
free(args);
}
macro_UseNewArgs(pLastFile->macroArgs);
pCurrentREPTBlock = pLastFile->pREPTBlock;
nCurrentREPTBlockSize = pLastFile->nREPTBlockSize;
nCurrentREPTBlockCount = pLastFile->nREPTBlockCount;
nCurrentREPTBodyFirstLine = pLastFile->nREPTBodyFirstLine;
break;
default:
fatalerror("%s: Internal error.\n", __func__);
}
macro_SetUniqueID(pLastFile->uniqueID);
nCurrentStatus = pLastFile->nStatus;
nFileStackDepth--;
free(*ppLastFile);
*ppLastFile = NULL;
yy_switch_to_buffer(CurrentFlexHandle);
return 0;
} }
int32_t fstk_GetLine(void) void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo)
{ {
struct sContext *pLastFile, **ppLastFile; dumpNodeAndParents(node);
fprintf(stderr, "(%" PRIu32 ")", lineNo);
switch (nCurrentStatus) {
case STAT_isInclude:
/* This is the normal mode, also used when including a file. */
return nLineNo;
case STAT_isMacro:
break; /* Peek top file of the stack */
case STAT_isMacroArg:
return nLineNo; /* ??? */
case STAT_isREPTBlock:
break; /* Peek top file of the stack */
default:
fatalerror("%s: Internal error.\n", __func__);
}
pLastFile = pFileStack;
if (pLastFile != NULL) {
while (pLastFile->next) {
ppLastFile = &(pLastFile->next);
pLastFile = *ppLastFile;
}
return pLastFile->nLine;
}
/*
* This is only reached if the lexer is in REPT or MACRO mode but there
* are no saved contexts with the origin of said REPT or MACRO.
*/
fatalerror("%s: Internal error.\n", __func__);
} }
int yywrap(void) void fstk_DumpCurrent(void)
{ {
return popcontext(); if (!contextStack) {
} fputs("at top level", stderr);
return;
/*
* Dump the context stack to stderr
*/
void fstk_Dump(void)
{
const struct sContext *pLastFile;
pLastFile = pFileStack;
while (pLastFile) {
fprintf(stderr, "%s(%" PRId32 ") -> ", pLastFile->tzFileName,
pLastFile->nLine);
pLastFile = pLastFile->next;
} }
fstk_Dump(contextStack->fileInfo, lexer_GetLineNo());
fprintf(stderr, "%s(%" PRId32 ")", tzCurrentFileName, nLineNo);
} }
void fstk_DumpToStr(char *buf, size_t buflen) struct FileStackNode *fstk_GetFileStack(void)
{ {
const struct sContext *pLastFile = pFileStack; struct FileStackNode *node = contextStack->fileInfo;
int retcode;
size_t len = buflen;
while (pLastFile) { /* Mark node and all of its parents as referenced if not already so they don't get freed */
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ") -> ", while (node && !node->referenced) {
pLastFile->tzFileName, pLastFile->nLine); node->ID = -1;
if (retcode < 0) node->referenced = true;
fatalerror("Failed to dump file stack to string: %s\n", strerror(errno)); node = node->parent;
else if (retcode >= len)
len = 0;
else
len -= retcode;
pLastFile = pLastFile->next;
} }
return contextStack->fileInfo;
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ")",
tzCurrentFileName, nLineNo);
if (retcode < 0)
fatalerror("Failed to dump file stack to string: %s\n", strerror(errno));
else if (retcode >= len)
len = 0;
else
len -= retcode;
if (!len)
warning(WARNING_LONG_STR, "File stack dump too long, got truncated\n");
} }
/* char const *fstk_GetFileName(void)
* Dump the string expansion stack to stderr
*/
void fstk_DumpStringExpansions(void)
{ {
const struct sStringExpansionPos *pExpansion = pCurrentStringExpansion; /* Iterating via the nodes themselves skips nested REPTs */
struct FileStackNode const *node = contextStack->fileInfo;
while (pExpansion) { while (node->type != NODE_FILE)
fprintf(stderr, "while expanding symbol \"%s\"\n", node = node->parent;
pExpansion->tzName); return ((struct FileStackNamedNode const *)node)->name;
pExpansion = pExpansion->pParent; }
void fstk_AddIncludePath(char const *path)
{
if (path[0] == '\0')
return;
if (nbIncPaths >= MAXINCPATHS) {
error("Too many include directories passed from command line\n");
return;
} }
size_t len = strlen(path);
size_t allocSize = len + (path[len - 1] != '/') + 1;
char *str = malloc(allocSize);
if (!str) {
/* Attempt to continue without that path */
error("Failed to allocate new include path: %s\n", strerror(errno));
return;
}
memcpy(str, path, len);
char *end = str + len - 1;
if (*end++ != '/')
*end++ = '/';
*end = '\0';
includePaths[nbIncPaths++] = str;
} }
/* static void printDep(char const *path)
* Extra includepath stuff
*/
void fstk_AddIncludePath(char *s)
{
if (NextIncPath == MAXINCPATHS)
fatalerror("Too many include directories passed from command line\n");
// Find last occurrence of slash; is it at the end of the string?
char const *lastSlash = strrchr(s, '/');
char const *pattern = lastSlash && *(lastSlash + 1) == 0 ? "%s" : "%s/";
if (snprintf(IncludePaths[NextIncPath++], _MAX_PATH, pattern,
s) >= _MAX_PATH)
fatalerror("Include path too long '%s'\n", s);
}
static void printdep(const char *fileName)
{ {
if (dependfile) { if (dependfile) {
fprintf(dependfile, "%s: %s\n", tzTargetFileName, fileName); fprintf(dependfile, "%s: %s\n", tzTargetFileName, path);
if (oGeneratePhonyDeps) if (oGeneratePhonyDeps)
fprintf(dependfile, "%s:\n", fileName); fprintf(dependfile, "%s:\n", path);
} }
} }
static FILE *getFile(char const *pathname) static bool isPathValid(char const *path)
{ {
struct stat statbuf; struct stat statbuf;
if (stat(pathname, &statbuf) != 0) if (stat(path, &statbuf) != 0)
return NULL; return false;
/* Reject directories */ /* Reject directories */
if (S_ISDIR(statbuf.st_mode)) return !S_ISDIR(statbuf.st_mode);
return NULL;
return fopen(pathname, "rb");
} }
FILE *fstk_FindFile(char const *fname, char **incPathUsed) bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
{ {
if (fname == NULL) if (!*size) {
return NULL; *size = 64; /* This is arbitrary, really */
*fullPath = realloc(*fullPath, *size);
char path[_MAX_PATH]; if (!*fullPath)
FILE *f = getFile(fname); error("realloc error during include path search: %s\n",
strerror(errno));
if (f) {
printdep(fname);
return f;
} }
for (size_t i = 0; i < NextIncPath; ++i) { if (*fullPath) {
/* for (size_t i = 0; i <= nbIncPaths; ++i) {
* The function snprintf() does not write more than `size` bytes char const *incPath = i ? includePaths[i - 1] : "";
* (including the terminating null byte ('\0')). If the output int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
* was truncated due to this limit, the return value is the
* number of characters (excluding the terminating null byte)
* which would have been written to the final string if enough
* space had been available. Thus, a return value of `size` or
* more means that the output was truncated.
*/
int fullpathlen = snprintf(path, sizeof(path), "%s%s",
IncludePaths[i], fname);
if (fullpathlen >= (int)sizeof(path)) /* Oh how I wish `asnprintf` was standard... */
continue; if (len >= *size) { /* `len` doesn't include the terminator, `size` does */
*size = len + 1;
*fullPath = realloc(*fullPath, *size);
if (!*fullPath) {
error("realloc error during include path search: %s\n",
strerror(errno));
break;
}
len = sprintf(*fullPath, "%s%s", incPath, path);
}
f = getFile(path); if (len < 0) {
if (f) { error("snprintf error during include path search: %s\n",
printdep(path); strerror(errno));
} else if (isPathValid(*fullPath)) {
if (incPathUsed) printDep(*fullPath);
*incPathUsed = IncludePaths[i]; return true;
return f; }
} }
} }
errno = ENOENT; errno = ENOENT;
if (oGeneratedMissingIncludes) if (oGeneratedMissingIncludes)
printdep(fname); printDep(path);
return NULL; return false;
}
bool yywrap(void)
{
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) {
size_t size = sizeof(*fileInfo) + sizeof(fileInfo->iters[0]) * fileInfo->reptDepth;
struct FileStackReptNode *copy = malloc(size);
if (!copy)
fatalerror("Failed to duplicate REPT file node: %s\n", strerror(errno));
/* Copy all info but the referencing */
memcpy(copy, fileInfo, size);
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 (fileInfo->iters[0] <= contextStack->nbReptIters) {
lexer_RestartRept(contextStack->fileInfo->lineNo);
contextStack->uniqueID = macro_UseNewUniqueID();
return false;
}
} else if (!contextStack->parent) {
return true;
}
dbgPrint("Popping context\n");
struct Context *context = contextStack;
contextStack = contextStack->parent;
contextDepth--;
lexer_DeleteState(context->lexerState);
/* Restore args if a macro (not REPT) saved them */
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(context);
lexer_SetState(contextStack->lexerState);
macro_SetUniqueID(contextStack->uniqueID);
return false;
} }
/* /*
* Set up an include file for parsing * 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
*/ */
void fstk_RunInclude(char *tzFileName) static void newContext(struct FileStackNode *fileInfo)
{ {
char *incPathUsed = ""; if (++contextDepth >= nMaxRecursionDepth)
FILE *f = fstk_FindFile(tzFileName, &incPathUsed); fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
struct Context *context = malloc(sizeof(*context));
if (f == NULL) { if (!context)
if (oGeneratedMissingIncludes) { 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;
}
void fstk_RunInclude(char const *path)
{
dbgPrint("Including path \"%s\"\n", path);
char *fullPath = NULL;
size_t size = 0;
if (!fstk_FindFile(path, &fullPath, &size)) {
free(fullPath);
if (oGeneratedMissingIncludes)
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; else
} error("Unable to open included file '%s': %s\n", path, strerror(errno));
error("Unable to open included file '%s': %s\n", tzFileName, strerror(errno));
return; return;
} }
dbgPrint("Full path: \"%s\"\n", fullPath);
pushcontext(); struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
nLineNo = 1;
nCurrentStatus = STAT_isInclude;
snprintf(tzCurrentFileName, sizeof(tzCurrentFileName), "%s%s",
incPathUsed, tzFileName);
if (verbose)
printf("Assembling %s\n", tzCurrentFileName);
pCurrentFile = f;
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
yy_switch_to_buffer(CurrentFlexHandle);
/* Dirty hack to give the INCLUDE directive a linefeed */ 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);
yyunput('\n'); newContext((struct FileStackNode *)fileInfo);
nLineNo--; 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);
} }
/* void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
* Set up a macro for parsing
*/
void fstk_RunMacro(char *s, struct MacroArgs *args)
{ {
struct Symbol const *sym = sym_FindSymbol(s); dbgPrint("Running macro \"%s\"\n", macroName);
int nPrintedChars;
if (sym == NULL) { struct Symbol *macro = sym_FindSymbol(macroName);
error("Macro \"%s\" not defined\n", s);
if (!macro) {
error("Macro \"%s\" not defined\n", macroName);
return; return;
} }
if (sym->type != SYM_MACRO) { if (macro->type != SYM_MACRO) {
error("\"%s\" is not a macro\n", s); error("\"%s\" is not a macro\n", macroName);
return; return;
} }
contextStack->macroArgs = macro_GetCurrentArgs();
pushcontext(); /* Compute total length of this node's name: <base name>::<macro> */
macro_SetUniqueID(nMacroCount++); size_t reptNameLen = 0;
/* Minus 1 because there is a newline at the beginning of the buffer */ struct FileStackNode const *node = macro->src;
nLineNo = sym->fileLine - 1;
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);
if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for macro invocation\n");
lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID();
macro_UseNewArgs(args); macro_UseNewArgs(args);
nCurrentStatus = STAT_isMacro;
nPrintedChars = snprintf(tzCurrentFileName, _MAX_PATH + 1,
"%s::%s", sym->fileName, s);
if (nPrintedChars > _MAX_PATH) {
popcontext();
fatalerror("File name + macro name is too large to fit into buffer\n");
}
pCurrentMacro = sym;
/* TODO: why is `strlen` being used when there's a macro size field? */
CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->macro,
strlen(pCurrentMacro->macro));
yy_switch_to_buffer(CurrentFlexHandle);
} }
/* void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
* Set up a repeat block for parsing
*/
void fstk_RunRept(uint32_t count, int32_t nReptLineNo)
{ {
if (count) { dbgPrint("Running REPT(%" PRIu32 ")\n", count);
static const char *tzReptStr = "::REPT~1"; if (count == 0)
return;
/* For error printing to make sense, fake nLineNo */ uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
nCurrentREPTBodyLastLine = nLineNo; ? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
nLineNo = nReptLineNo; : 0;
pushcontext(); struct FileStackReptNode *fileInfo = malloc(sizeof(*fileInfo)
macro_SetUniqueID(nMacroCount++); + (reptDepth + 1) * sizeof(fileInfo->iters[0]));
nCurrentREPTBlockCount = count;
nCurrentStatus = STAT_isREPTBlock;
nCurrentREPTBlockSize = ulNewMacroSize;
pCurrentREPTBlock = tzNewMacro;
nCurrentREPTBodyFirstLine = nReptLineNo + 1;
nLineNo = nReptLineNo;
if (strlen(tzCurrentFileName) + strlen(tzReptStr) > _MAX_PATH) if (!fileInfo) {
fatalerror("Cannot append \"%s\" to file path\n", tzReptStr); error("Failed to alloc file info for REPT: %s\n", strerror(errno));
strcat(tzCurrentFileName, tzReptStr); return;
CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock, nCurrentREPTBlockSize);
yy_switch_to_buffer(CurrentFlexHandle);
} }
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 rept block\n");
lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID();
contextStack->nbReptIters = count;
} }
/* void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
* Initialize the filestack routines
*/
void fstk_Init(char *pFileName)
{ {
char tzSymFileName[_MAX_PATH + 1 + 2]; struct LexerState *state = lexer_OpenFile(mainPath);
char *c = pFileName; if (!state)
int fileNameIndex = 0; fatalerror("Failed to open main file!\n");
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);
tzSymFileName[fileNameIndex++] = '"'; 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));
// minus 2 to account for trailing "\"\0" context->fileInfo = (struct FileStackNode *)fileInfo;
// minus 1 to avoid a buffer overflow in extreme cases /* lineNo and reptIter are unused on the top-level context */
while (*c && fileNameIndex < sizeof(tzSymFileName) - 2 - 1) { context->fileInfo->parent = NULL;
context->fileInfo->referenced = false;
context->fileInfo->type = NODE_FILE;
memcpy(fileInfo->name, fileName, len + 1);
if (*c == '"') { context->parent = NULL;
tzSymFileName[fileNameIndex++] = '\\'; context->lexerState = state;
} context->uniqueID = 0;
macro_SetUniqueID(0);
context->nbReptIters = 0;
tzSymFileName[fileNameIndex++] = *c; /* Now that it's set up properly, register the context */
++c; contextStack = context;
}
tzSymFileName[fileNameIndex++] = '"'; /*
tzSymFileName[fileNameIndex] = '\0'; * Check that max recursion depth won't allow overflowing node `malloc`s
* This assumes that the rept node is larger
sym_AddString("__FILE__", tzSymFileName); */
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
pFileStack = NULL; if (maxRecursionDepth > DEPTH_LIMIT) {
if (strcmp(pFileName, "-") == 0) { error("Recursion depth may not be higher than %zu, defaulting to "
pCurrentFile = stdin; EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
} else { } else {
pCurrentFile = fopen(pFileName, "rb"); nMaxRecursionDepth = maxRecursionDepth;
if (pCurrentFile == NULL)
fatalerror("Unable to open file '%s': %s\n", pFileName, strerror(errno));
} }
nFileStackDepth = 0; /* Make sure that the default of 64 is OK, though */
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
nMacroCount = 0; #undef DEPTH_LIMIT
nCurrentStatus = STAT_isInclude;
snprintf(tzCurrentFileName, _MAX_PATH + 1, "%s", pFileName);
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
yy_switch_to_buffer(CurrentFlexHandle);
nLineNo = 1;
} }

View File

@@ -1,698 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asm/asm.h"
#include "asm/lexer.h"
#include "asm/macro.h"
#include "asm/main.h"
#include "asm/rpn.h"
#include "asm/section.h"
#include "asm/warning.h"
#include "helpers.h"
#include "asmy.h"
bool oDontExpandStrings;
int32_t nGBGfxID = -1;
int32_t nBinaryID = -1;
static int32_t gbgfx2bin(char ch)
{
int32_t i;
for (i = 0; i <= 3; i++) {
if (CurrentOptions.gbgfx[i] == ch)
return i;
}
return 0;
}
static int32_t binary2bin(char ch)
{
int32_t i;
for (i = 0; i <= 1; i++) {
if (CurrentOptions.binary[i] == ch)
return i;
}
return 0;
}
static int32_t char2bin(char ch)
{
if (ch >= 'a' && ch <= 'f')
return (ch - 'a' + 10);
if (ch >= 'A' && ch <= 'F')
return (ch - 'A' + 10);
if (ch >= '0' && ch <= '9')
return (ch - '0');
return 0;
}
typedef int32_t(*x2bin) (char ch);
static int32_t ascii2bin(char *s)
{
char *start = s;
uint32_t radix = 10;
uint32_t result = 0;
x2bin convertfunc = char2bin;
switch (*s) {
case '$':
radix = 16;
s++;
convertfunc = char2bin;
break;
case '&':
radix = 8;
s++;
convertfunc = char2bin;
break;
case '`':
radix = 4;
s++;
convertfunc = gbgfx2bin;
break;
case '%':
radix = 2;
s++;
convertfunc = binary2bin;
break;
default:
/* Handle below */
break;
}
const uint32_t max_q = UINT32_MAX / radix;
const uint32_t max_r = UINT32_MAX % radix;
if (*s == '\0') {
/*
* There are no digits after the radix prefix
* (or the string is empty, which shouldn't happen).
*/
error("Invalid integer constant\n");
} else if (radix == 4) {
int32_t size = 0;
int32_t c;
while (*s != '\0') {
c = convertfunc(*s++);
result = result * 2 + ((c & 2) << 7) + (c & 1);
size++;
}
/*
* Extending a graphics constant longer than 8 pixels,
* the Game Boy tile width, produces a nonsensical result.
*/
if (size > 8) {
warning(WARNING_LARGE_CONSTANT, "Graphics constant '%s' is too long\n",
start);
}
} else {
bool overflow = false;
while (*s != '\0') {
int32_t digit = convertfunc(*s++);
if (result > max_q
|| (result == max_q && digit > max_r)) {
overflow = true;
}
result = result * radix + digit;
}
if (overflow)
warning(WARNING_LARGE_CONSTANT, "Integer constant '%s' is too large\n",
start);
}
return result;
}
uint32_t ParseFixedPoint(char *s, uint32_t size)
{
uint32_t i;
uint32_t dot = 0;
for (i = 0; i < size; i++) {
if (s[i] == '.') {
dot++;
if (dot == 2)
break;
}
}
yyskipbytes(i);
yylval.nConstValue = (int32_t)(atof(s) * 65536);
return 1;
}
uint32_t ParseNumber(char *s, uint32_t size)
{
char dest[256];
if (size > 255)
fatalerror("Number token too long\n");
strncpy(dest, s, size);
dest[size] = 0;
yylval.nConstValue = ascii2bin(dest);
yyskipbytes(size);
return 1;
}
/*
* If the symbol name ends before the end of the macro arg,
* return a pointer to the rest of the macro arg.
* Otherwise, return NULL.
*/
char const *AppendMacroArg(char whichArg, char *dest, size_t *destIndex)
{
char const *marg;
if (whichArg == '@')
marg = macro_GetUniqueIDStr();
else if (whichArg >= '1' && whichArg <= '9')
marg = macro_GetArg(whichArg - '0');
else
fatalerror("Invalid macro argument '\\%c' in symbol\n", whichArg);
if (!marg)
fatalerror("Macro argument '\\%c' not defined\n", whichArg);
char ch;
while ((ch = *marg) != 0) {
if ((ch >= 'a' && ch <= 'z')
|| (ch >= 'A' && ch <= 'Z')
|| (ch >= '0' && ch <= '9')
|| ch == '_'
|| ch == '@'
|| ch == '#'
|| ch == '.') {
if (*destIndex >= MAXSYMLEN)
fatalerror("Symbol too long\n");
dest[*destIndex] = ch;
(*destIndex)++;
} else {
return marg;
}
marg++;
}
return NULL;
}
uint32_t ParseSymbol(char *src, uint32_t size)
{
char dest[MAXSYMLEN + 1];
size_t srcIndex = 0;
size_t destIndex = 0;
char const *rest = NULL;
while (srcIndex < size) {
char ch = src[srcIndex++];
if (ch == '\\') {
/*
* We don't check if srcIndex is still less than size,
* but that can only fail to be true when the
* following char is neither '@' nor a digit.
* In that case, AppendMacroArg() will catch the error.
*/
ch = src[srcIndex++];
rest = AppendMacroArg(ch, dest, &destIndex);
/* If the symbol's end was in the middle of the token */
if (rest)
break;
} else {
if (destIndex >= MAXSYMLEN)
fatalerror("Symbol too long\n");
dest[destIndex++] = ch;
}
}
dest[destIndex] = 0;
/* Tell the lexer we read all bytes that we did */
yyskipbytes(srcIndex);
/*
* If an escape's expansion left some chars after the symbol's end,
* such as the `::` in a `Backup\1` expanded to `BackupCamX::`,
* put those into the buffer.
* Note that this NEEDS to be done after the `yyskipbytes` above.
*/
if (rest)
yyunputstr(rest);
/* If the symbol is an EQUS, expand it */
if (!oDontExpandStrings) {
struct Symbol const *sym = sym_FindSymbol(dest);
if (sym && sym->type == SYM_EQUS) {
char const *s;
lex_BeginStringExpansion(dest);
/* Feed the symbol's contents into the buffer */
yyunputstr(s = sym_GetStringValue(sym));
/* Lines inserted this way shall not increase nLineNo */
while (*s) {
if (*s++ == '\n')
nLineNo--;
}
return 0;
}
}
strcpy(yylval.tzSym, dest);
return 1;
}
uint32_t PutMacroArg(char *src, uint32_t size)
{
char const *s;
yyskipbytes(size);
if ((size == 2 && src[1] >= '1' && src[1] <= '9')) {
s = macro_GetArg(src[1] - '0');
if (s != NULL)
yyunputstr(s);
else
error("Macro argument '\\%c' not defined\n", src[1]);
} else {
error("Invalid macro argument '\\%c'\n", src[1]);
}
return 0;
}
uint32_t PutUniqueID(char *src, uint32_t size)
{
(void)src;
char const *s;
yyskipbytes(size);
s = macro_GetUniqueIDStr();
if (s != NULL)
yyunputstr(s);
else
error("Macro unique label string not defined\n");
return 0;
}
enum {
T_LEX_MACROARG = 3000,
T_LEX_MACROUNIQUE
};
const struct sLexInitString lexer_strings[] = {
{"adc", T_Z80_ADC},
{"add", T_Z80_ADD},
{"and", T_Z80_AND},
{"bit", T_Z80_BIT},
{"call", T_Z80_CALL},
{"ccf", T_Z80_CCF},
{"cpl", T_Z80_CPL},
{"cp", T_Z80_CP},
{"daa", T_Z80_DAA},
{"dec", T_Z80_DEC},
{"di", T_Z80_DI},
{"ei", T_Z80_EI},
{"halt", T_Z80_HALT},
{"inc", T_Z80_INC},
{"jp", T_Z80_JP},
{"jr", T_Z80_JR},
{"ld", T_Z80_LD},
{"ldi", T_Z80_LDI},
{"ldd", T_Z80_LDD},
{"ldio", T_Z80_LDIO},
{"ldh", T_Z80_LDIO},
{"nop", T_Z80_NOP},
{"or", T_Z80_OR},
{"pop", T_Z80_POP},
{"push", T_Z80_PUSH},
{"res", T_Z80_RES},
{"reti", T_Z80_RETI},
{"ret", T_Z80_RET},
{"rlca", T_Z80_RLCA},
{"rlc", T_Z80_RLC},
{"rla", T_Z80_RLA},
{"rl", T_Z80_RL},
{"rrc", T_Z80_RRC},
{"rrca", T_Z80_RRCA},
{"rra", T_Z80_RRA},
{"rr", T_Z80_RR},
{"rst", T_Z80_RST},
{"sbc", T_Z80_SBC},
{"scf", T_Z80_SCF},
{"set", T_POP_SET},
{"sla", T_Z80_SLA},
{"sra", T_Z80_SRA},
{"srl", T_Z80_SRL},
{"stop", T_Z80_STOP},
{"sub", T_Z80_SUB},
{"swap", T_Z80_SWAP},
{"xor", T_Z80_XOR},
{"nz", T_CC_NZ},
{"z", T_CC_Z},
{"nc", T_CC_NC},
/* Handled in list of registers */
/* { "c", T_TOKEN_C }, */
{"hli", T_MODE_HL_INC},
{"hld", T_MODE_HL_DEC},
{"$ff00+c", T_MODE_HW_C},
{"$ff00 + c", T_MODE_HW_C},
{"af", T_MODE_AF},
{"bc", T_MODE_BC},
{"de", T_MODE_DE},
{"hl", T_MODE_HL},
{"sp", T_MODE_SP},
{"a", T_TOKEN_A},
{"b", T_TOKEN_B},
{"c", T_TOKEN_C},
{"d", T_TOKEN_D},
{"e", T_TOKEN_E},
{"h", T_TOKEN_H},
{"l", T_TOKEN_L},
{"||", T_OP_LOGICOR},
{"&&", T_OP_LOGICAND},
{"==", T_OP_LOGICEQU},
{">", T_OP_LOGICGT},
{"<", T_OP_LOGICLT},
{">=", T_OP_LOGICGE},
{"<=", T_OP_LOGICLE},
{"!=", T_OP_LOGICNE},
{"!", T_OP_LOGICNOT},
{"|", T_OP_OR},
{"^", T_OP_XOR},
{"&", T_OP_AND},
{"<<", T_OP_SHL},
{">>", T_OP_SHR},
{"+", T_OP_ADD},
{"-", T_OP_SUB},
{"*", T_OP_MUL},
{"/", T_OP_DIV},
{"%", T_OP_MOD},
{"~", T_OP_NOT},
{"def", T_OP_DEF},
{"fragment", T_POP_FRAGMENT},
{"bank", T_OP_BANK},
{"align", T_OP_ALIGN},
{"round", T_OP_ROUND},
{"ceil", T_OP_CEIL},
{"floor", T_OP_FLOOR},
{"div", T_OP_FDIV},
{"mul", T_OP_FMUL},
{"sin", T_OP_SIN},
{"cos", T_OP_COS},
{"tan", T_OP_TAN},
{"asin", T_OP_ASIN},
{"acos", T_OP_ACOS},
{"atan", T_OP_ATAN},
{"atan2", T_OP_ATAN2},
{"high", T_OP_HIGH},
{"low", T_OP_LOW},
{"isconst", T_OP_ISCONST},
{"strcmp", T_OP_STRCMP},
{"strin", T_OP_STRIN},
{"strsub", T_OP_STRSUB},
{"strlen", T_OP_STRLEN},
{"strcat", T_OP_STRCAT},
{"strupr", T_OP_STRUPR},
{"strlwr", T_OP_STRLWR},
{"include", T_POP_INCLUDE},
{"printt", T_POP_PRINTT},
{"printi", T_POP_PRINTI},
{"printv", T_POP_PRINTV},
{"printf", T_POP_PRINTF},
{"export", T_POP_EXPORT},
{"xdef", T_POP_XDEF},
{"global", T_POP_GLOBAL},
{"ds", T_POP_DS},
{"db", T_POP_DB},
{"dw", T_POP_DW},
{"dl", T_POP_DL},
{"section", T_POP_SECTION},
{"purge", T_POP_PURGE},
{"rsreset", T_POP_RSRESET},
{"rsset", T_POP_RSSET},
{"incbin", T_POP_INCBIN},
{"charmap", T_POP_CHARMAP},
{"newcharmap", T_POP_NEWCHARMAP},
{"setcharmap", T_POP_SETCHARMAP},
{"pushc", T_POP_PUSHC},
{"popc", T_POP_POPC},
{"fail", T_POP_FAIL},
{"warn", T_POP_WARN},
{"fatal", T_POP_FATAL},
{"assert", T_POP_ASSERT},
{"static_assert", T_POP_STATIC_ASSERT},
{"macro", T_POP_MACRO},
/* Not needed but we have it here just to protect the name */
{"endm", T_POP_ENDM},
{"shift", T_POP_SHIFT},
{"rept", T_POP_REPT},
/* Not needed but we have it here just to protect the name */
{"endr", T_POP_ENDR},
{"load", T_POP_LOAD},
{"endl", T_POP_ENDL},
{"if", T_POP_IF},
{"else", T_POP_ELSE},
{"elif", T_POP_ELIF},
{"endc", T_POP_ENDC},
{"union", T_POP_UNION},
{"nextu", T_POP_NEXTU},
{"endu", T_POP_ENDU},
{"wram0", T_SECT_WRAM0},
{"vram", T_SECT_VRAM},
{"romx", T_SECT_ROMX},
{"rom0", T_SECT_ROM0},
{"hram", T_SECT_HRAM},
{"wramx", T_SECT_WRAMX},
{"sram", T_SECT_SRAM},
{"oam", T_SECT_OAM},
{"rb", T_POP_RB},
{"rw", T_POP_RW},
{"equ", T_POP_EQU},
{"equs", T_POP_EQUS},
/* Handled before in list of CPU instructions */
/* {"set", T_POP_SET}, */
{"=", T_POP_EQUAL},
{"pushs", T_POP_PUSHS},
{"pops", T_POP_POPS},
{"pusho", T_POP_PUSHO},
{"popo", T_POP_POPO},
{"opt", T_POP_OPT},
{NULL, 0}
};
const struct sLexFloat tNumberToken = {
ParseNumber,
T_NUMBER
};
const struct sLexFloat tFixedPointToken = {
ParseFixedPoint,
T_NUMBER
};
const struct sLexFloat tIDToken = {
ParseSymbol,
T_ID
};
const struct sLexFloat tMacroArgToken = {
PutMacroArg,
T_LEX_MACROARG
};
const struct sLexFloat tMacroUniqueToken = {
PutUniqueID,
T_LEX_MACROUNIQUE
};
void setup_lexer(void)
{
uint32_t id;
lex_Init();
lex_AddStrings(lexer_strings);
//Macro arguments
id = lex_FloatAlloc(&tMacroArgToken);
lex_FloatAddFirstRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '1', '9');
id = lex_FloatAlloc(&tMacroUniqueToken);
lex_FloatAddFirstRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '@', '@');
//Decimal constants
id = lex_FloatAlloc(&tNumberToken);
lex_FloatAddFirstRange(id, '0', '9');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddRange(id, '0', '9');
//Binary constants
id = lex_FloatAlloc(&tNumberToken);
nBinaryID = id;
lex_FloatAddFirstRange(id, '%', '%');
lex_FloatAddSecondRange(id, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddSecondRange(id, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
lex_FloatAddRange(id, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddRange(id, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
//Octal constants
id = lex_FloatAlloc(&tNumberToken);
lex_FloatAddFirstRange(id, '&', '&');
lex_FloatAddSecondRange(id, '0', '7');
lex_FloatAddRange(id, '0', '7');
//Gameboy gfx constants
id = lex_FloatAlloc(&tNumberToken);
nGBGfxID = id;
lex_FloatAddFirstRange(id, '`', '`');
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[0], CurrentOptions.gbgfx[0]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[1], CurrentOptions.gbgfx[1]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[2], CurrentOptions.gbgfx[2]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[3], CurrentOptions.gbgfx[3]);
//Hex constants
id = lex_FloatAlloc(&tNumberToken);
lex_FloatAddFirstRange(id, '$', '$');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddSecondRange(id, 'A', 'F');
lex_FloatAddSecondRange(id, 'a', 'f');
lex_FloatAddRange(id, '0', '9');
lex_FloatAddRange(id, 'A', 'F');
lex_FloatAddRange(id, 'a', 'f');
//ID 's
id = lex_FloatAlloc(&tIDToken);
lex_FloatAddFirstRange(id, 'a', 'z');
lex_FloatAddFirstRange(id, 'A', 'Z');
lex_FloatAddFirstRange(id, '_', '_');
lex_FloatAddSecondRange(id, '.', '.');
lex_FloatAddSecondRange(id, 'a', 'z');
lex_FloatAddSecondRange(id, 'A', 'Z');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddSecondRange(id, '_', '_');
lex_FloatAddSecondRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '@', '@');
lex_FloatAddSecondRange(id, '#', '#');
lex_FloatAddRange(id, '.', '.');
lex_FloatAddRange(id, 'a', 'z');
lex_FloatAddRange(id, 'A', 'Z');
lex_FloatAddRange(id, '0', '9');
lex_FloatAddRange(id, '_', '_');
lex_FloatAddRange(id, '\\', '\\');
lex_FloatAddRange(id, '@', '@');
lex_FloatAddRange(id, '#', '#');
//Local ID
id = lex_FloatAlloc(&tIDToken);
lex_FloatAddFirstRange(id, '.', '.');
lex_FloatAddSecondRange(id, 'a', 'z');
lex_FloatAddSecondRange(id, 'A', 'Z');
lex_FloatAddSecondRange(id, '_', '_');
lex_FloatAddRange(id, 'a', 'z');
lex_FloatAddRange(id, 'A', 'Z');
lex_FloatAddRange(id, '0', '9');
lex_FloatAddRange(id, '_', '_');
lex_FloatAddRange(id, '\\', '\\');
lex_FloatAddRange(id, '@', '@');
lex_FloatAddRange(id, '#', '#');
// "@"
id = lex_FloatAlloc(&tIDToken);
lex_FloatAddFirstRange(id, '@', '@');
//Fixed point constants
id = lex_FloatAlloc(&tFixedPointToken);
lex_FloatAddFirstRange(id, '.', '.');
lex_FloatAddFirstRange(id, '0', '9');
lex_FloatAddSecondRange(id, '.', '.');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddRange(id, '.', '.');
lex_FloatAddRange(id, '0', '9');
}

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,8 @@ struct MacroArgs {
sizeof(((struct MacroArgs){0}).args[0]) * (nbArgs)) sizeof(((struct MacroArgs){0}).args[0]) * (nbArgs))
static struct MacroArgs *macroArgs = NULL; static struct MacroArgs *macroArgs = NULL;
static uint32_t uniqueID = -1; static uint32_t uniqueID = 0;
static uint32_t maxUniqueID = 0;
/* /*
* The initialization is somewhat harmful, since it is never used, but it * The initialization is somewhat harmful, since it is never used, but it
* guarantees the size of the buffer will be correct. I was unable to find a * guarantees the size of the buffer will be correct. I was unable to find a
@@ -61,7 +62,7 @@ void macro_AppendArg(struct MacroArgs **argPtr, char *s)
#define macArgs (*argPtr) #define macArgs (*argPtr)
if (macArgs->nbArgs == MAXMACROARGS) if (macArgs->nbArgs == MAXMACROARGS)
error("A maximum of " EXPAND_AND_STR(MAXMACROARGS) error("A maximum of " EXPAND_AND_STR(MAXMACROARGS)
" arguments is allowed\n"); " arguments is allowed\n");
if (macArgs->nbArgs >= macArgs->capacity) { if (macArgs->nbArgs >= macArgs->capacity) {
macArgs->capacity *= 2; macArgs->capacity *= 2;
/* Check that overflow didn't roll us back */ /* Check that overflow didn't roll us back */
@@ -88,6 +89,9 @@ void macro_FreeArgs(struct MacroArgs *args)
char const *macro_GetArg(uint32_t i) char const *macro_GetArg(uint32_t i)
{ {
if (!macroArgs)
return NULL;
uint32_t realIndex = i + macroArgs->shift - 1; uint32_t realIndex = i + macroArgs->shift - 1;
return realIndex >= macroArgs->nbArgs ? NULL return realIndex >= macroArgs->nbArgs ? NULL
@@ -107,15 +111,23 @@ char const *macro_GetUniqueIDStr(void)
void macro_SetUniqueID(uint32_t id) void macro_SetUniqueID(uint32_t id)
{ {
uniqueID = id; uniqueID = id;
if (id == -1) { if (id == 0) {
uniqueIDPtr = NULL; uniqueIDPtr = NULL;
} else { } else {
if (uniqueID > maxUniqueID)
maxUniqueID = uniqueID;
/* The buffer is guaranteed to be the correct size */ /* The buffer is guaranteed to be the correct size */
sprintf(uniqueIDBuf, "_%" PRIu32, id); sprintf(uniqueIDBuf, "_%" PRIu32, id);
uniqueIDPtr = uniqueIDBuf; uniqueIDPtr = uniqueIDBuf;
} }
} }
uint32_t macro_UseNewUniqueID(void)
{
macro_SetUniqueID(++maxUniqueID);
return maxUniqueID;
}
void macro_ShiftCurrentArgs(void) void macro_ShiftCurrentArgs(void)
{ {
if (macroArgs->shift != macroArgs->nbArgs) if (macroArgs->shift != macroArgs->nbArgs)

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <float.h> #include <float.h>
#include <inttypes.h> #include <inttypes.h>
@@ -22,8 +23,10 @@
#include "asm/lexer.h" #include "asm/lexer.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/output.h" #include "asm/output.h"
#include "asm/rpn.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/warning.h" #include "asm/warning.h"
#include "asmy.h"
#include "extern/err.h" #include "extern/err.h"
#include "extern/getopt.h" #include "extern/getopt.h"
@@ -31,8 +34,6 @@
#include "helpers.h" #include "helpers.h"
#include "version.h" #include "version.h"
extern int yyparse(void);
size_t cldefines_index; size_t cldefines_index;
size_t cldefines_numindices; size_t cldefines_numindices;
size_t cldefines_bufsize; size_t cldefines_bufsize;
@@ -41,10 +42,6 @@ char **cldefines;
clock_t nStartClock, nEndClock; clock_t nStartClock, nEndClock;
uint32_t nTotalLines, nIFDepth; uint32_t nTotalLines, nIFDepth;
bool skipElif;
uint32_t unionStart[128], unionSize[128];
int32_t nLineNo;
#if defined(YYDEBUG) && YYDEBUG #if defined(YYDEBUG) && YYDEBUG
extern int yydebug; extern int yydebug;
@@ -74,66 +71,11 @@ struct sOptionStackEntry {
struct sOptionStackEntry *pOptionStack; struct sOptionStackEntry *pOptionStack;
void opt_SetCurrentOptions(struct sOptions *pOpt) void opt_SetCurrentOptions(struct sOptions *opt)
{ {
if (nGBGfxID != -1) { CurrentOptions = *opt;
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[0], lexer_SetGfxDigits(CurrentOptions.gbgfx);
CurrentOptions.gbgfx[0]); lexer_SetBinDigits(CurrentOptions.binary);
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
}
if (nBinaryID != -1) {
lex_FloatDeleteRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatDeleteRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
lex_FloatDeleteSecondRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatDeleteSecondRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
}
CurrentOptions = *pOpt;
if (nGBGfxID != -1) {
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
}
if (nBinaryID != -1) {
lex_FloatAddRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
lex_FloatAddSecondRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddSecondRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
}
} }
void opt_Parse(char *s) void opt_Parse(char *s)
@@ -251,6 +193,22 @@ static void opt_ParseDefines(void)
sym_AddString(cldefines[i], cldefines[i + 1]); sym_AddString(cldefines[i], cldefines[i + 1]);
} }
void upperstring(char *s)
{
while (*s) {
*s = toupper(*s);
s++;
}
}
void lowerstring(char *s)
{
while (*s) {
*s = tolower(*s);
s++;
}
}
/* Escapes Make-special chars from a string */ /* Escapes Make-special chars from a string */
static char *make_escape(const char *str) static char *make_escape(const char *str)
{ {
@@ -350,11 +308,11 @@ int main(int argc, char *argv[])
yydebug = 1; yydebug = 1;
#endif #endif
nMaxRecursionDepth = 64;
oGeneratePhonyDeps = false; oGeneratePhonyDeps = false;
oGeneratedMissingIncludes = false; oGeneratedMissingIncludes = false;
oFailedOnMissingInclude = false; oFailedOnMissingInclude = false;
tzTargetFileName = NULL; tzTargetFileName = NULL;
uint32_t maxRecursionDepth = 64;
size_t nTargetFileNameLen = 0; size_t nTargetFileNameLen = 0;
DefaultOptions.gbgfx[0] = '0'; DefaultOptions.gbgfx[0] = '0';
@@ -433,7 +391,7 @@ int main(int argc, char *argv[])
break; break;
case 'r': case 'r':
nMaxRecursionDepth = strtoul(optarg, &ep, 0); maxRecursionDepth = strtoul(optarg, &ep, 0);
if (optarg[0] == '\0' || *ep != '\0') if (optarg[0] == '\0' || *ep != '\0')
errx(1, "Invalid argument for option 'r'"); errx(1, "Invalid argument for option 'r'");
@@ -516,8 +474,6 @@ int main(int argc, char *argv[])
tzMainfile = argv[argc - 1]; tzMainfile = argv[argc - 1];
setup_lexer();
if (verbose) if (verbose)
printf("Assembling %s\n", tzMainfile); printf("Assembling %s\n", tzMainfile);
@@ -528,19 +484,20 @@ int main(int argc, char *argv[])
fprintf(dependfile, "%s: %s\n", tzTargetFileName, tzMainfile); fprintf(dependfile, "%s: %s\n", tzTargetFileName, tzMainfile);
} }
/* Init file stack; important to do first, since it provides the file name, line, etc */
lexer_Init();
fstk_Init(tzMainfile, maxRecursionDepth);
nStartClock = clock(); nStartClock = clock();
nLineNo = 1;
nTotalLines = 0; nTotalLines = 0;
nIFDepth = 0; nIFDepth = 0;
skipElif = true;
sym_Init(); sym_Init();
sym_SetExportAll(exportall); sym_SetExportAll(exportall);
fstk_Init(tzMainfile);
opt_ParseDefines(); opt_ParseDefines();
charmap_New("main", NULL); charmap_New("main", NULL);
yy_set_state(LEX_STATE_NORMAL);
opt_SetCurrentOptions(&DefaultOptions); opt_SetCurrentOptions(&DefaultOptions);
if (yyparse() != 0 || nbErrors != 0) if (yyparse() != 0 || nbErrors != 0)

View File

@@ -12,6 +12,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@@ -33,7 +34,8 @@
#include "platform.h" // strdup #include "platform.h" // strdup
struct Patch { struct Patch {
char tzFilename[_MAX_PATH + 1]; struct FileStackNode const *src;
uint32_t lineNo;
uint32_t nOffset; uint32_t nOffset;
struct Section *pcSection; struct Section *pcSection;
uint32_t pcOffset; uint32_t pcOffset;
@@ -62,19 +64,17 @@ static uint32_t nbSymbols = 0; /* Length of the above list */
static struct Assertion *assertions = NULL; static struct Assertion *assertions = NULL;
static struct FileStackNode *fileStackNodes = NULL;
/* /*
* Count the number of sections used in this object * Count the number of sections used in this object
*/ */
static uint32_t countsections(void) static uint32_t countsections(void)
{ {
struct Section *sect;
uint32_t count = 0; uint32_t count = 0;
sect = pSectionList; for (struct Section const *sect = pSectionList; sect; sect = sect->next)
while (sect) {
count++; count++;
sect = sect->next;
}
return count; return count;
} }
@@ -129,16 +129,60 @@ static void fputstring(char const *s, FILE *f)
fputc(0, 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 * Return a section's ID
*/ */
static uint32_t getsectid(struct Section const *sect) static uint32_t getsectid(struct Section const *sect)
{ {
struct Section const *sec; struct Section const *sec = pSectionList;
uint32_t ID = 0; uint32_t ID = 0;
sec = pSectionList;
while (sec) { while (sec) {
if (sec == sect) if (sec == sect)
return ID; return ID;
@@ -159,7 +203,10 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
*/ */
static void writepatch(struct Patch const *patch, FILE *f) 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(patch->nOffset, f);
fputlong(getSectIDIfAny(patch->pcSection), f); fputlong(getSectIDIfAny(patch->pcSection), f);
fputlong(patch->pcOffset, f); fputlong(patch->pcOffset, f);
@@ -206,26 +253,35 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
if (!sym_IsDefined(sym)) { if (!sym_IsDefined(sym)) {
fputc(SYMTYPE_IMPORT, f); fputc(SYMTYPE_IMPORT, f);
} else { } else {
assert(sym->src->ID != -1);
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f); fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
fputstring(sym->fileName, f); fputlong(sym->src->ID, f);
fputlong(sym->fileLine, f); fputlong(sym->fileLine, f);
fputlong(getSectIDIfAny(sym_GetSection(sym)), f); fputlong(getSectIDIfAny(sym_GetSection(sym)), f);
fputlong(sym->value, 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 * Returns a symbol's ID within the object file
* If the symbol does not have one, one is assigned by registering the symbol * If the symbol does not have one, one is assigned by registering the symbol
*/ */
static uint32_t getSymbolID(struct Symbol *sym) static uint32_t getSymbolID(struct Symbol *sym)
{ {
if (sym->ID == -1) { if (sym->ID == -1 && !sym_IsPC(sym))
sym->ID = nbSymbols++; registerSymbol(sym);
*objectSymbolsTail = sym;
objectSymbolsTail = &sym->next;
}
return sym->ID; 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 * 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, static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
uint32_t ofs)
{ {
struct Patch *patch = malloc(sizeof(struct Patch)); struct Patch *patch = malloc(sizeof(struct Patch));
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize; uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
struct FileStackNode *node = fstk_GetFileStack();
if (!patch) if (!patch)
fatalerror("No memory for patch: %s\n", strerror(errno)); 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) if (!patch->pRPN)
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno)); fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
patch->type = type; patch->type = type;
fstk_DumpToStr(patch->tzFilename, sizeof(patch->tzFilename)); patch->src = node;
out_RegisterNode(node);
patch->lineNo = lexer_GetLineNo();
patch->nOffset = ofs; patch->nOffset = ofs;
patch->pcSection = sect_GetSymbolSection(); patch->pcSection = sect_GetSymbolSection();
patch->pcOffset = sect_GetSymbolOffset(); patch->pcOffset = sect_GetSymbolOffset();
@@ -382,13 +441,28 @@ static void writeassert(struct Assertion *assert, FILE *f)
fputstring(assert->message, 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) static void registerExportedSymbol(struct Symbol *symbol, void *arg)
{ {
(void)arg; (void)arg;
if (sym_IsExported(symbol) && symbol->ID == -1) { if (sym_IsExported(symbol) && symbol->ID == -1) {
*objectSymbolsTail = symbol; registerSymbol(symbol);
objectSymbolsTail = &symbol->next;
nbSymbols++;
} }
} }
@@ -411,6 +485,15 @@ void out_WriteObject(void)
fputlong(nbSymbols, f); fputlong(nbSymbols, f);
fputlong(countsections(), 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) for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
writesymbol(sym, f); writesymbol(sym, f);

View File

@@ -258,8 +258,8 @@ static int32_t shift(int32_t shiftee, int32_t amount)
if (amount >= 0) { if (amount >= 0) {
// Left shift // Left shift
if (amount >= 32) { if (amount >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %"
amount); PRId32 "\n", amount);
return 0; return 0;
} else { } else {

View File

@@ -656,9 +656,15 @@ void out_BinaryFile(char const *s, int32_t startPos)
startPos = 0; startPos = 0;
} }
FILE *f = fstk_FindFile(s, NULL); char *fullPath = NULL;
size_t size = 0;
FILE *f = NULL;
if (fstk_FindFile(s, &fullPath, &size))
f = fopen(fullPath, "rb");
if (!f) { if (!f) {
free(fullPath);
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
@@ -699,6 +705,7 @@ void out_BinaryFile(char const *s, int32_t startPos)
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno)); error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
fclose(f); fclose(f);
free(fullPath);
} }
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length) void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
@@ -715,9 +722,15 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
if (length == 0) /* Don't even bother with 0-byte slices */ if (length == 0) /* Don't even bother with 0-byte slices */
return; return;
FILE *f = fstk_FindFile(s, NULL); char *fullPath = NULL;
size_t size = 0;
FILE *f = NULL;
if (fstk_FindFile(s, &fullPath, &size))
f = fopen(fullPath, "rb");
if (!f) { if (!f) {
free(fullPath);
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
@@ -767,6 +780,7 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
} }
fclose(f); fclose(f);
free(fullPath);
} }
/* /*

View File

@@ -23,6 +23,7 @@
#include "asm/macro.h" #include "asm/macro.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/mymath.h" #include "asm/mymath.h"
#include "asm/output.h"
#include "asm/section.h" #include "asm/section.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/util.h" #include "asm/util.h"
@@ -77,12 +78,55 @@ void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg)
static int32_t Callback_NARG(void) static int32_t Callback_NARG(void)
{ {
if (!macro_GetCurrentArgs()) {
error("_NARG does not make sense outside of a macro\n");
return 0;
}
return macro_NbArgs(); return macro_NbArgs();
} }
static int32_t Callback__LINE__(void) static int32_t Callback__LINE__(void)
{ {
return nLineNo; return lexer_GetLineNo();
}
static char const *Callback__FILE__(void)
{
/*
* FIXME: this is dangerous, and here's why this is CURRENTLY okay. It's still bad, fix it.
* There are only two call sites for this; one copies the contents directly, the other is
* EQUS expansions, which cannot straddle file boundaries. So this should be fine.
*/
static char *buf = NULL;
static size_t bufsize = 0;
char const *fileName = fstk_GetFileName();
size_t j = 1;
/* TODO: is there a way for a file name to be empty? */
assert(fileName[0]);
/* The assertion above ensures the loop runs at least once */
for (size_t i = 0; fileName[i]; i++, j++) {
/* Account for the extra backslash inserted below */
if (fileName[i] == '"')
j++;
/* Ensure there will be enough room; DO NOT PRINT ANYTHING ABOVE THIS!! */
if (j + 2 >= bufsize) { /* Always keep room for 2 tail chars */
bufsize = bufsize ? bufsize * 2 : 64;
buf = realloc(buf, bufsize);
if (!buf)
fatalerror("Failed to grow buffer for file name: %s\n",
strerror(errno));
}
/* Escape quotes, since we're returning a string */
if (fileName[i] == '"')
buf[j - 1] = '\\';
buf[j] = fileName[i];
}
/* Write everything after the loop, to ensure the buffer has been allocated */
buf[0] = '"';
buf[j++] = '"';
buf[j] = '\0';
return buf;
} }
static int32_t CallbackPC(void) static int32_t CallbackPC(void)
@@ -97,8 +141,8 @@ static int32_t CallbackPC(void)
*/ */
int32_t sym_GetValue(struct Symbol const *sym) int32_t sym_GetValue(struct Symbol const *sym)
{ {
if (sym_IsNumeric(sym) && sym->callback) if (sym_IsNumeric(sym) && sym->hasCallback)
return sym->callback(); return sym->numCallback();
if (sym->type == SYM_LABEL) if (sym->type == SYM_LABEL)
/* TODO: do not use section's org directly */ /* TODO: do not use section's org directly */
@@ -107,15 +151,35 @@ int32_t sym_GetValue(struct Symbol const *sym)
return sym->value; 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 * Update a symbol's definition filename and line
*/ */
static void updateSymbolFilename(struct Symbol *sym) static void updateSymbolFilename(struct Symbol *sym)
{ {
if (snprintf(sym->fileName, _MAX_PATH + 1, "%s", struct FileStackNode *oldSrc = sym->src;
tzCurrentFileName) > _MAX_PATH)
fatalerror("%s: File name is too long: '%s'\n", __func__, tzCurrentFileName); setSymbolFilename(sym);
sym->fileLine = fstk_GetLine(); /* 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 */
} }
/* /*
@@ -133,8 +197,9 @@ static struct Symbol *createsymbol(char const *s)
symbol->isExported = false; symbol->isExported = false;
symbol->isBuiltin = false; symbol->isBuiltin = false;
symbol->hasCallback = false;
symbol->section = NULL; symbol->section = NULL;
updateSymbolFilename(symbol); setSymbolFilename(symbol);
symbol->ID = -1; symbol->ID = -1;
symbol->next = NULL; symbol->next = NULL;
@@ -209,8 +274,7 @@ void sym_Purge(char const *symName)
labelScope = NULL; labelScope = NULL;
hash_RemoveElement(symbols, symbol->name); hash_RemoveElement(symbols, symbol->name);
if (symbol->type == SYM_MACRO) /* TODO: ideally, also unref the file stack nodes */
free(symbol->macro);
free(symbol); free(symbol);
} }
} }
@@ -229,7 +293,22 @@ uint32_t sym_GetPCValue(void)
} }
/* /*
* Return a constant symbols value * Return a constant symbol's value, assuming it's defined
*/
uint32_t sym_GetConstantSymValue(struct Symbol const *sym)
{
if (sym == PCSymbol)
return sym_GetPCValue();
else if (!sym_IsConstant(sym))
error("\"%s\" does not have a constant value\n", sym->name);
else
return sym_GetValue(sym);
return 0;
}
/*
* Return a constant symbol's value
*/ */
uint32_t sym_GetConstantValue(char const *s) uint32_t sym_GetConstantValue(char const *s)
{ {
@@ -237,12 +316,8 @@ uint32_t sym_GetConstantValue(char const *s)
if (sym == NULL) if (sym == NULL)
error("'%s' not defined\n", s); error("'%s' not defined\n", s);
else if (sym == PCSymbol)
return sym_GetPCValue();
else if (!sym_IsConstant(sym))
error("\"%s\" does not have a constant value\n", s);
else else
return sym_GetValue(sym); return sym_GetConstantSymValue(sym);
return 0; return 0;
} }
@@ -285,9 +360,11 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
if (!symbol) if (!symbol)
symbol = createsymbol(symbolName); symbol = createsymbol(symbolName);
else if (sym_IsDefined(symbol)) else if (sym_IsDefined(symbol)) {
error("'%s' already defined at %s(%" PRIu32 ")\n", symbolName, error("'%s' already defined at ", symbolName);
symbol->fileName, symbol->fileLine); dumpFilename(symbol);
putc('\n', stderr);
}
return symbol; return symbol;
} }
@@ -300,7 +377,6 @@ struct Symbol *sym_AddEqu(char const *symName, int32_t value)
struct Symbol *sym = createNonrelocSymbol(symName); struct Symbol *sym = createNonrelocSymbol(symName);
sym->type = SYM_EQU; sym->type = SYM_EQU;
sym->callback = NULL;
sym->value = value; sym->value = value;
return sym; return sym;
@@ -343,18 +419,19 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
{ {
struct Symbol *sym = findsymbol(symName, NULL); struct Symbol *sym = findsymbol(symName, NULL);
if (sym == NULL) if (sym == NULL) {
sym = createsymbol(symName); sym = createsymbol(symName);
else if (sym_IsDefined(sym) && sym->type != SYM_SET) } else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
error("'%s' already defined as %s at %s(%" PRIu32 ")\n", error("'%s' already defined as %s at ",
symName, sym->type == SYM_LABEL ? "label" : "constant", symName, sym->type == SYM_LABEL ? "label" : "constant");
sym->fileName, sym->fileLine); dumpFilename(sym);
else putc('\n', stderr);
/* TODO: can the scope be incorrect when talking over refs? */ } else {
/* TODO: can the scope be incorrect when taking over refs? */
updateSymbolFilename(sym); updateSymbolFilename(sym);
}
sym->type = SYM_SET; sym->type = SYM_SET;
sym->callback = NULL;
sym->value = value; sym->value = value;
return sym; return sym;
@@ -365,7 +442,7 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
* @param name The label's full name (so `.name` is invalid) * @param name The label's full name (so `.name` is invalid)
* @return The created symbol * @return The created symbol
*/ */
static struct Symbol *addSectionlessLabel(char const *name) static struct Symbol *addLabel(char const *name)
{ {
assert(name[0] != '.'); /* The symbol name must have been expanded prior */ assert(name[0] != '.'); /* The symbol name must have been expanded prior */
struct Symbol *sym = findsymbol(name, NULL); /* Due to this, don't look for expansions */ struct Symbol *sym = findsymbol(name, NULL); /* Due to this, don't look for expansions */
@@ -373,25 +450,19 @@ static struct Symbol *addSectionlessLabel(char const *name)
if (!sym) { if (!sym) {
sym = createsymbol(name); sym = createsymbol(name);
} else if (sym_IsDefined(sym)) { } else if (sym_IsDefined(sym)) {
error("'%s' already defined in %s(%" PRIu32 ")\n", error("'%s' already defined at ", name);
name, sym->fileName, sym->fileLine); dumpFilename(sym);
putc('\n', stderr);
return NULL; return NULL;
} else {
updateSymbolFilename(sym);
} }
/* If the symbol already exists as a ref, just "take over" it */ /* If the symbol already exists as a ref, just "take over" it */
sym->type = SYM_LABEL; sym->type = SYM_LABEL;
sym->callback = NULL;
sym->value = sect_GetSymbolOffset(); sym->value = sect_GetSymbolOffset();
if (exportall) if (exportall)
sym->isExported = true; sym->isExported = true;
sym->section = sect_GetSymbolSection(); sym->section = sect_GetSymbolSection();
updateSymbolFilename(sym);
return sym;
}
static struct Symbol *addLabel(char const *name)
{
struct Symbol *sym = addSectionlessLabel(name);
if (sym && !sym->section) if (sym && !sym->section)
error("Label \"%s\" created outside of a SECTION\n", name); error("Label \"%s\" created outside of a SECTION\n", name);
@@ -467,14 +538,14 @@ void sym_Export(char const *symName)
/* /*
* Add a macro definition * Add a macro definition
*/ */
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo) struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size)
{ {
struct Symbol *sym = createNonrelocSymbol(symName); struct Symbol *sym = createNonrelocSymbol(symName);
sym->type = SYM_MACRO; sym->type = SYM_MACRO;
sym->macroSize = ulNewMacroSize; sym->macroSize = size;
sym->macro = tzNewMacro; sym->macro = body;
updateSymbolFilename(sym); setSymbolFilename(sym); /* TODO: is this really necessary? */
/* /*
* The symbol is created at the line after the `endm`, * The symbol is created at the line after the `endm`,
* override this with the actual definition line * override this with the actual definition line
@@ -528,21 +599,36 @@ static inline char const *removeLeadingZeros(char const *ptr)
return ptr; return ptr;
} }
static inline struct Symbol *createBuiltinSymbol(char const *name)
{
struct Symbol *sym = createsymbol(name);
sym->isBuiltin = true;
sym->hasCallback = true;
sym->src = NULL;
sym->fileLine = 0;
return sym;
}
/* /*
* Initialize the symboltable * Initialize the symboltable
*/ */
void sym_Init(void) void sym_Init(void)
{ {
struct Symbol *_NARGSymbol = sym_AddEqu("_NARG", 0); PCSymbol = createBuiltinSymbol("@");
struct Symbol *__LINE__Symbol = sym_AddEqu("__LINE__", 0); struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG");
struct Symbol *__LINE__Symbol = createBuiltinSymbol("__LINE__");
struct Symbol *__FILE__Symbol = createBuiltinSymbol("__FILE__");
PCSymbol = addSectionlessLabel("@"); PCSymbol->type = SYM_LABEL;
PCSymbol->isBuiltin = true; PCSymbol->section = NULL;
PCSymbol->callback = CallbackPC; PCSymbol->numCallback = CallbackPC;
_NARGSymbol->isBuiltin = true; _NARGSymbol->type = SYM_EQU;
_NARGSymbol->callback = Callback_NARG; _NARGSymbol->numCallback = Callback_NARG;
__LINE__Symbol->isBuiltin = true; __LINE__Symbol->type = SYM_EQU;
__LINE__Symbol->callback = Callback__LINE__; __LINE__Symbol->numCallback = Callback__LINE__;
__FILE__Symbol->type = SYM_EQUS;
__FILE__Symbol->strCallback = Callback__FILE__;
sym_AddSet("_RS", 0)->isBuiltin = true; sym_AddSet("_RS", 0)->isBuiltin = true;
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true; sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <ctype.h>
#include <stdint.h> #include <stdint.h>
#include "asm/main.h" #include "asm/main.h"
@@ -27,6 +28,40 @@ uint32_t calchash(const char *s)
return hash; return hash;
} }
char const *print(int c)
{
static char buf[5]; /* '\xNN' + '\0' */
if (c == EOF)
return "EOF";
if (isprint(c)) {
buf[0] = c;
buf[1] = '\0';
return buf;
}
buf[0] = '\\';
switch (c) {
case '\n':
buf[1] = 'n';
break;
case '\r':
buf[1] = 'r';
break;
case '\t':
buf[1] = 't';
break;
default: /* Print as hex */
buf[1] = 'x';
sprintf(&buf[2], "%02hhx", c);
return buf;
}
buf[2] = '\0';
return buf;
}
size_t readUTF8Char(uint8_t *dest, char const *src) size_t readUTF8Char(uint8_t *dest, char const *src)
{ {
uint32_t state = 0; uint32_t state = 0;

View File

@@ -198,14 +198,14 @@ void processWarningFlag(char const *flag)
warnx("Unknown warning `%s`", flag); warnx("Unknown warning `%s`", flag);
} }
void verror(const char *fmt, va_list args, char const *flag) void printDiag(const char *fmt, va_list args, char const *type,
char const *flagfmt, char const *flag)
{ {
fputs("ERROR: ", stderr); fputs(type, stderr);
fstk_Dump(); fstk_DumpCurrent();
fprintf(stderr, flag ? ": [-Werror=%s]\n " : ":\n ", flag); fprintf(stderr, flagfmt, flag);
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
fstk_DumpStringExpansions(); lexer_DumpStringExpansions();
nbErrors++;
} }
void error(const char *fmt, ...) void error(const char *fmt, ...)
@@ -213,8 +213,9 @@ void error(const char *fmt, ...)
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
verror(fmt, args, NULL); printDiag(fmt, args, "ERROR: ", ":\n ", NULL);
va_end(args); va_end(args);
nbErrors++;
} }
noreturn_ void fatalerror(const char *fmt, ...) noreturn_ void fatalerror(const char *fmt, ...)
@@ -222,7 +223,7 @@ noreturn_ void fatalerror(const char *fmt, ...)
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
verror(fmt, args, NULL); printDiag(fmt, args, "FATAL: ", ":\n ", NULL);
va_end(args); va_end(args);
exit(1); exit(1);
@@ -240,7 +241,7 @@ void warning(enum WarningID id, char const *fmt, ...)
return; return;
case WARNING_ERROR: case WARNING_ERROR:
verror(fmt, args, flag); printDiag(fmt, args, "ERROR: ", ": [-Werror=%s]\n ", flag);
va_end(args); va_end(args);
return; return;
@@ -252,11 +253,7 @@ void warning(enum WarningID id, char const *fmt, ...)
break; break;
} }
fputs("warning: ", stderr); printDiag(fmt, args, "warning: ", ": [-W%s]\n ", flag);
fstk_Dump();
fprintf(stderr, ": [-W%s]\n ", flag);
vfprintf(stderr, fmt, args);
fstk_DumpStringExpansions();
va_end(args); va_end(args);
} }

View File

@@ -81,14 +81,14 @@ static void processLinkerScript(void)
/* Check if this doesn't conflict with what the code says */ /* Check if this doesn't conflict with what the code says */
if (section->isBankFixed && placement->bank != section->bank) 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); section->name);
if (section->isAddressFixed && placement->org != section->org) 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); section->name);
if (section->isAlignFixed if (section->isAlignFixed
&& (placement->org & section->alignMask) != 0) && (placement->org & section->alignMask) != 0)
error("Linker script contradicts \"%s\"'s alignment", error(NULL, 0, "Linker script contradicts \"%s\"'s alignment",
section->name); section->name);
section->isAddressFixed = true; section->isAddressFixed = true;

View File

@@ -6,8 +6,10 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@@ -39,11 +41,55 @@ bool disablePadding; /* -x */
static uint32_t nbErrors = 0; 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; 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); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
@@ -53,11 +99,15 @@ void error(char const *fmt, ...)
nbErrors++; nbErrors++;
} }
noreturn_ void fatal(char const *fmt, ...) noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{ {
va_list ap; va_list ap;
fprintf(stderr, "fatal: "); fputs("fatal: ", stderr);
if (where) {
dumpFileStack(where);
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
}
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
@@ -177,11 +227,11 @@ int main(int argc, char *argv[])
case 'p': case 'p':
value = strtoul(optarg, &endptr, 0); value = strtoul(optarg, &endptr, 0);
if (optarg[0] == '\0' || *endptr != '\0') { if (optarg[0] == '\0' || *endptr != '\0') {
error("Invalid argument for option 'p'"); error(NULL, 0, "Invalid argument for option 'p'");
value = 0xFF; value = 0xFF;
} }
if (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; value = 0xFF;
} }
padValue = value; padValue = value;
@@ -189,7 +239,7 @@ int main(int argc, char *argv[])
case 's': case 's':
/* FIXME: nobody knows what this does, figure it out */ /* FIXME: nobody knows what this does, figure it out */
(void)optarg; (void)optarg;
warnx("Nobody has any idea what `-s` does"); warning(NULL, 0, "Nobody has any idea what `-s` does");
break; break;
case 't': case 't':
is32kMode = true; is32kMode = true;
@@ -234,8 +284,8 @@ int main(int argc, char *argv[])
bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM; bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM;
/* Read all object files first, */ /* Read all object files first, */
while (curArgIndex < argc) for (obj_Setup(argc - curArgIndex); curArgIndex < argc; curArgIndex++)
obj_ReadFile(argv[curArgIndex++]); obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1);
/* then process them, */ /* then process them, */
obj_DoSanityChecks(); obj_DoSanityChecks();

View File

@@ -31,6 +31,11 @@ static struct SymbolList {
struct SymbolList *next; struct SymbolList *next;
} *symbolLists; } *symbolLists;
unsigned int nbObjFiles;
static struct {
struct FileStackNode *nodes;
uint32_t nbNodes;
} *nodes;
static struct Assertion *assertions; static struct Assertion *assertions;
/***** Helper functions for reading object files *****/ /***** Helper functions for reading object files *****/
@@ -170,12 +175,56 @@ static char *readstr(FILE *file)
/***** Functions to parse object files *****/ /***** 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 file The file to read from
* @param symbol The struct to fill * @param symbol The struct to fill
* @param fileName The filename to report in errors * @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", tryReadstr(symbol->name, file, "%s: Cannot read symbol name: %s",
fileName); 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 the symbol is defined in this file, read its definition */
if (symbol->type != SYMTYPE_IMPORT) { if (symbol->type != SYMTYPE_IMPORT) {
symbol->objFileName = fileName; symbol->objFileName = fileName;
tryReadstr(symbol->fileName, file, uint32_t nodeID;
"%s: Cannot read \"%s\"'s file name: %s",
tryReadlong(nodeID, file,
"%s: Cannot read \"%s\"'s node ID: %s",
fileName, symbol->name); fileName, symbol->name);
symbol->src = &fileNodes[nodeID];
tryReadlong(symbol->lineNo, file, tryReadlong(symbol->lineNo, file,
"%s: Cannot read \"%s\"'s line number: %s", "%s: Cannot read \"%s\"'s line number: %s",
fileName, symbol->name); 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 file The file to read from
* @param patch The struct to fill * @param patch The struct to fill
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
@@ -210,20 +262,25 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
*/ */
static void readPatch(FILE *file, struct Patch *patch, char const *fileName, static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
char const *sectName, uint32_t i, char const *sectName, uint32_t i,
struct Section *fileSections[]) struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
tryReadstr(patch->fileName, file, uint32_t nodeID;
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s name: %s",
tryReadlong(nodeID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
fileName, sectName, i); 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, tryReadlong(patch->offset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
fileName, sectName, i); fileName, sectName, i);
tryReadlong(patch->pcSectionID, file, tryReadlong(patch->pcSectionID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i); fileName, sectName, i);
patch->pcSection = patch->pcSectionID == -1 patch->pcSection = patch->pcSectionID == -1 ? NULL
? NULL : fileSections[patch->pcSectionID];
: fileSections[patch->pcSectionID];
tryReadlong(patch->pcOffset, file, tryReadlong(patch->pcOffset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i); fileName, sectName, i);
@@ -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", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName, sectName, i); fileName, sectName, i);
uint8_t *rpnExpression = patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
malloc(sizeof(*rpnExpression) * patch->rpnSize); if (!patch->rpnExpression)
size_t nbElementsRead = fread(rpnExpression, sizeof(*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); patch->rpnSize, file);
if (nbElementsRead != patch->rpnSize) if (nbElementsRead != patch->rpnSize)
errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s", errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName, sectName, i, fileName, sectName, i,
feof(file) ? "Unexpected end of file" : strerror(errno)); 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 section The struct to fill
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readSection(FILE *file, struct Section *section, static void readSection(FILE *file, struct Section *section, char const *fileName,
char const *fileName, struct Section *fileSections[]) struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
int32_t tmp; int32_t tmp;
uint8_t byte; uint8_t byte;
@@ -280,7 +338,7 @@ static void readSection(FILE *file, struct Section *section,
fileName, section->name); fileName, section->name);
section->isAddressFixed = tmp >= 0; section->isAddressFixed = tmp >= 0;
if (tmp > UINT16_MAX) { 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); section->name, tmp);
tmp = UINT16_MAX; 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", tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
fileName, section->name); fileName, section->name);
if (tmp > UINT16_MAX) { 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); section->name, tmp);
tmp = UINT16_MAX; tmp = UINT16_MAX;
} }
@@ -332,7 +390,7 @@ static void readSection(FILE *file, struct Section *section,
section->name); section->name);
for (uint32_t i = 0; i < section->nbPatches; i++) { for (uint32_t i = 0; i < section->nbPatches; i++) {
readPatch(file, &patches[i], fileName, section->name, readPatch(file, &patches[i], fileName, section->name,
i, fileSections); i, fileSections, fileNodes);
} }
section->patches = patches; 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, static void readAssertion(FILE *file, struct Assertion *assert,
char const *fileName, uint32_t i, char const *fileName, uint32_t i,
struct Section *fileSections[]) struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))]; char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i); 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", tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
fileName); fileName);
} }
@@ -394,11 +452,7 @@ static inline struct Section *getMainSection(struct Section *section)
return section; return section;
} }
/** void obj_ReadFile(char const *fileName, unsigned int fileID)
* Reads an object file of any supported format
* @param fileName The filename to report for errors
*/
void obj_ReadFile(char const *fileName)
{ {
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin; FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
@@ -438,6 +492,14 @@ void obj_ReadFile(char const *fileName)
nbSectionsToAssign += nbSections; 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 */ /* This file's symbols, kept to link sections to them */
struct Symbol **fileSymbols = struct Symbol **fileSymbols =
malloc(sizeof(*fileSymbols) * nbSymbols + 1); malloc(sizeof(*fileSymbols) * nbSymbols + 1);
@@ -464,7 +526,7 @@ void obj_ReadFile(char const *fileName)
if (!symbol) if (!symbol)
err(1, "%s: Couldn't create new symbol", fileName); err(1, "%s: Couldn't create new symbol", fileName);
readSymbol(file, symbol, fileName); readSymbol(file, symbol, fileName, nodes[fileID].nodes);
fileSymbols[i] = symbol; fileSymbols[i] = symbol;
if (symbol->type == SYMTYPE_EXPORT) if (symbol->type == SYMTYPE_EXPORT)
@@ -485,7 +547,7 @@ void obj_ReadFile(char const *fileName)
err(1, "%s: Couldn't create new section", fileName); err(1, "%s: Couldn't create new section", fileName);
fileSections[i]->nextu = NULL; fileSections[i]->nextu = NULL;
readSection(file, fileSections[i], fileName, fileSections); readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes);
fileSections[i]->fileSymbols = fileSymbols; fileSections[i]->fileSymbols = fileSymbols;
if (nbSymPerSect[i]) { if (nbSymPerSect[i]) {
fileSections[i]->symbols = malloc(nbSymPerSect[i] fileSections[i]->symbols = malloc(nbSymPerSect[i]
@@ -535,7 +597,7 @@ void obj_ReadFile(char const *fileName)
if (!assertion) if (!assertion)
err(1, "%s: Couldn't create new assertion", fileName); 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->fileSymbols = fileSymbols;
assertion->next = assertions; assertion->next = assertions;
assertions = assertion; assertions = assertion;
@@ -555,6 +617,15 @@ void obj_CheckAssertions(void)
patch_CheckAssertions(assertions); 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) static void freeSection(struct Section *section, void *arg)
{ {
(void)arg; (void)arg;
@@ -562,12 +633,8 @@ static void freeSection(struct Section *section, void *arg)
free(section->name); free(section->name);
if (sect_HasData(section->type)) { if (sect_HasData(section->type)) {
free(section->data); free(section->data);
for (int32_t i = 0; i < section->nbPatches; i++) { for (int32_t i = 0; i < section->nbPatches; i++)
struct Patch *patch = &section->patches[i]; free(section->patches[i].rpnExpression);
free(patch->fileName);
free(patch->rpnExpression);
}
free(section->patches); free(section->patches);
} }
free(section->symbols); free(section->symbols);
@@ -577,13 +644,20 @@ static void freeSection(struct Section *section, void *arg)
static void freeSymbol(struct Symbol *symbol) static void freeSymbol(struct Symbol *symbol)
{ {
free(symbol->name); free(symbol->name);
if (symbol->type != SYMTYPE_IMPORT)
free(symbol->fileName);
free(symbol); free(symbol);
} }
void obj_Cleanup(void) 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(); sym_CleanupSymbols();
sect_ForEach(freeSection, NULL); sect_ForEach(freeSection, NULL);

View File

@@ -6,11 +6,13 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "link/object.h"
#include "link/patch.h" #include "link/patch.h"
#include "link/section.h" #include "link/section.h"
#include "link/symbol.h" #include "link/symbol.h"
@@ -104,10 +106,10 @@ static void pushRPN(int32_t value)
stack.size++; stack.size++;
} }
static int32_t popRPN(char const *fileName) static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
{ {
if (stack.size == 0) if (stack.size == 0)
errx(1, "%s: Internal error, RPN stack empty", fileName); fatal(node, lineNo, "Internal error, RPN stack empty");
stack.size--; stack.size--;
return stack.buf[stack.size]; return stack.buf[stack.size];
@@ -121,16 +123,18 @@ static inline void freeRPNStack(void)
/* RPN operators */ /* RPN operators */
static uint32_t getRPNByte(uint8_t const **expression, int32_t *size, static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
char const *fileName) struct FileStackNode const *node, uint32_t lineNo)
{ {
if (!(*size)--) if (!(*size)--)
errx(1, "%s: RPN expression overread", fileName); fatal(node, lineNo, "Internal error, RPN expression overread");
return *(*expression)++; return *(*expression)++;
} }
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList, static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
uint32_t index) uint32_t index)
{ {
assert(index != -1); /* PC needs to be handled specially, not here */
struct Symbol const *symbol = symbolList[index]; struct Symbol const *symbol = symbolList[index];
/* If the symbol is defined elsewhere... */ /* If the symbol is defined elsewhere... */
@@ -150,7 +154,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
struct Symbol const * const *fileSymbols) struct Symbol const * const *fileSymbols)
{ {
/* Small shortcut to avoid a lot of repetition */ /* 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; uint8_t const *expression = patch->rpnExpression;
int32_t size = patch->rpnSize; int32_t size = patch->rpnSize;
@@ -159,7 +163,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
while (size > 0) { while (size > 0) {
enum RPNCommand command = getRPNByte(&expression, &size, enum RPNCommand command = getRPNByte(&expression, &size,
patch->fileName); patch->src, patch->lineNo);
int32_t value; int32_t value;
/* /*
@@ -187,7 +191,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_DIV: case RPN_DIV:
value = popRPN(); value = popRPN();
if (value == 0) { if (value == 0) {
error("%s: Division by 0", patch->fileName); error(patch->src, patch->lineNo, "Division by 0");
popRPN(); popRPN();
value = INT32_MAX; value = INT32_MAX;
} else { } else {
@@ -197,7 +201,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_MOD: case RPN_MOD:
value = popRPN(); value = popRPN();
if (value == 0) { if (value == 0) {
error("%s: Modulo by 0", patch->fileName); error(patch->src, patch->lineNo, "Modulo by 0");
popRPN(); popRPN();
value = 0; value = 0;
} else { } else {
@@ -269,17 +273,17 @@ static int32_t computeRPNExpr(struct Patch const *patch,
value = 0; value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->src, patch->lineNo) << shift;
symbol = getSymbol(fileSymbols, value); symbol = getSymbol(fileSymbols, value);
if (!symbol) { if (!symbol) {
error("%s: Requested BANK() of symbol \"%s\", which was not found", error(patch->src, patch->lineNo,
patch->fileName, "Requested BANK() of symbol \"%s\", which was not found",
fileSymbols[value]->name); fileSymbols[value]->name);
value = 1; value = 1;
} else if (!symbol->section) { } else if (!symbol->section) {
error("%s: Requested BANK() of non-label symbol \"%s\"", error(patch->src, patch->lineNo,
patch->fileName, "Requested BANK() of non-label symbol \"%s\"",
fileSymbols[value]->name); fileSymbols[value]->name);
value = 1; value = 1;
} else { } else {
@@ -289,14 +293,15 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_BANK_SECT: case RPN_BANK_SECT:
name = (char const *)expression; name = (char const *)expression;
while (getRPNByte(&expression, &size, patch->fileName)) while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
; ;
sect = sect_GetSection(name); sect = sect_GetSection(name);
if (!sect) { if (!sect) {
error("%s: Requested BANK() of section \"%s\", which was not found", error(patch->src, patch->lineNo,
patch->fileName, name); "Requested BANK() of section \"%s\", which was not found",
name);
value = 1; value = 1;
} else { } else {
value = sect->bank; value = sect->bank;
@@ -305,7 +310,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_BANK_SELF: case RPN_BANK_SELF:
if (!patch->pcSection) { 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; value = 1;
} else { } else {
value = patch->pcSection->bank; value = patch->pcSection->bank;
@@ -317,8 +323,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
if (value < 0 if (value < 0
|| (value > 0xFF && value < 0xFF00) || (value > 0xFF && value < 0xFF00)
|| value > 0xFFFF) || value > 0xFFFF)
error("%s: Value %" PRId32 " is not in HRAM range", error(patch->src, patch->lineNo,
patch->fileName, value); "Value %" PRId32 " is not in HRAM range", value);
value &= 0xFF; value &= 0xFF;
break; break;
@@ -328,8 +334,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
* They can be easily checked with a bitmask * They can be easily checked with a bitmask
*/ */
if (value & ~0x38) if (value & ~0x38)
error("%s: Value %" PRId32 " is not a RST vector", error(patch->src, patch->lineNo,
patch->fileName, value); "Value %" PRId32 " is not a RST vector", value);
value |= 0xC7; value |= 0xC7;
break; break;
@@ -337,32 +343,35 @@ static int32_t computeRPNExpr(struct Patch const *patch,
value = 0; value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->src, patch->lineNo) << shift;
break; break;
case RPN_SYM: case RPN_SYM:
value = 0; value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->src, patch->lineNo) << shift;
symbol = getSymbol(fileSymbols, value); if (value == -1) { /* PC */
if (!patch->pcSection) {
if (!symbol) { error(patch->src, patch->lineNo,
error("%s: Unknown symbol \"%s\"", "PC has no value outside a section");
patch->fileName, value = 0;
fileSymbols[value]->name); } else {
} else if (strcmp(symbol->name, "@")) { value = patch->pcOffset + patch->pcSection->org;
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 { } else {
value = patch->pcOffset + patch->pcSection->org; symbol = getSymbol(fileSymbols, value);
if (!symbol) {
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;
}
} }
break; break;
} }
@@ -371,8 +380,8 @@ static int32_t computeRPNExpr(struct Patch const *patch,
} }
if (stack.size > 1) if (stack.size > 1)
error("%s: RPN stack has %zu entries on exit, not 1", error(patch->src, patch->lineNo,
patch->fileName, stack.size); "RPN stack has %zu entries on exit, not 1", stack.size);
return popRPN(); return popRPN();
@@ -390,20 +399,20 @@ void patch_CheckAssertions(struct Assertion *assert)
assert->fileSymbols)) { assert->fileSymbols)) {
switch ((enum AssertionType)assert->patch.type) { switch ((enum AssertionType)assert->patch.type) {
case ASSERT_FATAL: case ASSERT_FATAL:
fatal("%s: %s", assert->patch.fileName, fatal(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
/* Not reached */ /* Not reached */
break; /* Here so checkpatch doesn't complain */ break; /* Here so checkpatch doesn't complain */
case ASSERT_ERROR: case ASSERT_ERROR:
error("%s: %s", assert->patch.fileName, error(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
break; break;
case ASSERT_WARN: case ASSERT_WARN:
warnx("%s: %s", assert->patch.fileName, warning(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
break; break;
} }
} }
@@ -442,8 +451,9 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
int16_t jumpOffset = value - address; int16_t jumpOffset = value - address;
if (jumpOffset < -128 || jumpOffset > 127) if (jumpOffset < -128 || jumpOffset > 127)
error("%s: jr target out of reach (expected -129 < %" PRId16 " < 128)", error(patch->src, patch->lineNo,
patch->fileName, jumpOffset); "jr target out of reach (expected -129 < %" PRId16 " < 128)",
jumpOffset);
dataSection->data[offset] = jumpOffset & 0xFF; dataSection->data[offset] = jumpOffset & 0xFF;
} else { } else {
/* Patch a certain number of bytes */ /* 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 if (value < types[patch->type].min
|| value > types[patch->type].max) || value > types[patch->type].max)
error("%s: Value %#" PRIx32 "%s is not %u-bit", error(patch->src, patch->lineNo,
patch->fileName, value, "Value %#" PRIx32 "%s is not %u-bit",
value < 0 ? " (maybe negative?)" : "", value, value < 0 ? " (maybe negative?)" : "",
types[patch->type].size * 8U); types[patch->type].size * 8U);
for (uint8_t i = 0; i < types[patch->type].size; i++) { for (uint8_t i = 0; i < types[patch->type].size; i++) {
dataSection->data[offset + i] = value & 0xFF; dataSection->data[offset + i] = value & 0xFF;

View File

@@ -8,9 +8,12 @@
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include "link/object.h"
#include "link/symbol.h" #include "link/symbol.h"
#include "link/main.h" #include "link/main.h"
#include "extern/err.h" #include "extern/err.h"
#include "hashmap.h" #include "hashmap.h"
@@ -40,11 +43,15 @@ void sym_AddSymbol(struct Symbol *symbol)
/* Check if the symbol already exists */ /* Check if the symbol already exists */
struct Symbol *other = hash_GetElement(symbols, symbol->name); struct Symbol *other = hash_GetElement(symbols, symbol->name);
if (other) if (other) {
errx(1, "\"%s\" both in %s from %s(%" PRId32 ") and in %s from %s(%" PRId32 ")", fprintf(stderr, "error: \"%s\" both in %s from ", symbol->name, symbol->objFileName);
symbol->name, dumpFileStack(symbol->src);
symbol->objFileName, symbol->fileName, symbol->lineNo, fprintf(stderr, "(%" PRIu32 ") and in %s from ",
other->objFileName, other->fileName, other->lineNo); symbol->lineNo, other->objFileName);
dumpFileStack(other->src);
fprintf(stderr, "(%" PRIu32 ")\n", other->lineNo);
exit(1);
}
/* If not, add it */ /* If not, add it */
bool collided = hash_AddElement(symbols, symbol->name, symbol); bool collided = hash_AddElement(symbols, symbol->name, symbol);

View File

@@ -16,7 +16,7 @@ This is the description of the object files used by
.Xr rgbasm 1 .Xr rgbasm 1
and and
.Xr rgblink 1 . .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. 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 .Pp
.Sh FILE STRUCTURE .Sh FILE STRUCTURE
@@ -34,34 +34,67 @@ is a 0terminated string of
; Header ; Header
BYTE ID[4] ; "RGB9" BYTE ID[4] ; "RGB9"
LONG RevisionNumber ; The format's revision number this file uses LONG RevisionNumber ; The format's revision number this file uses.
LONG NumberOfSymbols ; The number of symbols used in this file LONG NumberOfSymbols ; The number of symbols used in this file.
LONG NumberOfSections ; The number of sections 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 ; Symbols
REPT NumberOfSymbols ; Number of symbols defined in this object file. REPT NumberOfSymbols ; Number of symbols defined in this object file.
STRING Name ; The name of this symbol. Local symbols are stored STRING Name ; The name of this symbol. Local symbols are stored
; as "Scope.Symbol". ; as "Scope.Symbol".
BYTE Type ; 0 = LOCAL symbol only used in this file. BYTE Type ; 0 = LOCAL symbol only used in this file.
; 1 = IMPORT this symbol from elsewhere ; 1 = IMPORT this symbol from elsewhere
; 2 = EXPORT this symbol to other objects. ; 2 = EXPORT this symbol to other objects.
IF (Type & 0x7F) != 1 ; If symbol is 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. LONG LineNum ; Line number in the file where the symbol is defined.
LONG SectionID ; The section number (of this object file) in which LONG SectionID ; The section number (of this object file) in which
; this symbol is defined. If it doesn't belong to any ; this symbol is defined. If it doesn't belong to any
; specific section (like a constant), this field has ; specific section (like a constant), this field has
; the value -1. ; the value -1.
LONG Value ; The symbols value. It's the offset into that LONG Value ; The symbols value. It's the offset into that
; symbol's section. ; symbol's section.
ENDC ENDC
@@ -107,8 +140,10 @@ REPT NumberOfSections
REPT NumberOfPatches REPT NumberOfPatches
STRING SourceFile ; Name of the source file (for printing error LONG SourceFile ; ID of the source file node (for printing
; messages). ; error messages).
LONG LineNo ; Line at which the patch was created.
LONG Offset ; Offset into the section where patch should LONG Offset ; Offset into the section where patch should
; be applied (in bytes). ; be applied (in bytes).
@@ -145,7 +180,9 @@ LONG NumberOfAssertions
REPT 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. 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) , .It Li $50 Ta Li BANK(symbol) ,
a a
.Ar LONG .Ar LONG
Symbol ID follows. Symbol ID follows, where -1 means PC
.It Li $51 Ta Li BANK(section_name) , .It Li $51 Ta Li BANK(section_name) ,
a null-terminated string follows. a null-terminated string follows.
.It Li $52 Ta Li Current BANK() .It Li $52 Ta Li Current BANK()

View File

@@ -6,5 +6,5 @@ ERROR: assert.asm(18):
Expected constant expression: 'FloatingBase' is not constant at assembly time Expected constant expression: 'FloatingBase' is not constant at assembly time
ERROR: assert.asm(18): ERROR: assert.asm(18):
Assertion failed Assertion failed
ERROR: assert.asm(21): FATAL: assert.asm(21):
Assertion failed Assertion failed

View File

@@ -1,2 +1,2 @@
ERROR: divzero-instr.asm(2): FATAL: divzero-instr.asm(2):
Division by zero Division by zero

View File

@@ -1,2 +1,2 @@
ERROR: divzero-section-bank.asm(1): FATAL: divzero-section-bank.asm(1):
Division by zero Division by zero

4
test/asm/equs-nest.asm Normal file
View File

@@ -0,0 +1,4 @@
X1 equs "Y1 equs \"\\\"Success!\\\\n\\\"\""
Y1 equs "Z1"
X1
PRINTT Z1

0
test/asm/equs-nest.err Normal file
View File

1
test/asm/equs-nest.out Normal file
View File

@@ -0,0 +1 @@
Success!

View File

@@ -0,0 +1,4 @@
ACT equs "WARN \"First\"\nWARN \"Second\""
ACT
WARN "Third"

View File

@@ -0,0 +1,7 @@
warning: equs-newline.asm(3): [-Wuser]
First
while expanding symbol "ACT"
warning: equs-newline.asm(3): [-Wuser]
Second
warning: equs-newline.asm(4): [-Wuser]
Third

View File

2
test/asm/equs-purge.asm Normal file
View File

@@ -0,0 +1,2 @@
BYE equs "PURGE BYE\nWARN \"Crash?\"\n \n"
BYE

3
test/asm/equs-purge.err Normal file
View File

@@ -0,0 +1,3 @@
warning: equs-purge.asm(2): [-Wuser]
Crash?
while expanding symbol "BYE"

0
test/asm/equs-purge.out Normal file
View File

View File

@@ -1,2 +1,6 @@
recurse EQUS "recurse" recurse EQUS "recurse "
recurse recurse
; FIXME: also handle the following:
; recurse EQUS "recurse"
; recurse

View File

@@ -1,4 +1,4 @@
ERROR: equs-recursion.asm(2): FATAL: equs-recursion.asm(2):
Recursion limit (64) exceeded Recursion limit (64) exceeded
while expanding symbol "recurse" while expanding symbol "recurse"
while expanding symbol "recurse" while expanding symbol "recurse"
@@ -64,3 +64,4 @@ while expanding symbol "recurse"
while expanding symbol "recurse" while expanding symbol "recurse"
while expanding symbol "recurse" while expanding symbol "recurse"
while expanding symbol "recurse" while expanding symbol "recurse"
while expanding symbol "recurse"

1
test/asm/file-sym.asm Normal file
View File

@@ -0,0 +1 @@
PRINTT "{__FILE__}\n"

0
test/asm/file-sym.err Normal file
View File

1
test/asm/file-sym.out Normal file
View File

@@ -0,0 +1 @@
"file-sym.asm"

View File

@@ -1 +1 @@
x<EFBFBD> <EFBFBD>

View File

@@ -1,2 +1,3 @@
ERROR: garbage_char.asm(1): ERROR: garbage_char.asm(1):
Found garbage character: 0xFF Unknown character 0xFF
error: Assembly aborted (1 errors)!

13
test/asm/if-macro.asm Normal file
View File

@@ -0,0 +1,13 @@
m: macro
if 0
WARN "3"
else
WARN "5"
endc
endm
if 1
m
else
WARN "12"
endc

2
test/asm/if-macro.err Normal file
View File

@@ -0,0 +1,2 @@
warning: if-macro.asm(10) -> if-macro.asm::m(5): [-Wuser]
5

0
test/asm/if-macro.out Normal file
View File

View File

@@ -1,2 +1,2 @@
if {@} if "{@}"
endc endc

View File

@@ -1,2 +1,2 @@
ERROR: include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1): FATAL: include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1):
Recursion limit (64) exceeded Recursion limit (64) exceeded

View File

@@ -1,3 +1,3 @@
ERROR: label-redefinition.asm(7): ERROR: label-redefinition.asm(7):
'Sym' already defined in label-redefinition.asm::m(6) 'Sym' already defined at label-redefinition.asm(6) -> label-redefinition.asm::m(4)
error: Assembly aborted (1 errors)! error: Assembly aborted (1 errors)!

View File

@@ -2,6 +2,7 @@ m: MACRO
ENDM ENDM
m2: MACRO m2: MACRO
m \ ENDM m \
ENDM
m2 m2

View File

@@ -2,7 +2,9 @@ m: MACRO
ENDM ENDM
REPT 1 REPT 1
m ENDR m
ENDR
REPT 1 REPT 1
m \ ENDR m \
ENDR

View File

@@ -1,2 +1,2 @@
ERROR: load-overflow.asm(4): FATAL: load-overflow.asm(4):
Section 'Overflow' grew too big (max size = 0x8000 bytes, reached 0x8001). Section 'Overflow' grew too big (max size = 0x8000 bytes, reached 0x8001).

View File

@@ -1,3 +1,3 @@
ERROR: local-purge.asm(8): ERROR: local-purge.asm(8):
'.loc' not defined Interpolated symbol ".loc" does not exist
error: Assembly aborted (1 errors)! error: Assembly aborted (1 errors)!

View File

@@ -1 +1 @@
$0

View File

@@ -1,2 +1,2 @@
ERROR: local-ref-without-parent.asm(3): FATAL: local-ref-without-parent.asm(3):
Local label reference '.test' in main scope Local label reference '.test' in main scope

View File

@@ -0,0 +1,8 @@
WARN "Line 2"
m: macro
WARN "Line 4"
endm
WARN "Line 6"
m
WARN "Line 8"

View File

@@ -0,0 +1,8 @@
warning: macro-line-no.asm(2): [-Wuser]
Line 2
warning: macro-line-no.asm(6): [-Wuser]
Line 6
warning: macro-line-no.asm(7) -> macro-line-no.asm::m(4): [-Wuser]
Line 4
warning: macro-line-no.asm(8): [-Wuser]
Line 8

View File

View File

@@ -1,2 +1,2 @@
ERROR: macro-recursion.asm(4) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2): FATAL: macro-recursion.asm(4) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2):
Recursion limit (64) exceeded Recursion limit (64) exceeded

1
test/asm/narg-nosect.asm Normal file
View File

@@ -0,0 +1 @@
PRINTT "{_NARG}\n"

3
test/asm/narg-nosect.err Normal file
View File

@@ -0,0 +1,3 @@
ERROR: narg-nosect.asm(1):
_NARG does not make sense outside of a macro
error: Assembly aborted (1 errors)!

1
test/asm/narg-nosect.out Normal file
View File

@@ -0,0 +1 @@
$0

View File

@@ -3,3 +3,4 @@ WRAPPER equs "TRIN"
PRINTT "{S{WRAPPER}G}\n" PRINTT "{S{WRAPPER}G}\n"
PRINTT "{S{WRAPPER}G" PRINTT "{S{WRAPPER}G"
PRINTT "\n"

View File

@@ -1,2 +1,3 @@
ERROR: nested-brackets.asm(5): ERROR: nested-brackets.asm(5):
Missing } Missing }
error: Assembly aborted (1 errors)!

View File

@@ -1 +1,2 @@
OK OK
OK

Binary file not shown.

View File

@@ -1,2 +1,3 @@
ERROR: null-in-macro.asm(2): ERROR: null-in-macro.asm(4) -> null-in-macro.asm::foo(2):
Found null character Unknown character 0x00
error: Assembly aborted (1 errors)!

3
test/asm/opt-b.asm Normal file
View File

@@ -0,0 +1,3 @@
OPT b.X
PRINTV %..X.X.X.
PRINTT "\n"

0
test/asm/opt-b.err Normal file
View File

1
test/asm/opt-b.out Normal file
View File

@@ -0,0 +1 @@
$2A

3
test/asm/opt-g.asm Normal file
View File

@@ -0,0 +1,3 @@
OPT g.x0X
PRINTV `.x.x0X0X
PRINTT "\n"

0
test/asm/opt-g.err Normal file
View File

1
test/asm/opt-g.out Normal file
View File

@@ -0,0 +1 @@
$F55

View File

@@ -3,6 +3,6 @@ warning: overflow.asm(24): [-Wdiv]
warning: overflow.asm(25): [-Wdiv] warning: overflow.asm(25): [-Wdiv]
Division of -2147483648 by -1 yields -2147483648 Division of -2147483648 by -1 yields -2147483648
warning: overflow.asm(39): [-Wlarge-constant] warning: overflow.asm(39): [-Wlarge-constant]
Integer constant '4294967296' is too large Integer constant is too large
warning: overflow.asm(42): [-Wlarge-constant] warning: overflow.asm(42): [-Wlarge-constant]
Graphics constant '`333333333' is too long Graphics constant is too long, only 8 first pixels considered

View File

@@ -1,2 +1,2 @@
ERROR: pops-no-pushed-sections.asm(1): FATAL: pops-no-pushed-sections.asm(1):
No entries in the section stack No entries in the section stack

View File

@@ -1,4 +1,4 @@
ERROR: pops-restore-no-section.asm(9): ERROR: pops-restore-no-section.asm(9):
Label "DisallowedContent" created outside of a SECTION Label "DisallowedContent" created outside of a SECTION
ERROR: pops-restore-no-section.asm(10): FATAL: pops-restore-no-section.asm(10):
Code generation before SECTION directive Code generation before SECTION directive

View File

@@ -1,2 +1,2 @@
ERROR: remote-local-noexist.asm(7): FATAL: remote-local-noexist.asm(7):
'Parent.child.fail' is a nonsensical reference to a nested local symbol 'Parent.child.fail' is a nonsensical reference to a nested local symbol

3
test/asm/rept-0.asm Normal file
View File

@@ -0,0 +1,3 @@
REPT 0
WARN "2"
ENDR

0
test/asm/rept-0.err Normal file
View File

0
test/asm/rept-0.out Normal file
View File

View File

@@ -1,2 +1,2 @@
ERROR: rept-shift.asm(17) -> rept-shift.asm::m(14): FATAL: rept-shift.asm(17) -> rept-shift.asm::m(14):
Macro argument '\1' not defined Macro argument '\1' not defined

View File

@@ -6,5 +6,5 @@ ERROR: section-union.asm(37):
Section "test" already declared as fixed at $c000 Section "test" already declared as fixed at $c000
ERROR: section-union.asm(37): ERROR: section-union.asm(37):
Section "test" already declared as aligned to 256 bytes Section "test" already declared as aligned to 256 bytes
ERROR: section-union.asm(37): FATAL: section-union.asm(37):
Cannot create section "test" (3 errors) Cannot create section "test" (3 errors)

View File

@@ -1,3 +1,3 @@
ERROR: sym-collision.asm(26): ERROR: sym-collision.asm(26):
'dork' not defined Interpolated symbol "dork" does not exist
error: Assembly aborted (1 errors)! error: Assembly aborted (1 errors)!

View File

@@ -1,7 +1,7 @@
aqfj: $FE00 aqfj: $FE00
cxje: $FE01 cxje: $FE01
dgsd: $FE02 dgsd: $FE02
dork: $0 dork:
lxok: $FE04 lxok: $FE04
psgp: $FE05 psgp: $FE05
sfly: $FE06 sfly: $FE06

View File

@@ -1,2 +1,2 @@
ERROR: symbol-invalid-macro-arg.asm(1): FATAL: symbol-invalid-macro-arg.asm(1):
Invalid macro argument '\0' in symbol Invalid macro argument '\0'

View File

@@ -12,6 +12,7 @@ rc=0
bold=$(tput bold) bold=$(tput bold)
resbold=$(tput sgr0) resbold=$(tput sgr0)
red=$(tput setaf 1) red=$(tput setaf 1)
green=$(tput setaf 2)
rescolors=$(tput op) rescolors=$(tput op)
tryDiff () { tryDiff () {
diff -u --strip-trailing-cr $1 $2 || (echo "${bold}${red}${i%.asm}${variant}.$3 mismatch!${rescolors}${resbold}"; false) diff -u --strip-trailing-cr $1 $2 || (echo "${bold}${red}${i%.asm}${variant}.$3 mismatch!${rescolors}${resbold}"; false)
@@ -36,6 +37,7 @@ fi
for i in *.asm; do for i in *.asm; do
for variant in '' '.pipe'; do for variant in '' '.pipe'; do
echo -e "${bold}${green}${i%.asm}${variant}...${rescolors}${resbold}"
if [ -z "$variant" ]; then if [ -z "$variant" ]; then
../../rgbasm -Weverything -o $o $i > $output 2> $errput ../../rgbasm -Weverything -o $o $i > $output 2> $errput
desired_output=${i%.asm}.out desired_output=${i%.asm}.out
@@ -59,8 +61,8 @@ for i in *.asm; do
# Escape regex metacharacters # Escape regex metacharacters
subst="$(printf '%s\n' "$i" | sed 's:[][\/.^$*]:\\&:g')" subst="$(printf '%s\n' "$i" | sed 's:[][\/.^$*]:\\&:g')"
# Replace the file name with a dash to match changed output # Replace the file name with a dash to match changed output
sed "s/$subst/-/g" ${i%.asm}.out > $desired_output sed "s/$subst/<stdin>/g" ${i%.asm}.out > $desired_output
sed "s/$subst/-/g" ${i%.asm}.err > $desired_errput sed "s/$subst/<stdin>/g" ${i%.asm}.err > $desired_errput
fi fi
tryDiff $desired_output $output out tryDiff $desired_output $output out

View File

@@ -1,19 +1,19 @@
warning: unique-id.asm(12) -> unique-id.asm::m(4): [-Wuser] warning: unique-id.asm(12) -> unique-id.asm::m(4): [-Wuser]
_0
warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
_1 _1
warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser] warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
_2 _2
warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
_3
warning: unique-id.asm(12) -> unique-id.asm::m(8): [-Wuser] warning: unique-id.asm(12) -> unique-id.asm::m(8): [-Wuser]
_0 _1
warning: unique-id.asm(14) -> unique-id.asm::m(4): [-Wuser] warning: unique-id.asm(14) -> unique-id.asm::m(4): [-Wuser]
_3
warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
_4 _4
warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser] warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
_5 _5
warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
_6
warning: unique-id.asm(14) -> unique-id.asm::m(8): [-Wuser] warning: unique-id.asm(14) -> unique-id.asm::m(8): [-Wuser]
_3 _4
ERROR: unique-id.asm(15): FATAL: unique-id.asm(15):
Macro argument '\@' not defined Macro argument '\@' not defined
while expanding symbol "print" while expanding symbol "print"

View File

@@ -1,6 +1,6 @@
error: Section "conflicting alignment" is defined with conflicting 4-byte alignment (offset 0) and address $cafe error: Section "conflicting alignment" is defined with conflicting 4-byte alignment (offset 0) and address $cafe
--- ---
ERROR: -(18): ERROR: <stdin>(18):
Section "conflicting alignment" already declared as aligned to 4 bytes (offset 0) Section "conflicting alignment" already declared as aligned to 4 bytes (offset 0)
ERROR: -(18): FATAL: <stdin>(18):
Cannot create section "conflicting alignment" (1 errors) Cannot create section "conflicting alignment" (1 errors)

View File

@@ -1,6 +1,6 @@
error: Section "conflicting alignment" is defined with conflicting 8-byte alignment (offset 7) and 16-byte alignment (offset 14) error: Section "conflicting alignment" is defined with conflicting 8-byte alignment (offset 7) and 16-byte alignment (offset 14)
--- ---
ERROR: -(18): ERROR: <stdin>(18):
Section "conflicting alignment" already declared with incompatible 3-byte alignment (offset 7) Section "conflicting alignment" already declared with incompatible 3-byte alignment (offset 7)
ERROR: -(18): FATAL: <stdin>(18):
Cannot create section "conflicting alignment" (1 errors) Cannot create section "conflicting alignment" (1 errors)

Some files were not shown because too many files have changed in this diff Show More