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)
else()
if(DEVELOP)
add_compile_options(-Werror -Wall -Wextra -pedantic
add_compile_options(-Werror -Wall -Wextra -pedantic -Wno-type-limits
-Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5

View File

@@ -56,7 +56,6 @@ rgbasm_obj := \
src/asm/asmy.o \
src/asm/charmap.o \
src/asm/fstack.o \
src/asm/globlex.o \
src/asm/lexer.o \
src/asm/macro.o \
src/asm/main.o \
@@ -73,7 +72,7 @@ rgbasm_obj := \
src/hashmap.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 := \
src/link/assign.o \
@@ -187,7 +186,7 @@ checkpatch:
# compilation and make the continous integration infrastructure return failure.
develop:
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic \
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
-Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2 \
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \
@@ -199,7 +198,7 @@ develop:
-fsanitize=unreachable -fsanitize=vla-bound \
-fsanitize=signed-integer-overflow -fsanitize=bounds \
-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.
# This is not for Windows users!

View File

@@ -24,15 +24,8 @@
#define MAXMACROARGS 99999
#define MAXINCPATHS 128
extern int32_t nLineNo;
extern uint32_t nTotalLines;
extern uint32_t nIFDepth;
extern bool skipElif;
extern char tzCurrentFileName[_MAX_PATH + 1];
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 */

View File

@@ -21,36 +21,59 @@
#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 {
YY_BUFFER_STATE FlexHandle;
struct Symbol const *pMacro;
struct sContext *next;
char tzFileName[_MAX_PATH + 1];
struct MacroArgs *macroArgs;
uint32_t uniqueID;
int32_t nLine;
uint32_t nStatus;
FILE *pFile;
char *pREPTBlock;
uint32_t nREPTBlockCount;
uint32_t nREPTBlockSize;
int32_t nREPTBodyFirstLine;
int32_t nREPTBodyLastLine;
struct FileStackNode *next; /* Next node in the output linked list */
bool referenced; /* If referenced, don't free! */
uint32_t ID; /* Set only if referenced: ID within the object file, -1 if not output yet */
enum {
NODE_REPT,
NODE_FILE,
NODE_MACRO,
} type;
};
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);
void fstk_Init(char *s);
void fstk_Dump(void);
void fstk_DumpToStr(char *buf, size_t len);
void fstk_DumpStringExpansions(void);
void fstk_AddIncludePath(char *s);
void fstk_RunMacro(char *s, struct MacroArgs *args);
void fstk_RunRept(uint32_t count, int32_t nReptLineNo);
FILE *fstk_FindFile(char const *fname, char **incPathUsed);
int32_t fstk_GetLine(void);
struct FileStackNamedNode { /* NODE_FILE, NODE_MACRO */
struct FileStackNode node;
char name[]; /* File name for files, file::macro name for macros */
};
extern size_t nMaxRecursionDepth;
struct MacroArgs;
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo);
void fstk_DumpCurrent(void);
struct FileStackNode *fstk_GetFileStack(void);
/* The lifetime of the returned chars is until reaching the end of that file */
char const *fstk_GetFileName(void);
void fstk_AddIncludePath(char const *s);
/**
* @param path The user-provided file name
* @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 */

View File

@@ -9,78 +9,65 @@
#ifndef RGBDS_ASM_LEXER_H
#define RGBDS_ASM_LEXER_H
#include <stdint.h>
#include <stdio.h>
#define LEXHASHSIZE (1 << 11)
#define MAXSTRLEN 255
struct sLexInitString {
char *tzName;
uint32_t nToken;
struct LexerState;
extern struct LexerState *lexerState;
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 {
uint32_t (*Callback)(char *s, uint32_t size);
uint32_t nToken;
};
void lexer_SetMode(enum LexerMode mode);
void lexer_ToggleStringExpansion(bool enable);
struct yy_buffer_state {
/* Actual starting address */
char *pBufferRealStart;
/* Address where the data is initially written after a safety margin */
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);
char const *lexer_GetFileName(void);
uint32_t lexer_GetLineNo(void);
uint32_t lexer_GetColNo(void);
void lexer_DumpStringExpansions(void);
int yylex(void);
void yyunput(char c);
void yyunputstr(const char *s);
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);
void lexer_CaptureRept(char **capture, size_t *size);
void lexer_CaptureMacroBody(char **capture, size_t *size);
#endif /* RGBDS_ASM_LEXER_H */

View File

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

View File

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

View File

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

View File

@@ -35,18 +35,21 @@ struct Symbol {
bool isExported; /* Whether the symbol is to be exported */
bool isBuiltin; /* Whether the symbol is a built-in */
struct Section *section;
char fileName[_MAX_PATH + 1]; /* File where the symbol was defined. */
uint32_t fileLine; /* Line where the symbol was defined. */
struct FileStackNode *src; /* Where the symbol was defined */
uint32_t fileLine; /* Line where the symbol was defined */
bool hasCallback;
union {
struct { /* If sym_IsNumeric */
/* If sym_IsNumeric */
int32_t value;
int32_t (*callback)(void);
};
struct { /* For SYM_MACRO */
uint32_t macroSize;
int32_t (*numCallback)(void);
/* For SYM_MACRO */
struct {
size_t macroSize;
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) */
@@ -101,6 +104,8 @@ static inline bool sym_IsExported(struct Symbol const *sym)
*/
static inline char const *sym_GetStringValue(struct Symbol const *sym)
{
if (sym->hasCallback)
return sym->strCallback();
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_AddSet(char const *symName, int32_t value);
uint32_t sym_GetPCValue(void);
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
uint32_t sym_GetConstantValue(char const *s);
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_AddString(char const *symName, char const *value);
uint32_t sym_GetDefinedValue(char const *s);

View File

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

View File

@@ -29,15 +29,45 @@ extern bool beVerbose;
extern bool isWRA0Mode;
extern bool disablePadding;
struct FileStackNode {
struct FileStackNode *parent;
/* Line at which the parent context was exited; meaningless for the root level */
uint32_t lineNo;
enum {
NODE_REPT,
NODE_FILE,
NODE_MACRO,
} type;
union {
char *name; /* NODE_FILE, NODE_MACRO */
struct { /* NODE_REPT */
uint32_t reptDepth;
uint32_t *iters;
};
};
};
/* Helper macro for printing verbose-mode messages */
#define verbosePrint(...) do { \
if (beVerbose) \
fprintf(stderr, __VA_ARGS__); \
} while (0)
void error(char const *fmt, ...);
/**
* Dump a file stack to stderr
* @param node The leaf node to dump the context of
*/
char const *dumpFileStack(struct FileStackNode const *node);
noreturn_ void fatal(char const *fmt, ...);
void warning(struct FileStackNode const *where, uint32_t lineNo,
char const *fmt, ...) format_(printf, 3, 4);
void error(struct FileStackNode const *where, uint32_t lineNo,
char const *fmt, ...) format_(printf, 3, 4);
noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo,
char const *fmt, ...) format_(printf, 3, 4);
/**
* Opens a file if specified, and aborts on error.

View File

@@ -14,8 +14,9 @@
/**
* Read an object (.o) file, and add its info to the data structures.
* @param fileName A path to the object file to be read
* @param i The ID of the file
*/
void obj_ReadFile(char const *fileName);
void obj_ReadFile(char const *fileName, unsigned int i);
/**
* Perform validation on the object files' contents
@@ -27,6 +28,12 @@ void obj_DoSanityChecks(void);
*/
void obj_CheckAssertions(void);
/**
* Sets up object file reading
* @param nbFiles The number of object files that will be read
*/
void obj_Setup(unsigned int nbFiles);
/**
* `free`s all object memory that was allocated.
*/

View File

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

View File

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

View File

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

View File

@@ -32,4 +32,11 @@
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#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 */

View File

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

View File

@@ -39,62 +39,7 @@ uint32_t nListCountEmpty;
char *tzNewMacro;
uint32_t ulNewMacroSize;
int32_t nPCOffset;
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;
}
bool executeElseBlock; /* If this is set, ELIFs cannot be executed anymore */
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;
}
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)
{
size_t len = 0;
@@ -659,17 +332,70 @@ lines : /* empty */
| lines {
nListCountEmpty = 0;
nPCOffset = 0;
} line '\n' {
nLineNo++;
} line {
nTotalLines++;
}
;
line : label
| label cpu_command
| label macro
| label simple_pseudoop
| pseudoop
line : label '\n'
| label cpu_command '\n'
| label macro '\n'
| label simple_pseudoop '\n'
| 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 ;
@@ -699,9 +425,9 @@ label : /* empty */
;
macro : T_ID {
yy_set_state(LEX_STATE_MACROARGS);
lexer_SetMode(LEXER_RAW);
} macroargs {
yy_set_state(LEX_STATE_NORMAL);
lexer_SetMode(LEXER_NORMAL);
fstk_RunMacro($1, $3);
}
;
@@ -732,10 +458,6 @@ simple_pseudoop : include
| printt
| printv
| printi
| if
| elif
| else
| endc
| export
| db
| dw
@@ -786,9 +508,9 @@ align : T_OP_ALIGN uconst {
;
opt : T_POP_OPT {
yy_set_state(LEX_STATE_MACROARGS);
lexer_SetMode(LEXER_RAW);
} 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 {
uint32_t nDefinitionLineNo = nLineNo;
copyrept();
fstk_RunRept($2, nDefinitionLineNo);
uint32_t nDefinitionLineNo = lexer_GetLineNo();
char *body;
size_t size;
lexer_CaptureRept(&body, &size);
fstk_RunRept($2, nDefinitionLineNo, body, size);
}
;
macrodef : T_LABEL ':' T_POP_MACRO {
int32_t nDefinitionLineNo = nLineNo;
copymacro();
sym_AddMacro($1, nDefinitionLineNo);
int32_t nDefinitionLineNo = lexer_GetLineNo();
char *body;
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 {
oDontExpandStrings = true;
lexer_ToggleStringExpansion(false);
} 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); }
;
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 {
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_DEF {
oDontExpandStrings = true;
lexer_ToggleStringExpansion(false);
} '(' scoped_id ')' {
struct Symbol const *sym = sym_FindSymbol($4);
rpn_Number(&$$, !!sym);
oDontExpandStrings = false;
lexer_ToggleStringExpansion(true);
}
| T_OP_ROUND '(' const ')' {
rpn_Number(&$$, math_Round($3));

View File

@@ -6,554 +6,470 @@
* SPDX-License-Identifier: MIT
*/
/*
* FileStack routines
*/
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "asm/fstack.h"
#include "asm/lexer.h"
#include "asm/macro.h"
#include "asm/main.h"
#include "asm/output.h"
#include "asm/symbol.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)
#include "types.h"
struct Context {
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 unsigned int nFileStackDepth;
unsigned int nMaxRecursionDepth;
static struct Symbol const *pCurrentMacro;
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 struct Context *contextStack;
static size_t contextDepth = 0;
#define DEFAULT_MAX_DEPTH 64
size_t nMaxRecursionDepth;
static char *pCurrentREPTBlock;
static uint32_t nCurrentREPTBlockSize;
static uint32_t nCurrentREPTBlockCount;
static int32_t nCurrentREPTBodyFirstLine;
static int32_t nCurrentREPTBodyLastLine;
static unsigned int nbIncPaths = 0;
static char const *includePaths[MAXINCPATHS];
uint32_t ulMacroReturnValue;
/*
* 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)
char const *dumpNodeAndParents(struct FileStackNode const *node)
{
struct sContext **ppFileStack;
char const *name;
if (++nFileStackDepth > nMaxRecursionDepth)
fatalerror("Recursion limit (%u) exceeded\n", nMaxRecursionDepth);
if (node->type == NODE_REPT) {
assert(node->parent); /* REPT nodes should always have a parent */
struct FileStackReptNode const *reptInfo = (struct FileStackReptNode const *)node;
ppFileStack = &pFileStack;
while (*ppFileStack)
ppFileStack = &((*ppFileStack)->next);
*ppFileStack = malloc(sizeof(struct sContext));
if (*ppFileStack == NULL)
fatalerror("No memory for context\n");
(*ppFileStack)->FlexHandle = CurrentFlexHandle;
(*ppFileStack)->next = NULL;
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__);
name = dumpNodeAndParents(node->parent);
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
for (uint32_t i = reptInfo->reptDepth; i--; )
fprintf(stderr, "::REPT~%" PRIu32, reptInfo->iters[i]);
} else {
name = ((struct FileStackNamedNode const *)node)->name;
if (node->parent) {
dumpNodeAndParents(node->parent);
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
} else {
fputs(name, stderr);
}
(*ppFileStack)->uniqueID = macro_GetUniqueID();
nLineNo = 0;
}
return name;
}
static int32_t popcontext(void)
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo)
{
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;
}
}
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;
dumpNodeAndParents(node);
fprintf(stderr, "(%" PRIu32 ")", lineNo);
}
int32_t fstk_GetLine(void)
void fstk_DumpCurrent(void)
{
struct sContext *pLastFile, **ppLastFile;
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__);
if (!contextStack) {
fputs("at top level", stderr);
return;
}
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__);
fstk_Dump(contextStack->fileInfo, lexer_GetLineNo());
}
int yywrap(void)
struct FileStackNode *fstk_GetFileStack(void)
{
return popcontext();
}
struct FileStackNode *node = contextStack->fileInfo;
/*
* 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;
/* Mark node and all of its parents as referenced if not already so they don't get freed */
while (node && !node->referenced) {
node->ID = -1;
node->referenced = true;
node = node->parent;
}
fprintf(stderr, "%s(%" PRId32 ")", tzCurrentFileName, nLineNo);
return contextStack->fileInfo;
}
void fstk_DumpToStr(char *buf, size_t buflen)
char const *fstk_GetFileName(void)
{
const struct sContext *pLastFile = pFileStack;
int retcode;
size_t len = buflen;
/* Iterating via the nodes themselves skips nested REPTs */
struct FileStackNode const *node = contextStack->fileInfo;
while (pLastFile) {
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ") -> ",
pLastFile->tzFileName, pLastFile->nLine);
if (retcode < 0)
fatalerror("Failed to dump file stack to string: %s\n", strerror(errno));
else if (retcode >= len)
len = 0;
else
len -= retcode;
pLastFile = pLastFile->next;
while (node->type != NODE_FILE)
node = node->parent;
return ((struct FileStackNamedNode const *)node)->name;
}
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);
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");
}
/*
* Dump the string expansion stack to stderr
*/
void fstk_DumpStringExpansions(void)
{
const struct sStringExpansionPos *pExpansion = pCurrentStringExpansion;
while (pExpansion) {
fprintf(stderr, "while expanding symbol \"%s\"\n",
pExpansion->tzName);
pExpansion = pExpansion->pParent;
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;
}
/*
* 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)
static void printDep(char const *path)
{
if (dependfile) {
fprintf(dependfile, "%s: %s\n", tzTargetFileName, fileName);
fprintf(dependfile, "%s: %s\n", tzTargetFileName, path);
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;
if (stat(pathname, &statbuf) != 0)
return NULL;
if (stat(path, &statbuf) != 0)
return false;
/* Reject directories */
if (S_ISDIR(statbuf.st_mode))
return NULL;
return fopen(pathname, "rb");
return !S_ISDIR(statbuf.st_mode);
}
FILE *fstk_FindFile(char const *fname, char **incPathUsed)
bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
{
if (fname == NULL)
return NULL;
char path[_MAX_PATH];
FILE *f = getFile(fname);
if (f) {
printdep(fname);
return f;
if (!*size) {
*size = 64; /* This is arbitrary, really */
*fullPath = realloc(*fullPath, *size);
if (!*fullPath)
error("realloc error during include path search: %s\n",
strerror(errno));
}
for (size_t i = 0; i < NextIncPath; ++i) {
/*
* The function snprintf() does not write more than `size` bytes
* (including the terminating null byte ('\0')). If the output
* 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 (*fullPath) {
for (size_t i = 0; i <= nbIncPaths; ++i) {
char const *incPath = i ? includePaths[i - 1] : "";
int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
if (fullpathlen >= (int)sizeof(path))
continue;
/* Oh how I wish `asnprintf` was standard... */
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 (f) {
printdep(path);
if (incPathUsed)
*incPathUsed = IncludePaths[i];
return f;
if (len < 0) {
error("snprintf error during include path search: %s\n",
strerror(errno));
} else if (isPathValid(*fullPath)) {
printDep(*fullPath);
return true;
}
}
}
errno = ENOENT;
if (oGeneratedMissingIncludes)
printdep(fname);
return NULL;
printDep(path);
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 = "";
FILE *f = fstk_FindFile(tzFileName, &incPathUsed);
if (++contextDepth >= nMaxRecursionDepth)
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
struct Context *context = malloc(sizeof(*context));
if (f == NULL) {
if (oGeneratedMissingIncludes) {
if (!context)
fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno));
fileInfo->parent = contextStack->fileInfo;
fileInfo->lineNo = 0; /* Init to a default value, see struct definition for info */
fileInfo->referenced = false;
fileInfo->lineNo = lexer_GetLineNo();
context->fileInfo = fileInfo;
/*
* Link new entry to its parent so it's reachable later
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
*/
context->parent = contextStack;
contextStack = context;
}
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;
else
error("Unable to open included file '%s': %s\n", path, strerror(errno));
return;
}
error("Unable to open included file '%s': %s\n", tzFileName, strerror(errno));
dbgPrint("Full path: \"%s\"\n", fullPath);
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
if (!fileInfo) {
error("Failed to alloc file info for INCLUDE: %s\n", strerror(errno));
return;
}
fileInfo->node.type = NODE_FILE;
strcpy(fileInfo->name, fullPath);
free(fullPath);
pushcontext();
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 */
yyunput('\n');
nLineNo--;
newContext((struct FileStackNode *)fileInfo);
contextStack->lexerState = lexer_OpenFile(fileInfo->name);
if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for file include\n");
lexer_SetStateAtEOL(contextStack->lexerState);
/* We're back at top-level, so most things are reset */
contextStack->uniqueID = 0;
macro_SetUniqueID(0);
}
/*
* Set up a macro for parsing
*/
void fstk_RunMacro(char *s, struct MacroArgs *args)
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
{
struct Symbol const *sym = sym_FindSymbol(s);
int nPrintedChars;
dbgPrint("Running macro \"%s\"\n", macroName);
if (sym == NULL) {
error("Macro \"%s\" not defined\n", s);
struct Symbol *macro = sym_FindSymbol(macroName);
if (!macro) {
error("Macro \"%s\" not defined\n", macroName);
return;
}
if (sym->type != SYM_MACRO) {
error("\"%s\" is not a macro\n", s);
if (macro->type != SYM_MACRO) {
error("\"%s\" is not a macro\n", macroName);
return;
}
contextStack->macroArgs = macro_GetCurrentArgs();
pushcontext();
macro_SetUniqueID(nMacroCount++);
/* Minus 1 because there is a newline at the beginning of the buffer */
nLineNo = sym->fileLine - 1;
/* Compute total length of this node's name: <base name>::<macro> */
size_t reptNameLen = 0;
struct FileStackNode const *node = macro->src;
if (node->type == NODE_REPT) {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
/* 4294967295 = 2^32 - 1, aka UINT32_MAX */
reptNameLen += reptNode->reptDepth * strlen("::REPT~4294967295");
/* Look for next named node */
do {
node = node->parent;
} while (node->type == NODE_REPT);
}
struct FileStackNamedNode const *baseNode = (struct FileStackNamedNode const *)node;
size_t baseLen = strlen(baseNode->name);
size_t macroNameLen = strlen(macro->name);
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + baseLen
+ reptNameLen + 2 + macroNameLen + 1);
if (!fileInfo) {
error("Failed to alloc file info for \"%s\": %s\n", macro->name, strerror(errno));
return;
}
fileInfo->node.type = NODE_MACRO;
/* Print the name... */
char *dest = fileInfo->name;
memcpy(dest, baseNode->name, baseLen);
dest += baseLen;
if (node->type == NODE_REPT) {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
for (uint32_t i = reptNode->reptDepth; i--; ) {
int nbChars = sprintf(dest, "::REPT~%" PRIu32, reptNode->iters[i]);
if (nbChars < 0)
fatalerror("Failed to write macro invocation info: %s\n",
strerror(errno));
dest += nbChars;
}
}
*dest++ = ':';
*dest++ = ':';
memcpy(dest, macro->name, macroNameLen + 1);
newContext((struct FileStackNode *)fileInfo);
/* Line minus 1 because buffer begins with a newline */
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
macro->fileLine - 1);
if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for macro invocation\n");
lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID();
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);
}
/*
* Set up a repeat block for parsing
*/
void fstk_RunRept(uint32_t count, int32_t nReptLineNo)
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
{
if (count) {
static const char *tzReptStr = "::REPT~1";
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
if (count == 0)
return;
/* For error printing to make sense, fake nLineNo */
nCurrentREPTBodyLastLine = nLineNo;
nLineNo = nReptLineNo;
pushcontext();
macro_SetUniqueID(nMacroCount++);
nCurrentREPTBlockCount = count;
nCurrentStatus = STAT_isREPTBlock;
nCurrentREPTBlockSize = ulNewMacroSize;
pCurrentREPTBlock = tzNewMacro;
nCurrentREPTBodyFirstLine = nReptLineNo + 1;
nLineNo = nReptLineNo;
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
: 0;
struct FileStackReptNode *fileInfo = malloc(sizeof(*fileInfo)
+ (reptDepth + 1) * sizeof(fileInfo->iters[0]));
if (strlen(tzCurrentFileName) + strlen(tzReptStr) > _MAX_PATH)
fatalerror("Cannot append \"%s\" to file path\n", tzReptStr);
strcat(tzCurrentFileName, tzReptStr);
CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock, nCurrentREPTBlockSize);
yy_switch_to_buffer(CurrentFlexHandle);
if (!fileInfo) {
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
return;
}
fileInfo->node.type = NODE_REPT;
fileInfo->reptDepth = reptDepth + 1;
fileInfo->iters[0] = 1;
if (reptDepth)
/* Copy all parent iter counts */
memcpy(&fileInfo->iters[1],
((struct FileStackReptNode *)contextStack->fileInfo)->iters,
reptDepth * sizeof(fileInfo->iters[0]));
newContext((struct FileStackNode *)fileInfo);
/* Correct our line number, which currently points to the `ENDR` line */
contextStack->fileInfo->lineNo = reptLineNo;
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for rept block\n");
lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID();
contextStack->nbReptIters = count;
}
/*
* Initialize the filestack routines
*/
void fstk_Init(char *pFileName)
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
{
char tzSymFileName[_MAX_PATH + 1 + 2];
struct LexerState *state = lexer_OpenFile(mainPath);
char *c = pFileName;
int fileNameIndex = 0;
if (!state)
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"
// minus 1 to avoid a buffer overflow in extreme cases
while (*c && fileNameIndex < sizeof(tzSymFileName) - 2 - 1) {
context->fileInfo = (struct FileStackNode *)fileInfo;
/* lineNo and reptIter are unused on the top-level context */
context->fileInfo->parent = NULL;
context->fileInfo->referenced = false;
context->fileInfo->type = NODE_FILE;
memcpy(fileInfo->name, fileName, len + 1);
if (*c == '"') {
tzSymFileName[fileNameIndex++] = '\\';
}
context->parent = NULL;
context->lexerState = state;
context->uniqueID = 0;
macro_SetUniqueID(0);
context->nbReptIters = 0;
tzSymFileName[fileNameIndex++] = *c;
++c;
}
/* Now that it's set up properly, register the context */
contextStack = context;
tzSymFileName[fileNameIndex++] = '"';
tzSymFileName[fileNameIndex] = '\0';
sym_AddString("__FILE__", tzSymFileName);
pFileStack = NULL;
if (strcmp(pFileName, "-") == 0) {
pCurrentFile = stdin;
/*
* Check that max recursion depth won't allow overflowing node `malloc`s
* This assumes that the rept node is larger
*/
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
if (maxRecursionDepth > DEPTH_LIMIT) {
error("Recursion depth may not be higher than %zu, defaulting to "
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
} else {
pCurrentFile = fopen(pFileName, "rb");
if (pCurrentFile == NULL)
fatalerror("Unable to open file '%s': %s\n", pFileName, strerror(errno));
nMaxRecursionDepth = maxRecursionDepth;
}
nFileStackDepth = 0;
nMacroCount = 0;
nCurrentStatus = STAT_isInclude;
snprintf(tzCurrentFileName, _MAX_PATH + 1, "%s", pFileName);
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
yy_switch_to_buffer(CurrentFlexHandle);
nLineNo = 1;
/* Make sure that the default of 64 is OK, though */
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
#undef DEPTH_LIMIT
}

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))
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
* guarantees the size of the buffer will be correct. I was unable to find a
@@ -88,6 +89,9 @@ void macro_FreeArgs(struct MacroArgs *args)
char const *macro_GetArg(uint32_t i)
{
if (!macroArgs)
return NULL;
uint32_t realIndex = i + macroArgs->shift - 1;
return realIndex >= macroArgs->nbArgs ? NULL
@@ -107,15 +111,23 @@ char const *macro_GetUniqueIDStr(void)
void macro_SetUniqueID(uint32_t id)
{
uniqueID = id;
if (id == -1) {
if (id == 0) {
uniqueIDPtr = NULL;
} else {
if (uniqueID > maxUniqueID)
maxUniqueID = uniqueID;
/* The buffer is guaranteed to be the correct size */
sprintf(uniqueIDBuf, "_%" PRIu32, id);
uniqueIDPtr = uniqueIDBuf;
}
}
uint32_t macro_UseNewUniqueID(void)
{
macro_SetUniqueID(++maxUniqueID);
return maxUniqueID;
}
void macro_ShiftCurrentArgs(void)
{
if (macroArgs->shift != macroArgs->nbArgs)

View File

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

View File

@@ -12,6 +12,7 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -33,7 +34,8 @@
#include "platform.h" // strdup
struct Patch {
char tzFilename[_MAX_PATH + 1];
struct FileStackNode const *src;
uint32_t lineNo;
uint32_t nOffset;
struct Section *pcSection;
uint32_t pcOffset;
@@ -62,19 +64,17 @@ static uint32_t nbSymbols = 0; /* Length of the above list */
static struct Assertion *assertions = NULL;
static struct FileStackNode *fileStackNodes = NULL;
/*
* Count the number of sections used in this object
*/
static uint32_t countsections(void)
{
struct Section *sect;
uint32_t count = 0;
sect = pSectionList;
while (sect) {
for (struct Section const *sect = pSectionList; sect; sect = sect->next)
count++;
sect = sect->next;
}
return count;
}
@@ -129,16 +129,60 @@ static void fputstring(char const *s, FILE *f)
fputc(0, f);
}
static uint32_t getNbFileStackNodes(void)
{
return fileStackNodes ? fileStackNodes->ID + 1 : 0;
}
void out_RegisterNode(struct FileStackNode *node)
{
/* If node is not already registered, register it (and parents), and give it a unique ID */
while (node->ID == -1) {
node->ID = getNbFileStackNodes();
if (node->ID == -1)
fatalerror("Reached too many file stack nodes; try splitting the file up\n");
node->next = fileStackNodes;
fileStackNodes = node;
/* Also register the node's parents */
node = node->parent;
if (!node)
break;
}
}
void out_ReplaceNode(struct FileStackNode *node)
{
(void)node;
#if 0
This is code intended to replace a node, which is pretty useless until ref counting is added...
struct FileStackNode **ptr = &fileStackNodes;
/*
* The linked list is supposed to have decrementing IDs, so iterate with less memory reads,
* to hopefully hit the cache less. A debug check is added after, in case a change is made
* that breaks this assumption.
*/
for (uint32_t i = fileStackNodes->ID; i != node->ID; i--)
ptr = &(*ptr)->next;
assert((*ptr)->ID == node->ID);
node->next = (*ptr)->next;
assert(!node->next || node->next->ID == node->ID - 1); /* Catch inconsistencies early */
/* TODO: unreference the node */
*ptr = node;
#endif
}
/*
* Return a section's ID
*/
static uint32_t getsectid(struct Section const *sect)
{
struct Section const *sec;
struct Section const *sec = pSectionList;
uint32_t ID = 0;
sec = pSectionList;
while (sec) {
if (sec == sect)
return ID;
@@ -159,7 +203,10 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
*/
static void writepatch(struct Patch const *patch, FILE *f)
{
fputstring(patch->tzFilename, f);
assert(patch->src->ID != -1);
fputlong(patch->src->ID, f);
fputlong(patch->lineNo, f);
fputlong(patch->nOffset, f);
fputlong(getSectIDIfAny(patch->pcSection), f);
fputlong(patch->pcOffset, f);
@@ -206,26 +253,35 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
if (!sym_IsDefined(sym)) {
fputc(SYMTYPE_IMPORT, f);
} else {
assert(sym->src->ID != -1);
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
fputstring(sym->fileName, f);
fputlong(sym->src->ID, f);
fputlong(sym->fileLine, f);
fputlong(getSectIDIfAny(sym_GetSection(sym)), f);
fputlong(sym->value, f);
}
}
static void registerSymbol(struct Symbol *sym)
{
*objectSymbolsTail = sym;
objectSymbolsTail = &sym->next;
out_RegisterNode(sym->src);
if (nbSymbols == -1)
fatalerror("Registered too many symbols (%" PRIu32
"); try splitting up your files\n", (uint32_t)-1);
sym->ID = nbSymbols++;
}
/*
* Returns a symbol's ID within the object file
* If the symbol does not have one, one is assigned by registering the symbol
*/
static uint32_t getSymbolID(struct Symbol *sym)
{
if (sym->ID == -1) {
sym->ID = nbSymbols++;
*objectSymbolsTail = sym;
objectSymbolsTail = &sym->next;
}
if (sym->ID == -1 && !sym_IsPC(sym))
registerSymbol(sym);
return sym->ID;
}
@@ -303,22 +359,25 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
/*
* Allocate a new patch structure and link it into the list
* WARNING: all patches are assumed to eventually be written, so the file stack node is registered
*/
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
uint32_t ofs)
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
{
struct Patch *patch = malloc(sizeof(struct Patch));
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
struct FileStackNode *node = fstk_GetFileStack();
if (!patch)
fatalerror("No memory for patch: %s\n", strerror(errno));
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
if (!patch->pRPN)
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
patch->type = type;
fstk_DumpToStr(patch->tzFilename, sizeof(patch->tzFilename));
patch->src = node;
out_RegisterNode(node);
patch->lineNo = lexer_GetLineNo();
patch->nOffset = ofs;
patch->pcSection = sect_GetSymbolSection();
patch->pcOffset = sect_GetSymbolOffset();
@@ -382,13 +441,28 @@ static void writeassert(struct Assertion *assert, FILE *f)
fputstring(assert->message, f);
}
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
{
fputlong(node->parent ? node->parent->ID : -1, f);
fputlong(node->lineNo, f);
fputc(node->type, f);
if (node->type != NODE_REPT) {
fputstring(((struct FileStackNamedNode const *)node)->name, f);
} else {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
fputlong(reptNode->reptDepth, f);
/* Iters are stored by decreasing depth, so reverse the order for output */
for (uint32_t i = reptNode->reptDepth; i--; )
fputlong(reptNode->iters[i], f);
}
}
static void registerExportedSymbol(struct Symbol *symbol, void *arg)
{
(void)arg;
if (sym_IsExported(symbol) && symbol->ID == -1) {
*objectSymbolsTail = symbol;
objectSymbolsTail = &symbol->next;
nbSymbols++;
registerSymbol(symbol);
}
}
@@ -411,6 +485,15 @@ void out_WriteObject(void)
fputlong(nbSymbols, f);
fputlong(countsections(), f);
fputlong(getNbFileStackNodes(), f);
for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) {
writeFileStackNode(node, f);
if (node->next && node->next->ID != node->ID - 1)
fatalerror("Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
". Please report this to the developers!\n",
node->next->ID, node->ID);
}
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
writesymbol(sym, f);

View File

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

View File

@@ -656,9 +656,15 @@ void out_BinaryFile(char const *s, int32_t startPos)
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) {
free(fullPath);
if (oGeneratedMissingIncludes) {
oFailedOnMissingInclude = true;
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));
fclose(f);
free(fullPath);
}
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 */
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) {
free(fullPath);
if (oGeneratedMissingIncludes) {
oFailedOnMissingInclude = true;
return;
@@ -767,6 +780,7 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
}
fclose(f);
free(fullPath);
}
/*

View File

@@ -23,6 +23,7 @@
#include "asm/macro.h"
#include "asm/main.h"
#include "asm/mymath.h"
#include "asm/output.h"
#include "asm/section.h"
#include "asm/symbol.h"
#include "asm/util.h"
@@ -77,12 +78,55 @@ void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg)
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();
}
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)
@@ -97,8 +141,8 @@ static int32_t CallbackPC(void)
*/
int32_t sym_GetValue(struct Symbol const *sym)
{
if (sym_IsNumeric(sym) && sym->callback)
return sym->callback();
if (sym_IsNumeric(sym) && sym->hasCallback)
return sym->numCallback();
if (sym->type == SYM_LABEL)
/* TODO: do not use section's org directly */
@@ -107,15 +151,35 @@ int32_t sym_GetValue(struct Symbol const *sym)
return sym->value;
}
static void dumpFilename(struct Symbol const *sym)
{
if (!sym->src)
fputs("<builtin>", stderr);
else
fstk_Dump(sym->src, sym->fileLine);
}
/*
* Set a symbol's definition filename and line
*/
static void setSymbolFilename(struct Symbol *sym)
{
sym->src = fstk_GetFileStack();
sym->fileLine = lexer_GetLineNo();
}
/*
* Update a symbol's definition filename and line
*/
static void updateSymbolFilename(struct Symbol *sym)
{
if (snprintf(sym->fileName, _MAX_PATH + 1, "%s",
tzCurrentFileName) > _MAX_PATH)
fatalerror("%s: File name is too long: '%s'\n", __func__, tzCurrentFileName);
sym->fileLine = fstk_GetLine();
struct FileStackNode *oldSrc = sym->src;
setSymbolFilename(sym);
/* If the old node was referenced, ensure the new one is */
if (oldSrc->referenced && oldSrc->ID != -1)
out_RegisterNode(sym->src);
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
}
/*
@@ -133,8 +197,9 @@ static struct Symbol *createsymbol(char const *s)
symbol->isExported = false;
symbol->isBuiltin = false;
symbol->hasCallback = false;
symbol->section = NULL;
updateSymbolFilename(symbol);
setSymbolFilename(symbol);
symbol->ID = -1;
symbol->next = NULL;
@@ -209,8 +274,7 @@ void sym_Purge(char const *symName)
labelScope = NULL;
hash_RemoveElement(symbols, symbol->name);
if (symbol->type == SYM_MACRO)
free(symbol->macro);
/* TODO: ideally, also unref the file stack nodes */
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)
{
@@ -237,12 +316,8 @@ uint32_t sym_GetConstantValue(char const *s)
if (sym == NULL)
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
return sym_GetValue(sym);
return sym_GetConstantSymValue(sym);
return 0;
}
@@ -285,9 +360,11 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
if (!symbol)
symbol = createsymbol(symbolName);
else if (sym_IsDefined(symbol))
error("'%s' already defined at %s(%" PRIu32 ")\n", symbolName,
symbol->fileName, symbol->fileLine);
else if (sym_IsDefined(symbol)) {
error("'%s' already defined at ", symbolName);
dumpFilename(symbol);
putc('\n', stderr);
}
return symbol;
}
@@ -300,7 +377,6 @@ struct Symbol *sym_AddEqu(char const *symName, int32_t value)
struct Symbol *sym = createNonrelocSymbol(symName);
sym->type = SYM_EQU;
sym->callback = NULL;
sym->value = value;
return sym;
@@ -343,18 +419,19 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
{
struct Symbol *sym = findsymbol(symName, NULL);
if (sym == NULL)
if (sym == NULL) {
sym = createsymbol(symName);
else if (sym_IsDefined(sym) && sym->type != SYM_SET)
error("'%s' already defined as %s at %s(%" PRIu32 ")\n",
symName, sym->type == SYM_LABEL ? "label" : "constant",
sym->fileName, sym->fileLine);
else
/* TODO: can the scope be incorrect when talking over refs? */
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
error("'%s' already defined as %s at ",
symName, sym->type == SYM_LABEL ? "label" : "constant");
dumpFilename(sym);
putc('\n', stderr);
} else {
/* TODO: can the scope be incorrect when taking over refs? */
updateSymbolFilename(sym);
}
sym->type = SYM_SET;
sym->callback = NULL;
sym->value = value;
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)
* @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 */
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) {
sym = createsymbol(name);
} else if (sym_IsDefined(sym)) {
error("'%s' already defined in %s(%" PRIu32 ")\n",
name, sym->fileName, sym->fileLine);
error("'%s' already defined at ", name);
dumpFilename(sym);
putc('\n', stderr);
return NULL;
} else {
updateSymbolFilename(sym);
}
/* If the symbol already exists as a ref, just "take over" it */
sym->type = SYM_LABEL;
sym->callback = NULL;
sym->value = sect_GetSymbolOffset();
if (exportall)
sym->isExported = true;
sym->section = sect_GetSymbolSection();
updateSymbolFilename(sym);
return sym;
}
static struct Symbol *addLabel(char const *name)
{
struct Symbol *sym = addSectionlessLabel(name);
if (sym && !sym->section)
error("Label \"%s\" created outside of a SECTION\n", name);
@@ -467,14 +538,14 @@ void sym_Export(char const *symName)
/*
* 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);
sym->type = SYM_MACRO;
sym->macroSize = ulNewMacroSize;
sym->macro = tzNewMacro;
updateSymbolFilename(sym);
sym->macroSize = size;
sym->macro = body;
setSymbolFilename(sym); /* TODO: is this really necessary? */
/*
* The symbol is created at the line after the `endm`,
* override this with the actual definition line
@@ -528,21 +599,36 @@ static inline char const *removeLeadingZeros(char const *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
*/
void sym_Init(void)
{
struct Symbol *_NARGSymbol = sym_AddEqu("_NARG", 0);
struct Symbol *__LINE__Symbol = sym_AddEqu("__LINE__", 0);
PCSymbol = createBuiltinSymbol("@");
struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG");
struct Symbol *__LINE__Symbol = createBuiltinSymbol("__LINE__");
struct Symbol *__FILE__Symbol = createBuiltinSymbol("__FILE__");
PCSymbol = addSectionlessLabel("@");
PCSymbol->isBuiltin = true;
PCSymbol->callback = CallbackPC;
_NARGSymbol->isBuiltin = true;
_NARGSymbol->callback = Callback_NARG;
__LINE__Symbol->isBuiltin = true;
__LINE__Symbol->callback = Callback__LINE__;
PCSymbol->type = SYM_LABEL;
PCSymbol->section = NULL;
PCSymbol->numCallback = CallbackPC;
_NARGSymbol->type = SYM_EQU;
_NARGSymbol->numCallback = Callback_NARG;
__LINE__Symbol->type = SYM_EQU;
__LINE__Symbol->numCallback = Callback__LINE__;
__FILE__Symbol->type = SYM_EQUS;
__FILE__Symbol->strCallback = Callback__FILE__;
sym_AddSet("_RS", 0)->isBuiltin = true;
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT
*/
#include <ctype.h>
#include <stdint.h>
#include "asm/main.h"
@@ -27,6 +28,40 @@ uint32_t calchash(const char *s)
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)
{
uint32_t state = 0;

View File

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

View File

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

View File

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

View File

@@ -31,6 +31,11 @@ static struct SymbolList {
struct SymbolList *next;
} *symbolLists;
unsigned int nbObjFiles;
static struct {
struct FileStackNode *nodes;
uint32_t nbNodes;
} *nodes;
static struct Assertion *assertions;
/***** Helper functions for reading object files *****/
@@ -170,12 +175,56 @@ static char *readstr(FILE *file)
/***** Functions to parse object files *****/
/**
* Reads a RGB6 symbol from a file.
* Reads a file stack node form a file.
* @param file The file to read from
* @param nodes The file's array of nodes
* @param i The ID of the node in the array
* @param fileName The filename to report in errors
*/
static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint32_t i,
char const *fileName)
{
uint32_t parentID;
tryReadlong(parentID, file,
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
fileNodes[i].parent = parentID == -1 ? NULL : &fileNodes[parentID];
tryReadlong(fileNodes[i].lineNo, file,
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
fileName, i);
switch (fileNodes[i].type) {
case NODE_FILE:
case NODE_MACRO:
tryReadstr(fileNodes[i].name, file,
"%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, i);
break;
case NODE_REPT:
tryReadlong(fileNodes[i].reptDepth, file,
"%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
fileNodes[i].iters = malloc(sizeof(*fileNodes[i].iters) * fileNodes[i].reptDepth);
if (!fileNodes[i].iters)
fatal(NULL, 0, "%s: Failed to alloc node #%" PRIu32 "'s iters: %s",
fileName, i, strerror(errno));
for (uint32_t k = 0; k < fileNodes[i].reptDepth; k++)
tryReadlong(fileNodes[i].iters[k], file,
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
fileName, i, k);
if (!fileNodes[i].parent)
fatal(NULL, 0, "%s is not a valid object file: root node (#%"
PRIu32 ") may not be REPT", fileName, i);
}
}
/**
* Reads a symbol from a file.
* @param file The file to read from
* @param symbol The struct to fill
* @param fileName The filename to report in errors
*/
static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
static void readSymbol(FILE *file, struct Symbol *symbol,
char const *fileName, struct FileStackNode fileNodes[])
{
tryReadstr(symbol->name, file, "%s: Cannot read symbol name: %s",
fileName);
@@ -184,9 +233,12 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
/* If the symbol is defined in this file, read its definition */
if (symbol->type != SYMTYPE_IMPORT) {
symbol->objFileName = fileName;
tryReadstr(symbol->fileName, file,
"%s: Cannot read \"%s\"'s file name: %s",
uint32_t nodeID;
tryReadlong(nodeID, file,
"%s: Cannot read \"%s\"'s node ID: %s",
fileName, symbol->name);
symbol->src = &fileNodes[nodeID];
tryReadlong(symbol->lineNo, file,
"%s: Cannot read \"%s\"'s line number: %s",
fileName, symbol->name);
@@ -202,7 +254,7 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
}
/**
* Reads a RGB6 patch from a file.
* Reads a patch from a file.
* @param file The file to read from
* @param patch The struct to fill
* @param fileName The filename to report in errors
@@ -210,10 +262,16 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
*/
static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
char const *sectName, uint32_t i,
struct Section *fileSections[])
struct Section *fileSections[], struct FileStackNode fileNodes[])
{
tryReadstr(patch->fileName, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s name: %s",
uint32_t nodeID;
tryReadlong(nodeID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
fileName, sectName, i);
patch->src = &fileNodes[nodeID];
tryReadlong(patch->lineNo, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
fileName, sectName, i);
tryReadlong(patch->offset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
@@ -221,8 +279,7 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
tryReadlong(patch->pcSectionID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i);
patch->pcSection = patch->pcSectionID == -1
? NULL
patch->pcSection = patch->pcSectionID == -1 ? NULL
: fileSections[patch->pcSectionID];
tryReadlong(patch->pcOffset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
@@ -234,16 +291,17 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName, sectName, i);
uint8_t *rpnExpression =
malloc(sizeof(*rpnExpression) * patch->rpnSize);
size_t nbElementsRead = fread(rpnExpression, sizeof(*rpnExpression),
patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
if (!patch->rpnExpression)
err(1, "%s: Failed to alloc \"%s\"'s patch #%" PRIu32 "'s RPN expression",
fileName, sectName, i);
size_t nbElementsRead = fread(patch->rpnExpression, sizeof(*patch->rpnExpression),
patch->rpnSize, file);
if (nbElementsRead != patch->rpnSize)
errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName, sectName, i,
feof(file) ? "Unexpected end of file" : strerror(errno));
patch->rpnExpression = rpnExpression;
}
/**
@@ -252,8 +310,8 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
* @param section The struct to fill
* @param fileName The filename to report in errors
*/
static void readSection(FILE *file, struct Section *section,
char const *fileName, struct Section *fileSections[])
static void readSection(FILE *file, struct Section *section, char const *fileName,
struct Section *fileSections[], struct FileStackNode fileNodes[])
{
int32_t tmp;
uint8_t byte;
@@ -280,7 +338,7 @@ static void readSection(FILE *file, struct Section *section,
fileName, section->name);
section->isAddressFixed = tmp >= 0;
if (tmp > UINT16_MAX) {
error("\"%s\"'s org is too large (%" PRId32 ")",
error(NULL, 0, "\"%s\"'s org is too large (%" PRId32 ")",
section->name, tmp);
tmp = UINT16_MAX;
}
@@ -296,7 +354,7 @@ static void readSection(FILE *file, struct Section *section,
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
fileName, section->name);
if (tmp > UINT16_MAX) {
error("\"%s\"'s alignment offset is too large (%" PRId32 ")",
error(NULL, 0, "\"%s\"'s alignment offset is too large (%" PRId32 ")",
section->name, tmp);
tmp = UINT16_MAX;
}
@@ -332,7 +390,7 @@ static void readSection(FILE *file, struct Section *section,
section->name);
for (uint32_t i = 0; i < section->nbPatches; i++) {
readPatch(file, &patches[i], fileName, section->name,
i, fileSections);
i, fileSections, fileNodes);
}
section->patches = patches;
}
@@ -375,13 +433,13 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
*/
static void readAssertion(FILE *file, struct Assertion *assert,
char const *fileName, uint32_t i,
struct Section *fileSections[])
struct Section *fileSections[], struct FileStackNode fileNodes[])
{
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections);
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections, fileNodes);
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
fileName);
}
@@ -394,11 +452,7 @@ static inline struct Section *getMainSection(struct Section *section)
return section;
}
/**
* Reads an object file of any supported format
* @param fileName The filename to report for errors
*/
void obj_ReadFile(char const *fileName)
void obj_ReadFile(char const *fileName, unsigned int fileID)
{
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
@@ -438,6 +492,14 @@ void obj_ReadFile(char const *fileName)
nbSectionsToAssign += nbSections;
tryReadlong(nodes[fileID].nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
nodes[fileID].nodes = calloc(nodes[fileID].nbNodes, sizeof(nodes[fileID].nodes[0]));
if (!nodes[fileID].nodes)
err(1, "Failed to get memory for %s's nodes", fileName);
verbosePrint("Reading %u nodes...\n", nodes[fileID].nbNodes);
for (uint32_t i = 0; i < nodes[fileID].nbNodes; i++)
readFileStackNode(file, nodes[fileID].nodes, i, fileName);
/* This file's symbols, kept to link sections to them */
struct Symbol **fileSymbols =
malloc(sizeof(*fileSymbols) * nbSymbols + 1);
@@ -464,7 +526,7 @@ void obj_ReadFile(char const *fileName)
if (!symbol)
err(1, "%s: Couldn't create new symbol", fileName);
readSymbol(file, symbol, fileName);
readSymbol(file, symbol, fileName, nodes[fileID].nodes);
fileSymbols[i] = symbol;
if (symbol->type == SYMTYPE_EXPORT)
@@ -485,7 +547,7 @@ void obj_ReadFile(char const *fileName)
err(1, "%s: Couldn't create new section", fileName);
fileSections[i]->nextu = NULL;
readSection(file, fileSections[i], fileName, fileSections);
readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes);
fileSections[i]->fileSymbols = fileSymbols;
if (nbSymPerSect[i]) {
fileSections[i]->symbols = malloc(nbSymPerSect[i]
@@ -535,7 +597,7 @@ void obj_ReadFile(char const *fileName)
if (!assertion)
err(1, "%s: Couldn't create new assertion", fileName);
readAssertion(file, assertion, fileName, i, fileSections);
readAssertion(file, assertion, fileName, i, fileSections, nodes[fileID].nodes);
assertion->fileSymbols = fileSymbols;
assertion->next = assertions;
assertions = assertion;
@@ -555,6 +617,15 @@ void obj_CheckAssertions(void)
patch_CheckAssertions(assertions);
}
void obj_Setup(unsigned int nbFiles)
{
nbObjFiles = nbFiles;
if (nbFiles > SIZE_MAX / sizeof(*nodes))
fatal(NULL, 0, "Impossible to link more than %zu files!", SIZE_MAX / sizeof(*nodes));
nodes = malloc(sizeof(*nodes) * nbFiles);
}
static void freeSection(struct Section *section, void *arg)
{
(void)arg;
@@ -562,12 +633,8 @@ static void freeSection(struct Section *section, void *arg)
free(section->name);
if (sect_HasData(section->type)) {
free(section->data);
for (int32_t i = 0; i < section->nbPatches; i++) {
struct Patch *patch = &section->patches[i];
free(patch->fileName);
free(patch->rpnExpression);
}
for (int32_t i = 0; i < section->nbPatches; i++)
free(section->patches[i].rpnExpression);
free(section->patches);
}
free(section->symbols);
@@ -577,13 +644,20 @@ static void freeSection(struct Section *section, void *arg)
static void freeSymbol(struct Symbol *symbol)
{
free(symbol->name);
if (symbol->type != SYMTYPE_IMPORT)
free(symbol->fileName);
free(symbol);
}
void obj_Cleanup(void)
{
for (unsigned int i = 0; i < nbObjFiles; i++) {
for (uint32_t j = 0; j < nodes[i].nbNodes; j++) {
if (nodes[i].nodes[j].type == NODE_REPT)
free(nodes[i].nodes[j].iters);
}
free(nodes[i].nodes);
}
free(nodes);
sym_CleanupSymbols();
sect_ForEach(freeSection, NULL);

View File

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

View File

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

View File

@@ -16,7 +16,7 @@ This is the description of the object files used by
.Xr rgbasm 1
and
.Xr rgblink 1 .
.Em Please note that the specifications may change.
.Em Please note that the specifications may change .
This toolchain is in development and new features may require adding more information to the current format, or modifying some fields, which would break compatibility with older versions.
.Pp
.Sh FILE STRUCTURE
@@ -34,9 +34,42 @@ is a 0terminated string of
; Header
BYTE ID[4] ; "RGB9"
LONG RevisionNumber ; The format's revision number this file uses
LONG NumberOfSymbols ; The number of symbols used in this file
LONG NumberOfSections ; The number of sections used in this file
LONG RevisionNumber ; The format's revision number this file uses.
LONG NumberOfSymbols ; The number of symbols used in this file.
LONG NumberOfSections ; The number of sections used in this file.
; File info
LONG NumberOfNodes ; The number of nodes contained in this file.
REPT NumberOfNodes ; IMPORTANT NOTE: the nodes are actually written in
; **reverse** order, meaningthe node with ID 0 is
; the last one in the file!
LONG ParentID ; ID of the parent node, -1 means this is the root.
LONG ParentLineNo ; Line at which the parent context was exited.
; Meaningless on the root node.
BYTE Type ; 0 = REPT node
; 1 = File node
; 2 = Macro node
IF Type != 0 ; If the node is not a REPT...
STRING Name ; The node's name: either a file name, or macro name
; prefixed by its definition file name.
ELSE ; If the node is a REPT, it also contains the iter
; counts of all the parent REPTs.
LONG Depth ; Size of the array below.
LONG Iter[Depth] ; The number of REPT iterations by increasing depth.
ENDC
ENDR
; Symbols
@@ -51,7 +84,7 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file.
IF (Type & 0x7F) != 1 ; If symbol is defined in this object file.
STRING FileName ; File where the symbol is defined.
LONG SourceFile ; File where the symbol is defined.
LONG LineNum ; Line number in the file where the symbol is defined.
@@ -107,8 +140,10 @@ REPT NumberOfSections
REPT NumberOfPatches
STRING SourceFile ; Name of the source file (for printing error
; messages).
LONG SourceFile ; ID of the source file node (for printing
; error messages).
LONG LineNo ; Line at which the patch was created.
LONG Offset ; Offset into the section where patch should
; be applied (in bytes).
@@ -145,7 +180,9 @@ LONG NumberOfAssertions
REPT NumberOfAssertions
STRING SourceFile ; Name of the source file (for printing the failure).
LONG SourceFile ; ID of the source file node (for printing the failure).
LONG LineNo ; Line at which the assertion was created.
LONG Offset ; Offset into the section where the assertion is located.
@@ -209,7 +246,7 @@ with some bytes being special prefixes for integers and symbols.
.It Li $50 Ta Li BANK(symbol) ,
a
.Ar LONG
Symbol ID follows.
Symbol ID follows, where -1 means PC
.It Li $51 Ta Li BANK(section_name) ,
a null-terminated string follows.
.It Li $52 Ta Li Current BANK()

View File

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

View File

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

View File

@@ -1,2 +1,2 @@
ERROR: divzero-section-bank.asm(1):
FATAL: divzero-section-bank.asm(1):
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
; 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
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"

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):
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

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

View File

@@ -1,3 +1,3 @@
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)!

View File

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

View File

@@ -2,7 +2,9 @@ m: MACRO
ENDM
REPT 1
m ENDR
m
ENDR
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).

View File

@@ -1,3 +1,3 @@
ERROR: local-purge.asm(8):
'.loc' not defined
Interpolated symbol ".loc" does not exist
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

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

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"
PRINTT "\n"

View File

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

View File

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

Binary file not shown.

View File

@@ -1,2 +1,3 @@
ERROR: null-in-macro.asm(2):
Found null character
ERROR: null-in-macro.asm(4) -> null-in-macro.asm::foo(2):
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]
Division of -2147483648 by -1 yields -2147483648
warning: overflow.asm(39): [-Wlarge-constant]
Integer constant '4294967296' is too large
Integer constant is too large
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

View File

@@ -1,4 +1,4 @@
ERROR: pops-restore-no-section.asm(9):
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

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

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

View File

@@ -6,5 +6,5 @@ ERROR: section-union.asm(37):
Section "test" already declared as fixed at $c000
ERROR: section-union.asm(37):
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)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,19 +1,19 @@
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
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
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]
_0
_1
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
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
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]
_3
ERROR: unique-id.asm(15):
_4
FATAL: unique-id.asm(15):
Macro argument '\@' not defined
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: -(18):
ERROR: <stdin>(18):
Section "conflicting alignment" already declared as aligned to 4 bytes (offset 0)
ERROR: -(18):
FATAL: <stdin>(18):
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: -(18):
ERROR: <stdin>(18):
Section "conflicting alignment" already declared with incompatible 3-byte alignment (offset 7)
ERROR: -(18):
FATAL: <stdin>(18):
Cannot create section "conflicting alignment" (1 errors)

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