mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-22 11:12:07 +00:00
Run clang-format on everything (#1332)
This commit is contained in:
@@ -1,29 +1,30 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
|
||||
#include <errno.h>
|
||||
#include <new>
|
||||
#include <map>
|
||||
#include <new>
|
||||
#include <stack>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
// Charmaps are stored using a structure known as "trie".
|
||||
// Essentially a tree, where each nodes stores a single character's worth of info:
|
||||
// whether there exists a mapping that ends at the current character,
|
||||
struct CharmapNode {
|
||||
bool isTerminal; // Whether there exists a mapping that ends here
|
||||
uint8_t value; // If the above is true, its corresponding value
|
||||
uint8_t value; // If the above is true, its corresponding value
|
||||
// This MUST be indexes and not pointers, because pointers get invalidated by reallocation!
|
||||
size_t next[255]; // Indexes of where to go next, 0 = nowhere
|
||||
};
|
||||
@@ -38,8 +39,7 @@ static std::map<std::string, Charmap> charmaps;
|
||||
static Charmap *currentCharmap;
|
||||
std::stack<Charmap *> charmapStack;
|
||||
|
||||
void charmap_New(char const *name, char const *baseName)
|
||||
{
|
||||
void charmap_New(char const *name, char const *baseName) {
|
||||
Charmap *base = nullptr;
|
||||
|
||||
if (baseName != nullptr) {
|
||||
@@ -68,8 +68,7 @@ void charmap_New(char const *name, char const *baseName)
|
||||
currentCharmap = &charmap;
|
||||
}
|
||||
|
||||
void charmap_Set(char const *name)
|
||||
{
|
||||
void charmap_Set(char const *name) {
|
||||
auto search = charmaps.find(name);
|
||||
|
||||
if (search == charmaps.end())
|
||||
@@ -78,13 +77,11 @@ void charmap_Set(char const *name)
|
||||
currentCharmap = &search->second;
|
||||
}
|
||||
|
||||
void charmap_Push()
|
||||
{
|
||||
void charmap_Push() {
|
||||
charmapStack.push(currentCharmap);
|
||||
}
|
||||
|
||||
void charmap_Pop()
|
||||
{
|
||||
void charmap_Pop() {
|
||||
if (charmapStack.empty()) {
|
||||
error("No entries in the charmap stack\n");
|
||||
return;
|
||||
@@ -94,8 +91,7 @@ void charmap_Pop()
|
||||
charmapStack.pop();
|
||||
}
|
||||
|
||||
void charmap_Add(char *mapping, uint8_t value)
|
||||
{
|
||||
void charmap_Add(char *mapping, uint8_t value) {
|
||||
Charmap &charmap = *currentCharmap;
|
||||
size_t nodeIdx = 0;
|
||||
|
||||
@@ -124,8 +120,7 @@ void charmap_Add(char *mapping, uint8_t value)
|
||||
node.value = value;
|
||||
}
|
||||
|
||||
bool charmap_HasChar(char const *input)
|
||||
{
|
||||
bool charmap_HasChar(char const *input) {
|
||||
Charmap const &charmap = *currentCharmap;
|
||||
size_t nodeIdx = 0;
|
||||
|
||||
@@ -139,14 +134,12 @@ bool charmap_HasChar(char const *input)
|
||||
return charmap.nodes[nodeIdx].isTerminal;
|
||||
}
|
||||
|
||||
void charmap_Convert(char const *input, std::vector<uint8_t> &output)
|
||||
{
|
||||
void charmap_Convert(char const *input, std::vector<uint8_t> &output) {
|
||||
while (charmap_ConvertNext(input, &output))
|
||||
;
|
||||
}
|
||||
|
||||
size_t charmap_ConvertNext(char const *&input, std::vector<uint8_t> *output)
|
||||
{
|
||||
size_t charmap_ConvertNext(char const *&input, std::vector<uint8_t> *output) {
|
||||
// The goal is to match the longest mapping possible.
|
||||
// For that, advance through the trie with each character read.
|
||||
// If that would lead to a dead end, rewind characters until the last match, and output.
|
||||
@@ -194,11 +187,13 @@ size_t charmap_ConvertNext(char const *&input, std::vector<uint8_t> *output)
|
||||
|
||||
// Warn if this character is not mapped but any others are
|
||||
if (charmap.nodes.size() > 1)
|
||||
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s\n",
|
||||
printChar(firstChar));
|
||||
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s\n", printChar(firstChar));
|
||||
else if (charmap.name != DEFAULT_CHARMAP_NAME)
|
||||
warning(WARNING_UNMAPPED_CHAR_2, "Unmapped character %s not in "
|
||||
DEFAULT_CHARMAP_NAME " charmap\n", printChar(firstChar));
|
||||
warning(
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
"Unmapped character %s not in " DEFAULT_CHARMAP_NAME " charmap\n",
|
||||
printChar(firstChar)
|
||||
);
|
||||
|
||||
return codepointLen;
|
||||
|
||||
|
||||
@@ -2,20 +2,21 @@
|
||||
|
||||
// Fixed-point math routines
|
||||
|
||||
#include "asm/fixpoint.hpp"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "asm/fixpoint.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
#define fix2double(i, q) ((double)((i) / pow(2.0, q)))
|
||||
#define double2fix(d, q) ((int32_t)round((d) * pow(2.0, q)))
|
||||
#define double2fix(d, q) ((int32_t)round((d)*pow(2.0, q)))
|
||||
|
||||
// 2*pi radians == 1 turn
|
||||
#define turn2rad(f) ((f) * (M_PI * 2))
|
||||
@@ -23,87 +24,70 @@
|
||||
|
||||
uint8_t fixPrecision;
|
||||
|
||||
uint8_t fix_Precision()
|
||||
{
|
||||
uint8_t fix_Precision() {
|
||||
return fixPrecision;
|
||||
}
|
||||
|
||||
double fix_PrecisionFactor()
|
||||
{
|
||||
double fix_PrecisionFactor() {
|
||||
return pow(2.0, fixPrecision);
|
||||
}
|
||||
|
||||
int32_t fix_Sin(int32_t i, int32_t q)
|
||||
{
|
||||
int32_t fix_Sin(int32_t i, int32_t q) {
|
||||
return double2fix(sin(turn2rad(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_Cos(int32_t i, int32_t q)
|
||||
{
|
||||
int32_t fix_Cos(int32_t i, int32_t q) {
|
||||
return double2fix(cos(turn2rad(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_Tan(int32_t i, int32_t q)
|
||||
{
|
||||
int32_t fix_Tan(int32_t i, int32_t q) {
|
||||
return double2fix(tan(turn2rad(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_ASin(int32_t i, int32_t q)
|
||||
{
|
||||
int32_t fix_ASin(int32_t i, int32_t q) {
|
||||
return double2fix(rad2turn(asin(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_ACos(int32_t i, int32_t q)
|
||||
{
|
||||
int32_t fix_ACos(int32_t i, int32_t q) {
|
||||
return double2fix(rad2turn(acos(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_ATan(int32_t i, int32_t q)
|
||||
{
|
||||
int32_t fix_ATan(int32_t i, int32_t q) {
|
||||
return double2fix(rad2turn(atan(fix2double(i, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_ATan2(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
int32_t fix_ATan2(int32_t i, int32_t j, int32_t q) {
|
||||
return double2fix(rad2turn(atan2(fix2double(i, q), fix2double(j, q))), q);
|
||||
}
|
||||
|
||||
int32_t fix_Mul(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
int32_t fix_Mul(int32_t i, int32_t j, int32_t q) {
|
||||
return double2fix(fix2double(i, q) * fix2double(j, q), q);
|
||||
}
|
||||
|
||||
int32_t fix_Div(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
int32_t fix_Div(int32_t i, int32_t j, int32_t q) {
|
||||
return double2fix(fix2double(i, q) / fix2double(j, q), q);
|
||||
}
|
||||
|
||||
int32_t fix_Mod(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
int32_t fix_Mod(int32_t i, int32_t j, int32_t q) {
|
||||
return double2fix(fmod(fix2double(i, q), fix2double(j, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Pow(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
int32_t fix_Pow(int32_t i, int32_t j, int32_t q) {
|
||||
return double2fix(pow(fix2double(i, q), fix2double(j, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Log(int32_t i, int32_t j, int32_t q)
|
||||
{
|
||||
int32_t fix_Log(int32_t i, int32_t j, int32_t q) {
|
||||
return double2fix(log(fix2double(i, q)) / log(fix2double(j, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Round(int32_t i, int32_t q)
|
||||
{
|
||||
int32_t fix_Round(int32_t i, int32_t q) {
|
||||
return double2fix(round(fix2double(i, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Ceil(int32_t i, int32_t q)
|
||||
{
|
||||
int32_t fix_Ceil(int32_t i, int32_t q) {
|
||||
return double2fix(ceil(fix2double(i, q)), q);
|
||||
}
|
||||
|
||||
int32_t fix_Floor(int32_t i, int32_t q)
|
||||
{
|
||||
int32_t fix_Floor(int32_t i, int32_t q) {
|
||||
return double2fix(floor(fix2double(i, q)), q);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "asm/format.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
@@ -9,11 +11,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "asm/fixpoint.hpp"
|
||||
#include "asm/format.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
void FormatSpec::useCharacter(int c)
|
||||
{
|
||||
void FormatSpec::useCharacter(int c) {
|
||||
if (state == FORMAT_INVALID)
|
||||
return;
|
||||
|
||||
@@ -99,14 +99,12 @@ invalid:
|
||||
}
|
||||
}
|
||||
|
||||
void FormatSpec::finishCharacters()
|
||||
{
|
||||
void FormatSpec::finishCharacters() {
|
||||
if (!isValid())
|
||||
state = FORMAT_INVALID;
|
||||
}
|
||||
|
||||
void FormatSpec::printString(char *buf, size_t bufLen, char const *value)
|
||||
{
|
||||
void FormatSpec::printString(char *buf, size_t bufLen, char const *value) {
|
||||
if (isEmpty()) {
|
||||
// No format was specified
|
||||
type = 's';
|
||||
@@ -149,8 +147,7 @@ void FormatSpec::printString(char *buf, size_t bufLen, char const *value)
|
||||
buf[totalLen] = '\0';
|
||||
}
|
||||
|
||||
void FormatSpec::printNumber(char *buf, size_t bufLen, uint32_t value)
|
||||
{
|
||||
void FormatSpec::printNumber(char *buf, size_t bufLen, uint32_t value) {
|
||||
if (isEmpty()) {
|
||||
// No format was specified; default to uppercase $hex
|
||||
type = 'X';
|
||||
@@ -175,12 +172,12 @@ void FormatSpec::printNumber(char *buf, size_t bufLen, uint32_t value)
|
||||
}
|
||||
}
|
||||
|
||||
char prefixChar = !prefix ? 0
|
||||
: type == 'X' ? '$'
|
||||
: type == 'x' ? '$'
|
||||
: type == 'b' ? '%'
|
||||
: type == 'o' ? '&'
|
||||
: 0;
|
||||
char prefixChar = !prefix ? 0
|
||||
: type == 'X' ? '$'
|
||||
: type == 'x' ? '$'
|
||||
: type == 'b' ? '%'
|
||||
: type == 'o' ? '&'
|
||||
: 0;
|
||||
|
||||
char valueBuf[262]; // Max 5 digits + decimal + 255 fraction digits + terminator
|
||||
|
||||
@@ -215,15 +212,16 @@ void FormatSpec::printNumber(char *buf, size_t bufLen, uint32_t value)
|
||||
cappedFracWidth = 255;
|
||||
}
|
||||
|
||||
snprintf(valueBuf, sizeof(valueBuf), "%.*f", (int)cappedFracWidth,
|
||||
value / fix_PrecisionFactor());
|
||||
snprintf(
|
||||
valueBuf, sizeof(valueBuf), "%.*f", (int)cappedFracWidth, value / fix_PrecisionFactor()
|
||||
);
|
||||
} else {
|
||||
char const *spec = type == 'd' ? "%" PRId32
|
||||
: type == 'u' ? "%" PRIu32
|
||||
: type == 'X' ? "%" PRIX32
|
||||
: type == 'x' ? "%" PRIx32
|
||||
: type == 'o' ? "%" PRIo32
|
||||
: "%" PRId32;
|
||||
char const *spec = type == 'd' ? "%" PRId32
|
||||
: type == 'u' ? "%" PRIu32
|
||||
: type == 'X' ? "%" PRIX32
|
||||
: type == 'x' ? "%" PRIx32
|
||||
: type == 'o' ? "%" PRIo32
|
||||
: "%" PRId32;
|
||||
|
||||
snprintf(valueBuf, sizeof(valueBuf), spec, value);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <new>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
@@ -15,13 +15,14 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "platform.hpp" // S_ISDIR (stat macro)
|
||||
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/macro.hpp"
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
#include "error.hpp"
|
||||
#include "platform.hpp" // S_ISDIR (stat macro)
|
||||
|
||||
struct Context {
|
||||
FileStackNode *fileInfo;
|
||||
@@ -39,7 +40,7 @@ static std::stack<Context> contextStack;
|
||||
size_t maxRecursionDepth;
|
||||
|
||||
// The first include path for `fstk_FindFile` to try is none at all
|
||||
static std::vector<std::string> includePaths = { "" };
|
||||
static std::vector<std::string> includePaths = {""};
|
||||
|
||||
static const char *preIncludeName;
|
||||
|
||||
@@ -63,8 +64,7 @@ std::string const &FileStackNode::name() const {
|
||||
return std::get<std::string>(data);
|
||||
}
|
||||
|
||||
static const char *dumpNodeAndParents(FileStackNode const &node)
|
||||
{
|
||||
static const char *dumpNodeAndParents(FileStackNode const &node) {
|
||||
char const *name;
|
||||
|
||||
if (node.type == NODE_REPT) {
|
||||
@@ -73,7 +73,7 @@ static const char *dumpNodeAndParents(FileStackNode const &node)
|
||||
|
||||
name = dumpNodeAndParents(*node.parent);
|
||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node.lineNo, name);
|
||||
for (uint32_t i = nodeIters.size(); i--; )
|
||||
for (uint32_t i = nodeIters.size(); i--;)
|
||||
fprintf(stderr, "::REPT~%" PRIu32, nodeIters[i]);
|
||||
} else {
|
||||
name = node.name().c_str();
|
||||
@@ -87,14 +87,12 @@ static const char *dumpNodeAndParents(FileStackNode const &node)
|
||||
return name;
|
||||
}
|
||||
|
||||
void FileStackNode::dump(uint32_t curLineNo) const
|
||||
{
|
||||
void FileStackNode::dump(uint32_t curLineNo) const {
|
||||
dumpNodeAndParents(*this);
|
||||
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
|
||||
}
|
||||
|
||||
void fstk_DumpCurrent()
|
||||
{
|
||||
void fstk_DumpCurrent() {
|
||||
if (contextStack.empty()) {
|
||||
fputs("at top level", stderr);
|
||||
return;
|
||||
@@ -102,8 +100,7 @@ void fstk_DumpCurrent()
|
||||
contextStack.top().fileInfo->dump(lexer_GetLineNo());
|
||||
}
|
||||
|
||||
FileStackNode *fstk_GetFileStack()
|
||||
{
|
||||
FileStackNode *fstk_GetFileStack() {
|
||||
if (contextStack.empty())
|
||||
return nullptr;
|
||||
|
||||
@@ -117,8 +114,7 @@ FileStackNode *fstk_GetFileStack()
|
||||
return topNode;
|
||||
}
|
||||
|
||||
char const *fstk_GetFileName()
|
||||
{
|
||||
char const *fstk_GetFileName() {
|
||||
// Iterating via the nodes themselves skips nested REPTs
|
||||
FileStackNode const *node = contextStack.top().fileInfo;
|
||||
|
||||
@@ -127,8 +123,7 @@ char const *fstk_GetFileName()
|
||||
return node->name().c_str();
|
||||
}
|
||||
|
||||
void fstk_AddIncludePath(char const *path)
|
||||
{
|
||||
void fstk_AddIncludePath(char const *path) {
|
||||
if (path[0] == '\0')
|
||||
return;
|
||||
|
||||
@@ -138,8 +133,7 @@ void fstk_AddIncludePath(char const *path)
|
||||
str += '/';
|
||||
}
|
||||
|
||||
void fstk_SetPreIncludeFile(char const *path)
|
||||
{
|
||||
void fstk_SetPreIncludeFile(char const *path) {
|
||||
if (preIncludeName)
|
||||
warnx("Overriding pre-included filename %s", preIncludeName);
|
||||
preIncludeName = path;
|
||||
@@ -147,8 +141,7 @@ void fstk_SetPreIncludeFile(char const *path)
|
||||
printf("Pre-included filename %s\n", preIncludeName);
|
||||
}
|
||||
|
||||
static void printDep(char const *path)
|
||||
{
|
||||
static void printDep(char const *path) {
|
||||
if (dependfile) {
|
||||
fprintf(dependfile, "%s: %s\n", targetFileName.c_str(), path);
|
||||
if (generatePhonyDeps)
|
||||
@@ -156,8 +149,7 @@ static void printDep(char const *path)
|
||||
}
|
||||
}
|
||||
|
||||
static bool isPathValid(char const *path)
|
||||
{
|
||||
static bool isPathValid(char const *path) {
|
||||
struct stat statbuf;
|
||||
|
||||
if (stat(path, &statbuf) != 0)
|
||||
@@ -167,9 +159,8 @@ static bool isPathValid(char const *path)
|
||||
return !S_ISDIR(statbuf.st_mode);
|
||||
}
|
||||
|
||||
std::string *fstk_FindFile(char const *path)
|
||||
{
|
||||
std::string *fullPath = new(std::nothrow) std::string();
|
||||
std::string *fstk_FindFile(char const *path) {
|
||||
std::string *fullPath = new (std::nothrow) std::string();
|
||||
|
||||
if (!fullPath) {
|
||||
error("Failed to allocate string during include path search: %s\n", strerror(errno));
|
||||
@@ -189,20 +180,22 @@ std::string *fstk_FindFile(char const *path)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool yywrap()
|
||||
{
|
||||
bool yywrap() {
|
||||
uint32_t ifDepth = lexer_GetIFDepth();
|
||||
|
||||
if (ifDepth != 0)
|
||||
fatalerror("Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
||||
ifDepth, ifDepth == 1 ? "" : "s");
|
||||
fatalerror(
|
||||
"Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
||||
ifDepth,
|
||||
ifDepth == 1 ? "" : "s"
|
||||
);
|
||||
|
||||
if (Context &context = contextStack.top(); context.fileInfo->type == NODE_REPT) {
|
||||
// The context is a REPT or FOR block, which may loop
|
||||
|
||||
// If the node is referenced, we can't edit it; duplicate it
|
||||
if (context.fileInfo->referenced) {
|
||||
context.fileInfo = new(std::nothrow) FileStackNode(*context.fileInfo);
|
||||
context.fileInfo = new (std::nothrow) FileStackNode(*context.fileInfo);
|
||||
if (!context.fileInfo)
|
||||
fatalerror("Failed to duplicate REPT file node: %s\n", strerror(errno));
|
||||
// Copy all info but the referencing
|
||||
@@ -255,8 +248,7 @@ bool yywrap()
|
||||
// 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.
|
||||
// Callers should set `contextStack.top().lexerState` after this so it is not `nullptr`.
|
||||
static Context &newContext(FileStackNode &fileInfo)
|
||||
{
|
||||
static Context &newContext(FileStackNode &fileInfo) {
|
||||
if (contextStack.size() > maxRecursionDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||
|
||||
@@ -277,15 +269,13 @@ static Context &newContext(FileStackNode &fileInfo)
|
||||
return context;
|
||||
}
|
||||
|
||||
void fstk_RunInclude(char const *path)
|
||||
{
|
||||
void fstk_RunInclude(char const *path) {
|
||||
std::string *fullPath = fstk_FindFile(path);
|
||||
|
||||
if (!fullPath) {
|
||||
if (generatedMissingIncludes) {
|
||||
if (verbose)
|
||||
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
|
||||
path, strerror(errno));
|
||||
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n", path, strerror(errno));
|
||||
failedOnMissingInclude = true;
|
||||
} else {
|
||||
error("Unable to open included file '%s': %s\n", path, strerror(errno));
|
||||
@@ -293,7 +283,7 @@ void fstk_RunInclude(char const *path)
|
||||
return;
|
||||
}
|
||||
|
||||
FileStackNode *fileInfo = new(std::nothrow) FileStackNode();
|
||||
FileStackNode *fileInfo = new (std::nothrow) FileStackNode();
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for INCLUDE: %s\n", strerror(errno));
|
||||
@@ -317,8 +307,7 @@ void fstk_RunInclude(char const *path)
|
||||
|
||||
// Similar to `fstk_RunInclude`, but not subject to `-MG`, and
|
||||
// calling `lexer_SetState` instead of `lexer_SetStateAtEOL`.
|
||||
static void runPreIncludeFile()
|
||||
{
|
||||
static void runPreIncludeFile() {
|
||||
if (!preIncludeName)
|
||||
return;
|
||||
|
||||
@@ -329,7 +318,7 @@ static void runPreIncludeFile()
|
||||
return;
|
||||
}
|
||||
|
||||
FileStackNode *fileInfo = new(std::nothrow) FileStackNode();
|
||||
FileStackNode *fileInfo = new (std::nothrow) FileStackNode();
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for pre-include: %s\n", strerror(errno));
|
||||
@@ -348,8 +337,7 @@ static void runPreIncludeFile()
|
||||
context.uniqueID = macro_UndefUniqueID();
|
||||
}
|
||||
|
||||
void fstk_RunMacro(char const *macroName, MacroArgs &args)
|
||||
{
|
||||
void fstk_RunMacro(char const *macroName, MacroArgs &args) {
|
||||
Symbol *macro = sym_FindExactSymbol(macroName);
|
||||
|
||||
if (!macro) {
|
||||
@@ -362,7 +350,7 @@ void fstk_RunMacro(char const *macroName, MacroArgs &args)
|
||||
}
|
||||
contextStack.top().macroArgs = macro_GetCurrentArgs();
|
||||
|
||||
FileStackNode *fileInfo = new(std::nothrow) FileStackNode();
|
||||
FileStackNode *fileInfo = new (std::nothrow) FileStackNode();
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for \"%s\": %s\n", macro->name, strerror(errno));
|
||||
@@ -383,12 +371,11 @@ void fstk_RunMacro(char const *macroName, MacroArgs &args)
|
||||
if (macro->src->type == NODE_REPT) {
|
||||
std::vector<uint32_t> const &srcIters = macro->src->iters();
|
||||
|
||||
for (uint32_t i = srcIters.size(); i--; ) {
|
||||
for (uint32_t i = srcIters.size(); i--;) {
|
||||
char buf[sizeof("::REPT~4294967295")]; // UINT32_MAX
|
||||
|
||||
if (sprintf(buf, "::REPT~%" PRIu32, srcIters[i]) < 0)
|
||||
fatalerror("Failed to write macro invocation info: %s\n",
|
||||
strerror(errno));
|
||||
fatalerror("Failed to write macro invocation info: %s\n", strerror(errno));
|
||||
fileInfoName.append(buf);
|
||||
}
|
||||
}
|
||||
@@ -398,19 +385,19 @@ void fstk_RunMacro(char const *macroName, MacroArgs &args)
|
||||
Context &context = newContext(*fileInfo);
|
||||
std::string_view *macroView = macro->getMacro();
|
||||
|
||||
lexer_OpenFileView(context.lexerState, "MACRO", macroView->data(), macroView->size(),
|
||||
macro->fileLine);
|
||||
lexer_OpenFileView(
|
||||
context.lexerState, "MACRO", macroView->data(), macroView->size(), macro->fileLine
|
||||
);
|
||||
lexer_SetStateAtEOL(&context.lexerState);
|
||||
context.uniqueID = macro_UseNewUniqueID();
|
||||
macro_UseNewArgs(&args);
|
||||
}
|
||||
|
||||
static bool newReptContext(int32_t reptLineNo, char const *body, size_t size)
|
||||
{
|
||||
static bool newReptContext(int32_t reptLineNo, char const *body, size_t size) {
|
||||
uint32_t reptDepth = contextStack.top().fileInfo->type == NODE_REPT
|
||||
? contextStack.top().fileInfo->iters().size()
|
||||
: 0;
|
||||
FileStackNode *fileInfo = new(std::nothrow) FileStackNode();
|
||||
? contextStack.top().fileInfo->iters().size()
|
||||
: 0;
|
||||
FileStackNode *fileInfo = new (std::nothrow) FileStackNode();
|
||||
|
||||
if (!fileInfo) {
|
||||
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
|
||||
@@ -420,7 +407,9 @@ static bool newReptContext(int32_t reptLineNo, char const *body, size_t size)
|
||||
fileInfo->data = std::vector<uint32_t>{1};
|
||||
if (reptDepth) {
|
||||
// Append all parent iter counts
|
||||
fileInfo->iters().insert(fileInfo->iters().end(), RANGE(contextStack.top().fileInfo->iters()));
|
||||
fileInfo->iters().insert(
|
||||
fileInfo->iters().end(), RANGE(contextStack.top().fileInfo->iters())
|
||||
);
|
||||
}
|
||||
|
||||
Context &context = newContext(*fileInfo);
|
||||
@@ -434,8 +423,7 @@ static bool newReptContext(int32_t reptLineNo, char const *body, size_t size)
|
||||
return true;
|
||||
}
|
||||
|
||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char const *body, size_t size)
|
||||
{
|
||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char const *body, size_t size) {
|
||||
if (count == 0)
|
||||
return;
|
||||
if (!newReptContext(reptLineNo, body, size))
|
||||
@@ -444,9 +432,15 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, char const *body, size_t s
|
||||
contextStack.top().nbReptIters = count;
|
||||
}
|
||||
|
||||
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||
int32_t reptLineNo, char const *body, size_t size)
|
||||
{
|
||||
void fstk_RunFor(
|
||||
char const *symName,
|
||||
int32_t start,
|
||||
int32_t stop,
|
||||
int32_t step,
|
||||
int32_t reptLineNo,
|
||||
char const *body,
|
||||
size_t size
|
||||
) {
|
||||
Symbol *sym = sym_AddVar(symName, start);
|
||||
|
||||
if (sym->type != SYM_VAR)
|
||||
@@ -462,8 +456,9 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||
error("FOR cannot have a step value of 0\n");
|
||||
|
||||
if ((step > 0 && start > stop) || (step < 0 && start < stop))
|
||||
warning(WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n",
|
||||
start, stop, step);
|
||||
warning(
|
||||
WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n", start, stop, step
|
||||
);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
@@ -479,14 +474,12 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||
context.forName = symName;
|
||||
}
|
||||
|
||||
void fstk_StopRept()
|
||||
{
|
||||
void fstk_StopRept() {
|
||||
// Prevent more iterations
|
||||
contextStack.top().nbReptIters = 0;
|
||||
}
|
||||
|
||||
bool fstk_Break()
|
||||
{
|
||||
bool fstk_Break() {
|
||||
if (contextStack.top().fileInfo->type != NODE_REPT) {
|
||||
error("BREAK can only be used inside a REPT/FOR block\n");
|
||||
return false;
|
||||
@@ -496,22 +489,20 @@ bool fstk_Break()
|
||||
return true;
|
||||
}
|
||||
|
||||
void fstk_NewRecursionDepth(size_t newDepth)
|
||||
{
|
||||
void fstk_NewRecursionDepth(size_t newDepth) {
|
||||
if (contextStack.size() > newDepth + 1)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", newDepth);
|
||||
maxRecursionDepth = newDepth;
|
||||
}
|
||||
|
||||
void fstk_Init(char const *mainPath, size_t maxDepth)
|
||||
{
|
||||
void fstk_Init(char const *mainPath, size_t maxDepth) {
|
||||
Context &context = contextStack.emplace();
|
||||
|
||||
if (!lexer_OpenFile(context.lexerState, mainPath))
|
||||
fatalerror("Failed to open main file\n");
|
||||
lexer_SetState(&context.lexerState);
|
||||
|
||||
FileStackNode *fileInfo = new(std::nothrow) FileStackNode();
|
||||
FileStackNode *fileInfo = new (std::nothrow) FileStackNode();
|
||||
|
||||
if (!fileInfo)
|
||||
fatalerror("Failed to allocate memory for main file info: %s\n", strerror(errno));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,15 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "asm/macro.hpp"
|
||||
|
||||
#include <errno.h>
|
||||
#include <new>
|
||||
#include <inttypes.h>
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "asm/macro.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#define MAXMACROARGS 99999
|
||||
@@ -19,8 +20,7 @@ static uint32_t maxUniqueID = 0;
|
||||
static char uniqueIDBuf[sizeof("_u4294967295")] = {}; // UINT32_MAX
|
||||
static char *uniqueIDPtr = nullptr;
|
||||
|
||||
void MacroArgs::append(std::string s)
|
||||
{
|
||||
void MacroArgs::append(std::string s) {
|
||||
if (s.empty())
|
||||
warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument\n");
|
||||
if (args.size() == MAXMACROARGS)
|
||||
@@ -28,18 +28,15 @@ void MacroArgs::append(std::string s)
|
||||
args.push_back(s);
|
||||
}
|
||||
|
||||
MacroArgs *macro_GetCurrentArgs()
|
||||
{
|
||||
MacroArgs *macro_GetCurrentArgs() {
|
||||
return macroArgs;
|
||||
}
|
||||
|
||||
void macro_UseNewArgs(MacroArgs *args)
|
||||
{
|
||||
void macro_UseNewArgs(MacroArgs *args) {
|
||||
macroArgs = args;
|
||||
}
|
||||
|
||||
char const *macro_GetArg(uint32_t i)
|
||||
{
|
||||
char const *macro_GetArg(uint32_t i) {
|
||||
if (!macroArgs)
|
||||
return nullptr;
|
||||
|
||||
@@ -48,8 +45,7 @@ char const *macro_GetArg(uint32_t i)
|
||||
return realIndex >= macroArgs->args.size() ? nullptr : macroArgs->args[realIndex].c_str();
|
||||
}
|
||||
|
||||
char const *macro_GetAllArgs()
|
||||
{
|
||||
char const *macro_GetAllArgs() {
|
||||
if (!macroArgs)
|
||||
return nullptr;
|
||||
|
||||
@@ -85,13 +81,11 @@ char const *macro_GetAllArgs()
|
||||
return str;
|
||||
}
|
||||
|
||||
uint32_t macro_GetUniqueID()
|
||||
{
|
||||
uint32_t macro_GetUniqueID() {
|
||||
return uniqueID;
|
||||
}
|
||||
|
||||
char const *macro_GetUniqueIDStr()
|
||||
{
|
||||
char const *macro_GetUniqueIDStr() {
|
||||
// Generate a new unique ID on the first use of `\@`
|
||||
if (uniqueID == 0)
|
||||
macro_SetUniqueID(++maxUniqueID);
|
||||
@@ -99,8 +93,7 @@ char const *macro_GetUniqueIDStr()
|
||||
return uniqueIDPtr;
|
||||
}
|
||||
|
||||
void macro_SetUniqueID(uint32_t id)
|
||||
{
|
||||
void macro_SetUniqueID(uint32_t id) {
|
||||
uniqueID = id;
|
||||
if (id == 0 || id == (uint32_t)-1) {
|
||||
uniqueIDPtr = nullptr;
|
||||
@@ -112,26 +105,23 @@ void macro_SetUniqueID(uint32_t id)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t macro_UseNewUniqueID()
|
||||
{
|
||||
uint32_t macro_UseNewUniqueID() {
|
||||
// A new ID will be generated on the first use of `\@`
|
||||
macro_SetUniqueID(0);
|
||||
return uniqueID;
|
||||
}
|
||||
|
||||
uint32_t macro_UndefUniqueID()
|
||||
{
|
||||
uint32_t macro_UndefUniqueID() {
|
||||
// No ID will be generated; use of `\@` is an error
|
||||
macro_SetUniqueID((uint32_t)-1);
|
||||
return uniqueID;
|
||||
}
|
||||
|
||||
void macro_ShiftCurrentArgs(int32_t count)
|
||||
{
|
||||
void macro_ShiftCurrentArgs(int32_t count) {
|
||||
if (!macroArgs) {
|
||||
error("Cannot shift macro arguments outside of a macro\n");
|
||||
} else if (size_t nbArgs = macroArgs->args.size();
|
||||
count > 0 && ((uint32_t)count > nbArgs || macroArgs->shift > nbArgs - count)) {
|
||||
count > 0 && ((uint32_t)count > nbArgs || macroArgs->shift > nbArgs - count)) {
|
||||
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their end\n");
|
||||
macroArgs->shift = nbArgs;
|
||||
} else if (count < 0 && macroArgs->shift < (uint32_t)-count) {
|
||||
@@ -142,7 +132,6 @@ void macro_ShiftCurrentArgs(int32_t count)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t macro_NbArgs()
|
||||
{
|
||||
uint32_t macro_NbArgs() {
|
||||
return macroArgs->args.size() - macroArgs->shift;
|
||||
}
|
||||
|
||||
138
src/asm/main.cpp
138
src/asm/main.cpp
@@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "asm/main.hpp"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
@@ -7,43 +9,43 @@
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "extern/getopt.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
#include "asm/fixpoint.hpp"
|
||||
#include "asm/format.hpp"
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/opt.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/rpn.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
#include "parser.hpp"
|
||||
|
||||
#include "extern/getopt.hpp"
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "error.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#ifdef __clang__
|
||||
#if __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__)
|
||||
#define __SANITIZE_ADDRESS__
|
||||
#endif // __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__)
|
||||
#endif // __clang__
|
||||
#if __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__)
|
||||
#define __SANITIZE_ADDRESS__
|
||||
#endif // __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__)
|
||||
#endif // __clang__
|
||||
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
// There are known, non-trivial to fix leaks. We would still like to have `make develop'
|
||||
// detect memory corruption, though.
|
||||
extern "C" {
|
||||
char const *__asan_default_options() { return "detect_leaks=0"; }
|
||||
char const *__asan_default_options() {
|
||||
return "detect_leaks=0";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -65,8 +67,7 @@ bool verbose;
|
||||
bool warnings; // True to enable warnings, false to disable them.
|
||||
|
||||
// Escapes Make-special chars from a string
|
||||
static std::string make_escape(std::string &str)
|
||||
{
|
||||
static std::string make_escape(std::string &str) {
|
||||
std::string escaped;
|
||||
size_t pos = 0;
|
||||
|
||||
@@ -98,54 +99,53 @@ static int depType; // Variants of `-M`
|
||||
// This is because long opt matching, even to a single char, is prioritized
|
||||
// over short opt matching
|
||||
static option const longopts[] = {
|
||||
{ "binary-digits", required_argument, nullptr, 'b' },
|
||||
{ "define", required_argument, nullptr, 'D' },
|
||||
{ "export-all", no_argument, nullptr, 'E' },
|
||||
{ "gfx-chars", required_argument, nullptr, 'g' },
|
||||
{ "nop-after-halt", no_argument, nullptr, 'H' },
|
||||
{ "halt-without-nop", no_argument, nullptr, 'h' },
|
||||
{ "include", required_argument, nullptr, 'I' },
|
||||
{ "preserve-ld", no_argument, nullptr, 'L' },
|
||||
{ "auto-ldh", no_argument, nullptr, 'l' },
|
||||
{ "dependfile", required_argument, nullptr, 'M' },
|
||||
{ "MG", no_argument, &depType, 'G' },
|
||||
{ "MP", no_argument, &depType, 'P' },
|
||||
{ "MT", required_argument, &depType, 'T' },
|
||||
{ "warning", required_argument, nullptr, 'W' },
|
||||
{ "MQ", required_argument, &depType, 'Q' },
|
||||
{ "output", required_argument, nullptr, 'o' },
|
||||
{ "preinclude", required_argument, nullptr, 'P' },
|
||||
{ "pad-value", required_argument, nullptr, 'p' },
|
||||
{ "q-precision", required_argument, nullptr, 'Q' },
|
||||
{ "recursion-depth", required_argument, nullptr, 'r' },
|
||||
{ "version", no_argument, nullptr, 'V' },
|
||||
{ "verbose", no_argument, nullptr, 'v' },
|
||||
{ "warning", required_argument, nullptr, 'W' },
|
||||
{ "max-errors", required_argument, nullptr, 'X' },
|
||||
{ nullptr, no_argument, nullptr, 0 }
|
||||
{"binary-digits", required_argument, nullptr, 'b'},
|
||||
{"define", required_argument, nullptr, 'D'},
|
||||
{"export-all", no_argument, nullptr, 'E'},
|
||||
{"gfx-chars", required_argument, nullptr, 'g'},
|
||||
{"nop-after-halt", no_argument, nullptr, 'H'},
|
||||
{"halt-without-nop", no_argument, nullptr, 'h'},
|
||||
{"include", required_argument, nullptr, 'I'},
|
||||
{"preserve-ld", no_argument, nullptr, 'L'},
|
||||
{"auto-ldh", no_argument, nullptr, 'l'},
|
||||
{"dependfile", required_argument, nullptr, 'M'},
|
||||
{"MG", no_argument, &depType, 'G'},
|
||||
{"MP", no_argument, &depType, 'P'},
|
||||
{"MT", required_argument, &depType, 'T'},
|
||||
{"warning", required_argument, nullptr, 'W'},
|
||||
{"MQ", required_argument, &depType, 'Q'},
|
||||
{"output", required_argument, nullptr, 'o'},
|
||||
{"preinclude", required_argument, nullptr, 'P'},
|
||||
{"pad-value", required_argument, nullptr, 'p'},
|
||||
{"q-precision", required_argument, nullptr, 'Q'},
|
||||
{"recursion-depth", required_argument, nullptr, 'r'},
|
||||
{"version", no_argument, nullptr, 'V'},
|
||||
{"verbose", no_argument, nullptr, 'v'},
|
||||
{"warning", required_argument, nullptr, 'W'},
|
||||
{"max-errors", required_argument, nullptr, 'X'},
|
||||
{nullptr, no_argument, nullptr, 0 }
|
||||
};
|
||||
|
||||
static void printUsage()
|
||||
{
|
||||
static void printUsage() {
|
||||
fputs(
|
||||
"Usage: rgbasm [-EHhLlVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n"
|
||||
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
||||
" [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
|
||||
" [-r depth] [-W warning] [-X max_errors] <file>\n"
|
||||
"Useful options:\n"
|
||||
" -E, --export-all export all labels\n"
|
||||
" -M, --dependfile <path> set the output dependency file\n"
|
||||
" -o, --output <path> set the output object file\n"
|
||||
" -p, --pad-value <value> set the value to use for `ds'\n"
|
||||
" -V, --version print RGBASM version and exit\n"
|
||||
" -W, --warning <warning> enable or disable warnings\n"
|
||||
"\n"
|
||||
"For help, use `man rgbasm' or go to https://rgbds.gbdev.io/docs/\n",
|
||||
stderr);
|
||||
"Usage: rgbasm [-EHhLlVvw] [-b chars] [-D name[=value]] [-g chars] [-I path]\n"
|
||||
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
||||
" [-o out_file] [-P include_file] [-p pad_value] [-Q precision]\n"
|
||||
" [-r depth] [-W warning] [-X max_errors] <file>\n"
|
||||
"Useful options:\n"
|
||||
" -E, --export-all export all labels\n"
|
||||
" -M, --dependfile <path> set the output dependency file\n"
|
||||
" -o, --output <path> set the output object file\n"
|
||||
" -p, --pad-value <value> set the value to use for `ds'\n"
|
||||
" -V, --version print RGBASM version and exit\n"
|
||||
" -W, --warning <warning> enable or disable warnings\n"
|
||||
"\n"
|
||||
"For help, use `man rgbasm' or go to https://rgbds.gbdev.io/docs/\n",
|
||||
stderr
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int main(int argc, char *argv[]) {
|
||||
time_t now = time(nullptr);
|
||||
char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH");
|
||||
|
||||
@@ -213,8 +213,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
case 'H':
|
||||
if (warnOnHaltNop)
|
||||
warning(WARNING_OBSOLETE,
|
||||
"Automatic `nop` after `halt` (the `-H` flag) is deprecated\n");
|
||||
warning(
|
||||
WARNING_OBSOLETE, "Automatic `nop` after `halt` (the `-H` flag) is deprecated\n"
|
||||
);
|
||||
else
|
||||
errx("`-H` and `-h` don't make sense together");
|
||||
haltNop = true;
|
||||
@@ -240,8 +241,10 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
case 'l':
|
||||
if (warnOnLdOpt)
|
||||
warning(WARNING_OBSOLETE,
|
||||
"Automatic `ld` to `ldh` optimization (the `-l` flag) is deprecated\n");
|
||||
warning(
|
||||
WARNING_OBSOLETE,
|
||||
"Automatic `ld` to `ldh` optimization (the `-l` flag) is deprecated\n"
|
||||
);
|
||||
else
|
||||
errx("`-L` and `-l` don't make sense together");
|
||||
optimizeLoads = true;
|
||||
@@ -371,7 +374,9 @@ int main(int argc, char *argv[])
|
||||
targetFileName = objectName;
|
||||
|
||||
if (argc == musl_optind) {
|
||||
fputs("FATAL: Please specify an input file (pass `-` to read from standard input)\n", stderr);
|
||||
fputs(
|
||||
"FATAL: Please specify an input file (pass `-` to read from standard input)\n", stderr
|
||||
);
|
||||
printUsage();
|
||||
exit(1);
|
||||
} else if (argc != musl_optind + 1) {
|
||||
@@ -387,7 +392,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (dependfile) {
|
||||
if (targetFileName.empty())
|
||||
errx("Dependency files can only be created if a target file is specified with either -o, -MQ or -MT");
|
||||
errx("Dependency files can only be created if a target file is specified with either "
|
||||
"-o, -MQ or -MT");
|
||||
|
||||
fprintf(dependfile, "%s: %s\n", targetFileName.c_str(), mainFileName);
|
||||
}
|
||||
|
||||
@@ -33,59 +33,48 @@ struct OptStackEntry {
|
||||
|
||||
static std::stack<OptStackEntry> stack;
|
||||
|
||||
void opt_B(char const chars[2])
|
||||
{
|
||||
void opt_B(char const chars[2]) {
|
||||
lexer_SetBinDigits(chars);
|
||||
}
|
||||
|
||||
void opt_G(char const chars[4])
|
||||
{
|
||||
void opt_G(char const chars[4]) {
|
||||
lexer_SetGfxDigits(chars);
|
||||
}
|
||||
|
||||
void opt_P(uint8_t padByte)
|
||||
{
|
||||
void opt_P(uint8_t padByte) {
|
||||
fillByte = padByte;
|
||||
}
|
||||
|
||||
void opt_Q(uint8_t precision)
|
||||
{
|
||||
void opt_Q(uint8_t precision) {
|
||||
fixPrecision = precision;
|
||||
}
|
||||
|
||||
void opt_R(size_t newDepth)
|
||||
{
|
||||
void opt_R(size_t newDepth) {
|
||||
fstk_NewRecursionDepth(newDepth);
|
||||
lexer_CheckRecursionDepth();
|
||||
}
|
||||
|
||||
void opt_H(bool warn)
|
||||
{
|
||||
void opt_H(bool warn) {
|
||||
warnOnHaltNop = warn;
|
||||
}
|
||||
|
||||
void opt_h(bool halt)
|
||||
{
|
||||
void opt_h(bool halt) {
|
||||
haltNop = halt;
|
||||
}
|
||||
|
||||
void opt_L(bool optimize)
|
||||
{
|
||||
void opt_L(bool optimize) {
|
||||
optimizeLoads = optimize;
|
||||
}
|
||||
|
||||
void opt_l(bool warn)
|
||||
{
|
||||
void opt_l(bool warn) {
|
||||
warnOnLdOpt = warn;
|
||||
}
|
||||
|
||||
void opt_W(char *flag)
|
||||
{
|
||||
void opt_W(char *flag) {
|
||||
processWarningFlag(flag);
|
||||
}
|
||||
|
||||
void opt_Parse(char *s)
|
||||
{
|
||||
void opt_Parse(char *s) {
|
||||
switch (s[0]) {
|
||||
case 'b':
|
||||
if (strlen(&s[1]) == 2)
|
||||
@@ -239,8 +228,7 @@ void opt_Parse(char *s)
|
||||
}
|
||||
}
|
||||
|
||||
void opt_Push()
|
||||
{
|
||||
void opt_Push() {
|
||||
OptStackEntry entry;
|
||||
|
||||
// Both of these are pulled from lexer.hpp
|
||||
@@ -273,8 +261,7 @@ void opt_Push()
|
||||
stack.push(entry);
|
||||
}
|
||||
|
||||
void opt_Pop()
|
||||
{
|
||||
void opt_Pop() {
|
||||
if (stack.empty()) {
|
||||
error("No entries in the option stack\n");
|
||||
return;
|
||||
|
||||
@@ -2,30 +2,31 @@
|
||||
|
||||
// Outputs an objectfile
|
||||
|
||||
#include "asm/output.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <deque>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/rpn.hpp"
|
||||
#include "asm/section.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#include "error.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
|
||||
struct Assertion {
|
||||
Patch patch;
|
||||
Section *section;
|
||||
@@ -42,8 +43,7 @@ static std::deque<Assertion> assertions;
|
||||
static std::deque<FileStackNode *> fileStackNodes;
|
||||
|
||||
// Write a long to a file (little-endian)
|
||||
static void putlong(uint32_t i, FILE *f)
|
||||
{
|
||||
static void putlong(uint32_t i, FILE *f) {
|
||||
putc(i, f);
|
||||
putc(i >> 8, f);
|
||||
putc(i >> 16, f);
|
||||
@@ -51,15 +51,13 @@ static void putlong(uint32_t i, FILE *f)
|
||||
}
|
||||
|
||||
// Write a NUL-terminated string to a file
|
||||
static void putstring(char const *s, FILE *f)
|
||||
{
|
||||
static void putstring(char const *s, FILE *f) {
|
||||
while (*s)
|
||||
putc(*s++, f);
|
||||
putc(0, f);
|
||||
}
|
||||
|
||||
void out_RegisterNode(FileStackNode *node)
|
||||
{
|
||||
void out_RegisterNode(FileStackNode *node) {
|
||||
// If node is not already registered, register it (and parents), and give it a unique ID
|
||||
for (; node && node->ID == (uint32_t)-1; node = node->parent) {
|
||||
node->ID = fileStackNodes.size();
|
||||
@@ -67,8 +65,7 @@ void out_RegisterNode(FileStackNode *node)
|
||||
}
|
||||
}
|
||||
|
||||
void out_ReplaceNode(FileStackNode * /* node */)
|
||||
{
|
||||
void out_ReplaceNode(FileStackNode * /* node */) {
|
||||
#if 0
|
||||
This is code intended to replace a node, which is pretty useless until ref counting is added...
|
||||
|
||||
@@ -84,8 +81,7 @@ This is code intended to replace a node, which is pretty useless until ref count
|
||||
}
|
||||
|
||||
// Return a section's ID, or -1 if the section is not in the list
|
||||
static uint32_t getSectIDIfAny(Section *sect)
|
||||
{
|
||||
static uint32_t getSectIDIfAny(Section *sect) {
|
||||
if (!sect)
|
||||
return (uint32_t)-1;
|
||||
|
||||
@@ -98,8 +94,7 @@ static uint32_t getSectIDIfAny(Section *sect)
|
||||
}
|
||||
|
||||
// Write a patch to a file
|
||||
static void writepatch(Patch const &patch, FILE *f)
|
||||
{
|
||||
static void writepatch(Patch const &patch, FILE *f) {
|
||||
assert(patch.src->ID != (uint32_t)-1);
|
||||
putlong(patch.src->ID, f);
|
||||
putlong(patch.lineNo, f);
|
||||
@@ -112,8 +107,7 @@ static void writepatch(Patch const &patch, FILE *f)
|
||||
}
|
||||
|
||||
// Write a section to a file
|
||||
static void writesection(Section const §, FILE *f)
|
||||
{
|
||||
static void writesection(Section const §, FILE *f) {
|
||||
putstring(sect.name.c_str(), f);
|
||||
|
||||
putlong(sect.size, f);
|
||||
@@ -138,8 +132,7 @@ static void writesection(Section const §, FILE *f)
|
||||
}
|
||||
|
||||
// Write a symbol to a file
|
||||
static void writesymbol(Symbol const &sym, FILE *f)
|
||||
{
|
||||
static void writesymbol(Symbol const &sym, FILE *f) {
|
||||
putstring(sym.name, f);
|
||||
if (!sym.isDefined()) {
|
||||
putc(SYMTYPE_IMPORT, f);
|
||||
@@ -154,8 +147,7 @@ static void writesymbol(Symbol const &sym, FILE *f)
|
||||
}
|
||||
}
|
||||
|
||||
static void registerSymbol(Symbol &sym)
|
||||
{
|
||||
static void registerSymbol(Symbol &sym) {
|
||||
sym.ID = objectSymbols.size();
|
||||
objectSymbols.push_back(&sym);
|
||||
out_RegisterNode(sym.src);
|
||||
@@ -163,19 +155,17 @@ static void registerSymbol(Symbol &sym)
|
||||
|
||||
// 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(Symbol &sym)
|
||||
{
|
||||
static uint32_t getSymbolID(Symbol &sym) {
|
||||
if (sym.ID == (uint32_t)-1 && !sym_IsPC(&sym))
|
||||
registerSymbol(sym);
|
||||
return sym.ID;
|
||||
}
|
||||
|
||||
static void writerpn(std::vector<uint8_t> &rpnexpr, const std::vector<uint8_t> &rpn)
|
||||
{
|
||||
static void writerpn(std::vector<uint8_t> &rpnexpr, const std::vector<uint8_t> &rpn) {
|
||||
char symName[512];
|
||||
size_t rpnptr = 0;
|
||||
|
||||
for (size_t offset = 0; offset < rpn.size(); ) {
|
||||
for (size_t offset = 0; offset < rpn.size();) {
|
||||
uint8_t rpndata = rpn[offset++];
|
||||
|
||||
switch (rpndata) {
|
||||
@@ -262,8 +252,7 @@ static void writerpn(std::vector<uint8_t> &rpnexpr, const std::vector<uint8_t> &
|
||||
}
|
||||
}
|
||||
|
||||
static void initpatch(Patch &patch, uint32_t type, Expression const &expr, uint32_t ofs)
|
||||
{
|
||||
static void initpatch(Patch &patch, uint32_t type, Expression const &expr, uint32_t ofs) {
|
||||
FileStackNode *node = fstk_GetFileStack();
|
||||
|
||||
patch.type = type;
|
||||
@@ -290,8 +279,7 @@ static void initpatch(Patch &patch, uint32_t type, Expression const &expr, uint3
|
||||
}
|
||||
|
||||
// Create a new patch (includes the rpn expr)
|
||||
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift)
|
||||
{
|
||||
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift) {
|
||||
// Add the patch to the list
|
||||
Patch &patch = currentSection->patches.emplace_front();
|
||||
|
||||
@@ -304,22 +292,21 @@ void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32
|
||||
}
|
||||
|
||||
// Creates an assert that will be written to the object file
|
||||
void out_CreateAssert(enum AssertionType type, Expression const &expr, char const *message, uint32_t ofs)
|
||||
{
|
||||
void out_CreateAssert(
|
||||
enum AssertionType type, Expression const &expr, char const *message, uint32_t ofs
|
||||
) {
|
||||
Assertion &assertion = assertions.emplace_front();
|
||||
|
||||
initpatch(assertion.patch, type, expr, ofs);
|
||||
assertion.message = message;
|
||||
}
|
||||
|
||||
static void writeassert(Assertion &assert, FILE *f)
|
||||
{
|
||||
static void writeassert(Assertion &assert, FILE *f) {
|
||||
writepatch(assert.patch, f);
|
||||
putstring(assert.message.c_str(), f);
|
||||
}
|
||||
|
||||
static void writeFileStackNode(FileStackNode const &node, FILE *f)
|
||||
{
|
||||
static void writeFileStackNode(FileStackNode const &node, FILE *f) {
|
||||
putlong(node.parent ? node.parent->ID : (uint32_t)-1, f);
|
||||
putlong(node.lineNo, f);
|
||||
putc(node.type, f);
|
||||
@@ -330,13 +317,12 @@ static void writeFileStackNode(FileStackNode const &node, FILE *f)
|
||||
|
||||
putlong(nodeIters.size(), f);
|
||||
// Iters are stored by decreasing depth, so reverse the order for output
|
||||
for (uint32_t i = nodeIters.size(); i--; )
|
||||
for (uint32_t i = nodeIters.size(); i--;)
|
||||
putlong(nodeIters[i], f);
|
||||
}
|
||||
}
|
||||
|
||||
static void registerUnregisteredSymbol(Symbol &sym)
|
||||
{
|
||||
static void registerUnregisteredSymbol(Symbol &sym) {
|
||||
// Check for symbol->src, to skip any built-in symbol from rgbasm
|
||||
if (sym.src && sym.ID == (uint32_t)-1) {
|
||||
registerSymbol(sym);
|
||||
@@ -344,8 +330,7 @@ static void registerUnregisteredSymbol(Symbol &sym)
|
||||
}
|
||||
|
||||
// Write an objectfile
|
||||
void out_WriteObject()
|
||||
{
|
||||
void out_WriteObject() {
|
||||
FILE *f;
|
||||
|
||||
if (strcmp(objectName, "-")) {
|
||||
@@ -374,9 +359,12 @@ void out_WriteObject()
|
||||
|
||||
// The list is supposed to have decrementing IDs
|
||||
if (it + 1 != fileStackNodes.end() && it[1]->ID != node->ID - 1)
|
||||
fatalerror("Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
|
||||
". Please report this to the developers!\n",
|
||||
it[1]->ID, node->ID);
|
||||
fatalerror(
|
||||
"Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
|
||||
". Please report this to the developers!\n",
|
||||
it[1]->ID,
|
||||
node->ID
|
||||
);
|
||||
}
|
||||
|
||||
for (Symbol const *sym : objectSymbols)
|
||||
@@ -394,8 +382,7 @@ void out_WriteObject()
|
||||
}
|
||||
|
||||
// Set the objectfilename
|
||||
void out_SetFileName(char *s)
|
||||
{
|
||||
void out_SetFileName(char *s) {
|
||||
if (objectName)
|
||||
warnx("Overriding output filename %s", objectName);
|
||||
objectName = s;
|
||||
|
||||
4002
src/asm/parser.y
4002
src/asm/parser.y
File diff suppressed because it is too large
Load Diff
166
src/asm/rpn.cpp
166
src/asm/rpn.cpp
@@ -2,6 +2,8 @@
|
||||
|
||||
// Controls RPN expressions for objectfiles
|
||||
|
||||
#include "asm/rpn.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
@@ -12,18 +14,16 @@
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "opmath.hpp"
|
||||
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/rpn.hpp"
|
||||
#include "asm/section.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#include "opmath.hpp"
|
||||
|
||||
// Init a RPN expression
|
||||
static void initExpression(Expression &expr)
|
||||
{
|
||||
static void initExpression(Expression &expr) {
|
||||
expr.reason = nullptr;
|
||||
expr.isKnown = true;
|
||||
expr.isSymbol = false;
|
||||
@@ -33,8 +33,7 @@ static void initExpression(Expression &expr)
|
||||
|
||||
// Makes an expression "not known", also setting its error message
|
||||
template<typename... Ts>
|
||||
static void makeUnknown(Expression &expr, Ts ...parts)
|
||||
{
|
||||
static void makeUnknown(Expression &expr, Ts... parts) {
|
||||
expr.isKnown = false;
|
||||
expr.reason = new std::string();
|
||||
if (!expr.reason)
|
||||
@@ -42,10 +41,9 @@ static void makeUnknown(Expression &expr, Ts ...parts)
|
||||
(expr.reason->append(parts), ...);
|
||||
}
|
||||
|
||||
static uint8_t *reserveSpace(Expression &expr, uint32_t size)
|
||||
{
|
||||
static uint8_t *reserveSpace(Expression &expr, uint32_t size) {
|
||||
if (!expr.rpn) {
|
||||
expr.rpn = new(std::nothrow) std::vector<uint8_t>();
|
||||
expr.rpn = new (std::nothrow) std::vector<uint8_t>();
|
||||
if (!expr.rpn)
|
||||
fatalerror("Failed to allocate RPN expression: %s\n", strerror(errno));
|
||||
}
|
||||
@@ -57,22 +55,19 @@ static uint8_t *reserveSpace(Expression &expr, uint32_t size)
|
||||
}
|
||||
|
||||
// Free the RPN expression
|
||||
void rpn_Free(Expression &expr)
|
||||
{
|
||||
void rpn_Free(Expression &expr) {
|
||||
delete expr.rpn;
|
||||
delete expr.reason;
|
||||
initExpression(expr);
|
||||
}
|
||||
|
||||
// Add symbols, constants and operators to expression
|
||||
void rpn_Number(Expression &expr, uint32_t val)
|
||||
{
|
||||
void rpn_Number(Expression &expr, uint32_t val) {
|
||||
initExpression(expr);
|
||||
expr.val = val;
|
||||
}
|
||||
|
||||
void rpn_Symbol(Expression &expr, char const *symName)
|
||||
{
|
||||
void rpn_Symbol(Expression &expr, char const *symName) {
|
||||
Symbol *sym = sym_FindScopedSymbol(symName);
|
||||
|
||||
if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
||||
@@ -98,8 +93,7 @@ void rpn_Symbol(Expression &expr, char const *symName)
|
||||
}
|
||||
}
|
||||
|
||||
static void bankSelf(Expression &expr)
|
||||
{
|
||||
static void bankSelf(Expression &expr) {
|
||||
initExpression(expr);
|
||||
|
||||
if (!currentSection) {
|
||||
@@ -114,8 +108,7 @@ static void bankSelf(Expression &expr)
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_BankSymbol(Expression &expr, char const *symName)
|
||||
{
|
||||
void rpn_BankSymbol(Expression &expr, char const *symName) {
|
||||
Symbol const *sym = sym_FindScopedSymbol(symName);
|
||||
|
||||
// The @ symbol is treated differently.
|
||||
@@ -147,8 +140,7 @@ void rpn_BankSymbol(Expression &expr, char const *symName)
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_BankSection(Expression &expr, char const *sectionName)
|
||||
{
|
||||
void rpn_BankSection(Expression &expr, char const *sectionName) {
|
||||
initExpression(expr);
|
||||
|
||||
Section *section = sect_FindSectionByName(sectionName);
|
||||
@@ -167,8 +159,7 @@ void rpn_BankSection(Expression &expr, char const *sectionName)
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_SizeOfSection(Expression &expr, char const *sectionName)
|
||||
{
|
||||
void rpn_SizeOfSection(Expression &expr, char const *sectionName) {
|
||||
initExpression(expr);
|
||||
|
||||
Section *section = sect_FindSectionByName(sectionName);
|
||||
@@ -187,8 +178,7 @@ void rpn_SizeOfSection(Expression &expr, char const *sectionName)
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_StartOfSection(Expression &expr, char const *sectionName)
|
||||
{
|
||||
void rpn_StartOfSection(Expression &expr, char const *sectionName) {
|
||||
initExpression(expr);
|
||||
|
||||
Section *section = sect_FindSectionByName(sectionName);
|
||||
@@ -207,8 +197,7 @@ void rpn_StartOfSection(Expression &expr, char const *sectionName)
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_SizeOfSectionType(Expression &expr, enum SectionType type)
|
||||
{
|
||||
void rpn_SizeOfSectionType(Expression &expr, enum SectionType type) {
|
||||
initExpression(expr);
|
||||
makeUnknown(expr, "Section type's size is not known");
|
||||
|
||||
@@ -219,8 +208,7 @@ void rpn_SizeOfSectionType(Expression &expr, enum SectionType type)
|
||||
*ptr++ = type;
|
||||
}
|
||||
|
||||
void rpn_StartOfSectionType(Expression &expr, enum SectionType type)
|
||||
{
|
||||
void rpn_StartOfSectionType(Expression &expr, enum SectionType type) {
|
||||
initExpression(expr);
|
||||
makeUnknown(expr, "Section type's start is not known");
|
||||
|
||||
@@ -231,8 +219,7 @@ void rpn_StartOfSectionType(Expression &expr, enum SectionType type)
|
||||
*ptr++ = type;
|
||||
}
|
||||
|
||||
void rpn_CheckHRAM(Expression &expr, const Expression &src)
|
||||
{
|
||||
void rpn_CheckHRAM(Expression &expr, const Expression &src) {
|
||||
expr = src;
|
||||
expr.isSymbol = false;
|
||||
|
||||
@@ -247,8 +234,7 @@ void rpn_CheckHRAM(Expression &expr, const Expression &src)
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_CheckRST(Expression &expr, const Expression &src)
|
||||
{
|
||||
void rpn_CheckRST(Expression &expr, const Expression &src) {
|
||||
expr = src;
|
||||
|
||||
if (expr.isKnown) {
|
||||
@@ -264,9 +250,8 @@ void rpn_CheckRST(Expression &expr, const Expression &src)
|
||||
}
|
||||
|
||||
// Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
||||
void rpn_CheckNBit(Expression const &expr, uint8_t n)
|
||||
{
|
||||
assert(n != 0); // That doesn't make sense
|
||||
void rpn_CheckNBit(Expression const &expr, uint8_t n) {
|
||||
assert(n != 0); // That doesn't make sense
|
||||
assert(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB
|
||||
|
||||
if (expr.isKnown) {
|
||||
@@ -279,8 +264,7 @@ void rpn_CheckNBit(Expression const &expr, uint8_t n)
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Expression::getConstVal() const
|
||||
{
|
||||
int32_t Expression::getConstVal() const {
|
||||
if (!isKnown) {
|
||||
error("Expected constant expression: %s\n", reason->c_str());
|
||||
return 0;
|
||||
@@ -288,8 +272,7 @@ int32_t Expression::getConstVal() const
|
||||
return val;
|
||||
}
|
||||
|
||||
void rpn_LOGNOT(Expression &expr, const Expression &src)
|
||||
{
|
||||
void rpn_LOGNOT(Expression &expr, const Expression &src) {
|
||||
expr = src;
|
||||
expr.isSymbol = false;
|
||||
|
||||
@@ -301,15 +284,13 @@ void rpn_LOGNOT(Expression &expr, const Expression &src)
|
||||
}
|
||||
}
|
||||
|
||||
Symbol const *Expression::symbolOf() const
|
||||
{
|
||||
Symbol const *Expression::symbolOf() const {
|
||||
if (!isSymbol)
|
||||
return nullptr;
|
||||
return sym_FindScopedSymbol((char const *)&(*rpn)[1]);
|
||||
}
|
||||
|
||||
bool Expression::isDiffConstant(Symbol const *sym) const
|
||||
{
|
||||
bool Expression::isDiffConstant(Symbol const *sym) const {
|
||||
// Check if both expressions only refer to a single symbol
|
||||
Symbol const *sym1 = symbolOf();
|
||||
|
||||
@@ -328,8 +309,7 @@ bool Expression::isDiffConstant(Symbol const *sym) const
|
||||
*
|
||||
* @return The constant result if it can be computed, or -1 otherwise.
|
||||
*/
|
||||
static int32_t tryConstMask(Expression const &lhs, Expression const &rhs)
|
||||
{
|
||||
static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
||||
Symbol const *lhsSymbol = lhs.symbolOf();
|
||||
Symbol const *rhsSymbol = lhsSymbol ? nullptr : rhs.symbolOf();
|
||||
bool lhsIsSymbol = lhsSymbol && lhsSymbol->getSection();
|
||||
@@ -362,8 +342,9 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs)
|
||||
return (symbolOfs + sect.alignOfs) & ~unknownBits;
|
||||
}
|
||||
|
||||
void rpn_BinaryOp(enum RPNCommand op, Expression &expr, const Expression &src1, const Expression &src2)
|
||||
{
|
||||
void rpn_BinaryOp(
|
||||
enum RPNCommand op, Expression &expr, const Expression &src1, const Expression &src2
|
||||
) {
|
||||
expr.isSymbol = false;
|
||||
int32_t constMaskVal;
|
||||
|
||||
@@ -417,43 +398,47 @@ void rpn_BinaryOp(enum RPNCommand op, Expression &expr, const Expression &src1,
|
||||
break;
|
||||
case RPN_SHL:
|
||||
if (src2.val < 0)
|
||||
warning(WARNING_SHIFT_AMOUNT,
|
||||
"Shifting left by negative amount %" PRId32 "\n",
|
||||
src2.val);
|
||||
warning(
|
||||
WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32 "\n", src2.val
|
||||
);
|
||||
|
||||
if (src2.val >= 32)
|
||||
warning(WARNING_SHIFT_AMOUNT,
|
||||
"Shifting left by large amount %" PRId32 "\n", src2.val);
|
||||
warning(
|
||||
WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", src2.val
|
||||
);
|
||||
|
||||
expr.val = op_shift_left(src1.val, src2.val);
|
||||
break;
|
||||
case RPN_SHR:
|
||||
if (src1.val < 0)
|
||||
warning(WARNING_SHIFT,
|
||||
"Shifting right negative value %" PRId32 "\n", src1.val);
|
||||
warning(WARNING_SHIFT, "Shifting right negative value %" PRId32 "\n", src1.val);
|
||||
|
||||
if (src2.val < 0)
|
||||
warning(WARNING_SHIFT_AMOUNT,
|
||||
"Shifting right by negative amount %" PRId32 "\n",
|
||||
src2.val);
|
||||
warning(
|
||||
WARNING_SHIFT_AMOUNT,
|
||||
"Shifting right by negative amount %" PRId32 "\n",
|
||||
src2.val
|
||||
);
|
||||
|
||||
if (src2.val >= 32)
|
||||
warning(WARNING_SHIFT_AMOUNT,
|
||||
"Shifting right by large amount %" PRId32 "\n",
|
||||
src2.val);
|
||||
warning(
|
||||
WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", src2.val
|
||||
);
|
||||
|
||||
expr.val = op_shift_right(src1.val, src2.val);
|
||||
break;
|
||||
case RPN_USHR:
|
||||
if (src2.val < 0)
|
||||
warning(WARNING_SHIFT_AMOUNT,
|
||||
"Shifting right by negative amount %" PRId32 "\n",
|
||||
src2.val);
|
||||
warning(
|
||||
WARNING_SHIFT_AMOUNT,
|
||||
"Shifting right by negative amount %" PRId32 "\n",
|
||||
src2.val
|
||||
);
|
||||
|
||||
if (src2.val >= 32)
|
||||
warning(WARNING_SHIFT_AMOUNT,
|
||||
"Shifting right by large amount %" PRId32 "\n",
|
||||
src2.val);
|
||||
warning(
|
||||
WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", src2.val
|
||||
);
|
||||
|
||||
expr.val = op_shift_right_unsigned(src1.val, src2.val);
|
||||
break;
|
||||
@@ -465,9 +450,12 @@ void rpn_BinaryOp(enum RPNCommand op, Expression &expr, const Expression &src1,
|
||||
fatalerror("Division by zero\n");
|
||||
|
||||
if (src1.val == INT32_MIN && src2.val == -1) {
|
||||
warning(WARNING_DIV,
|
||||
"Division of %" PRId32 " by -1 yields %" PRId32 "\n",
|
||||
INT32_MIN, INT32_MIN);
|
||||
warning(
|
||||
WARNING_DIV,
|
||||
"Division of %" PRId32 " by -1 yields %" PRId32 "\n",
|
||||
INT32_MIN,
|
||||
INT32_MIN
|
||||
);
|
||||
expr.val = INT32_MIN;
|
||||
} else {
|
||||
expr.val = op_divide(src1.val, src2.val);
|
||||
@@ -521,8 +509,13 @@ void rpn_BinaryOp(enum RPNCommand op, Expression &expr, const Expression &src1,
|
||||
// Convert the left-hand expression if it's constant
|
||||
if (src1.isKnown) {
|
||||
uint32_t lval = src1.val;
|
||||
uint8_t bytes[] = {RPN_CONST, (uint8_t)lval, (uint8_t)(lval >> 8),
|
||||
(uint8_t)(lval >> 16), (uint8_t)(lval >> 24)};
|
||||
uint8_t bytes[] = {
|
||||
RPN_CONST,
|
||||
(uint8_t)lval,
|
||||
(uint8_t)(lval >> 8),
|
||||
(uint8_t)(lval >> 16),
|
||||
(uint8_t)(lval >> 24),
|
||||
};
|
||||
expr.rpnPatchSize = sizeof(bytes);
|
||||
expr.rpn = nullptr;
|
||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
||||
@@ -545,8 +538,13 @@ void rpn_BinaryOp(enum RPNCommand op, Expression &expr, const Expression &src1,
|
||||
|
||||
// If the right expression is constant, merge a shim instead
|
||||
uint32_t rval = src2.val;
|
||||
uint8_t bytes[] = {RPN_CONST, (uint8_t)rval, (uint8_t)(rval >> 8),
|
||||
(uint8_t)(rval >> 16), (uint8_t)(rval >> 24)};
|
||||
uint8_t bytes[] = {
|
||||
RPN_CONST,
|
||||
(uint8_t)rval,
|
||||
(uint8_t)(rval >> 8),
|
||||
(uint8_t)(rval >> 16),
|
||||
(uint8_t)(rval >> 24),
|
||||
};
|
||||
if (src2.isKnown) {
|
||||
ptr = bytes;
|
||||
len = sizeof(bytes);
|
||||
@@ -569,23 +567,20 @@ void rpn_BinaryOp(enum RPNCommand op, Expression &expr, const Expression &src1,
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_HIGH(Expression &expr, const Expression &src)
|
||||
{
|
||||
void rpn_HIGH(Expression &expr, const Expression &src) {
|
||||
expr = src;
|
||||
expr.isSymbol = false;
|
||||
|
||||
if (expr.isKnown) {
|
||||
expr.val = (uint32_t)expr.val >> 8 & 0xFF;
|
||||
} else {
|
||||
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR,
|
||||
RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR, RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||
expr.rpnPatchSize += sizeof(bytes);
|
||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_LOW(Expression &expr, const Expression &src)
|
||||
{
|
||||
void rpn_LOW(Expression &expr, const Expression &src) {
|
||||
expr = src;
|
||||
expr.isSymbol = false;
|
||||
|
||||
@@ -599,16 +594,14 @@ void rpn_LOW(Expression &expr, const Expression &src)
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_ISCONST(Expression &expr, const Expression &src)
|
||||
{
|
||||
void rpn_ISCONST(Expression &expr, const Expression &src) {
|
||||
initExpression(expr);
|
||||
expr.val = src.isKnown;
|
||||
expr.isKnown = true;
|
||||
expr.isSymbol = false;
|
||||
}
|
||||
|
||||
void rpn_NEG(Expression &expr, const Expression &src)
|
||||
{
|
||||
void rpn_NEG(Expression &expr, const Expression &src) {
|
||||
expr = src;
|
||||
expr.isSymbol = false;
|
||||
|
||||
@@ -620,8 +613,7 @@ void rpn_NEG(Expression &expr, const Expression &src)
|
||||
}
|
||||
}
|
||||
|
||||
void rpn_NOT(Expression &expr, const Expression &src)
|
||||
{
|
||||
void rpn_NOT(Expression &expr, const Expression &src) {
|
||||
expr = src;
|
||||
expr.isSymbol = false;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "asm/section.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <deque>
|
||||
@@ -9,21 +11,20 @@
|
||||
#include <stack>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/rpn.hpp"
|
||||
#include "asm/section.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#include "error.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
|
||||
uint8_t fillByte;
|
||||
|
||||
struct UnionStackEntry {
|
||||
@@ -50,8 +51,7 @@ char const *currentLoadScope = nullptr;
|
||||
int32_t loadOffset; // Offset into the LOAD section's parent (see sect_GetOutputOffset)
|
||||
|
||||
// A quick check to see if we have an initialized section
|
||||
attr_(warn_unused_result) static bool checksection()
|
||||
{
|
||||
attr_(warn_unused_result) static bool checksection() {
|
||||
if (currentSection)
|
||||
return true;
|
||||
|
||||
@@ -61,35 +61,38 @@ attr_(warn_unused_result) static bool checksection()
|
||||
|
||||
// A quick check to see if we have an initialized section that can contain
|
||||
// this much initialized data
|
||||
attr_(warn_unused_result) static bool checkcodesection()
|
||||
{
|
||||
attr_(warn_unused_result) static bool checkcodesection() {
|
||||
if (!checksection())
|
||||
return false;
|
||||
|
||||
if (sect_HasData(currentSection->type))
|
||||
return true;
|
||||
|
||||
error("Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
|
||||
currentSection->name.c_str());
|
||||
error(
|
||||
"Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
|
||||
currentSection->name.c_str()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
attr_(warn_unused_result) static bool checkSectionSize(Section const §, uint32_t size)
|
||||
{
|
||||
attr_(warn_unused_result) static bool checkSectionSize(Section const §, uint32_t size) {
|
||||
uint32_t maxSize = sectionTypeInfo[sect.type].size;
|
||||
|
||||
// If the new size is reasonable, keep going
|
||||
if (size <= maxSize)
|
||||
return true;
|
||||
|
||||
error("Section '%s' grew too big (max size = 0x%" PRIX32
|
||||
" bytes, reached 0x%" PRIX32 ").\n", sect.name.c_str(), maxSize, size);
|
||||
error(
|
||||
"Section '%s' grew too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32 ").\n",
|
||||
sect.name.c_str(),
|
||||
maxSize,
|
||||
size
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the section has grown too much.
|
||||
attr_(warn_unused_result) static bool reserveSpace(uint32_t delta_size)
|
||||
{
|
||||
attr_(warn_unused_result) static bool reserveSpace(uint32_t delta_size) {
|
||||
// This check is here to trap broken code that generates sections that are too big and to
|
||||
// prevent the assembler from generating huge object files or trying to allocate too much
|
||||
// memory.
|
||||
@@ -97,20 +100,19 @@ attr_(warn_unused_result) static bool reserveSpace(uint32_t delta_size)
|
||||
|
||||
// If the section has already overflowed, skip the check to avoid erroring out ad nauseam
|
||||
if (currentSection->size != UINT32_MAX
|
||||
&& !checkSectionSize(*currentSection, curOffset + loadOffset + delta_size))
|
||||
&& !checkSectionSize(*currentSection, curOffset + loadOffset + delta_size))
|
||||
// Mark the section as overflowed, to avoid repeating the error
|
||||
currentSection->size = UINT32_MAX;
|
||||
|
||||
if (currentLoadSection && currentLoadSection->size != UINT32_MAX
|
||||
&& !checkSectionSize(*currentLoadSection, curOffset + delta_size))
|
||||
&& !checkSectionSize(*currentLoadSection, curOffset + delta_size))
|
||||
currentLoadSection->size = UINT32_MAX;
|
||||
|
||||
return currentSection->size != UINT32_MAX
|
||||
&& (!currentLoadSection || currentLoadSection->size != UINT32_MAX);
|
||||
&& (!currentLoadSection || currentLoadSection->size != UINT32_MAX);
|
||||
}
|
||||
|
||||
Section *sect_FindSectionByName(char const *name)
|
||||
{
|
||||
Section *sect_FindSectionByName(char const *name) {
|
||||
for (Section § : sectionList) {
|
||||
if (sect.name == name)
|
||||
return §
|
||||
@@ -119,14 +121,15 @@ Section *sect_FindSectionByName(char const *name)
|
||||
}
|
||||
|
||||
#define mask(align) ((1U << (align)) - 1)
|
||||
#define fail(...) do { \
|
||||
error(__VA_ARGS__); \
|
||||
nbSectErrors++; \
|
||||
} while (0)
|
||||
#define fail(...) \
|
||||
do { \
|
||||
error(__VA_ARGS__); \
|
||||
nbSectErrors++; \
|
||||
} while (0)
|
||||
|
||||
static unsigned int mergeSectUnion(Section §, enum SectionType type, uint32_t org,
|
||||
uint8_t alignment, uint16_t alignOffset)
|
||||
{
|
||||
static unsigned int mergeSectUnion(
|
||||
Section §, enum SectionType type, uint32_t org, uint8_t alignment, uint16_t alignOffset
|
||||
) {
|
||||
assert(alignment < 16); // Should be ensured by the caller
|
||||
unsigned int nbSectErrors = 0;
|
||||
|
||||
@@ -138,11 +141,15 @@ static unsigned int mergeSectUnion(Section §, enum SectionType type, uint32_
|
||||
if (org != (uint32_t)-1) {
|
||||
// If both are fixed, they must be the same
|
||||
if (sect.org != (uint32_t)-1 && sect.org != org)
|
||||
fail("Section already declared as fixed at different address $%04"
|
||||
PRIx32 "\n", sect.org);
|
||||
fail(
|
||||
"Section already declared as fixed at different address $%04" PRIx32 "\n", sect.org
|
||||
);
|
||||
else if (sect.align != 0 && (mask(sect.align) & (org - sect.alignOfs)))
|
||||
fail("Section already declared as aligned to %u bytes (offset %"
|
||||
PRIu16 ")\n", 1U << sect.align, sect.alignOfs);
|
||||
fail(
|
||||
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
||||
1U << sect.align,
|
||||
sect.alignOfs
|
||||
);
|
||||
else
|
||||
// Otherwise, just override
|
||||
sect.org = org;
|
||||
@@ -151,13 +158,18 @@ static unsigned int mergeSectUnion(Section §, enum SectionType type, uint32_
|
||||
// Make sure any fixed address given is compatible
|
||||
if (sect.org != (uint32_t)-1) {
|
||||
if ((sect.org - alignOffset) & mask(alignment))
|
||||
fail("Section already declared as fixed at incompatible address $%04"
|
||||
PRIx32 "\n", sect.org);
|
||||
// Check if alignment offsets are compatible
|
||||
fail(
|
||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||
sect.org
|
||||
);
|
||||
// Check if alignment offsets are compatible
|
||||
} else if ((alignOffset & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
||||
fail("Section already declared with incompatible %u"
|
||||
"-byte alignment (offset %" PRIu16 ")\n",
|
||||
1U << sect.align, sect.alignOfs);
|
||||
fail(
|
||||
"Section already declared with incompatible %u"
|
||||
"-byte alignment (offset %" PRIu16 ")\n",
|
||||
1U << sect.align,
|
||||
sect.alignOfs
|
||||
);
|
||||
} else if (alignment > sect.align) {
|
||||
// If the section is not fixed, its alignment is the largest of both
|
||||
sect.align = alignment;
|
||||
@@ -168,8 +180,8 @@ static unsigned int mergeSectUnion(Section §, enum SectionType type, uint32_
|
||||
return nbSectErrors;
|
||||
}
|
||||
|
||||
static unsigned int mergeFragments(Section §, uint32_t org, uint8_t alignment, uint16_t alignOffset)
|
||||
{
|
||||
static unsigned int
|
||||
mergeFragments(Section §, uint32_t org, uint8_t alignment, uint16_t alignOffset) {
|
||||
assert(alignment < 16); // Should be ensured by the caller
|
||||
unsigned int nbSectErrors = 0;
|
||||
|
||||
@@ -181,11 +193,16 @@ static unsigned int mergeFragments(Section §, uint32_t org, uint8_t alignmen
|
||||
|
||||
// If both are fixed, they must be the same
|
||||
if (sect.org != (uint32_t)-1 && sect.org != curOrg)
|
||||
fail("Section already declared as fixed at incompatible address $%04"
|
||||
PRIx32 "\n", sect.org);
|
||||
fail(
|
||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||
sect.org
|
||||
);
|
||||
else if (sect.align != 0 && (mask(sect.align) & (curOrg - sect.alignOfs)))
|
||||
fail("Section already declared as aligned to %u bytes (offset %"
|
||||
PRIu16 ")\n", 1U << sect.align, sect.alignOfs);
|
||||
fail(
|
||||
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n",
|
||||
1U << sect.align,
|
||||
sect.alignOfs
|
||||
);
|
||||
else
|
||||
// Otherwise, just override
|
||||
sect.org = curOrg;
|
||||
@@ -199,13 +216,18 @@ static unsigned int mergeFragments(Section §, uint32_t org, uint8_t alignmen
|
||||
// Make sure any fixed address given is compatible
|
||||
if (sect.org != (uint32_t)-1) {
|
||||
if ((sect.org - curOfs) & mask(alignment))
|
||||
fail("Section already declared as fixed at incompatible address $%04"
|
||||
PRIx32 "\n", sect.org);
|
||||
// Check if alignment offsets are compatible
|
||||
fail(
|
||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||
sect.org
|
||||
);
|
||||
// Check if alignment offsets are compatible
|
||||
} else if ((curOfs & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
|
||||
fail("Section already declared with incompatible %u"
|
||||
"-byte alignment (offset %" PRIu16 ")\n",
|
||||
1U << sect.align, sect.alignOfs);
|
||||
fail(
|
||||
"Section already declared with incompatible %u"
|
||||
"-byte alignment (offset %" PRIu16 ")\n",
|
||||
1U << sect.align,
|
||||
sect.alignOfs
|
||||
);
|
||||
} else if (alignment > sect.align) {
|
||||
// If the section is not fixed, its alignment is the largest of both
|
||||
sect.align = alignment;
|
||||
@@ -216,9 +238,15 @@ static unsigned int mergeFragments(Section §, uint32_t org, uint8_t alignmen
|
||||
return nbSectErrors;
|
||||
}
|
||||
|
||||
static void mergeSections(Section §, enum SectionType type, uint32_t org, uint32_t bank,
|
||||
uint8_t alignment, uint16_t alignOffset, enum SectionModifier mod)
|
||||
{
|
||||
static void mergeSections(
|
||||
Section §,
|
||||
enum SectionType type,
|
||||
uint32_t org,
|
||||
uint32_t bank,
|
||||
uint8_t alignment,
|
||||
uint16_t alignOffset,
|
||||
enum SectionModifier mod
|
||||
) {
|
||||
unsigned int nbSectErrors = 0;
|
||||
|
||||
if (type != sect.type)
|
||||
@@ -230,9 +258,9 @@ static void mergeSections(Section §, enum SectionType type, uint32_t org, ui
|
||||
switch (mod) {
|
||||
case SECTION_UNION:
|
||||
case SECTION_FRAGMENT:
|
||||
nbSectErrors += mod == SECTION_UNION ?
|
||||
mergeSectUnion(sect, type, org, alignment, alignOffset) :
|
||||
mergeFragments(sect, org, alignment, alignOffset);
|
||||
nbSectErrors += mod == SECTION_UNION
|
||||
? mergeSectUnion(sect, type, org, alignment, alignOffset)
|
||||
: mergeFragments(sect, org, alignment, alignOffset);
|
||||
|
||||
// Common checks
|
||||
|
||||
@@ -241,8 +269,7 @@ static void mergeSections(Section §, enum SectionType type, uint32_t org, ui
|
||||
sect.bank = bank;
|
||||
// If both specify a bank, it must be the same one
|
||||
else if (bank != (uint32_t)-1 && sect.bank != bank)
|
||||
fail("Section already declared with different bank %" PRIu32 "\n",
|
||||
sect.bank);
|
||||
fail("Section already declared with different bank %" PRIu32 "\n", sect.bank);
|
||||
break;
|
||||
|
||||
case SECTION_NORMAL:
|
||||
@@ -254,16 +281,26 @@ static void mergeSections(Section §, enum SectionType type, uint32_t org, ui
|
||||
}
|
||||
|
||||
if (nbSectErrors)
|
||||
fatalerror("Cannot create section \"%s\" (%u error%s)\n",
|
||||
sect.name.c_str(), nbSectErrors, nbSectErrors == 1 ? "" : "s");
|
||||
fatalerror(
|
||||
"Cannot create section \"%s\" (%u error%s)\n",
|
||||
sect.name.c_str(),
|
||||
nbSectErrors,
|
||||
nbSectErrors == 1 ? "" : "s"
|
||||
);
|
||||
}
|
||||
|
||||
#undef fail
|
||||
|
||||
// Create a new section, not yet in the list.
|
||||
static Section *createSection(char const *name, enum SectionType type, uint32_t org, uint32_t bank,
|
||||
uint8_t alignment, uint16_t alignOffset, enum SectionModifier mod)
|
||||
{
|
||||
static Section *createSection(
|
||||
char const *name,
|
||||
enum SectionType type,
|
||||
uint32_t org,
|
||||
uint32_t bank,
|
||||
uint8_t alignment,
|
||||
uint16_t alignOffset,
|
||||
enum SectionModifier mod
|
||||
) {
|
||||
// Add the new section to the list (order doesn't matter)
|
||||
Section § = sectionList.emplace_front();
|
||||
|
||||
@@ -286,9 +323,13 @@ static Section *createSection(char const *name, enum SectionType type, uint32_t
|
||||
}
|
||||
|
||||
// Find a section by name and type. If it doesn't exist, create it.
|
||||
static Section *getSection(char const *name, enum SectionType type, uint32_t org,
|
||||
SectionSpec const &attrs, enum SectionModifier mod)
|
||||
{
|
||||
static Section *getSection(
|
||||
char const *name,
|
||||
enum SectionType type,
|
||||
uint32_t org,
|
||||
SectionSpec const &attrs,
|
||||
enum SectionModifier mod
|
||||
) {
|
||||
uint32_t bank = attrs.bank;
|
||||
uint8_t alignment = attrs.alignment;
|
||||
uint16_t alignOffset = attrs.alignOfs;
|
||||
@@ -296,29 +337,41 @@ static Section *getSection(char const *name, enum SectionType type, uint32_t org
|
||||
// First, validate parameters, and normalize them if applicable
|
||||
|
||||
if (bank != (uint32_t)-1) {
|
||||
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
|
||||
&& type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
|
||||
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM
|
||||
&& type != SECTTYPE_WRAMX)
|
||||
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
|
||||
else if (bank < sectionTypeInfo[type].firstBank || bank > sectionTypeInfo[type].lastBank)
|
||||
error("%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04"
|
||||
PRIx32 ")\n", sectionTypeInfo[type].name.c_str(), bank,
|
||||
sectionTypeInfo[type].firstBank, sectionTypeInfo[type].lastBank);
|
||||
error(
|
||||
"%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04" PRIx32 ")\n",
|
||||
sectionTypeInfo[type].name.c_str(),
|
||||
bank,
|
||||
sectionTypeInfo[type].firstBank,
|
||||
sectionTypeInfo[type].lastBank
|
||||
);
|
||||
} else if (nbbanks(type) == 1) {
|
||||
// If the section type only has a single bank, implicitly force it
|
||||
bank = sectionTypeInfo[type].firstBank;
|
||||
}
|
||||
|
||||
if (alignOffset >= 1 << alignment) {
|
||||
error("Alignment offset (%" PRIu16 ") must be smaller than alignment size (%u)\n",
|
||||
alignOffset, 1U << alignment);
|
||||
error(
|
||||
"Alignment offset (%" PRIu16 ") must be smaller than alignment size (%u)\n",
|
||||
alignOffset,
|
||||
1U << alignment
|
||||
);
|
||||
alignOffset = 0;
|
||||
}
|
||||
|
||||
if (org != (uint32_t)-1) {
|
||||
if (org < sectionTypeInfo[type].startAddr || org > endaddr(type))
|
||||
error("Section \"%s\"'s fixed address $%04" PRIx32
|
||||
" is outside of range [$%04" PRIx16 "; $%04" PRIx16 "]\n",
|
||||
name, org, sectionTypeInfo[type].startAddr, endaddr(type));
|
||||
error(
|
||||
"Section \"%s\"'s fixed address $%04" PRIx32 " is outside of range [$%04" PRIx16
|
||||
"; $%04" PRIx16 "]\n",
|
||||
name,
|
||||
org,
|
||||
sectionTypeInfo[type].startAddr,
|
||||
endaddr(type)
|
||||
);
|
||||
}
|
||||
|
||||
if (alignment != 0) {
|
||||
@@ -331,12 +384,14 @@ static Section *getSection(char const *name, enum SectionType type, uint32_t org
|
||||
|
||||
if (org != (uint32_t)-1) {
|
||||
if ((org - alignOffset) & mask)
|
||||
error("Section \"%s\"'s fixed address doesn't match its alignment\n",
|
||||
name);
|
||||
error("Section \"%s\"'s fixed address doesn't match its alignment\n", name);
|
||||
alignment = 0; // Ignore it if it's satisfied
|
||||
} else if (sectionTypeInfo[type].startAddr & mask) {
|
||||
error("Section \"%s\"'s alignment cannot be attained in %s\n",
|
||||
name, sectionTypeInfo[type].name.c_str());
|
||||
error(
|
||||
"Section \"%s\"'s alignment cannot be attained in %s\n",
|
||||
name,
|
||||
sectionTypeInfo[type].name.c_str()
|
||||
);
|
||||
alignment = 0; // Ignore it if it's unattainable
|
||||
org = 0;
|
||||
} else if (alignment == 16) {
|
||||
@@ -361,16 +416,14 @@ static Section *getSection(char const *name, enum SectionType type, uint32_t org
|
||||
}
|
||||
|
||||
// Set the current section
|
||||
static void changeSection()
|
||||
{
|
||||
static void changeSection() {
|
||||
if (!currentUnionStack.empty())
|
||||
fatalerror("Cannot change the section within a UNION\n");
|
||||
|
||||
sym_SetCurrentSymbolScope(nullptr);
|
||||
}
|
||||
|
||||
bool Section::isSizeKnown() const
|
||||
{
|
||||
bool Section::isSizeKnown() const {
|
||||
// SECTION UNION and SECTION FRAGMENT can still grow
|
||||
if (modifier != SECTION_NORMAL)
|
||||
return false;
|
||||
@@ -389,9 +442,13 @@ bool Section::isSizeKnown() const
|
||||
}
|
||||
|
||||
// Set the current section by name and type
|
||||
void sect_NewSection(char const *name, enum SectionType type, uint32_t org,
|
||||
SectionSpec const &attrs, enum SectionModifier mod)
|
||||
{
|
||||
void sect_NewSection(
|
||||
char const *name,
|
||||
enum SectionType type,
|
||||
uint32_t org,
|
||||
SectionSpec const &attrs,
|
||||
enum SectionModifier mod
|
||||
) {
|
||||
if (currentLoadSection)
|
||||
fatalerror("Cannot change the section within a `LOAD` block\n");
|
||||
|
||||
@@ -409,9 +466,13 @@ void sect_NewSection(char const *name, enum SectionType type, uint32_t org,
|
||||
}
|
||||
|
||||
// Set the current section by name and type
|
||||
void sect_SetLoadSection(char const *name, enum SectionType type, uint32_t org,
|
||||
SectionSpec const &attrs, enum SectionModifier mod)
|
||||
{
|
||||
void sect_SetLoadSection(
|
||||
char const *name,
|
||||
enum SectionType type,
|
||||
uint32_t org,
|
||||
SectionSpec const &attrs,
|
||||
enum SectionModifier mod
|
||||
) {
|
||||
// Important info: currently, UNION and LOAD cannot interact, since UNION is prohibited in
|
||||
// "code" sections, whereas LOAD is restricted to them.
|
||||
// Therefore, any interactions are NOT TESTED, so lift either of those restrictions at
|
||||
@@ -444,8 +505,7 @@ void sect_SetLoadSection(char const *name, enum SectionType type, uint32_t org,
|
||||
currentLoadSection = sect;
|
||||
}
|
||||
|
||||
void sect_EndLoadSection()
|
||||
{
|
||||
void sect_EndLoadSection() {
|
||||
if (!currentLoadSection) {
|
||||
error("Found `ENDL` outside of a `LOAD` block\n");
|
||||
return;
|
||||
@@ -458,25 +518,21 @@ void sect_EndLoadSection()
|
||||
sym_SetCurrentSymbolScope(currentLoadScope);
|
||||
}
|
||||
|
||||
Section *sect_GetSymbolSection()
|
||||
{
|
||||
Section *sect_GetSymbolSection() {
|
||||
return currentLoadSection ? currentLoadSection : currentSection;
|
||||
}
|
||||
|
||||
// The offset into the section above
|
||||
uint32_t sect_GetSymbolOffset()
|
||||
{
|
||||
uint32_t sect_GetSymbolOffset() {
|
||||
return curOffset;
|
||||
}
|
||||
|
||||
uint32_t sect_GetOutputOffset()
|
||||
{
|
||||
uint32_t sect_GetOutputOffset() {
|
||||
return curOffset + loadOffset;
|
||||
}
|
||||
|
||||
// Returns how many bytes need outputting for the specified alignment and offset to succeed
|
||||
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset)
|
||||
{
|
||||
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset) {
|
||||
Section *sect = sect_GetSymbolSection();
|
||||
if (!sect)
|
||||
return 0;
|
||||
@@ -492,11 +548,10 @@ uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset)
|
||||
// We need `(pcValue + curOffset + return value) % (1 << alignment) == offset`
|
||||
uint16_t pcValue = isFixed ? sect->org : sect->alignOfs;
|
||||
return static_cast<uint16_t>(offset - curOffset - pcValue)
|
||||
% (1u << std::min(alignment, curAlignment));
|
||||
% (1u << std::min(alignment, curAlignment));
|
||||
}
|
||||
|
||||
void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
||||
{
|
||||
void sect_AlignPC(uint8_t alignment, uint16_t offset) {
|
||||
if (!checksection())
|
||||
return;
|
||||
|
||||
@@ -505,12 +560,16 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
||||
|
||||
if (sect->org != (uint32_t)-1) {
|
||||
if ((sect->org + curOffset - offset) % alignSize)
|
||||
error("Section's fixed address fails required alignment (PC = $%04" PRIx32
|
||||
")\n", sect->org + curOffset);
|
||||
} else if (sect->align != 0 && (((sect->alignOfs + curOffset) % (1u << sect->align))
|
||||
- offset) % alignSize) {
|
||||
error("Section's alignment fails required alignment (offset from section start = $%04"
|
||||
PRIx32 ")\n", curOffset);
|
||||
error(
|
||||
"Section's fixed address fails required alignment (PC = $%04" PRIx32 ")\n",
|
||||
sect->org + curOffset
|
||||
);
|
||||
} else if (sect->align != 0 && (((sect->alignOfs + curOffset) % (1u << sect->align)) - offset) % alignSize) {
|
||||
error(
|
||||
"Section's alignment fails required alignment (offset from section start = $%04" PRIx32
|
||||
")\n",
|
||||
curOffset
|
||||
);
|
||||
} else if (alignment >= 16) {
|
||||
// Treat an alignment large enough as fixing the address.
|
||||
// Note that this also ensures that a section's alignment never becomes 16 or greater.
|
||||
@@ -526,8 +585,7 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
||||
}
|
||||
}
|
||||
|
||||
static void growSection(uint32_t growth)
|
||||
{
|
||||
static void growSection(uint32_t growth) {
|
||||
curOffset += growth;
|
||||
if (curOffset + loadOffset > currentSection->size)
|
||||
currentSection->size = curOffset + loadOffset;
|
||||
@@ -535,33 +593,28 @@ static void growSection(uint32_t growth)
|
||||
currentLoadSection->size = curOffset;
|
||||
}
|
||||
|
||||
static void writebyte(uint8_t byte)
|
||||
{
|
||||
static void writebyte(uint8_t byte) {
|
||||
currentSection->data[sect_GetOutputOffset()] = byte;
|
||||
growSection(1);
|
||||
}
|
||||
|
||||
static void writeword(uint16_t b)
|
||||
{
|
||||
static void writeword(uint16_t b) {
|
||||
writebyte(b & 0xFF);
|
||||
writebyte(b >> 8);
|
||||
}
|
||||
|
||||
static void writelong(uint32_t b)
|
||||
{
|
||||
static void writelong(uint32_t b) {
|
||||
writebyte(b & 0xFF);
|
||||
writebyte(b >> 8);
|
||||
writebyte(b >> 16);
|
||||
writebyte(b >> 24);
|
||||
}
|
||||
|
||||
static void createPatch(enum PatchType type, Expression const &expr, uint32_t pcShift)
|
||||
{
|
||||
static void createPatch(enum PatchType type, Expression const &expr, uint32_t pcShift) {
|
||||
out_CreatePatch(type, expr, sect_GetOutputOffset(), pcShift);
|
||||
}
|
||||
|
||||
void sect_StartUnion()
|
||||
{
|
||||
void sect_StartUnion() {
|
||||
// Important info: currently, UNION and LOAD cannot interact, since UNION is prohibited in
|
||||
// "code" sections, whereas LOAD is restricted to them.
|
||||
// Therefore, any interactions are NOT TESTED, so lift either of those restrictions at
|
||||
@@ -576,11 +629,10 @@ void sect_StartUnion()
|
||||
return;
|
||||
}
|
||||
|
||||
currentUnionStack.push({ .start = curOffset, .size = 0 });
|
||||
currentUnionStack.push({.start = curOffset, .size = 0});
|
||||
}
|
||||
|
||||
static void endUnionMember()
|
||||
{
|
||||
static void endUnionMember() {
|
||||
UnionStackEntry &member = currentUnionStack.top();
|
||||
uint32_t memberSize = curOffset - member.start;
|
||||
|
||||
@@ -589,8 +641,7 @@ static void endUnionMember()
|
||||
curOffset = member.start;
|
||||
}
|
||||
|
||||
void sect_NextUnionMember()
|
||||
{
|
||||
void sect_NextUnionMember() {
|
||||
if (currentUnionStack.empty()) {
|
||||
error("Found NEXTU outside of a UNION construct\n");
|
||||
return;
|
||||
@@ -598,8 +649,7 @@ void sect_NextUnionMember()
|
||||
endUnionMember();
|
||||
}
|
||||
|
||||
void sect_EndUnion()
|
||||
{
|
||||
void sect_EndUnion() {
|
||||
if (currentUnionStack.empty()) {
|
||||
error("Found ENDU outside of a UNION construct\n");
|
||||
return;
|
||||
@@ -609,15 +659,13 @@ void sect_EndUnion()
|
||||
currentUnionStack.pop();
|
||||
}
|
||||
|
||||
void sect_CheckUnionClosed()
|
||||
{
|
||||
void sect_CheckUnionClosed() {
|
||||
if (!currentUnionStack.empty())
|
||||
error("Unterminated UNION construct\n");
|
||||
}
|
||||
|
||||
// Output an absolute byte
|
||||
void sect_AbsByte(uint8_t b)
|
||||
{
|
||||
void sect_AbsByte(uint8_t b) {
|
||||
if (!checkcodesection())
|
||||
return;
|
||||
if (!reserveSpace(1))
|
||||
@@ -626,8 +674,7 @@ void sect_AbsByte(uint8_t b)
|
||||
writebyte(b);
|
||||
}
|
||||
|
||||
void sect_AbsByteGroup(uint8_t const *s, size_t length)
|
||||
{
|
||||
void sect_AbsByteGroup(uint8_t const *s, size_t length) {
|
||||
if (!checkcodesection())
|
||||
return;
|
||||
if (!reserveSpace(length))
|
||||
@@ -637,8 +684,7 @@ void sect_AbsByteGroup(uint8_t const *s, size_t length)
|
||||
writebyte(*s++);
|
||||
}
|
||||
|
||||
void sect_AbsWordGroup(uint8_t const *s, size_t length)
|
||||
{
|
||||
void sect_AbsWordGroup(uint8_t const *s, size_t length) {
|
||||
if (!checkcodesection())
|
||||
return;
|
||||
if (!reserveSpace(length * 2))
|
||||
@@ -648,8 +694,7 @@ void sect_AbsWordGroup(uint8_t const *s, size_t length)
|
||||
writeword(*s++);
|
||||
}
|
||||
|
||||
void sect_AbsLongGroup(uint8_t const *s, size_t length)
|
||||
{
|
||||
void sect_AbsLongGroup(uint8_t const *s, size_t length) {
|
||||
if (!checkcodesection())
|
||||
return;
|
||||
if (!reserveSpace(length * 4))
|
||||
@@ -660,8 +705,7 @@ void sect_AbsLongGroup(uint8_t const *s, size_t length)
|
||||
}
|
||||
|
||||
// Skip this many bytes
|
||||
void sect_Skip(uint32_t skip, bool ds)
|
||||
{
|
||||
void sect_Skip(uint32_t skip, bool ds) {
|
||||
if (!checksection())
|
||||
return;
|
||||
if (!reserveSpace(skip))
|
||||
@@ -671,8 +715,13 @@ void sect_Skip(uint32_t skip, bool ds)
|
||||
growSection(skip);
|
||||
} else {
|
||||
if (!ds)
|
||||
warning(WARNING_EMPTY_DATA_DIRECTIVE, "%s directive without data in ROM\n",
|
||||
(skip == 4) ? "DL" : (skip == 2) ? "DW" : "DB");
|
||||
warning(
|
||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||
"%s directive without data in ROM\n",
|
||||
(skip == 4) ? "DL"
|
||||
: (skip == 2) ? "DW"
|
||||
: "DB"
|
||||
);
|
||||
// We know we're in a code SECTION
|
||||
while (skip--)
|
||||
writebyte(fillByte);
|
||||
@@ -681,8 +730,7 @@ void sect_Skip(uint32_t skip, bool ds)
|
||||
|
||||
// Output a relocatable byte. Checking will be done to see if it
|
||||
// is an absolute value in disguise.
|
||||
void sect_RelByte(Expression &expr, uint32_t pcShift)
|
||||
{
|
||||
void sect_RelByte(Expression &expr, uint32_t pcShift) {
|
||||
if (!checkcodesection())
|
||||
return;
|
||||
if (!reserveSpace(1))
|
||||
@@ -699,8 +747,7 @@ void sect_RelByte(Expression &expr, uint32_t pcShift)
|
||||
|
||||
// Output several copies of a relocatable byte. Checking will be done to see if
|
||||
// it is an absolute value in disguise.
|
||||
void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs)
|
||||
{
|
||||
void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs) {
|
||||
if (!checkcodesection())
|
||||
return;
|
||||
if (!reserveSpace(n))
|
||||
@@ -723,8 +770,7 @@ void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs)
|
||||
|
||||
// Output a relocatable word. Checking will be done to see if
|
||||
// it's an absolute value in disguise.
|
||||
void sect_RelWord(Expression &expr, uint32_t pcShift)
|
||||
{
|
||||
void sect_RelWord(Expression &expr, uint32_t pcShift) {
|
||||
if (!checkcodesection())
|
||||
return;
|
||||
if (!reserveSpace(2))
|
||||
@@ -741,8 +787,7 @@ void sect_RelWord(Expression &expr, uint32_t pcShift)
|
||||
|
||||
// Output a relocatable longword. Checking will be done to see if
|
||||
// is an absolute value in disguise.
|
||||
void sect_RelLong(Expression &expr, uint32_t pcShift)
|
||||
{
|
||||
void sect_RelLong(Expression &expr, uint32_t pcShift) {
|
||||
if (!checkcodesection())
|
||||
return;
|
||||
if (!reserveSpace(2))
|
||||
@@ -759,8 +804,7 @@ void sect_RelLong(Expression &expr, uint32_t pcShift)
|
||||
|
||||
// Output a PC-relative relocatable byte. Checking will be done to see if it
|
||||
// is an absolute value in disguise.
|
||||
void sect_PCRelByte(Expression &expr, uint32_t pcShift)
|
||||
{
|
||||
void sect_PCRelByte(Expression &expr, uint32_t pcShift) {
|
||||
if (!checkcodesection())
|
||||
return;
|
||||
if (!reserveSpace(1))
|
||||
@@ -782,8 +826,7 @@ void sect_PCRelByte(Expression &expr, uint32_t pcShift)
|
||||
offset = sym->getValue() - (pc->getValue() + 1);
|
||||
|
||||
if (offset < -128 || offset > 127) {
|
||||
error("jr target out of reach (expected -129 < %" PRId16 " < 128)\n",
|
||||
offset);
|
||||
error("jr target out of reach (expected -129 < %" PRId16 " < 128)\n", offset);
|
||||
writebyte(0);
|
||||
} else {
|
||||
writebyte(offset);
|
||||
@@ -793,8 +836,7 @@ void sect_PCRelByte(Expression &expr, uint32_t pcShift)
|
||||
}
|
||||
|
||||
// Output a binary file
|
||||
void sect_BinaryFile(char const *s, int32_t startPos)
|
||||
{
|
||||
void sect_BinaryFile(char const *s, int32_t startPos) {
|
||||
if (startPos < 0) {
|
||||
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
|
||||
startPos = 0;
|
||||
@@ -834,8 +876,7 @@ void sect_BinaryFile(char const *s, int32_t startPos)
|
||||
goto cleanup;
|
||||
} else {
|
||||
if (errno != ESPIPE)
|
||||
error("Error determining size of INCBIN file '%s': %s\n",
|
||||
s, strerror(errno));
|
||||
error("Error determining size of INCBIN file '%s': %s\n", s, strerror(errno));
|
||||
// The file isn't seekable, so we'll just skip bytes
|
||||
while (startPos--)
|
||||
(void)fgetc(f);
|
||||
@@ -854,8 +895,7 @@ cleanup:
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
||||
{
|
||||
void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length) {
|
||||
if (start_pos < 0) {
|
||||
error("Start position cannot be negative (%" PRId32 ")\n", start_pos);
|
||||
start_pos = 0;
|
||||
@@ -900,16 +940,20 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
||||
}
|
||||
|
||||
if ((start_pos + length) > fsize) {
|
||||
error("Specified range in INCBIN is out of bounds (%" PRIu32 " + %" PRIu32
|
||||
" > %" PRIu32 ")\n", start_pos, length, fsize);
|
||||
error(
|
||||
"Specified range in INCBIN is out of bounds (%" PRIu32 " + %" PRIu32 " > %" PRIu32
|
||||
")\n",
|
||||
start_pos,
|
||||
length,
|
||||
fsize
|
||||
);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fseek(f, start_pos, SEEK_SET);
|
||||
} else {
|
||||
if (errno != ESPIPE)
|
||||
error("Error determining size of INCBIN file '%s': %s\n",
|
||||
s, strerror(errno));
|
||||
error("Error determining size of INCBIN file '%s': %s\n", s, strerror(errno));
|
||||
// The file isn't seekable, so we'll just skip bytes
|
||||
while (start_pos--)
|
||||
(void)fgetc(f);
|
||||
@@ -923,8 +967,7 @@ void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
|
||||
} else if (ferror(f)) {
|
||||
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
|
||||
} else {
|
||||
error("Premature end of file (%" PRId32 " bytes left to read)\n",
|
||||
length + 1);
|
||||
error("Premature end of file (%" PRId32 " bytes left to read)\n", length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,15 +976,14 @@ cleanup:
|
||||
}
|
||||
|
||||
// Section stack routines
|
||||
void sect_PushSection()
|
||||
{
|
||||
void sect_PushSection() {
|
||||
sectionStack.push_front({
|
||||
.section = currentSection,
|
||||
.loadSection = currentLoadSection,
|
||||
.scope = sym_GetCurrentSymbolScope(),
|
||||
.offset = curOffset,
|
||||
.loadOffset = loadOffset,
|
||||
.unionStack = {},
|
||||
.section = currentSection,
|
||||
.loadSection = currentLoadSection,
|
||||
.scope = sym_GetCurrentSymbolScope(),
|
||||
.offset = curOffset,
|
||||
.loadOffset = loadOffset,
|
||||
.unionStack = {},
|
||||
});
|
||||
|
||||
// Reset the section scope
|
||||
@@ -951,8 +993,7 @@ void sect_PushSection()
|
||||
std::swap(currentUnionStack, sectionStack.front().unionStack);
|
||||
}
|
||||
|
||||
void sect_PopSection()
|
||||
{
|
||||
void sect_PopSection() {
|
||||
if (sectionStack.empty())
|
||||
fatalerror("No entries in the section stack\n");
|
||||
|
||||
@@ -971,8 +1012,7 @@ void sect_PopSection()
|
||||
std::swap(currentUnionStack, entry.unionStack);
|
||||
}
|
||||
|
||||
void sect_EndSection()
|
||||
{
|
||||
void sect_EndSection() {
|
||||
if (!currentSection)
|
||||
fatalerror("Cannot end the section outside of a SECTION\n");
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "asm/symbol.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
@@ -7,26 +9,25 @@
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <time.h>
|
||||
#include <variant>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "util.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include "asm/fixpoint.hpp"
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/macro.hpp"
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/section.hpp"
|
||||
#include "asm/symbol.hpp"
|
||||
#include "util.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#include "error.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
std::map<std::string, Symbol> symbols;
|
||||
|
||||
static const char *labelScope; // Current section's label scope
|
||||
@@ -38,19 +39,16 @@ static char savedTIMESTAMP_ISO8601_LOCAL[256];
|
||||
static char savedTIMESTAMP_ISO8601_UTC[256];
|
||||
static bool exportAll;
|
||||
|
||||
bool sym_IsPC(Symbol const *sym)
|
||||
{
|
||||
bool sym_IsPC(Symbol const *sym) {
|
||||
return sym == PCSymbol;
|
||||
}
|
||||
|
||||
void sym_ForEach(void (*callback)(Symbol &))
|
||||
{
|
||||
void sym_ForEach(void (*callback)(Symbol &)) {
|
||||
for (auto &it : symbols)
|
||||
callback(it.second);
|
||||
}
|
||||
|
||||
static int32_t Callback_NARG()
|
||||
{
|
||||
static int32_t Callback_NARG() {
|
||||
if (!macro_GetCurrentArgs()) {
|
||||
error("_NARG does not make sense outside of a macro\n");
|
||||
return 0;
|
||||
@@ -58,15 +56,13 @@ static int32_t Callback_NARG()
|
||||
return macro_NbArgs();
|
||||
}
|
||||
|
||||
static int32_t CallbackPC()
|
||||
{
|
||||
static int32_t CallbackPC() {
|
||||
Section const *section = sect_GetSymbolSection();
|
||||
|
||||
return section ? section->org + sect_GetSymbolOffset() : 0;
|
||||
}
|
||||
|
||||
int32_t Symbol::getValue() const
|
||||
{
|
||||
int32_t Symbol::getValue() const {
|
||||
assert(std::holds_alternative<int32_t>(data) || std::holds_alternative<int32_t (*)()>(data));
|
||||
if (int32_t const *value = std::get_if<int32_t>(&data); value) {
|
||||
// TODO: do not use section's org directly
|
||||
@@ -75,29 +71,28 @@ int32_t Symbol::getValue() const
|
||||
return getOutputValue();
|
||||
}
|
||||
|
||||
int32_t Symbol::getOutputValue() const
|
||||
{
|
||||
return std::visit(Visitor{
|
||||
[](int32_t value) -> int32_t { return value; },
|
||||
[](int32_t (*callback)()) -> int32_t { return callback(); },
|
||||
[](auto &) -> int32_t { return 0; }
|
||||
}, data);
|
||||
int32_t Symbol::getOutputValue() const {
|
||||
return std::visit(
|
||||
Visitor{
|
||||
[](int32_t value) -> int32_t { return value; },
|
||||
[](int32_t (*callback)()) -> int32_t { return callback(); },
|
||||
[](auto &) -> int32_t { return 0; },
|
||||
},
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
std::string_view *Symbol::getMacro() const
|
||||
{
|
||||
std::string_view *Symbol::getMacro() const {
|
||||
assert(std::holds_alternative<std::string_view *>(data));
|
||||
return std::get<std::string_view *>(data);
|
||||
}
|
||||
|
||||
std::string *Symbol::getEqus() const
|
||||
{
|
||||
std::string *Symbol::getEqus() const {
|
||||
assert(std::holds_alternative<std::string *>(data));
|
||||
return std::get<std::string *>(data);
|
||||
}
|
||||
|
||||
static void dumpFilename(Symbol const &sym)
|
||||
{
|
||||
static void dumpFilename(Symbol const &sym) {
|
||||
if (sym.src)
|
||||
sym.src->dump(sym.fileLine);
|
||||
else if (sym.fileLine == 0)
|
||||
@@ -107,15 +102,13 @@ static void dumpFilename(Symbol const &sym)
|
||||
}
|
||||
|
||||
// Set a symbol's definition filename and line
|
||||
static void setSymbolFilename(Symbol &sym)
|
||||
{
|
||||
sym.src = fstk_GetFileStack(); // This is `nullptr` for built-ins
|
||||
static void setSymbolFilename(Symbol &sym) {
|
||||
sym.src = fstk_GetFileStack(); // This is `nullptr` for built-ins
|
||||
sym.fileLine = sym.src ? lexer_GetLineNo() : 0; // This is 1 for built-ins
|
||||
}
|
||||
|
||||
// Update a symbol's definition filename and line
|
||||
static void updateSymbolFilename(Symbol &sym)
|
||||
{
|
||||
static void updateSymbolFilename(Symbol &sym) {
|
||||
FileStackNode *oldSrc = sym.src;
|
||||
|
||||
setSymbolFilename(sym);
|
||||
@@ -126,8 +119,7 @@ static void updateSymbolFilename(Symbol &sym)
|
||||
}
|
||||
|
||||
// Create a new symbol by name
|
||||
static Symbol &createsymbol(char const *symName)
|
||||
{
|
||||
static Symbol &createsymbol(char const *symName) {
|
||||
Symbol &sym = symbols[symName];
|
||||
|
||||
if (snprintf(sym.name, MAXSYMLEN + 1, "%s", symName) > MAXSYMLEN)
|
||||
@@ -144,9 +136,8 @@ static Symbol &createsymbol(char const *symName)
|
||||
|
||||
// Creates the full name of a local symbol in a given scope, by prepending
|
||||
// the name with the parent symbol's name.
|
||||
static void fullSymbolName(char *output, size_t outputSize,
|
||||
char const *localName, char const *scopeName)
|
||||
{
|
||||
static void
|
||||
fullSymbolName(char *output, size_t outputSize, char const *localName, char const *scopeName) {
|
||||
int ret = snprintf(output, outputSize, "%s%s", scopeName, localName);
|
||||
|
||||
if (ret < 0)
|
||||
@@ -155,27 +146,23 @@ static void fullSymbolName(char *output, size_t outputSize,
|
||||
fatalerror("Symbol name is too long: '%s%s'\n", scopeName, localName);
|
||||
}
|
||||
|
||||
static void assignStringSymbol(Symbol &sym, char const *value)
|
||||
{
|
||||
std::string *equs = new(std::nothrow) std::string(value);
|
||||
static void assignStringSymbol(Symbol &sym, char const *value) {
|
||||
std::string *equs = new (std::nothrow) std::string(value);
|
||||
if (!equs)
|
||||
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
||||
sym.type = SYM_EQUS;
|
||||
sym.data = equs;
|
||||
}
|
||||
|
||||
Symbol *sym_FindExactSymbol(char const *symName)
|
||||
{
|
||||
Symbol *sym_FindExactSymbol(char const *symName) {
|
||||
auto search = symbols.find(symName);
|
||||
return search != symbols.end() ? &search->second : nullptr;
|
||||
}
|
||||
|
||||
Symbol *sym_FindScopedSymbol(char const *symName)
|
||||
{
|
||||
Symbol *sym_FindScopedSymbol(char const *symName) {
|
||||
if (char const *localName = strchr(symName, '.'); localName) {
|
||||
if (strchr(localName + 1, '.'))
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
|
||||
symName);
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n", symName);
|
||||
// If auto-scoped local label, expand the name
|
||||
if (localName == symName) { // Meaning, the name begins with the dot
|
||||
char fullName[MAXSYMLEN + 1];
|
||||
@@ -187,8 +174,7 @@ Symbol *sym_FindScopedSymbol(char const *symName)
|
||||
return sym_FindExactSymbol(symName);
|
||||
}
|
||||
|
||||
Symbol *sym_FindScopedValidSymbol(char const *symName)
|
||||
{
|
||||
Symbol *sym_FindScopedValidSymbol(char const *symName) {
|
||||
Symbol *sym = sym_FindScopedSymbol(symName);
|
||||
|
||||
// `@` has no value outside a section
|
||||
@@ -202,14 +188,12 @@ Symbol *sym_FindScopedValidSymbol(char const *symName)
|
||||
return sym;
|
||||
}
|
||||
|
||||
Symbol const *sym_GetPC()
|
||||
{
|
||||
Symbol const *sym_GetPC() {
|
||||
return PCSymbol;
|
||||
}
|
||||
|
||||
// Purge a symbol
|
||||
void sym_Purge(std::string const &symName)
|
||||
{
|
||||
void sym_Purge(std::string const &symName) {
|
||||
Symbol *sym = sym_FindScopedValidSymbol(symName.c_str());
|
||||
|
||||
if (!sym) {
|
||||
@@ -230,8 +214,7 @@ void sym_Purge(std::string const &symName)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t sym_GetPCValue()
|
||||
{
|
||||
uint32_t sym_GetPCValue() {
|
||||
Section const *sect = sect_GetSymbolSection();
|
||||
|
||||
if (!sect)
|
||||
@@ -244,8 +227,7 @@ uint32_t sym_GetPCValue()
|
||||
}
|
||||
|
||||
// Return a constant symbol's value, assuming it's defined
|
||||
uint32_t Symbol::getConstantValue() const
|
||||
{
|
||||
uint32_t Symbol::getConstantValue() const {
|
||||
if (sym_IsPC(this))
|
||||
return sym_GetPCValue();
|
||||
|
||||
@@ -257,8 +239,7 @@ uint32_t Symbol::getConstantValue() const
|
||||
}
|
||||
|
||||
// Return a constant symbol's value
|
||||
uint32_t sym_GetConstantValue(char const *symName)
|
||||
{
|
||||
uint32_t sym_GetConstantValue(char const *symName) {
|
||||
if (Symbol const *sym = sym_FindScopedSymbol(symName); sym)
|
||||
return sym->getConstantValue();
|
||||
|
||||
@@ -266,13 +247,11 @@ uint32_t sym_GetConstantValue(char const *symName)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char const *sym_GetCurrentSymbolScope()
|
||||
{
|
||||
char const *sym_GetCurrentSymbolScope() {
|
||||
return labelScope;
|
||||
}
|
||||
|
||||
void sym_SetCurrentSymbolScope(char const *newScope)
|
||||
{
|
||||
void sym_SetCurrentSymbolScope(char const *newScope) {
|
||||
labelScope = newScope;
|
||||
}
|
||||
|
||||
@@ -283,8 +262,7 @@ void sym_SetCurrentSymbolScope(char const *newScope)
|
||||
* @param symName The name of the symbol to create
|
||||
* @param numeric If false, the symbol may not have been referenced earlier
|
||||
*/
|
||||
static Symbol *createNonrelocSymbol(char const *symName, bool numeric)
|
||||
{
|
||||
static Symbol *createNonrelocSymbol(char const *symName, bool numeric) {
|
||||
Symbol *sym = sym_FindExactSymbol(symName);
|
||||
|
||||
if (!sym) {
|
||||
@@ -306,8 +284,7 @@ static Symbol *createNonrelocSymbol(char const *symName, bool numeric)
|
||||
}
|
||||
|
||||
// Add an equated symbol
|
||||
Symbol *sym_AddEqu(char const *symName, int32_t value)
|
||||
{
|
||||
Symbol *sym_AddEqu(char const *symName, int32_t value) {
|
||||
Symbol *sym = createNonrelocSymbol(symName, true);
|
||||
|
||||
if (!sym)
|
||||
@@ -319,8 +296,7 @@ Symbol *sym_AddEqu(char const *symName, int32_t value)
|
||||
return sym;
|
||||
}
|
||||
|
||||
Symbol *sym_RedefEqu(char const *symName, int32_t value)
|
||||
{
|
||||
Symbol *sym_RedefEqu(char const *symName, int32_t value) {
|
||||
Symbol *sym = sym_FindExactSymbol(symName);
|
||||
|
||||
if (!sym)
|
||||
@@ -355,8 +331,7 @@ Symbol *sym_RedefEqu(char const *symName, int32_t value)
|
||||
* of the string are enough: sym_AddString("M_PI", "3.1415"). This is the same
|
||||
* as ``` M_PI EQUS "3.1415" ```
|
||||
*/
|
||||
Symbol *sym_AddString(char const *symName, char const *value)
|
||||
{
|
||||
Symbol *sym_AddString(char const *symName, char const *value) {
|
||||
Symbol *sym = createNonrelocSymbol(symName, false);
|
||||
|
||||
if (!sym)
|
||||
@@ -366,8 +341,7 @@ Symbol *sym_AddString(char const *symName, char const *value)
|
||||
return sym;
|
||||
}
|
||||
|
||||
Symbol *sym_RedefString(char const *symName, char const *value)
|
||||
{
|
||||
Symbol *sym_RedefString(char const *symName, char const *value) {
|
||||
Symbol *sym = sym_FindExactSymbol(symName);
|
||||
|
||||
if (!sym)
|
||||
@@ -395,15 +369,15 @@ Symbol *sym_RedefString(char const *symName, char const *value)
|
||||
}
|
||||
|
||||
// Alter a mutable symbol's value
|
||||
Symbol *sym_AddVar(char const *symName, int32_t value)
|
||||
{
|
||||
Symbol *sym_AddVar(char const *symName, int32_t value) {
|
||||
Symbol *sym = sym_FindExactSymbol(symName);
|
||||
|
||||
if (!sym) {
|
||||
sym = &createsymbol(symName);
|
||||
} else if (sym->isDefined() && sym->type != SYM_VAR) {
|
||||
error("'%s' already defined as %s at ",
|
||||
symName, sym->type == SYM_LABEL ? "label" : "constant");
|
||||
error(
|
||||
"'%s' already defined as %s at ", symName, sym->type == SYM_LABEL ? "label" : "constant"
|
||||
);
|
||||
dumpFilename(*sym);
|
||||
putc('\n', stderr);
|
||||
return sym;
|
||||
@@ -422,8 +396,7 @@ Symbol *sym_AddVar(char const *symName, int32_t value)
|
||||
* @param symName The label's full name (so `.name` is invalid)
|
||||
* @return The created symbol
|
||||
*/
|
||||
static Symbol *addLabel(char const *symName)
|
||||
{
|
||||
static Symbol *addLabel(char const *symName) {
|
||||
assert(symName[0] != '.'); // The symbol name must have been expanded prior
|
||||
Symbol *sym = sym_FindExactSymbol(symName);
|
||||
|
||||
@@ -452,8 +425,7 @@ static Symbol *addLabel(char const *symName)
|
||||
}
|
||||
|
||||
// Add a local (`.name` or `Parent.name`) relocatable symbol
|
||||
Symbol *sym_AddLocalLabel(char const *symName)
|
||||
{
|
||||
Symbol *sym_AddLocalLabel(char const *symName) {
|
||||
// Assuming no dots in `labelScope` if defined
|
||||
assert(!labelScope || !strchr(labelScope, '.'));
|
||||
|
||||
@@ -464,13 +436,11 @@ Symbol *sym_AddLocalLabel(char const *symName)
|
||||
|
||||
// Check for something after the dot in `localName`
|
||||
if (localName[1] == '\0') {
|
||||
fatalerror("'%s' is a nonsensical reference to an empty local label\n",
|
||||
symName);
|
||||
fatalerror("'%s' is a nonsensical reference to an empty local label\n", symName);
|
||||
}
|
||||
// Check for more than one dot in `localName`
|
||||
if (strchr(localName + 1, '.'))
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local label\n",
|
||||
symName);
|
||||
fatalerror("'%s' is a nonsensical reference to a nested local label\n", symName);
|
||||
|
||||
if (localName == symName) {
|
||||
if (!labelScope) {
|
||||
@@ -486,8 +456,7 @@ Symbol *sym_AddLocalLabel(char const *symName)
|
||||
}
|
||||
|
||||
// Add a relocatable symbol
|
||||
Symbol *sym_AddLabel(char const *symName)
|
||||
{
|
||||
Symbol *sym_AddLabel(char const *symName) {
|
||||
Symbol *sym = addLabel(symName);
|
||||
|
||||
// Set the symbol as the new scope
|
||||
@@ -499,8 +468,7 @@ Symbol *sym_AddLabel(char const *symName)
|
||||
static uint32_t anonLabelID;
|
||||
|
||||
// Add an anonymous label
|
||||
Symbol *sym_AddAnonLabel()
|
||||
{
|
||||
Symbol *sym_AddAnonLabel() {
|
||||
if (anonLabelID == UINT32_MAX) {
|
||||
error("Only %" PRIu32 " anonymous labels can be created!", anonLabelID);
|
||||
return nullptr;
|
||||
@@ -513,22 +481,29 @@ Symbol *sym_AddAnonLabel()
|
||||
}
|
||||
|
||||
// Write an anonymous label's name to a buffer
|
||||
void sym_WriteAnonLabelName(char buf[MAXSYMLEN + 1], uint32_t ofs, bool neg)
|
||||
{
|
||||
void sym_WriteAnonLabelName(char buf[MAXSYMLEN + 1], uint32_t ofs, bool neg) {
|
||||
uint32_t id = 0;
|
||||
|
||||
if (neg) {
|
||||
if (ofs > anonLabelID)
|
||||
error("Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
|
||||
" ha%s been created so far\n",
|
||||
ofs, anonLabelID, anonLabelID == 1 ? "s" : "ve");
|
||||
error(
|
||||
"Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
|
||||
" ha%s been created so far\n",
|
||||
ofs,
|
||||
anonLabelID,
|
||||
anonLabelID == 1 ? "s" : "ve"
|
||||
);
|
||||
else
|
||||
id = anonLabelID - ofs;
|
||||
} else {
|
||||
ofs--; // We're referencing symbols that haven't been created yet...
|
||||
if (ofs > UINT32_MAX - anonLabelID)
|
||||
error("Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
|
||||
" may still be created\n", ofs + 1, UINT32_MAX - anonLabelID);
|
||||
error(
|
||||
"Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
|
||||
" may still be created\n",
|
||||
ofs + 1,
|
||||
UINT32_MAX - anonLabelID
|
||||
);
|
||||
else
|
||||
id = anonLabelID + ofs;
|
||||
}
|
||||
@@ -537,8 +512,7 @@ void sym_WriteAnonLabelName(char buf[MAXSYMLEN + 1], uint32_t ofs, bool neg)
|
||||
}
|
||||
|
||||
// Export a symbol
|
||||
void sym_Export(char const *symName)
|
||||
{
|
||||
void sym_Export(char const *symName) {
|
||||
if (symName[0] == '!') {
|
||||
error("Anonymous labels cannot be exported\n");
|
||||
return;
|
||||
@@ -553,14 +527,13 @@ void sym_Export(char const *symName)
|
||||
}
|
||||
|
||||
// Add a macro definition
|
||||
Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char const *body, size_t size)
|
||||
{
|
||||
Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char const *body, size_t size) {
|
||||
Symbol *sym = createNonrelocSymbol(symName, false);
|
||||
|
||||
if (!sym)
|
||||
return nullptr;
|
||||
|
||||
std::string_view *macro = new(std::nothrow) std::string_view(body, size);
|
||||
std::string_view *macro = new (std::nothrow) std::string_view(body, size);
|
||||
if (!macro)
|
||||
fatalerror("No memory for macro: %s\n", strerror(errno));
|
||||
sym->type = SYM_MACRO;
|
||||
@@ -576,8 +549,7 @@ Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char const *body, s
|
||||
|
||||
// Flag that a symbol is referenced in an RPN expression
|
||||
// and create it if it doesn't exist yet
|
||||
Symbol *sym_Ref(char const *symName)
|
||||
{
|
||||
Symbol *sym_Ref(char const *symName) {
|
||||
Symbol *sym = sym_FindScopedSymbol(symName);
|
||||
|
||||
if (!sym) {
|
||||
@@ -598,13 +570,11 @@ Symbol *sym_Ref(char const *symName)
|
||||
}
|
||||
|
||||
// Set whether to export all relocatable symbols by default
|
||||
void sym_SetExportAll(bool set)
|
||||
{
|
||||
void sym_SetExportAll(bool set) {
|
||||
exportAll = set;
|
||||
}
|
||||
|
||||
static Symbol *createBuiltinSymbol(char const *symName)
|
||||
{
|
||||
static Symbol *createBuiltinSymbol(char const *symName) {
|
||||
Symbol *sym = &createsymbol(symName);
|
||||
|
||||
sym->isBuiltin = true;
|
||||
@@ -615,8 +585,7 @@ static Symbol *createBuiltinSymbol(char const *symName)
|
||||
}
|
||||
|
||||
// Initialize the symboltable
|
||||
void sym_Init(time_t now)
|
||||
{
|
||||
void sym_Init(time_t now) {
|
||||
PCSymbol = createBuiltinSymbol("@");
|
||||
PCSymbol->type = SYM_LABEL;
|
||||
PCSymbol->data = CallbackPC;
|
||||
@@ -627,11 +596,12 @@ void sym_Init(time_t now)
|
||||
|
||||
sym_AddVar("_RS", 0)->isBuiltin = true;
|
||||
|
||||
#define addSym(fn, name, val) do { \
|
||||
Symbol *sym = fn(name, val); \
|
||||
assert(sym); \
|
||||
sym->isBuiltin = true; \
|
||||
} while (0)
|
||||
#define addSym(fn, name, val) \
|
||||
do { \
|
||||
Symbol *sym = fn(name, val); \
|
||||
assert(sym); \
|
||||
sym->isBuiltin = true; \
|
||||
} while (0)
|
||||
#define addNumber(name, val) addSym(sym_AddEqu, name, val)
|
||||
#define addString(name, val) addSym(sym_AddString, name, val)
|
||||
|
||||
@@ -653,15 +623,21 @@ void sym_Init(time_t now)
|
||||
|
||||
strftime(savedTIME, sizeof(savedTIME), "\"%H:%M:%S\"", time_local);
|
||||
strftime(savedDATE, sizeof(savedDATE), "\"%d %B %Y\"", time_local);
|
||||
strftime(savedTIMESTAMP_ISO8601_LOCAL,
|
||||
sizeof(savedTIMESTAMP_ISO8601_LOCAL), "\"%Y-%m-%dT%H:%M:%S%z\"",
|
||||
time_local);
|
||||
strftime(
|
||||
savedTIMESTAMP_ISO8601_LOCAL,
|
||||
sizeof(savedTIMESTAMP_ISO8601_LOCAL),
|
||||
"\"%Y-%m-%dT%H:%M:%S%z\"",
|
||||
time_local
|
||||
);
|
||||
|
||||
const tm *time_utc = gmtime(&now);
|
||||
|
||||
strftime(savedTIMESTAMP_ISO8601_UTC,
|
||||
sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
|
||||
time_utc);
|
||||
strftime(
|
||||
savedTIMESTAMP_ISO8601_UTC,
|
||||
sizeof(savedTIMESTAMP_ISO8601_UTC),
|
||||
"\"%Y-%m-%dT%H:%M:%SZ\"",
|
||||
time_utc
|
||||
);
|
||||
|
||||
addString("__TIME__", savedTIME);
|
||||
addString("__DATE__", savedDATE);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
@@ -8,48 +10,46 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
#include "error.hpp"
|
||||
#include "itertools.hpp"
|
||||
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/main.hpp"
|
||||
|
||||
unsigned int nbErrors = 0;
|
||||
unsigned int maxErrors = 0;
|
||||
|
||||
static const enum WarningState defaultWarnings[ARRAY_SIZE(warningStates)] = {
|
||||
WARNING_ENABLED, // WARNING_ASSERT
|
||||
WARNING_DISABLED, // WARNING_BACKWARDS_FOR
|
||||
WARNING_DISABLED, // WARNING_BUILTIN_ARG
|
||||
WARNING_DISABLED, // WARNING_CHARMAP_REDEF
|
||||
WARNING_DISABLED, // WARNING_DIV
|
||||
WARNING_DISABLED, // WARNING_EMPTY_DATA_DIRECTIVE
|
||||
WARNING_DISABLED, // WARNING_EMPTY_MACRO_ARG
|
||||
WARNING_DISABLED, // WARNING_EMPTY_STRRPL
|
||||
WARNING_DISABLED, // WARNING_LARGE_CONSTANT
|
||||
WARNING_DISABLED, // WARNING_LONG_STR
|
||||
WARNING_DISABLED, // WARNING_MACRO_SHIFT
|
||||
WARNING_ENABLED, // WARNING_NESTED_COMMENT
|
||||
WARNING_ENABLED, // WARNING_OBSOLETE
|
||||
WARNING_DISABLED, // WARNING_SHIFT
|
||||
WARNING_DISABLED, // WARNING_SHIFT_AMOUNT
|
||||
WARNING_ENABLED, // WARNING_USER
|
||||
WARNING_ENABLED, // WARNING_ASSERT
|
||||
WARNING_DISABLED, // WARNING_BACKWARDS_FOR
|
||||
WARNING_DISABLED, // WARNING_BUILTIN_ARG
|
||||
WARNING_DISABLED, // WARNING_CHARMAP_REDEF
|
||||
WARNING_DISABLED, // WARNING_DIV
|
||||
WARNING_DISABLED, // WARNING_EMPTY_DATA_DIRECTIVE
|
||||
WARNING_DISABLED, // WARNING_EMPTY_MACRO_ARG
|
||||
WARNING_DISABLED, // WARNING_EMPTY_STRRPL
|
||||
WARNING_DISABLED, // WARNING_LARGE_CONSTANT
|
||||
WARNING_DISABLED, // WARNING_LONG_STR
|
||||
WARNING_DISABLED, // WARNING_MACRO_SHIFT
|
||||
WARNING_ENABLED, // WARNING_NESTED_COMMENT
|
||||
WARNING_ENABLED, // WARNING_OBSOLETE
|
||||
WARNING_DISABLED, // WARNING_SHIFT
|
||||
WARNING_DISABLED, // WARNING_SHIFT_AMOUNT
|
||||
WARNING_ENABLED, // WARNING_USER
|
||||
|
||||
WARNING_ENABLED, // WARNING_NUMERIC_STRING_1
|
||||
WARNING_DISABLED, // WARNING_NUMERIC_STRING_2
|
||||
WARNING_ENABLED, // WARNING_TRUNCATION_1
|
||||
WARNING_DISABLED, // WARNING_TRUNCATION_2
|
||||
WARNING_ENABLED, // WARNING_UNMAPPED_CHAR_1
|
||||
WARNING_DISABLED, // WARNING_UNMAPPED_CHAR_2
|
||||
WARNING_ENABLED, // WARNING_NUMERIC_STRING_1
|
||||
WARNING_DISABLED, // WARNING_NUMERIC_STRING_2
|
||||
WARNING_ENABLED, // WARNING_TRUNCATION_1
|
||||
WARNING_DISABLED, // WARNING_TRUNCATION_2
|
||||
WARNING_ENABLED, // WARNING_UNMAPPED_CHAR_1
|
||||
WARNING_DISABLED, // WARNING_UNMAPPED_CHAR_2
|
||||
};
|
||||
|
||||
enum WarningState warningStates[ARRAY_SIZE(warningStates)];
|
||||
|
||||
bool warningsAreErrors; // Set if `-Werror` was specified
|
||||
|
||||
static enum WarningState warningState(enum WarningID id)
|
||||
{
|
||||
static enum WarningState warningState(enum WarningID id) {
|
||||
// Check if warnings are globally disabled
|
||||
if (!warnings)
|
||||
return WARNING_DISABLED;
|
||||
@@ -68,35 +68,35 @@ static enum WarningState warningState(enum WarningID id)
|
||||
}
|
||||
|
||||
static const char * const warningFlags[NB_WARNINGS] = {
|
||||
"assert",
|
||||
"backwards-for",
|
||||
"builtin-args",
|
||||
"charmap-redef",
|
||||
"div",
|
||||
"empty-data-directive",
|
||||
"empty-macro-arg",
|
||||
"empty-strrpl",
|
||||
"large-constant",
|
||||
"long-string",
|
||||
"macro-shift",
|
||||
"nested-comment",
|
||||
"obsolete",
|
||||
"shift",
|
||||
"shift-amount",
|
||||
"user",
|
||||
"assert",
|
||||
"backwards-for",
|
||||
"builtin-args",
|
||||
"charmap-redef",
|
||||
"div",
|
||||
"empty-data-directive",
|
||||
"empty-macro-arg",
|
||||
"empty-strrpl",
|
||||
"large-constant",
|
||||
"long-string",
|
||||
"macro-shift",
|
||||
"nested-comment",
|
||||
"obsolete",
|
||||
"shift",
|
||||
"shift-amount",
|
||||
"user",
|
||||
|
||||
// Parametric warnings
|
||||
"numeric-string",
|
||||
"numeric-string",
|
||||
"truncation",
|
||||
"truncation",
|
||||
"unmapped-char",
|
||||
"unmapped-char",
|
||||
// Parametric warnings
|
||||
"numeric-string",
|
||||
"numeric-string",
|
||||
"truncation",
|
||||
"truncation",
|
||||
"unmapped-char",
|
||||
"unmapped-char",
|
||||
|
||||
// Meta warnings
|
||||
"all",
|
||||
"extra",
|
||||
"everything", // Especially useful for testing
|
||||
// Meta warnings
|
||||
"all",
|
||||
"extra",
|
||||
"everything", // Especially useful for testing
|
||||
};
|
||||
|
||||
static const struct {
|
||||
@@ -104,13 +104,12 @@ static const struct {
|
||||
uint8_t nbLevels;
|
||||
uint8_t defaultLevel;
|
||||
} paramWarnings[] = {
|
||||
{ "numeric-string", 2, 1 },
|
||||
{ "truncation", 2, 2 },
|
||||
{ "unmapped-char", 2, 1 },
|
||||
{"numeric-string", 2, 1},
|
||||
{"truncation", 2, 2},
|
||||
{"unmapped-char", 2, 1},
|
||||
};
|
||||
|
||||
static bool tryProcessParamWarning(char const *flag, uint8_t param, enum WarningState state)
|
||||
{
|
||||
static bool tryProcessParamWarning(char const *flag, uint8_t param, enum WarningState state) {
|
||||
enum WarningID baseID = PARAM_WARNINGS_START;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(paramWarnings); i++) {
|
||||
@@ -126,17 +125,19 @@ static bool tryProcessParamWarning(char const *flag, uint8_t param, enum Warning
|
||||
param = paramWarnings[i].defaultLevel;
|
||||
} else if (param > maxParam) {
|
||||
if (param != 255) // Don't warn if already capped
|
||||
warnx("Got parameter %" PRIu8
|
||||
" for warning flag \"%s\", but the maximum is %"
|
||||
PRIu8 "; capping.\n",
|
||||
param, flag, maxParam);
|
||||
warnx(
|
||||
"Got parameter %" PRIu8
|
||||
" for warning flag \"%s\", but the maximum is %" PRIu8 "; capping.\n",
|
||||
param,
|
||||
flag,
|
||||
maxParam
|
||||
);
|
||||
param = maxParam;
|
||||
}
|
||||
|
||||
// Set the first <param> to enabled/error, and disable the rest
|
||||
for (uint8_t ofs = 0; ofs < maxParam; ofs++) {
|
||||
warningStates[baseID + ofs] =
|
||||
ofs < param ? state : WARNING_DISABLED;
|
||||
warningStates[baseID + ofs] = ofs < param ? state : WARNING_DISABLED;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -146,73 +147,70 @@ static bool tryProcessParamWarning(char const *flag, uint8_t param, enum Warning
|
||||
return false;
|
||||
}
|
||||
|
||||
enum MetaWarningCommand {
|
||||
META_WARNING_DONE = NB_WARNINGS
|
||||
};
|
||||
enum MetaWarningCommand { META_WARNING_DONE = NB_WARNINGS };
|
||||
|
||||
// Warnings that probably indicate an error
|
||||
static uint8_t const _wallCommands[] = {
|
||||
WARNING_BACKWARDS_FOR,
|
||||
WARNING_BUILTIN_ARG,
|
||||
WARNING_CHARMAP_REDEF,
|
||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||
WARNING_EMPTY_STRRPL,
|
||||
WARNING_LARGE_CONSTANT,
|
||||
WARNING_LONG_STR,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_NUMERIC_STRING_1,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
META_WARNING_DONE
|
||||
WARNING_BACKWARDS_FOR,
|
||||
WARNING_BUILTIN_ARG,
|
||||
WARNING_CHARMAP_REDEF,
|
||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||
WARNING_EMPTY_STRRPL,
|
||||
WARNING_LARGE_CONSTANT,
|
||||
WARNING_LONG_STR,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_NUMERIC_STRING_1,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
META_WARNING_DONE,
|
||||
};
|
||||
|
||||
// Warnings that are less likely to indicate an error
|
||||
static uint8_t const _wextraCommands[] = {
|
||||
WARNING_EMPTY_MACRO_ARG,
|
||||
WARNING_MACRO_SHIFT,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_NUMERIC_STRING_2,
|
||||
WARNING_TRUNCATION_1,
|
||||
WARNING_TRUNCATION_2,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
META_WARNING_DONE
|
||||
WARNING_EMPTY_MACRO_ARG,
|
||||
WARNING_MACRO_SHIFT,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_NUMERIC_STRING_2,
|
||||
WARNING_TRUNCATION_1,
|
||||
WARNING_TRUNCATION_2,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
META_WARNING_DONE,
|
||||
};
|
||||
|
||||
// Literally everything. Notably useful for testing
|
||||
static uint8_t const _weverythingCommands[] = {
|
||||
WARNING_BACKWARDS_FOR,
|
||||
WARNING_BUILTIN_ARG,
|
||||
WARNING_DIV,
|
||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||
WARNING_EMPTY_MACRO_ARG,
|
||||
WARNING_EMPTY_STRRPL,
|
||||
WARNING_LARGE_CONSTANT,
|
||||
WARNING_LONG_STR,
|
||||
WARNING_MACRO_SHIFT,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_SHIFT,
|
||||
WARNING_SHIFT_AMOUNT,
|
||||
WARNING_NUMERIC_STRING_1,
|
||||
WARNING_NUMERIC_STRING_2,
|
||||
WARNING_TRUNCATION_1,
|
||||
WARNING_TRUNCATION_2,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
// WARNING_USER,
|
||||
META_WARNING_DONE
|
||||
WARNING_BACKWARDS_FOR,
|
||||
WARNING_BUILTIN_ARG,
|
||||
WARNING_DIV,
|
||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||
WARNING_EMPTY_MACRO_ARG,
|
||||
WARNING_EMPTY_STRRPL,
|
||||
WARNING_LARGE_CONSTANT,
|
||||
WARNING_LONG_STR,
|
||||
WARNING_MACRO_SHIFT,
|
||||
WARNING_NESTED_COMMENT,
|
||||
WARNING_OBSOLETE,
|
||||
WARNING_SHIFT,
|
||||
WARNING_SHIFT_AMOUNT,
|
||||
WARNING_NUMERIC_STRING_1,
|
||||
WARNING_NUMERIC_STRING_2,
|
||||
WARNING_TRUNCATION_1,
|
||||
WARNING_TRUNCATION_2,
|
||||
WARNING_UNMAPPED_CHAR_1,
|
||||
WARNING_UNMAPPED_CHAR_2,
|
||||
// WARNING_USER,
|
||||
META_WARNING_DONE,
|
||||
};
|
||||
|
||||
static uint8_t const *metaWarningCommands[NB_META_WARNINGS] = {
|
||||
_wallCommands,
|
||||
_wextraCommands,
|
||||
_weverythingCommands
|
||||
_wallCommands,
|
||||
_wextraCommands,
|
||||
_weverythingCommands,
|
||||
};
|
||||
|
||||
void processWarningFlag(char *flag)
|
||||
{
|
||||
void processWarningFlag(char *flag) {
|
||||
static bool setError = false;
|
||||
|
||||
// First, try to match against a "meta" warning
|
||||
@@ -221,11 +219,11 @@ void processWarningFlag(char *flag)
|
||||
if (!strcmp(flag, warningFlags[id])) {
|
||||
// We got a match!
|
||||
if (setError)
|
||||
errx("Cannot make meta warning \"%s\" into an error",
|
||||
flag);
|
||||
errx("Cannot make meta warning \"%s\" into an error", flag);
|
||||
|
||||
for (uint8_t const *ptr = metaWarningCommands[id - META_WARNINGS_START];
|
||||
*ptr != META_WARNING_DONE; ptr++) {
|
||||
*ptr != META_WARNING_DONE;
|
||||
ptr++) {
|
||||
// Warning flag, set without override
|
||||
if (warningStates[*ptr] == WARNING_DEFAULT)
|
||||
warningStates[*ptr] = WARNING_ENABLED;
|
||||
@@ -252,16 +250,16 @@ void processWarningFlag(char *flag)
|
||||
setError = false;
|
||||
return;
|
||||
|
||||
// Otherwise, allow parsing as another flag
|
||||
// Otherwise, allow parsing as another flag
|
||||
}
|
||||
}
|
||||
|
||||
// Well, it's either a normal warning or a mistake
|
||||
|
||||
enum WarningState state = setError ? WARNING_ERROR
|
||||
// Not an error, then check if this is a negation
|
||||
: strncmp(flag, "no-", strlen("no-")) ? WARNING_ENABLED
|
||||
: WARNING_DISABLED;
|
||||
// Not an error, then check if this is a negation
|
||||
: strncmp(flag, "no-", strlen("no-")) ? WARNING_ENABLED
|
||||
: WARNING_DISABLED;
|
||||
char *rootFlag = state == WARNING_DISABLED ? flag + strlen("no-") : flag;
|
||||
|
||||
// Is this a "parametric" warning?
|
||||
@@ -284,8 +282,7 @@ void processWarningFlag(char *flag)
|
||||
// Avoid overflowing!
|
||||
if (param > UINT8_MAX - (*ptr - '0')) {
|
||||
if (!warned)
|
||||
warnx("Invalid warning flag \"%s\": capping parameter at 255\n",
|
||||
flag);
|
||||
warnx("Invalid warning flag \"%s\": capping parameter at 255\n", flag);
|
||||
warned = true; // Only warn once, cap always
|
||||
param = 255;
|
||||
continue;
|
||||
@@ -303,8 +300,7 @@ void processWarningFlag(char *flag)
|
||||
return;
|
||||
}
|
||||
*equals = '\0'; // Truncate the param at the '='
|
||||
if (tryProcessParamWarning(rootFlag, param,
|
||||
param == 0 ? WARNING_DISABLED : state))
|
||||
if (tryProcessParamWarning(rootFlag, param, param == 0 ? WARNING_DISABLED : state))
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -327,9 +323,9 @@ void processWarningFlag(char *flag)
|
||||
warnx("Unknown warning `%s`", flag);
|
||||
}
|
||||
|
||||
void printDiag(char const *fmt, va_list args, char const *type,
|
||||
char const *flagfmt, char const *flag)
|
||||
{
|
||||
void printDiag(
|
||||
char const *fmt, va_list args, char const *type, char const *flagfmt, char const *flag
|
||||
) {
|
||||
fputs(type, stderr);
|
||||
fputs(": ", stderr);
|
||||
fstk_DumpCurrent();
|
||||
@@ -339,8 +335,7 @@ void printDiag(char const *fmt, va_list args, char const *type,
|
||||
lexer_DumpStringExpansions();
|
||||
}
|
||||
|
||||
void error(char const *fmt, ...)
|
||||
{
|
||||
void error(char const *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
@@ -350,12 +345,15 @@ void error(char const *fmt, ...)
|
||||
// This intentionally makes 0 act as "unlimited" (or at least "limited to sizeof(unsigned)")
|
||||
nbErrors++;
|
||||
if (nbErrors == maxErrors)
|
||||
errx("The maximum of %u error%s was reached (configure with \"-X/--max-errors\"); assembly aborted!",
|
||||
maxErrors, maxErrors == 1 ? "" : "s");
|
||||
errx(
|
||||
"The maximum of %u error%s was reached (configure with \"-X/--max-errors\"); assembly "
|
||||
"aborted!",
|
||||
maxErrors,
|
||||
maxErrors == 1 ? "" : "s"
|
||||
);
|
||||
}
|
||||
|
||||
[[noreturn]] void fatalerror(char const *fmt, ...)
|
||||
{
|
||||
[[noreturn]] void fatalerror(char const *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
@@ -365,8 +363,7 @@ void error(char const *fmt, ...)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void warning(enum WarningID id, char const *fmt, ...)
|
||||
{
|
||||
void warning(enum WarningID id, char const *fmt, ...) {
|
||||
char const *flag = warningFlags[id];
|
||||
va_list args;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user